Compare commits

...

1023 Commits

Author SHA1 Message Date
Quantum
299703bd52 [idd] helper: implement mode deletion 2025-11-08 03:40:22 -05: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
Geoffrey McRae
27fe47cbe2 [obs] dmabuf: enable dmabuf by default
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
2025-03-06 14:39:51 +11:00
Geoffrey McRae
aefbebff9c [obs] cosmetics: adhere to 80 column rule 2025-03-06 14:39:31 +11:00
Geoffrey McRae
9174b1ae0f [host] d12: perform full re-initialization if the heapTest fails
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
2025-03-05 16:44:29 +11:00
Geoffrey McRae
f6b7ea11c8 [host] app: fix complation on windows with mingw 2025-03-05 16:34:07 +11:00
Geoffrey McRae
2c50ce4dbd [github] build: add libfontconfig-dev dependency 2025-03-05 16:25:01 +11:00
Geoffrey McRae
f6b0752e45 [github] build: use native packages now ubuntu 24.04 is available 2025-03-05 16:22:00 +11:00
Geoffrey McRae
50fee59b29 [client] app: fix broken keyboard input after imgui update 2025-03-05 16:07:06 +11:00
Geoffrey McRae
554f5bf75d [client] overlay: use graph address as imgui id 2025-03-05 15:25:34 +11:00
Geoffrey McRae
b43f572af0 Revert "[client] overlay/status: don't return damage rect if nothing was drawn"
This reverts commit 7e9e38faa5 as it
causes screen corruption when moving imgui overlay dialogs around.
2025-03-05 15:18:29 +11:00
Geoffrey McRae
d9f2df361d [client] cimgui: update to 1.91.8 2025-03-05 15:16:31 +11:00
Geoffrey McRae
6dcf178879 [github] linux-host: add missing dependency for xcb/shm 2025-03-05 13:32:33 +11:00
Geoffrey McRae
ea00b623ed [github] linux-host: add missing dependency for gio/gio-unix 2025-03-05 13:27:43 +11:00
Geoffrey McRae
711c932380 [all] common: fix compilation on clang 2025-03-05 12:48:20 +11:00
Geoffrey McRae
be52a86a9a [all] repos: update LGMP and PureSpice submodules 2025-03-05 12:44:58 +11:00
Geoffrey McRae
420eaebb71 [cmake] all: update cmake_minimum_required version to 3.10 2025-03-05 12:27:23 +11:00
Geoffrey McRae
551298ed5b [doc] all: update copyright year 2025-03-05 12:24:38 +11:00
Mark Stosberg
41008add12 [client] linux: Install .desktop file and icon
SVGs have had a great support for Linux for about a decade, so only
install that for simplicity.
2025-03-05 11:12:42 +11:00
Jonathan Rubenstein
32d1b8063e [doc] requirements: Modify language of DMABUF for iGPUS 2025-03-05 11:11:54 +11:00
Jonathan Rubenstein
b0227a8ff8 [doc] Add hypervisor and framebuffer to words.txt
Used `python ./sort_words.py -s -a framebuffer -a hypervisor`
This also properly sorted the list
2025-03-05 11:11:54 +11:00
Jonathan Rubenstein
a1c713556d [doc] install_libvirt: Link to iGPUs should use DMABUF 2025-03-05 11:11:54 +11:00
Jonathan Rubenstein
a0cf34df73 [doc] requirements: Add section encouraging DMABUF use for iGPUs 2025-03-05 11:11:54 +11:00
Jonathan Rubenstein
fbb489b9b6 [github] pr-check: Automatically add review requesting changes when PR author is not found in AUTHORS 2025-03-05 11:02:45 +11:00
Chris Spencer
03ca20d3e4 [client] overlay/msg: fix race condition in render
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
If an overlay is closed with overlayMsg_close, the message can be freed
while it is still being used by msg_render, resulting in a segfault. Lock
the message list for the duration of msg_render to fix this.
2025-03-05 10:59:05 +11:00
Chris Spencer
7e9e38faa5 [client] overlay/status: don't return damage rect if nothing was drawn
The renderer contains an optimization to skip ImGui if there is nothing to
show, but the status overlay always returns a damage rect, even if it
didn't draw anything, so the optimization can never take effect.
2025-03-05 10:58:10 +11:00
Chris Spencer
0fd6f59bbb [client] egl: fix desktop render dimensions check
Code now matches the error message.
2025-03-05 10:57:20 +11:00
Justin Gatzen
661efd107e [host] app: fix app state race in lgmpTimer thread
While resuming from hibernation lgmpTimer may transition app.state to
APP_STATE_REINIT_LGMP. Sometimes the transition is lost if the app_main
processing loop also changes app.state simultaneously. This seems to
occur frequently with the hibernation use case.

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

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

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

this looks nicer when pasted into the XML editor and applied

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

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

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

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

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

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

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

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

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

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

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

Ref: https://github.com/NVIDIA/open-gpu-kernel-modules/discussions/243#discussioncomment-3283415
2023-03-09 09:20:01 +11:00
Geoffrey McRae
bbc9204bfe [client] egl: ensure the preprocessed files have a null terminator 2023-03-09 09:15:39 +11:00
Geoffrey McRae
894b8b0c22 [client] x11: generate synthetic EnterNotify event on cursor leave
If the cursor was grabbed the window the cursor moves over when it is
ungrabbed will recieve an EnterNotify event with the mode of
NotifyUngrab, unfortunatly some window manages such as i3 will ignore
this message and as such focus follows mouse will not function
correctly. This patch injects a normal EnterNotify to work around this
issue.
2023-03-05 16:42:47 +11:00
Geoffrey McRae
91b0cba145 [client] egl: switch from mediump to highp
This fixes a rounding issue on certain hardware (NVidia) which actually
implement mediump as half precision (16-bit) float. It's safe to assume
`highp` is available as if the GPU does not support it, then the shader
compiler will try to find a lower precision that is supported by the GPU
2023-03-05 14:10:21 +11:00
Geoffrey McRae
2f48798db9 [client] wayland: check for failure of wl_display_connect 2023-02-28 23:38:44 +11:00
Geoffrey McRae
254b370bbf [client] audio/pipewire: fix compilation under clang 2023-01-24 13:46:22 +11:00
Geoffrey McRae
86efc47505 [client] audio/pipewire: PW_KEY_TARGET_OBJECT only exists as of 0.3.44 2023-01-24 13:43:17 +11:00
Geoffrey McRae
b559d6b9bc [client] audio/pipewire: add options to set the default out/rec devices 2023-01-24 13:35:25 +11:00
Geoffrey McRae
48cd0c3789 [client] audio: add missing earlyInit call for audio devs 2023-01-24 13:35:18 +11:00
Geoffrey McRae
6f8e6f9a19 [client] x11: catch and print details of X11 errors with a backtrace 2023-01-05 17:33:01 +11:00
Geoffrey McRae
188f25c6bf [host] nvfbc: increase retry timeout to 1 second 2022-12-09 08:00:03 +11:00
Geoffrey McRae
8619f787b9 [host] nvfbc: retry on failure to init
@quantum has observed nvfbc under rare circumstances fail to initialize,
this adds a retry to the init with a short delay to hopefully recover
from this situation.
2022-12-08 21:24:11 +11:00
Quantum
60ac03ebaf [client] wayland: implement window size setting for xdg-shell
This should allow win:autoResize to work on Wayland when the compositor
supports such an operation.
2022-12-08 21:08:33 +11:00
Quantum
e1ebde3cd2 [host] windows: log to stderr that logs will continue in file
This prevents users from posting the stderr as if it's the only output.
2022-12-08 21:08:07 +11:00
Quantum
f519904c38 [host] app: clarify that config file not found is not fatal
This prevents users from thinking this is the problem they are facing.
2022-12-08 21:08:07 +11:00
Geoffrey McRae
fa6f1abaac [obs] fix compatibility with updated LGMP build 2022-11-08 00:00:39 +11:00
Geoffrey McRae
875242fe15 [host] app: improve throttleFPS logic 2022-11-07 22:56:20 +11:00
Geoffrey McRae
20b5957999 [client] update LGMP to fix buffer overflow bug 2022-11-07 21:57:10 +11:00
Geoffrey McRae
f0cb9d1167 [client] main: fix spice display fallback when waiting for LGMP upgrade 2022-11-07 19:16:52 +11:00
Geoffrey McRae
6cd88a70ad [host/client] lgmp: update to fix possible race issue 2022-11-07 14:44:26 +11:00
Geoffrey McRae
697bbcd6d4 [host] app: correct timer restart on lgmp corruption recovery 2022-11-07 13:36:57 +11:00
Geoffrey McRae
ecca5720a9 [host] app: ensure that rand will be random 2022-11-07 13:26:52 +11:00
Geoffrey McRae
50e856f823 [host] lgmp: update LGMP again to fix bug in last update 2022-11-07 13:20:24 +11:00
Geoffrey McRae
6359bb9acd [host] lgmp: update to fix failure to randomize the session ID 2022-11-07 13:12:51 +11:00
Geoffrey McRae
938011fce6 [module] swap offset & size in printk output for consistency 2022-11-07 13:12:51 +11:00
Geoffrey McRae
d09a10299e [module] cosmetics 2022-11-07 13:12:51 +11:00
Geoffrey McRae
8e706636d3 [host] app: don't stop the lgmpTimer on session recovery 2022-11-07 12:20:25 +11:00
Ali Abdel-Qader
352cd2fafe [client] remove non-prototype function declarations
With -Wstrict-prototypes on non-protyped functions are deprecated and
functions must include a void parameter if they do not take parameters.
2022-11-01 08:03:15 +11:00
Chris Spencer
081a0a419d [client] audio: use actual device period if larger than expected maximum
This is rare and I'm not sure what causes it, but PipeWire sometimes uses a
larger period size than requested for no obvious reason (e.g., we could
request a period size of 512, but PipeWire uses 2048 anyway). This causes
us to stay in a permanent state of underrunning because the target latency
is too low.

With this change, we use the actual device period in the target latency
calculation if it is larger than the expected maximum. We may still get
some glitches at the beginning of playback (because the startup latency is
based upon the expected maximum period size), but it will recover after a
few seconds as it adjusts to the new target latency.
2022-11-01 08:02:37 +11:00
esi
7e42e6cdce [obs] Fix function call causing crash on lgUpdate 2022-11-01 08:01:54 +11:00
Quantum
d857b2a36e [cmake] CheckSubmodule: check for nanosvg 2022-11-01 08:01:08 +11:00
Quantum
ba64a2d400 [repos] nanosvg: convert to submodule
This is fine now that nanosvg can be compiled with -Wstrict-prototypes
without warning.
2022-11-01 08:01:08 +11:00
Geoffrey McRae
9d8bc46812 [client] keybind: fix typo 2022-11-01 08:00:37 +11:00
Geoffrey McRae
5b6095ad05 [repo] update PureSpice to fix cursor visibillity issue 2022-09-21 12:17:20 +10:00
Geoffrey McRae
2ba23e8646 [doc] install: fix forumla again 2022-09-20 07:13:27 +10:00
Geoffrey McRae
5fd9bc6a84 [doc] install: remove syntax highliting from ivshmem formula 2022-09-19 22:18:25 +10:00
Geoffrey McRae
7446fe8c99 [doc] install: cleanup ivshmem forumla formatting 2022-09-19 22:15:40 +10:00
Geoffrey McRae
136410daac [doc] install: be a bit clearer about calculating the ivshmem size 2022-09-19 22:11:17 +10:00
Quantum
646a725c0e [client] spice: wait 1 second before attempting to use spice video
This prevents the flash of spice display when it's not set to mirror
in the VM.
2022-09-19 14:59:59 +10:00
Quantum
b6f994b511 [doc] css: add custom style for <kbd> elements
This makes them look like actual keys.
2022-09-19 14:59:40 +10:00
Quantum
9b0af43a42 [doc] usage: improve ini syntax documentation
This uses syntax highlighting for the config file and also documents
the ; comment character.
2022-09-19 14:59:19 +10:00
Quantum
0f1eb0fa7f [doc] usage: fix command name bolding
Note that **``looking-glass-client``** is not valid syntax.
2022-09-19 14:59:01 +10:00
Quantum
bf8f4c17f0 [client] opengl: render cursor in spice mode 2022-09-19 14:58:47 +10:00
Quantum
d9ffb98c9b [client] wayland: fix attempt to confine when pointer is locked
This fixes the following crash when attempt to toggle capture at the
right after closing the overlay:

    zwp_pointer_constraints_v1@12: error 1: a pointer constraint with a wl_pointer of the same wl_seat is already on this surface
2022-09-19 14:58:27 +10:00
Quantum
0f261abb07 [client] egl: recalculate mouse position when toggling spice
Without this step, the cursor with be transformed for rendering the
spice cursor, not the IVSHMEM cursor.
2022-09-19 11:43:25 +10:00
Quantum
1dacf7b2de [repos] update PureSpice to fix none cursor handling 2022-09-19 11:24:42 +10:00
Tudor Brindus
6a9075b412 [doc]: a bunch of consistency fixes 2022-09-19 11:14:09 +10:00
Quantum
0c63a901be [client] main: add support for spice cursor channel 2022-09-19 11:13:53 +10:00
Quantum
1fd00ba26c [client] render_queue: support cursor operations 2022-09-19 11:13:53 +10:00
Quantum
f14d135266 [client] egl: support rendering cursors in spice display mode 2022-09-19 11:13:53 +10:00
Quantum
ce342029d8 [repos] update PureSpice to include cursor channel 2022-09-19 11:13:53 +10:00
Tudor Brindus
6d19e85fa4 [doc]: use arrow characters 2022-09-19 10:30:08 +10:00
Tudor Brindus
2780f6c22f [doc]: make title casing consistent 2022-09-19 10:30:08 +10:00
Tudor Brindus
33bcf19164 [doc]: usage.rst: update supported configuration options 2022-09-19 10:30:08 +10:00
Tudor Brindus
86793a657e [doc]: usage.rst:: add new keybindings 2022-09-19 10:30:08 +10:00
Geoffrey McRae
0ee45d8a70 [doc] add common ivshmem sizes and additional warnings/notes 2022-09-19 09:59:26 +10:00
Geoffrey McRae
3b16fb1baa [doc] fix typo 2022-09-18 20:43:29 +10:00
Geoffrey McRae
57c2e47cbf [doc] add comment to state that NvFBC reads NVFBC_PRIV_DATA if set 2022-09-18 20:42:46 +10:00
Geoffrey McRae
a5e6065d39 [doc] added host usage documentation for DXGI and NvFBC 2022-09-18 20:27:03 +10:00
Geoffrey McRae
b10d912ab6 [doc] update copyright year 2022-09-18 19:28:19 +10:00
Geoffrey McRae
9a26519e9b [doc] added spencercw to the words list 2022-09-18 18:54:55 +10:00
Geoffrey McRae
5a02e600c3 [doc] added spencercw to the LG docs credits 2022-09-18 18:54:08 +10:00
Geoffrey McRae
6cbbf4a2a0 [resources] adjusted spacing on updated icon 2022-09-18 18:25:28 +10:00
Geoffrey McRae
f42d1e0888 [resources] updated icons to the correct glyph 2022-09-18 18:18:10 +10:00
Geoffrey McRae
def838b883 [doc] update FAQ regarding Audio support 2022-09-18 14:33:17 +10:00
Geoffrey McRae
7e032f67be [doc] reorder and update installation section 2022-09-18 14:17:17 +10:00
Geoffrey McRae
7275f249c9 [doc] fix another typo 2022-09-18 12:22:06 +10:00
Geoffrey McRae
de5030f564 [doc] fix my inabillity to spell :) 2022-09-18 12:21:11 +10:00
Geoffrey McRae
f489006531 [doc] reorder install to before usage and change information re IVSHMEM 2022-09-18 12:17:05 +10:00
Geoffrey McRae
571a5da50f [doc] add requirements page 2022-09-18 12:12:49 +10:00
Geoffrey McRae
5385853f3f [client] x11: set _NET_WM_PID
Fixes #1013
2022-09-18 10:10:13 +10:00
Geoffrey McRae
0149549251 [client] egl: fix usage `destRect, 0x0 is top left
This fixes the letterbox clip being incorrect when rounding is occuring
due to the inverted coordinate space.
2022-09-17 16:02:11 +10:00
Quantum
f5e68711d0 [client] wayland: handle wl_data_source.target action
There is no action to be taken by the Looking Glass client, but a
handler needs to exist as certain other Wayland clients chooses to
send this message for copy-paste operations despite the fact it's
supposed to be used for drag-and-drop negotiation.
2022-08-14 12:05:51 +10:00
esi
28cba2e2b3 [obs] Allow the client to auto-recover
Previously, if the client's subscription to the frame buffer became
invalid for any reason, the video feed in OBS would freeze until the
user goes in and changes any of the settings.  This commit allows the
plugin to automatically attempt to recover.
2022-08-09 15:17:58 +10:00
Geoffrey McRae
d376dc4b5a [client] egl: fix the incorrect mask for dmabuf plane0 modifier 2022-07-30 22:08:47 +10:00
Geoffrey McRae
af51ea6d0b [client] egl: add support for EGL_EXT_image_dma_buf_import_modifiers 2022-07-30 15:55:20 +10:00
Quantum
eb1774f955 [client] keybind: add ScrollLock+C to cycle microphone defaults
This makes it possible to change the default action taken the next time
an application tries to open the microphone without restarting the
client.
2022-07-13 07:06:04 +10:00
Quantum
ae38db4915 [client] main: move micDefaultState into g_state 2022-07-13 07:06:04 +10:00
Quantum
9dee9ed7bb [client] main: better error when no display server is available
This commit makes it show a prettier error message when no display
server is available, including the display servers compiled. This
replaces the old assert.

Example output:

[E] 572277145932              main.c:1167 | lg_run                         | No display servers available, tried:
[E] 572277145934              main.c:1169 | lg_run                         | * Wayland
[E] 572277145935              main.c:1169 | lg_run                         | * X11
2022-07-13 07:05:07 +10:00
Quantum
0dabfdc432 [client] displayservers: add name field 2022-07-13 07:05:07 +10:00
Geoffrey McRae
97c5b8c3a7 [host] fix building under void linux
Closes #1012
2022-07-13 07:02:17 +10:00
Geoffrey McRae
e98913f182 [all] add 0xdc to AUTHORS 2022-07-01 08:37:28 +10:00
Daniel Cordero
4f3682fece Add a setting that hides the mouse cursor
Give OBS plugin users the option of hiding or showing the mouse cursor, since
Looking Glass renders it client-side.
2022-07-01 08:34:19 +10:00
Geoffrey McRae
9a9f9d433e [client] x11: add dependency on xkbcommon 2022-06-30 15:09:24 +10:00
Quantum
d24459b27f [client] wayland: correctly convert scan codes to numbers
Note that xkb scan code = linux scan code + 8.
2022-06-30 08:38:29 +10:00
Geoffrey McRae
da04a6dd54 [client] all: use the defined keyboard mapping for keybinds
Fixes #1007
2022-06-29 18:26:40 +10:00
Kenny.ch
ed0cae84c8 [module] Bump kvmfr module to 0.0.8 after latest fix for Kernel 5.18 2022-06-20 19:12:24 +10:00
Jonathan Rubenstein
681106e5c3 [github] Add initial CODEOWNERS file for docs/
Jonathan Rubenstein added as owner of docs/ directory
2022-06-20 19:11:58 +10:00
Jonathan Rubenstein
8a45a74fb1 [common] appstrings: Amend blurb for jjrcop with fluff
Very cheeky but is self-authored :D
2022-06-20 19:09:16 +10:00
Quantum
8b68a96ee1 [client] wayland: fix infinite resize loop
The intention has been to keep wlWm.needsResize true when skipping
resize, but I accidentally negated the value.
2022-06-01 22:41:20 -04:00
Leo1998
cb29de80f9 update AUTHORS 2022-06-01 06:47:10 +10:00
Leo1998
d20e319ccf [module] fix compile on 5.18 2022-06-01 06:47:10 +10:00
Geoffrey McRae
286e7622b8 [client] main: fix failure to startup when the guest VM has no UUID 2022-05-30 13:43:51 +10:00
Quantum
20d459d113 [client] wayland: deal getting scale before size
Certain window managers give us a scale before it gives us a size.
This commit makes the Wayland backend avoid passing a zero size to
wp_viewport_set_source, which is a protocol error.
2022-05-29 14:52:58 +10:00
Quantum
20b2130596 [ci] switch to checkout v2
With checkout v1, it checks out all the repository history, which takes
~20 seconds to complete with all the submodules that we have. With v2,
takes <10 seconds to complete the checkout by virtue of checking out
only the latest commit.

For some cursed reason, spell check starts failing if docs uses v2
checkout, but doesn't fail if it uses v1.
2022-05-29 11:16:43 +10:00
Quantum
bf9023d6f8 [client] cmake: pass -Wstrict-prototypes for C files only
This avoids the following warning in C++ code:

    cc1plus: warning: command-line option ‘-Wstrict-prototypes’ is
    valid for C/ObjC but not for C++
2022-05-29 11:16:16 +10:00
Quantum
a8521b821e [all] cmake: standardize indentation to 2 spaces 2022-05-29 11:16:16 +10:00
Geoffrey McRae
0799910e70 [client] app: fix segfault when jitRender + spice display is in use 2022-05-28 00:52:03 +10:00
Quantum
8b8b580f63 [client] overlay/status: re-rasterize at high DPI when needed
When the window scale goes above the scale the SVGs were rasterized
at, we re-rasterize them at the necessary scale for a more crisp
appearance.
2022-05-27 18:19:18 +10:00
Quantum
712b1cbc46 [client] overlay/graphs: allow unregistering after shutdown
The Wayland display server has a graph for presentation times, but the
backend shuts down after the overlays, so calling
overlayGraph_unregister causes memory corruption. We can avoid this
problem by making unregister a noop after all graphs have been freed.

This is safe because updating the graph doesn't use the graph handle:
instead you update the ringbuffer which is owned by the user.
2022-05-27 18:18:15 +10:00
Geoffrey McRae
fba7c80b2f [client] interface: remove needsRender from renderers 2022-05-27 14:38:42 +10:00
Geoffrey McRae
a4f5bc7320 [client] render_queue: fix failure to show spice at startup 2022-05-27 14:02:00 +10:00
Geoffrey McRae
48735cd001 [client] main: don't start the overlay tick timer until init is done 2022-05-27 13:42:47 +10:00
Geoffrey McRae
123be552a4 [client] spice: fix duplicate keybind registration 2022-05-27 13:29:28 +10:00
Geoffrey McRae
aba8c5b499 [client] splash: added LG url, version and copyright strings 2022-05-27 12:39:08 +10:00
Geoffrey McRae
56ec98524c [client] egl: perform full damage if a post processing option is changed 2022-05-27 11:40:20 +10:00
Geoffrey McRae
9ccd93bfd8 [client] app: add option to disable dimming in overlay mode 2022-05-27 11:36:39 +10:00
Geoffrey McRae
f17dfdc9b3 [client] config: cosmetics 2022-05-27 11:30:24 +10:00
Geoffrey McRae
39c1f99446 [client] splash: add back support for win:quickSplash 2022-05-27 11:27:50 +10:00
Quantum
7e8849180d [client] audio: allow microphone recording to be toggled after dialog 2022-05-27 11:22:49 +10:00
Quantum
146d9a2a53 [client] main: remove micAlwaysAllow from g_params 2022-05-27 11:22:12 +10:00
Quantum
7cb6ccd6f5 [client] audio: switch to use config value audio:micDefault 2022-05-27 11:22:12 +10:00
Quantum
9b910eced1 [client] config: replace audio:micAlwaysAllow with audio:micDefault 2022-05-27 11:22:12 +10:00
Quantum
3e079e0489 [client] main: add micDefaultState to state and params 2022-05-27 11:22:12 +10:00
Geoffrey McRae
1e660fb7e1 [client] splash: pre-calculate & cache the radial gradient vectors 2022-05-27 10:23:10 +10:00
Geoffrey McRae
1770defea2 [client] spice: wait for the spice connection to finish at startup
A failure to connect to spice would cause LG to exit late, this adds a
startup condition that prevents the LG initialization to complete until
the spice connection has been established.
2022-05-27 09:22:10 +10:00
Geoffrey McRae
b94869249c [client] main: don't show the splash when transitioning to spice 2022-05-27 02:24:01 +10:00
Geoffrey McRae
a9e3ab9d18 [client] egl: remove no longer used drawing functions 2022-05-27 02:13:07 +10:00
Geoffrey McRae
fec45dfe9c [client] egl: remove unused splash shaders 2022-05-27 02:10:38 +10:00
Geoffrey McRae
5de175c1f3 [client] all: unify the LG splash screen into an overlay 2022-05-27 02:07:20 +10:00
Geoffrey McRae
8974ae4fb5 [client] add SVG loading support and use icons for status display
This brings nanosvg into the project for SVG loading and rendering.
Unfortunatly we can not at this time use a submodule for this project
until https://github.com/memononen/nanosvg/pull/214 is merged.
2022-05-26 04:11:31 +10:00
Geoffrey McRae
8aa36144dc overlay: move init/free to the context of the render thread
This is done to allow overlays to make use of the renderer during
init/free.
2022-05-26 00:46:13 +10:00
Geoffrey McRae
c737b12a3b [client] update PureSpice submodule for uncompressed video support 2022-05-25 05:15:21 +10:00
Geoffrey McRae
6384a8d006 [client] main: fix race condition on usage of events 2022-05-24 11:06:47 +10:00
Geoffrey McRae
d1e421f8a8 [client] spice: delay showing the spice display until spice is ready 2022-05-24 09:48:54 +10:00
Geoffrey McRae
c0da28247d [client] spice: update PureSpice to fix display disconnection issue 2022-05-24 07:24:20 +10:00
Geoffrey McRae
6cbfa6e734 [client] egl: add spice display support 2022-05-24 06:57:33 +10:00
Geoffrey McRae
faae785c44 [client] spice: initialize new surfaces to black 2022-05-24 00:17:08 +10:00
Geoffrey McRae
b2221b114e [client] spice: more fixes to video source transition 2022-05-24 00:05:22 +10:00
Geoffrey McRae
47b2a26898 [client] app: better handling of transition to/from spice display 2022-05-23 22:35:27 +10:00
Geoffrey McRae
b5dfbcb5a2 [client] egl: added missing case from texture init 2022-05-23 20:55:02 +10:00
Geoffrey McRae
247e867f18 [client] egl: implemented SPICE display support 2022-05-22 18:19:58 +10:00
Geoffrey McRae
6699018ed1 [client] egl: check for null before free 2022-05-22 12:05:02 +10:00
Geoffrey McRae
947db38bc9 [client] egl: fix mistake in rect clamping 2022-05-22 11:53:46 +10:00
Geoffrey McRae
16f39450b5 [client] spice: added initial framework for spice display fallback 2022-05-22 11:45:11 +10:00
Geoffrey McRae
ffd27ac82c [client] update PureSpice submodule 2022-05-22 11:14:48 +10:00
Geoffrey McRae
1fcdcc8725 [client] egl: allow for partial texture updates 2022-05-21 21:21:16 +10:00
Geoffrey McRae
7ad3610276 [client] spice: update submodule to apply new socket handling changes 2022-05-20 02:10:17 +10:00
Geoffrey McRae
a41ab81a90 [host] nvfbc: try all NVIDIA adapters in the system
This will allow LG to start if the VM still has a virtual device as
the primary output.
2022-05-18 16:27:17 +10:00
Geoffrey McRae
0f8c0b5fb3 [host] dxgi: fix incorrect skip logic 2022-05-18 15:59:31 +10:00
Geoffrey McRae
57e20007db [host] dxgi: don't try to use devices without d3d support
This change should allow LG to work even if a virtual device is still
attached to the VM even though it might be capturing the wrong display.
2022-05-18 15:38:42 +10:00
Geoffrey McRae
2901e7aec5 [client] egl: only upload the damage rects if they actually changed
Profiling shows that a considerable amount of time is spent in
glBindBuffer and glBufferSubData when the damage rects are updated.
Since the amount of data here is quite small it's far faster to check if
it's different then to just blindly overwrite the buffer on each call.

Profiled on an Intel CPU with UHD P630 Graphics using magic-trace
2022-05-17 00:51:13 +10:00
Geoffrey McRae
6b9fa2b628 [host] capture: fix compilation with NvFBC C++ sources
The prior commit to expose the FrameBuffer internals makes use of an
atomic from `stdatomic.h`. Unfortunatly C++ has no notion of _Atomic and
as such `stdatomic.h` is not compatible. To work around this we instead
just forward declare the type here.
2022-05-16 22:09:11 +10:00
matthewjmc
53c843d9dd [common] Update framebuffer metadata + references 2022-05-16 20:01:09 +10:00
matthewjmc
78d2b76313 Update AUTHORS 2022-05-16 20:01:09 +10:00
Geoffrey McRae
d7704b13c0 [client] x11: set window posision before entering fullscreen
This fixes an issue where the window position would be ignored if the
application was launched in full screen mode from the command line
causing the client to enter full screen on the wrong monitor in
multi-monitor configurations.
2022-05-15 20:41:06 +10:00
Geoffrey McRae
76d2c69b46 [doc] allow monero (XMR) crypto addresses 2022-05-15 17:23:47 +10:00
Geoffrey McRae
febc2ec980 [doc] add XMR to the list of valid words 2022-05-15 17:20:53 +10:00
Geoffrey McRae
81aa24d4d3 [client] overlay/config: general UX changes
* Moved the LG license and version onto a seperate tab.
* Added general donation section and link to the website donation page
* Removed donation details under gnif's section
2022-05-15 17:16:07 +10:00
Geoffrey McRae
d8f2125543 [common/doc] add monero (XMR) donation address 2022-05-15 16:47:57 +10:00
Jonathan Rubenstein
aec2c78bd2 [doc] usage: Update Full Command Line Options 2022-05-15 16:42:27 +10:00
Jonathan Rubenstein
5797fbb4e5 [doc] usage: Move Command Line Options to the top
Also reformats common parameters, and lists them at the top man page
style
2022-05-15 16:42:27 +10:00
Jonathan Rubenstein
836e7ab998 [doc] build: Change link text leading to client_install 2022-05-15 16:42:27 +10:00
Jonathan Rubenstein
0759a7664b [doc] build: Change Client Building notes to full cmake commands
This may help novices or people who have trouble constructing the commands
themselves
2022-05-15 16:42:27 +10:00
Jonathan Rubenstein
0db17f803b [doc] build: Adjust flow of Client Building section 2022-05-15 16:42:27 +10:00
Jonathan Rubenstein
4d57671bf1 [doc] usage: Move client install instructions to install.rst 2022-05-15 16:42:27 +10:00
Jonathan Rubenstein
30780ce445 [doc] Create usage.rst from install.rst
No edits
2022-05-15 16:42:27 +10:00
Geoffrey McRae
3b55ac5420 [client] x11: check for null data from XGetWindowProperty
This fixes a reported segfault when a window manager fails to provide
valid EWMH values.

Fixes #987
2022-05-15 16:38:17 +10:00
Geoffrey McRae
32fbcaffd2 [client] spice: fix spice shutdown race
Fixes #960
2022-05-15 16:28:37 +10:00
Geoffrey McRae
0a9a9ed57e [client] config: enhance input:escapeKey to accept a KEY_* string value
This makes it possible to define the escape key by name rather then just
it's integer code, while still allowing fallback to using an integer
value for codes that may not be defined.

Example: `input:escapeKey=KEY_F1`

An invalid string value will also print a list of all valid string
values.
2022-05-15 16:11:33 +10:00
Geoffrey McRae
0a768a5a7f [client] main: add new option for integer only upscaling
The new option `win:intUpscale` will limit upscaling to integer sizes
only if enabled.
2022-05-09 18:23:53 +10:00
Geoffrey McRae
6afd262a27 [client] egl: update AMD FSR to 1.0.2 2022-05-04 15:29:21 +10:00
Geoffrey McRae
87077dfe6e [host] nvfbc: add downscale support 2022-05-04 13:38:49 +10:00
Geoffrey McRae
7ed18e24e2 [host] linux/pw: add missing include for min define 2022-05-04 12:28:20 +10:00
Geoffrey McRae
60834a5719 [host] xcb/pipewire/nfbc: use min and correct inverted logic 2022-05-04 12:23:20 +10:00
Geoffrey McRae
d5e2689d64 [host] nvfbc: fix inverted truncated frame logic 2022-05-04 11:58:04 +10:00
Geoffrey McRae
7b7a06b63f [client] fix invalid bitwise comparison 2022-05-04 11:02:02 +10:00
Geoffrey McRae
81f91caf0e [host] pw: fix build due to frame info structure changes 2022-05-04 10:59:44 +10:00
Geoffrey McRae
3d727a2254 [host] xcb: fix build due to frame info structure changes 2022-05-04 10:58:19 +10:00
Geoffrey McRae
e4a4e2331a [host] nvfbc: fix build due to frame info structure changes 2022-05-04 10:37:52 +10:00
Geoffrey McRae
8682ec207e [host] dxgi: check for invalid usage of d3d12 & downsampling 2022-05-04 10:23:42 +10:00
Geoffrey McRae
c8a5293645 [host] dxgi: refactor dxgi_copyFrame 2022-05-04 09:27:52 +10:00
Geoffrey McRae
15334c89d6 [host] dxgi: match the last entry in the downsample rule list 2022-05-03 17:58:41 +10:00
Geoffrey McRae
2eec459b47 [host] dxgi: finish downsample support for d3d11 backend 2022-05-03 12:15:24 +10:00
Geoffrey McRae
947325e00d [host] dxgi: fix failure to copy to texture with sub resources
`CopyResource` will silently fail when trying to copy from a texture
with no subresources to one with. Instead we must use `ResolveSubresource`
2022-05-02 15:53:20 +10:00
Geoffrey McRae
eae559b4c9 [client/obs] update to support downscaled frames coming from the host 2022-05-01 19:51:25 +10:00
Geoffrey McRae
3134ec84de [host] dxgi: add support for downsampling the capture before sending
This is an experimental & incomplete feature for those using
supersampling. Anything > 1200p will be downsampled by 50% before
copying out of the GPU to save on memory bandwidth.

Unfinished! Has issues with damage tracking and currently can not
be configured. Only dx11 has been tested at this point, everything
else will likely have problems/crash.
2022-05-01 19:45:44 +10:00
Netboy3
132d0e3c42 [client] audio/pw: pw_stream_get_time() deprecated
pw_stream_get_time() is deprecated in PipeWire 0.3.50.
Use pw_stream_get_time_n() instead based on PipeWire
library version.
2022-04-14 01:47:34 +10:00
Babbaj
4bbdd30284 [all] Add Babbaj to AUTHORS 2022-04-09 16:19:11 +10:00
Babbaj
6d06320fb2 [client] audio/pw: fix muting
Pipewire documents the mute parameter as a bool, however `pw_stream_set_control` expects a float value and converts it to a bool.

6ad6300ec6/src/pipewire/stream.c (L2063)
2022-04-09 16:19:11 +10:00
Quantum
f3fe774f69 [client] overlay/record: do not invalidate window during shutdown 2022-03-19 18:52:07 +11:00
Quantum
e053c014f7 [client] audio: display record indicator when necessary 2022-03-19 18:52:07 +11:00
Quantum
9c8a8a1b44 [client] config: add new option audio:micShowIndicator
This will be used to control the display of the microphone recording
indicator.
2022-03-19 18:52:07 +11:00
Quantum
1685249f3a [client] overlay: add record indicator 2022-03-19 18:52:07 +11:00
Quantum
97cef000fd [client] audio: avoid prompting when changing record format
If a recording is already in progress, we should not prompt again.
2022-03-19 15:10:39 +11:00
Quantum
8f45290beb [client] audio: cancel confirm dialog when a new recording starts 2022-03-19 10:04:23 +11:00
Quantum
9afe170413 [client] audio: prompt before allowing audio
If the user clicks no, the guest only receives silence.
2022-03-19 10:04:23 +11:00
Quantum
dd6d9c44df [client] config: add new audio:micAlwaysAllow option
This will be used to always grant access to microphones instead of
prompting every time.
2022-03-19 10:04:23 +11:00
Quantum
fb5a71c47e [client] audiodevs/pipewire: implement proper recording stop/restart
Before, pipewire_recordStop did nothing since pw.record.active was
always false.
2022-03-19 10:04:23 +11:00
Quantum
75370e464d [client] overlay/msg: fix type for app_msgBoxClose
It should not be taking a pointer to MsgBoxHandle.

Also changed the type of MsgBoxHandle to prevent similar bugs.
2022-03-19 10:04:23 +11:00
Quantum
c55d0a82f2 [client] overlay: add support for confirmation dialogs 2022-03-19 10:04:23 +11:00
Quantum
f28084e653 [client] core: remove state tracking in core_updateOverlayState
The state is never updated when a message box is dismissed, so the
cursor is never displayed when a second message box shows up.

The only other caller, app_setOverlay, has state tracking already.
2022-03-19 10:04:23 +11:00
vmfortress
6c76d6ada5 [host] xcb: fixed early start of pointer thread by adding xcb_start 2022-03-08 06:12:17 +11:00
Geoffrey McRae
3a8cb6a613 [client/common] fixes for issues detected through static analysis. 2022-03-07 10:14:52 +11:00
Tudor Brindus
a3820536ab [client] overlay: make "Show timing graphs" checkbox consistent in case 2022-03-06 17:21:32 +11:00
vmfortress
36f97f08ad [host] dxgi: add nanosecond-scale sleep capability to d3d12
The nsleep() call lets d3d12 sleep for a more precise amount of
time while maintaining the current millisecond-scale sleep
interface in the configuration file.
2022-03-06 17:21:14 +11:00
Shootfast
48cf099638 [client] input: fix confine_pointer argument marshalling
Under Wayland, if the mouse pointer is disconnected whilst captured
(like say via KVM switch), the waylandWarpPointer code will be called
but the pointer will be NULL. This results in the cryptic message:

error marshalling arguments for confine_pointer (signature noo?ou): null value passed for arg 2
Error marshalling request: Invalid argument

This patch adds a check on the wlWm.pointer pointer before attempting
to warp the pointer, and avoids the crash.
2022-03-05 09:33:45 +11:00
Tudor Brindus
88d60d4b3d [client] enforce building with -Wstrict-prototypes 2022-02-28 11:56:26 +11:00
Tudor Brindus
3189c7bcd6 [client] kb: update for ImGui 1.87 2022-02-28 11:56:26 +11:00
Tudor Brindus
d1da0d62ed [client] update cimgui submodule 2022-02-28 11:56:26 +11:00
Chris Spencer
72033f3822 [client] audio: reduce hardcoded minimum latency
The current minimum target latency is partially based upon the default qemu
behaviour whereby audio packets are delivered in a sawtooth pattern, with
packet timestamps drifting between 5ms above and below the measured clock.
This 5ms error is baked into the minimum target latency to avoid
underrunning.

This sawtooth pattern can be reduced by specifying a lower timer period in
the qemu configuration, so remove it from the hardcoded minimum latency and
add it to the default configurable buffer latency instead. This allows
users that have configured their VM appropriately to reduce the overall
latency.
2022-02-28 11:52:16 +11:00
Chris Spencer
c2523be4b4 [client] audio: reduce resampler latency
The best quality resampler has an intrinsic latency of about 3ms, and the
processing itself takes another 1-2ms per 10ms block. The faster setting
has an intrinsic latency of about 0.4ms, with about 0.04ms processing time.
This makes for an overall saving of about 4ms, with negligible loss in
quality.
2022-02-27 23:47:43 +11:00
Chris Spencer
7efc274e81 [client] audio: use block comments 2022-02-27 23:47:43 +11:00
Chris Spencer
7c2d493bb5 [client] audio: add latency tuning parameter
This adds a new `audio:bufferLatency` option which allows the user to
adjust the amount of buffering LG does over the absolute bare minimum. By
default, this is set large enough to absorb typical timing jitter from
Spice. Users may reduce this if they care more about latency than audio
quality.
2022-02-25 20:41:47 +11:00
Chris Spencer
9908b737b0 [client] audio: make the requested audio device period size configurable
This adds a new `audio:periodSize` option which defaults to 2048 frames.
For PipeWire, this controls the `PIPEWIRE_LATENCY` value. For PulseAudio,
the controls the target buffer length (`tlength`) value.
2022-02-25 20:41:47 +11:00
Chris Spencer
0dad9b1e76 [client] audio: fix latency calculation if audio device starts early
If the audio device starts earlier than required, we slew the read pointer
backwards to avoid underrunning. We need to apply this same offset to the
recorded device position, otherwise the Spice thread will think playback is
further ahead than it really is and inject unnecessary latency to
compensate.
2022-02-25 20:41:47 +11:00
Chris Spencer
a13c90bd27 [client] audio/pa: wait until stream is ready before starting playback
Uncorking the stream does not work if the stream is not ready yet.
2022-02-25 20:41:47 +11:00
Chris Spencer
84b5478b02 [client] audio/pa: fix assertion failure when keep alive playback stops
When the 'keep alive' playback times out, playback is stopped from the
audio callback, resulting in an assertion failure inside PulseAudio as we
try to lock the main loop thread while already inside it.
2022-02-25 20:41:47 +11:00
Tudor Brindus
38340d3497 [common] allow building with -Wstrict-prototypes 2022-02-25 20:38:44 +11:00
Tudor Brindus
eeefc15e46 [host] build with -Wstrict-prototypes 2022-02-25 20:38:44 +11:00
Tudor Brindus
91d6e3a82a [client] allow building with -Wstrict-prototypes
This is not yet turned on because cimgui does not build with it enabled.
2022-02-25 20:38:44 +11:00
Geoffrey McRae
7387a4a8e1 [all] update submodules 2022-02-14 15:14:36 +11:00
Chris Spencer
d9dc399522 [client] audio/pw: request real-time priority
This is as per the PipeWire ALSA plugin [1]. The existing
`PW_STREAM_FLAG_RT_PROCESS` flag is misleading and doesn't really have
anything to do with real-time priority; it just tells PipeWire to pull data
from the application synchronously from its main processing thread. More
detail at [2].

[1] f5d47c079e/pipewire-alsa/alsa-plugins/pcm_pipewire.c
[2] https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/2024
2022-02-14 15:11:43 +11:00
Chris Spencer
70158a64e7 [client] audio: open device earlier
The actual time between opening the device and the device starting to pull
data can range anywhere between nearly instant and hundreds of
milliseconds. To minimise startup latency, open the device as soon as the
first playback data is received from Spice. If the device starts earlier
than required, insert a period of silence at the beginning of playback to
avoid underrunning. If it starts later, just accept the higher latency and
let the adaptive resampling deal with it.
2022-02-14 15:09:13 +11:00
Chris Spencer
1ca43c4727 [common] appstrings: add spencercw 2022-02-14 15:09:13 +11:00
Quantum
fc96b6691e [host] linux: remove useless libX11 dependency 2022-02-14 15:08:29 +11:00
Quantum
5a94f82f10 [host] linux: remove useless GL dependency 2022-02-14 15:08:29 +11:00
Quantum
5b7c38a4dd [host] ci: install pipewire on Linux 2022-02-12 22:13:31 +11:00
Quantum
f01489720f [host] linux: build with PipeWire by default
Since the client already depends on PipeWire by default, there is no
reason why the host shouldn't.
2022-02-12 22:13:31 +11:00
Geoffrey McRae
202116786c [client] main: fix invalid bit logic 2022-02-10 20:42:25 +11:00
Geoffrey McRae
8b4551c39c [all] convert KVMFR frame bools to flags in a bitfield
This will allow us to add additional flags in the future while remaining
backwards compatible with the host.
2022-02-10 20:32:38 +11:00
Geoffrey McRae
29698362ed [client] x11: added request activation support 2022-02-10 20:20:34 +11:00
Geoffrey McRae
f24db8d0cd Revert "[client] x11: check if the EWMH atoms exist before using them"
This reverts commit cfd2e6ff32.
2022-02-10 13:36:40 +11:00
Geoffrey McRae
cfd2e6ff32 [client] x11: check if the EWMH atoms exist before using them 2022-02-10 13:35:19 +11:00
Chris Spencer
e96311eb7b [client] audio: keep audio device open after playback
We can set the startup latency for the next playback far more precisely if
we have the device open already.

Only keep the device open with no playback for 30 seconds to avoid keeping
the device open unnecessarily forever.
2022-02-10 07:50:01 +11:00
Chris Spencer
0d97a51802 [client] audio: increase startup latency
Underruns can still happen quite easily at the beginning of playback,
particularly at very low latency settings. Further increase the startup
latency to avoid this.
2022-02-10 07:50:01 +11:00
Chris Spencer
5e1b8f2abe [client] spice: update submodule to disable audio draining 2022-02-10 07:50:01 +11:00
Netboy3
e0c0451b52 [client] x11: Add inline icon
Many X11 window managers will present an application on their
taskbar as a combination of the application name and an icon
imagery pulled from the X-Property _NET_WM_ICON. Applications
built under frameworks such as Qt or GTK have this property
populated by the framework. This commit adds the Atom _NET_WM_ICON
and populates it with a 64x64 icon of Looking Glass.
2022-02-09 12:07:55 +11:00
Tudor Brindus
9ddfa585ec [host] cleanup some missing (void) parameters 2022-02-08 16:37:17 +11:00
Tudor Brindus
0ea188faf8 [client] ci: stop installing wayland-protocols from apt 2022-02-08 16:05:51 +11:00
Tudor Brindus
e1ac838796 [client] wayland: use wayland-protocols from submodule 2022-02-08 16:05:51 +11:00
Tudor Brindus
770a4279ee [repos] add wayland-protocol submodule 2022-02-08 16:05:51 +11:00
Geoffrey McRae
1cfbcba813 [client] main: fix failure to check KVMFR udata at connect 2022-02-08 15:50:22 +11:00
Paul Hollinsky
3890c72159 [client] egl: use texture sampler for desktop
The desktop doesn't need its own sampler, there is already an identically
configured one in the `desktop->texture`.

For some reason, using the texture sampler fixes a black screen issue
with my GTX 660 using the 470.86 driver. Maybe hitting some limit
for how many samplers can be allocated?
2022-02-08 15:34:54 +11:00
Paul Hollinsky
4223a5e38f [client] egl: remove extra sampler from TextureBuffer
It was unused, there is a sampler in EGL_Texture (base).
2022-02-08 15:34:54 +11:00
Tudor Brindus
809e1095bd [host] windows: plumb guest activation request to host 2022-02-08 15:27:27 +11:00
Tudor Brindus
fd28d0604e [host/client] kvmfr: request activation based on guest state 2022-02-08 15:27:27 +11:00
Tudor Brindus
30c57f411d [client] ci: install newer wayland-protocols repository
The Ubuntu one lacks `xdg-activation-v1`, despite having been released 6
months ago. Pull in a newer one from @quantum5's repository.
2022-02-08 14:54:55 +11:00
Tudor Brindus
9cd8027901 [client] main: request WM activation on first frame 2022-02-08 14:54:55 +11:00
Tudor Brindus
969ac4d1d1 [client] wayland: activate our window if we're allowed 2022-02-08 14:54:55 +11:00
Tudor Brindus
da548e3858 [client] ds: expose activation requesting 2022-02-08 14:54:55 +11:00
Tudor Brindus
21a349343b [client] wayland: implement activation request 2022-02-08 14:54:55 +11:00
Tudor Brindus
4ee6bdf198 [client] wayland: bind xdg_activation_v1 when available 2022-02-08 14:54:55 +11:00
Tudor Brindus
b13582a911 [client] wayland: build xdg-activation-v1 header 2022-02-08 14:54:55 +11:00
Chris Spencer
05ca59ed48 [client] audio/pw: increase startup latency
PipeWire startup latency varies wildly depending on what else is, or was
last using the audio device. In the worst case, PipeWire can request two
full buffers within a very short period of time immediately at the start of
playback, so make sure we've got enough data in the buffer to support this.
2022-02-04 16:27:12 +11:00
Chris Spencer
e1e60fdaa6 [client] audio: tune target latency
The target latency is now based upon the device maximum period size
(which may be configured by setting the `PIPEWIRE_LATENCY` environment
variable if using PipeWire), with some allowance for timing jitter from
Spice and the audio device.

PipeWire can change the period size dynamically at any time which must be
taken into account when selecting the target latency to avoid underruns
when the period size is increased. This is explained in detail within the
commit body.
2022-02-04 16:27:12 +11:00
Chris Spencer
ca29fe80a6 Revert "[client] audio: tune the target latency based on the latency jitter"
This reverts commit febd081202.

This causes severe underruns when the quantum size increases.
2022-02-04 16:27:12 +11:00
Geoffrey McRae
35bf30910b Revert "[common] option: fix bounds check in the argument parser."
This reverts commit db78c8e468.
I need some sleep, this was already fine as it was...
2022-01-30 20:25:02 +11:00
Geoffrey McRae
db78c8e468 [common] option: fix bounds check in the argument parser.
The recent `pwnkit` exploit brought this to my attention, not that we
are a setuid process we should still do this properly... who knows where
this code might get used in the future.
2022-01-30 19:56:06 +11:00
Geoffrey McRae
febd081202 [client] audio: tune the target latency based on the latency jitter 2022-01-28 12:11:56 +11:00
Geoffrey McRae
5bbc1d44bf [client] audio/pw: get the period size to determine when to start audio
Previously this was hardcoded to 100ms which is far too high in most
instances, instead we get the initial period size and use whichever is
greater out of 50ms or the period size.

The idea is to reduce the amount of time it takes for the latency to
come down after initial stream start.
2022-01-28 12:08:58 +11:00
Geoffrey McRae
22b968ff53 [client] audio: change the audio latency graph sample point
This removes the need for locking while also giving a better result in
the graph output. Also when the graph is disabled via the overlay
options it will no longer cause redraws.
2022-01-28 10:59:12 +11:00
Geoffrey McRae
a0477466d2 Revert "[client] audio: allow the audiodev to return the periodFrames"
This reverts commit 41884bfcc5.

PipeWire can change it's period size on the fly on us making this
approach invalid.
2022-01-28 10:00:35 +11:00
Geoffrey McRae
c2a766c2ee [client] audio: fix setfault due to failure to properly reset 2022-01-27 19:20:16 +11:00
Geoffrey McRae
4ff39616b2 [common] rb: correct invalid accesses of atomics 2022-01-27 18:20:03 +11:00
Geoffrey McRae
2201ed869e [github] add libsamplerate0-dev to the workflow 2022-01-27 18:05:24 +11:00
Geoffrey McRae
d0b3c09456 [doc] build: add missing audio dependencies 2022-01-27 18:03:11 +11:00
Geoffrey McRae
a560a610d9 [client] audio: allow building without any audio support 2022-01-27 18:03:11 +11:00
Geoffrey McRae
a7db3d3a0f [client] audio: check for malloc failure 2022-01-27 18:03:11 +11:00
Geoffrey McRae
016001da67 [client] audio: cosmetics 2022-01-27 18:03:11 +11:00
Geoffrey McRae
41884bfcc5 [client] audio: allow the audiodev to return the periodFrames
This change allows the audiodevs to return the minimum period frames
needed to start playback instead of having to rely on a pull to obtain
these details.

Additionally we are using this information to select an initial start
latency as well as to train the desired latency in order to keep it as
low as possible.
2022-01-27 18:03:11 +11:00
Chris Spencer
dd2d84a080 [client] audio: adjust playback speed to match audio device clock
This change is based on the techniques described in [1] and [2].

The input audio stream from Spice is not synchronised to the audio playback
device. While the input and output may be both nominally running at 48 kHz,
when compared against each other, they will differ by a tiny fraction of a
percent. Given enough time (typically on the order of a few hours), this
will result in the ring buffer becoming completely full or completely
empty. It will stay in this state permanently, periodically resulting in
glitches as the buffer repeatedly underruns or overruns.

To address this, adjust the speed of the received data to match the rate at
which it is being consumed by the audio device. This will result in a
slight pitch shift, but the changes should be small and smooth enough that
this is unnoticeable to the user.

The process works roughly as follows:
1. Every time audio data is received from Spice, or consumed by the audio
   device, sample the current time. These are fed into a pair of delay
   locked loops to produce smoothed approximations of the two clocks.
2. Compute the difference between the two clocks and compare this against
   the target latency to produce an error value. This error value will be
   quite stable during normal operation, but can change quite rapidly due
   to external factors, particularly at the start of playback. To smooth
   out any sudden changes in playback speed, which would be noticeable to
   the user, this value is also filtered through another delay locked loop.
3. Feed this error value into a PI controller to produce a ratio value.
   This is the target playback speed in order to bring the error value
   towards zero.
4. Resample the input audio using the computed ratio to apply the speed
   change. The output of the resampler is what is ultimately inserted into
   the ring buffer for consumption by the audio device.

Since this process targets a specific latency value, rather than simply
trying to rate match the input and output, it also has the effect of
'correcting' latency issues. If a high latency application (such as a media
player) is already running, the time between requesting the start of
playback and the audio device actually starting to consume samples can be
very high, easily in the hundreds of milliseconds. The changes here will
automatically adjust the playback speed over the course of a few minutes to
bring the latency back down to the target value.

[1] https://kokkinizita.linuxaudio.org/papers/adapt-resamp.pdf
[2] https://kokkinizita.linuxaudio.org/papers/usingdll.pdf
2022-01-27 18:03:11 +11:00
Chris Spencer
599fdd6ffd [common] ringbuffer: add unbounded mode
In unbounded mode, the read and write pointers are free to move
independently of one another. This is useful where the input and output
streams are progressing at the same rate on average, and we want to keep
the latency stable in the event than an underrun or overrun occurs.

If an underrun occurs (i.e., there is not enough data in the buffer to
satisfy a read request), the missing values with be filled with zeros. When
the writer catches up, the same number of values will be skipped from the
input.

If an overrun occurs (i.e., there is not enough free space in the buffer to
satisfy a write request), excess values will be discarded. When the reader
catches up, the same number of values will be zeroed in the output.

Unbounded mode is currently unused since our audio input and output
streams are not synchronised. This will be implemented in a later commit.

Also reimplemented as a lock-free queue which is safer for use in audio
device callbacks.
2022-01-27 18:03:11 +11:00
Chris Spencer
b34b253814 [client] audio: stop playback immediately if new playback is started
If a new playback is started while the previous playback is still flushing,
we simply allow the stream to continue playing and effectively cancel the
flush. In general this is not safe because there may not be enough data in
the buffer to avoid underrunning. We could handle this better later by
trying to insert the right number of silent samples into the buffer, but
for now just completely stop the previous stream before starting the new
one.
2022-01-27 18:03:11 +11:00
Chris Spencer
68b42e1c1a [client] audio/pw: drop restarting state
Automatically restarting playback once draining has completed could result
in playback starting too early (i.e., before there is enough data in the
ring buffer to avoid underrunning). `audio_playbackData` will keep invoking
`start` until it returns true anyway, so we can just allow draining to
complete normally and wait for `start` to be called again.
2022-01-27 18:03:11 +11:00
Chris Spencer
8580978321 [client] audio/pw: drop redundant flushing state
We do not stop the audio device until after the internal buffer has already
been flushed, so this state does nothing useful.
2022-01-27 18:03:11 +11:00
Quantum
d93510e9f2 [host] linux: allow getting system version
Result is something like:

    Debian GNU/Linux 11 (bullseye), kernel: Linux 5.14.0-0.bpo.2-amd64 on x86_64
2022-01-27 05:47:53 +11:00
Quantum
75ec3c0478 [host] nvfbc: shorten nvfbc_getName() result
To avoid client showing "Using    : NVFBC (NVidia Frame Buffer Capt".
This happens because the string is truncated to 31 characters to fit
in the char capture[32]; member of KVMFRRecord_VMInfo.
2022-01-26 23:06:47 +11:00
Quantum
e85fd68d82 [host] windows: read ProductName from registry if possible
For Windows 10, it so happens that the major.minor is 10.0. This is not
usually a given, e.g. on Windows 7 where it would read 6.1, on
Windows 8 it would read 6.2, and on Windows 8.1 it would read 6.3.

This is obviously undesirable, so we should just read the ProductName
from registry if possible. This results in something like:

    OS Name: Windows 10 Pro for Workstations (Build: 19043)
2022-01-26 23:06:14 +11:00
Quantum
f247d7f0da [host] app: don't read from UUID if none is returned
This allows the Linux host to start.
2022-01-26 23:05:31 +11:00
Geoffrey McRae
b0568ca404 [client] egl: use a sigmoid curve for nv 2022-01-26 23:03:35 +11:00
Geoffrey McRae
3c9b9e6370 [client] main: fix heap-buffer-overflow on cursor update 2022-01-26 20:05:51 +11:00
Geoffrey McRae
db3d20f935 [host] windows: report windows version information 2022-01-26 19:58:46 +11:00
Geoffrey McRae
ccdf7b7c0e [host] app: report the full capture name to the client 2022-01-26 19:32:55 +11:00
Geoffrey McRae
efa49391fc [client] fix race segfault on pointer queue unsubscribe/timeout 2022-01-26 17:20:12 +11:00
Geoffrey McRae
fb4bdaee2b [client] egl: set the active preset if specified at launch 2022-01-26 16:07:17 +11:00
Geoffrey McRae
c7389285f9 [client] egl: fix null pointer access when no preset is set 2022-01-26 16:04:15 +11:00
Geoffrey McRae
aa426d13a7 [client] egl: added egl:preset to load a default preset at startup 2022-01-26 16:00:07 +11:00
Geoffrey McRae
89c83dafc1 [client] egl: make egl less noisy unless debug is specified 2022-01-26 15:42:33 +11:00
Geoffrey McRae
05e363e009 [client] x11: cleanup duplicated code 2022-01-26 15:30:38 +11:00
Geoffrey McRae
e17b289759 [client] x11: sync the mouse on meta resize 2022-01-26 14:55:30 +11:00
Geoffrey McRae
79e986cc60 [client] x11: fix failure to unfocus when performing meta resize/move 2022-01-26 14:43:11 +11:00
Geoffrey McRae
22f3cf5ba6 [client] egl: fix masked color cursor blend operation for xor drawing 2022-01-26 12:23:05 +11:00
Geoffrey McRae
3067bdaa15 [client] egl: properly apply xor mask to masked color cursors 2022-01-26 12:11:43 +11:00
Geoffrey McRae
f3ebde7d9f [common] ivshmem: fix memory leak 2022-01-25 03:37:56 +11:00
Geoffrey McRae
905c1d7f58 [common] ivshmem: check for failure to find a device 2022-01-25 03:37:56 +11:00
Quantum
11800029f0 [client] core: redraw cursor after warping guest cursor 2022-01-24 17:07:15 +11:00
Quantum
71901414d1 [client] overlay: realign cursor when turning off overlay
This is only done in non-capture mode to avoid messing up games.
2022-01-24 17:07:15 +11:00
Geoffrey McRae
96fa8891c8 [client] egl: fixed incorrect drawing of masked color cursors 2022-01-24 06:56:32 +11:00
Geoffrey McRae
1082875b8e [client] opengl: fix startup since ImGui upgrade 2022-01-24 06:42:16 +11:00
Geoffrey McRae
dc918c55b6 [client] main: only copy the needed cursor data instead of everything 2022-01-24 06:36:15 +11:00
Geoffrey McRae
a8ba014b52 [client] main: lgmpClientMessageDone is not idempotent 2022-01-24 04:10:49 +11:00
Quantum
9a6aa3ce66 [client] egl: remove duplicate #include "app.h" 2022-01-23 08:49:15 +11:00
Quantum
f2fbb2b27c [client] opengl: make draw functions static 2022-01-23 08:49:01 +11:00
Geoffrey McRae
829db8a0e4 [client] spice: update the submodule to fix invalid header errors 2022-01-22 18:23:33 +11:00
Geoffrey McRae
9601bc677f [client] audio: report the buffered frames and not the buffer length 2022-01-22 16:06:16 +11:00
Jonathan Rubenstein
aba30e9541 [doc] build: Add source comment for listing deps 2022-01-20 17:49:35 +11:00
Geoffrey McRae
c84879717f [client] audio: fix failure to properly handle restart if draining 2022-01-20 07:02:00 +11:00
Geoffrey McRae
b3c81bcedf [client] audio: fix use after free race 2022-01-19 18:50:39 +11:00
Geoffrey McRae
7f4dcd1ced [client] spice: update PureSpice submodule to reduce audio latency 2022-01-19 18:32:22 +11:00
Geoffrey McRae
15f76339c8 [client] audio: move the memory copy into the pull function 2022-01-19 10:29:49 +11:00
Geoffrey McRae
99536eaf9d dxgi: check for memory leaks in d3d12 2022-01-19 09:58:14 +11:00
Geoffrey McRae
f8b4874799 dxgi: cosmetics 2022-01-19 09:58:14 +11:00
Geoffrey McRae
cff64ee7d3 dxgi: cache shared handles instead of re-creating them
This will cache up to 10 handles, in practice I have never seen DXGI
return anything but the same resource each time but we allow for more
anyway should MS change something in the future.

Should the cache get over filled it is disabled entirely and we revert
to the original behaviour.
2022-01-19 09:58:07 +11:00
Geoffrey McRae
04ae9217e8 [client] audio: allow the audiodev to determine the start fill level 2022-01-19 01:52:19 +11:00
Geoffrey McRae
46da447429 [client] audio: fix latency calculation 2022-01-19 00:58:48 +11:00
Geoffrey McRae
4b080f7610 [client] audio: don't lock when consuming frames from the buffer 2022-01-19 00:25:52 +11:00
Geoffrey McRae
d6bbc4f89c [client] audio/pw: return the actual playback latency 2022-01-19 00:03:16 +11:00
Geoffrey McRae
4fadf3a130 [client] audio: tell the audiodev to stop on the last packet, not after 2022-01-18 23:43:12 +11:00
Geoffrey McRae
73dc08e5f9 [client] audio: remove duplicated line 2022-01-18 23:42:02 +11:00
Geoffrey McRae
07c92ec2e8 [client] audio: drain buffers on stop instead of just discarding them 2022-01-18 23:39:05 +11:00
Geoffrey McRae
b334f22223 [client] audio: rework audiodevs to be pull model from a common buffer 2022-01-18 09:02:44 +11:00
Geoffrey McRae
aad65c1cab [client] graphs: overlay on removal of graph 2022-01-17 22:53:52 +11:00
Geoffrey McRae
0ad26b7da7 [client] audio: redraw the graphs if they have been updated 2022-01-17 22:49:19 +11:00
Geoffrey McRae
775ac7ce8b [client] audio: reduce timing graph sample count to 30 seconds 2022-01-17 22:23:37 +11:00
Geoffrey McRae
689cc53255 [client] audio: add audio playback latency interface and graph 2022-01-17 22:13:41 +11:00
Geoffrey McRae
5629655f74 [client] audio/pw: fix memory leak and gracefully shutdown 2022-01-17 22:10:41 +11:00
Geoffrey McRae
54e7542414 [client] overlay/graph: actually remove unregistered overlays
ll now supports removal of elements, so actually do it
2022-01-17 22:09:41 +11:00
Geoffrey McRae
464fee3e20 [client] overlay/graphs: allow the graph to have a custom title format 2022-01-17 22:08:56 +11:00
Geoffrey McRae
42ed0d7638 [client] app: allow key-repeat to work with keybinds 2022-01-17 20:33:57 +11:00
Geoffrey McRae
5a3fe151e4 [client] main: don't use Linux keybinds if the guest is not Linux 2022-01-17 20:26:45 +11:00
Quantum
afd5e2d057 [host] dxgi: remove duplicate AcquireLock print 2022-01-17 15:12:54 +11:00
Quantum
508c491967 [host] dxgi: allow the debug layer to be turned on via config 2022-01-17 15:12:54 +11:00
Quantum
b117bbafe5 [host] dxgi: add ability to capture OutputDebugMessage
This is how Direct3D 12's debug layer outputs messages. Normally, these
are read by the debugger, but we want them logged instead.
2022-01-17 15:12:54 +11:00
Quantum
5392f815af [vendor] directx: add d3d12sdklayers.h
This is an MIT-licensed header from Microsoft, which contains the
Direct3D 12 debug layer.

This header is slightly modified to be able to compile on older
MinGW versions.
2022-01-17 15:12:54 +11:00
Quantum
4c271f8744 [host] dxgi: move d3d12.h to vendor/directx 2022-01-17 15:12:54 +11:00
Jonathan Rubenstein
745169fae2 [client] Add capture mode support for media keys
Supports Play/Pause, Stop, Next, and Previous
2022-01-17 15:09:36 +11:00
Jonathan Rubenstein
7f79352320 [client] Add support for volume keys in capture mode
These include Volume Up, Volume Down, and Mute

Co-authored-by: Quantum <quantum2048@gmail.com>
2022-01-17 15:09:36 +11:00
Quantum
b020372972 [client] imgui: allow arrows to be displayed 2022-01-17 15:09:03 +11:00
Quantum
5fe529f213 [client] spice: allow volume control keys to be sent to the guest
These are implemented as ScrollLock+Up/Down for volume up and down, and
ScrollLock+M to toggle audio mute. These should prove useful especially
when Looking Glass now supports streaming audio, and the volume is
defined in the guest and set on the output stream.
2022-01-17 15:09:03 +11:00
Quantum
7c91c922e6 [client] input: avoid reentrancy when realigning guest cursor
This prevents LGMP_ERR_QUEUE_FULL from happening with high polling rate
mice, which is caused by receiving many more mouse events while the
guest cursor warps, triggering more warps.
2022-01-15 19:25:30 +11:00
Geoffrey McRae
9c49dc6efd [common] linux/time: fix memory leak 2022-01-14 14:19:16 +11:00
Chris Spencer
f635077a2c [client] egl: increase texture processing timeout
On my machine (Intel UHD Graphics 770), texture processing occasionally
(about 5% of the time) takes more than 20ms (the highest I have seen is
around 32ms) when the host resolution is 2560x1440. This results in the
frame being discarded and the client displays a stale image. Increase the
timeout to 40ms.
2022-01-14 12:31:02 +11:00
arcnmx
a9b5302a51 [module] fix compile on 5.16
The `DMA_BUF` namespace was introduced in:
16b0314aa7
2022-01-14 12:14:21 +11:00
Chris Spencer
3d0a8f6987 [host] dxgi: fix frame damage method parameters
The buffer input sizes to the `IDXGIOutputDuplication` methods are measured
in bytes. This dramatically increases the number of dirty/move rects that
can be handled.
2022-01-13 09:34:14 +11:00
Chris Spencer
786a252b23 [client] x11: don't use primary selection for clipboard
This behaviour is more consistent with other applications where text
selections do not influence explicit clipboard operations.
2022-01-13 08:18:56 +11:00
Chris Spencer
f145225dbc [host] dxgi: fix d3d11 assertion failure
`DEBUG_ASSERT(!this)` in `d3d11_create` is firing on the second
instantiation because we are not clearing `this` in `d3d11_free`.
2022-01-13 08:17:59 +11:00
Geoffrey McRae
b38a5ce89e [client] spice: update submodule to fix corrpution during connect 2022-01-13 02:21:48 +11:00
Geoffrey McRae
344d2ec599 [common] linux: replace create_timer with a single threaded timer
Now LG uses a 25Hz tick timer it is an issue that `create_timer` spawns
a new thread for every single timer event, so instead multiplex all the
timers into a single thread with a 1ms resolution.
2022-01-12 13:00:12 +11:00
Geoffrey McRae
6bba9bc25d [client/common] move ll from the client into the common code module 2022-01-12 12:22:18 +11:00
Geoffrey McRae
1851002fc1 [client] all: remove ll_walk and migrate over to ll_forEachNL 2022-01-12 12:17:29 +11:00
Geoffrey McRae
b99e1ea38e [client] ll: fix error in ll_forEachNL macro 2022-01-12 12:17:06 +11:00
Geoffrey McRae
2ecfa0a3ec [client] msg: add missing header file 2022-01-12 10:08:29 +11:00
Geoffrey McRae
ca0bc7c514 [client] close message boxes if the client connects 2022-01-12 10:04:16 +11:00
Geoffrey McRae
4122841b09 [client] spice: fix memory leak 2022-01-12 10:03:56 +11:00
Geoffrey McRae
e94252ad65 squash with ll 2022-01-12 10:03:31 +11:00
Geoffrey McRae
6fc0c69b2e [client] overlay/msg: provide a method to close messages from code 2022-01-12 09:35:09 +11:00
Geoffrey McRae
ced952a4c6 [client] ll: add new functionallity to allow removal of items 2022-01-12 09:33:36 +11:00
Geoffrey McRae
4411d21135 [client] spice: update submodule to prevent segfault on shutdown 2022-01-12 07:42:01 +11:00
Geoffrey McRae
70683010a6 [client] spice: update submodule to fix double free bug 2022-01-12 07:16:00 +11:00
vmfortress
7da2becfbd [host] dxgi: Replace standard asserts with DEBUG_ASSERT 2022-01-11 11:11:42 +11:00
Chris Spencer
8a61c8ebc2 [client] audio/pw: use rate matching
This can prevent glitches when the PipeWire quantum size changes.
2022-01-11 09:45:30 +11:00
Chris Spencer
ef9b2958ec [client] audio/pw: set maximum node latency
This prevents severe buffer underruns if the PipeWire quantum is bigger
than the ring buffer size. This could happen if a media player is running
at the same time as Looking Glass if it requests a very large quantum size,
for example.
2022-01-11 09:45:30 +11:00
Chris Spencer
e72e138267 [client] audio/pw: delay playback to avoid glitches 2022-01-11 09:45:30 +11:00
Chris Spencer
4c389a9274 [client] audio/pw: flush playback buffers before stopping
This stops the end of the playback from being truncated. It also prevents
an audible glitch when playback next starts due to the truncated data being
left behind in the ring buffer.
2022-01-11 09:45:30 +11:00
Chris Spencer
b9c646074d [client] audio/pw: don't discard playback data
This can cause significant glitching, particularly around the start of
playback.
2022-01-11 09:45:30 +11:00
Quantum
042a7d0925 [host] dxgi: add configurable sleep before D3D12 copy 2022-01-10 14:45:51 +11:00
Quantum
c69b19e68f [host] dxgi: add option to disable damage-aware copies 2022-01-10 14:45:51 +11:00
Quantum
cf7d501bc4 [host] dxgi: allow copy backend selection 2022-01-10 14:45:51 +11:00
Quantum
68e5b812a9 [host] dxgi: add preRelease callback
This is meant to avoid freeing the texture before the copy has finished.
2022-01-10 14:45:51 +11:00
Quantum
5a93f1e00c [host] dxgi: implement Direct3D 12 texture copy backend 2022-01-10 14:45:51 +11:00
Quantum
891f00a011 [host] dxgi: add d3d12.h from latest MinGW
This header was added in late 2020 and hasn't made its way into the GitHub
Actions image yet.
2022-01-10 14:45:51 +11:00
Quantum
137171a8a2 [host] dxgi: refactor to support additional copy backends 2022-01-10 14:45:51 +11:00
Geoffrey McRae
36892839f3 [host] lgmp: update repo to fix compliler error 2022-01-09 21:25:46 +11:00
Geoffrey McRae
0fc87576f3 [client] core: fallback to manual realignment if the LGMP message fails 2022-01-09 21:25:40 +11:00
Geoffrey McRae
3ffefb5281 [host] lgmp: update submodule to fix memory alignment issues 2022-01-09 21:19:41 +11:00
Geoffrey McRae
fd12d9901a [host] app: dont use pointers when realloc may have changed them
This code was completely broken and corrupts the stack, replace it with
something that is actually safe.
2022-01-09 21:19:17 +11:00
Geoffrey McRae
c05282c38c [host] cmake: add ubsan and asan to CMake (needs clang64 on windows)
Note that this still is a pain to make work as you require the MS
runtime DLLs, and to build with clang64.
2022-01-09 21:18:32 +11:00
Quantum
a391e271c3 [host] dxgi: damage all textures when skipping frame 2022-01-09 16:36:26 +11:00
Geoffrey McRae
24193aaaa6 [client] main: added user feedback during LGMP/KVMFR version check 2022-01-09 02:15:18 +11:00
Geoffrey McRae
f9b907a6b1 [client] msg: allow messages to contain blank lines and separators 2022-01-09 02:14:01 +11:00
Geoffrey McRae
b8866a2ce4 [client] spice: upgrade submodule to avoid errors on intentional stop 2022-01-08 20:33:18 +11:00
Geoffrey McRae
d42e409728 [client] spice: show message when connected to the wrong guest 2022-01-08 19:32:58 +11:00
Geoffrey McRae
780cf5f362 [client] overlay: add modal message dialog support 2022-01-08 18:58:48 +11:00
Geoffrey McRae
0080e5f1b9 [client] overlay: add app_invalidateOverlay method 2022-01-08 15:18:40 +11:00
Geoffrey McRae
ad6fa5a504 [client] app: move all alert management into overlay/alert.c 2022-01-08 14:33:12 +11:00
Geoffrey McRae
db2e38ae4d [client] overlay: add 25Hz tick function
This allows an overlay to manage itself for timed events like
alerts/messages, etc.
2022-01-08 14:33:07 +11:00
Geoffrey McRae
35334333ac [client] imgui: render twice for alerts
When using jitRender, or on the first frame of an alert the window
doesn't get resized immediately causing it to cut off the end of the
text.

ImGui needs two passes to calulate the bounding box for automatically
sized windows, this is per it's design and not a bug, see:

https://github.com/ocornut/imgui/issues/2158#issuecomment-434223618
2022-01-08 00:46:16 +11:00
Geoffrey McRae
ec0bd6adc8 [client] imgui: update to cimgui 1.86 2022-01-08 00:26:12 +11:00
Quantum
8e8d8834de [client] main: print guest CPU socket count information 2022-01-07 21:03:20 +11:00
Quantum
bf059a6eda [host] app: send CPU socket count information 2022-01-07 21:03:20 +11:00
Quantum
2834c7d95b [common] kvmfr: add field for CPU socket count 2022-01-07 21:03:20 +11:00
Quantum
2099161b7e [client] cpuinfo: implement CPU socket count for Windows 2022-01-07 21:03:20 +11:00
Quantum
a40a964b30 [client] cpuinfo: implement CPU socket count for Linux 2022-01-07 21:03:20 +11:00
Quantum
194241c5a3 [common] cpuinfo: add sockets to interface 2022-01-07 21:03:20 +11:00
Geoffrey McRae
32134b33ea [client] audio: remove more debug output spam 2022-01-07 16:35:46 +11:00
Geoffrey McRae
9d894065c8 [client] audio: remove debug spam 2022-01-07 16:27:29 +11:00
Johnathon Weaver
62c5d68fc6 Update AUTHORS 2022-01-07 01:46:35 +11:00
Johnathon Weaver
0f998582b9 [host] nvfbc: Fix dwmapi linking error
Fixed linking for DwmFlush and also rearranged as per how DXGI is.
2022-01-07 01:46:35 +11:00
Geoffrey McRae
7263159428 [client] audio/pw: implement record support 2022-01-07 00:54:44 +11:00
Geoffrey McRae
52f06ec332 [client] audio: don't call record.mute if it's not supported 2022-01-07 00:22:35 +11:00
Geoffrey McRae
7f93bbd675 [client] audio/pw: fixed another search/replace mistake 2022-01-07 00:09:34 +11:00
Geoffrey McRae
5c20a851c6 [client] audio/pw: fix search/replace error 2022-01-06 23:58:02 +11:00
Geoffrey McRae
11acaa2957 [client] audio/pw: refactor to use playback for playback methods 2022-01-06 23:56:12 +11:00
Geoffrey McRae
fe7973ea24 [client] audio: implement record interface and glue 2022-01-06 23:49:20 +11:00
Geoffrey McRae
ff2ca20235 [client] audio: always store the volume & mute state for restore 2022-01-06 23:31:39 +11:00
Geoffrey McRae
a114ea3de4 [client] audio: move audio code into it's own unit 2022-01-06 23:24:13 +11:00
Geoffrey McRae
e6bd36ec7c [client] audio: refactor audio to playback and add record funcs 2022-01-06 22:47:22 +11:00
Geoffrey McRae
34e5f7e968 [host] windows: fix usage of MCSS and try to get priority "Capture" 2022-01-06 20:14:55 +11:00
Geoffrey McRae
2f8b139131 [host] windows: set DwmFlush default to off
This new feature while helps on some systems, others using freesync or
higher refresh rates where the capture can't keep up will limit to
fractions of the refresh rate. Better to disable and allow users to
opt-in.
2022-01-06 19:20:08 +11:00
Geoffrey McRae
b058cbe9fe [host] nvfbc: add DwmFlush here too as it makes a large difference 2022-01-06 19:01:29 +11:00
Geoffrey McRae
443f98d2fa [host] windows: opt into the Multimedia Class Schedule Service
This improves overall application performance as windows will give this
process higher priority for low latency multimedia tasks.
2022-01-06 18:41:48 +11:00
Geoffrey McRae
92f27cc0f0 [host] dxgi: use DwmFlush to sync to presentation interval
This change reduces the host GPU and CPU load by a large margin
improving guest system performance along with removing latency spikes
when moving the mouse. This is default enabled but can be disabled with
the new option `dxgi:dwmFlush=no` as it limits the capture rate to the
refresh rate of the guests output which may not be desireable.
2022-01-06 18:39:08 +11:00
Geoffrey McRae
208b722348 [client] main: failure to find a renderer is an error 2022-01-06 15:19:35 +11:00
Geoffrey McRae
67509d7a2d [client] main: format the guest information a bit better 2022-01-06 15:19:16 +11:00
Geoffrey McRae
c20bb27b67 [client] main: move checkUUID to after informational prints are done
This change makes the client print the check failure as the last thing
before disconnecting making it more obvious to the user.
2022-01-06 15:18:10 +11:00
Geoffrey McRae
8cdeaceed9 [client] main: fix reversed check logic from testing/debug 2022-01-06 14:40:34 +11:00
Geoffrey McRae
7bcd0dd97f [client] main: if spice is in use check the guest uuid matches
If the guest supports sending us it's UUID and PureSpice has also
reported the guest's UUID, check them to see if the user has
accidentially connected to the wrong spice socket.
2022-01-06 14:38:46 +11:00
Geoffrey McRae
5bb1f01dea [host] windows: parse the UUID from the SMBIOS 2022-01-06 14:22:38 +11:00
Geoffrey McRae
297d0be2dc [obs] allow for the larger KVMFR header size now 2022-01-05 21:57:49 +11:00
Geoffrey McRae
fdb38a227e [host] app: implement stubs for platform specific guest information 2022-01-05 21:04:57 +11:00
Geoffrey McRae
7ccd202d36 [client] main: fix out by one error in the bounds check for os type 2022-01-05 20:40:13 +11:00
Geoffrey McRae
177a997883 [client] main: parse the KVMFR records and log the details 2022-01-05 20:27:43 +11:00
Geoffrey McRae
b3f6c75ade [host] app: zero memory allocated for lgmp userdata 2022-01-05 20:13:12 +11:00
Geoffrey McRae
912ca62a7b [common] only define min/max if they have not already been defined 2022-01-05 19:45:09 +11:00
Geoffrey McRae
952ebea2c5 [all] refresh copyright dates 2022-01-05 19:42:46 +11:00
Geoffrey McRae
0d27092ef5 [all] move min/max and upcast macros into common/util.h 2022-01-05 19:41:57 +11:00
Geoffrey McRae
ebf20dd108 [host] nvfbc: fix failure to startup 2022-01-05 19:31:47 +11:00
Geoffrey McRae
7cc9b5f77c [host] app: remove debug line and fix my failure to count 2022-01-05 19:25:51 +11:00
Geoffrey McRae
0ccc84959e [host] app: fix out by one compiler warning 2022-01-05 19:23:19 +11:00
Geoffrey McRae
ba9f2b85b6 [host/client] kvmfr: update to include extra user data about the VM
This change allows the host to provide information to the client about
how the VM is configured, information such as the UUID, CPU
configuration and capture method both for informational display in the
client as well as debugging in the client's logs.

The format of the records allows this to be extended later with new
record types without needing to bump the KVMFR version.
2022-01-05 19:18:43 +11:00
Geoffrey McRae
ed61a7adf9 [client] spice: update PureSpice submodule to fix UUID format bug 2022-01-05 12:39:05 +11:00
Geoffrey McRae
d708651c53 [client] egl: check for null gl strings 2022-01-05 12:38:38 +11:00
Geoffrey McRae
0d00936aac [client] spice: update PureSpice submodule 2022-01-04 19:06:14 +11:00
Geoffrey McRae
6347f02efe [client] fix accidental submodule version change 2022-01-04 11:04:56 +11:00
Geoffrey McRae
dfdc407bc6 [all] ci: libdecor-dev is now libdecor-0-dev 2022-01-04 10:48:13 +11:00
Geoffrey McRae
ac2c62e560 [host] intiialize the app state earlier to prevent ovewriting re-inits
If there is LGMP corruption the LGMP thread will set the state to
REINIT which if this happens early enough will get overwritten if the
inital app state is set too late. Instead set the application initial
state early to avoid this.
2022-01-04 10:40:00 +11:00
Geoffrey McRae
4b8255aa28 [client] spice: allow spice startup to happen in parallel 2022-01-01 21:07:55 +11:00
Geoffrey McRae
b6fedf1420 update purespice submodule 2022-01-01 20:51:54 +11:00
Geoffrey McRae
c8b4787cb1 [client] don't report a spice error on graceful shutdown 2021-12-30 21:02:53 +11:00
Geoffrey McRae
d43126f433 [client] main: release the grab if the host application is stopped 2021-12-30 17:08:19 +11:00
Geoffrey McRae
6f39434bdc [host] correct reinit logic when LGMP corruption is detected 2021-12-30 14:34:45 +11:00
Geoffrey McRae
9b202d5566 [host] detect header corruption and re-initialize if so 2021-12-30 13:49:33 +11:00
Geoffrey McRae
764e52fb20 [client] undo commented code from debugging 2021-12-30 13:19:59 +11:00
Geoffrey McRae
6f17e89b16 [host] lgmp: update to fix regression in initialization of the header 2021-12-30 13:00:56 +11:00
Geoffrey McRae
d8e7a83226 [host] lgmp: update the lgmp submodule 2021-12-30 12:49:49 +11:00
Geoffrey McRae
c74d48691f [client] spice: update submodule and update to use new PSConfig members 2021-12-29 20:22:07 +11:00
Geoffrey McRae
7c8f42855d [client] spice: update submodule for improved logging 2021-12-29 18:18:33 +11:00
Geoffrey McRae
d1a765c179 [client] spice: update PureSpice submodule and adjust for new API usage 2021-12-29 16:02:22 +11:00
Geoffrey McRae
2ed3c82de0 [common] provide debug print methods for dependent libraries to use 2021-12-29 16:01:42 +11:00
Geoffrey McRae
17b77cfbc1 [client] spice: update PureSpice submodule and update includes 2021-12-29 00:18:25 +11:00
Geoffrey McRae
65ba2e8df9 [client] spice: update submodule and refactor calls & types 2021-12-28 22:04:35 +11:00
Quantum
e7fdf7e77a [host] pipewire: report stream state
This should provide some useful debug information and report stream
errors.
2021-12-28 19:23:14 +11:00
Jonathan Rubenstein
aa5922a1b4 [version.cmake] Add reminder to synchronize git-describe with docs/lgrelease.py 2021-12-28 19:18:42 +11:00
Geoffrey McRae
142902b7b3 [doc] fix typo and add Debian to the word list 2021-12-28 19:07:33 +11:00
Quantum
10110dd940 [host] app: support force quitting
This makes it much less painful to develop the host, as sometimes it
hangs and ^C doesn't work.
2021-12-28 19:04:47 +11:00
Geoffrey McRae
192fb1cdc7 [doc] add link to the wiki for dependencies on other distributions
ref #909
2021-12-28 19:04:24 +11:00
Geoffrey McRae
35efa551ef [client] egl: determine mouse scale using both horiz and vert size 2021-12-28 19:04:24 +11:00
Jonathan Rubenstein
f53adc7a05 [doc] build: Fix apt command overflowing page 2021-12-28 18:50:56 +11:00
Quantum
a21e897bb5 [host] pipewire: use new format for 10-bit colour
I recently added little-endian 10-bit colour formats to PipeWire, which
is what we actually use. The old r210 format is big endian.
2021-12-28 18:50:29 +11:00
Quantum
136737f25b [client] egl: simplify EGL torus code 2021-12-28 10:19:36 +11:00
Geoffrey McRae
95987a9c91 [client] core: set the correct cursor when exiting overlay 2021-12-27 11:52:06 +11:00
Geoffrey McRae
bbd9c84896 [client] core: invalidate the pointer state when overlay is disabled 2021-12-27 11:22:12 +11:00
vmfortress
8ab130deba xcb: added basic cursor support via xcb_fixes
xcb interface now properly supports cursor integration through
a `pointerThread` similar to the nvfbc implementation.
2021-12-27 11:10:07 +11:00
vmfortress
fbf294efd9 xcb: Fixed return value of xcb_deinit
`xcb_deinit` returns `true` to fit the capture interface standard.
2021-12-27 11:10:07 +11:00
vmfortress
2824238b4d xcb: added xcb_stop and xcb_initOptions support
Added basic functions and the necessary variables for the
`.initOptions` and `.stop` items in the capture interface.
2021-12-27 11:10:07 +11:00
Geoffrey McRae
bb74a9d9c8 [client] core: don't try to send LGMP messages if the video is stopped
If the video stream is stopped the LGMP session is not valid, so we
can't send messages to the client.
2021-12-27 09:55:56 +11:00
Jonathan Rubenstein
9ff476bd09 [doc] Minor cleanup to release spelling check
Co-authored-by: Guanzhong Chen <quantum2048@gmail.com>
2021-12-27 09:23:31 +11:00
Jonathan Rubenstein
6ef3fea05e [doc] Version no longer gets spellchecked 2021-12-27 09:23:31 +11:00
Geoffrey McRae
02ec25b008 [client] audio/pw: it's Looking Glass, not LookingGlass 2021-12-26 18:49:35 +11:00
Geoffrey McRae
4e75c576b2 [client] ci: add new libpulse-dev dependency 2021-12-26 18:30:05 +11:00
Geoffrey McRae
90dd1f3913 [client] audio/pa: added initial pulseaudio implementation 2021-12-26 18:22:51 +11:00
Jonathan Rubenstein
a8ddf72318 [doc] conf: Fix typo in release git-describe command
We got the gist of it, but in the end, didn't git it right.
2021-12-26 14:12:39 +11:00
Jonathan Rubenstein
5d9db8b2f5 [docs] all: Change nav background from transparent to explicit 2021-12-26 13:57:05 +11:00
Jonathan Rubenstein
672cd246ab [docs] all: Fix mobile layout 2021-12-26 13:57:05 +11:00
Jonathan Rubenstein
936688ddac [doc] all: Center content when there's room 2021-12-26 13:28:40 +11:00
Jonathan Rubenstein
ff6c46f7ca [docs] all: Switch to sphinx-readthedocs-theme
From https://github.com/readthedocs/sphinx_rtd_theme
2021-12-26 12:46:23 +11:00
Quantum
4dccd725bf [doc] automatically detect version
We prefer the VERSION file if available, otherwise falling back to
querying version directly from git.
2021-12-26 12:08:24 +11:00
Quantum
6f8745a89b [host] installer: enable DPI awareness
This should make the installer look less blurry on high DPI displays.
2021-12-26 11:34:43 +11:00
Quantum
f971a01801 [host] ci: test IVSHMEM installer build 2021-12-26 11:31:32 +11:00
Quantum
3d1eedd4ef [host] installer: add ability to install IVSHMEM driver
To use this, run makensis with -DIVSHMEM with the driver files in the
ivshmem subdirectory under the build directory.
2021-12-26 11:31:32 +11:00
Quantum
d073f9969c [host] installer: clean up trailing whitespace 2021-12-26 11:31:32 +11:00
WYF
b21d842f0e [host] nvfbc: add an option to specify adapter 2021-12-26 11:14:17 +11:00
Geoffrey McRae
9fa643484c [client] audio/pw: use scaling forumula provided by quantum 2021-12-26 11:09:42 +11:00
Geoffrey McRae
64b64b61be [client] audio/pw: implement volume and mute control 2021-12-26 11:09:42 +11:00
Geoffrey McRae
433a5420cb [client] audio: update PureSpice and add support for volume control/mute 2021-12-26 11:09:42 +11:00
Geoffrey McRae
e408ea51e2 [client] audio/pw: gracefully shutdown when asked to 2021-12-26 11:09:42 +11:00
Geoffrey McRae
cca6492069 [client] audio: call audioDev->free on spice thread exit 2021-12-26 11:09:42 +11:00
Quantum
141d5d3731 [client] ci: build with PipeWire 2021-12-26 11:09:42 +11:00
Geoffrey McRae
ebdc847ef1 [client] audio/pw: don't re-create an idle stream with matching format 2021-12-26 11:09:42 +11:00
Geoffrey McRae
2ea24516d2 [client] audio/pw: limit how much data gets buffered to reduce latency 2021-12-26 11:09:42 +11:00
Geoffrey McRae
dd04a46403 [client] audio/pw: make use of the new ringbuffer consume/append funcs 2021-12-26 11:09:42 +11:00
Geoffrey McRae
d99ec3e9c0 [common] ringbuffer: remove exta locking and add append/consume funcs
As the ringbuffer is now in use for audio it makes sense to provide bulk
append and consume functions that are thread safe instead of adding
locking over all of the functions. This partially reverts the prior
commit that added the extra locking.
2021-12-26 11:09:42 +11:00
Geoffrey McRae
f403033ab1 [client] audio/pw: properly manage the stream state 2021-12-26 11:09:42 +11:00
Geoffrey McRae
11ef94c134 [client] audio/pw: set the node name 2021-12-26 11:09:42 +11:00
Geoffrey McRae
75e46128d4 [client] audio/pw: don't actually stop when SPICE signals a stop 2021-12-26 11:09:42 +11:00
Geoffrey McRae
e810577317 [client] audio: initial addition of PipeWire audio support via SPICE 2021-12-26 11:09:42 +11:00
Geoffrey McRae
8ba4b56dba [common] ringbuffer: added shift and locking for thread safety 2021-12-26 11:09:42 +11:00
Geoffrey McRae
d69069fb09 [client] egl: keep the mouse cursor 1:1 when downscaling
This keeps the cursor a usable size when the guest is running a high
resolution and downscaling (ie, 4K -> FHD).
2021-12-26 11:08:42 +11:00
429 changed files with 59160 additions and 5501 deletions

4
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,4 @@
# Jonathan Rubenstein (JJRcop)
# - Primary documentation manager. Does not require direct approval for every
# - change, but should be consulted for large additions and changes.
/doc/ jrubcop@gmail.com

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

@@ -2,7 +2,7 @@ name: build
on: [push, pull_request]
jobs:
client:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
@@ -12,12 +12,9 @@ jobs:
wayland_shell: [xdg-shell, libdecor]
build_type: [Release, Debug]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Install libdecor PPA
run: sudo add-apt-repository ppa:christianrauch/libdecoration
if: ${{ matrix.wayland_shell == 'libdecor' }}
- name: Update apt
run: |
sudo apt-get update
@@ -28,8 +25,10 @@ jobs:
libspice-protocol-dev nettle-dev \
libgl-dev libgles-dev \
libx11-dev libxss-dev libxi-dev libxinerama-dev libxcursor-dev libxpresent-dev \
libwayland-dev wayland-protocols libxkbcommon-dev \
$([ '${{ matrix.wayland_shell }}' = libdecor ] && echo 'libdecor-dev libdbus-1-dev') \
libwayland-dev libxkbcommon-dev \
libfontconfig-dev \
libsamplerate0-dev libpipewire-0.3-dev libpulse-dev \
$([ '${{ matrix.wayland_shell }}' = libdecor ] && echo 'libdecor-0-dev libdbus-1-dev') \
$([ '${{ matrix.compiler.cc }}' = clang ] && echo 'clang-tools')
sudo pip3 install pyenchant
- name: Configure client
@@ -43,7 +42,7 @@ jobs:
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DCMAKE_LINKER:FILEPATH=/usr/bin/ld \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DENABLE_LIBDECOR=${{ matrix.wayland_shell == 'libdecor' }} \
-DENABLE_LIBDECOR=${{ matrix.wayland_shell == 'libdecor' }} \
..
- name: Build client
run: |
@@ -58,7 +57,7 @@ jobs:
module:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Build kernel module
@@ -69,7 +68,7 @@ jobs:
host-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Update apt
@@ -77,7 +76,8 @@ jobs:
sudo apt-get update
- name: Install Linux host dependencies
run: |
sudo apt-get install binutils-dev libgl1-mesa-dev libxcb-xfixes0-dev
sudo apt-get install binutils-dev libglib2.0-dev libxcb-xfixes0-dev \
libpipewire-0.3-dev libxcb-shm0-dev
- name: Configure Linux host
run: |
mkdir host/build
@@ -89,9 +89,9 @@ jobs:
make -j$(nproc)
host-windows-cross:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Update apt
@@ -113,13 +113,40 @@ jobs:
run: |
cd host/build
makensis platform/Windows/installer.nsi
- name: Build Windows host installer with IVSHMEM drivers
run: |
cd host/build
curl https://dl.quantum2.xyz/ivshmem.tar.gz | tar xz
makensis -DIVSHMEM platform/Windows/installer.nsi
host-windows-native:
runs-on: windows-latest
runs-on: windows-2025
steps:
- uses: actions/checkout@v1
- 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
@@ -134,13 +161,43 @@ 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:
matrix:
cc: [gcc, clang]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Update apt
@@ -170,8 +227,8 @@ jobs:
sudo apt-get update
- name: Install docs dependencies
run: |
sudo apt-get install python3-sphinx
sudo pip3 install sphinxcontrib-spelling
sudo apt-get install python3-sphinx python3-sphinx-rtd-theme \
python3-sphinxcontrib.spelling
- name: Build docs
run: |
cd doc

View File

@@ -3,16 +3,36 @@ on: pull_request
jobs:
authors:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v1
- name: Check AUTHORS file
id: check-authors
run: |
user="$(curl -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' -s https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }} | jq -r .user.login)"
echo "user=$user" >> "$GITHUB_OUTPUT"
echo "Checking if GitHub user $user is in AUTHORS file..."
if grep -q -E '> \('"$user"'\)' AUTHORS; then
echo "$user found in AUTHORS file, all good!"
else
echo "$user not found in AUTHORS file."
echo "Please add yourself to the AUTHORS file and try again."
exit 1
echo "not-found=yes" >> "$GITHUB_OUTPUT"
fi
- name: 'Not found: Create review requesting changes'
if: ${{ steps.check-authors.outputs.not-found }}
uses: actions/github-script@v7
with:
script: |
github.rest.pulls.createReview({
owner: context.issue.owner,
repo: context.issue.repo,
pull_number: context.issue.number,
event: "REQUEST_CHANGES",
body: "@${{ steps.check-authors.outputs.user }} not found in AUTHORS file.\n" +
"Please add yourself to the AUTHORS file and try again."
});
- name: 'Not found: Fail job'
if: ${{ steps.check-authors.outputs.not-found }}
run: exit 1

9
.gitignore vendored
View File

@@ -10,3 +10,12 @@ module/modules.order
*/build
__pycache__
*.py[co]
*/.vs
*.user
idd/Debug
idd/x64
idd/*/x64
idd/*/Debug
idd/*/VersionInfo.h
idd/*/VERSION
idd/packages

6
.gitmodules vendored
View File

@@ -7,3 +7,9 @@
[submodule "repos/cimgui"]
path = repos/cimgui
url = https://github.com/cimgui/cimgui.git
[submodule "repos/wayland-protocols"]
path = repos/wayland-protocols
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)

22
AUTHORS
View File

@@ -9,7 +9,7 @@ arcnmx <arcnmx@users.noreply.github.com> (arcnmx)
TheCakeIsNaOH <TheCakeIsNaOH@gmail.com> (TheCakeIsNaOH)
NamoDev <namodev@users.noreply.github.com> (NamoDev)
feltcat <58396817+feltcat@users.noreply.github.com> (feltcat)
Ali Abdel-Qader <abdelqaderali@protonmail.com>
Ali Abdel-Qader <abdelqaderali@protonmail.com> (thrifty-txt)
Jack Karamanian <karamanian.jack@gmail.com>
Mikko Rasa <tdb@tdb.fi> (DataBeaver)
Omar Pakker <Omar007@users.noreply.github.com> (Omar007)
@@ -54,3 +54,23 @@ thejavascriptman <thejavascriptman@outlook.com> (thejavascriptman)
vroad <396351+vroad@users.noreply.github.com> (vroad)
williamvds <w.vigolodasilva@gmail.com> (williamvds)
SytheZN <sythe.zn@gmail.com> (SytheZN)
RTXUX <wyf@rtxux.xyz> (RTXUX)
Vincent LaRocca <vincentmlarocca@gmail.com> (VMFortress)
Johnathon Paul Weaver <weaver123_johnathon@hotmail.com> (8BallBomBom)
Chris Spencer <spencercw@gmail.com> (spencercw)
Mark Boorer <markboo99@gmail.com> (Shootfast)
babbaj <babbaj45@gmail.com> (Babbaj)
Matthew McMullin <matthew@mcmullin.one> (matthewjmc)
Leonard Fricke <leonard.fricke98@gmail.com> (Leo1998)
David Meier <meier_david_91@hotmail.com> (Kenny.ch)
Daniel Cordero <looking-glass@0xdc.io> (0xdc)
esi <git@esibun.net> (esibun)
MakiseKurisu <saberconer@gmail.com> (MakiseKurisu)
Zenithal <i@zenithal.me> (ZenithalHourlyRate)
Kamplom <6284968128@protonmail.ch> (kamplom)
Jacob McNamee <jacob@jacobmcnamee.com> (jacobmcnamee)
Marco Antonio J. Costa <marco.antonio.costa@gmail.com> (majcosta)
rs189 <35667100+rs189@users.noreply.github.com> (rs189)
Jérôme Poulin <jeromepoulin@gmail.com> (ticpu)
Marco Rodolfi <marco.rodolfi@tuta.io> (RodoMa92)
Stewart Borle <stewi1014@gmail.com> (stewi1014)

View File

@@ -19,4 +19,4 @@ https://looking-glass.io/downloads
Source code for the documentation can be found in the `/doc` directory.
You may view this locally as HTML by running `make html` with `python3-sphinx`
installed.
and `python3-sphinx-rtd-theme` installed.

View File

@@ -1,16 +1,16 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.10)
project(looking-glass-client C CXX)
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(FATAL_ERROR
"\n"
"In-source builds are not supported\n"
"See build instructions provided in: "
"${PROJECT_TOP}/doc/build.rst\n"
"Refusing to continue"
)
message(FATAL_ERROR
"\n"
"In-source builds are not supported\n"
"See build instructions provided in: "
"${PROJECT_TOP}/doc/build.rst\n"
"Refusing to continue"
)
endif()
list(APPEND CMAKE_MODULE_PATH "${PROJECT_TOP}/cmake/" "${PROJECT_SOURCE_DIR}/cmake/")
@@ -42,24 +42,18 @@ add_feature_info(ENABLE_ASAN ENABLE_ASAN "AddressSanitizer support.")
option(ENABLE_UBSAN "Build with UndefinedBehaviorSanitizer" OFF)
add_feature_info(ENABLE_UBSAN ENABLE_UBSAN "UndefinedBehaviorSanitizer support.")
option(ENABLE_X11 "Build with X11 support" ON)
add_feature_info(ENABLE_X11 ENABLE_X11 "X11 support.")
option(ENABLE_PIPEWIRE "Build with PipeWire audio output support" ON)
add_feature_info(ENABLE_PIPEWIRE ENABLE_PIPEWIRE "PipeWire audio support.")
option(ENABLE_WAYLAND "Build with Wayland support" ON)
add_feature_info(ENABLE_WAYLAND ENABLE_WAYLAND "Wayland support.")
option(ENABLE_LIBDECOR "Build with libdecor support" OFF)
add_feature_info(ENABLE_LIBDECOR ENABLE_LIBDECOR "libdecor support.")
if (NOT ENABLE_X11 AND NOT ENABLE_WAYLAND)
message(FATAL_ERROR "Either ENABLE_X11 or ENABLE_WAYLAND must be on")
endif()
option(ENABLE_PULSEAUDIO "Build with PulseAudio audio output support" ON)
add_feature_info(ENABLE_PULSEAUDIO ENABLE_PULSEAUDIO "PulseAudio audio support.")
add_compile_options(
"-Wall"
"-Wextra"
"-Wno-sign-compare"
"-Wno-unused-parameter"
"$<$<COMPILE_LANGUAGE:C>:-Wstrict-prototypes>"
"$<$<C_COMPILER_ID:GNU>:-Wimplicit-fallthrough=2>"
"-Werror"
"-Wfatal-errors"
@@ -94,49 +88,61 @@ add_definitions(-D ATOMIC_LOCKING)
add_definitions(-D GL_GLEXT_PROTOTYPES)
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/version.c
${CMAKE_BINARY_DIR}/_version.c
COMMAND ${CMAKE_COMMAND} -D PROJECT_TOP=${PROJECT_TOP} -P
${PROJECT_TOP}/version.cmake
OUTPUT ${CMAKE_BINARY_DIR}/version.c
${CMAKE_BINARY_DIR}/_version.c
COMMAND ${CMAKE_COMMAND} -D PROJECT_TOP=${PROJECT_TOP} -P
${PROJECT_TOP}/version.cmake
)
include_directories(
${PROJECT_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/include
${PROJECT_TOP}
${PROJECT_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}
${CMAKE_BINARY_DIR}/include
${PROJECT_TOP}/repos/nanosvg/src
)
link_libraries(
${CMAKE_DL_LIBS}
rt
m
${CMAKE_DL_LIBS}
rt
m
)
set(SOURCES
${CMAKE_BINARY_DIR}/version.c
src/main.c
src/core.c
src/app.c
src/config.c
src/keybind.c
src/ll.c
src/util.c
src/clipboard.c
src/kb.c
src/gl_dynprocs.c
src/egl_dynprocs.c
src/eglutil.c
src/overlay_utils.c
${CMAKE_BINARY_DIR}/version.c
src/main.c
src/core.c
src/app.c
src/message.c
src/audio.c
src/config.c
src/keybind.c
src/util.c
src/clipboard.c
src/kb.c
src/gl_dynprocs.c
src/egl_dynprocs.c
src/eglutil.c
src/overlay_utils.c
src/render_queue.c
src/evdev.c
src/overlay/alert.c
src/overlay/fps.c
src/overlay/graphs.c
src/overlay/help.c
src/overlay/config.c
src/overlay/splash.c
src/overlay/alert.c
src/overlay/fps.c
src/overlay/graphs.c
src/overlay/help.c
src/overlay/config.c
src/overlay/msg.c
src/overlay/status.c
)
# Force cimgui to build as a static library.
set(IMGUI_STATIC "yes" CACHE STRING "Build as a static library")
add_definitions("-DCIMGUI_USE_OPENGL2=1")
add_definitions("-DCIMGUI_USE_OPENGL3=1")
add_subdirectory("${PROJECT_TOP}/resources" "${CMAKE_BINARY_DIR}/resources")
add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common" )
add_subdirectory("${PROJECT_TOP}/repos/LGMP/lgmp" "${CMAKE_BINARY_DIR}/LGMP" )
add_subdirectory("${PROJECT_TOP}/repos/PureSpice" "${CMAKE_BINARY_DIR}/PureSpice")
@@ -145,23 +151,41 @@ add_subdirectory("${PROJECT_TOP}/repos/cimgui" "${CMAKE_BINARY_DIR}/cimgui" E
add_subdirectory(displayservers)
add_subdirectory(renderers)
configure_file("${PROJECT_TOP}/resources/looking-glass-client.desktop.in" "${CMAKE_BINARY_DIR}/resources/looking-glass-client.desktop" @ONLY)
add_executable(looking-glass-client ${SOURCES})
target_compile_definitions(looking-glass-client PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1)
target_link_libraries(looking-glass-client
${EXE_FLAGS}
PkgConfig::FONTCONFIG
lg_common
displayservers
lgmp
purespice
renderers
cimgui
${EXE_FLAGS}
PkgConfig::FONTCONFIG
lg_resources
lg_common
displayservers
lgmp
purespice
renderers
cimgui
)
if (ENABLE_PIPEWIRE OR ENABLE_PULSEAUDIO)
add_definitions(-D ENABLE_AUDIO)
add_subdirectory(audiodevs)
pkg_check_modules(SAMPLERATE REQUIRED IMPORTED_TARGET samplerate)
target_link_libraries(looking-glass-client
PkgConfig::SAMPLERATE
audiodevs
)
endif()
install(TARGETS looking-glass-client
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT binary)
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT binary)
install(FILES "${CMAKE_BINARY_DIR}/resources/looking-glass-client.desktop"
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications")
install(FILES "${PROJECT_TOP}/resources/lg-logo.svg"
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps"
RENAME "looking-glass.svg")
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)

View File

@@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 3.10)
project(audiodevs LANGUAGES C)
set(AUDIODEV_H "${CMAKE_BINARY_DIR}/include/dynamic/audiodev.h")
set(AUDIODEV_C "${CMAKE_BINARY_DIR}/src/audiodev.c")
file(WRITE ${AUDIODEV_H} "#include \"interface/audiodev.h\"\n\n")
file(APPEND ${AUDIODEV_H} "extern struct LG_AudioDevOps * LG_AudioDevs[];\n\n")
file(WRITE ${AUDIODEV_C} "#include \"interface/audiodev.h\"\n\n")
file(APPEND ${AUDIODEV_C} "#include <stddef.h>\n\n")
set(AUDIODEVS "_")
set(AUDIODEVS_LINK "_")
function(add_audiodev name)
set(AUDIODEVS "${AUDIODEVS};${name}" PARENT_SCOPE)
set(AUDIODEVS_LINK "${AUDIODEVS_LINK};audiodev_${name}" PARENT_SCOPE)
add_subdirectory(${name})
endfunction()
# Add/remove audiodevs here!
if(ENABLE_PIPEWIRE)
add_audiodev(PipeWire)
endif()
if(ENABLE_PULSEAUDIO)
add_audiodev(PulseAudio)
endif()
list(REMOVE_AT AUDIODEVS 0)
list(REMOVE_AT AUDIODEVS_LINK 0)
list(LENGTH AUDIODEVS AUDIODEV_COUNT)
file(APPEND ${AUDIODEV_H} "#define LG_AUDIODEV_COUNT ${AUDIODEV_COUNT}\n")
foreach(audiodev ${AUDIODEVS})
file(APPEND ${AUDIODEV_C} "extern struct LG_AudioDevOps LGAD_${audiodev};\n")
endforeach()
file(APPEND ${AUDIODEV_C} "\nconst struct LG_AudioDevOps * LG_AudioDevs[] =\n{\n")
foreach(audiodev ${AUDIODEVS})
file(APPEND ${AUDIODEV_C} " &LGAD_${audiodev},\n")
endforeach()
file(APPEND ${AUDIODEV_C} " NULL\n};")
add_library(audiodevs STATIC ${AUDIODEV_C})
target_link_libraries(audiodevs ${AUDIODEVS_LINK})

View File

@@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.10)
project(audiodev_PipeWire LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(AUDIODEV_PipeWire REQUIRED IMPORTED_TARGET
libpipewire-0.3
)
add_library(audiodev_PipeWire STATIC
pipewire.c
)
target_link_libraries(audiodev_PipeWire
PkgConfig::AUDIODEV_PipeWire
lg_common
)
target_include_directories(audiodev_PipeWire
PRIVATE
src
)

View File

@@ -0,0 +1,647 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "interface/audiodev.h"
#include <spa/param/audio/format-utils.h>
#include <spa/param/props.h>
#include <pipewire/pipewire.h>
#include <math.h>
#include "common/debug.h"
#include "common/stringutils.h"
#include "common/util.h"
#include "common/option.h"
typedef enum
{
STREAM_STATE_INACTIVE,
STREAM_STATE_ACTIVE,
STREAM_STATE_DRAINING
}
StreamState;
struct PipeWire
{
struct pw_loop * loop;
struct pw_context * context;
struct pw_thread_loop * thread;
struct
{
struct pw_stream * stream;
struct spa_io_rate_match * rateMatch;
struct pw_time time;
int channels;
int sampleRate;
int stride;
LG_AudioPullFn pullFn;
int maxPeriodFrames;
int startFrames;
StreamState state;
}
playback;
struct
{
struct pw_stream * stream;
int channels;
int sampleRate;
int stride;
LG_AudioPushFn pushFn;
bool active;
}
record;
};
static struct PipeWire pw = {0};
static void pipewire_onPlaybackIoChanged(void * userdata, uint32_t id,
void * data, uint32_t size)
{
switch (id)
{
case SPA_IO_RateMatch:
pw.playback.rateMatch = data;
break;
}
}
static void pipewire_onPlaybackProcess(void * userdata)
{
struct pw_buffer * pbuf;
#if PW_CHECK_VERSION(0, 3, 50)
if (pw_stream_get_time_n(pw.playback.stream, &pw.playback.time,
sizeof(pw.playback.time)) < 0)
#else
if (pw_stream_get_time(pw.playback.stream, &pw.playback.time) < 0)
#endif
DEBUG_ERROR("pw_stream_get_time failed");
if (!(pbuf = pw_stream_dequeue_buffer(pw.playback.stream)))
{
DEBUG_WARN("out of buffers");
return;
}
struct spa_buffer * sbuf = pbuf->buffer;
uint8_t * dst;
if (!(dst = sbuf->datas[0].data))
return;
int frames = sbuf->datas[0].maxsize / pw.playback.stride;
if (pw.playback.rateMatch && pw.playback.rateMatch->size > 0)
frames = min(frames, pw.playback.rateMatch->size);
frames = pw.playback.pullFn(dst, frames);
if (!frames)
{
sbuf->datas[0].chunk->size = 0;
pw_stream_queue_buffer(pw.playback.stream, pbuf);
return;
}
pbuf->size = frames;
sbuf->datas[0].chunk->offset = 0;
sbuf->datas[0].chunk->stride = pw.playback.stride;
sbuf->datas[0].chunk->size = frames * pw.playback.stride;
pw_stream_queue_buffer(pw.playback.stream, pbuf);
}
static void pipewire_onPlaybackDrained(void * userdata)
{
pw_thread_loop_lock(pw.thread);
pw_stream_set_active(pw.playback.stream, false);
pw.playback.state = STREAM_STATE_INACTIVE;
pw_thread_loop_unlock(pw.thread);
}
static struct Option pipewire_options[] =
{
{
.module = "pipewire",
.name = "outDevice",
.description = "The default playback device to use",
.type = OPTION_TYPE_STRING
},
{
.module = "pipewire",
.name = "recDevice",
.description = "The default record device to use",
.type = OPTION_TYPE_STRING
},
{0}
};
static void pipewire_earlyInit(void)
{
option_register(pipewire_options);
}
static bool pipewire_init(void)
{
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(
// Request real-time priority on the PipeWire threads
PW_KEY_CONFIG_NAME, "client-rt.conf",
NULL
),
0);
#endif
if (!pw.context)
{
DEBUG_ERROR("Failed to create a context");
goto err;
}
/* this is just to test for PipeWire availabillity */
struct pw_core * core = pw_context_connect(pw.context, NULL, 0);
if (!core)
goto err_context;
/* PipeWire is available so create the loop thread and start it */
pw.thread = pw_thread_loop_new_full(pw.loop, "PipeWire", NULL);
if (!pw.thread)
{
DEBUG_ERROR("Failed to create the thread loop");
goto err_context;
}
pw_thread_loop_start(pw.thread);
return true;
err_context:
pw_context_destroy(pw.context);
err:
pw_loop_destroy(pw.loop);
pw_deinit();
return false;
}
static void pipewire_playbackStopStream(void)
{
if (!pw.playback.stream)
return;
pw_thread_loop_lock(pw.thread);
pw_stream_destroy(pw.playback.stream);
pw.playback.stream = NULL;
pw.playback.rateMatch = NULL;
pw_thread_loop_unlock(pw.thread);
}
static void pipewire_playbackSetup(int channels, int sampleRate,
int requestedPeriodFrames, int * maxPeriodFrames, int * startFrames,
LG_AudioPullFn pullFn)
{
const struct spa_pod * params[1];
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
static const struct pw_stream_events events =
{
.version = PW_VERSION_STREAM_EVENTS,
.io_changed = pipewire_onPlaybackIoChanged,
.process = pipewire_onPlaybackProcess,
.drained = pipewire_onPlaybackDrained
};
if (pw.playback.stream &&
pw.playback.channels == channels &&
pw.playback.sampleRate == sampleRate)
{
*maxPeriodFrames = pw.playback.maxPeriodFrames;
*startFrames = pw.playback.startFrames;
return;
}
pipewire_playbackStopStream();
char requestedNodeLatency[32];
snprintf(requestedNodeLatency, sizeof(requestedNodeLatency), "%d/%d",
requestedPeriodFrames, sampleRate);
pw.playback.channels = channels;
pw.playback.sampleRate = sampleRate;
pw.playback.stride = sizeof(float) * channels;
pw.playback.pullFn = pullFn;
pw_thread_loop_lock(pw.thread);
struct pw_properties * props =
pw_properties_new(
PW_KEY_APP_NAME , "Looking Glass",
PW_KEY_NODE_NAME , "Looking Glass",
PW_KEY_MEDIA_TYPE , "Audio",
PW_KEY_MEDIA_CATEGORY, "Playback",
PW_KEY_MEDIA_ROLE , "Music",
PW_KEY_NODE_LATENCY , requestedNodeLatency,
NULL
);
const char * device = option_get_string("pipewire", "outDevice");
if (device)
{
#ifdef PW_KEY_TARGET_OBJECT
pw_properties_set(props, PW_KEY_TARGET_OBJECT, device);
#else
pw_properties_set(props, PW_KEY_NODE_TARGET, device);
#endif
}
pw.playback.stream = pw_stream_new_simple(
pw.loop,
"Looking Glass",
props,
&events,
NULL
);
// The user can override the default node latency with the PIPEWIRE_LATENCY
// environment variable, so get the actual node latency value from the stream.
// The actual quantum size may be lower than this value depending on what else
// is using the audio device, but we can treat this value as a maximum
const struct pw_properties * properties =
pw_stream_get_properties(pw.playback.stream);
const char *actualNodeLatency =
pw_properties_get(properties, PW_KEY_NODE_LATENCY);
DEBUG_ASSERT(actualNodeLatency != NULL);
unsigned num, denom;
if (sscanf(actualNodeLatency, "%u/%u", &num, &denom) != 2 ||
denom != sampleRate)
{
DEBUG_WARN(
"PIPEWIRE_LATENCY value '%s' is invalid or does not match stream sample "
"rate; using %d/%d", actualNodeLatency, requestedPeriodFrames,
sampleRate);
struct spa_dict_item items[] = {
{ PW_KEY_NODE_LATENCY, requestedNodeLatency }
};
pw_stream_update_properties(pw.playback.stream,
&SPA_DICT_INIT_ARRAY(items));
pw.playback.maxPeriodFrames = requestedPeriodFrames;
}
else
pw.playback.maxPeriodFrames = num;
// If the previous quantum size was very small, PipeWire can request two full
// periods almost immediately at the start of playback
pw.playback.startFrames = pw.playback.maxPeriodFrames * 2;
*maxPeriodFrames = pw.playback.maxPeriodFrames;
*startFrames = pw.playback.startFrames;
if (!pw.playback.stream)
{
pw_thread_loop_unlock(pw.thread);
DEBUG_ERROR("Failed to create the stream");
return;
}
params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
&SPA_AUDIO_INFO_RAW_INIT(
.format = SPA_AUDIO_FORMAT_F32,
.channels = channels,
.rate = sampleRate
));
pw_stream_connect(
pw.playback.stream,
PW_DIRECTION_OUTPUT,
PW_ID_ANY,
PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS |
PW_STREAM_FLAG_INACTIVE,
params, 1);
pw_thread_loop_unlock(pw.thread);
}
static void pipewire_playbackStart(void)
{
if (!pw.playback.stream)
return;
if (pw.playback.state != STREAM_STATE_ACTIVE)
{
pw_thread_loop_lock(pw.thread);
switch (pw.playback.state)
{
case STREAM_STATE_INACTIVE:
pw_stream_set_active(pw.playback.stream, true);
pw.playback.state = STREAM_STATE_ACTIVE;
break;
case STREAM_STATE_DRAINING:
// We are in the middle of draining the PipeWire buffers; we need to
// wait for this to complete before allowing the new playback to start
break;
default:
DEBUG_UNREACHABLE();
}
pw_thread_loop_unlock(pw.thread);
}
}
static void pipewire_playbackStop(void)
{
if (pw.playback.state != STREAM_STATE_ACTIVE)
return;
pw_thread_loop_lock(pw.thread);
pw_stream_flush(pw.playback.stream, true);
pw.playback.state = STREAM_STATE_DRAINING;
pw_thread_loop_unlock(pw.thread);
}
static void pipewire_playbackVolume(int channels, const uint16_t volume[])
{
if (channels != pw.playback.channels)
return;
float param[channels];
for(int i = 0; i < channels; ++i)
param[i] = 9.3234e-7 * pow(1.000211902, volume[i]) - 0.000172787;
pw_thread_loop_lock(pw.thread);
pw_stream_set_control(pw.playback.stream, SPA_PROP_channelVolumes,
channels, param, 0);
pw_thread_loop_unlock(pw.thread);
}
static void pipewire_playbackMute(bool mute)
{
pw_thread_loop_lock(pw.thread);
float val = mute ? 1.0f : 0.0f;
pw_stream_set_control(pw.playback.stream, SPA_PROP_mute, 1, &val, 0);
pw_thread_loop_unlock(pw.thread);
}
static uint64_t pipewire_playbackLatency(void)
{
#if PW_CHECK_VERSION(0, 3, 50)
if (pw.playback.time.rate.num == 0)
return 0;
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
// diff in ns
int64_t diff = SPA_TIMESPEC_TO_NSEC(&ts) - pw.playback.time.now;
// elapsed frames
int64_t elapsed =
(pw.playback.time.rate.denom * diff) /
(pw.playback.time.rate.num * SPA_NSEC_PER_SEC);
const int64_t buffered = pw.playback.time.buffered + pw.playback.time.queued;
int64_t latency = (buffered * 1000 / pw.playback.sampleRate) +
((pw.playback.time.delay - elapsed) * 1000 *
pw.playback.time.rate.num / pw.playback.time.rate.denom);
return max(0, -latency);
#else
return pw.playback.time.delay + pw.playback.time.queued / pw.playback.stride;
#endif
}
static void pipewire_recordStopStream(void)
{
if (!pw.record.stream)
return;
pw_thread_loop_lock(pw.thread);
pw_stream_destroy(pw.record.stream);
pw.record.stream = NULL;
pw_thread_loop_unlock(pw.thread);
}
static void pipewire_onRecordProcess(void * userdata)
{
struct pw_buffer * pbuf;
if (!(pbuf = pw_stream_dequeue_buffer(pw.record.stream)))
{
DEBUG_WARN("out of buffers");
return;
}
struct spa_buffer * sbuf = pbuf->buffer;
uint8_t * dst;
if (!(dst = sbuf->datas[0].data))
return;
dst += sbuf->datas[0].chunk->offset;
pw.record.pushFn(dst,
min(
sbuf->datas[0].chunk->size,
sbuf->datas[0].maxsize - sbuf->datas[0].chunk->offset) / pw.record.stride
);
pw_stream_queue_buffer(pw.record.stream, pbuf);
}
static void pipewire_recordStart(int channels, int sampleRate,
LG_AudioPushFn pushFn)
{
const struct spa_pod * params[1];
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
static const struct pw_stream_events events =
{
.version = PW_VERSION_STREAM_EVENTS,
.process = pipewire_onRecordProcess
};
if (pw.record.stream &&
pw.record.channels == channels &&
pw.record.sampleRate == sampleRate)
{
if (!pw.record.active)
{
pw_thread_loop_lock(pw.thread);
pw_stream_set_active(pw.record.stream, true);
pw.record.active = true;
pw_thread_loop_unlock(pw.thread);
}
return;
}
pipewire_recordStopStream();
pw.record.channels = channels;
pw.record.sampleRate = sampleRate;
pw.record.stride = sizeof(uint16_t) * channels;
pw.record.pushFn = pushFn;
struct pw_properties * props =
pw_properties_new(
PW_KEY_NODE_NAME , "Looking Glass",
PW_KEY_MEDIA_TYPE , "Audio",
PW_KEY_MEDIA_CATEGORY, "Capture",
PW_KEY_MEDIA_ROLE , "Music",
NULL
);
const char * device = option_get_string("pipewire", "recDevice");
if (device)
{
#ifdef PW_KEY_TARGET_OBJECT
pw_properties_set(props, PW_KEY_TARGET_OBJECT, device);
#else
pw_properties_set(props, PW_KEY_NODE_TARGET, device);
#endif
}
pw_thread_loop_lock(pw.thread);
pw.record.stream = pw_stream_new_simple(
pw.loop,
"Looking Glass",
props,
&events,
NULL
);
if (!pw.record.stream)
{
pw_thread_loop_unlock(pw.thread);
DEBUG_ERROR("Failed to create the stream");
return;
}
params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
&SPA_AUDIO_INFO_RAW_INIT(
.format = SPA_AUDIO_FORMAT_S16,
.channels = channels,
.rate = sampleRate
));
pw_stream_connect(
pw.record.stream,
PW_DIRECTION_INPUT,
PW_ID_ANY,
PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS,
params, 1);
pw_thread_loop_unlock(pw.thread);
pw.record.active = true;
}
static void pipewire_recordStop(void)
{
if (!pw.record.active)
return;
pw_thread_loop_lock(pw.thread);
pw_stream_set_active(pw.record.stream, false);
pw.record.active = false;
pw_thread_loop_unlock(pw.thread);
}
static void pipewire_recordVolume(int channels, const uint16_t volume[])
{
if (channels != pw.record.channels)
return;
float param[channels];
for(int i = 0; i < channels; ++i)
param[i] = 9.3234e-7 * pow(1.000211902, volume[i]) - 0.000172787;
pw_thread_loop_lock(pw.thread);
pw_stream_set_control(pw.record.stream, SPA_PROP_channelVolumes,
channels, param, 0);
pw_thread_loop_unlock(pw.thread);
}
static void pipewire_recordMute(bool mute)
{
pw_thread_loop_lock(pw.thread);
float val = mute ? 1.0f : 0.0f;
pw_stream_set_control(pw.record.stream, SPA_PROP_mute, 1, &val, 0);
pw_thread_loop_unlock(pw.thread);
}
static void pipewire_free(void)
{
pipewire_playbackStopStream();
pipewire_recordStopStream();
pw_thread_loop_stop(pw.thread);
pw_thread_loop_destroy(pw.thread);
pw_context_destroy(pw.context);
pw_loop_destroy(pw.loop);
pw.loop = NULL;
pw.context = NULL;
pw.thread = NULL;
pw_deinit();
}
struct LG_AudioDevOps LGAD_PipeWire =
{
.name = "PipeWire",
.earlyInit = pipewire_earlyInit,
.init = pipewire_init,
.free = pipewire_free,
.playback =
{
.setup = pipewire_playbackSetup,
.start = pipewire_playbackStart,
.stop = pipewire_playbackStop,
.volume = pipewire_playbackVolume,
.mute = pipewire_playbackMute,
.latency = pipewire_playbackLatency
},
.record =
{
.start = pipewire_recordStart,
.stop = pipewire_recordStop,
.volume = pipewire_recordVolume,
.mute = pipewire_recordMute
}
};

View File

@@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.10)
project(audiodev_PulseAudio LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(AUDIODEV_PulseAudio REQUIRED IMPORTED_TARGET
libpulse
)
add_library(audiodev_PulseAudio STATIC
pulseaudio.c
)
target_link_libraries(audiodev_PulseAudio
PkgConfig::AUDIODEV_PulseAudio
lg_common
)
target_include_directories(audiodev_PulseAudio
PRIVATE
src
)

View File

@@ -0,0 +1,393 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "interface/audiodev.h"
#include <pulse/pulseaudio.h>
#include <string.h>
#include <math.h>
#include "common/debug.h"
struct PulseAudio
{
pa_threaded_mainloop * loop;
pa_mainloop_api * api;
pa_context * context;
pa_operation * contextSub;
pa_stream * sink;
int sinkIndex;
bool sinkCorked;
bool sinkMuted;
bool sinkStarting;
int sinkMaxPeriodFrames;
int sinkStartFrames;
int sinkSampleRate;
int sinkChannels;
int sinkStride;
LG_AudioPullFn sinkPullFn;
};
static struct PulseAudio pa = {0};
static void pulseaudio_sink_input_cb(pa_context *c, const pa_sink_input_info *i,
int eol, void *userdata)
{
if (eol < 0 || eol == 1)
return;
pa.sinkIndex = i->index;
}
static void pulseaudio_subscribe_cb(pa_context *c,
pa_subscription_event_type_t t, uint32_t index, void *userdata)
{
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)
{
case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
pa.sinkIndex = 0;
else
{
pa_operation *o = pa_context_get_sink_input_info(c, index,
pulseaudio_sink_input_cb, NULL);
pa_operation_unref(o);
}
break;
}
}
static void pulseaudio_ctx_state_change_cb(pa_context * c, void * userdata)
{
switch (pa_context_get_state(c))
{
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
break;
case PA_CONTEXT_READY:
DEBUG_INFO("Connected to PulseAudio server");
pa_context_set_subscribe_callback(c, pulseaudio_subscribe_cb, NULL);
pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL);
pa_threaded_mainloop_signal(pa.loop, 0);
break;
case PA_CONTEXT_TERMINATED:
if (pa.contextSub)
{
pa_operation_unref(pa.contextSub);
pa.contextSub = NULL;
}
break;
case PA_CONTEXT_FAILED:
default:
DEBUG_ERROR("context error: %s", pa_strerror(pa_context_errno(c)));
break;
}
}
static bool pulseaudio_init(void)
{
pa.loop = pa_threaded_mainloop_new();
if (!pa.loop)
{
DEBUG_ERROR("Failed to create the main loop");
goto err;
}
pa.api = pa_threaded_mainloop_get_api(pa.loop);
if (pa_signal_init(pa.api) != 0)
{
DEBUG_ERROR("Failed to init signals");
goto err_loop;
}
if (pa_threaded_mainloop_start(pa.loop) < 0)
{
DEBUG_ERROR("Failed to start the main loop");
goto err_loop;
}
pa_proplist * propList = pa_proplist_new();
if (!propList)
{
DEBUG_ERROR("Failed to create the proplist");
goto err_thread;
}
pa_proplist_sets(propList, PA_PROP_MEDIA_ROLE, "video");
pa_threaded_mainloop_lock(pa.loop);
pa.context = pa_context_new_with_proplist(
pa.api,
"Looking Glass",
propList);
if (!pa.context)
{
DEBUG_ERROR("Failed to create the context");
goto err_context;
}
pa_context_set_state_callback(pa.context,
pulseaudio_ctx_state_change_cb, NULL);
if (pa_context_connect(pa.context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0)
{
DEBUG_ERROR("Failed to connect to the context server");
goto err_context;
}
for(;;)
{
pa_context_state_t state = pa_context_get_state(pa.context);
if(!PA_CONTEXT_IS_GOOD(state))
{
DEBUG_ERROR("Context is bad");
goto err_context;
}
if (state == PA_CONTEXT_READY)
break;
pa_threaded_mainloop_wait(pa.loop);
}
pa_threaded_mainloop_unlock(pa.loop);
pa_proplist_free(propList);
return true;
err_context:
pa_threaded_mainloop_unlock(pa.loop);
pa_proplist_free(propList);
err_thread:
pa_threaded_mainloop_stop(pa.loop);
err_loop:
pa_threaded_mainloop_free(pa.loop);
err:
return false;
}
static void pulseaudio_sink_close_nl(void)
{
if (!pa.sink)
return;
pa_stream_set_write_callback(pa.sink, NULL, NULL);
pa_stream_flush(pa.sink, NULL, NULL);
pa_stream_unref(pa.sink);
pa.sink = NULL;
}
static void pulseaudio_free(void)
{
pa_threaded_mainloop_lock(pa.loop);
pulseaudio_sink_close_nl();
pa_context_set_state_callback(pa.context, NULL, NULL);
pa_context_set_subscribe_callback(pa.context, NULL, NULL);
pa_context_disconnect(pa.context);
pa_context_unref(pa.context);
if (pa.contextSub)
{
pa_operation_unref(pa.contextSub);
pa.contextSub = NULL;
}
pa_threaded_mainloop_unlock(pa.loop);
}
static void pulseaudio_state_cb(pa_stream * p, void * userdata)
{
if (pa.sinkStarting && pa_stream_get_state(pa.sink) == PA_STREAM_READY)
{
pa_stream_cork(pa.sink, 0, NULL, NULL);
pa.sinkCorked = false;
pa.sinkStarting = false;
}
}
static void pulseaudio_write_cb(pa_stream * p, size_t nbytes, void * userdata)
{
// PulseAudio tries to pull data from the stream as soon as it is created for
// some reason, even though it is corked
if (pa.sinkCorked)
return;
uint8_t * dst;
pa_stream_begin_write(p, (void **)&dst, &nbytes);
int frames = nbytes / pa.sinkStride;
frames = pa.sinkPullFn(dst, frames);
pa_stream_write(p, dst, frames * pa.sinkStride, NULL, 0, PA_SEEK_RELATIVE);
}
static void pulseaudio_underflow_cb(pa_stream * p, void * userdata)
{
DEBUG_WARN("Underflow");
}
static void pulseaudio_overflow_cb(pa_stream * p, void * userdata)
{
DEBUG_WARN("Overflow");
}
static void pulseaudio_setup(int channels, int sampleRate,
int requestedPeriodFrames, int * maxPeriodFrames, int * startFrames,
LG_AudioPullFn pullFn)
{
if (pa.sink && pa.sinkChannels == channels && pa.sinkSampleRate == sampleRate)
{
*maxPeriodFrames = pa.sinkMaxPeriodFrames;
*startFrames = pa.sinkStartFrames;
return;
}
pa_sample_spec spec = {
.format = PA_SAMPLE_FLOAT32,
.rate = sampleRate,
.channels = channels
};
int stride = channels * sizeof(float);
int bufferSize = requestedPeriodFrames * 2 * stride;
pa_buffer_attr attribs =
{
.maxlength = -1,
.tlength = bufferSize,
.prebuf = 0,
.minreq = (uint32_t)-1
};
pa_threaded_mainloop_lock(pa.loop);
pulseaudio_sink_close_nl();
pa.sinkChannels = channels;
pa.sinkSampleRate = sampleRate;
pa.sink = pa_stream_new(pa.context, "Looking Glass", &spec, NULL);
pa_stream_set_state_callback (pa.sink, pulseaudio_state_cb , NULL);
pa_stream_set_write_callback (pa.sink, pulseaudio_write_cb , NULL);
pa_stream_set_underflow_callback(pa.sink, pulseaudio_underflow_cb, NULL);
pa_stream_set_overflow_callback (pa.sink, pulseaudio_overflow_cb , NULL);
pa_stream_connect_playback(pa.sink, NULL, &attribs, PA_STREAM_START_CORKED,
NULL, NULL);
pa.sinkStride = stride;
pa.sinkPullFn = pullFn;
pa.sinkMaxPeriodFrames = requestedPeriodFrames;
pa.sinkCorked = true;
pa.sinkStarting = false;
// If something else is, or was recently using a small latency value,
// PulseAudio can request way more data at startup than is reasonable
pa.sinkStartFrames = requestedPeriodFrames * 4;
*maxPeriodFrames = requestedPeriodFrames;
*startFrames = pa.sinkStartFrames;
pa_threaded_mainloop_unlock(pa.loop);
}
static void pulseaudio_start(void)
{
if (!pa.sink)
return;
pa_threaded_mainloop_lock(pa.loop);
pa_stream_state_t state = pa_stream_get_state(pa.sink);
if (state == PA_STREAM_CREATING)
pa.sinkStarting = true;
else
{
pa_stream_cork(pa.sink, 0, NULL, NULL);
pa.sinkCorked = false;
}
pa_threaded_mainloop_unlock(pa.loop);
}
static void pulseaudio_stop(void)
{
if (!pa.sink)
return;
bool needLock = !pa_threaded_mainloop_in_thread(pa.loop);
if (needLock)
pa_threaded_mainloop_lock(pa.loop);
pa_stream_cork(pa.sink, 1, NULL, NULL);
pa.sinkCorked = true;
pa.sinkStarting = false;
if (needLock)
pa_threaded_mainloop_unlock(pa.loop);
}
static void pulseaudio_volume(int channels, const uint16_t volume[])
{
if (!pa.sink || !pa.sinkIndex)
return;
struct pa_cvolume v = { .channels = channels };
for(int i = 0; i < channels; ++i)
v.values[i] = pa_sw_volume_from_linear(
9.3234e-7 * pow(1.000211902, volume[i]) - 0.000172787);
pa_threaded_mainloop_lock(pa.loop);
pa_context_set_sink_input_volume(pa.context, pa.sinkIndex, &v, NULL, NULL);
pa_threaded_mainloop_unlock(pa.loop);
}
static void pulseaudio_mute(bool mute)
{
if (!pa.sink || !pa.sinkIndex || pa.sinkMuted == mute)
return;
pa.sinkMuted = mute;
pa_threaded_mainloop_lock(pa.loop);
pa_context_set_sink_input_mute(pa.context, pa.sinkIndex, mute, NULL, NULL);
pa_threaded_mainloop_unlock(pa.loop);
}
struct LG_AudioDevOps LGAD_PulseAudio =
{
.name = "PulseAudio",
.init = pulseaudio_init,
.free = pulseaudio_free,
.playback =
{
.setup = pulseaudio_setup,
.start = pulseaudio_start,
.stop = pulseaudio_stop,
.volume = pulseaudio_volume,
.mute = pulseaudio_mute
}
};

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.10)
project(displayservers LANGUAGES C)
set(DISPLAYSERVER_H "${CMAKE_BINARY_DIR}/include/dynamic/displayservers.h")
@@ -18,6 +18,16 @@ function(add_displayserver name)
add_subdirectory(${name})
endfunction()
option(ENABLE_X11 "Build with X11 support" ON)
add_feature_info(ENABLE_X11 ENABLE_X11 "X11 support.")
option(ENABLE_WAYLAND "Build with Wayland support" ON)
add_feature_info(ENABLE_WAYLAND ENABLE_WAYLAND "Wayland support.")
if (NOT ENABLE_X11 AND NOT ENABLE_WAYLAND)
message(FATAL_ERROR "Either ENABLE_X11 or ENABLE_WAYLAND must be on")
endif()
# Add/remove displayservers here!
if (ENABLE_WAYLAND)
add_displayserver(Wayland)

View File

@@ -1,98 +1,42 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.10)
project(displayserver_Wayland LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(DISPLAYSERVER_Wayland REQUIRED IMPORTED_TARGET
wayland-client
wayland-cursor
xkbcommon
)
set(DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES "")
set(displayserver_Wayland_SHELL_SRC "")
if (ENABLE_LIBDECOR)
pkg_check_modules(DISPLAYSERVER_Wayland_LIBDECOR REQUIRED IMPORTED_TARGET
libdecor-0
)
list(APPEND DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES PkgConfig::DISPLAYSERVER_Wayland_LIBDECOR)
list(APPEND displayserver_Wayland_SHELL_SRC shell_libdecor.c)
add_compile_definitions(ENABLE_LIBDECOR)
else()
list(APPEND displayserver_Wayland_SHELL_SRC shell_xdg.c)
endif()
add_library(displayserver_Wayland STATIC
clipboard.c
cursor.c
gl.c
idle.c
input.c
output.c
poll.c
presentation.c
state.c
registry.c
wayland.c
window.c
${displayserver_Wayland_SHELL_SRC}
)
target_link_libraries(displayserver_Wayland
PkgConfig::DISPLAYSERVER_Wayland
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES}
lg_common
)
target_include_directories(displayserver_Wayland
PRIVATE
src
wayland-client
wayland-cursor
xkbcommon
)
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols>=1.15)
pkg_get_variable(WAYLAND_PROTOCOLS_BASE wayland-protocols pkgdatadir)
macro(wayland_generate protocol_file output_file)
add_custom_command(OUTPUT "${output_file}.h"
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" client-header "${protocol_file}" "${output_file}.h"
DEPENDS "${protocol_file}"
VERBATIM)
add_library(displayserver_Wayland STATIC
activation.c
clipboard.c
cursor.c
gl.c
idle.c
input.c
output.c
poll.c
presentation.c
state.c
registry.c
wayland.c
window.c
)
add_custom_command(OUTPUT "${output_file}.c"
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${protocol_file}" "${output_file}.c"
DEPENDS "${protocol_file}"
VERBATIM)
add_subdirectory(protocol)
add_subdirectory(desktops)
target_sources(displayserver_Wayland PRIVATE "${output_file}.h" "${output_file}.c")
endmacro()
target_link_libraries(displayserver_Wayland
PkgConfig::DISPLAYSERVER_Wayland
lg_common
wayland_protocol
wayland_desktops
)
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/wayland")
include_directories("${CMAKE_BINARY_DIR}/wayland")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/stable/presentation-time/presentation-time.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-presentation-time-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/stable/viewporter/viewporter.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-viewporter-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-decoration-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-relative-pointer-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-pointer-constraints-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-idle-inhibit-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-output/xdg-output-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-output-unstable-v1-client-protocol")
target_include_directories(displayserver_Wayland
PRIVATE
.
)

View File

@@ -0,0 +1,71 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"
#include <stdbool.h>
#include <wayland-client.h>
#include "common/debug.h"
bool waylandActivationInit(void)
{
if (!wlWm.xdgActivation)
DEBUG_WARN("xdg_activation_v1 not exported by compositor, will not be able "
"to request host focus on behalf of guest applications");
return true;
}
void waylandActivationFree(void)
{
if (wlWm.xdgActivation)
{
xdg_activation_v1_destroy(wlWm.xdgActivation);
}
}
static void activationTokenDone(void * data,
struct xdg_activation_token_v1 * xdgToken, const char * token)
{
xdg_activation_v1_activate(wlWm.xdgActivation, token, wlWm.surface);
xdg_activation_token_v1_destroy(xdgToken);
}
static const struct xdg_activation_token_v1_listener activationTokenListener = {
.done = &activationTokenDone,
};
void waylandActivationRequestActivation(void)
{
if (!wlWm.xdgActivation) return;
struct xdg_activation_token_v1 * token =
xdg_activation_v1_get_activation_token(wlWm.xdgActivation);
if (!token)
{
DEBUG_ERROR("failed to retrieve XDG activation token");
return;
}
xdg_activation_token_v1_add_listener(token, &activationTokenListener, NULL);
xdg_activation_token_v1_set_surface(token, wlWm.surface);
xdg_activation_token_v1_commit(token);
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -376,7 +376,7 @@ static void clipboardReadCancel(struct ClipboardRead * data)
static void clipboardReadCallback(uint32_t events, void * opaque)
{
struct ClipboardRead * data = opaque;
if (events & EPOLLERR)
if (events & EPOLLERR)
{
clipboardReadCancel(data);
return;
@@ -475,6 +475,7 @@ void waylandCBRequest(LG_ClipboardData type)
close(data->fd);
free(data->buf);
free(data);
return;
}
wlCb.currentRead = data;
@@ -512,6 +513,13 @@ error:
free(data);
}
static void dataSourceHandleTarget(void * data, struct wl_data_source * source,
const char * mimetype)
{
// Certain Wayland clients send this for copy-paste operations even though
// it only makes sense for drag-and-drop. We just do nothing.
}
static void dataSourceHandleSend(void * data, struct wl_data_source * source,
const char * mimetype, int fd)
{
@@ -547,7 +555,8 @@ static void dataSourceHandleCancelled(void * data,
}
static const struct wl_data_source_listener dataSourceListener = {
.send = dataSourceHandleSend,
.target = dataSourceHandleTarget,
.send = dataSourceHandleSend,
.cancelled = dataSourceHandleCancelled,
};

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -0,0 +1,58 @@
cmake_minimum_required(VERSION 3.10)
project(wayland_desktops LANGUAGES C)
set(DESKTOP_H "${CMAKE_BINARY_DIR}/include/dynamic/wayland_desktops.h")
set(DESKTOP_C "${CMAKE_BINARY_DIR}/src/wayland_desktops.c")
file(WRITE ${DESKTOP_H} "#include \"interface/desktop.h\"\n\n")
file(APPEND ${DESKTOP_H} "extern struct WL_DesktopOps * WL_Desktops[];\n\n")
file(WRITE ${DESKTOP_C} "#include \"interface/desktop.h\"\n\n")
file(APPEND ${DESKTOP_C} "#include <stddef.h>\n\n")
set(DESKTOPS "_")
set(DESKTOPS_LINK "_")
function(add_desktop name)
set(DESKTOPS "${DESKTOPS};${name}" PARENT_SCOPE)
set(DESKTOPS_LINK "${DESKTOPS_LINK};wayland_desktop_${name}" PARENT_SCOPE)
add_subdirectory(${name})
endfunction()
# Add/remove desktops here!
# the first entry here is the default
add_desktop(xdg)
pkg_check_modules(LIBDECOR IMPORTED_TARGET libdecor-0)
if(LIBDECOR_FOUND)
option(ENABLE_LIBDECOR "Build with libdecor support" ON)
else()
option(ENABLE_LIBDECOR "Build with libdecor support" OFF)
endif()
add_feature_info(ENABLE_LIBDECOR ENABLE_LIBDECOR "libdecor support.")
if (ENABLE_LIBDECOR)
add_desktop(libdecor)
endif()
list(REMOVE_AT DESKTOPS 0)
list(REMOVE_AT DESKTOPS_LINK 0)
list(LENGTH DESKTOPS DESKTOP_COUNT)
file(APPEND ${DESKTOP_H} "#define WL_DESKTOP_COUNT ${DESKTOP_COUNT}\n")
foreach(desktop ${DESKTOPS})
file(APPEND ${DESKTOP_C} "extern struct WL_DesktopOps WLD_${desktop};\n")
endforeach()
file(APPEND ${DESKTOP_C} "\nconst struct WL_DesktopOps * WL_Desktops[] =\n{\n")
foreach(desktop ${DESKTOPS})
file(APPEND ${DESKTOP_C} " &WLD_${desktop},\n")
endforeach()
file(APPEND ${DESKTOP_C} " NULL\n};")
add_library(wayland_desktops STATIC ${DESKTOP_C})
target_link_libraries(wayland_desktops ${DESKTOPS_LINK})
target_include_directories(wayland_desktops
PRIVATE
../
)

View File

@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.10)
project(wayland_desktop_libdecor LANGUAGES C)
add_library(wayland_desktop_libdecor STATIC
libdecor.c
)
target_link_libraries(wayland_desktop_libdecor
lg_common
wayland_protocol
PkgConfig::LIBDECOR
)
include_directories(
"../../"
)

View File

@@ -0,0 +1,281 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "interface/desktop.h"
#include "wayland-xdg-shell-client-protocol.h"
#include "wayland.h"
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <sys/epoll.h>
#include <libdecor.h>
#include <wayland-client.h>
#include "app.h"
#include "common/debug.h"
// Maximum number of fds we can process at once in waylandWait
#define MAX_EPOLL_EVENTS 10
typedef struct LibDecorState
{
bool configured;
struct libdecor * libdecor;
struct libdecor_frame * libdecorFrame;
int32_t width, height;
bool needsResize;
bool fullscreen;
bool borderless;
uint32_t resizeSerial;
}
LibDecorState;
static LibDecorState state = {0};
struct libdecor_configuration
{
uint32_t serial;
bool has_window_state;
enum libdecor_window_state window_state;
bool has_size;
int window_width;
int window_height;
};
static void libdecorHandleError(struct libdecor * context, enum libdecor_error error,
const char *message)
{
DEBUG_ERROR("Got libdecor error (%d): %s", error, message);
}
static void libdecorFrameConfigure(struct libdecor_frame * frame,
struct libdecor_configuration * configuration, void * opaque)
{
if (!state.configured)
{
xdg_surface_ack_configure(libdecor_frame_get_xdg_surface(frame), configuration->serial);
state.configured = true;
return;
}
int width, height;
if (libdecor_configuration_get_content_size(configuration, frame, &width, &height))
{
state.width = width;
state.height = height;
struct libdecor_state * s = libdecor_state_new(width, height);
libdecor_frame_commit(state.libdecorFrame, s, NULL);
libdecor_state_free(s);
}
enum libdecor_window_state windowState;
if (libdecor_configuration_get_window_state(configuration, &windowState))
state.fullscreen = windowState & LIBDECOR_WINDOW_STATE_FULLSCREEN;
state.resizeSerial = configuration->serial;
waylandNeedsResize();
}
static void libdecorFrameClose(struct libdecor_frame * frame, void * opaque)
{
app_handleCloseEvent();
}
static void libdecorFrameCommit(struct libdecor_frame * frame, void * opaque)
{
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
static struct libdecor_interface libdecorListener =
{
libdecorHandleError,
};
static struct libdecor_frame_interface libdecorFrameListener =
{
libdecorFrameConfigure,
libdecorFrameClose,
libdecorFrameCommit,
};
#pragma GCC diagnostic pop
static void libdecorCallback(uint32_t events, void * opaque)
{
libdecor_dispatch(state.libdecor, 0);
}
static bool libdecor_shellInit(
struct wl_display * display, struct wl_surface * surface,
const char * title, const char * appId, bool fullscreen,
bool maximize, bool borderless, bool resizable)
{
state.libdecor = libdecor_new(display, &libdecorListener);
state.libdecorFrame = libdecor_decorate(state.libdecor, surface,
&libdecorFrameListener, NULL);
libdecor_frame_set_app_id(state.libdecorFrame, appId);
libdecor_frame_set_title(state.libdecorFrame, title);
libdecor_frame_map(state.libdecorFrame);
if (fullscreen)
libdecor_frame_set_fullscreen(state.libdecorFrame, NULL);
if (maximize)
libdecor_frame_set_maximized(state.libdecorFrame);
if (borderless)
libdecor_frame_set_visibility(state.libdecorFrame, false);
state.borderless = borderless;
if (resizable)
libdecor_frame_set_capabilities(state.libdecorFrame,
LIBDECOR_ACTION_RESIZE);
else
libdecor_frame_unset_capabilities(state.libdecorFrame,
LIBDECOR_ACTION_RESIZE);
while (!state.configured)
libdecor_dispatch(state.libdecor, 0);
if (!waylandPollRegister(libdecor_get_fd(state.libdecor),
libdecorCallback, NULL, EPOLLIN))
{
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
return false;
}
return true;
}
static void libdecor_shellAckConfigureIfNeeded(void)
{
if (state.resizeSerial)
{
xdg_surface_ack_configure(
libdecor_frame_get_xdg_surface(state.libdecorFrame), state.resizeSerial);
state.resizeSerial = 0;
}
}
static void libdecor_setFullscreen(bool fs)
{
if (fs)
libdecor_frame_set_fullscreen(state.libdecorFrame, NULL);
else
libdecor_frame_unset_fullscreen(state.libdecorFrame);
if (!state.borderless)
libdecor_frame_set_visibility(state.libdecorFrame, !fs);
}
static bool libdecor_getFullscreen(void)
{
return state.fullscreen;
}
static void libdecor_minimize(void)
{
libdecor_frame_set_minimized(state.libdecorFrame);
}
static void libdecor_shellResize(int w, int h)
{
if (!libdecor_frame_is_floating(state.libdecorFrame))
return;
state.width = w;
state.height = h;
struct libdecor_state * s = libdecor_state_new(w, h);
libdecor_frame_commit(state.libdecorFrame, s, NULL);
libdecor_state_free(s);
waylandNeedsResize();
}
static void libdecor_setSize(int w, int h)
{
state.width = w;
state.height = h;
}
static void libdecor_getSize(int * w, int * h)
{
*w = state.width;
*h = state.height;
}
static bool libdecor_registryGlobalHandler(void * data,
struct wl_registry * registry, uint32_t name, const char * interface,
uint32_t version)
{
return false;
}
bool libdecor_pollInit(struct wl_display * display)
{
return true;
}
void libdecor_pollWait(struct wl_display * display, int epollFd,
unsigned int time)
{
libdecor_dispatch(state.libdecor, 0);
struct epoll_event events[MAX_EPOLL_EVENTS];
int count;
if ((count = epoll_wait(epollFd, events, MAX_EPOLL_EVENTS, time)) < 0)
{
if (errno != EINTR)
DEBUG_INFO("epoll failed: %s", strerror(errno));
return;
}
for (int i = 0; i < count; ++i)
{
struct WaylandPoll * poll = events[i].data.ptr;
if (!poll->removed)
poll->callback(events[i].events, poll->opaque);
}
}
WL_DesktopOps WLD_libdecor =
{
.name = "libdecor",
.compositor = "gnome-shell",
.shellInit = libdecor_shellInit,
.shellAckConfigureIfNeeded = libdecor_shellAckConfigureIfNeeded,
.setFullscreen = libdecor_setFullscreen,
.getFullscreen = libdecor_getFullscreen,
.minimize = libdecor_minimize,
.shellResize = libdecor_shellResize,
.setSize = libdecor_setSize,
.getSize = libdecor_getSize,
.registryGlobalHandler = libdecor_registryGlobalHandler,
.pollInit = libdecor_pollInit,
.pollWait = libdecor_pollWait
};

View File

@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.10)
project(wayland_desktop_xdg LANGUAGES C)
add_library(wayland_desktop_xdg STATIC
xdg.c
)
target_link_libraries(wayland_desktop_xdg
lg_common
wayland_protocol
)
include_directories(
"../../"
)

View File

@@ -0,0 +1,322 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"
#include "wayland-xdg-shell-client-protocol.h"
#include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
#include <stdbool.h>
#include <string.h>
#include <sys/epoll.h>
#include <errno.h>
#include <wayland-client.h>
#include "app.h"
#include "common/debug.h"
// Maximum number of fds we can process at once in waylandWait
#define MAX_EPOLL_EVENTS 10
typedef struct XDGState
{
bool configured;
struct xdg_wm_base * wmBase;
struct xdg_surface * surface;
struct xdg_toplevel * toplevel;
struct zxdg_decoration_manager_v1 * decorationManager;
struct zxdg_toplevel_decoration_v1 * toplevelDecoration;
int32_t width, height;
uint32_t resizeSerial;
bool fullscreen;
bool floating;
bool resizable;
int displayFd;
}
XDGState;
static XDGState state = {0};
// XDG WM base listeners.
static void xdgWmBasePing(void * data, struct xdg_wm_base * xdgWmBase, uint32_t serial)
{
xdg_wm_base_pong(xdgWmBase, serial);
}
static const struct xdg_wm_base_listener xdgWmBaseListener = {
.ping = xdgWmBasePing,
};
// XDG Surface listeners.
static void xdgSurfaceConfigure(void * data, struct xdg_surface * xdgSurface,
uint32_t serial)
{
if (state.configured)
{
state.resizeSerial = serial;
waylandNeedsResize();
}
else
{
xdg_surface_ack_configure(xdgSurface, serial);
state.configured = true;
}
}
static const struct xdg_surface_listener xdgSurfaceListener = {
.configure = xdgSurfaceConfigure,
};
// XDG Toplevel listeners.
static void xdgToplevelConfigure(void * data, struct xdg_toplevel * xdgToplevel,
int32_t width, int32_t height, struct wl_array * states)
{
state.width = width;
state.height = height;
state.fullscreen = false;
state.floating = true;
enum xdg_toplevel_state * s;
wl_array_for_each(s, states)
{
switch (*s)
{
case XDG_TOPLEVEL_STATE_FULLSCREEN:
state.fullscreen = true;
// fallthrough
case XDG_TOPLEVEL_STATE_MAXIMIZED:
case XDG_TOPLEVEL_STATE_TILED_LEFT:
case XDG_TOPLEVEL_STATE_TILED_RIGHT:
case XDG_TOPLEVEL_STATE_TILED_TOP:
case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
state.floating = false;
break;
default:
break;
}
}
}
static void xdgToplevelClose(void * data, struct xdg_toplevel * xdgToplevel)
{
app_handleCloseEvent();
}
static const struct xdg_toplevel_listener xdgToplevelListener = {
.configure = xdgToplevelConfigure,
.close = xdgToplevelClose,
};
bool xdg_shellInit(struct wl_display * display, struct wl_surface * surface,
const char * title, const char * appId, bool fullscreen, bool maximize, bool borderless,
bool resizable)
{
if (!state.wmBase)
{
DEBUG_ERROR("Compositor missing xdg_wm_base, will not proceed");
return false;
}
xdg_wm_base_add_listener(state.wmBase, &xdgWmBaseListener, NULL);
state.surface = xdg_wm_base_get_xdg_surface(state.wmBase, surface);
xdg_surface_add_listener(state.surface, &xdgSurfaceListener, NULL);
state.toplevel = xdg_surface_get_toplevel(state.surface);
xdg_toplevel_add_listener(state.toplevel, &xdgToplevelListener, NULL);
xdg_toplevel_set_title(state.toplevel, title);
xdg_toplevel_set_app_id(state.toplevel, appId);
if (fullscreen)
xdg_toplevel_set_fullscreen(state.toplevel, NULL);
if (maximize)
xdg_toplevel_set_maximized(state.toplevel);
if (!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(
state.decorationManager, state.toplevel);
if (state.toplevelDecoration)
{
zxdg_toplevel_decoration_v1_set_mode(state.toplevelDecoration,
borderless ?
ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
}
}
return true;
}
static void xdg_shellAckConfigureIfNeeded(void)
{
if (state.resizeSerial)
{
xdg_surface_ack_configure(state.surface, state.resizeSerial);
state.resizeSerial = 0;
}
}
static void xdg_setFullscreen(bool fs)
{
if (fs)
xdg_toplevel_set_fullscreen(state.toplevel, NULL);
else
xdg_toplevel_unset_fullscreen(state.toplevel);
}
static bool xdg_getFullscreen(void)
{
return state.fullscreen;
}
static void xdg_minimize(void)
{
xdg_toplevel_set_minimized(state.toplevel);
}
static void xdg_shellResize(int w, int h)
{
if (!state.floating || !state.resizable)
return;
state.width = w;
state.height = h;
xdg_surface_set_window_geometry(state.surface, 0, 0, w, h);
waylandNeedsResize();
}
static void xdg_setSize(int w, int h)
{
state.width = w;
state.height = h;
}
static void xdg_getSize(int * w, int * h)
{
*w = state.width;
*h = state.height;
}
static bool xdg_registryGlobalHandler(void * data,
struct wl_registry * registry, uint32_t name, const char * interface,
uint32_t version)
{
if (!strcmp(interface, xdg_wm_base_interface.name))
{
state.wmBase = wl_registry_bind(registry, name,
&xdg_wm_base_interface, 1);
return true;
}
if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name))
{
state.decorationManager = wl_registry_bind(registry, name,
&zxdg_decoration_manager_v1_interface, 1);
return true;
}
return false;
}
static void waylandDisplayCallback(uint32_t events, void * opaque)
{
struct wl_display * display = (struct wl_display *)opaque;
if (events & EPOLLERR)
wl_display_cancel_read(display);
else
wl_display_read_events(display);
wl_display_dispatch_pending(display);
}
static bool xdg_pollInit(struct wl_display * display)
{
state.displayFd = wl_display_get_fd(display);
if (!waylandPollRegister(state.displayFd, waylandDisplayCallback,
display, EPOLLIN))
{
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
return false;
}
return true;
}
void xdg_pollWait(struct wl_display * display, int epollFd,
unsigned int time)
{
while (wl_display_prepare_read(display))
wl_display_dispatch_pending(display);
wl_display_flush(display);
struct epoll_event events[MAX_EPOLL_EVENTS];
int count;
if ((count = epoll_wait(epollFd, events, MAX_EPOLL_EVENTS, time)) < 0)
{
if (errno != EINTR)
DEBUG_INFO("epoll failed: %s", strerror(errno));
wl_display_cancel_read(display);
return;
}
bool sawDisplay = false;
for (int i = 0; i < count; ++i) {
struct WaylandPoll * poll = events[i].data.ptr;
if (!poll->removed)
poll->callback(events[i].events, poll->opaque);
if (poll->fd == state.displayFd)
sawDisplay = true;
}
if (!sawDisplay)
wl_display_cancel_read(display);
}
WL_DesktopOps WLD_xdg =
{
.name = "xdg",
.compositor = "",
.shellInit = xdg_shellInit,
.shellAckConfigureIfNeeded = xdg_shellAckConfigureIfNeeded,
.setFullscreen = xdg_setFullscreen,
.getFullscreen = xdg_getFullscreen,
.minimize = xdg_minimize,
.shellResize = xdg_shellResize,
.setSize = xdg_setSize,
.getSize = xdg_getSize,
.registryGlobalHandler = xdg_registryGlobalHandler,
.pollInit = xdg_pollInit,
.pollWait = xdg_pollWait
};

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -88,16 +88,26 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
if (wlWm.needsResize)
{
wl_egl_window_resize(wlWm.eglWindow, wl_fixed_to_int(wlWm.width * wlWm.scale),
wl_fixed_to_int(wlWm.height * wlWm.scale), 0, 0);
bool skipResize = false;
if (wlWm.fractionalScale)
int width, height;
wlWm.desktop->getSize(&width, &height);
wl_egl_window_resize(wlWm.eglWindow, wl_fixed_to_int(width * wlWm.scale),
wl_fixed_to_int(height * wlWm.scale), 0, 0);
if (width == 0 || height == 0)
skipResize = true;
else if (wlWm.fractionalScale)
{
wl_surface_set_buffer_scale(wlWm.surface, 1);
if (!wlWm.viewport)
wlWm.viewport = wp_viewporter_get_viewport(wlWm.viewporter, wlWm.surface);
wp_viewport_set_source(wlWm.viewport, 0, 0, wlWm.width * wlWm.scale, wlWm.height * wlWm.scale);
wp_viewport_set_destination(wlWm.viewport, wlWm.width, wlWm.height);
wp_viewport_set_source(
wlWm.viewport,
wl_fixed_from_int(-1), wl_fixed_from_int(-1),
wl_fixed_from_int(-1), wl_fixed_from_int(-1)
);
wp_viewport_set_destination(wlWm.viewport, width, height);
}
else
{
@@ -117,18 +127,18 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
}
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
wl_region_add(region, 0, 0, wlWm.width, wlWm.height);
wl_region_add(region, 0, 0, width, height);
wl_surface_set_opaque_region(wlWm.surface, region);
wl_region_destroy(region);
app_handleResizeEvent(wlWm.width, wlWm.height, wl_fixed_to_double(wlWm.scale),
app_handleResizeEvent(width, height, wl_fixed_to_double(wlWm.scale),
(struct Border) {0, 0, 0, 0});
app_invalidateWindow(true);
waylandStopWaitFrame();
wlWm.needsResize = false;
wlWm.needsResize = skipResize;
}
waylandShellAckConfigureIfNeeded();
wlWm.desktop->shellAckConfigureIfNeeded();
}
#endif

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -209,6 +209,17 @@ done:
close(fd);
}
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);
if (sym == XKB_KEY_NoSymbol)
return 0;
sym = xkb_keysym_to_upper(sym);
return xkb_keysym_to_utf32(sym);
}
static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
uint32_t serial, struct wl_surface * surface, struct wl_array * keys)
{
@@ -454,7 +465,7 @@ void waylandGrabPointer(void)
INTERLOCKED_SECTION(wlWm.confineLock,
{
if (!wlWm.confinedPointer)
if (!wlWm.confinedPointer && !wlWm.lockedPointer)
{
wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,
@@ -579,10 +590,13 @@ void waylandWarpPointer(int x, int y, bool exiting)
return;
}
int width, height;
wlWm.desktop->getSize(&width, &height);
if (x < 0) x = 0;
else if (x >= wlWm.width) x = wlWm.width - 1;
else if (x >= width) x = width - 1;
if (y < 0) y = 0;
else if (y >= wlWm.height) y = wlWm.height - 1;
else if (y >= height) y = height - 1;
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
wl_region_add(region, x, y, 1, 1);
@@ -616,7 +630,10 @@ void waylandRealignPointer(void)
void waylandGuestPointerUpdated(double x, double y, double localX, double localY)
{
if (!wlWm.warpSupport || !wlWm.pointerInSurface || wlWm.lockedPointer)
if ( !wlWm.pointer ||
!wlWm.warpSupport ||
!wlWm.pointerInSurface ||
wlWm.lockedPointer )
return;
waylandWarpPointer((int) localX, (int) localY, false);

View File

@@ -0,0 +1,64 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_WAYLAND_DESKTOP_H_
#define _H_WAYLAND_DESKTOP_H_
#include <stdbool.h>
#include <wayland-client.h>
typedef struct WL_DesktopOps
{
// the friendly name
const char * name;
// the compositor process name to match
const char * compositor;
bool (*shellInit)(
struct wl_display * display, struct wl_surface * surface,
const char * title, const char * appId, bool fullscreen, bool maximize,
bool borderless, bool resizable);
void (*shellAckConfigureIfNeeded)(void);
void (*setFullscreen)(bool fs);
bool (*getFullscreen)(void);
void (*minimize)(void);
void (*shellResize)(int w, int h);
void (*setSize)(int w, int h);
void (*getSize)(int * w, int * h);
bool (*registryGlobalHandler)(
void * data, struct wl_registry * registry,
uint32_t name, const char * interface, uint32_t version);
bool (*pollInit)(struct wl_display * display);
void (*pollWait)(struct wl_display * display, int epollFd, unsigned int time);
}
WL_DesktopOps;
#endif

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -30,23 +30,6 @@
#include "common/debug.h"
#include "common/locking.h"
#ifdef ENABLE_LIBDECOR
#include <libdecor.h>
#endif
#define EPOLL_EVENTS 10 // Maximum number of fds we can process at once in waylandWait
#ifndef ENABLE_LIBDECOR
static void waylandDisplayCallback(uint32_t events, void * opaque)
{
if (events & EPOLLERR)
wl_display_cancel_read(wlWm.display);
else
wl_display_read_events(wlWm.display);
wl_display_dispatch_pending(wlWm.display);
}
#endif
bool waylandPollInit(void)
{
wlWm.epollFd = epoll_create1(EPOLL_CLOEXEC);
@@ -61,58 +44,12 @@ bool waylandPollInit(void)
LG_LOCK_INIT(wlWm.pollLock);
LG_LOCK_INIT(wlWm.pollFreeLock);
#ifndef ENABLE_LIBDECOR
wlWm.displayFd = wl_display_get_fd(wlWm.display);
if (!waylandPollRegister(wlWm.displayFd, waylandDisplayCallback, NULL, EPOLLIN))
{
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
return false;
}
#endif
return true;
return wlWm.desktop->pollInit(wlWm.display);
}
void waylandWait(unsigned int time)
{
#ifdef ENABLE_LIBDECOR
libdecor_dispatch(wlWm.libdecor, 0);
#else
while (wl_display_prepare_read(wlWm.display))
wl_display_dispatch_pending(wlWm.display);
wl_display_flush(wlWm.display);
#endif
struct epoll_event events[EPOLL_EVENTS];
int count;
if ((count = epoll_wait(wlWm.epollFd, events, EPOLL_EVENTS, time)) < 0)
{
if (errno != EINTR)
DEBUG_INFO("epoll failed: %s", strerror(errno));
#ifndef ENABLE_LIBDECOR
wl_display_cancel_read(wlWm.display);
#endif
return;
}
#ifndef ENABLE_LIBDECOR
bool sawDisplay = false;
#endif
for (int i = 0; i < count; ++i) {
struct WaylandPoll * poll = events[i].data.ptr;
if (!poll->removed)
poll->callback(events[i].events, poll->opaque);
#ifndef ENABLE_LIBDECOR
if (poll->fd == wlWm.displayFd)
sawDisplay = true;
#endif
}
#ifndef ENABLE_LIBDECOR
if (!sawDisplay)
wl_display_cancel_read(wlWm.display);
#endif
wlWm.desktop->pollWait(wlWm.display, wlWm.epollFd, time);
INTERLOCKED_SECTION(wlWm.pollFreeLock,
{
struct WaylandPoll * node;

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -86,7 +86,8 @@ bool waylandPresentationInit(void)
if (wlWm.presentation)
{
wlWm.photonTimings = ringbuffer_new(256, sizeof(float));
wlWm.photonGraph = app_registerGraph("PHOTON", wlWm.photonTimings, 0.0f, 30.0f);
wlWm.photonGraph = app_registerGraph("PHOTON", wlWm.photonTimings,
0.0f, 30.0f, NULL);
wp_presentation_add_listener(wlWm.presentation, &presentationListener, NULL);
}
return true;
@@ -108,10 +109,17 @@ void waylandPresentationFrame(void)
return;
struct FrameData * data = malloc(sizeof(*data));
if (!data)
{
DEBUG_ERROR("out of memory");
return;
}
if (clock_gettime(wlWm.clkId, &data->sent))
{
DEBUG_ERROR("clock_gettime failed: %s\n", strerror(errno));
free(data);
return;
}
struct wp_presentation_feedback * feedback = wp_presentation_feedback(wlWm.presentation, wlWm.surface);

View File

@@ -0,0 +1,71 @@
cmake_minimum_required(VERSION 3.10)
project(wayland_protocol LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(WAYLAND REQUIRED IMPORTED_TARGET
wayland-client
wayland-cursor
xkbcommon
)
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
add_library(wayland_protocol STATIC
)
macro(wayland_generate protocol_file output_file)
add_custom_command(OUTPUT "${output_file}.h"
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" client-header "${protocol_file}" "${output_file}.h"
DEPENDS "${protocol_file}"
VERBATIM)
add_custom_command(OUTPUT "${output_file}.c"
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${protocol_file}" "${output_file}.c"
DEPENDS "${protocol_file}"
VERBATIM)
target_sources(wayland_protocol PRIVATE "${output_file}.h" "${output_file}.c")
endmacro()
set(WAYLAND_PROTOCOLS_BASE "${PROJECT_TOP}/repos/wayland-protocols")
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/wayland")
include_directories("${CMAKE_BINARY_DIR}/wayland")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/stable/presentation-time/presentation-time.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-presentation-time-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/stable/viewporter/viewporter.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-viewporter-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-decoration-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-relative-pointer-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-pointer-constraints-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-idle-inhibit-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-output/xdg-output-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-output-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/staging/xdg-activation/xdg-activation-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-activation-v1-client-protocol")
target_link_libraries(wayland_protocol
PkgConfig::WAYLAND
)
target_include_directories(wayland_protocol
PUBLIC
"${CMAKE_BINARY_DIR}/wayland"
)

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -40,13 +40,6 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
wlWm.compositor = wl_registry_bind(wlWm.registry, name,
// we only need v3 to run, but v4 can use eglSwapBuffersWithDamageKHR
&wl_compositor_interface, version > 4 ? 4 : version);
#ifndef ENABLE_LIBDECOR
else if (!strcmp(interface, xdg_wm_base_interface.name))
wlWm.xdgWmBase = wl_registry_bind(wlWm.registry, name, &xdg_wm_base_interface, 1);
else if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name))
wlWm.xdgDecorationManager = wl_registry_bind(wlWm.registry, name,
&zxdg_decoration_manager_v1_interface, 1);
#endif
else if (!strcmp(interface, wp_presentation_interface.name))
wlWm.presentation = wl_registry_bind(wlWm.registry, name,
&wp_presentation_interface, 1);
@@ -72,6 +65,12 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
wlWm.xdgOutputManager = wl_registry_bind(wlWm.registry, name,
// we only need v2 to run, but v3 saves a callback
&zxdg_output_manager_v1_interface, version > 3 ? 3 : version);
else if (!strcmp(interface, xdg_activation_v1_interface.name))
wlWm.xdgActivation = wl_registry_bind(wlWm.registry, name,
&xdg_activation_v1_interface, 1);
else if (wlWm.desktop->registryGlobalHandler(
data, registry, name, interface, version))
return;
}
static void registryGlobalRemoveHandler(void * data,

View File

@@ -1,178 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <sys/epoll.h>
#include <libdecor.h>
#include <wayland-client.h>
#include "app.h"
#include "common/debug.h"
struct libdecor_configuration {
uint32_t serial;
bool has_window_state;
enum libdecor_window_state window_state;
bool has_size;
int window_width;
int window_height;
};
static void libdecorHandleError(struct libdecor * context, enum libdecor_error error,
const char *message)
{
DEBUG_ERROR("Got libdecor error (%d): %s", error, message);
}
static void libdecorFrameConfigure(struct libdecor_frame * frame,
struct libdecor_configuration * configuration, void * opaque)
{
if (!wlWm.configured)
{
xdg_surface_ack_configure(libdecor_frame_get_xdg_surface(frame), configuration->serial);
wlWm.configured = true;
return;
}
int width, height;
if (libdecor_configuration_get_content_size(configuration, frame, &width, &height))
{
wlWm.width = width;
wlWm.height = height;
struct libdecor_state * state = libdecor_state_new(wlWm.width, wlWm.height);
libdecor_frame_commit(wlWm.libdecorFrame, state, NULL);
libdecor_state_free(state);
}
enum libdecor_window_state windowState;
if (libdecor_configuration_get_window_state(configuration, &windowState))
wlWm.fullscreen = windowState & LIBDECOR_WINDOW_STATE_FULLSCREEN;
wlWm.needsResize = true;
wlWm.resizeSerial = configuration->serial;
app_invalidateWindow(true);
waylandStopWaitFrame();
}
static void libdecorFrameClose(struct libdecor_frame * frame, void * opaque)
{
app_handleCloseEvent();
}
static void libdecorFrameCommit(struct libdecor_frame * frame, void * opaque)
{
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
static struct libdecor_interface libdecorListener = {
libdecorHandleError,
};
static struct libdecor_frame_interface libdecorFrameListener = {
libdecorFrameConfigure,
libdecorFrameClose,
libdecorFrameCommit,
};
#pragma GCC diagnostic pop
static void libdecorCallback(uint32_t events, void * opaque)
{
libdecor_dispatch(wlWm.libdecor, 0);
}
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
{
wlWm.libdecor = libdecor_new(wlWm.display, &libdecorListener);
wlWm.libdecorFrame = libdecor_decorate(wlWm.libdecor, wlWm.surface, &libdecorFrameListener, NULL);
libdecor_frame_set_app_id(wlWm.libdecorFrame, "looking-glass-client");
libdecor_frame_set_title(wlWm.libdecorFrame, title);
libdecor_frame_map(wlWm.libdecorFrame);
if (resizable)
libdecor_frame_set_capabilities(wlWm.libdecorFrame, LIBDECOR_ACTION_RESIZE);
else
libdecor_frame_unset_capabilities(wlWm.libdecorFrame, LIBDECOR_ACTION_RESIZE);
while (!wlWm.configured)
libdecor_dispatch(wlWm.libdecor, 0);
if (!waylandPollRegister(libdecor_get_fd(wlWm.libdecor), libdecorCallback, NULL, EPOLLIN))
{
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
return false;
}
return true;
}
void waylandShellAckConfigureIfNeeded(void)
{
if (wlWm.resizeSerial)
{
xdg_surface_ack_configure(libdecor_frame_get_xdg_surface(wlWm.libdecorFrame), wlWm.resizeSerial);
wlWm.resizeSerial = 0;
}
}
void waylandSetFullscreen(bool fs)
{
if (fs)
libdecor_frame_set_fullscreen(wlWm.libdecorFrame, NULL);
else
libdecor_frame_unset_fullscreen(wlWm.libdecorFrame);
libdecor_frame_set_visibility(wlWm.libdecorFrame, !fs);
}
bool waylandGetFullscreen(void)
{
return wlWm.fullscreen;
}
void waylandMinimize(void)
{
libdecor_frame_set_minimized(wlWm.libdecorFrame);
}
void waylandShellResize(int w, int h)
{
if (!libdecor_frame_is_floating(wlWm.libdecorFrame))
return;
wlWm.width = w;
wlWm.height = h;
struct libdecor_state * state = libdecor_state_new(w, h);
libdecor_frame_commit(wlWm.libdecorFrame, state, NULL);
libdecor_state_free(state);
wlWm.needsResize = true;
app_invalidateWindow(true);
waylandStopWaitFrame();
}

View File

@@ -1,160 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"
#include <stdbool.h>
#include <wayland-client.h>
#include "app.h"
#include "common/debug.h"
// XDG WM base listeners.
static void xdgWmBasePing(void * data, struct xdg_wm_base * xdgWmBase, uint32_t serial)
{
xdg_wm_base_pong(xdgWmBase, serial);
}
static const struct xdg_wm_base_listener xdgWmBaseListener = {
.ping = xdgWmBasePing,
};
// XDG Surface listeners.
static void xdgSurfaceConfigure(void * data, struct xdg_surface * xdgSurface,
uint32_t serial)
{
if (wlWm.configured)
{
wlWm.needsResize = true;
wlWm.resizeSerial = serial;
app_invalidateWindow(true);
waylandStopWaitFrame();
}
else
{
xdg_surface_ack_configure(xdgSurface, serial);
wlWm.configured = true;
}
}
static const struct xdg_surface_listener xdgSurfaceListener = {
.configure = xdgSurfaceConfigure,
};
// XDG Toplevel listeners.
static void xdgToplevelConfigure(void * data, struct xdg_toplevel * xdgToplevel,
int32_t width, int32_t height, struct wl_array * states)
{
wlWm.width = width;
wlWm.height = height;
wlWm.fullscreen = false;
enum xdg_toplevel_state * state;
wl_array_for_each(state, states)
{
if (*state == XDG_TOPLEVEL_STATE_FULLSCREEN)
wlWm.fullscreen = true;
}
}
static void xdgToplevelClose(void * data, struct xdg_toplevel * xdgToplevel)
{
app_handleCloseEvent();
}
static const struct xdg_toplevel_listener xdgToplevelListener = {
.configure = xdgToplevelConfigure,
.close = xdgToplevelClose,
};
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
{
if (!wlWm.xdgWmBase)
{
DEBUG_ERROR("Compositor missing xdg_wm_base, will not proceed");
return false;
}
xdg_wm_base_add_listener(wlWm.xdgWmBase, &xdgWmBaseListener, NULL);
wlWm.xdgSurface = xdg_wm_base_get_xdg_surface(wlWm.xdgWmBase, wlWm.surface);
xdg_surface_add_listener(wlWm.xdgSurface, &xdgSurfaceListener, NULL);
wlWm.xdgToplevel = xdg_surface_get_toplevel(wlWm.xdgSurface);
xdg_toplevel_add_listener(wlWm.xdgToplevel, &xdgToplevelListener, NULL);
xdg_toplevel_set_title(wlWm.xdgToplevel, title);
xdg_toplevel_set_app_id(wlWm.xdgToplevel, "looking-glass-client");
if (fullscreen)
xdg_toplevel_set_fullscreen(wlWm.xdgToplevel, NULL);
if (maximize)
xdg_toplevel_set_maximized(wlWm.xdgToplevel);
if (wlWm.xdgDecorationManager)
{
wlWm.xdgToplevelDecoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
wlWm.xdgDecorationManager, wlWm.xdgToplevel);
if (wlWm.xdgToplevelDecoration)
{
zxdg_toplevel_decoration_v1_set_mode(wlWm.xdgToplevelDecoration,
borderless ?
ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
}
}
return true;
}
void waylandShellAckConfigureIfNeeded(void)
{
if (wlWm.resizeSerial)
{
xdg_surface_ack_configure(wlWm.xdgSurface, wlWm.resizeSerial);
wlWm.resizeSerial = 0;
}
}
void waylandSetFullscreen(bool fs)
{
if (fs)
xdg_toplevel_set_fullscreen(wlWm.xdgToplevel, NULL);
else
xdg_toplevel_unset_fullscreen(wlWm.xdgToplevel);
}
bool waylandGetFullscreen(void)
{
return wlWm.fullscreen;
}
void waylandMinimize(void)
{
xdg_toplevel_set_minimized(wlWm.xdgToplevel);
}
void waylandShellResize(int w, int h)
{
//TODO: Implement resize for XDG.
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -24,10 +24,13 @@
#include <signal.h>
#include <string.h>
#include <wayland-client.h>
#include <sys/socket.h>
#include "common/debug.h"
#include "common/option.h"
#include "dynamic/wayland_desktops.h"
static struct Option waylandOptions[] =
{
{
@@ -68,18 +71,69 @@ static bool waylandProbe(void)
return getenv("WAYLAND_DISPLAY") != NULL;
}
static bool getCompositor(char * dst, size_t size)
{
int fd = wl_display_get_fd(wlWm.display);
struct ucred ucred;
socklen_t len = sizeof(struct ucred);
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1)
{
DEBUG_ERROR("Failed to get the pid of the socket");
return false;
}
char path[64];
snprintf(path, sizeof(path), "/proc/%d/comm", ucred.pid);
FILE *fp = fopen(path, "r");
if (!fp)
{
DEBUG_ERROR("Failed to open %s", path);
return false;
}
if (!fgets(dst, size, fp))
{
DEBUG_ERROR("Failed to read %s", path);
fclose(fp);
return false;
}
fclose(fp);
dst[strlen(dst) - 1] = 0;
return true;
}
static bool waylandInit(const LG_DSInitParams params)
{
memset(&wlWm, 0, sizeof(wlWm));
wlWm.desktop = WL_Desktops[0];
wlWm.display = wl_display_connect(NULL);
if (!wlWm.display)
return false;
// select the desktop interface based on the compositor process name
char compositor[1024];
if (getCompositor(compositor, sizeof(compositor)))
{
DEBUG_INFO("Compositor: %s", compositor);
for(int i = 0; i < WL_DESKTOP_COUNT; ++i)
if (strcmp(WL_Desktops[i]->compositor, compositor) == 0)
{
wlWm.desktop = WL_Desktops[i];
break;
}
}
else
DEBUG_WARN("Compositor: UNKNOWN");
DEBUG_INFO("Selected : %s", wlWm.desktop->name);
wl_list_init(&wlWm.surfaceOutputs);
wlWm.warpSupport = option_get_bool("wayland", "warpSupport");
wlWm.useFractionalScale = option_get_bool("wayland", "fractionScale");
wlWm.display = wl_display_connect(NULL);
wlWm.width = params.w;
wlWm.height = params.h;
if (!waylandPollInit())
return false;
@@ -89,6 +143,9 @@ static bool waylandInit(const LG_DSInitParams params)
if (!waylandRegistryInit())
return false;
if (!waylandActivationInit())
return false;
if (!waylandIdleInit())
return false;
@@ -101,7 +158,9 @@ static bool waylandInit(const LG_DSInitParams params)
if (!waylandInputInit())
return false;
if (!waylandWindowInit(params.title, params.fullscreen, params.maximize, params.borderless, params.resizable))
wlWm.desktop->setSize(params.w, params.h);
if (!waylandWindowInit(params.title, params.appId, params.fullscreen, params.maximize,
params.borderless, params.resizable))
return false;
if (!waylandEGLInit(params.w, params.h))
@@ -112,9 +171,6 @@ static bool waylandInit(const LG_DSInitParams params)
return false;
#endif
wlWm.width = params.w;
wlWm.height = params.h;
return true;
}
@@ -149,8 +205,31 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
return false;
}
void waylandNeedsResize(void)
{
wlWm.needsResize = true;
app_invalidateWindow(true);
waylandStopWaitFrame();
}
static void waylandSetFullscreen(bool fs)
{
wlWm.desktop->setFullscreen(fs);
}
static bool waylandGetFullscreen(void)
{
return wlWm.desktop->getFullscreen();
}
static void waylandMinimize(void)
{
wlWm.desktop->minimize();
}
struct LG_DisplayServerOps LGDS_Wayland =
{
.name = "Wayland",
.setup = waylandSetup,
.probe = waylandProbe,
.earlyInit = waylandEarlyInit,
@@ -184,9 +263,11 @@ struct LG_DisplayServerOps LGDS_Wayland =
.uncapturePointer = waylandUncapturePointer,
.grabKeyboard = waylandGrabKeyboard,
.ungrabKeyboard = waylandUngrabKeyboard,
.getCharCode = waylandGetCharCode,
.warpPointer = waylandWarpPointer,
.realignPointer = waylandRealignPointer,
.isValidPointerPos = waylandIsValidPointerPos,
.requestActivation = waylandActivationRequestActivation,
.inhibitIdle = waylandInhibitIdle,
.uninhibitIdle = waylandUninhibitIdle,
.wait = waylandWait,

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -37,16 +37,16 @@
#include "common/countedbuffer.h"
#include "common/ringbuffer.h"
#include "interface/displayserver.h"
#include "interface/desktop.h"
#include "wayland-xdg-shell-client-protocol.h"
#include "wayland-presentation-time-client-protocol.h"
#include "wayland-viewporter-client-protocol.h"
#include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
#include "wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
#include "wayland-xdg-output-unstable-v1-client-protocol.h"
#include "wayland-xdg-activation-v1-client-protocol.h"
typedef void (*WaylandPollCallback)(uint32_t events, void * opaque);
@@ -99,6 +99,8 @@ struct WaylandDSState
bool pointerInSurface;
bool focusedOnSurface;
WL_DesktopOps * desktop;
struct wl_display * display;
struct wl_surface * surface;
struct wl_registry * registry;
@@ -106,12 +108,9 @@ struct WaylandDSState
struct wl_shm * shm;
struct wl_compositor * compositor;
int32_t width, height;
wl_fixed_t scale;
bool fractionalScale;
bool needsResize;
bool fullscreen;
uint32_t resizeSerial;
bool configured;
bool warpSupport;
double cursorX, cursorY;
@@ -132,17 +131,6 @@ struct WaylandDSState
RingBuffer photonTimings;
GraphHandle photonGraph;
#ifdef ENABLE_LIBDECOR
struct libdecor * libdecor;
struct libdecor_frame * libdecorFrame;
#else
struct xdg_wm_base * xdgWmBase;
struct xdg_surface * xdgSurface;
struct xdg_toplevel * xdgToplevel;
struct zxdg_decoration_manager_v1 * xdgDecorationManager;
struct zxdg_toplevel_decoration_v1 * xdgToplevelDecoration;
#endif
const char * cursorThemeName;
int cursorSize;
int cursorScale;
@@ -180,6 +168,8 @@ struct WaylandDSState
struct zwp_idle_inhibit_manager_v1 * idleInhibitManager;
struct zwp_idle_inhibitor_v1 * idleInhibitor;
struct xdg_activation_v1 * xdgActivation;
struct wp_viewporter * viewporter;
struct wp_viewport * viewport;
struct zxdg_output_manager_v1 * xdgOutputManager;
@@ -231,6 +221,11 @@ struct WCBState
extern struct WaylandDSState wlWm;
extern struct WCBState wlCb;
// activation module
bool waylandActivationInit(void);
void waylandActivationFree(void);
void waylandActivationRequestActivation(void);
// clipboard module
bool waylandCBInit(void);
void waylandCBRequest(LG_ClipboardData type);
@@ -282,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);
@@ -305,16 +301,8 @@ void waylandPresentationFree(void);
bool waylandRegistryInit(void);
void waylandRegistryFree(void);
// shell module
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable);
void waylandShellAckConfigureIfNeeded(void);
void waylandSetFullscreen(bool fs);
bool waylandGetFullscreen(void);
void waylandMinimize(void);
void waylandShellResize(int w, int h);
// window module
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable);
bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, bool maximize, bool borderless, bool resizable);
void waylandWindowFree(void);
void waylandWindowUpdateScale(void);
void waylandSetWindowSize(int x, int y);
@@ -322,3 +310,4 @@ bool waylandIsValidPointerPos(int x, int y);
bool waylandWaitFrame(void);
void waylandSkipFrame(void);
void waylandStopWaitFrame(void);
void waylandNeedsResize(void);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -57,6 +57,12 @@ void waylandWindowUpdateScale(void)
static void wlSurfaceEnterHandler(void * data, struct wl_surface * surface, struct wl_output * output)
{
struct SurfaceOutput * node = malloc(sizeof(*node));
if (!node)
{
DEBUG_ERROR("out of memory");
return;
}
node->output = output;
wl_list_insert(&wlWm.surfaceOutputs, &node->link);
waylandWindowUpdateScale();
@@ -79,7 +85,7 @@ static const struct wl_surface_listener wlSurfaceListener = {
.leave = wlSurfaceLeaveHandler,
};
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, bool maximize, bool borderless, bool resizable)
{
wlWm.scale = wl_fixed_from_int(1);
@@ -106,7 +112,8 @@ bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool
wl_surface_add_listener(wlWm.surface, &wlSurfaceListener, NULL);
if (!waylandShellInit(title, fullscreen, maximize, borderless, resizable))
if (!wlWm.desktop->shellInit(wlWm.display, wlWm.surface,
title, appId, fullscreen, maximize, borderless, resizable))
return false;
wl_surface_commit(wlWm.surface);
@@ -121,12 +128,14 @@ void waylandWindowFree(void)
void waylandSetWindowSize(int x, int y)
{
waylandShellResize(x, y);
wlWm.desktop->shellResize(x, y);
}
bool waylandIsValidPointerPos(int x, int y)
{
return x >= 0 && x < wlWm.width && y >= 0 && y < wlWm.height;
int width, height;
wlWm.desktop->getSize(&width, &height);
return x >= 0 && x < width && y >= 0 && y < height;
}
static void frameHandler(void * opaque, struct wl_callback * callback, unsigned int data)

View File

@@ -1,31 +1,37 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.10)
project(displayserver_X11 LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(DISPLAYSERVER_X11 REQUIRED IMPORTED_TARGET
x11
xi
xfixes
xscrnsaver
xinerama
xcursor
xpresent
x11
xi
xfixes
xscrnsaver
xinerama
xcursor
xpresent
xkbcommon
)
add_library(displayserver_X11 STATIC
x11.c
atoms.c
clipboard.c
x11.c
atoms.c
clipboard.c
cursor.c
wm/default.c
wm/i3.c
)
add_definitions(-D GLX_GLXEXT_PROTOTYPES)
target_link_libraries(displayserver_X11
PkgConfig::DISPLAYSERVER_X11
lg_common
PkgConfig::DISPLAYSERVER_X11
lg_common
lg_resources
)
target_include_directories(displayserver_X11
PRIVATE
src
PRIVATE
.
)

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -28,14 +28,17 @@
DEF_ATOM(_NET_REQUEST_FRAME_EXTENTS, True) \
DEF_ATOM(_NET_FRAME_EXTENTS, True) \
DEF_ATOM(_NET_WM_BYPASS_COMPOSITOR, False) \
DEF_ATOM(_NET_WM_ICON, True) \
DEF_ATOM(_NET_WM_STATE, True) \
DEF_ATOM(_NET_WM_STATE_FULLSCREEN, True) \
DEF_ATOM(_NET_WM_STATE_FOCUSED, True) \
DEF_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ, True) \
DEF_ATOM(_NET_WM_STATE_MAXIMIZED_VERT, True) \
DEF_ATOM(_NET_WM_STATE_DEMANDS_ATTENTION, True) \
DEF_ATOM(_NET_WM_WINDOW_TYPE, True) \
DEF_ATOM(_NET_WM_WINDOW_TYPE_NORMAL, True) \
DEF_ATOM(_NET_WM_WINDOW_TYPE_UTILITY, True) \
DEF_ATOM(_NET_WM_PID, True) \
DEF_ATOM(WM_DELETE_WINDOW, True) \
DEF_ATOM(_MOTIF_WM_HINTS, True) \
\

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -60,40 +60,40 @@ static void x11CBSelectionIncr(const XPropertyEvent e);
static void x11CBSelectionNotify(const XSelectionEvent e);
static void x11CBXFixesSelectionNotify(const XFixesSelectionNotifyEvent e);
bool x11CBEventThread(const XEvent xe)
bool x11CBEventThread(const XEvent * xe)
{
switch(xe.type)
switch(xe->type)
{
case SelectionRequest:
x11CBSelectionRequest(xe.xselectionrequest);
x11CBSelectionRequest(xe->xselectionrequest);
return true;
case SelectionClear:
x11CBSelectionClear(xe.xselectionclear);
x11CBSelectionClear(xe->xselectionclear);
return true;
case SelectionNotify:
x11CBSelectionNotify(xe.xselection);
x11CBSelectionNotify(xe->xselection);
return true;
case PropertyNotify:
if (xe.xproperty.state != PropertyNewValue)
if (xe->xproperty.state != PropertyNewValue)
break;
if (xe.xproperty.atom == x11atoms.SEL_DATA)
if (xe->xproperty.atom == x11atoms.SEL_DATA)
{
if (x11cb.lowerBound == 0)
return true;
x11CBSelectionIncr(xe.xproperty);
x11CBSelectionIncr(xe->xproperty);
return true;
}
break;
default:
if (xe.type == x11.eventBase + XFixesSelectionNotify)
if (xe->type == x11.eventBase + XFixesSelectionNotify)
{
XFixesSelectionNotifyEvent * sne = (XFixesSelectionNotifyEvent *)&xe;
XFixesSelectionNotifyEvent * sne = (XFixesSelectionNotifyEvent *)xe;
x11CBXFixesSelectionNotify(*sne);
return true;
}
@@ -103,7 +103,7 @@ bool x11CBEventThread(const XEvent xe)
return false;
}
bool x11CBInit()
bool x11CBInit(void)
{
x11cb.aCurSelection = BadValue;
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
@@ -123,8 +123,6 @@ bool x11CBInit()
return false;
}
XFixesSelectSelectionInput(x11.display, x11.window,
XA_PRIMARY, XFixesSetSelectionOwnerNotifyMask);
XFixesSelectSelectionInput(x11.display, x11.window,
x11atoms.CLIPBOARD, XFixesSetSelectionOwnerNotifyMask);
@@ -154,6 +152,12 @@ static void x11CBReplyFn(void * opaque, LG_ClipboardData type,
static void x11CBSelectionRequest(const XSelectionRequestEvent e)
{
XEvent * s = malloc(sizeof(*s));
if (!s)
{
DEBUG_ERROR("out of memory");
return;
}
s->xselection.type = SelectionNotify;
s->xselection.requestor = e.requestor;
s->xselection.selection = e.selection;
@@ -205,7 +209,7 @@ send:
static void x11CBSelectionClear(const XSelectionClearEvent e)
{
if (e.selection != XA_PRIMARY && e.selection != x11atoms.CLIPBOARD)
if (e.selection != x11atoms.CLIPBOARD)
return;
x11cb.aCurSelection = BadValue;
@@ -291,7 +295,7 @@ out:
static void x11CBXFixesSelectionNotify(const XFixesSelectionNotifyEvent e)
{
// check if the selection is valid and it isn't ourself
if ((e.selection != XA_PRIMARY && e.selection != x11atoms.CLIPBOARD) ||
if (e.selection != x11atoms.CLIPBOARD ||
e.owner == x11.window || e.owner == 0)
{
return;
@@ -396,7 +400,6 @@ void x11CBNotice(LG_ClipboardData type)
{
x11cb.haveRequest = true;
x11cb.type = type;
XSetSelectionOwner(x11.display, XA_PRIMARY , x11.window, CurrentTime);
XSetSelectionOwner(x11.display, x11atoms.CLIPBOARD, x11.window, CurrentTime);
XFlush(x11.display);
}
@@ -404,7 +407,6 @@ void x11CBNotice(LG_ClipboardData type)
void x11CBRelease(void)
{
x11cb.haveRequest = false;
XSetSelectionOwner(x11.display, XA_PRIMARY , None, CurrentTime);
XSetSelectionOwner(x11.display, x11atoms.CLIPBOARD, None, CurrentTime);
XFlush(x11.display);
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -26,9 +26,9 @@
#include "interface/displayserver.h"
bool x11CBEventThread(const XEvent xe);
bool x11CBEventThread(const XEvent * xe);
bool x11CBInit();
bool x11CBInit(void);
void x11CBNotice(LG_ClipboardData type);
void x11CBRelease(void);
void x11CBRequest(LG_ClipboardData type);

View File

@@ -0,0 +1,105 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "cursor.h"
#include "common/util.h"
#include <string.h>
#include <errno.h>
struct MemFile
{
const char * data;
int size;
int pos;
};
static int x11cursor_read(XcursorFile *file, unsigned char * buf, int len)
{
struct MemFile * f = (struct MemFile *)file->closure;
if (f->pos == f->size)
return 0;
len = min(f->size - f->pos, len);
memcpy(buf, f->data + f->pos, len);
f->pos += len;
return len;
}
static int x11cursor_write(XcursorFile *file, unsigned char * buf, int len)
{
errno = -EINVAL;
return -1;
}
static int x11cursor_seek(XcursorFile *file, long offset, int whence)
{
struct MemFile * f = (struct MemFile *)file->closure;
long target;
switch(whence)
{
case SEEK_SET:
target = offset;
break;
case SEEK_CUR:
target = f->pos + offset;
break;
case SEEK_END:
target = f->size + offset;
break;
default:
errno = -EINVAL;
return -1;
}
if (target < 0 || target > f->size)
{
errno = -EINVAL;
return -1;
}
f->pos = target;
return target;
}
XcursorImages * x11cursor_load(const char * cursor, int size)
{
struct MemFile closure =
{
.data = cursor,
.size = size,
.pos = 0
};
XcursorFile f =
{
.closure = &closure,
.read = x11cursor_read,
.write = x11cursor_write,
.seek = x11cursor_seek
};
return XcursorXcFileLoadAllImages(&f);
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -18,12 +18,12 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#ifndef _H_X11DS_CURSOR_
#define _H_X11DS_CURSOR_
#include "model.h"
#include <X11/Xlib.h>
#include <X11/Xcursor/Xcursor.h>
void egl_drawTorus(EGL_Model * model, unsigned int pts, float x, float y,
float inner, float outer);
XcursorImages * x11cursor_load(const char * cursor, int size);
void egl_drawTorusArc(EGL_Model * model, unsigned int pts, float x, float y,
float inner, float outer, float s, float e);
#endif

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -18,16 +18,22 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_X11DS_WM_
#define _H_X11DS_WM_
#include <stdint.h>
#include <stdbool.h>
struct ll;
struct ll * ll_new();
void ll_free (struct ll * list);
void ll_push (struct ll * list, void * data);
bool ll_shift (struct ll * list, void ** data);
bool ll_peek_head(struct ll * list, void ** data);
bool ll_peek_tail(struct ll * list, void ** data);
unsigned int ll_count (struct ll * list);
typedef struct X11WM
{
void (*setup)(void);
bool (*init)(void);
void (*deinit)(void);
void (*setFullscreen)(bool enable);
}
X11WM;
void ll_reset (struct ll * list);
bool ll_walk (struct ll * list, void ** data);
extern X11WM X11WM_Default;
extern X11WM X11WM_i3;
#endif

View File

@@ -0,0 +1,71 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_X11DS_WM_DEFAULT_
#define _H_X11DS_WM_DEFAULT_
#include "wm.h"
#include "x11.h"
#include "atoms.h"
static void wm_default_setup(void)
{
}
static bool wm_default_init(void)
{
return true;
}
static void wm_default_deinit(void)
{
}
static void wm_default_setFullscreen(bool enable)
{
XEvent e =
{
.xclient = {
.type = ClientMessage,
.send_event = true,
.message_type = x11atoms._NET_WM_STATE,
.format = 32,
.window = x11.window,
.data.l = {
enable ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
x11atoms._NET_WM_STATE_FULLSCREEN,
0
}
}
};
XSendEvent(x11.display, DefaultRootWindow(x11.display), False,
SubstructureNotifyMask | SubstructureRedirectMask, &e);
};
X11WM X11WM_Default =
{
.setup = wm_default_setup,
.init = wm_default_init,
.deinit = wm_default_deinit,
.setFullscreen = wm_default_setFullscreen
};
#endif

View File

@@ -0,0 +1,193 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_X11DS_WM_DEFAULT_
#define _H_X11DS_WM_DEFAULT_
#include "wm.h"
#include "x11.h"
#include "atoms.h"
#include "common/debug.h"
#include "common/option.h"
#include "common/util.h"
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
static struct Option options[] =
{
// app options
{
.module = "i3",
.name = "globalFullScreen",
.description = "Use i3's global full screen feature (spans all monitors)",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false,
},
{0}
};
struct i3state
{
int sock;
bool globalFullScreen;
};
static struct i3state i3;
static void wm_i3_setup(void)
{
option_register(options);
}
static bool wm_i3_init(void)
{
memset(&i3, 0, sizeof(i3));
i3.globalFullScreen = option_get_bool("i3", "globalFullScreen");
FILE * fd = popen("i3 --get-socketpath", "r");
if (!fd)
return false;
struct sockaddr_un addr = { .sun_family = AF_UNIX };
char * path = (char *)&addr.sun_path;
int pathLen;
if ((pathLen = fread(path, 1, sizeof(addr.sun_path), fd)) <= 0)
{
pclose(fd);
return false;
}
pclose(fd);
if(path[pathLen-1] == '\n')
--pathLen;
path[pathLen] = '\0';
i3.sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (i3.sock < 0)
{
DEBUG_ERROR("Failed to create socket for i3 IPC");
return false;
}
if (connect(i3.sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
DEBUG_ERROR("Failed to connect to the i3 IPC socket");
perror("connect");
goto err_socket;
}
DEBUG_INFO("i3 IPC Connected");
return true;
err_socket:
close(i3.sock);
return false;
}
static void wm_i3_deinit(void)
{
close(i3.sock);
}
static void wm_i3_setFullscreen(bool enable)
{
if (!i3.globalFullScreen)
{
X11WM_Default.setFullscreen(enable);
return;
}
struct i3Msg
{
char magic[6];
uint32_t length;
uint32_t type;
char payload[0];
}
__attribute__((packed));
#define I3_IPC_TYPE_RUN_COMMAND 0
char cmd[128];
int cmdLen = snprintf(cmd, sizeof(cmd),
"[id=%lu] fullscreen toggle global",
x11.window);
struct i3Msg *msg = alloca(sizeof(struct i3Msg) + cmdLen);
memcpy(msg->magic, "i3-ipc", 6);
msg->length = cmdLen;
msg->type = I3_IPC_TYPE_RUN_COMMAND;
memcpy(msg->payload, cmd, cmdLen);
int msgSize = sizeof(*msg) + msg->length;
char * buf = (char *)msg;
while(msgSize)
{
int wrote = write(i3.sock, buf, msgSize);
if (wrote <= 0)
{
DEBUG_WARN("i3 IPC communication failure");
return;
}
buf += wrote;
msgSize -= wrote;
}
if ((msgSize = read(i3.sock, msg, sizeof(*msg))) < 0)
{
DEBUG_WARN("i3 IPC read failure");
return;
}
if (memcmp(msg->magic, "i3-ipc", 6) != 0 ||
msg->type != I3_IPC_TYPE_RUN_COMMAND)
{
DEBUG_WARN("i3 IPC unexpected reply");
return;
}
// read and discard the payload
while(msg->length)
{
int len = read(i3.sock, cmd, min(msg->length, sizeof(cmd)));
if (len <= 0)
{
DEBUG_WARN("i3 IPC failed to read payload");
return;
}
msg->length -= len;
}
};
X11WM X11WM_i3 =
{
.setup = wm_i3_setup,
.init = wm_i3_init,
.deinit = wm_i3_deinit,
.setFullscreen = wm_i3_setFullscreen
};
#endif

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -23,6 +23,11 @@
#include "x11.h"
#include "atoms.h"
#include "clipboard.h"
#include "cursor.h"
#include "resources/icondata.h"
#include "resources/no-input-cursor/16.xcur.h"
#include "resources/no-input-cursor/32.xcur.h"
#include <string.h>
#include <unistd.h>
@@ -35,6 +40,8 @@
#include <X11/extensions/Xpresent.h>
#include <X11/Xcursor/Xcursor.h>
#include <xkbcommon/xkbcommon.h>
#include <GL/glx.h>
#include <GL/glxext.h>
@@ -50,10 +57,6 @@
#include "common/event.h"
#include "util.h"
#define _NET_WM_STATE_REMOVE 0
#define _NET_WM_STATE_ADD 1
#define _NET_WM_STATE_TOGGLE 2
struct X11DSState x11;
struct MwmHints
@@ -163,6 +166,8 @@ static void x11DoPresent(uint64_t msc)
static void x11Setup(void)
{
X11WM_Default.setup();
X11WM_i3 .setup();
}
static bool x11Probe(void)
@@ -189,14 +194,14 @@ static void x11CheckEWMHSupport(void)
if (XGetWindowProperty(x11.display, DefaultRootWindow(x11.display),
x11atoms._NET_SUPPORTING_WM_CHECK, 0, ~0L, False, XA_WINDOW,
&type, &fmt, &num, &bytes, &data) != Success)
&type, &fmt, &num, &bytes, &data) != Success || !data)
goto out;
Window * windowFromRoot = (Window *)data;
if (XGetWindowProperty(x11.display, *windowFromRoot,
x11atoms._NET_SUPPORTING_WM_CHECK, 0, ~0L, False, XA_WINDOW,
&type, &fmt, &num, &bytes, &data) != Success)
&type, &fmt, &num, &bytes, &data) != Success || !data)
goto out_root;
Window * windowFromChild = (Window *)data;
@@ -205,7 +210,7 @@ static void x11CheckEWMHSupport(void)
if (XGetWindowProperty(x11.display, DefaultRootWindow(x11.display),
x11atoms._NET_SUPPORTED, 0, ~0L, False, AnyPropertyType,
&type, &fmt, &num, &bytes, &data) != Success)
&type, &fmt, &num, &bytes, &data) != Success || !data)
goto out_child;
Atom * supported = (Atom *)data;
@@ -213,7 +218,7 @@ static void x11CheckEWMHSupport(void)
if (XGetWindowProperty(x11.display, *windowFromRoot,
x11atoms._NET_WM_NAME, 0, ~0L, False, AnyPropertyType,
&type, &fmt, &num, &bytes, &data) != Success)
&type, &fmt, &num, &bytes, &data) != Success || !data)
goto out_supported;
char * wmName = (char *)data;
@@ -224,9 +229,11 @@ static void x11CheckEWMHSupport(void)
x11.ewmhHasFocusEvent = true;
}
DEBUG_INFO("EWMH-complient window manager detected: %s", wmName);
DEBUG_INFO("EWMH-compliant window manager detected: %s", wmName);
x11.ewmhSupport = true;
if (strcmp(wmName, "i3") == 0)
x11.wm = &X11WM_i3;
XFree(wmName);
out_supported:
@@ -239,17 +246,37 @@ out:
return;
}
static int x11ErrorHandler(Display * display, XErrorEvent * error)
{
char errorText[1024];
XGetErrorText(display, error->error_code, errorText, sizeof(errorText));
DEBUG_ERROR("X11 Error: %s", errorText);
DEBUG_PRINT_BACKTRACE();
return 0;
}
static int x11IOErrorHandler(Display * display)
{
DEBUG_FATAL("Fatal X11 IO Error");
return 0;
}
static bool x11Init(const LG_DSInitParams params)
{
XIDeviceInfo *devinfo;
int count;
int event, error;
XSetErrorHandler(x11ErrorHandler);
XSetIOErrorHandler(x11IOErrorHandler);
memset(&x11, 0, sizeof(x11));
x11.xValuator = -1;
x11.yValuator = -1;
x11.display = XOpenDisplay(NULL);
x11.display = XOpenDisplay(NULL);
x11.jitRender = params.jitRender;
x11.wm = &X11WM_Default;
XSetWindowAttributes swa =
{
@@ -316,7 +343,7 @@ static bool x11Init(const LG_DSInitParams params)
XClassHint hint =
{
.res_name = strdup(params.title),
.res_class = strdup("looking-glass-client")
.res_class = strdup(params.appId)
};
XSetClassHint(x11.display, x11.window, &hint);
free(hint.res_name);
@@ -347,6 +374,31 @@ static bool x11Init(const LG_DSInitParams params)
// check for Extended Window Manager Hints support
x11CheckEWMHSupport();
if (!x11.wm->init())
{
x11.wm = &X11WM_Default;
if (!x11.wm->init())
{
DEBUG_ERROR("Failed to initialize the X11 window manager subsystem");
goto fail_window;
}
}
if (x11atoms._NET_WM_PID)
{
pid_t pid = getpid();
XChangeProperty(
x11.display,
x11.window,
x11atoms._NET_WM_PID,
XA_CARDINAL,
32,
PropModeReplace,
(unsigned char *)&pid,
1
);
}
if (params.borderless)
{
if (x11atoms._MOTIF_WM_HINTS)
@@ -397,23 +449,14 @@ static bool x11Init(const LG_DSInitParams params)
);
}
Atom wmState[3] = {0};
int wmStateCount = 0;
if (params.fullscreen)
{
x11.fullscreen = true;
wmState[wmStateCount++] = x11atoms._NET_WM_STATE_FULLSCREEN;
}
if (params.maximize)
{
wmState[wmStateCount++] = x11atoms._NET_WM_STATE_MAXIMIZED_HORZ;
wmState[wmStateCount++] = x11atoms._NET_WM_STATE_MAXIMIZED_VERT;
}
Atom wmState[2] =
{
x11atoms._NET_WM_STATE_MAXIMIZED_HORZ,
x11atoms._NET_WM_STATE_MAXIMIZED_VERT
};
if (wmStateCount)
{
XChangeProperty(
x11.display,
x11.window,
@@ -422,7 +465,7 @@ static bool x11Init(const LG_DSInitParams params)
32,
PropModeReplace,
(unsigned char *)&wmState,
wmStateCount
2
);
}
@@ -448,7 +491,7 @@ static bool x11Init(const LG_DSInitParams params)
if (XIQueryVersion(x11.display, &major, &minor) != Success)
{
DEBUG_ERROR("Failed to query the XInput version");
return false;
goto fail_window;
}
DEBUG_INFO("X11 XInput %d.%d in use", major, minor);
@@ -522,6 +565,10 @@ static bool x11Init(const LG_DSInitParams params)
goto fail_window;
}
XDisplayKeycodes(x11.display, &x11.minKeycode, &x11.maxKeycode);
x11.keysyms = XGetKeyboardMapping(x11.display, x11.minKeycode,
x11.maxKeycode - x11.minKeycode, &x11.symsPerKeycode);
XIFreeDeviceInfo(devinfo);
XQueryExtension(x11.display, "XInputExtension", &x11.xinputOp, &event, &error);
@@ -532,11 +579,8 @@ static bool x11Init(const LG_DSInitParams params)
eventmask.mask_len = sizeof(mask);
eventmask.mask = mask;
if (!x11.ewmhHasFocusEvent)
{
XISetMask(mask, XI_FocusIn );
XISetMask(mask, XI_FocusOut);
}
XISetMask(mask, XI_FocusIn );
XISetMask(mask, XI_FocusOut);
XISetMask(mask, XI_Enter );
XISetMask(mask, XI_Leave );
@@ -565,6 +609,17 @@ static bool x11Init(const LG_DSInitParams params)
1
);
XChangeProperty(
x11.display,
x11.window,
x11atoms._NET_WM_ICON,
XA_CARDINAL,
32,
PropModeReplace,
(unsigned char *)icondata,
sizeof(icondata) / sizeof(icondata[0])
);
/* create the blank cursor */
{
static char data[] = { 0x00 };
@@ -575,29 +630,17 @@ static bool x11Init(const LG_DSInitParams params)
XFreePixmap(x11.display, temp);
}
/* create the square cursor */
{
static char data[] = { 0x07, 0x05, 0x07 };
static char mask[] = { 0xff, 0xff, 0xff };
XcursorImages * images;
if (params.largeCursorDot)
images = x11cursor_load(b_no_input_cursor_32_xcur,
b_no_input_cursor_32_xcur_size);
else
images = x11cursor_load(b_no_input_cursor_16_xcur,
b_no_input_cursor_16_xcur_size);
Colormap cmap = DefaultColormap(x11.display, DefaultScreen(x11.display));
XColor colors[2] =
{
{ .pixel = BlackPixelOfScreen(DefaultScreenOfDisplay(x11.display)) },
{ .pixel = WhitePixelOfScreen(DefaultScreenOfDisplay(x11.display)) }
};
XQueryColors(x11.display, cmap, colors, 2);
Pixmap img = XCreateBitmapFromData(x11.display, x11.window, data, 3, 3);
Pixmap msk = XCreateBitmapFromData(x11.display, x11.window, mask, 3, 3);
x11.cursors[LG_POINTER_SQUARE] = XCreatePixmapCursor(x11.display, img, msk,
&colors[0], &colors[1], 1, 1);
XFreePixmap(x11.display, img);
XFreePixmap(x11.display, msk);
}
x11.cursors[LG_POINTER_SQUARE] =
XcursorImagesLoadCursor(x11.display, images);
XcursorImagesDestroy(images);
/* initialize the rest of the cursors */
const char * cursorLookup[LG_POINTER_COUNT] = {
@@ -655,6 +698,9 @@ static bool x11Init(const LG_DSInitParams params)
if (!params.center)
XMoveWindow(x11.display, x11.window, params.x, params.y);
if (params.fullscreen)
x11.doFullscreenOnExpose = true;
XSetLocaleModifiers(""); // Load XMODIFIERS
x11.xim = XOpenIM(x11.display, 0, 0, 0);
@@ -735,6 +781,10 @@ static void x11Free(void)
if (x11.cursors[i])
XFreeCursor(x11.display, x11.cursors[i]);
if (x11.keysyms)
XFree(x11.keysyms);
x11.wm->deinit();
XCloseDisplay(x11.display);
}
@@ -847,7 +897,7 @@ static int x11EventThread(void * unused)
XNextEvent(x11.display, &xe);
// call the clipboard handling code
if (x11CBEventThread(xe))
if (x11CBEventThread(&xe))
continue;
switch(xe.type)
@@ -896,6 +946,11 @@ static int x11EventThread(void * unused)
{
atomic_store(&x11.lastWMEvent, microtime());
x11.invalidateAll = true;
if (x11.doFullscreenOnExpose)
{
x11SetFullscreen(true);
x11.doFullscreenOnExpose = false;
}
break;
}
@@ -1022,6 +1077,28 @@ static void updateModifiers(void)
);
}
static void setFocus(bool focused, double x, double y)
{
if (x11.focused == focused)
return;
x11.focused = focused;
app_updateCursorPos(x, y);
app_handleFocusEvent(focused);
}
static int x11GetCharCode(int detail)
{
detail += x11.minKeycode;
if (detail < x11.minKeycode || detail > x11.maxKeycode)
return 0;
KeySym sym = x11.keysyms[(detail - x11.minKeycode) *
x11.symsPerKeycode];
sym = xkb_keysym_to_upper(sym);
return xkb_keysym_to_utf32(sym);
}
static void x11XInputEvent(XGenericEventCookie *cookie)
{
static int button_state = 0;
@@ -1030,43 +1107,50 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
{
case XI_FocusIn:
{
XIFocusOutEvent *xie = cookie->data;
if (x11.ewmhHasFocusEvent)
{
// if meta ungrab for move/resize
if (xie->mode == XINotifyUngrab)
setFocus(true, xie->event_x, xie->event_y);
return;
}
atomic_store(&x11.lastWMEvent, microtime());
if (x11.focused)
return;
XIFocusOutEvent *xie = cookie->data;
if (xie->mode != XINotifyNormal &&
xie->mode != XINotifyWhileGrabbed &&
xie->mode != XINotifyUngrab)
return;
x11.focused = true;
app_updateCursorPos(xie->event_x, xie->event_y);
app_handleFocusEvent(true);
setFocus(true, xie->event_x, xie->event_y);
return;
}
case XI_FocusOut:
{
XIFocusOutEvent *xie = cookie->data;
if (x11.ewmhHasFocusEvent)
{
// if meta grab for move/resize
if (xie->mode == XINotifyGrab)
setFocus(false, xie->event_x, xie->event_y);
return;
}
atomic_store(&x11.lastWMEvent, microtime());
if (!x11.focused)
return;
XIFocusOutEvent *xie = cookie->data;
if (xie->mode != XINotifyNormal &&
xie->mode != XINotifyWhileGrabbed &&
xie->mode != XINotifyGrab)
return;
app_updateCursorPos(xie->event_x, xie->event_y);
app_handleFocusEvent(false);
x11.focused = false;
setFocus(false, xie->event_x, xie->event_y);
return;
}
@@ -1097,6 +1181,46 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
app_updateCursorPos(xie->event_x, xie->event_y);
app_handleEnterEvent(false);
x11.entered = false;
/**
* Because there is a race with the pointer ungrab the enter event for the
* next window is sometimes sent with the mode NotifyUngrab, unfortunatly
* some window managers such as i3 will ignore these which breaks focus
* follows mouse mode. To correct this we generate and send a normal
* EnterNotify event.
*/
int root_x, root_y, win_x, win_y;
Window root_win, child_win;
unsigned int mask;
XQueryPointer(x11.display, DefaultRootWindow(x11.display), &root_win,
&child_win, &root_x, &root_y, &win_x, &win_y, &mask);
int target_x, target_y;
Window target_root;
XTranslateCoordinates(x11.display, DefaultRootWindow(x11.display),
child_win, root_x, root_y, &target_x, &target_y, &target_root);
XEvent event;
memset(&event, 0, sizeof(event));
event.type = EnterNotify;
event.xcrossing.serial = 0;
event.xcrossing.send_event = True;
event.xcrossing.display = x11.display;
event.xcrossing.window = child_win;
event.xcrossing.root = root_win;
event.xcrossing.subwindow = child_win;
event.xcrossing.time = CurrentTime;
event.xcrossing.mode = NotifyNormal;
event.xcrossing.detail = NotifyNonlinear;
event.xcrossing.same_screen = True;
event.xcrossing.focus = False;
event.xcrossing.x = target_x;
event.xcrossing.y = target_y;
event.xcrossing.x_root = root_x;
event.xcrossing.y_root = root_y;
XSendEvent(x11.display, child_win, True, EnterWindowMask, &event);
XFlush(x11.display);
return;
}
@@ -1106,7 +1230,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIDeviceEvent *device = cookie->data;
app_handleKeyPress(device->detail - 8);
app_handleKeyPress(device->detail - x11.minKeycode);
if (!x11.xic || !app_isOverlayMode())
return;
@@ -1156,7 +1280,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIDeviceEvent *device = cookie->data;
app_handleKeyRelease(device->detail - 8);
app_handleKeyRelease(device->detail - x11.minKeycode);
if (!x11.xic || !app_isOverlayMode())
return;
@@ -1185,7 +1309,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIRawEvent *raw = cookie->data;
app_handleKeyPress(raw->detail - 8);
app_handleKeyPress(raw->detail - x11.minKeycode);
return;
}
@@ -1195,7 +1319,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIRawEvent *raw = cookie->data;
app_handleKeyRelease(raw->detail - 8);
app_handleKeyRelease(raw->detail - x11.minKeycode);
return;
}
@@ -1799,6 +1923,28 @@ static bool x11IsValidPointerPos(int x, int y)
return ret;
}
static void x11RequestActivation(void)
{
XEvent e =
{
.xclient = {
.type = ClientMessage,
.send_event = true,
.message_type = x11atoms._NET_WM_STATE,
.format = 32,
.window = x11.window,
.data.l = {
_NET_WM_STATE_ADD,
x11atoms._NET_WM_STATE_DEMANDS_ATTENTION,
0
}
}
};
XSendEvent(x11.display, DefaultRootWindow(x11.display), False,
SubstructureNotifyMask | SubstructureRedirectMask, &e);
}
static void x11InhibitIdle(void)
{
XScreenSaverSuspend(x11.display, true);
@@ -1824,24 +1970,7 @@ static void x11SetFullscreen(bool fs)
if (x11.fullscreen == fs)
return;
XEvent e =
{
.xclient = {
.type = ClientMessage,
.send_event = true,
.message_type = x11atoms._NET_WM_STATE,
.format = 32,
.window = x11.window,
.data.l = {
fs ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
x11atoms._NET_WM_STATE_FULLSCREEN,
0
}
}
};
XSendEvent(x11.display, DefaultRootWindow(x11.display), False,
SubstructureNotifyMask | SubstructureRedirectMask, &e);
x11.wm->setFullscreen(fs);
}
static bool x11GetFullscreen(void)
@@ -1856,6 +1985,7 @@ static void x11Minimize(void)
struct LG_DisplayServerOps LGDS_X11 =
{
.name = "X11",
.setup = x11Setup,
.probe = x11Probe,
.earlyInit = x11EarlyInit,
@@ -1884,11 +2014,13 @@ struct LG_DisplayServerOps LGDS_X11 =
.ungrabPointer = x11UngrabPointer,
.capturePointer = x11CapturePointer,
.uncapturePointer = x11UncapturePointer,
.getCharCode = x11GetCharCode,
.grabKeyboard = x11GrabKeyboard,
.ungrabKeyboard = x11UngrabKeyboard,
.warpPointer = x11WarpPointer,
.realignPointer = x11RealignPointer,
.isValidPointerPos = x11IsValidPointerPos,
.requestActivation = x11RequestActivation,
.inhibitIdle = x11InhibitIdle,
.uninhibitIdle = x11UninhibitIdle,
.wait = x11Wait,

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -33,6 +33,7 @@
#include "interface/displayserver.h"
#include "common/thread.h"
#include "common/types.h"
#include "wm.h"
enum Modifiers
{
@@ -48,11 +49,20 @@ enum Modifiers
#define MOD_COUNT (MOD_SUPER_RIGHT + 1)
#define _NET_WM_STATE_REMOVE 0
#define _NET_WM_STATE_ADD 1
#define _NET_WM_STATE_TOGGLE 2
struct X11DSState
{
Display * display;
Window window;
XVisualInfo * visual;
X11WM * wm;
int minKeycode, maxKeycode;
int symsPerKeycode;
KeySym * keysyms;
//Extended Window Manager Hints
//ref: https://specifications.freedesktop.org/wm-spec/latest/
@@ -61,6 +71,7 @@ struct X11DSState
_Atomic(uint64_t) lastWMEvent;
bool invalidateAll;
bool doFullscreenOnExpose;
int xpresentOp;
bool jitRender;

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -58,9 +58,9 @@ void app_resyncMouseBasic(void);
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);
void app_handleKeyRelease(int scancode);
void app_handleKeyboardTyped(const char * typed);
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);
@@ -102,11 +102,21 @@ int app_renderOverlay(struct Rect * rects, int maxRects);
void app_freeOverlays(void);
/**
* invalidate the window to update the overlay, if renderTwice is set the imgui
* render code will run twice so that auto sized windows are calculated correctly
*/
void app_invalidateOverlay(bool renderTwice);
struct OverlayGraph;
typedef struct OverlayGraph * GraphHandle;
typedef const char * (*GraphFormatFn)(const char * name,
float min, float max, float avg, float freq, float last);
GraphHandle app_registerGraph(const char * name, RingBuffer buffer, float min, float max);
GraphHandle app_registerGraph(const char * name, RingBuffer buffer,
float min, float max, GraphFormatFn formatFn);
void app_unregisterGraph(GraphHandle handle);
void app_invalidateGraph(GraphHandle handle);
void app_overlayConfigRegister(const char * title,
void (*callback)(void * udata, int * id), void * udata);
@@ -128,18 +138,31 @@ void app_clipboardRequest(const LG_ClipboardReplyFn replyFn, void * opaque);
*/
void app_alert(LG_MsgAlert type, const char * fmt, ...);
typedef struct MsgBoxHandle * MsgBoxHandle;
MsgBoxHandle app_msgBox(const char * caption, const char * fmt, ...);
typedef void (*MsgBoxConfirmCallback)(bool yes, void * opaque);
MsgBoxHandle app_confirmMsgBox(const char * caption,
MsgBoxConfirmCallback callback, void * opaque, const char * fmt, ...);
void app_msgBoxClose(MsgBoxHandle handle);
typedef struct KeybindHandle * KeybindHandle;
typedef void (*KeybindFn)(int sc, void * opaque);
void app_showRecord(bool show);
/**
* Register a handler for the <super>+<key> combination
* @param sc The scancode to register
* @param charcode The charcode to register (used instead of sc if non zero)
* @param callback The function to be called when the combination is pressed
* @param opaque A pointer to be passed to the callback, may be NULL
* @retval A handle for the binding or NULL on failure.
* The caller is required to release the handle via `app_releaseKeybind` when it is no longer required
*/
KeybindHandle app_registerKeybind(int sc, KeybindFn callback, void * opaque, const char * description);
KeybindHandle app_registerKeybind(int sc, int charcode, KeybindFn callback,
void * opaque, const char * description);
/**
* Release an existing key binding
@@ -152,4 +175,20 @@ void app_releaseKeybind(KeybindHandle * handle);
*/
void app_releaseAllKeybinds(void);
bool app_guestIsLinux(void);
bool app_guestIsWindows(void);
bool app_guestIsOSX(void);
bool app_guestIsBSD(void);
bool app_guestIsOther(void);
/**
* Enable/disable the LG display
*/
void app_stopVideo(bool stop);
/**
* Enable/disable the spice display
*/
bool app_useSpiceDisplay(bool enable);
#endif

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

51
client/include/evdev.h Normal file
View File

@@ -0,0 +1,51 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdbool.h>
/**
* initialize configuration options
*/
void evdev_earlyInit(void);
/**
* start the evdev layer
*/
bool evdev_start(void);
/**
* stop the evdev layer
*/
void evdev_stop(void);
/**
* grab the keyboard for exclusive access
*/
void evdev_grabKeyboard(void);
/**
* ungrab the keyboard
*/
void evdev_ungrabKeyboard(void);
/**
* returns true if input should only be processed by evdev
*/
bool evdev_isExclusive(void);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -0,0 +1,89 @@
/**
* 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_I_AUDIODEV_
#define _H_I_AUDIODEV_
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
typedef int (*LG_AudioPullFn)(uint8_t * dst, int frames);
typedef void (*LG_AudioPushFn)(uint8_t * src, int frames);
struct LG_AudioDevOps
{
/* internal name of the audio for debugging */
const char * name;
/* called very early to allow for option registration, optional */
void (*earlyInit)(void);
/* called to initialize the audio backend */
bool (*init)(void);
/* final free */
void (*free)(void);
struct
{
/* setup the stream for playback but don't start it yet
* Note: the pull function returns f32 samples
*/
void (*setup)(int channels, int sampleRate, int requestedPeriodFrames,
int * maxPeriodFrames, int * startFrames, LG_AudioPullFn pullFn);
/* called when there is data available to start playback */
void (*start)(void);
/* called when SPICE reports the audio stream has stopped */
void (*stop)(void);
/* [optional] called to set the volume of the channels */
void (*volume)(int channels, const uint16_t volume[]);
/* [optional] called to set muting of the output */
void (*mute)(bool mute);
/* return the current total playback latency in microseconds */
uint64_t (*latency)(void);
}
playback;
struct
{
/* start the record stream
* Note: currently SPICE only supports S16 samples so always assume so
*/
void (*start)(int channels, int sampleRate, LG_AudioPushFn pushFn);
/* called when SPICE reports the audio stream has stopped */
void (*stop)(void);
/* [optional] called to set the volume of the channels */
void (*volume)(int channels, const uint16_t volume[]);
/* [optional] called to set muting of the input */
void (*mute)(bool mute);
}
record;
};
#endif

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -84,12 +84,14 @@ LG_DSPointer;
typedef struct LG_DSInitParams
{
const char * title;
const char * appId;
int x, y, w, h;
bool center;
bool fullscreen;
bool resizable;
bool borderless;
bool maximize;
bool largeCursorDot;
// if true the renderer requires an OpenGL context
bool opengl;
@@ -110,6 +112,8 @@ typedef struct LGEvent LGEvent;
struct LG_DisplayServerOps
{
const char * name;
/* called before options are parsed, useful for registering options */
void (*setup)(void);
@@ -123,13 +127,13 @@ struct LG_DisplayServerOps
bool (*init)(const LG_DSInitParams params);
/* called at startup after window creation, renderer and SPICE is ready */
void (*startup)();
void (*startup)(void);
/* called just before final window destruction, before final free */
void (*shutdown)();
void (*shutdown)(void);
/* final free */
void (*free)();
void (*free)(void);
/*
* return a system specific property, returns false if unsupported or failure
@@ -170,14 +174,17 @@ struct LG_DisplayServerOps
/* dm specific cursor implementations */
void (*guestPointerUpdated)(double x, double y, double localX, double localY);
void (*setPointer)(LG_DSPointer pointer);
void (*grabKeyboard)();
void (*ungrabKeyboard)();
void (*grabKeyboard)(void);
void (*ungrabKeyboard)(void);
/* (un)grabPointer is used to toggle cursor tracking/confine in normal mode */
void (*grabPointer)();
void (*ungrabPointer)();
void (*grabPointer)(void);
void (*ungrabPointer)(void);
/* (un)capturePointer is used do toggle special cursor tracking in capture mode */
void (*capturePointer)();
void (*uncapturePointer)();
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);
@@ -185,14 +192,17 @@ struct LG_DisplayServerOps
/* called when the client needs to realign the pointer. This should simply
* call the appropriate app_handleMouse* method for the platform with zero
* deltas */
void (*realignPointer)();
void (*realignPointer)(void);
/* returns true if the position specified is actually valid */
bool (*isValidPointerPos)(int x, int y);
/* called to disable/enable the screensaver */
void (*inhibitIdle)();
void (*uninhibitIdle)();
void (*inhibitIdle)(void);
void (*uninhibitIdle)(void);
/* called to request activation */
void (*requestActivation)(void);
/* wait for the specified time without blocking UI processing/event loops */
void (*wait)(unsigned int time);
@@ -246,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

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -25,6 +25,8 @@
#include "common/types.h"
#define TICK_RATE 25
struct LG_OverlayOps
{
/* internal name of the overlay for debugging */
@@ -43,6 +45,10 @@ struct LG_OverlayOps
* optional, if omitted assumes false */
bool (*needs_render)(void * udata, bool interactive);
/* return true if the overlay currently requires overlay mode
* optional, if omitted assumes false */
bool (*needs_overlay)(void * udata);
/* perform the actual drawing/rendering
*
* `interactive` is true if the application is currently in overlay interaction
@@ -59,6 +65,15 @@ struct LG_OverlayOps
int (*render)(void * udata, bool interactive, struct Rect * windowRects,
int maxRects);
/* called TICK_RATE times a second by the application
*
* Note: This may not run in the same context as `render`!
*
* return true if the frame needs to be rendered
* optional, if omitted assumes false
*/
bool (*tick)(void * udata, unsigned long long tickCount);
/* TODO: add load/save settings capabillity */
};

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -27,17 +27,22 @@
#include "common/framebuffer.h"
#define IS_LG_RENDERER_VALID(x) \
((x)->getName && \
(x)->create && \
(x)->initialize && \
(x)->deinitialize && \
(x)->onRestart && \
(x)->onResize && \
(x)->onMouseShape && \
(x)->onMouseEvent && \
(x)->renderStartup && \
(x)->needsRender && \
(x)->render)
((x)->getName && \
(x)->create && \
(x)->initialize && \
(x)->deinitialize && \
(x)->onRestart && \
(x)->onResize && \
(x)->onMouseShape && \
(x)->onMouseEvent && \
(x)->renderStartup && \
(x)->render && \
(x)->createTexture && \
(x)->freeTexture && \
(x)->spiceConfigure && \
(x)->spiceDrawFill && \
(x)->spiceDrawBitmap && \
(x)->spiceShow)
typedef struct LG_RendererParams
{
@@ -66,9 +71,15 @@ LG_RendererRotate;
typedef struct LG_RendererFormat
{
FrameType type; // frame type
unsigned int width; // image width
unsigned int height; // image height
FrameType type; // frame type
bool hdr; // if the frame is HDR or not
bool hdrPQ; // if the HDR content is PQ mapped
unsigned int screenWidth; // actual width of the host
unsigned int screenHeight; // actual height of the host
unsigned int dataWidth; // the width of the packed data
unsigned int dataHeight; // the height of the packed data
unsigned int frameWidth; // width of frame transmitted
unsigned int frameHeight; // height of frame transmitted
unsigned int stride; // scanline width (zero if compresed)
unsigned int pitch; // scanline bytes (or compressed size)
unsigned int bpp; // bits per pixel (zero if compressed)
@@ -139,8 +150,8 @@ typedef struct LG_RendererOps
/* called when the mouse has moved or changed visibillity
* Context: cursorThread */
bool (*onMouseEvent)(LG_Renderer * renderer, const bool visible, const int x,
const int y);
bool (*onMouseEvent)(LG_Renderer * renderer, const bool visible, int x, int y,
const int hx, const int hy);
/* called when the frame format has changed
* Context: frameThread */
@@ -156,15 +167,36 @@ typedef struct LG_RendererOps
* Context: renderThread */
bool (*renderStartup)(LG_Renderer * renderer, bool useDMA);
/* returns if the render method must be called even if nothing has changed.
* Context: renderThread */
bool (*needsRender)(LG_Renderer * renderer);
/* called to render the scene
* Context: renderThread */
bool (*render)(LG_Renderer * renderer, LG_RendererRotate rotate,
const bool newFrame, const bool invalidateWindow,
void (*preSwap)(void * udata), void * udata);
/* called to create a texture from the specified 32-bit RGB image data. This
* method is for use with Dear ImGui
* Context: renderThread */
void * (*createTexture)(LG_Renderer * renderer,
int width, int height, uint8_t * data);
/* called to free a texture previously created by createTexture. This method
* is for use with Dear ImGui
* Context: renderThread */
void (*freeTexture)(LG_Renderer * renderer, void * texture);
/* setup the spice display */
void (*spiceConfigure)(LG_Renderer * renderer, int width, int height);
/* draw a filled rect on the spice display with the specified color */
void (*spiceDrawFill)(LG_Renderer * renderer, int x, int y, int width,
int height, uint32_t color);
/* draw an image on the spice display, data is RGBA32 */
void (*spiceDrawBitmap)(LG_Renderer * renderer, int x, int y, int width,
int height, int stride, uint8_t * data, bool topDown);
/* show the spice display */
void (*spiceShow)(LG_Renderer * renderer, bool show);
}
LG_RendererOps;

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -27,9 +27,23 @@
typedef struct ImVec2 ImVec2;
typedef struct
{
void * tex;
int width;
int height;
}
OverlayImage;
void overlayGetImGuiRect(struct Rect * rect);
ImVec2 * overlayGetScreenSize(void);
void overlayTextURL(const char * url, const char * text);
void overlayTextMaybeURL(const char * text, bool wrapped);
// create a texture from a SVG and scale it to fit the supplied width & height
bool overlayLoadSVG(const char * data, unsigned int size, OverlayImage * image,
int width, int height);
void overlayFreeImage(OverlayImage * image);
#endif

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -24,12 +24,7 @@
#include <stdlib.h>
#include <stdbool.h>
#include "common/types.h"
#define min(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; })
#define max(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; })
#define UPCAST(type, x) \
(type *)((uintptr_t)(x) - offsetof(type, base))
#include "common/util.h"
// reads the specified file into a new buffer
// the callee must free the buffer

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.10)
project(renderers LANGUAGES C)
set(RENDERER_H "${CMAKE_BINARY_DIR}/include/dynamic/renderers.h")

View File

@@ -1,122 +1,118 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.10)
project(renderer_EGL LANGUAGES C CXX)
find_package(PkgConfig)
pkg_check_modules(RENDERER_EGL REQUIRED IMPORTED_TARGET
egl
gl
egl
gl
)
pkg_check_modules(RENDERER_EGL_OPT IMPORTED_TARGET
wayland-egl
wayland-egl
)
find_program(AWK NAMES gawk mawk original-awk awk)
if(AWK MATCHES ".+-NOTFOUND")
message(FATAL_ERROR "FATAL: some known version of awk couldn't be found (${AWK}).")
message(FATAL_ERROR "FATAL: some known version of awk couldn't be found (${AWK}).")
else()
message(STATUS "Using awk: ${AWK}")
message(STATUS "Using awk: ${AWK}")
endif()
include(MakeObject)
function(build_shaders header_dir)
file(GLOB headers "${header_dir}/*.h")
set(EGL_SHADER_PROCESSED)
foreach(shader ${ARGN})
set(out_f "${CMAKE_CURRENT_BINARY_DIR}/${shader}")
add_custom_command(OUTPUT "${out_f}"
COMMAND "${AWK}" -f "${CMAKE_CURRENT_SOURCE_DIR}/glsl.include.awk"
"${CMAKE_CURRENT_SOURCE_DIR}/${shader}" > "${out_f}"
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/${shader}"
DEPENDS ${headers}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/shader"
COMMENT "Preprocessing shader ${shader}"
VERBATIM
)
endforeach()
file(GLOB headers "${header_dir}/*.h")
set(EGL_SHADER_PROCESSED)
foreach(shader ${ARGN})
set(out_f "${CMAKE_CURRENT_BINARY_DIR}/${shader}")
add_custom_command(OUTPUT "${out_f}"
COMMAND "${AWK}" -f "${CMAKE_CURRENT_SOURCE_DIR}/glsl.include.awk"
"${CMAKE_CURRENT_SOURCE_DIR}/${shader}" > "${out_f}"
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/${shader}"
DEPENDS ${headers}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/shader"
COMMENT "Preprocessing shader ${shader}"
VERBATIM
)
endforeach()
set(CMAKE_CURRENT_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
make_object(
EGL_SHADER
${ARGN}
)
set(CMAKE_CURRENT_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
make_object(
EGL_SHADER
${ARGN}
)
set(EGL_SHADER_OBJS "${EGL_SHADER_OBJS}" PARENT_SCOPE)
set(EGL_SHADER_INCS "${EGL_SHADER_INCS}" PARENT_SCOPE)
set(EGL_SHADER_OBJS "${EGL_SHADER_OBJS}" PARENT_SCOPE)
set(EGL_SHADER_INCS "${EGL_SHADER_INCS}" PARENT_SCOPE)
endfunction()
build_shaders(
shader
shader/desktop.vert
shader/desktop_rgb.frag
shader/cursor.vert
shader/cursor_rgb.frag
shader/cursor_mono.frag
shader/damage.vert
shader/damage.frag
shader/splash_bg.vert
shader/splash_bg.frag
shader/splash_logo.vert
shader/splash_logo.frag
shader/basic.vert
shader/ffx_cas.frag
shader/ffx_fsr1_easu.frag
shader/ffx_fsr1_rcas.frag
shader/downscale.frag
shader/downscale_lanczos2.frag
shader/downscale_linear.frag
shader
shader/desktop.vert
shader/desktop_rgb.frag
shader/cursor.vert
shader/cursor_rgb.frag
shader/cursor_mono.frag
shader/damage.vert
shader/damage.frag
shader/basic.vert
shader/convert_24bit.frag
shader/ffx_cas.frag
shader/ffx_fsr1_easu.frag
shader/ffx_fsr1_rcas.frag
shader/downscale.frag
shader/downscale_lanczos2.frag
shader/downscale_linear.frag
)
make_defines(
"${CMAKE_CURRENT_SOURCE_DIR}/shader/desktop_rgb.frag"
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
"${CMAKE_CURRENT_SOURCE_DIR}/shader/desktop_rgb.frag"
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
)
add_library(renderer_EGL STATIC
egl.c
egldebug.c
shader.c
texture_util.c
texture.c
texture_buffer.c
texture_framebuffer.c
texture_dmabuf.c
model.c
desktop.c
desktop_rects.c
cursor.c
draw.c
splash.c
damage.c
framebuffer.c
postprocess.c
ffx.c
filter.c
filter_ffx_cas.c
filter_ffx_fsr1.c
filter_downscale.c
${EGL_SHADER_OBJS}
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
${PROJECT_TOP}/repos/cimgui/imgui/backends/imgui_impl_opengl3.cpp
egl.c
egldebug.c
shader.c
texture_util.c
texture.c
texture_buffer.c
texture_framebuffer.c
texture_dmabuf.c
model.c
desktop.c
desktop_rects.c
cursor.c
damage.c
framebuffer.c
postprocess.c
ffx.c
filter.c
filter_24bit.c
filter_ffx_cas.c
filter_ffx_fsr1.c
filter_downscale.c
${EGL_SHADER_OBJS}
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
${PROJECT_TOP}/repos/cimgui/imgui/backends/imgui_impl_opengl3.cpp
)
target_compile_definitions(renderer_EGL PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1 IMGUI_IMPL_OPENGL_ES3)
target_link_libraries(renderer_EGL
PkgConfig::RENDERER_EGL
lg_common
PkgConfig::RENDERER_EGL
lg_common
cimgui
cimgui
)
if(RENDERER_EGL_OPT_FOUND)
target_link_libraries(renderer_EGL
PkgConfig::RENDERER_EGL_OPT
)
target_link_libraries(renderer_EGL
PkgConfig::RENDERER_EGL_OPT
)
endif()
target_include_directories(renderer_EGL
PRIVATE
src
${EGL_SHADER_INCS}
PRIVATE
src
${EGL_SHADER_INCS}
)

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -42,6 +42,7 @@ struct CursorTex
struct EGL_Texture * texture;
struct EGL_Shader * shader;
GLuint uMousePos;
GLuint uScale;
GLuint uRotate;
GLuint uCBMode;
};
@@ -73,7 +74,9 @@ struct EGL_Cursor
int cbMode;
_Atomic(struct CursorPos) pos;
_Atomic(struct CursorPos) hs;
_Atomic(struct CursorSize) size;
_Atomic(float) scale;
struct CursorTex norm;
struct CursorTex mono;
@@ -84,7 +87,7 @@ static bool cursorTexInit(struct CursorTex * t,
const char * vertex_code , size_t vertex_size,
const char * fragment_code, size_t fragment_size)
{
if (!egl_textureInit(&t->texture, NULL, EGL_TEXTYPE_BUFFER, false))
if (!egl_textureInit(&t->texture, NULL, EGL_TEXTYPE_BUFFER))
{
DEBUG_ERROR("Failed to initialize the cursor texture");
return false;
@@ -97,34 +100,28 @@ static bool cursorTexInit(struct CursorTex * t,
}
if (!egl_shaderCompile(t->shader,
vertex_code, vertex_size, fragment_code, fragment_size))
vertex_code, vertex_size, fragment_code, fragment_size, false, NULL))
{
DEBUG_ERROR("Failed to compile the cursor shader");
return false;
}
t->uMousePos = egl_shaderGetUniform(t->shader, "mouse" );
t->uRotate = egl_shaderGetUniform(t->shader, "rotate");
t->uCBMode = egl_shaderGetUniform(t->shader, "cbMode");
t->uMousePos = egl_shaderGetUniform(t->shader, "mouse" );
t->uScale = egl_shaderGetUniform(t->shader, "scale" );
t->uRotate = egl_shaderGetUniform(t->shader, "rotate" );
t->uCBMode = egl_shaderGetUniform(t->shader, "cbMode" );
return true;
}
static inline void setCursorTexUniforms(EGL_Cursor * cursor,
struct CursorTex * t, bool mono, float x, float y, float w, float h)
struct CursorTex * t, bool mono, float x, float y,
float w, float h, float scale)
{
if (mono)
{
glUniform4f(t->uMousePos, x, y, w, h / 2);
glUniform1i(t->uRotate , cursor->rotate);
glUniform1i(t->uCBMode , cursor->cbMode);
}
else
{
glUniform4f(t->uMousePos, x, y, w, h);
glUniform1i(t->uRotate , cursor->rotate);
glUniform1i(t->uCBMode , cursor->cbMode);
}
glUniform4f(t->uMousePos, x, y, w, mono ? h / 2 : h);
glUniform1f(t->uScale , scale);
glUniform1i(t->uRotate , cursor->rotate);
glUniform1i(t->uCBMode , cursor->cbMode);
}
static void cursorTexFree(struct CursorTex * t)
@@ -166,9 +163,12 @@ bool egl_cursorInit(EGL_Cursor ** cursor)
(*cursor)->cbMode = option_get_int("egl", "cbMode");
struct CursorPos pos = { .x = 0, .y = 0 };
struct CursorPos hs = { .x = 0, .y = 0 };
struct CursorSize size = { .w = 0, .h = 0 };
atomic_init(&(*cursor)->pos, pos);
atomic_init(&(*cursor)->size, size);
atomic_init(&(*cursor)->pos , pos );
atomic_init(&(*cursor)->hs , hs );
atomic_init(&(*cursor)->size , size);
atomic_init(&(*cursor)->scale, 1.0f);
return true;
}
@@ -229,11 +229,19 @@ void egl_cursorSetSize(EGL_Cursor * cursor, const float w, const float h)
atomic_store(&cursor->size, size);
}
void egl_cursorSetState(EGL_Cursor * cursor, const bool visible, const float x, const float y)
void egl_cursorSetScale(EGL_Cursor * cursor, const float scale)
{
atomic_store(&cursor->scale, scale);
}
void egl_cursorSetState(EGL_Cursor * cursor, const bool visible,
const float x, const float y, const float hx, const float hy)
{
cursor->visible = visible;
struct CursorPos pos = { .x = x, .y = y};
struct CursorPos pos = { .x = x , .y = y };
struct CursorPos hs = { .x = hx, .y = hy };
atomic_store(&cursor->pos, pos);
atomic_store(&cursor->hs , hs);
}
struct CursorState egl_cursorRender(EGL_Cursor * cursor,
@@ -252,22 +260,43 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
switch(cursor->type)
{
case LG_CURSOR_MASKED_COLOR:
// fall through
{
uint32_t xor[cursor->height][cursor->width];
for(int y = 0; y < cursor->height; ++y)
for(int x = 0; x < cursor->width; ++x)
{
uint32_t * src = (uint32_t *)(data + (cursor->stride * y) + x * 4);
const bool masked = (*src & 0xFF000000) != 0;
if (masked)
*src = xor[y][x] = *src & 0x00FFFFFF;
else
{
xor[y][x] = 0xFF000000;
*src |= 0xFF000000;
}
}
egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA,
cursor->width, cursor->height, cursor->width, sizeof(xor[0]));
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true);
}
// fall through
case LG_CURSOR_COLOR:
{
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->stride);
egl_textureUpdate(cursor->norm.texture, data);
egl_modelSetTexture(cursor->model, cursor->norm.texture);
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
cursor->width, cursor->height, cursor->width, cursor->stride);
egl_textureUpdate(cursor->norm.texture, data, true);
break;
}
case LG_CURSOR_MONOCHROME:
{
uint32_t and[cursor->width * cursor->height];
uint32_t xor[cursor->width * cursor->height];
uint32_t and[cursor->height][cursor->width];
uint32_t xor[cursor->height][cursor->width];
for(int y = 0; y < cursor->height; ++y)
{
for(int x = 0; x < cursor->width; ++x)
{
const uint8_t * srcAnd = data + (cursor->stride * y) + (x / 8);
@@ -276,14 +305,17 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
const uint32_t andMask = (*srcAnd & mask) ? 0xFFFFFFFF : 0xFF000000;
const uint32_t xorMask = (*srcXor & mask) ? 0x00FFFFFF : 0x00000000;
and[y * cursor->width + x] = andMask;
xor[y * cursor->width + x] = xorMask;
and[y][x] = andMask;
xor[y][x] = xorMask;
}
}
egl_textureSetup (cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4);
egl_textureSetup (cursor->mono.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4);
egl_textureUpdate(cursor->norm.texture, (uint8_t *)and);
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor);
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
cursor->width, cursor->height, cursor->width, sizeof(and[0]));
egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA,
cursor->width, cursor->height, cursor->width, sizeof(xor[0]));
egl_textureUpdate(cursor->norm.texture, (uint8_t *)and, true);
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true);
break;
}
}
@@ -292,8 +324,15 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
cursor->rotate = rotate;
struct CursorPos pos = atomic_load(&cursor->pos);
struct CursorSize size = atomic_load(&cursor->size);
struct CursorPos pos = atomic_load(&cursor->pos );
float scale = atomic_load(&cursor->scale);
struct CursorPos hs = atomic_load(&cursor->hs );
struct CursorSize size = atomic_load(&cursor->size );
pos.x -= hs.x * scale;
pos.y -= hs.y * scale;
size.w *= scale;
size.h *= scale;
struct CursorState state = {
.visible = true,
@@ -342,13 +381,33 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
case LG_CURSOR_MONOCHROME:
{
egl_shaderUse(cursor->norm.shader);
setCursorTexUniforms(cursor, &cursor->norm, true, pos.x, pos.y, size.w, size.h);
setCursorTexUniforms(cursor, &cursor->norm, true, pos.x, pos.y,
size.w, size.h, scale);
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
egl_modelSetTexture(cursor->model, cursor->norm.texture);
egl_modelRender(cursor->model);
egl_shaderUse(cursor->mono.shader);
setCursorTexUniforms(cursor, &cursor->mono, true, pos.x, pos.y, size.w, size.h);
setCursorTexUniforms(cursor, &cursor->mono, true, pos.x, pos.y,
size.w, size.h, scale);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
egl_modelSetTexture(cursor->model, cursor->mono.texture);
egl_modelRender(cursor->model);
break;
}
case LG_CURSOR_MASKED_COLOR:
{
egl_shaderUse(cursor->norm.shader);
setCursorTexUniforms(cursor, &cursor->norm, false, pos.x, pos.y,
size.w, size.h, scale);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
egl_modelSetTexture(cursor->model, cursor->norm.texture);
egl_modelRender(cursor->model);
egl_shaderUse(cursor->mono.shader);
setCursorTexUniforms(cursor, &cursor->mono, false, pos.x, pos.y,
size.w, size.h, scale);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
egl_modelSetTexture(cursor->model, cursor->mono.texture);
egl_modelRender(cursor->model);
@@ -358,17 +417,10 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
case LG_CURSOR_COLOR:
{
egl_shaderUse(cursor->norm.shader);
setCursorTexUniforms(cursor, &cursor->norm, false, pos.x, pos.y, size.w, size.h);
setCursorTexUniforms(cursor, &cursor->norm, false, pos.x, pos.y,
size.w, size.h, scale);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
egl_modelRender(cursor->model);
break;
}
case LG_CURSOR_MASKED_COLOR:
{
egl_shaderUse(cursor->mono.shader);
setCursorTexUniforms(cursor, &cursor->mono, false, pos.x, pos.y, size.w, size.h);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
egl_modelSetTexture(cursor->model, cursor->norm.texture);
egl_modelRender(cursor->model);
break;
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -45,8 +45,10 @@ bool egl_cursorSetShape(
void egl_cursorSetSize(EGL_Cursor * cursor, const float x, const float y);
void egl_cursorSetScale(EGL_Cursor * cursor, const float scale);
void egl_cursorSetState(EGL_Cursor * cursor, const bool visible,
const float x, const float y);
const float x, const float y, const float hx, const float hy);
struct CursorState egl_cursorRender(EGL_Cursor * cursor,
LG_RendererRotate rotate, int width, int height);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -76,7 +76,8 @@ bool egl_damageInit(EGL_Damage ** damage)
if (!egl_shaderCompile((*damage)->shader,
b_shader_damage_vert, b_shader_damage_vert_size,
b_shader_damage_frag, b_shader_damage_frag_size))
b_shader_damage_frag, b_shader_damage_frag_size,
false, NULL))
{
DEBUG_ERROR("Failed to compile the damage shader");
return false;

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -46,9 +46,14 @@ struct DesktopShader
EGL_Shader * shader;
GLint uTransform;
GLint uDesktopSize;
GLint uSamplerType;
GLint uScaleAlgo;
GLint uNVGain;
GLint uCBMode;
GLint uIsHDR;
GLint uMapHDRtoSDR;
GLint uMapHDRGain;
GLint uMapHDRPQ;
};
struct EGL_Desktop
@@ -57,15 +62,20 @@ struct EGL_Desktop
EGLDisplay * display;
EGL_Texture * texture;
GLuint sampler;
struct DesktopShader shader;
struct DesktopShader dmaShader, shader;
EGL_DesktopRects * mesh;
CountedBuffer * matrix;
// internals
int width, height;
bool hdr;
bool hdrPQ;
LG_RendererRotate rotate;
bool useSpice;
int spiceWidth, spiceHeight;
EGL_Texture * spiceTexture;
// scale algorithm
int scaleAlgo;
@@ -79,6 +89,11 @@ struct EGL_Desktop
bool useDMA;
LG_RendererFormat format;
// map HDR content to SDR
bool mapHDRtoSDR;
int peakLuminance;
int maxCLL;
EGL_PostProcess * pp;
_Atomic(bool) processFrame;
};
@@ -89,7 +104,8 @@ void toggleNV(int key, void * opaque);
static bool egl_initDesktopShader(
struct DesktopShader * shader,
const char * vertex_code , size_t vertex_size,
const char * fragment_code, size_t fragment_size
const char * fragment_code, size_t fragment_size,
bool useDMA
)
{
if (!egl_shaderInit(&shader->shader))
@@ -97,16 +113,21 @@ static bool egl_initDesktopShader(
if (!egl_shaderCompile(shader->shader,
vertex_code , vertex_size,
fragment_code, fragment_size))
fragment_code, fragment_size,
useDMA, NULL))
{
return false;
}
shader->uTransform = egl_shaderGetUniform(shader->shader, "transform" );
shader->uDesktopSize = egl_shaderGetUniform(shader->shader, "desktopSize");
shader->uScaleAlgo = egl_shaderGetUniform(shader->shader, "scaleAlgo" );
shader->uNVGain = egl_shaderGetUniform(shader->shader, "nvGain" );
shader->uCBMode = egl_shaderGetUniform(shader->shader, "cbMode" );
shader->uDesktopSize = egl_shaderGetUniform(shader->shader, "desktopSize" );
shader->uTransform = egl_shaderGetUniform(shader->shader, "transform" );
shader->uScaleAlgo = egl_shaderGetUniform(shader->shader, "scaleAlgo" );
shader->uNVGain = egl_shaderGetUniform(shader->shader, "nvGain" );
shader->uCBMode = egl_shaderGetUniform(shader->shader, "cbMode" );
shader->uIsHDR = egl_shaderGetUniform(shader->shader, "isHDR" );
shader->uMapHDRtoSDR = egl_shaderGetUniform(shader->shader, "mapHDRtoSDR" );
shader->uMapHDRGain = egl_shaderGetUniform(shader->shader, "mapHDRGain" );
shader->uMapHDRPQ = egl_shaderGetUniform(shader->shader, "mapHDRPQ" );
return true;
}
@@ -126,21 +147,12 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
desktop->display = display;
if (!egl_textureInit(&desktop->texture, display,
useDMA ? EGL_TEXTYPE_DMABUF : EGL_TEXTYPE_FRAMEBUFFER, true))
useDMA ? EGL_TEXTYPE_DMABUF : EGL_TEXTYPE_FRAMEBUFFER))
{
DEBUG_ERROR("Failed to initialize the desktop texture");
return false;
}
if (!egl_initDesktopShader(
&desktop->shader,
b_shader_desktop_vert , b_shader_desktop_vert_size,
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size))
{
DEBUG_ERROR("Failed to initialize the generic desktop shader");
return false;
}
if (!egl_desktopRectsInit(&desktop->mesh, maxRects))
{
DEBUG_ERROR("Failed to initialize the desktop mesh");
@@ -154,7 +166,28 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
return false;
}
app_registerKeybind(KEY_N, toggleNV, desktop,
if (!egl_initDesktopShader(
&desktop->shader,
b_shader_desktop_vert , b_shader_desktop_vert_size,
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size,
false))
{
DEBUG_ERROR("Failed to initialize the desktop shader");
return false;
}
if (useDMA)
if (!egl_initDesktopShader(
&desktop->dmaShader,
b_shader_desktop_vert , b_shader_desktop_vert_size,
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size,
true))
{
DEBUG_ERROR("Failed to initialize the desktop DMA shader");
return false;
}
app_registerKeybind(0, 'N', toggleNV, desktop,
"Toggle night vision mode");
desktop->nvMax = option_get_int("egl", "nvGainMax");
@@ -163,12 +196,19 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
desktop->scaleAlgo = option_get_int("egl", "scale" );
desktop->useDMA = useDMA;
desktop->mapHDRtoSDR = option_get_bool("egl", "mapHDRtoSDR" );
desktop->peakLuminance = option_get_int ("egl", "peakLuminance");
desktop->maxCLL = option_get_int ("egl", "maxCLL" );
if (!egl_postProcessInit(&desktop->pp))
{
DEBUG_ERROR("Failed to initialize the post process manager");
return false;
}
// this MUST be first
egl_postProcessAdd(desktop->pp, &egl_filter24bitOps);
egl_postProcessAdd(desktop->pp, &egl_filterDownscaleOps);
egl_postProcessAdd(desktop->pp, &egl_filterFFXCASOps );
egl_postProcessAdd(desktop->pp, &egl_filterFFXFSR1Ops );
@@ -202,10 +242,12 @@ void egl_desktopFree(EGL_Desktop ** desktop)
if (!*desktop)
return;
egl_textureFree (&(*desktop)->texture );
egl_shaderFree (&(*desktop)->shader.shader);
egl_desktopRectsFree(&(*desktop)->mesh );
countedBufferRelease(&(*desktop)->matrix );
egl_textureFree (&(*desktop)->texture );
egl_textureFree (&(*desktop)->spiceTexture );
egl_shaderFree (&(*desktop)->shader .shader);
egl_shaderFree (&(*desktop)->dmaShader.shader);
egl_desktopRectsFree(&(*desktop)->mesh );
countedBufferRelease(&(*desktop)->matrix );
egl_postProcessFree(&(*desktop)->pp);
@@ -228,7 +270,8 @@ void egl_desktopConfigUI(EGL_Desktop * desktop)
for (int i = 0; i < EGL_SCALE_MAX; ++i)
{
bool selected = i == desktop->scaleAlgo;
if (igSelectableBool(algorithmNames[i], selected, 0, (ImVec2) { 0.0f, 0.0f }))
if (igSelectable_Bool(algorithmNames[i], selected, 0,
(ImVec2) { 0.0f, 0.0f }))
desktop->scaleAlgo = i;
if (selected)
igSetItemDefaultFocus();
@@ -250,6 +293,28 @@ void egl_desktopConfigUI(EGL_Desktop * desktop)
}
igSliderInt("##nvgain", &desktop->nvGain, 0, desktop->nvMax, format, 0);
igPopItemWidth();
bool mapHDRtoSDR = desktop->mapHDRtoSDR;
int peakLuminance = desktop->peakLuminance;
int maxCLL = desktop->maxCLL;
igSeparator();
igCheckbox("Map HDR content to SDR", &mapHDRtoSDR);
igSliderInt("Peak Luminance", &peakLuminance, 1, 10000,
"%d nits",
ImGuiInputTextFlags_CharsDecimal);
igSliderInt("Max content light level", &maxCLL, 1, 10000,
"%d nits", ImGuiInputTextFlags_CharsDecimal);
if (mapHDRtoSDR != desktop->mapHDRtoSDR ||
peakLuminance != desktop->peakLuminance ||
maxCLL != desktop->maxCLL)
{
desktop->mapHDRtoSDR = mapHDRtoSDR;
desktop->peakLuminance = max(1, peakLuminance);
desktop->maxCLL = max(1, maxCLL);
app_invalidateWindow(true);
}
}
bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
@@ -275,41 +340,46 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
pixFmt = EGL_PF_RGBA16F;
break;
case FRAME_TYPE_BGR_32:
pixFmt = EGL_PF_BGR_32;
break;
case FRAME_TYPE_RGB_24:
pixFmt = EGL_PF_RGB_24;
break;
default:
DEBUG_ERROR("Unsupported frame format");
return false;
}
desktop->width = format.width;
desktop->height = format.height;
desktop->width = format.frameWidth;
desktop->height = format.frameHeight;
desktop->hdr = format.hdr;
desktop->hdrPQ = format.hdrPQ;
if (!egl_textureSetup(
desktop->texture,
pixFmt,
format.width,
format.height,
format.pitch
desktop->format.dataWidth,
desktop->format.dataHeight,
desktop->format.stride,
desktop->format.pitch
))
{
DEBUG_ERROR("Failed to setup the desktop texture");
return false;
}
glGenSamplers(1, &desktop->sampler);
glSamplerParameteri(desktop->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(desktop->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(desktop->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glSamplerParameteri(desktop->sampler, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
return true;
}
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd,
const FrameDamageRect * damageRects, int damageRectsCount)
{
if (desktop->useDMA && dmaFd >= 0)
if (likely(desktop->useDMA && dmaFd >= 0))
{
if (egl_textureUpdateFromDMA(desktop->texture, frame, dmaFd))
if (likely(egl_textureUpdateFromDMA(desktop->texture, frame, dmaFd)))
{
atomic_store(&desktop->processFrame, true);
return true;
@@ -335,7 +405,7 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dma
egl_textureFree(&desktop->texture);
if (!egl_textureInit(&desktop->texture, desktop->display,
EGL_TEXTYPE_FRAMEBUFFER, true))
EGL_TEXTYPE_FRAMEBUFFER))
{
DEBUG_ERROR("Failed to initialize the desktop texture");
return false;
@@ -345,8 +415,8 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dma
return false;
}
if (egl_textureUpdateFromFrame(desktop->texture, frame,
damageRects, damageRectsCount))
if (likely(egl_textureUpdateFromFrame(desktop->texture, frame,
damageRects, damageRectsCount)))
{
atomic_store(&desktop->processFrame, true);
return true;
@@ -365,11 +435,30 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
LG_RendererRotate rotate, const struct DamageRects * rects)
{
if (outputWidth == 0 && outputHeight == 0)
EGL_Texture * tex;
int width, height;
bool dma;
if (unlikely(desktop->useSpice))
{
tex = desktop->spiceTexture;
width = desktop->spiceWidth;
height = desktop->spiceHeight;
dma = false;
}
else
{
tex = desktop->texture;
width = desktop->width;
height = desktop->height;
dma = desktop->useDMA;
}
if (unlikely(outputWidth == 0 || outputHeight == 0))
DEBUG_FATAL("outputWidth || outputHeight == 0");
enum EGL_TexStatus status;
if ((status = egl_textureProcess(desktop->texture)) != EGL_TEX_STATUS_OK)
if (unlikely((status = egl_textureProcess(tex)) != EGL_TEX_STATUS_OK))
{
if (status != EGL_TEX_STATUS_NOTREADY)
DEBUG_ERROR("Failed to process the desktop texture");
@@ -378,26 +467,32 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
int scaleAlgo = EGL_SCALE_NEAREST;
egl_desktopRectsMatrix((float *)desktop->matrix->data,
desktop->width, desktop->height, x, y, scaleX, scaleY, rotate);
egl_desktopRectsUpdate(desktop->mesh, rects, desktop->width, desktop->height);
width, height, x, y, scaleX, scaleY, rotate);
egl_desktopRectsUpdate(desktop->mesh, rects, width, height);
if (atomic_exchange(&desktop->processFrame, false) ||
egl_postProcessConfigModified(desktop->pp))
egl_postProcessRun(desktop->pp, desktop->texture, desktop->mesh,
desktop->width, desktop->height, outputWidth, outputHeight);
egl_postProcessRun(desktop->pp, tex, desktop->mesh,
width, height, outputWidth, outputHeight, dma);
unsigned int finalSizeX, finalSizeY;
GLuint texture = egl_postProcessGetOutput(desktop->pp,
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);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, desktop->sampler);
egl_textureBind(texture);
if (finalSizeX > desktop->width || finalSizeY > desktop->height)
if (finalSizeX > width || finalSizeY > height)
scaleType = EGL_DESKTOP_DOWNSCALE;
switch (desktop->scaleAlgo)
@@ -420,7 +515,13 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
scaleAlgo = desktop->scaleAlgo;
}
const struct DesktopShader * shader = &desktop->shader;
const struct DesktopShader * shader =
desktop->useDMA && texture == desktop->texture ?
&desktop->dmaShader : &desktop->shader;
const float mapHDRGain =
(float)desktop->maxCLL / desktop->peakLuminance;
EGL_Uniform uniforms[] =
{
{
@@ -431,7 +532,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
{
.type = EGL_UNIFORM_TYPE_2F,
.location = shader->uDesktopSize,
.f = { desktop->width, desktop->height },
.f = { width, height },
},
{
.type = EGL_UNIFORM_TYPE_M3x2FV,
@@ -448,6 +549,26 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
.type = EGL_UNIFORM_TYPE_1I,
.location = shader->uCBMode,
.f = { desktop->cbMode }
},
{
.type = EGL_UNIFORM_TYPE_1I,
.location = shader->uIsHDR,
.i = { desktop->hdr }
},
{
.type = EGL_UNIFORM_TYPE_1I,
.location = shader->uMapHDRtoSDR,
.i = { desktop->mapHDRtoSDR }
},
{
.type = EGL_UNIFORM_TYPE_1F,
.location = shader->uMapHDRGain,
.f = { mapHDRGain }
},
{
.type = EGL_UNIFORM_TYPE_1I,
.location = shader->uMapHDRPQ,
.f = { desktop->hdrPQ }
}
};
@@ -457,3 +578,61 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height)
{
if (!desktop->spiceTexture)
if (!egl_textureInit(&desktop->spiceTexture, desktop->display,
EGL_TEXTYPE_BUFFER_MAP))
{
DEBUG_ERROR("Failed to initialize the spice desktop texture");
return;
}
if (!egl_textureSetup(
desktop->spiceTexture,
EGL_PF_BGRA,
width,
height,
width,
width * 4
))
{
DEBUG_ERROR("Failed to setup the spice desktop texture");
return;
}
desktop->spiceWidth = width;
desktop->spiceHeight = height;
}
void egl_desktopSpiceDrawFill(EGL_Desktop * desktop, int x, int y, int width,
int height, uint32_t color)
{
/* this is a fairly hacky way to do this, but since it's only for the fallback
* spice display it's not really an issue */
uint32_t line[width];
for(int x = 0; x < width; ++x)
line[x] = color;
for(int dy = 0; dy < height; ++dy)
egl_textureUpdateRect(desktop->spiceTexture,
x, y + dy, width, 1, width, sizeof(line), (uint8_t *)line, false);
atomic_store(&desktop->processFrame, true);
}
void egl_desktopSpiceDrawBitmap(EGL_Desktop * desktop, int x, int y, int width,
int height, int stride, uint8_t * data, bool topDown)
{
egl_textureUpdateRect(desktop->spiceTexture,
x, y, width, height, width, stride, data, topDown);
atomic_store(&desktop->processFrame, true);
}
void egl_desktopSpiceShow(EGL_Desktop * desktop, bool show)
{
desktop->useSpice = show;
atomic_store(&desktop->processFrame, true);
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -50,3 +50,10 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
unsigned int outputHeight, const float x, const float y,
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
LG_RendererRotate rotate, const struct DamageRects * rects);
void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height);
void egl_desktopSpiceDrawFill(EGL_Desktop * desktop, int x, int y, int width,
int height, uint32_t color);
void egl_desktopSpiceDrawBitmap(EGL_Desktop * desktop, int x, int y, int width,
int height, int stride, uint8_t * data, bool topDown);
void egl_desktopSpiceShow(EGL_Desktop * desktop, bool show);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -32,6 +32,10 @@
struct EGL_DesktopRects
{
GLfloat * lastVertices;
int lastVerticesCount;
int lastVerticesSize;
GLuint buffers[2];
GLuint vao;
int count;
@@ -88,6 +92,7 @@ void egl_desktopRectsFree(EGL_DesktopRects ** rects_)
glDeleteVertexArrays(1, &rects->vao);
glDeleteBuffers(2, rects->buffers);
free(rects->lastVertices);
free(rects);
*rects_ = NULL;
}
@@ -113,7 +118,8 @@ void egl_desktopRectsUpdate(EGL_DesktopRects * rects, const struct DamageRects *
return;
}
GLfloat vertices[(!data || data->count < 0 ? 1 : data->count) * 8];
const int count = (!data || data->count < 0 ? 1 : data->count) * 8;
GLfloat vertices[count];
if (!data || data->count < 0)
{
FrameDamageRect full = {
@@ -131,6 +137,30 @@ void egl_desktopRectsUpdate(EGL_DesktopRects * rects, const struct DamageRects *
rectToVertices(vertices + i * 8, data->rects + i);
}
// check if the value actually changed and needs updating
if (count == rects->lastVerticesCount &&
memcmp(rects->lastVertices, vertices, sizeof(GLfloat) * count) == 0)
return;
// ensure the local storage is large enough
if (count > rects->lastVerticesSize)
{
if (rects->lastVertices)
free(rects->lastVertices);
rects->lastVertices = malloc(sizeof(GLfloat) * count);
if (!rects->lastVertices)
{
DEBUG_ERROR("out of memory");
return;
}
rects->lastVerticesSize = count;
}
// copy the last value for later comparison
rects->lastVerticesCount = count;
memcpy(rects->lastVertices, vertices, sizeof(GLfloat) * count);
glBindBuffer(GL_ARRAY_BUFFER, rects->buffers[0]);
glBufferSubData(GL_ARRAY_BUFFER, 0, rects->count * 8 * sizeof(GLfloat), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
@@ -266,7 +296,7 @@ bool egl_screenToDesktop(struct FrameDamageRect * output, const double matrix[6]
void egl_desktopRectsRender(EGL_DesktopRects * rects)
{
if (!rects->count)
if (unlikely(!rects->count))
return;
glBindVertexArray(rects->vao);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,69 +0,0 @@
/**
* Looking Glass
* Copyright © 2017-2021 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 "draw.h"
#include <stdlib.h>
#include <math.h>
void egl_drawTorus(EGL_Model * model, unsigned int pts, float x, float y,
float inner, float outer)
{
GLfloat * v = malloc(sizeof(*v) * (pts + 1) * 6);
GLfloat * dst = v;
for(unsigned int i = 0; i <= pts; ++i)
{
const float angle = (i / (float)pts) * M_PI * 2.0f;
const float c = cos(angle);
const float s = sin(angle);
*dst = x + (inner * c); ++dst;
*dst = y + (inner * s); ++dst;
*dst = 0.0f; ++dst;
*dst = x + (outer * c); ++dst;
*dst = y + (outer * s); ++dst;
*dst = 0.0f; ++dst;
}
egl_modelAddVerts(model, v, NULL, (pts + 1) * 2);
free(v);
}
void egl_drawTorusArc(EGL_Model * model, unsigned int pts, float x, float y,
float inner, float outer, float s, float e)
{
GLfloat * v = malloc(sizeof(*v) * (pts + 1) * 6);
GLfloat * dst = v;
for(unsigned int i = 0; i <= pts; ++i)
{
const float angle = s + ((i / (float)pts) * e);
const float c = cos(angle);
const float s = sin(angle);
*dst = x + (inner * c); ++dst;
*dst = y + (inner * s); ++dst;
*dst = 0.0f; ++dst;
*dst = x + (outer * c); ++dst;
*dst = y + (outer * s); ++dst;
*dst = 0.0f; ++dst;
}
egl_modelAddVerts(model, v, NULL, (pts + 1) * 2);
free(v);
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -20,6 +20,7 @@
#include "interface/renderer.h"
#include "common/util.h"
#include "common/debug.h"
#include "common/KVMFR.h"
#include "common/option.h"
@@ -39,18 +40,15 @@
#include <math.h>
#include <string.h>
#include "app.h"
#include "egl_dynprocs.h"
#include "model.h"
#include "shader.h"
#include "damage.h"
#include "desktop.h"
#include "cursor.h"
#include "splash.h"
#include "postprocess.h"
#include "util.h"
#define SPLASH_FADE_TIME 1000000
#define MAX_BUFFER_AGE 3
#define DESKTOP_DAMAGE_COUNT 4
#define MAX_ACCUMULATED_DAMAGE ((KVMFR_MAX_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2) * MAX_BUFFER_AGE)
@@ -78,15 +76,11 @@ struct Inst
EGL_Desktop * desktop; // the desktop
EGL_Cursor * cursor; // the mouse cursor
EGL_Splash * splash; // the splash screen
EGL_Damage * damage; // the damage display
bool imgui; // if imgui was initialized
LG_RendererFormat format;
bool formatValid;
bool start;
uint64_t waitFadeTime;
bool waitDone;
int width, height;
float uiScale;
@@ -103,9 +97,11 @@ struct Inst
bool cursorVisible;
int cursorX , cursorY;
int cursorHX , cursorHY;
float mouseWidth , mouseHeight;
float mouseScaleX, mouseScaleY;
bool showDamage;
bool scalePointer;
struct CursorState cursorLast;
@@ -122,6 +118,9 @@ struct Inst
RingBuffer importTimings;
GraphHandle importGraph;
bool showSpice;
int spiceWidth, spiceHeight;
};
static struct Option egl_options[] =
@@ -197,6 +196,34 @@ static struct Option egl_options[] =
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
{
.module = "egl",
.name = "scalePointer",
.description = "Keep the pointer size 1:1 when downscaling",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "egl",
.name = "mapHDRtoSDR",
.description = "Map HDR content to the SDR color space",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "egl",
.name = "peakLuminance",
.description = "The peak luminance level in nits for HDR to SDR mapping",
.type = OPTION_TYPE_INT,
.value.x_int = 250,
},
{
.module = "egl",
.name = "maxCLL",
.description = "Maximum content light level in nits for HDR to SDR mapping",
.type = OPTION_TYPE_INT,
.value.x_int = 10000,
},
{0}
};
@@ -249,7 +276,8 @@ static bool egl_create(LG_Renderer ** renderer, const LG_RendererParams params,
this->desktopDamage[0].count = -1;
this->importTimings = ringbuffer_new(256, sizeof(float));
this->importGraph = app_registerGraph("IMPORT", this->importTimings, 0.0f, 5.0f);
this->importGraph = app_registerGraph("IMPORT", this->importTimings,
0.0f, 5.0f, NULL);
*needsOpenGL = false;
return true;
@@ -269,12 +297,10 @@ static void egl_deinitialize(LG_Renderer * renderer)
if (this->imgui)
ImGui_ImplOpenGL3_Shutdown();
app_unregisterGraph(this->importGraph);
ringbuffer_free(&this->importTimings);
egl_desktopFree(&this->desktop);
egl_cursorFree (&this->cursor);
egl_splashFree (&this->splash);
egl_damageFree (&this->damage);
LG_LOCK_FREE(this->lock);
@@ -313,7 +339,6 @@ static void egl_onRestart(LG_Renderer * renderer)
eglDestroyContext(this->display, this->frameContext);
this->frameContext = NULL;
this->start = false;
INTERLOCKED_SECTION(this->desktopDamageLock, {
this->desktopDamage[this->desktopDamageIdx].count = -1;
@@ -322,6 +347,17 @@ static void egl_onRestart(LG_Renderer * renderer)
static void egl_calc_mouse_size(struct Inst * this)
{
if (this->showSpice)
{
this->mouseScaleX = 2.0f / this->spiceWidth;
this->mouseScaleY = 2.0f / this->spiceHeight;
egl_cursorSetSize(this->cursor,
(this->mouseWidth * (1.0f / this->spiceWidth )) * this->scaleX,
(this->mouseHeight * (1.0f / this->spiceHeight)) * this->scaleY
);
return;
}
if (!this->formatValid)
return;
@@ -331,18 +367,18 @@ static void egl_calc_mouse_size(struct Inst * this)
{
case LG_ROTATE_0:
case LG_ROTATE_180:
this->mouseScaleX = 2.0f / this->format.width;
this->mouseScaleY = 2.0f / this->format.height;
w = this->format.width;
h = this->format.height;
this->mouseScaleX = 2.0f / this->format.screenWidth;
this->mouseScaleY = 2.0f / this->format.screenHeight;
w = this->format.screenWidth;
h = this->format.screenHeight;
break;
case LG_ROTATE_90:
case LG_ROTATE_270:
this->mouseScaleX = 2.0f / this->format.height;
this->mouseScaleY = 2.0f / this->format.width;
w = this->format.height;
h = this->format.width;
this->mouseScaleX = 2.0f / this->format.screenHeight;
this->mouseScaleY = 2.0f / this->format.screenWidth;
w = this->format.screenHeight;
h = this->format.screenWidth;
break;
default:
@@ -371,6 +407,19 @@ static void egl_calc_mouse_size(struct Inst * this)
static void egl_calc_mouse_state(struct Inst * this)
{
if (this->showSpice)
{
egl_cursorSetState(
this->cursor,
this->cursorVisible,
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleX,
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleY,
((float)this->cursorHX * this->mouseScaleX) * this->scaleX,
((float)this->cursorHY * this->mouseScaleY) * this->scaleY
);
return;
}
if (!this->formatValid)
return;
@@ -381,8 +430,10 @@ static void egl_calc_mouse_state(struct Inst * this)
egl_cursorSetState(
this->cursor,
this->cursorVisible,
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleX,
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleY
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleX,
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleY,
((float)this->cursorHX * this->mouseScaleX) * this->scaleX,
((float)this->cursorHY * this->mouseScaleY) * this->scaleY
);
break;
@@ -391,8 +442,10 @@ static void egl_calc_mouse_state(struct Inst * this)
egl_cursorSetState(
this->cursor,
this->cursorVisible,
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleY,
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleX
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleY,
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleX,
((float)this->cursorHX * this->mouseScaleX) * this->scaleY,
((float)this->cursorHY * this->mouseScaleY) * this->scaleX
);
break;
}
@@ -406,14 +459,14 @@ static void egl_update_scale_type(struct Inst * this)
{
case LG_ROTATE_0:
case LG_ROTATE_180:
width = this->format.width;
height = this->format.height;
width = this->format.frameWidth;
height = this->format.frameHeight;
break;
case LG_ROTATE_90:
case LG_ROTATE_270:
width = this->format.height;
height = this->format.width;
width = this->format.frameHeight;
height = this->format.frameWidth;
break;
}
@@ -449,8 +502,8 @@ static void egl_onResize(LG_Renderer * renderer, const int width, const int heig
if (destRect.valid)
{
this->translateX = 1.0f - (((this->destRect.w / 2) + this->destRect.x) * 2) / (float)this->width;
this->translateY = 1.0f - (((this->destRect.h / 2) + this->destRect.y) * 2) / (float)this->height;
this->translateX = -1.0f + (((this->destRect.w / 2) + this->destRect.x) * 2) / (float)this->width;
this->translateY = 1.0f - (((this->destRect.h / 2) + this->destRect.y) * 2) / (float)this->height;
this->scaleX = (float)this->destRect.w / (float)this->width;
this->scaleY = (float)this->destRect.h / (float)this->height;
this->viewportWidth = this->destRect.w;
@@ -465,6 +518,16 @@ static void egl_onResize(LG_Renderer * renderer, const int width, const int heig
this->screenScaleY = 1.0f / this->height;
egl_calc_mouse_state(this);
if (this->scalePointer)
{
float scale = max(1.0f,
this->formatValid ?
max(
(float)this->format.screenWidth / this->width,
(float)this->format.screenHeight / this->height)
: 1.0f);
egl_cursorSetScale(this->cursor, scale);
}
INTERLOCKED_SECTION(this->desktopDamageLock, {
this->desktopDamage[this->desktopDamageIdx].count = -1;
@@ -472,6 +535,7 @@ static void egl_onResize(LG_Renderer * renderer, const int width, const int heig
// this is needed to refresh the font atlas texture
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplOpenGL3_Init("#version 300 es");
ImGui_ImplOpenGL3_NewFrame();
egl_damageResize(this->damage, this->translateX, this->translateY, this->scaleX, this->scaleY);
@@ -497,12 +561,15 @@ static bool egl_onMouseShape(LG_Renderer * renderer, const LG_RendererCursor cur
return true;
}
static bool egl_onMouseEvent(LG_Renderer * renderer, const bool visible, const int x, const int y)
static bool egl_onMouseEvent(LG_Renderer * renderer, const bool visible,
int x, int y, const int hx, const int hy)
{
struct Inst * this = UPCAST(struct Inst, renderer);
this->cursorVisible = visible;
this->cursorX = x;
this->cursorY = y;
this->cursorX = x + hx;
this->cursorY = y + hy;
this->cursorHX = hx;
this->cursorHY = hy;
egl_calc_mouse_state(this);
return true;
}
@@ -514,7 +581,7 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo
this->formatValid = true;
/* this event runs in a second thread so we need to init it here */
if (!this->frameContext)
if (unlikely(!this->frameContext))
{
static EGLint attrs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
@@ -534,8 +601,14 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo
}
}
if (likely(this->scalePointer))
{
float scale = max(1.0f, (float)format.screenWidth / this->width);
egl_cursorSetScale(this->cursor, scale);
}
egl_update_scale_type(this);
egl_damageSetup(this->damage, format.width, format.height);
egl_damageSetup(this->damage, format.frameWidth, format.frameHeight);
/* we need full screen damage when the format changes */
INTERLOCKED_SECTION(this->desktopDamageLock, {
@@ -551,23 +624,27 @@ static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int d
struct Inst * this = UPCAST(struct Inst, renderer);
uint64_t start = nanotime();
if (!egl_desktopUpdate(this->desktop, frame, dmaFd, damageRects, damageRectsCount))
if (unlikely(!egl_desktopUpdate(
this->desktop, frame, dmaFd, damageRects, damageRectsCount)))
{
DEBUG_INFO("Failed to to update the desktop");
return false;
}
ringbuffer_push(this->importTimings, &(float){ (nanotime() - start) * 1e-6f });
this->start = true;
INTERLOCKED_SECTION(this->desktopDamageLock, {
struct DesktopDamage * damage = this->desktopDamage + this->desktopDamageIdx;
if (damage->count == -1 || damageRectsCount == 0 ||
damage->count + damageRectsCount >= KVMFR_MAX_DAMAGE_RECTS)
if (unlikely(
damage->count == -1 ||
damageRectsCount == 0 ||
damage->count + damageRectsCount >= KVMFR_MAX_DAMAGE_RECTS))
{
damage->count = -1;
}
else
{
memcpy(damage->rects + damage->count, damageRects, damageRectsCount * sizeof(FrameDamageRect));
memcpy(damage->rects + damage->count, damageRects,
damageRectsCount * sizeof(FrameDamageRect));
damage->count += damageRectsCount;
}
});
@@ -699,7 +776,7 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
EGLint attr[] =
{
EGL_BUFFER_SIZE , 24,
EGL_BUFFER_SIZE , 30,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SAMPLE_BUFFERS , maxSamples > 0 ? 1 : 0,
EGL_SAMPLES , maxSamples,
@@ -736,7 +813,13 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
}
const char * client_exts = eglQueryString(this->display, EGL_EXTENSIONS);
bool debugContext = option_get_bool("egl", "debug");
if (!client_exts)
{
DEBUG_ERROR("Failed to query EGL_EXTENSIONS");
return false;
}
bool debug = option_get_bool("egl", "debug");
EGLint ctxattr[5];
int ctxidx = 0;
@@ -746,17 +829,17 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
if (maj > 1 || (maj == 1 && min >= 5))
{
ctxattr[ctxidx++] = EGL_CONTEXT_OPENGL_DEBUG;
ctxattr[ctxidx++] = debugContext ? EGL_TRUE : EGL_FALSE;
ctxattr[ctxidx++] = debug ? EGL_TRUE : EGL_FALSE;
}
else if (util_hasGLExt(client_exts, "EGL_KHR_create_context"))
{
ctxattr[ctxidx++] = EGL_CONTEXT_FLAGS_KHR;
ctxattr[ctxidx++] = debugContext ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0;
ctxattr[ctxidx++] = debug ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0;
}
else if (debugContext)
else if (debug)
DEBUG_WARN("Cannot create debug contexts before EGL 1.5 without EGL_KHR_create_context");
ctxattr[ctxidx++] = EGL_NONE;
ctxattr[ctxidx] = EGL_NONE;
this->context = eglCreateContext(this->display, this->configs, EGL_NO_CONTEXT, ctxattr);
if (this->context == EGL_NO_CONTEXT)
@@ -784,15 +867,30 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
eglMakeCurrent(this->display, this->surface, this->surface, this->context);
const char * gl_exts = (const char *)glGetString(GL_EXTENSIONS);
if (!gl_exts)
{
DEBUG_ERROR("Failed to query GL_EXTENSIONS");
return false;
}
const char * vendor = (const char *)glGetString(GL_VENDOR);
if (!vendor)
{
DEBUG_ERROR("Failed to query GL_VENDOR");
return false;
}
DEBUG_INFO("EGL : %d.%d", maj, min);
DEBUG_INFO("Vendor : %s", vendor);
DEBUG_INFO("Renderer: %s", glGetString(GL_RENDERER));
DEBUG_INFO("Version : %s", glGetString(GL_VERSION ));
DEBUG_INFO("EGL APIs: %s", eglQueryString(this->display, EGL_CLIENT_APIS));
DEBUG_INFO("EGL Exts: %s", client_exts);
DEBUG_INFO("GL Exts : %s", gl_exts);
if (debug)
{
DEBUG_INFO("EGL Exts: %s", client_exts);
DEBUG_INFO("GL Exts : %s", gl_exts);
}
GLint esMaj, esMin;
glGetIntegerv(GL_MAJOR_VERSION, &esMaj);
@@ -818,6 +916,8 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
if (this->noSwapDamage)
DEBUG_WARN("egl:noSwapDamage specified, disabling swap buffers with damage.");
this->scalePointer = option_get_bool("egl", "scalePointer");
if (!g_egl_dynProcs.glEGLImageTargetTexture2DOES)
DEBUG_INFO("glEGLImageTargetTexture2DOES unavilable, DMA support disabled");
else if (!g_egl_dynProcs.eglCreateImage || !g_egl_dynProcs.eglDestroyImage)
@@ -839,7 +939,7 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
}
}
if (debugContext)
if (debug)
{
if ((esMaj > 3 || (esMaj == 3 && esMin >= 2)) && g_egl_dynProcs.glDebugMessageCallback)
{
@@ -871,12 +971,6 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
return false;
}
if (!egl_splashInit(&this->splash))
{
DEBUG_ERROR("Failed to initialize the splash screen");
return false;
}
if (!egl_damageInit(&this->damage))
{
DEBUG_ERROR("Failed to initialize the damage display");
@@ -895,12 +989,6 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
return true;
}
static bool egl_needsRender(LG_Renderer * renderer)
{
struct Inst * this = UPCAST(struct Inst, renderer);
return !this->waitDone;
}
inline static EGLint egl_bufferAge(struct Inst * this)
{
if (!this->hasBufferAge)
@@ -927,21 +1015,26 @@ inline static void renderLetterBox(struct Inst * this)
if (hLB)
{
glScissor(0.0f, 0.0f, this->destRect.x + 0.5f, this->height + 0.5f);
// left
glScissor(0, 0, this->destRect.x, this->height);
glClear(GL_COLOR_BUFFER_BIT);
// right
float x2 = this->destRect.x + this->destRect.w;
glScissor(x2 - 0.5f, 0.0f, this->width - x2 + 1.0f, this->height + 1.0f);
glScissor(x2, 0, this->width - x2, this->height);
glClear(GL_COLOR_BUFFER_BIT);
}
if (vLB)
{
glScissor(0.0f, 0.0f, this->width + 0.5f, this->destRect.y + 0.5f);
// top
glScissor(0, this->height - this->destRect.y,
this->width, this->destRect.y);
glClear(GL_COLOR_BUFFER_BIT);
float y2 = this->destRect.y + this->destRect.h;
glScissor(0.0f, y2 - 0.5f, this->width + 1.0f, this->height - y2 + 1.0f);
// bottom
int y2 = this->destRect.y + this->destRect.h;
glScissor(0, 0, this->width, this->height - y2);
glClear(GL_COLOR_BUFFER_BIT);
}
@@ -955,8 +1048,9 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
{
struct Inst * this = UPCAST(struct Inst, renderer);
EGLint bufferAge = egl_bufferAge(this);
bool renderAll = invalidateWindow || !this->start || this->hadOverlay ||
bufferAge <= 0 || bufferAge > MAX_BUFFER_AGE;
bool renderAll = invalidateWindow || this->hadOverlay ||
bufferAge <= 0 || bufferAge > MAX_BUFFER_AGE ||
this->showSpice;
bool hasOverlay = false;
struct CursorState cursorState = { .visible = false };
@@ -969,14 +1063,14 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
accumulated->count = 0;
INTERLOCKED_SECTION(this->desktopDamageLock, {
if (!renderAll)
if (likely(!renderAll))
{
for (int i = 0; i < bufferAge; ++i)
{
struct DesktopDamage * damage = this->desktopDamage +
IDX_AGO(this->desktopDamageIdx, i, DESKTOP_DAMAGE_COUNT);
if (damage->count < 0)
if (unlikely(damage->count < 0))
{
renderAll = true;
break;
@@ -989,8 +1083,8 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
int y = rect->y > 0 ? rect->y - 1 : 0;
accumulated->rects[accumulated->count++] = (struct FrameDamageRect) {
.x = x, .y = y,
.width = min(this->format.width - x, rect->width + 2),
.height = min(this->format.height - y, rect->height + 2),
.width = min(this->format.frameWidth - x, rect->width + 2),
.height = min(this->format.frameHeight - y, rect->height + 2),
};
}
}
@@ -1000,10 +1094,11 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
this->desktopDamage[this->desktopDamageIdx].count = 0;
});
if (!renderAll)
if (likely(!renderAll))
{
double matrix[6];
egl_screenToDesktopMatrix(matrix, this->format.width, this->format.height,
egl_screenToDesktopMatrix(matrix,
this->format.frameWidth, this->format.frameHeight,
this->translateX, this->translateY, this->scaleX, this->scaleY, rotate,
this->width, this->height);
@@ -1013,7 +1108,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
int count = this->overlayHistoryCount[idx];
struct Rect * damage = this->overlayHistory[idx];
if (count < 0)
if (unlikely(count < 0))
{
renderAll = true;
break;
@@ -1022,15 +1117,16 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
for (int j = 0; j < count; ++j)
accumulated->count += egl_screenToDesktop(
accumulated->rects + accumulated->count, matrix, damage + j,
this->format.width, this->format.height
this->format.frameWidth, this->format.frameHeight
);
}
accumulated->count = rectsMergeOverlapping(accumulated->rects, accumulated->count);
accumulated->count = rectsMergeOverlapping(accumulated->rects,
accumulated->count);
}
++this->overlayHistoryIdx;
if (this->start && this->destRect.w > 0 && this->destRect.h > 0)
if (likely(this->destRect.w > 0 && this->destRect.h > 0))
{
if (egl_desktopRender(this->desktop,
this->destRect.w, this->destRect.h,
@@ -1038,14 +1134,6 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
this->scaleX , this->scaleY ,
this->scaleType , rotate, renderAll ? NULL : accumulated))
{
if (!this->waitFadeTime)
{
if (!this->params.quickSplash)
this->waitFadeTime = microtime() + SPLASH_FADE_TIME;
else
this->waitDone = true;
}
cursorState = egl_cursorRender(this->cursor,
(this->format.rotate + rotate) % LG_ROTATE_MAX,
this->width, this->height);
@@ -1056,70 +1144,39 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
renderLetterBox(this);
if (!this->waitDone)
{
float a = 1.0f;
if (!this->waitFadeTime)
a = 1.0f;
else
{
uint64_t t = microtime();
if (t > this->waitFadeTime)
this->waitDone = true;
else
{
uint64_t delta = this->waitFadeTime - t;
a = 1.0f / SPLASH_FADE_TIME * delta;
}
}
if (!this->waitDone)
{
egl_splashRender(this->splash, a, this->splashRatio);
hasOverlay = true;
}
}
else if (!this->start)
{
egl_splashRender(this->splash, 1.0f, this->splashRatio);
hasOverlay = true;
}
hasOverlay |= egl_damageRender(this->damage, rotate, newFrame ? desktopDamage : NULL);
hasOverlay |= invalidateWindow;
hasOverlay |=
egl_damageRender(this->damage, rotate, newFrame ? desktopDamage : NULL) |
invalidateWindow;
struct Rect damage[KVMFR_MAX_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2];
int damageIdx = app_renderOverlay(damage, MAX_OVERLAY_RECTS);
switch (damageIdx)
if (unlikely(damageIdx != 0))
{
case 0: // no overlay
break;
case -1: // full damage
if (damageIdx == -1)
hasOverlay = true;
// fallthrough
default:
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData());
for (int i = 0; i < damageIdx; ++i)
damage[i].y = this->height - damage[i].y - damage[i].h;
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData());
for (int i = 0; i < damageIdx; ++i)
damage[i].y = this->height - damage[i].y - damage[i].h;
}
if (damageIdx >= 0 && cursorState.visible)
if (likely(damageIdx >= 0 && cursorState.visible))
damage[damageIdx++] = cursorState.rect;
int overlayHistoryIdx = this->overlayHistoryIdx % DESKTOP_DAMAGE_COUNT;
if (hasOverlay)
if (unlikely(hasOverlay))
this->overlayHistoryCount[overlayHistoryIdx] = -1;
else
{
if (damageIdx > 0)
memcpy(this->overlayHistory[overlayHistoryIdx], damage, damageIdx * sizeof(struct Rect));
if (unlikely(damageIdx > 0))
memcpy(this->overlayHistory[overlayHistoryIdx],
damage, damageIdx * sizeof(struct Rect));
this->overlayHistoryCount[overlayHistoryIdx] = damageIdx;
}
if (!hasOverlay && !this->hadOverlay)
if (unlikely(!hasOverlay && !this->hadOverlay))
{
if (this->cursorLast.visible)
damage[damageIdx++] = this->cursorLast.rect;
@@ -1130,7 +1187,8 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
else
{
double matrix[6];
egl_desktopToScreenMatrix(matrix, this->format.width, this->format.height,
egl_desktopToScreenMatrix(matrix,
this->format.frameWidth, this->format.frameHeight,
this->translateX, this->translateY, this->scaleX, this->scaleY, rotate,
this->width, this->height);
@@ -1145,10 +1203,78 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
this->cursorLast = cursorState;
preSwap(udata);
app_eglSwapBuffers(this->display, this->surface, damage, this->noSwapDamage ? 0 : damageIdx);
app_eglSwapBuffers(this->display, this->surface, damage,
this->noSwapDamage ? 0 : damageIdx);
return true;
}
static void * egl_createTexture(LG_Renderer * renderer,
int width, int height, uint8_t * data)
{
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA,
width,
height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
data);
glBindTexture(GL_TEXTURE_2D, 0);
return (void*)(intptr_t)tex;
}
static void egl_freeTexture(LG_Renderer * renderer, void * texture)
{
GLuint tex = (GLuint)(intptr_t)texture;
glDeleteTextures(1, &tex);
}
static void egl_spiceConfigure(LG_Renderer * renderer, int width, int height)
{
struct Inst * this = UPCAST(struct Inst, renderer);
this->spiceWidth = width;
this->spiceHeight = height;
egl_desktopSpiceConfigure(this->desktop, width, height);
}
static void egl_spiceDrawFill(LG_Renderer * renderer, int x, int y, int width,
int height, uint32_t color)
{
struct Inst * this = UPCAST(struct Inst, renderer);
egl_desktopSpiceDrawFill(this->desktop, x, y, width, height, color);
}
static void egl_spiceDrawBitmap(LG_Renderer * renderer, int x, int y, int width,
int height, int stride, uint8_t * data, bool topDown)
{
struct Inst * this = UPCAST(struct Inst, renderer);
egl_desktopSpiceDrawBitmap(this->desktop, x, y, width, height, stride,
data, topDown);
}
static void egl_spiceShow(LG_Renderer * renderer, bool show)
{
struct Inst * this = UPCAST(struct Inst, renderer);
this->showSpice = show;
egl_calc_mouse_size(this);
egl_desktopSpiceShow(this->desktop, show);
}
struct LG_RendererOps LGR_EGL =
{
.getName = egl_getName,
@@ -1164,6 +1290,12 @@ struct LG_RendererOps LGR_EGL =
.onFrameFormat = egl_onFrameFormat,
.onFrame = egl_onFrame,
.renderStartup = egl_renderStartup,
.needsRender = egl_needsRender,
.render = egl_render
.render = egl_render,
.createTexture = egl_createTexture,
.freeTexture = egl_freeTexture,
.spiceConfigure = egl_spiceConfigure,
.spiceDrawFill = egl_spiceDrawFill,
.spiceDrawBitmap = egl_spiceDrawBitmap,
.spiceShow = egl_spiceShow
};

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -25,6 +25,8 @@
typedef enum EGL_TexType
{
EGL_TEXTYPE_BUFFER,
EGL_TEXTYPE_BUFFER_MAP,
EGL_TEXTYPE_BUFFER_STREAM,
EGL_TEXTYPE_FRAMEBUFFER,
EGL_TEXTYPE_DMABUF
}
@@ -35,7 +37,10 @@ typedef enum EGL_PixelFormat
EGL_PF_RGBA,
EGL_PF_BGRA,
EGL_PF_RGBA10,
EGL_PF_RGBA16F
EGL_PF_RGBA16F,
EGL_PF_BGR_32,
EGL_PF_RGB_24,
EGL_PF_RGB_24_32
}
EGL_PixelFormat;
@@ -58,13 +63,17 @@ typedef struct EGL_TexSetup
/* the height of the texture in pixels */
size_t height;
/* the stide of the texture in bytes */
/* the row length of the texture in pixels */
size_t stride;
/* the row length of the texture in bytes */
size_t pitch;
}
EGL_TexSetup;
typedef enum EGL_FilterType
{
EGL_FILTER_TYPE_INTERNAL,
EGL_FILTER_TYPE_EFFECT,
EGL_FILTER_TYPE_UPSCALE,
EGL_FILTER_TYPE_DOWNSCALE

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -68,18 +68,21 @@ typedef struct EGL_FilterOps
/* reads filter state from options */
void (*loadState)(EGL_Filter * filter);
/* set the input format of the filter */
/* set the input format of the filter
* useDMA will be true if the texture provided needs to use samplerExternalOES
*/
bool (*setup)(EGL_Filter * filter, enum EGL_PixelFormat pixFmt,
unsigned int width, unsigned int height);
unsigned int width, unsigned int height,
unsigned int desktopWidth, unsigned int desktopHeight, bool useDMA);
/* set the output resolution hint for the filter
* this is optional and only a hint */
void (*setOutputResHint)(EGL_Filter * filter,
unsigned int x, unsigned int y);
/* returns the output resolution of the filter */
/* returns the output resolution and pixel format of the filter */
void (*getOutputRes)(EGL_Filter * filter,
unsigned int *x, unsigned int *y);
unsigned int *x, unsigned int *y, enum EGL_PixelFormat *pixFmt);
/* prepare the shader for use
* A filter can return false to bypass it */
@@ -87,8 +90,8 @@ typedef struct EGL_FilterOps
/* runs the filter on the provided texture
* returns the processed texture as the output */
GLuint (*run)(EGL_Filter * filter, EGL_FilterRects * rects,
GLuint texture);
EGL_Texture * (*run)(EGL_Filter * filter, EGL_FilterRects * rects,
EGL_Texture * texture);
/* called when the filter output is no loger needed so it can release memory
* this is optional */
@@ -102,6 +105,12 @@ typedef struct EGL_Filter
}
EGL_Filter;
static inline void egl_filterEarlyInit(const EGL_FilterOps * ops)
{
if (ops->earlyInit)
ops->earlyInit();
}
static inline bool egl_filterInit(const EGL_FilterOps * ops, EGL_Filter ** filter)
{
if (!ops->init(filter))
@@ -119,23 +128,30 @@ static inline void egl_filterFree(EGL_Filter ** filter)
static inline bool egl_filterImguiConfig(EGL_Filter * filter)
{
return filter->ops.imguiConfig(filter);
if (filter->ops.imguiConfig)
return filter->ops.imguiConfig(filter);
return false;
}
static inline void egl_filterSaveState(EGL_Filter * filter)
{
filter->ops.saveState(filter);
if (filter->ops.saveState)
filter->ops.saveState(filter);
}
static inline void egl_filterLoadState(EGL_Filter * filter)
{
filter->ops.loadState(filter);
if (filter->ops.loadState)
filter->ops.loadState(filter);
}
static inline bool egl_filterSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
unsigned int desktopWidth, unsigned int desktopHeight,
bool useDMA)
{
return filter->ops.setup(filter, pixFmt, width, height);
return filter->ops.setup(filter, pixFmt, width, height,
desktopWidth, desktopHeight, useDMA);
}
static inline void egl_filterSetOutputResHint(EGL_Filter * filter,
@@ -146,9 +162,9 @@ static inline void egl_filterSetOutputResHint(EGL_Filter * filter,
}
static inline void egl_filterGetOutputRes(EGL_Filter * filter,
unsigned int *x, unsigned int *y)
unsigned int *x, unsigned int *y, enum EGL_PixelFormat *pixFmt)
{
return filter->ops.getOutputRes(filter, x, y);
return filter->ops.getOutputRes(filter, x, y, pixFmt);
}
static inline bool egl_filterPrepare(EGL_Filter * filter)
@@ -156,8 +172,8 @@ static inline bool egl_filterPrepare(EGL_Filter * filter)
return filter->ops.prepare(filter);
}
static inline GLuint egl_filterRun(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
static inline EGL_Texture * egl_filterRun(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture)
{
return filter->ops.run(filter, rects, texture);
}

View File

@@ -0,0 +1,221 @@
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "filter.h"
#include "framebuffer.h"
#include <math.h>
#include "common/array.h"
#include "common/debug.h"
#include "common/option.h"
#include "cimgui.h"
#include "basic.vert.h"
#include "convert_24bit.frag.h"
typedef struct EGL_Filter24bit
{
EGL_Filter base;
bool enable;
EGL_PixelFormat format;
int useDMA;
unsigned int width, height;
unsigned int desktopWidth, desktopHeight;
bool prepared;
EGL_Uniform uOutputSize;
EGL_Shader * shader;
EGL_Framebuffer * fb;
GLuint sampler[2];
}
EGL_Filter24bit;
static bool egl_filter24bitInit(EGL_Filter ** filter)
{
EGL_Filter24bit * this = calloc(1, sizeof(*this));
if (!this)
{
DEBUG_ERROR("Failed to allocate ram");
return false;
}
this->useDMA = -1;
if (!egl_shaderInit(&this->shader))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_framebufferInit(&this->fb))
{
DEBUG_ERROR("Failed to initialize the framebuffer");
goto error_shader;
}
glGenSamplers(ARRAY_LENGTH(this->sampler), this->sampler);
glSamplerParameteri(this->sampler[0], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glSamplerParameteri(this->sampler[0], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(this->sampler[0], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glSamplerParameteri(this->sampler[0], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
glSamplerParameteri(this->sampler[1], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(this->sampler[1], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(this->sampler[1], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glSamplerParameteri(this->sampler[1], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
*filter = &this->base;
return true;
error_shader:
egl_shaderFree(&this->shader);
error_this:
free(this);
return false;
}
static void egl_filter24bitFree(EGL_Filter * filter)
{
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
egl_shaderFree(&this->shader);
egl_framebufferFree(&this->fb);
glDeleteSamplers(ARRAY_LENGTH(this->sampler), this->sampler);
free(this);
}
static bool egl_filter24bitSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
unsigned int desktopWidth, unsigned int desktopHeight,
bool useDMA)
{
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
if (pixFmt != EGL_PF_BGR_32 && pixFmt != EGL_PF_RGB_24_32)
return false;
if (this->useDMA != useDMA || this->format != pixFmt)
{
EGL_ShaderDefine defines[] =
{
{"OUTPUT", pixFmt == EGL_PF_BGR_32 ? "fragColor.bgra" : "fragColor.rgba" },
{0}
};
if (!egl_shaderCompile(this->shader,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_convert_24bit_frag, b_shader_convert_24bit_frag_size,
useDMA, defines)
)
{
DEBUG_ERROR("Failed to compile the shader");
return false;
}
this->uOutputSize.type = EGL_UNIFORM_TYPE_2F;
this->uOutputSize.location =
egl_shaderGetUniform(this->shader, "outputSize");
this->useDMA = useDMA;
this->prepared = false;
}
if (this->prepared &&
this->width == width &&
this->height == height &&
this->desktopWidth == desktopWidth &&
this->desktopHeight == desktopHeight)
return true;
if (!egl_framebufferSetup(this->fb, EGL_PF_BGRA, desktopWidth, desktopHeight))
return false;
this->format = pixFmt;
this->width = width;
this->height = height;
this->desktopWidth = desktopWidth;
this->desktopHeight = desktopHeight;
this->prepared = false;
return true;
}
static void egl_filter24bitGetOutputRes(EGL_Filter * filter,
unsigned int *width, unsigned int *height, enum EGL_PixelFormat *pixFmt)
{
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
*width = this->desktopWidth;
*height = this->desktopHeight;
*pixFmt = EGL_PF_BGRA;
}
static bool egl_filter24bitPrepare(EGL_Filter * filter)
{
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
if (this->prepared)
return true;
this->uOutputSize.f[0] = this->desktopWidth;
this->uOutputSize.f[1] = this->desktopHeight;
egl_shaderSetUniforms(this->shader, &this->uOutputSize, 1);
this->prepared = true;
return true;
}
static EGL_Texture * egl_filter24bitRun(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture)
{
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
egl_framebufferBind(this->fb);
glActiveTexture(GL_TEXTURE0);
egl_textureBind(texture);
glBindSampler(0, this->sampler[0]);
egl_shaderUse(this->shader);
egl_filterRectsRender(this->shader, rects);
return egl_framebufferGetTexture(this->fb);
}
EGL_FilterOps egl_filter24bitOps =
{
.id = "24bit",
.name = "24bit",
.type = EGL_FILTER_TYPE_INTERNAL,
.earlyInit = NULL,
.init = egl_filter24bitInit,
.free = egl_filter24bitFree,
.imguiConfig = NULL,
.saveState = NULL,
.loadState = NULL,
.setup = egl_filter24bitSetup,
.getOutputRes = egl_filter24bitGetOutputRes,
.prepare = egl_filter24bitPrepare,
.run = egl_filter24bitRun
};

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -60,6 +60,7 @@ typedef struct EGL_FilterDownscale
EGL_Shader * lanczos2;
DownscaleFilter filter;
int useDMA;
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
float pixelSize;
@@ -157,55 +158,26 @@ static bool egl_filterDownscaleInit(EGL_Filter ** filter)
return false;
}
this->useDMA = -1;
if (!egl_shaderInit(&this->nearest))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_shaderCompile(this->nearest,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_downscale_frag, b_shader_downscale_frag_size)
)
{
DEBUG_ERROR("Failed to compile the shader");
goto error_shader;
}
if (!egl_shaderInit(&this->linear))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_shaderCompile(this->linear,
b_shader_basic_vert, b_shader_basic_vert_size,
b_shader_downscale_linear_frag, b_shader_downscale_linear_frag_size)
)
{
DEBUG_ERROR("Failed to compile the shader");
goto error_shader;
}
if (!egl_shaderInit(&this->lanczos2))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_shaderCompile(this->lanczos2,
b_shader_basic_vert, b_shader_basic_vert_size,
b_shader_downscale_lanczos2_frag, b_shader_downscale_lanczos2_frag_size)
)
{
DEBUG_ERROR("Failed to compile the shader");
goto error_shader;
}
this->uNearest.type = EGL_UNIFORM_TYPE_3F;
this->uNearest.location =
egl_shaderGetUniform(this->nearest, "uConfig");
if (!egl_framebufferInit(&this->fb))
{
DEBUG_ERROR("Failed to initialize the framebuffer");
@@ -268,7 +240,7 @@ static bool egl_filterDownscaleImguiConfig(EGL_Filter * filter)
for (int i = 0; i < DOWNSCALE_COUNT; ++i)
{
bool selected = i == this->filter;
if (igSelectableBool(filterNames[i], selected, 0, (ImVec2) { 0.0f, 0.0f }))
if (igSelectable_Bool(filterNames[i], selected, 0, (ImVec2) { 0.0f, 0.0f }))
{
redraw = true;
this->filter = i;
@@ -326,7 +298,9 @@ static bool egl_filterDownscaleImguiConfig(EGL_Filter * filter)
}
static bool egl_filterDownscaleSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
unsigned int desktopWidth, unsigned int desktopHeight,
bool useDMA)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
@@ -336,6 +310,48 @@ static bool egl_filterDownscaleSetup(EGL_Filter * filter,
if (!this->enable)
return false;
if (this->useDMA != useDMA)
{
if (!egl_shaderCompile(this->nearest,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_downscale_frag,
b_shader_downscale_frag_size,
useDMA, NULL)
)
{
DEBUG_ERROR("Failed to compile the shader");
return false;
}
if (!egl_shaderCompile(this->linear,
b_shader_basic_vert, b_shader_basic_vert_size,
b_shader_downscale_linear_frag,
b_shader_downscale_linear_frag_size,
useDMA, NULL)
)
{
DEBUG_ERROR("Failed to compile the shader");
return false;
}
if (!egl_shaderCompile(this->lanczos2,
b_shader_basic_vert, b_shader_basic_vert_size,
b_shader_downscale_lanczos2_frag,
b_shader_downscale_lanczos2_frag_size,
useDMA, NULL)
)
{
DEBUG_ERROR("Failed to compile the shader");
return false;
}
this->uNearest.type = EGL_UNIFORM_TYPE_3F;
this->uNearest.location =
egl_shaderGetUniform(this->nearest, "uConfig");
this->useDMA = useDMA;
}
if (this->prepared &&
pixFmt == this->pixFmt &&
this->width == width &&
@@ -354,11 +370,12 @@ static bool egl_filterDownscaleSetup(EGL_Filter * filter,
}
static void egl_filterDownscaleGetOutputRes(EGL_Filter * filter,
unsigned int *width, unsigned int *height)
unsigned int *width, unsigned int *height, enum EGL_PixelFormat *pixFmt)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
*width = this->width;
*height = this->height;
*pixFmt = this->pixFmt;
}
static bool egl_filterDownscalePrepare(EGL_Filter * filter)
@@ -385,15 +402,15 @@ static bool egl_filterDownscalePrepare(EGL_Filter * filter)
return true;
}
static GLuint egl_filterDownscaleRun(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
static EGL_Texture * egl_filterDownscaleRun(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
egl_framebufferBind(this->fb);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
egl_textureBind(texture);
EGL_Shader * shader;

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -37,6 +37,7 @@ typedef struct EGL_FilterFFXCAS
EGL_Shader * shader;
bool enable;
int useDMA;
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
float sharpness;
@@ -106,21 +107,14 @@ static bool egl_filterFFXCASInit(EGL_Filter ** filter)
return false;
}
this->useDMA = -1;
if (!egl_shaderInit(&this->shader))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_shaderCompile(this->shader,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_cas_frag, b_shader_ffx_cas_frag_size)
)
{
DEBUG_ERROR("Failed to compile the shader");
goto error_shader;
}
this->consts = countedBufferNew(8 * sizeof(GLuint));
if (!this->consts)
{
@@ -128,12 +122,6 @@ static bool egl_filterFFXCASInit(EGL_Filter ** filter)
goto error_shader;
}
egl_shaderSetUniforms(this->shader, &(EGL_Uniform) {
.type = EGL_UNIFORM_TYPE_4UIV,
.location = egl_shaderGetUniform(this->shader, "uConsts"),
.v = this->consts,
}, 1);
egl_filterFFXCASLoadState(&this->base);
if (!egl_framebufferInit(&this->fb))
@@ -220,13 +208,36 @@ static bool egl_filterFFXCASImguiConfig(EGL_Filter * filter)
}
static bool egl_filterFFXCASSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
unsigned int desktopWidth, unsigned int desktopHeight,
bool useDMA)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
if (!this->enable)
return false;
if (this->useDMA != useDMA)
{
if (!egl_shaderCompile(this->shader,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_cas_frag, b_shader_ffx_cas_frag_size,
useDMA, NULL)
)
{
DEBUG_ERROR("Failed to compile the shader");
return false;
}
egl_shaderSetUniforms(this->shader, &(EGL_Uniform) {
.type = EGL_UNIFORM_TYPE_4UIV,
.location = egl_shaderGetUniform(this->shader, "uConsts"),
.v = this->consts,
}, 1);
this->useDMA = useDMA;
}
if (pixFmt == this->pixFmt && this->width == width && this->height == height)
return true;
@@ -243,11 +254,12 @@ static bool egl_filterFFXCASSetup(EGL_Filter * filter,
}
static void egl_filterFFXCASGetOutputRes(EGL_Filter * filter,
unsigned int *width, unsigned int *height)
unsigned int *width, unsigned int *height, enum EGL_PixelFormat *pixFmt)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
*width = this->width;
*height = this->height;
*pixFmt = this->pixFmt;
}
static bool egl_filterFFXCASPrepare(EGL_Filter * filter)
@@ -262,15 +274,15 @@ static bool egl_filterFFXCASPrepare(EGL_Filter * filter)
return true;
}
static GLuint egl_filterFFXCASRun(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
static EGL_Texture * egl_filterFFXCASRun(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
egl_framebufferBind(this->fb);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
egl_textureBind(texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->shader);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -42,6 +42,7 @@ typedef struct EGL_FilterFFXFSR1
CountedBuffer * consts;
EGL_Uniform easuUniform[2], rcasUniform;
int useDMA;
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
unsigned int inWidth, inHeight;
@@ -109,6 +110,8 @@ static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
return false;
}
this->useDMA = -1;
if (!egl_shaderInit(&this->easu))
{
DEBUG_ERROR("Failed to initialize the Easu shader");
@@ -121,18 +124,10 @@ static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
goto error_esau;
}
if (!egl_shaderCompile(this->easu,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_fsr1_easu_frag, b_shader_ffx_fsr1_easu_frag_size)
)
{
DEBUG_ERROR("Failed to compile the Easu shader");
goto error_rcas;
}
if (!egl_shaderCompile(this->rcas,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_fsr1_rcas_frag, b_shader_ffx_fsr1_rcas_frag_size)
b_shader_ffx_fsr1_rcas_frag, b_shader_ffx_fsr1_rcas_frag_size,
false, NULL)
)
{
DEBUG_ERROR("Failed to compile the Rcas shader");
@@ -148,14 +143,6 @@ static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
egl_filterFFXFSR1LoadState(&this->base);
this->easuUniform[0].type = EGL_UNIFORM_TYPE_4UIV;
this->easuUniform[0].location =
egl_shaderGetUniform(this->easu, "uConsts");
this->easuUniform[0].v = this->consts;
this->easuUniform[1].type = EGL_UNIFORM_TYPE_2F;
this->easuUniform[1].location =
egl_shaderGetUniform(this->easu, "uOutRes");
this->rcasUniform.type = EGL_UNIFORM_TYPE_4UI;
this->rcasUniform.location = egl_shaderGetUniform(this->rcas, "uConsts");
rcasUpdateUniform(this);
@@ -335,13 +322,38 @@ static void egl_filterFFXFSR1SetOutputResHint(EGL_Filter * filter,
}
static bool egl_filterFFXFSR1Setup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
unsigned int desktopWidth, unsigned int desktopHeight,
bool useDMA)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
if (!this->enable)
return false;
if (this->useDMA != useDMA)
{
if (!egl_shaderCompile(this->easu,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_fsr1_easu_frag, b_shader_ffx_fsr1_easu_frag_size,
useDMA, NULL)
)
{
DEBUG_ERROR("Failed to compile the Easu shader");
return false;
}
this->easuUniform[0].type = EGL_UNIFORM_TYPE_4UIV;
this->easuUniform[0].location =
egl_shaderGetUniform(this->easu, "uConsts");
this->easuUniform[0].v = this->consts;
this->easuUniform[1].type = EGL_UNIFORM_TYPE_2F;
this->easuUniform[1].location =
egl_shaderGetUniform(this->easu, "uOutRes");
this->useDMA = useDMA;
}
this->active = this->width > width && this->height > height;
if (!this->active)
return false;
@@ -371,11 +383,12 @@ static bool egl_filterFFXFSR1Setup(EGL_Filter * filter,
}
static void egl_filterFFXFSR1GetOutputRes(EGL_Filter * filter,
unsigned int *width, unsigned int *height)
unsigned int *width, unsigned int *height, enum EGL_PixelFormat *pixFmt)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
*width = this->width;
*height = this->height;
*pixFmt = this->pixFmt;
}
static bool egl_filterFFXFSR1Prepare(EGL_Filter * filter)
@@ -395,15 +408,15 @@ static bool egl_filterFFXFSR1Prepare(EGL_Filter * filter)
return true;
}
static GLuint egl_filterFFXFSR1Run(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
static EGL_Texture * egl_filterFFXFSR1Run(EGL_Filter * filter,
EGL_FilterRects * rects, EGL_Texture * texture)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
// pass 1, Easu
egl_framebufferBind(this->easuFb);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
egl_textureBind(texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->easu);
egl_filterRectsRender(this->easu, rects);
@@ -412,7 +425,7 @@ static GLuint egl_filterFFXFSR1Run(EGL_Filter * filter,
// pass 2, Rcas
egl_framebufferBind(this->rcasFb);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
egl_textureBind(texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->rcas);
egl_filterRectsRender(this->rcas, rects);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -20,6 +20,7 @@
#pragma once
extern EGL_FilterOps egl_filter24bitOps;
extern EGL_FilterOps egl_filterDownscaleOps;
extern EGL_FilterOps egl_filterFFXCASOps;
extern EGL_FilterOps egl_filterFFXFSR1Ops;

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -40,9 +40,10 @@ bool egl_framebufferInit(EGL_Framebuffer ** fb)
return false;
}
if (!egl_textureInit(&this->tex, NULL, EGL_TEXTYPE_BUFFER, false))
if (!egl_textureInit(&this->tex, NULL, EGL_TEXTYPE_BUFFER))
{
DEBUG_ERROR("Failed to initialize the texture");
free(this);
return false;
}
@@ -64,14 +65,14 @@ void egl_framebufferFree(EGL_Framebuffer ** fb)
bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt,
unsigned int width, unsigned int height)
{
if (!egl_textureSetup(this->tex, pixFmt, width, height, 0))
if (!egl_textureSetup(this->tex, pixFmt, width, height, 0, 0))
{
DEBUG_ERROR("Failed to setup the texture");
return false;
}
GLuint tex;
egl_textureGet(this->tex, &tex, NULL, NULL);
egl_textureGet(this->tex, &tex, NULL, NULL, NULL);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -100,9 +101,7 @@ void egl_framebufferBind(EGL_Framebuffer * this)
glViewport(0, 0, this->tex->format.width, this->tex->format.height);
}
GLuint egl_framebufferGetTexture(EGL_Framebuffer * this)
EGL_Texture * egl_framebufferGetTexture(EGL_Framebuffer * this)
{
GLuint output;
egl_textureGet(this->tex, &output, NULL, NULL);
return output;
return this->tex;
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -32,4 +32,4 @@ bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt,
void egl_framebufferBind(EGL_Framebuffer * this);
GLuint egl_framebufferGetTexture(EGL_Framebuffer * this);
EGL_Texture * egl_framebufferGetTexture(EGL_Framebuffer * this);

View File

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

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -23,7 +23,7 @@
#include "texture.h"
#include "common/debug.h"
#include "ll.h"
#include "common/ll.h"
#include <stdlib.h>
#include <string.h>
@@ -124,10 +124,31 @@ void egl_modelSetDefault(EGL_Model * model, bool flipped)
void egl_modelAddVerts(EGL_Model * model, const GLfloat * verticies, const GLfloat * uvs, const size_t count)
{
struct FloatList * fl = malloc(sizeof(*fl));
if (!fl)
{
DEBUG_ERROR("out of memory");
return;
}
fl->count = count;
fl->v = malloc(sizeof(GLfloat) * count * 3);
fl->u = malloc(sizeof(GLfloat) * count * 2);
fl->v = malloc(sizeof(GLfloat) * count * 3);
if (!fl->v)
{
DEBUG_ERROR("out of memory");
free(fl);
return;
}
fl->u = malloc(sizeof(GLfloat) * count * 2);
if (!fl->u)
{
DEBUG_ERROR("out of memory");
free(fl->v);
free(fl);
return;
}
memcpy(fl->v, verticies, sizeof(GLfloat) * count * 3);
if (uvs)
@@ -164,18 +185,20 @@ void egl_modelRender(EGL_Model * model)
/* buffer the verticies */
struct FloatList * fl;
for(ll_reset(model->verticies); ll_walk(model->verticies, (void **)&fl);)
ll_lock(model->verticies);
ll_forEachNL(model->verticies, item, fl)
{
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(GLfloat) * fl->count * 3, fl->v);
offset += sizeof(GLfloat) * fl->count * 3;
}
/* buffer the uvs */
for(ll_reset(model->verticies); ll_walk(model->verticies, (void **)&fl);)
ll_forEachNL(model->verticies, item, fl)
{
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(GLfloat) * fl->count * 2, fl->u);
offset += sizeof(GLfloat) * fl->count * 2;
}
ll_unlock(model->verticies);
/* set up vertex arrays in the VAO */
glEnableVertexAttribArray(0);
@@ -199,11 +222,13 @@ void egl_modelRender(EGL_Model * model)
/* draw the arrays */
GLint offset = 0;
struct FloatList * fl;
for(ll_reset(model->verticies); ll_walk(model->verticies, (void **)&fl);)
ll_lock(model->verticies);
ll_forEachNL(model->verticies, item, fl)
{
glDrawArrays(GL_TRIANGLE_STRIP, offset, fl->count);
offset += fl->count;
}
ll_unlock(model->verticies);
/* unbind and cleanup */
glBindTexture(GL_TEXTURE_2D, 0);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -48,8 +48,8 @@ static const EGL_FilterOps * EGL_Filters[] =
struct EGL_PostProcess
{
Vector filters;
GLuint output;
Vector filters, internalFilters;
EGL_Texture * output;
unsigned int outputX, outputY;
_Atomic(bool) modified;
@@ -74,14 +74,22 @@ void egl_postProcessEarlyInit(void)
.type = OPTION_TYPE_STRING,
.value.x_string = ""
},
{
.module = "egl",
.name = "preset",
.description = "The initial filter preset to load",
.type = OPTION_TYPE_STRING
},
{ 0 }
};
option_register(options);
for (int i = 0; i < ARRAY_LENGTH(EGL_Filters); ++i)
EGL_Filters[i]->earlyInit();
egl_filterEarlyInit(EGL_Filters[i]);
}
static void loadPreset(struct EGL_PostProcess * this, const char * name);
static void loadPresetList(struct EGL_PostProcess * this)
{
DIR * dir = NULL;
@@ -114,6 +122,9 @@ static void loadPresetList(struct EGL_PostProcess * this)
}
struct dirent * entry;
const char * preset = option_get_string("egl", "preset");
this->activePreset = -1;
while ((entry = readdir(dir)) != NULL)
{
if (entry->d_type != DT_REG)
@@ -127,10 +138,21 @@ static void loadPresetList(struct EGL_PostProcess * this)
goto fail;
}
stringlist_push(this->presets, name);
if (preset && strcmp(preset, name) == 0)
this->activePreset = stringlist_count(this->presets) - 1;
}
closedir(dir);
this->activePreset = -1;
if (preset)
{
if (this->activePreset > -1)
loadPreset(this, preset);
else
DEBUG_WARN("egl:preset '%s' does not exist", preset);
}
return;
fail:
@@ -334,7 +356,8 @@ static bool presetsUI(struct EGL_PostProcess * this)
for (unsigned i = 0; i < stringlist_count(this->presets); ++i)
{
bool selected = i == this->activePreset;
if (igSelectableBool(stringlist_at(this->presets, i), selected, 0, (ImVec2) { 0.0f, 0.0f }))
if (igSelectable_Bool(stringlist_at(this->presets, i), selected, 0,
(ImVec2) { 0.0f, 0.0f }))
{
this->activePreset = i;
redraw = true;
@@ -365,7 +388,7 @@ static bool presetsUI(struct EGL_PostProcess * this)
if (igButton("Save preset as...", (ImVec2) { 0.0f, 0.0f }))
{
this->presetEdit[0] = '\0';
igOpenPopup("Save preset as...", ImGuiPopupFlags_None);
igOpenPopup_Str("Save preset as...", ImGuiPopupFlags_None);
}
igSameLine(0.0f, -1.0f);
@@ -401,7 +424,7 @@ static bool presetsUI(struct EGL_PostProcess * this)
}
if (this->presetError)
igOpenPopup("Preset error", ImGuiPopupFlags_None);
igOpenPopup_Str("Preset error", ImGuiPopupFlags_None);
if (igBeginPopupModal("Preset error", NULL, ImGuiWindowFlags_AlwaysAutoResize))
{
@@ -425,7 +448,7 @@ static bool presetsUI(struct EGL_PostProcess * this)
static void drawDropTarget(void)
{
igPushStyleColorVec4(ImGuiCol_Separator, (ImVec4) { 1.0f, 1.0f, 0.0f, 1.0f });
igPushStyleColor_Vec4(ImGuiCol_Separator, (ImVec4) { 1.0f, 1.0f, 0.0f, 1.0f });
igSeparator();
igPopStyleColor(1);
}
@@ -441,7 +464,8 @@ static void configUI(void * opaque, int * id)
static size_t mouseIdx = -1;
static bool moving = false;
static size_t moveIdx = 0;
bool doMove = false;
bool doMove = false;
ImVec2 window, pos;
igGetWindowPos(&window);
@@ -456,8 +480,8 @@ static void configUI(void * opaque, int * id)
if (moving && mouseIdx < moveIdx && i == mouseIdx)
drawDropTarget();
igPushIDPtr(filter);
bool draw = igCollapsingHeaderBoolPtr(filter->ops.name, NULL, 0);
igPushID_Ptr(filter);
bool draw = igCollapsingHeader_BoolPtr(filter->ops.name, NULL, 0);
if (igIsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
mouseIdx = i;
@@ -495,16 +519,23 @@ static void configUI(void * opaque, int * id)
{
EGL_Filter * tmp = filters[moveIdx];
if (mouseIdx > moveIdx) // moving down
memmove(filters + moveIdx, filters + moveIdx + 1, (mouseIdx - moveIdx) * sizeof(EGL_Filter *));
memmove(
filters + moveIdx,
filters + moveIdx + 1,
(mouseIdx - moveIdx) * sizeof(EGL_Filter *));
else // moving up
memmove(filters + mouseIdx + 1, filters + mouseIdx, (moveIdx - mouseIdx) * sizeof(EGL_Filter *));
memmove(
filters + mouseIdx + 1,
filters + mouseIdx,
(moveIdx - mouseIdx) * sizeof(EGL_Filter *));
filters[mouseIdx] = tmp;
}
if (redraw)
{
atomic_store(&this->modified, true);
app_invalidateWindow(false);
app_invalidateWindow(true);
}
}
@@ -517,16 +548,24 @@ bool egl_postProcessInit(EGL_PostProcess ** pp)
return false;
}
if (!vector_create(&this->filters, sizeof(EGL_Filter *), ARRAY_LENGTH(EGL_Filters)))
if (!vector_create(&this->filters,
sizeof(EGL_Filter *), ARRAY_LENGTH(EGL_Filters)))
{
DEBUG_ERROR("Failed to allocate the filter list");
goto error_this;
}
if (!vector_create(&this->internalFilters,
sizeof(EGL_Filter *), ARRAY_LENGTH(EGL_Filters)))
{
DEBUG_ERROR("Failed to allocate the filter list");
goto error_filters;
}
if (!egl_desktopRectsInit(&this->rects, 1))
{
DEBUG_ERROR("Failed to initialize the desktop rects");
goto error_filters;
goto error_internal;
}
loadPresetList(this);
@@ -536,6 +575,9 @@ bool egl_postProcessInit(EGL_PostProcess ** pp)
*pp = this;
return true;
error_internal:
vector_destroy(&this->internalFilters);
error_filters:
vector_destroy(&this->filters);
@@ -556,6 +598,10 @@ void egl_postProcessFree(EGL_PostProcess ** pp)
egl_filterFree(filter);
vector_destroy(&this->filters);
vector_forEachRef(filter, &this->internalFilters)
egl_filterFree(filter);
vector_destroy(&this->internalFilters);
free(this->presetDir);
if (this->presets)
stringlist_free(&this->presets);
@@ -572,7 +618,10 @@ bool egl_postProcessAdd(EGL_PostProcess * this, const EGL_FilterOps * ops)
if (!egl_filterInit(ops, &filter))
return false;
vector_push(&this->filters, &filter);
if (ops->type == EGL_FILTER_TYPE_INTERNAL)
vector_push(&this->internalFilters, &filter);
else
vector_push(&this->filters, &filter);
return true;
}
@@ -583,7 +632,7 @@ bool egl_postProcessConfigModified(EGL_PostProcess * this)
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
EGL_DesktopRects * rects, int desktopWidth, int desktopHeight,
unsigned int targetX, unsigned int targetY)
unsigned int targetX, unsigned int targetY, bool useDMA)
{
if (targetX == 0 && targetY == 0)
DEBUG_FATAL("targetX || targetY == 0");
@@ -591,8 +640,11 @@ bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
EGL_Filter * lastFilter = NULL;
unsigned int sizeX, sizeY;
GLuint texture;
if (egl_textureGet(tex, &texture, &sizeX, &sizeY) != EGL_TEX_STATUS_OK)
//TODO: clean this up
GLuint _unused;
EGL_PixelFormat pixFmt;
if (egl_textureGet(tex, &_unused,
&sizeX, &sizeY, &pixFmt) != EGL_TEX_STATUS_OK)
return false;
if (atomic_exchange(&this->modified, false))
@@ -613,22 +665,36 @@ bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
};
EGL_Filter * filter;
vector_forEach(filter, &this->filters)
EGL_Texture * texture = tex;
const Vector * lists[] =
{
egl_filterSetOutputResHint(filter, targetX, targetY);
&this->internalFilters,
&this->filters,
NULL
};
if (!egl_filterSetup(filter, tex->format.pixFmt, sizeX, sizeY) ||
!egl_filterPrepare(filter))
continue;
for(const Vector ** filters = lists; *filters; ++filters)
vector_forEach(filter, *filters)
{
egl_filterSetOutputResHint(filter, targetX, targetY);
texture = egl_filterRun(filter, &filterRects, texture);
egl_filterGetOutputRes(filter, &sizeX, &sizeY);
if (!egl_filterSetup(filter, pixFmt, sizeX, sizeY,
desktopWidth, desktopHeight, useDMA) ||
!egl_filterPrepare(filter))
continue;
if (lastFilter)
egl_filterRelease(lastFilter);
texture = egl_filterRun(filter, &filterRects, texture);
egl_filterGetOutputRes(filter, &sizeX, &sizeY, &pixFmt);
lastFilter = filter;
}
if (lastFilter)
egl_filterRelease(lastFilter);
lastFilter = filter;
// the first filter to run will convert to a normal texture
useDMA = false;
}
this->output = texture;
this->outputX = sizeX;
@@ -636,7 +702,7 @@ bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
return true;
}
GLuint egl_postProcessGetOutput(EGL_PostProcess * this,
EGL_Texture * egl_postProcessGetOutput(EGL_PostProcess * this,
unsigned int * outputX, unsigned int * outputY)
{
*outputX = this->outputX;

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -41,7 +41,7 @@ bool egl_postProcessConfigModified(EGL_PostProcess * this);
* targetX/Y is the final target output dimension hint if scalers are present */
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
EGL_DesktopRects * rects, int desktopWidth, int desktopHeight,
unsigned int targetX, unsigned int targetY);
unsigned int targetX, unsigned int targetY, bool useDMA);
GLuint egl_postProcessGetOutput(EGL_PostProcess * this,
EGL_Texture * egl_postProcessGetOutput(EGL_PostProcess * this,
unsigned int * outputX, unsigned int * outputY);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -20,6 +20,7 @@
#include "shader.h"
#include "common/debug.h"
#include "common/stringutils.h"
#include "util.h"
#include <stdlib.h>
@@ -64,7 +65,9 @@ void egl_shaderFree(EGL_Shader ** shader)
*shader = NULL;
}
bool egl_shaderLoad(EGL_Shader * this, const char * vertex_file, const char * fragment_file)
bool egl_shaderLoad(EGL_Shader * this,
const char * vertex_file, const char * fragment_file, bool useDMA,
const EGL_ShaderDefine * defines)
{
char * vertex_code, * fragment_code;
size_t vertex_size, fragment_size;
@@ -86,13 +89,16 @@ bool egl_shaderLoad(EGL_Shader * this, const char * vertex_file, const char * fr
DEBUG_INFO("Loaded fragment shader: %s", fragment_file);
bool ret = egl_shaderCompile(this, vertex_code, vertex_size, fragment_code, fragment_size);
bool ret = egl_shaderCompile(this,
vertex_code, vertex_size, fragment_code, fragment_size,
useDMA, defines);
free(vertex_code);
free(fragment_code);
return ret;
}
bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
static bool shaderCompile(EGL_Shader * this, const char * vertex_code,
size_t vertex_size, const char * fragment_code, size_t fragment_size)
{
if (this->hasShader)
@@ -119,10 +125,15 @@ bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
if (logLength > 0)
{
char *log = malloc(logLength + 1);
glGetShaderInfoLog(vertexShader, logLength, NULL, log);
log[logLength] = 0;
DEBUG_ERROR("%s", log);
free(log);
if (!log)
DEBUG_ERROR("out of memory");
else
{
glGetShaderInfoLog(vertexShader, logLength, NULL, log);
log[logLength] = 0;
DEBUG_ERROR("%s", log);
free(log);
}
}
glDeleteShader(vertexShader);
@@ -145,10 +156,15 @@ bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
if (logLength > 0)
{
char *log = malloc(logLength + 1);
glGetShaderInfoLog(fragmentShader, logLength, NULL, log);
log[logLength] = 0;
DEBUG_ERROR("%s", log);
free(log);
if (!log)
DEBUG_ERROR("out of memory");
else
{
glGetShaderInfoLog(fragmentShader, logLength, NULL, log);
log[logLength] = 0;
DEBUG_ERROR("%s", log);
free(log);
}
}
glDeleteShader(fragmentShader);
@@ -194,6 +210,149 @@ bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
return true;
}
bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
size_t vertex_size, const char * fragment_code, size_t fragment_size,
bool useDMA, const EGL_ShaderDefine * defines)
{
bool result = false;
char * processed = NULL;
char * newCode = NULL;
if (useDMA)
{
const char search[] = "sampler2D";
const char replace[] = "samplerExternalOES";
const char * offset = NULL;
int instances = 0;
while((offset = memsearch(
fragment_code, fragment_size,
search , sizeof(search)-1,
offset)))
{
++instances;
offset += sizeof(search)-1;
}
const int diff = (sizeof(replace) - sizeof(search)) * instances;
const int newLen = fragment_size + diff;
newCode = malloc(newLen + 1);
if (!newCode)
{
DEBUG_ERROR("Out of memory");
goto exit;
}
const char * src = fragment_code;
char * dst = newCode;
for(int i = 0; i < instances; ++i)
{
const char * pos = strstr(src, search);
const int offset = pos - src;
memcpy(dst, src, offset);
dst += offset;
src = pos + sizeof(search)-1;
memcpy(dst, replace, sizeof(replace)-1);
dst += sizeof(replace)-1;
}
const int final = fragment_size - (src - fragment_code);
memcpy(dst, src, final);
dst[final] = '\0';
fragment_code = newCode;
fragment_size = newLen;
}
if (defines)
{
// find the end of any existing lines starting with #
bool newLine = true;
bool skip = false;
int insertPos = 0;
for(int i = 0; i < fragment_size; ++i)
{
if (skip)
{
if (fragment_code[i] == '\n')
skip = false;
continue;
}
switch(fragment_code[i])
{
case '\n':
newLine = true;
continue;
case ' ':
case '\t':
case '\r':
continue;
case '#':
if (newLine)
{
skip = true;
continue;
}
//fallthrough
default:
newLine = false;
break;
}
if (!newLine)
{
insertPos = i;
if (insertPos > 0)
--insertPos;
break;
}
}
int processedLen = fragment_size;
const char * defineFormat = "#define %s %s\n";
for(const EGL_ShaderDefine * define = defines; define->name; ++define)
processedLen += snprintf(NULL, 0, defineFormat, define->name, define->value);
processed = malloc(processedLen);
if (!processed)
{
DEBUG_ERROR("Out of memory");
goto exit;
}
memcpy(processed, fragment_code, insertPos);
int offset = insertPos;
for(const EGL_ShaderDefine * define = defines; define->name; ++define)
offset += sprintf(processed + offset, defineFormat,
define->name, define->value);
memcpy(
processed + offset,
fragment_code + insertPos,
fragment_size - insertPos);
fragment_code = processed;
fragment_size = processedLen;
}
result = shaderCompile(this,
vertex_code , vertex_size,
fragment_code, fragment_size);
exit:
free(processed);
free(newCode);
return result;
}
void egl_shaderSetUniforms(EGL_Shader * this, EGL_Uniform * uniforms, int count)
{
egl_shaderFreeUniforms(this);
@@ -201,6 +360,12 @@ void egl_shaderSetUniforms(EGL_Shader * this, EGL_Uniform * uniforms, int count)
{
free(this->uniforms);
this->uniforms = malloc(sizeof(*this->uniforms) * count);
if (!this->uniforms)
{
DEBUG_ERROR("out of memory");
return;
}
this->uniformCount = count;
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -93,14 +93,22 @@ typedef struct EGL_Uniform
}
EGL_Uniform;
typedef struct EGL_ShaderDefine
{
const char * name;
const char * value;
}
EGL_ShaderDefine;
bool egl_shaderInit(EGL_Shader ** shader);
void egl_shaderFree(EGL_Shader ** shader);
bool egl_shaderLoad(EGL_Shader * model, const char * vertex_file,
const char * fragment_file);
const char * fragment_file, bool useDMA, const EGL_ShaderDefine * defines);
bool egl_shaderCompile(EGL_Shader * model, const char * vertex_code,
size_t vertex_size, const char * fragment_code, size_t fragment_size);
size_t vertex_size, const char * fragment_code, size_t fragment_size,
bool useDMA, const EGL_ShaderDefine * defines);
void egl_shaderSetUniforms(EGL_Shader * shader, EGL_Uniform * uniforms,
int count);

View File

@@ -1,5 +1,5 @@
#version 300 es
precision mediump float;
precision highp float;
layout(location = 0) in vec2 vertex;
out vec2 fragCoord;

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