Compare commits

..

1152 Commits

Author SHA1 Message Date
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
Jonathan Rubenstein
74444f8eed [docs] Minor semantic spit and polish
Co-authored-by: Guanzhong Chen <quantum2048@gmail.com>
Co-authored-by: Netboy3 <1472804+netboy3@users.noreply.github.com>
2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
6c43650cd3 [doc] spelling fixes 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
181ee2b4f5 [doc] module: Kernel Module polish 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
5bef733647 [doc] install: Update command line options 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
22cef47bc4 [doc] install: Overlay Filter config polish 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
5b25e20a2e [doc] install: Overlay Mode polish 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
bb5c7a222c [doc] conf: Whitespace 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
39ea6b0587 [doc] install: Config file loading polish 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
ddc6cb5277 [doc] obs: Install polish 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
b13a79880b [doc] faq: GNOME on Wayland polish 2021-12-26 10:02:07 +11:00
Jonathan Rubenstein
53fdc2e148 [docs] build: Better flow for Wayland tip 2021-12-26 10:02:07 +11:00
Tudor Brindus
9872d2e407 [host] dxgi: fix typo in debug log message 2021-12-26 09:49:03 +11:00
Quantum
3ccf6de868 [all] gitignore: ignore __pycache__ and *.py[co]
This prevents accidental commits of the compiled spellchecker.
2021-12-26 09:46:31 +11:00
Quantum
12461196c3 [host] nvfbc: fix comments in updateDamageRects 2021-12-24 15:30:04 +11:00
Quantum
15ec80e80d [client] input: fix race between window size and guest cursor
g_state.posInfoValid could become valid after the guest reports the
cursor position, in which case we did not show the cursor until another
update occurs.

This commit eliminates the race by performing the update when
g_state.posInfoValid becomes true.
2021-12-24 13:16:52 +11:00
Netboy3
d6eb72331c [doc] module: Bring back older style XML
Older libvirt and QEMU require older style config
so put it back with version instructions.
2021-12-24 10:24:57 +11:00
Quantum
eea0ced627 [client] wayland: handle NULL wl_keyboard on destruction 2021-12-24 10:22:23 +11:00
Quantum
94684324f4 [client] wayland: don't create confines on uncapture without wl_pointer 2021-12-24 10:17:16 +11:00
Quantum
194afa2d75 [client] wayland: create wl_relative_pointer when creating wl_pointer 2021-12-24 10:17:16 +11:00
Quantum
d96b2ef1fb [client] wayland: clean up objects when wl_pointer disappears 2021-12-24 10:17:16 +11:00
Geoffrey McRae
ad40ea4195 [client] x11: don't override the focus state if no EWMH focus support 2021-12-22 11:25:54 +11:00
Geoffrey McRae
65948034dd [client] x11: be more agressive grabbing the pointer 2021-12-21 21:51:43 +11:00
Netboy3
9d47ca4f12 [doc] module: Modify libvirt commandline block to use JSON
Latest versions of QEMU are running out of PCIe mapping
through libvirt as the commandline device is mapping
ivshmem before other root devices.
Use JSON style configuration in the commandline block.
JSON arguments are loaded after all regular ones
and will guarantee to get loaded after all other libvirt mapped
PCIe devices.
2021-12-21 15:38:28 +11:00
Geoffrey McRae
27c7054505 [client] x11: protect against msc overflow with jitRender 2021-12-21 10:47:55 +11:00
Geoffrey McRae
02b59ba8f7 [client] x11: don't use the atomic msc value when we already have it 2021-12-21 10:47:11 +11:00
Quantum
a5727262cd [client] wayland: make cursor change work without wl_pointer 2021-12-16 11:36:39 +11:00
Geoffrey McRae
43545a4e17 [all] cmake: refuse to perform in-source builds 2021-12-15 10:56:27 +11:00
Geoffrey McRae
adbe333414 [client] egl: dmabuf can be used without GL_EXT_buffer_storage support 2021-12-15 06:10:30 +11:00
Geoffrey McRae
5f80ce91e8 [client] x11: fix broken grab retry logic 2021-12-15 01:43:09 +11:00
Geoffrey McRae
b6fa296d5a [client] x11: work around issue with desktop switch on i3
For an unknwon reason when LG is on another desktop (hidden) and the
user switches to that desktop, the first attempt to grab the pointer
results in a GrabFrozen result. This adds some simple retry logic to
attempt again after a short (100ms) delay which seems to resolve the
issue.
2021-12-15 00:23:44 +11:00
Geoffrey McRae
2e170ad06f [client] x11: properly detect WMEH support for focus events 2021-12-15 00:17:33 +11:00
jonpas
a6720db749 [docs] obs: fix installation instructions grammar
regression in bc022c77f4
2021-12-14 21:19:25 +11:00
Geoffrey McRae
aa6cf72718 [release] B5-rc1
After many months of hard work from our team, I am pleased to announce Beta 5 Release Candidate 1 which brings with it a huge number of improvements.

If you like this project and find it useful and would like to help out you can support this project directly by using the following platforms.

* [GitHub](https://github.com/sponsors/gnif)
* [Ko-Fi](https://ko-fi.com/lookingglass)
* [Patreon](https://www.patreon.com/gnif)
* [Paypal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ESQ72XUPGKXRY)
* BTC - 14ZFcYjsKPiVreHqcaekvHGL846u3ZuT13
* ETH - 0x6f8aEe454384122bF9ed28f025FBCe2Bce98db85

Changelog:

**B4-rc1**
* removed SDL displayserver
* reduced amount of GPU buffer allocations in DMABUF import path
* implemented frame damage tracking, used to reduce amount of data copies needed
* added frame damage display mode in EGL backend
* added cimgui/imgui to the project
* added frame timings collection and display
* improved LGMP cursor performance by releasing the memory faster
* implemented eglSwapBuffersWithDamage for X11
* don't allow the mouse to trigger redraws if the video feed is disabled
* New "Overlay Mode" which allows interaction with imgui windows/widgets
* Allow the FPS display to be moved
* Observe XDG_CONFIG_DIR when looking for the configuration
* Added EGL texture import timing graph
* Alerts are now rendered using ImGui
* Added high DPI support
* add JIT rendering mode for wayland
* improve fractional scaling for wayland
* made numbering of IVSHMEM devices consistent
* Added guest cursor warp support to the KVMFR protocol
* Added the ability to have post-processing filters
* Added AMD FidelityFX CAS post-processor
* Added AMD FidelityFX FSR upscaling post-processor
* add DMABUF import support in OBS plugin
* add keyboard LED synchronization
* fix EGL backend not being detected on some systems (e.g. mesa without libglvnd) (edited)
* fix cursor blinking issue on certain hardware (e.g. old Intel integrated graphics)
* Guest cursor is now aligned to the local cursor on window entry using guest cursor warp (Fixes mouse acceleration issues)
* add ability to reorder post-processing filters
* add ability to load and save post-processing filter presets.
* add ability to capture via xdg-desktop-portal+PipeWire on Linux
* windows host installers are now distributed as 64-bit executables
2021-12-14 19:29:48 +11:00
Quantum
15066c7345 [client] egl: handle eglQuerySurface(EGL_BUFFER_AGE_EXT) error 2021-12-14 14:35:30 +11:00
Geoffrey McRae
88a95aeab0 [client] x11: fix issue with grab when clicking on the unfocused window
Fixes #856
2021-12-10 05:27:58 +11:00
Geoffrey McRae
abd6502c9d [client] x11: cosmetics 2021-12-09 19:53:34 +11:00
Quantum
50feacad13 [all] cmake: fail if wrong submodule version is checked out
This runs `git submodule summary` and fails the build if there is any
output. For developers, use `cmake -DDEVELOPER=yes` to bypass this
check.
2021-12-02 22:08:03 +11:00
Quantum
58f83da7bb [client] egl: increase the damage box for the cursor 2021-12-02 22:07:37 +11:00
Quantum
3e9a21d3b9 [client] egl: use linear filter when not scaling
This appears to eliminate artifacts related to non-full screen paints
due to precision errors with the nearest filter.
2021-12-02 22:07:24 +11:00
Geoffrey McRae
9780f51558 [client] x11: fix failure to set window position correctly when set 2021-11-30 13:04:39 +11:00
Geoffrey McRae
bc022c77f4 [docs] obs: update instructions to make use of USER_INSTALL 2021-11-22 10:42:46 +11:00
Geoffrey McRae
8167ef2c4a [client] egl: make use of glsl's textureSize function 2021-11-12 07:41:59 +11:00
Geoffrey McRae
a21eee26ab [client] main: fix buffer overflow due to cursor data size change 2021-11-02 01:01:17 +11:00
vmfortress
7075fe2c54 [common] time: fixed time values in nsleep
Time values of scientific notation replaced with expanded values
and `tv_nsec` simplified with modulo
2021-11-01 20:58:42 +11:00
Geoffrey McRae
e82f8911a6 [client] main: malloc buffer for cursor data instead of using the stack 2021-11-01 13:45:30 +11:00
Geoffrey McRae
b515fa80d5 [host] nvfbc: be more intellegent when unionizing disjointed sets 2021-10-27 00:00:38 +11:00
Geoffrey McRae
affc3f51f8 [common] rects: fix error introduced in cosmetics patch
`x2` and `y2` are required here as they need to operate on the original
x & y before they are clamped. Can't believe I missed this *facepalm*
2021-10-26 23:35:09 +11:00
Geoffrey McRae
6078b11200 Revert "[common] rects: fix damage regression introduced in the cosmetics patch"
This was correct, brainfart moment
2021-10-26 22:26:20 +11:00
Geoffrey McRae
68a9504366 [common] rects: fix damage regression introduced in the cosmetics patch 2021-10-26 22:15:54 +11:00
Geoffrey McRae
67ea8e06ba [client] egl: use alloca instead of typecasted char array 2021-10-24 22:57:21 +11:00
Geoffrey McRae
9d71655273 [common] rects: fix return value of removeRects 2021-10-24 22:22:13 +11:00
Geoffrey McRae
2f0b97a487 [common] rects: de-dup code and don't needlessly copy rect over itself 2021-10-24 22:19:28 +11:00
Geoffrey McRae
f69b869282 [common] rects: cosmetics 2021-10-24 22:05:30 +11:00
Geoffrey McRae
bc7cbf1173 [common] fix out by one error in rectsIntersect 2021-10-24 13:31:41 +11:00
Quantum
ad6e3f96e6 [client] egl: reset damage on renderer restart 2021-10-23 18:38:21 +11:00
Geoffrey McRae
fe712b7ec9 [client] egl: damage the full screen if the frame format changes 2021-10-22 18:51:49 +11:00
Jonathan Rubenstein
6a898c1e7c [doc] install: Add instructions to disable memballoon in libvirt 2021-10-21 08:49:01 +11:00
Quantum
0990689df5 [doc] spelling: add "dejavu" to word list 2021-10-20 17:13:29 +11:00
Quantum
db0e03328c [doc] build: update dependencies for post-B4 changes 2021-10-20 16:58:55 +11:00
Quantum
edf1e341da [common] rects: fix rectIntersects 2021-10-20 16:32:41 +11:00
Geoffrey McRae
9969896876 [host] app: zero the new client sub count
If a new client connects but there has not been a capture timeout the
new client count wont be zeroed on a valid capture. If there is a
timeout later the host will still think there is a new client and
re-send a frame when it should not. This fixes this by reading the value
on a valid frame which zeros the new subs count.
2021-10-20 15:56:35 +11:00
Geoffrey McRae
9c5e34df0f [client] x11: handle EINTR properly in epoll loop 2021-10-20 15:40:50 +11:00
Geoffrey McRae
dca5da02a0 [client] egl: fix undefined behaviour with zero size array 2021-10-20 13:34:16 +11:00
Jonathan Rubenstein
c5f71d18c4 [doc] module: Add unloading note for manual install 2021-10-20 11:31:18 +11:00
Jonathan Rubenstein
84a43fb651 [doc] Move DKMS above manual and mark recommended 2021-10-20 11:31:18 +11:00
Quantum
2858ad3f7e [client] egl: fallback when EGL_RENDER_BUFFER fails
This allows the client to work when the OpenGL implementation fails if
EGL_RENDER_BUFFER is passed, printing a warning. This should fix issues
with Nvidia proprietary drivers on Wayland.
2021-10-20 11:31:00 +11:00
Quantum
9ddd260b22 [client] egl: log glCheckFramebufferStatus error 2021-10-20 11:30:40 +11:00
Geoffrey McRae
bc34dc9e24 [client] egl: blame NVIDIA if dmabuf fails on NVIDIA hardware
NVIDIA still do not implement a complete/working DMABUF implementation
yet advertise support. Best to tell the public to complain to NVIDIA
instead of assuming it's a LG bug or an issue with their system.
2021-10-20 11:28:29 +11:00
Geoffrey McRae
edc9825c04 [client] egl: check for invalid desktop render dimensions 2021-10-20 11:17:29 +11:00
Geoffrey McRae
70a751b58d [client] egl/filters: bypass setup/prepare logic if disabled/inactive
filters are now not run if `egl_filterSetup` returns false, as such we
do not need additional `enable` checks in `prepare` or `run` and we can
bypass the filters even earlier if they are not enabled reducing load.
2021-10-20 10:57:53 +11:00
Quantum
fc037ccc95 [client] egl: handle filter setup returning false
As discussed, this should just skip the filter as if it's disabled.
2021-10-20 10:48:18 +11:00
Quantum
74418106de [client] egl: skip downscale filter setup when not enabled
Since this->prepared will never be set to true unless the filter is
enabled, this results in the framebuffer setup being done every frame
for no reason, causing a lot of texture reallocations.
2021-10-20 10:47:59 +11:00
Quantum
c9e8de334a [doc] module: make cgroups configuration more visible 2021-10-18 16:17:52 +11:00
Quantum
13fabc2e4a [host] windows: fix NSIS install directory on 64-bit builds
This is probably an NSIS bug.
2021-10-18 16:17:36 +11:00
Quantum
8dcf7af791 [client] egl: fix parentheses in IDX_AGO definition
The old definition could break if complex expressions were passed in
as arguments.
2021-10-17 06:10:30 +11:00
Quantum
2d858da0f1 [client] egl: fix egl_bufferAge naming 2021-10-17 06:10:30 +11:00
Quantum
ee211803e4 [client] egl: fix letterbox rendering
Add additional half pixel in all directions to ensure erasure of
content in the letterbox area under all circumstances.
2021-10-17 06:10:30 +11:00
Netboy3
c3d2ad92c5 [client] Change default grabKeyboardOnFocus to false 2021-10-15 11:36:38 +11:00
Jonathan Rubenstein
7ce7dec272 [doc] build: Add new build deps to apt-get 2021-10-15 11:36:18 +11:00
SytheZN
12321a8880 [client] wayland: implement resizing for libdecor 2021-10-14 18:17:00 +11:00
Quantum
148ab0278e [client] egl: add debug options to disable damage tracking
This should aid in figuring out the source of damage tracking bugs.
2021-10-14 17:01:48 +11:00
Quantum
d60dcb718b [client] cmake: correctly detect non-gawk awks
Forgetting NAMES meant that cmake was searching for gawk only.
2021-10-14 17:01:37 +11:00
Geoffrey McRae
e914e56c48 [client] stop the cursorThread if video feed is disabled
The cursorThread prevents the host from going to sleep when the
video feed is disabled as it's subscribed to the cursor queue. Stopping
the cursorThread will unsubscribe from the queue and allow the host
application to disable capture.
2021-10-06 20:05:31 +11:00
Quantum
24fa580519 [client] opengl: fix getProcAddressGL2 naming
TabNine autocompleted the function too hard.
2021-10-01 10:12:55 +10:00
Quantum
35c57a862e [client] ci: check for improper usage of GL functions
This runs gl-check on the CI and fails the build if broken.
2021-10-01 10:12:40 +10:00
Quantum
e0c1394c33 [client] add gl-check script to check GL function calls
This prevents us from exceeding the minimum GL/EGL versions that we are
targetting or forced to use indirection for due to lack of guarantee in
the ABI.
2021-10-01 10:12:40 +10:00
Quantum
6370350006 [client] opengl: indirectly access non-OpenGL 1.3 functions
This commit adds check for the extensions that we need and then calls
the functions indirectly through gl_dynprocs.

This should improve compatibility with older versions of OpenGL, as we
now fallback to the ARB extensions if possible, and in the case of
glGenerateMipmap, we can handle the function not existing at all.
2021-10-01 01:45:11 +10:00
Quantum
ad4b40fad6 [client] opengl: add gl_dynprocs module
This is similar to egl_dynprocs, except for OpenGL functions.
2021-10-01 01:45:11 +10:00
Quantum
3f72de78da [client] opengl: use util_hasGLExt instead of custom logic 2021-10-01 01:45:11 +10:00
Quantum
4a76401c34 [client] egl: make the last eglDestroyImage call indirect
Missed this one in the last PR.
2021-09-29 18:28:07 +10:00
Quantum
072c54977e [client] egl: use eglCreateImage and eglDestroyImage indirectly
The dmabuf path is optional, so we shouldn't require those functions to
link our program.
2021-09-29 17:48:50 +10:00
Quantum
5c7f168370 [client] egl_dynprocs: use official prototypes from system headers 2021-09-29 17:48:50 +10:00
Quantum
778d27f08a [common] debug: fix incorrect use of #elif defined 2021-09-29 17:48:03 +10:00
Quantum
12840a8324 [client] x11: load glXSwapIntervalEXT dynamically
The Linux OpenGL ABI does not guarantee that glXSwapIntervalEXT will be
exported statically from any library, and indeed on some systems this
function does not link at load time, e.g. with amdgpu-pro. All other
GLX functions that we use are from GLX 1.0, which is guaranteed to be
exported statically.

This commit solves this issue by using glXGetProcAddressARB to load the
function. Note that only the ARB version of glXGetProcAddress is
guaranteed to exist by the Linux OpenGL ABI, which is why we must use
it.
2021-09-29 17:47:36 +10:00
Quantum
1f24ab0742 [host] nvfbc: avoid waking up pointer thread for no reason
If the wait times out, we used to simply restart the loop, which causes
it to check this->stop and exit if set to true. However, nvfbc_stop
already calls lgSignalEvent, which would wake up the pointer thread to
perform the check, so there is no need to set a timeout on the wait.
2021-09-29 17:47:02 +10:00
Quantum
57d220a43b [common] open: detach xdg-open instead of waiting for it
Sometimes, e.g. when xdg-open has to start the browser, the xdg-open
process can stay around until the browser exits, which freezes the
client. Instead, we should not wait for xdg-open to exit.

However, we can't simply not call wait, as that would leave the
xdg-open process around as a zombie. We could turn off the SIGCHLD
handler, but that's a global solution to a local problem. Instead, we
call setsid and fork again to detach the xdg-open process as if it's a
daemon, and let init take care of the reaping process.

Co-Authored-By: Tudor Brindus <me@tbrindus.ca>
2021-09-27 13:35:08 +10:00
Quantum
bc65de5987 [host] installer: build 64-bit NSIS installers
The host is a 64-bit application, so requiring 64-bit Windows isn't an
issue. However, not using 32-bit installers has an advantage: we don't
need to require WoW64.
2021-09-21 22:26:10 +10:00
Quantum
df4a964496 [client] spice: stop if pointer left during guest warp
This prevents attempts to grab the pointer after the guest side warp
finishes if the pointer has left the window in the meantime. On Wayland,
this would result in the pointer moving to the middle of the window when
the confine is created.
2021-09-16 06:25:45 +10:00
Quantum
72f3a9f3cf [client] wayland: free presentation objects
Since the display server owns a graph, it was necessary to move the overlay
graphs destruction so that it happens after display server destruction.
2021-09-11 11:52:52 +10:00
Geoffrey McRae
09d93abf1a [all] update submodules 2021-09-11 10:50:16 +10:00
Geoffrey McRae
2aa236e1f9 [common] options: check for realloc failure 2021-09-11 10:38:07 +10:00
Geoffrey McRae
3cefe9f9b5 [client] config: window width & height are unsigned values 2021-09-11 10:30:31 +10:00
Geoffrey McRae
e249106ddf [client] app: remove dead code 2021-09-11 10:22:44 +10:00
Geoffrey McRae
ab3738624f [client] x11: reduce the scope of deltats 2021-09-11 10:11:00 +10:00
Geoffrey McRae
25ed3632f7 [client] x11: remove dead code 2021-09-11 10:06:52 +10:00
Geoffrey McRae
3adb7ca4b2 [client] wayland: fix possible memory leak 2021-09-11 10:03:27 +10:00
Quantum
5fbb34f8f3 [doc] add upscale/downscale and related words to dictionary 2021-09-04 17:23:13 +10:00
Quantum
20acb9002a [doc] install: fix missing new line warning 2021-09-04 13:35:08 +10:00
Quantum
4ee83a2b31 [client] ci: check --help spelling 2021-09-04 13:31:55 +10:00
Quantum
909a9a903f [doc] lgspell: allow spell checking arbitrary text 2021-09-04 13:31:55 +10:00
Quantum
ce091fd4e4 [client] main: correctly handle EINTR from nanosleep
Previously, all progress made during sleep is reset, so if the thread keeps
getting interrupted before the sleep finishes, the sleep will never complete.
2021-09-04 13:31:30 +10:00
Quantum
49725c9ea4 [doc] ci: run CI build with spell check 2021-09-04 13:30:51 +10:00
Quantum
753b44e34f [doc] support building with spell check 2021-09-04 13:30:51 +10:00
Quantum
1b58f2592c [client] egl: make filters damage aware
This saves a lot of GPU power for partial updates. Running testufo with
lanczos downscaling and FSR upscaling consumed over 90 W, but with this
commit, consumed only 75 W.
2021-09-04 13:30:24 +10:00
Quantum
e9bf225c75 [client] egl: remove useless RenderStep struct 2021-09-04 13:28:49 +10:00
Quantum
f287b4625d [client] config: fix usage of "ie" 2021-09-04 13:28:34 +10:00
Quantum
43b0e80f93 [common] ivshmem: use e.g. instead of incorrect ie for example 2021-09-04 13:28:34 +10:00
Quantum
92155de98d [client] config: fix spelling of synchronize 2021-09-04 13:28:15 +10:00
Quantum
94d7ed300e [docs] ci: build documentation on GitHub Actions 2021-09-04 13:27:51 +10:00
Netboy3
924bd9e543 [doc] Correct a few spelling mistakes 2021-09-04 13:27:35 +10:00
Netboy3
c4eadda389 [doc] install: Add Configuration Widget section
Add a section to cover the configuration widget, the
various tabs and settings and the new post-processing
filter stack.
2021-09-04 13:27:35 +10:00
Netboy3
f563e67e19 [doc] install: Remove scale toggle from key bindings 2021-09-04 13:27:35 +10:00
Netboy3
d7e4536e57 [doc] install: Add additional command line options
Add wayland:fractionScale and reorder options.
2021-09-04 13:27:35 +10:00
Quantum
ab033d84b1 [common] cpuinfo: handle more than 64 threads on Windows
The old code will not correctly report the number of threads on CPUs with
more than one processor group, i.e. when there are more than 64 logical
processors (threads).
2021-09-04 13:25:24 +10:00
Quantum
286da11172 [host] windows: don't register exit event when none exists 2021-09-04 13:25:06 +10:00
Quantum
ac926e4458 [client] egl: support debug contexts on older EGL versions 2021-09-04 13:24:45 +10:00
Quantum
26d2feb4d8 [client] egl: remove warning disabling in ffx.c 2021-09-04 13:24:34 +10:00
Quantum
ffabdd348c [common] cpuinfo: trim trailing whitespace from model name on Windows 2021-08-31 20:57:34 +10:00
Geoffrey McRae
5fc561fa63 [common] cpuinfo: trim any trailing whitespace from CPU model 2021-08-31 20:20:11 +10:00
Quantum
977a4f6323 [client] egl: remove useless comment in filter_downscale.c 2021-08-31 20:14:39 +10:00
Quantum
8514f35474 [common] windebug: handle FormatMessage failure 2021-08-31 20:14:29 +10:00
Quantum
e05bb196f0 [host] app: print CPU information on startup 2021-08-31 20:14:10 +10:00
Quantum
c7666b065a [client] main: print out CPU information on startup 2021-08-31 20:14:10 +10:00
Quantum
0faafbff47 [common] cpuinfo: implement for Windows 2021-08-31 20:14:10 +10:00
Quantum
34fb2f9076 [common] cpuinfo: implement lgDebugCPU
This is a helper function that can be run at startup to quickly generate
a debug print containing CPU information.
2021-08-31 20:14:10 +10:00
Quantum
a6112feddb [common] cpuinfo: implement for linux by parsing /proc/cpuinfo 2021-08-31 20:14:10 +10:00
Quantum
815aac28fb [common] cpuinfo: add common library 2021-08-31 20:14:10 +10:00
Quantum
79a9127c04 [client] imgui: use consistent modal background colours
The translucent white modal background sort of cancels out the dark
background we apply to the overlay, which is undesirable. It should
instead further darken the background.

For consistency, we now use igGetColorU32Col(ImGuiCol_ModalWindowDimBg)
to draw the overlay background, to avoid hardcoding the same colour in
multiple places.
2021-08-31 09:09:26 +10:00
Quantum
31249da533 [client] imgui: run animations at consistent speeds
Currently, this is visible through how fast the cursor blinks, with it
blinking faster at higher refresh rates. This commit makes the timing
consistent.
2021-08-31 08:16:07 +10:00
Quantum
f4df690d9f [client] egl: fix preset tooltip always showing 2021-08-30 18:47:48 +10:00
Quantum
239cb6a92b [client] egl: add separator after presets 2021-08-30 18:45:21 +10:00
Quantum
876a4125bf [client] egl: correctly select the preset when saving over it 2021-08-30 18:45:21 +10:00
Quantum
ffbf6cd8b4 [client] egl: make preset UI more intuitive
This commit makes selecting a preset load it, and changes "Create preset" to
"Save preset as...".
2021-08-30 18:45:21 +10:00
Quantum
39e42ba735 [common] option: change option_dump to option_dump_preset
This new function dumps all options marked as preset instead of dumping
individual sections. This should allow filter options to not be all grouped
into the [eglFilter] section.
2021-08-30 18:32:16 +10:00
Quantum
3345ff8448 [client] egl: mark all preset-only options as such 2021-08-30 18:21:54 +10:00
Quantum
44850f1699 [common] option: add preset-only options that don't show up in help 2021-08-30 18:21:54 +10:00
Quantum
8a2ae6860e [client] egl: warn when attempting to save without preset selected 2021-08-30 18:21:54 +10:00
Quantum
1717555187 [client] egl: implement preset deletion 2021-08-30 18:21:54 +10:00
Quantum
dc27638025 [client] egl: implement loading and saving of filter order 2021-08-30 18:21:54 +10:00
Quantum
c85cc7d668 [client] egl: show preset errors as modal dialogs 2021-08-30 18:21:54 +10:00
Quantum
311f7241c6 [client] egl: implement preset loading/saving logic 2021-08-30 18:21:54 +10:00
Quantum
b7b93f624c [client] egl: implement options loading/saving for downscale 2021-08-30 18:21:54 +10:00
Quantum
084837b936 [client] egl: add loadState and saveState for filters 2021-08-30 18:21:54 +10:00
Quantum
4adb425337 [client] egl: add UI for presets list 2021-08-30 18:21:54 +10:00
Quantum
e11246d46e [common] stringlist: implement item removal 2021-08-30 18:21:54 +10:00
Quantum
f0beedb5ba [common] vector: implement item removal 2021-08-30 18:21:54 +10:00
Quantum
bbd39b8185 [common] option: implement the ability to set option values
This can then be used to update the options from EGL filters, and then
dumping them to files.
2021-08-30 18:21:54 +10:00
Quantum
f0624ccf89 [common] option: implement ability to dump config into ini
This is intended to be used for saving filter options into an ini file.
2021-08-30 18:21:54 +10:00
Tudor Brindus
e22a070dd3 [common] appstrings: add blurb for xyene 2021-08-30 17:46:24 +10:00
Quantum
0c27111260 [common] option: return NAN when float option doesn't exist
false is not a float value.
2021-08-30 17:40:35 +10:00
Quantum
5225d2e97f [client] egl: fix framebuffer leaking textures 2021-08-28 19:17:24 +10:00
Quantum
e5f2b3079e [common] stringlist: use vector for storage 2021-08-28 19:17:15 +10:00
Quantum
e6df0acad9 [common] vector: eliminate double allocation when possible
This commit creates two constructor/destructor pairs for vector:
* vector_alloc/vector_free dynamically allocates the vector itself
* vector_create/vector_destroy uses existing Vector objects
2021-08-28 19:17:15 +10:00
Quantum
ba527761ef [common] vector: inline common operations 2021-08-28 19:17:15 +10:00
Quantum
ceff9dca9b [client] egl: simplify filter moving logic with memmove
This avoids duplicating the entire array of filters.
2021-08-25 05:35:50 +10:00
Quantum
e040b88bf0 [common] ivshmem: switch to using vectors 2021-08-24 22:10:36 +10:00
Quantum
7c7eff8dba [client] egl: make texture_dmabuf.c use vector
This replaces the custom memory management code.
2021-08-24 22:10:36 +10:00
Quantum
377757e743 [common] vector: add indexed iteration modes 2021-08-24 22:10:36 +10:00
Quantum
53b4b4818b [common] vector: allow inplace construction with vector_push
This makes vector_push return a pointer to the pushed element.

It also allows the user to push a NULL pointer, which means allocating the
memory for the element but do not copy anything into it.
2021-08-24 22:10:36 +10:00
Quantum
07d3d6cbe7 [common] vector: implement a clear operation 2021-08-24 22:10:36 +10:00
Quantum
b71838a530 [common] vector: allow vector_free to be used with NULL
This is done for consistency with free.
2021-08-24 22:10:36 +10:00
Geoffrey McRae
b118c3b681 [client] egl: implement nicer drag & drop re-ordering of filters 2021-08-24 22:05:46 +10:00
Quantum
e5e76d784e [client] egl: allow postprocessing filters to be reordered by dragging 2021-08-22 21:36:13 +10:00
Quantum
99761b195f [client] egl: switch postprocessing filters to use vectors
This will allow them to be reordered much more easily.
2021-08-22 21:36:13 +10:00
Quantum
24e0343156 [common] vector: add new data structure 2021-08-22 21:36:13 +10:00
Quantum
0b70aa49d0 [doc] add documentation about libdecor 2021-08-21 23:02:20 +10:00
Quantum
1e2caf4c9f [host] pipewire: implement basic capture
It works, with the following limitations:
1. user is forced to select the monitor through platform-specific mechanisms
   every time the client starts.
2. cursor is composed onto the screen, and no position can be reported.
2021-08-21 23:01:11 +10:00
Quantum
28eae3bd86 [host] linux: add skeleton pipewire capture backend 2021-08-21 23:01:11 +10:00
Quantum
4b3aaa7e0c [host] cmake: report capture backends enabled 2021-08-20 21:03:50 +10:00
Quantum
164dd00490 [common] rects: fix typo in rectContains 2021-08-20 17:13:04 +10:00
Quantum
9bd205a527 [client] imgui: fix modifier key modification race
imgui really hates it when we update the modifier key state after igNewFrame.
The result is:

    void ImGui::ErrorCheckEndFrameSanityChecks(): Assertion
    `(key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) &&
    "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"'
    failed.

Therefore, we buffer the modifier state information and update it in the IO
object right before we call igNewFrame.
2021-08-20 13:21:50 +10:00
Quantum
c0fa6c414c [client] spice: do not warp host cursor if guest cursor is not visible
This avoids warping the host cursor when the guest-side warp has not finished,
which will result in the host cursor exiting at the wrong position if it exits
at that moment.
2021-08-20 13:21:26 +10:00
Quantum
11a661ce3a [client] spice: don't display mouse before realignment finishes
This avoids showing the mouse briefly at the old position when reentering
the window.
2021-08-20 13:21:26 +10:00
Geoffrey McRae
c246b4a719 Revert "[client] core: realign in the enter/focus handlers if possible"
This reverts commit 17617cc421.
2021-08-19 23:19:59 +10:00
Geoffrey McRae
17617cc421 [client] core: realign in the enter/focus handlers if possible 2021-08-19 22:57:03 +10:00
Geoffrey McRae
e1a4401ffa [client] core: wait for the host to process the cursor move 2021-08-19 22:17:22 +10:00
Geoffrey McRae
4b3a79c110 [client] x11: remove print from debugging 2021-08-19 21:35:54 +10:00
Geoffrey McRae
3c3c0f70be [all] bump the LGMP version to obtain access to data serial tracking 2021-08-19 21:29:03 +10:00
Geoffrey McRae
2d470b8deb [client] x11: filter out virtual/grabbed XIEnterEvents 2021-08-19 21:29:03 +10:00
Quantum
9aa0d3ddab [client] egl: fix context creation on EGL 1.4
EGL_CONTEXT_OPENGL_DEBUG is only defined in EGL 1.5, and therefore, we should
not be passing it on older versions of EGL.
2021-08-19 21:28:56 +10:00
Quantum
429620c48b [client] egl: dynamically import glBufferStorageEXT
On some implementations (e.g. llvmpipe), the function can only be queried via
eglGetProcAddress.
2021-08-19 21:28:45 +10:00
Quantum
5a906131eb [all] cmake: tell users to clone submodules when they haven't
This gives users a command to run that will automagically fix the submodule
situation, and should reduce the amount of support requests.
2021-08-19 21:28:36 +10:00
Quantum
1021c9ce92 [client] x11: implement keyboard modifiers 2021-08-19 21:28:21 +10:00
Quantum
ce3f11fd40 [client] x11: implement keyboard typing 2021-08-19 21:28:21 +10:00
Quantum
bb91b41c64 [client] egl: look at 3x3 around the pixel instead of 4x4
Using 4x4 means that some pixels will be outside of the lanczos window. The
ideal lanczos function should in fact be zero in those areas, so we shouldn't
waste time processing those pixels.

I can't notice any difference in the results.
2021-08-19 15:52:44 +10:00
Quantum
520460669c [client] egl: set gl_Position.z in cursor vertex shader 2021-08-19 12:24:55 +10:00
Quantum
1c7d14169e [client] kb: rename key code arrays from xfree86_* to linux_*
We are using Linux key codes defined in input-event-codes.h, not XFree86
stuff.
2021-08-17 19:08:07 +10:00
Quantum
ccda264648 [common] windebug: pass FORMAT_MESSAGE_IGNORE_INSERTS to FormatMessage
This avoids problems when the error message we are told to format contains
inserts like %1.

See https://devblogs.microsoft.com/oldnewthing/20071128-00/?p=24353 for
details (or for fun).
2021-08-17 19:07:41 +10:00
Quantum
2ff32b230e [client] cmake: don't install cimgui.a into CMAKE_INSTALL_PREFIX 2021-08-16 20:02:43 +10:00
Quantum
2dbd4f168e [all] cmake: provide an uninstall target 2021-08-16 19:56:37 +10:00
Quantum
4ecf749f7e [host] remove all casts around malloc 2021-08-16 16:26:58 +10:00
Quantum
2de9e3e9be [common] remove all casts around malloc 2021-08-16 16:26:58 +10:00
Quantum
81c38e825c [client] remove all casts around malloc
The cast is unnecessary in C and should be removed to avoid clutter.
2021-08-16 16:26:58 +10:00
Quantum
fd4a4114e6 [client] egl: pad areas of the desktop repainted to cover overlays
We pad the screen coordinates and then convert to desktop coordinates,
so that the padding will always be a pixel wide on screen.
2021-08-16 16:26:18 +10:00
Quantum
cdda89cef7 [host] use correct argument order for calloc 2021-08-16 16:25:59 +10:00
Quantum
104141eec1 [client] use correct argument order for calloc 2021-08-16 16:25:59 +10:00
Quantum
4d907cecab [common] use correct argument order for calloc
The signature for calloc is void *calloc(size_t num, size_t size), where num
is the number of elements to allocate, and size is the size. Therefore, to
allocate a single struct, we should pass 1 for num and the size of the struct
as size.

In some places, we use the opposite order, and we should flip it.
2021-08-16 16:25:59 +10:00
Quantum
b7d3bbbd82 [client] egl: use standard-compliant way of EGL detection
According to the documentation for eglQueryString:

> EGL_BAD_DISPLAY is generated if display is not an EGL display connection,
> unless display is EGL_NO_DISPLAY and name is EGL_EXTENSIONS.

Therefore, we should check EGL by doing:

    eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS)

Indeed, the old way of eglQueryString(EGL_NO_DISPLAY, EGL_VERSION) works on
libglvnd but not using mesa's libEGL.so directly.

Also added a warning to make it more obvious that EGL is not available.
2021-08-16 16:25:48 +10:00
Quantum
8a5efef622 [client] spice: avoid spice_key_modifiers when input is disabled 2021-08-16 16:25:11 +10:00
Tudor Brindus
14ad83c6b8 [client] use variable-relative sizeof where possible 2021-08-16 16:22:55 +10:00
Tudor Brindus
1c5620ba25 [common] use variable-relative sizeof where possible 2021-08-16 16:22:55 +10:00
Tudor Brindus
982b4e6625 [host] use variable-relative sizeof where possible 2021-08-16 16:22:55 +10:00
Quantum
c3f7327187 [obs] display DMABUF option but disable it on older OBS
This allows users to be aware that the option exists and they should upgrade.
2021-08-15 18:01:15 +10:00
Quantum
8f5afe1848 [client] egl: clamp sharpness settings in filters
While the slider does not allow you to get out of range by dragging,
the user could still type in out of range values, so we clamp the values.
2021-08-15 18:01:03 +10:00
Quantum
36073586e7 [client] egl: add tooltip about Ctrl+Click on sharpness sliders
With the new keymap feature, we are now able to properly support letting
the user enter exact values into the sliders. This commit adds a tooltip
to help the user discover this feature.

Note that this currently only works on Wayland. The X11 backend will need
to call app_handleKeyboardModifiers.
2021-08-15 18:01:03 +10:00
Quantum
c89518ead4 [common] option: use isspace from <ctype.h> 2021-08-15 18:00:52 +10:00
Quantum
7cd0c55847 [client] wayland: support high DPI cursors when needed 2021-08-15 09:49:38 +10:00
Quantum
2dd1ad53f8 [client] wayland: respect XCURSOR_THEME and XCURSOR_SIZE env variables 2021-08-15 09:46:55 +10:00
Quantum
d35c448058 [client] wayland: set keyboard LED state when changed 2021-08-15 09:46:31 +10:00
Quantum
3a00277e93 [client] spice: add ability to set keyboard LED state in guest
FIXME: update PureSpice
2021-08-15 09:46:31 +10:00
Geoffrey McRae
0f6f89fa5b [client] update PureSpice submodule 2021-08-15 09:42:03 +10:00
Quantum
bbd173000f [client] egl: clamp downscale factor range 2021-08-14 14:57:33 +10:00
Quantum
5b2fce0830 [client] ci: add libxkbcommon-dev dependency 2021-08-14 14:47:00 +10:00
Quantum
96738ab9d0 [client] egl: make downscale filter use text input widget
The major/minor pixel size hack is too confusing. This commit replaces
that with a text input and a slider.
2021-08-14 14:44:26 +10:00
Quantum
7045760490 [client] wayland: add keyboard typing handling with xkbcommon 2021-08-14 14:44:26 +10:00
Quantum
9414449408 [client] app: add ability to receive keyboard typing in overlays 2021-08-14 14:44:26 +10:00
Quantum
5f3bd778c0 [client] egl: add debug prints for renderStartup errors 2021-08-14 12:20:12 +10:00
Quantum
f66486b0c7 [client] egl/downscale: implement filter switching 2021-08-14 12:19:50 +10:00
Quantum
2c02e6c4a0 [client] egl: add linear downscale filter shader 2021-08-14 12:19:50 +10:00
Quantum
94de061587 [client] egl: implement lanczos filter shader 2021-08-14 12:19:50 +10:00
Quantum
16adbab5d4 [client] all: remove needless initalization 2021-08-14 12:19:07 +10:00
Quantum
579f998519 [client] all: replace assert with DEBUG_ASSERT 2021-08-14 12:19:07 +10:00
Quantum
85a96d1e06 [client] all: use DEBUG_UNREACHABLE instead of assert
Due to the way assert is defined in standard C, compilers in release mode
will not treat it as unreachable. This explains a lot about those pesky
uninitialized variable bugs, actually.
2021-08-14 12:19:07 +10:00
Quantum
b2630024a7 [common] all: switch asserts to DEBUG_ASSERT 2021-08-14 12:19:07 +10:00
Quantum
4f7ce91e7f [host] capture: switch all asserts to DEBUG_ASSERT 2021-08-14 12:19:07 +10:00
Quantum
a9241f6710 [common] debug: add DEBUG_UNREACHABLE() macro
This lets us mark code as unreachable and signals the compiler that this
is the case with __builtin_unreachable().

We also mark DEBUG_FATAL as unreachable.
2021-08-14 12:19:07 +10:00
Quantum
be1306f91a [common] debug: add DEBUG_ASSERT macro
This, unlike the standard assert macro, is guaranteed to print the failed
assertion to our log file, and tests the assertion even with NDEBUG defined
so we can more easily catch failures in production binaries without crashing
the program.

The motivation of this is how MinGW handles assertion failures: it creates a
dialog window that the headless user will not be able to see, and blocks the
program from being restarted by the service. Since the failed assertion is
displayed in the dialog, it doesn't print anything to the log, making it
impossible to diagnose issues.
2021-08-14 12:19:07 +10:00
Quantum
10ee6cd031 [host] nvfbc: read nvfbc:diffRes option with the correct type 2021-08-14 09:21:34 +10:00
Quantum
e5d252290d [common] array: add ALIGN_PAD macro for common logic
ALIGN_PAD(x, a) returns x rounded up to the nearest multiple of a.
2021-08-14 08:05:29 +10:00
Quantum
712dcee07f [host] app: remove useless ALIGN_DN and ALIGN_UP macros 2021-08-14 08:05:18 +10:00
Quantum
dda927da18 [obs] implement dmabuf import support on OBS 27+ 2021-08-13 20:25:35 +10:00
Quantum
717b90366b [common] ivshmem: use correct page alignment logic
Before, if the size is exactly the multiple of the page size, an extra padding
page is added for no reason. This commit fixes the logic and also uses the
page size obtained dynamically.
2021-08-13 20:24:15 +10:00
Quantum
a76b274e1a [client] main: use ARRAY_LENGTH macro 2021-08-13 20:24:01 +10:00
Quantum
074341e421 [host] windows/crash: do not report absolute paths on build machines
This commit makes the crash handler show relative paths instead of absolute
ones, which makes the stack traces generated easier to read.

On the other hand, absolute paths makes sense on Linux, since the user is
expected to build the binaries themselves, and gdb will be able to find the
source code.
2021-08-13 20:23:40 +10:00
Quantum
acd5ce51db [host] dxgi: use FAILED macro instead of comparing against S_OK 2021-08-13 20:21:50 +10:00
Quantum
d3ea9662bf [host] nvfbc: remove rectangles that are entirely contained in others
This makes nvfbc report less useless damage and makes the client run faster.
2021-08-13 20:21:27 +10:00
Quantum
e945955d13 [common] rects: add rectsRejectContained function
This function will remove rectangles in a list that are entirely contained
in another rectangle in the same list.
2021-08-13 20:21:27 +10:00
Quantum
566c89e9d8 [host] dxgi: correctly count moved rectangles 2021-08-13 20:21:08 +10:00
Quantum
8c18817e2d [client] egl: don't generate mipmaps in downscale filter
When using DMABUF, the mipmaps can cause driver hangs and crashes.
2021-08-12 17:03:18 +10:00
Geoffrey McRae
35bd641d2a [client] overlay: remove the unused menu bar 2021-08-12 15:54:16 +10:00
Geoffrey McRae
117e88c240 [client] egl: add new downscale filter 2021-08-12 15:54:16 +10:00
Quantum
b3173bdddc [host] dxgi: include correct DXGI headers
The declarations in dxgi_extra.h are not missing, they are in dxgi1_2.h and
dxgi1_5.h, which exist in MinGW-w64 since 2017.
2021-08-12 12:35:45 +10:00
Quantum
61a4b0744d [host] dxgi: use standard MinGW libd3d11.a
MinGW has been shipping this file since 2014, and that version contains the
only function from the dll that we call: D3D11CreateDevice.
2021-08-12 11:58:34 +10:00
Geoffrey McRae
6387bf2d2e [client] rework the configuration overlay to allow for tabs 2021-08-12 09:04:45 +10:00
Geoffrey McRae
fe6339fc77 [client] egl: re-order CAS to before FSR for better results 2021-08-12 06:56:16 +10:00
Quantum
3f8c7c8d0d [client] egl: fix buffer overflow in desktop_rects module
The module has been changed to support variable amount of rectangles,
so we should just allocate a VLA.
2021-08-11 21:23:13 +10:00
Quantum
543c97987b [client] egl: remove needless precision quantifiers
We simply use precision mediump float; for everything. We don't actually
need highp anyways, and we don't use it for stuff like CAS or FSR.
2021-08-11 20:47:46 +10:00
Geoffrey McRae
06da52acfc [client] egl/fsr: release consts when no longer needed 2021-08-11 20:47:03 +10:00
Geoffrey McRae
5a2f34d71c [client] egl/cas: release consts when no longer needed 2021-08-11 20:42:56 +10:00
Quantum
8b2db071d8 [client] egl: precompute CAS filter constants on CPU 2021-08-11 20:38:42 +10:00
Quantum
3a1a9121eb [client] egl: make FSR filter show inactive in config when disabled 2021-08-11 20:17:17 +10:00
Quantum
f80b67bc50 [client] egl: precompute FSR filter constants on CPU 2021-08-11 20:16:39 +10:00
Quantum
fe823b6172 [client] egl: display FSR equivalent quality mode
This also displays a tooltip to explain that quality can be changed by
altering guest resolution and also show the resolutions needed to achieve
each quality mode.
2021-08-11 20:05:27 +10:00
Quantum
c4c60fd330 [client] egl: update FSR filter state upon resolution change 2021-08-11 20:05:27 +10:00
Geoffrey McRae
5a5b867c73 Revert "[client] egl: make FSR detect an input size change and activate if valid"
This reverts commit 73f125dcc7.
2021-08-11 20:05:07 +10:00
Geoffrey McRae
73f125dcc7 [client] egl: make FSR detect an input size change and activate if valid 2021-08-11 20:02:29 +10:00
Quantum
9bded74543 [host] dxgi: use CopySubresourceRegion when possible
This commit adds damage tracking to the DXGI textures, and only copies the
damaged areas to the textures with ID3D11DeviceContext::CopySubresourceRegion.

The sleep logic in waitFrame makes it difficult for this to reduce the
latency, but removing it shows significant improvements (6-7 ms to ~3 ms)
when a tiny portion of the screen is damaged, while showing no difference on
full screen damage.
2021-08-11 19:01:52 +10:00
Quantum
7e982a6658 [client] util: replace util_mergeOverlappingRects with common version 2021-08-11 19:01:52 +10:00
Quantum
604b44d6d8 [common] rects: add rectsMergeOverlapping helper function
This is based on the client's util_mergeOverlappingRects.
2021-08-11 19:01:52 +10:00
Quantum
22bbc2457e [client] wayland: fix deadlock when using wayland:warpSupport=no 2021-08-11 19:01:18 +10:00
Geoffrey McRae
f0ea882165 [client] egl: cleanup texture filtering/post-processing 2021-08-11 18:53:36 +10:00
Geoffrey McRae
f78154d282 [client] egl: fixes to shader post-process pipeline 2021-08-11 06:31:01 +10:00
Quantum
cd5ecf3e5a [client] egl: don't erase damage when invalidating whole window 2021-08-11 02:43:08 +10:00
Quantum
a850a1b51b [client] egl: implement C wrappers for FidelityFX constant computation 2021-08-11 02:42:55 +10:00
Quantum
6e28d7a4a5 [client] egl: exempt shaders from copyright refresh script 2021-08-11 02:42:24 +10:00
Quantum
4a06f7cfd5 [client] cmake: make MakeObject use relative paths
This prevents issues like obscure characters getting transformed in symbol
names, resulting in an endless game of whack-a-mole finding symbols that are
replaced, such as 58964ce317.
2021-08-11 02:42:12 +10:00
Quantum
c1a362f8d3 [client] egl: handle \r character when processing #includes 2021-08-11 02:41:54 +10:00
Quantum
9f4afcd944 [common] crash: use DEBUG_WINERROR on windows 2021-08-11 02:41:33 +10:00
Geoffrey McRae
4f1136d0cd [client] core: dont warp the cursor if the overlay is active 2021-08-10 16:08:13 +10:00
Geoffrey McRae
127d3acd96 [client] egl: use a texel based version of textureGather for FSR 2021-08-10 14:21:46 +10:00
Quantum
ccee347740 [client] egl: don't define FSR_RCAS_F in ffx_fsr1_rcas.frag 2021-08-10 13:57:23 +10:00
Geoffrey McRae
c3a143732c [client] egl: cosmetics 2021-08-10 13:46:48 +10:00
Geoffrey McRae
dc0b3a8d45 [client] egl: rework post process filters and add AMD FXR 2021-08-10 13:46:48 +10:00
Quantum
3b751a2017 [client] egl: perform full copy for framebuffer textures after resize
This prevents the code from using damage rectangles that are no longer on the
screen, causing an out-of-bounds write.
2021-08-10 13:42:25 +10:00
Geoffrey McRae
230ce81eb8 [client] egl: allocate space for the initial texture dimensions
This fixes a buffer overrun when writing to the dimensions array
2021-08-10 09:41:56 +10:00
Geoffrey McRae
e707f9d933 [client] egl: enable ffxCAS if disabled and the sharpness is changed 2021-08-10 07:56:24 +10:00
Geoffrey McRae
64ed383128 [client] egl: re-process the texture and invalidate if a setting changed 2021-08-10 07:51:23 +10:00
Quantum
685499a0e0 [client] egl: prefer gawk and mawk when building shaders
We'd rather use known versions of awk if possible for ease of troubleshooting.
2021-08-10 06:17:38 +10:00
Quantum
705250f23d [client] egl: correct assign to gl_Position in basic.vert
gl_Position is expected to be using homogeneous coordinates, which requires
w to be a coordinate scale factor, usually 1.0. z should also be set in order
for depth to be well-defined. Therefore, we should set gl_Position.zw to
vec2(0.0, 1.0).
2021-08-10 06:10:42 +10:00
Quantum
eb680086ef [client] egl: correctly use flexible array members for BindData
Array size of 0 is a gcc extension, the standard C is not declaring a size.
2021-08-10 06:10:42 +10:00
Geoffrey McRae
58964ce317 [client] cmake: replace - with _ too 2021-08-10 02:35:16 +10:00
Geoffrey McRae
1128eb0e84 [client] x11: don't hang when there are no message pending 2021-08-10 01:47:03 +10:00
Geoffrey McRae
e41cbf5f32 [common] option: option_get_float should return a float 2021-08-10 01:36:12 +10:00
Geoffrey McRae
f2b8ff9e8d [client] app: make overlay mode more transparent 2021-08-10 01:15:31 +10:00
Geoffrey McRae
cc3494437a [client] egl: add ffx cas configuration options 2021-08-10 01:10:08 +10:00
Geoffrey McRae
88eada3494 [common] option: add support for float option types 2021-08-10 01:09:40 +10:00
Geoffrey McRae
37faccd014 [client] egl: allow ffxCAS sharpness configuration 2021-08-10 00:54:54 +10:00
Geoffrey McRae
30e6a258ad [client] egl: cleanup pointer mess in egl_desktopInit 2021-08-10 00:48:41 +10:00
Geoffrey McRae
d24bc75519 [client] egl: added missing vertex shader file 2021-08-09 23:14:01 +10:00
Geoffrey McRae
92de467edc [client] egl: add ffx_cas post process filter 2021-08-09 23:12:58 +10:00
Quantum
9b1d03fcfe [client] egl: implement #include for shaders with awk 2021-08-09 22:04:07 +10:00
Geoffrey McRae
4eda01949d [client] egl: give pp filters the dimensions of all prior textures 2021-08-09 22:02:07 +10:00
Geoffrey McRae
062d18d5fa [client] egl: don't allocate the texture ringbuffer unless needed 2021-08-09 18:28:52 +10:00
Geoffrey McRae
04a54598b3 [client] egl: set a default scale for textures without filters 2021-08-09 18:27:10 +10:00
Geoffrey McRae
79dcc6d4f1 Revert "[client] egl: set a default scale for textures without filters"
This reverts commit 57a74c156b.
Pushed a ton of changes that should not have been pushed
2021-08-09 18:26:30 +10:00
Geoffrey McRae
57a74c156b [client] egl: set a default scale for textures without filters 2021-08-09 18:24:33 +10:00
Geoffrey McRae
6882e5c59f [client] egl: provide the texture scale to the desktop shader
If the texture has a post-processing filter that has scaled the texture,
the desktop fragment shader needs to know this if it's doing linear
scaling.
2021-08-09 18:22:28 +10:00
Geoffrey McRae
f7f8060447 [client] egl: allow setting an output scale for a post-process shader 2021-08-09 17:57:36 +10:00
Quantum
53461d7515 [client] egl: simplify desktop vertex shader
In GLSL, using the / operator on two vectors of the same size divides the
vector component-wise, i.e. vec2(a, b) / vec2(c, d) == vec2(a / c, b / d).
2021-08-09 17:51:13 +10:00
Quantum
9b87f4ba5e [client] egl: cycle through multiple textures for dmabuf
This avoids race conditions in GL drivers when attempting to render and
call glEGLImageTargetTexture2DOES on the same texture.

Also, when using glEGLImageTargetTexture2DOES, we do not need to allocate
storage for textures.
2021-08-09 17:12:11 +10:00
Quantum
5e13549f74 [host] windows: use DEBUG_WINERROR for MsgWaitForMultipleObjects 2021-08-09 17:07:25 +10:00
Quantum
87a21f5f5e [host] windows: use DEBUG_WINERROR for CallNtPowerInformation
We need to use the function RtlNtStatusToDosError to convert NTSTATUS to
Windows error codes.
2021-08-09 17:07:25 +10:00
Quantum
9246e00163 [host] windows: use DEBUG_WINERROR for exit event opening code 2021-08-09 17:07:25 +10:00
Geoffrey McRae
1fd726eed7 [client] x11: be less sensitive to frame skips
External events like launching other applications can cause latency
spikes while X11 initializes the application, we should only start
adjusting our delay if we see excessive skips over a 1s period.
2021-08-09 15:51:01 +10:00
Geoffrey McRae
bc7e59c9d7 [client] x11: prevent present event loop underruns
Queue up and maintain a list of presentation timestamps to avoid spikes
caused by X11 event processing latency.
2021-08-09 15:24:12 +10:00
Geoffrey McRae
179eaef29d [client] x11: switch to epoll for event wait loop 2021-08-09 15:23:44 +10:00
Geoffrey McRae
f50ef4c23c [client] egl: remove includes from testing 2021-08-09 14:13:03 +10:00
Geoffrey McRae
86d6b67337 [client] egl: rework egl to accomodate post-processing filtering 2021-08-09 14:08:10 +10:00
Geoffrey McRae
30ad28ffd1 [common] CountedBuffer: cosmetics 2021-08-09 14:07:39 +10:00
Geoffrey McRae
69f6532b8d [common] ringbuffer: allow reverse iteration 2021-08-09 14:06:32 +10:00
Geoffrey McRae
91d1b8d2cd [client] egl: refactor egl_texture_free to use project naming standards 2021-08-08 17:52:13 +10:00
Geoffrey McRae
baf9661530 [client] egl: remove texture->ops indirection 2021-08-08 17:31:52 +10:00
Geoffrey McRae
2141046da9 [client] opengl: refactor to use project naming standards 2021-08-08 17:21:25 +10:00
Geoffrey McRae
266ad27998 [client] egl: refactor to use project naming standard 2021-08-08 17:16:10 +10:00
Geoffrey McRae
f4a925a750 [client] main: destroy and finish fontconfig usage
Fixes ASAN reported memory leak
2021-08-08 16:21:48 +10:00
Geoffrey McRae
30ed563504 [client] interface: refactor to use camlCase function names 2021-08-08 15:43:42 +10:00
Geoffrey McRae
d347b28481 [client] egl: implement free for texture frambuffer 2021-08-08 15:35:13 +10:00
Geoffrey McRae
f8ae291090 [client] interface: switch to using UPCAST for the renderer's data 2021-08-08 15:32:01 +10:00
Geoffrey McRae
45d1f27fb4 [client] interface: rename LG_Renderer to LG_RendererOps
Part of the standardisation of using the `Ops` suffix for all interfaces
2021-08-08 14:43:04 +10:00
Geoffrey McRae
1a8267d55a [client] interface: cleanup the renderer interface
Removes the silly typedefs and adds some basic documentation as to the
usage of each function.
2021-08-08 14:39:40 +10:00
Quantum
b822e255d8 [client] egl: attempt DMABUF import and fallback if it fails
This should deal with drivers not supporting our DMABUF without attempting
to identify the drivers and blacklist them.
2021-08-08 09:53:47 +10:00
Geoffrey McRae
037b76750a [client] egl: revert glsync changes
`process` and `bind` are called from the same thread in order, there is
no need for atomic usage here.

This reverts commit 3d7dbd6371.
This reverts commit b3db1ba10b.
2021-08-08 09:44:59 +10:00
Geoffrey McRae
e949f2f8d2 [client] egl: texture_framebuffer should call the stream init 2021-08-08 09:43:28 +10:00
Geoffrey McRae
88c91d4752 [egl] texture: use more appropriate vairable names (parent & this) 2021-08-08 09:35:56 +10:00
Geoffrey McRae
3d7dbd6371 [client] egl: sync is now an atomic, access it as such 2021-08-08 09:26:36 +10:00
Quantum
b3db1ba10b [client] egl: eliminate GLsync object leaks
It used to be the case that we overwrite this->sync even if it was non-zero
when updating the texture, without deleting the sync object. If we update
faster than we render, the result would be leaking sync objects.

This commit ensures that sync objects are deleted when they are replaced.
2021-08-08 09:19:04 +10:00
Geoffrey McRae
16f68d6b1b [client] main: don't call stopWaitFrame if jitRender is not enabled 2021-08-08 09:01:48 +10:00
Geoffrey McRae
64da3465b8 [client] x11: invalidate the full window after timeout from expose
Invalidating the entire window on an Expose event causes poor WM
performance when dragging the window around. Instead flag to redraw and
wait for the expose events to stop for 100ms before doing it.
2021-08-08 08:49:46 +10:00
Geoffrey McRae
12d256c7c8 [client] egl: do a full redraw if nightvision is toggled 2021-08-08 08:42:08 +10:00
Quantum
3e32e01c30 [client] egl/imgui: use imgui for night vision gain configuration 2021-08-08 08:42:01 +10:00
Quantum
ac3677d9ae [client] egl: implement partial copies for framebuffer textures
This uses the same line sweep algorithm originally created to copy DXGI
textures to IVSHMEM to implement the copy from IVSHMEM to memory-mapped
pixel buffer objects.
2021-08-08 08:30:11 +10:00
Quantum
0462cee9db [common] rects: implement routine to copy rectangles from framebuffer 2021-08-08 08:30:11 +10:00
Quantum
cab95c5eed [common] rects: refactor rect buffer copy code to common module
This also fixes a bug of accidentally multiplying the stride by 4 when
the stride is already in bytes and not pixels.
2021-08-08 08:30:11 +10:00
Quantum
4205e49786 [common] appstrings: add blurb for quantum 2021-08-07 08:54:56 +10:00
Geoffrey McRae
e755f0befa [client] egl: correct letterbox area clear attempt #3 - sigh 2021-08-07 04:00:18 +10:00
Geoffrey McRae
3e08e7aafa [client] x11: prevent skew from enter/leave/focus and fullscreen events 2021-08-07 03:49:32 +10:00
Geoffrey McRae
9f6ad864ed [client] egl: correct letterbox area clear attempt #2 :) 2021-08-07 03:44:47 +10:00
Geoffrey McRae
ec56b2760a [client] x11: don't allow window manager events to skew cal timing 2021-08-07 03:39:11 +10:00
Geoffrey McRae
e5a138d854 [client] egl: properly clear the letterbox areas 2021-08-07 03:28:52 +10:00
Geoffrey McRae
ad256e0b00 [client] x11: improve presentation sync calibration 2021-08-07 02:36:11 +10:00
Geoffrey McRae
162b1b93db [client] main: don't include the swap into the render timings
If vsync is enabled the swap will block until vblank skewing the timing
metrics.
2021-08-07 01:45:42 +10:00
Geoffrey McRae
0ec66ba210 [client] main: increase render graph max scale to 10ms
Some GPUs (like my K1200) easly exceed 2ms on the render time making
this graph useless.
2021-08-07 01:45:42 +10:00
Quantum
3d29967a8d [host] dxgi: copy only damaged regions to IVSHMEM
This implementation uses a line sweep algorithm to copy the precisely the
intersection of all accumulated damage rectangles, ensuring that every
pixel is copied exactly once, and no pixel is ever copied multiple times.
Furthermore, once a row has been swept, we update the framebuffer write
pointer immediately.
2021-08-06 22:55:15 +10:00
Quantum
5d3c00717a [client] egl/imgui: use imgui for scaling algorithm selection 2021-08-06 22:49:49 +10:00
Quantum
dc7fd74327 [client] egl: refactor config dialog into main egl module
This will allow other things like scaling to be implemented.
2021-08-06 22:49:49 +10:00
Quantum
5b26017a8a [client] overlay: move separator above donation prompt
It looks really weird having a separator right after a sentence ending in :.
A separator makes the list look detached from the paragraph that introduces
it, which looks awkward. Instead, this commit moves the separator before the
introducing paragraph.

Also added logic to properly pluralize the sentence.
2021-08-06 22:49:02 +10:00
Quantum
3651852430 [host] nvfbc: round pitch to multiple of 128 for dmabuf import
Certain drivers do not support pitches that are not multiples of 128 bytes,
and instead just does some kind of rounding internally. On DXGI, this is not
a problem because the API rounds pixel pitch, but NvFBC does not. This causes
certain resolutions to simply not work with dmabuf, most notably 3440x1440,
which is 1440p ultrawide.

Since we are copying pixels with the CPU anyways, we might as well round the
pitch up to 128 bytes (32 pixels).
2021-08-06 22:48:33 +10:00
Quantum
6bd454f77f [client] overlay/config: switch to use new URL widget when possible 2021-08-05 22:43:46 +10:00
Quantum
08f3ad504c [client] imgui: implement new widget for displaying URLs
Note that actually opening the browser is not implemented yet.
2021-08-05 22:43:46 +10:00
Quantum
2f8ebc29e8 [common] open: implement opening URLs in browser
This is currently only implemented for Linux.

On Windows, ShellExecute should be used, but that should be done when it's
actually needed so it could be tested.
2021-08-05 22:43:46 +10:00
Geoffrey McRae
2856928b57 [host] windows: implement KVMFR_FEATURE_SETCURSORPOS 2021-08-05 22:35:22 +10:00
Geoffrey McRae
afbee641b1 [client] implement support for KVMFR_FEATURE_SETCURSORPOS 2021-08-05 22:19:35 +10:00
Geoffrey McRae
95bbd67dea [common] add new KVMFR structs and fields for feature support 2021-08-05 22:19:11 +10:00
Geoffrey McRae
a3de0b2a59 [all] bump LGMP version again 2021-08-05 22:14:50 +10:00
Geoffrey McRae
719fec0a45 [client] don't terminate after successful upgrade of LGMP 2021-08-05 21:45:50 +10:00
Geoffrey McRae
2fa09dbd95 [client] allow the user to upgrade if the LGMP version doesn't match 2021-08-05 21:43:49 +10:00
Geoffrey McRae
3f3430de3f [all] update LGMP to obtain access to new client messaging feature 2021-08-05 21:43:49 +10:00
Netboy3
68eaf46d7c [doc] index: Modify Level1Techs URL to use a redirect 2021-08-05 12:40:17 +10:00
Quantum
65e550a61c [client] egl: pad buffer damage by 1px when rendering desktop
We want to add extra padding to deal with scaling, which may end up blending
with neighbouring pixels.
2021-08-05 08:05:30 +10:00
Quantum
4da0c64583 [host] nvfbc: make diff map size configurable
This commit adds a new host configuration option, nvfbc:diffRes, which
specifies the dimensions of every block in the diff map. This defaults to
128, meaning the default 128x128 block size.

Since block sizes other than 128x128 is not guaranteed to be supported by
NvFBC, the function NvFBCGetDiffMapBlockSize was introduced to query the
support and output the actual block size used.
2021-08-05 07:27:28 +10:00
Geoffrey McRae
0603a55492 [client] x11: prevent possible calibration underflow 2021-08-05 07:11:23 +10:00
Geoffrey McRae
a37b527bbd [client] main: make core overlays register before everything else
The display servers and renderers may want to register their own
overlays in the future, as such we need g_state.overlays to be
initialized to allow for this.
2021-08-05 07:01:28 +10:00
Geoffrey McRae
0af558345f [client] wayland: fix build with libdecor after invalidateWindow change 2021-08-05 06:58:43 +10:00
Geoffrey McRae
44f815409d [client] imgui: ensure the pointer shape is correct
Since we only update imgui's cursor location when the overlay is
enabled, if the last cursor position was showing a shape that is
incorrect when we re-enter the overlay the cursor will be wrong. This
corrects this by updating the location as we enter overlay mode.
2021-08-05 06:55:41 +10:00
Geoffrey McRae
6e7f39edee [client] app: allow selective full invalidation
Overlays only need to trigger a new frame if they have changed and not
full window invalidation, this change allows for this.
2021-08-05 06:47:36 +10:00
Geoffrey McRae
6c84c0eca6 [client] overlay: move keybinds and config into the overlays
This adds a new `earlyInit` call which allows the overlay to register
options before actually being intialized. Also the keybind handling and
state tracking for each overlay has been moved internal to the overlay
itself.
2021-08-05 06:40:06 +10:00
Geoffrey McRae
d90e658e3b [client] main: fix incorrectly hiding the cursor dot in captureOnly mode 2021-08-05 06:11:12 +10:00
Geoffrey McRae
85f3a71dd5 [client] overlay: remember and restore the grab and pointer state
When entering overlay mode if the cursor was previously grabbed we
should restore the state when exiting overlay mode. This will also
correct the pointer setting it to NONE or SQUARE depending on the prior
grab state.
2021-08-05 06:03:09 +10:00
Geoffrey McRae
38ddfc0b61 [client] core: never allow warp when the overlay is active 2021-08-05 05:48:59 +10:00
Geoffrey McRae
ff01a197f3 [client] overlay: don't use the internal function directly for config 2021-08-05 00:57:54 +10:00
Geoffrey McRae
6c44bbb53e [client] egl: use a ui switch for damage display instead of a keybind 2021-08-05 00:56:31 +10:00
Geoffrey McRae
f3f0157d3c [client] overlay: allow registration of runtime configuration options 2021-08-05 00:56:31 +10:00
Quantum
4e81c7f724 [host] windows: make copyright string in resource use © 2021-08-04 21:16:35 +10:00
Quantum
51b9cd4e5a [all] copyright: use unicode copyright sign ©
This is done for consistency with the license strings in appstrings.c.
2021-08-04 21:16:35 +10:00
Quantum
ecf59ac7d9 [common] appstrings: make script refresh copyright and license 2021-08-04 21:16:35 +10:00
Quantum
0941bd0fe5 [client] imgui: move cursor change to after rendering overlays
This allows it to take into the overlays into consideration when deciding
which cursor is to be shown.
2021-08-04 10:52:05 +10:00
Quantum
8ebaf92006 [client] main: rename frameTimings to uploadTimings 2021-08-04 10:38:30 +10:00
Quantum
62cd5e9c57 [client] main: add graph for time spent rendering
This is the time spent in the on_render function, not the time between
frames. Having this information helps diagnose slowdowns in renderers.
2021-08-04 10:38:30 +10:00
Geoffrey McRae
9192e2039a [client] imgui: another missed file
Clearly I need to get some sleep :)
2021-08-04 10:37:05 +10:00
Geoffrey McRae
1885e2093b [client] imgui: added missed files from the last commit 2021-08-04 10:29:48 +10:00
Geoffrey McRae
d2c36b8449 [client] overlay: add new configuration overlay [wip] 2021-08-04 10:27:47 +10:00
Geoffrey McRae
80c9f7223a [client] wayland: fix failure to build 2021-08-04 07:21:57 +10:00
Geoffrey McRae
c15d0dc672 [client] ds: waitFrame now returns a bool to force rendering if needed
X11 needs to calibrate to get the best possible latency, as such it
needs the scene to render so that the render time of the scene can be
accounted for in the delay calculation.
2021-08-04 06:49:35 +10:00
Geoffrey McRae
0f7fa32d12 [client] x11: move sleep and calibration code outside of the event loop
Sleeping in the x11 event loop is not ideal as it will introduce latency
when processing other events, instead do this in the waitFrame handler.
2021-08-04 06:34:27 +10:00
Geoffrey McRae
6933c278ce [client] x11: cosmetics 2021-08-04 06:14:27 +10:00
Geoffrey McRae
7fc717a839 [client] x11: don't setup XPresent if jitRender is not enabled 2021-08-04 06:12:21 +10:00
Geoffrey McRae
4e435e6199 [client] ds: tell the display server if jitRender is requested 2021-08-04 06:05:42 +10:00
Geoffrey McRae
366ec16a63 [client] config: use DEBUG_WARN, not fprintf for output 2021-08-04 05:55:34 +10:00
Geoffrey McRae
04c9694ffa [client] config: ensure mouseRedraw is on if jitRender is in use 2021-08-04 05:54:59 +10:00
Geoffrey McRae
f7682c289a [client] egl: always render the black bar areas
Failure to render these causes artifacts/ghosting when the overlays are
using this area, as such we need to always render this area of the
screen.
2021-08-03 22:29:04 +10:00
Geoffrey McRae
4b4a75475a [client] egl: fix out by one error with letterbox rendering
This replaces the scaled `destRect` with a version that uses doubles
correcting the rounding error that is causing a failure to properly
clear the black bar areas.
2021-08-03 22:27:46 +10:00
Quantum
55703b61b7 [client] egl: remove texture copy in DMABUF path
With more efficient rendering due to buffer age support, we no longer need
to copy the texture to avoid excessive dmabuf copies.
2021-08-03 21:37:31 +10:00
Quantum
8545d15c85 [client] egl: warn when EGL_EXT_buffer_age is not supported 2021-08-03 21:37:31 +10:00
Quantum
87aac8cf03 [client] egl: use buffer age extension to render only damaged parts
We avoid rendering any area that has not changed since the buffer was used
and also not covered by an overlay.
2021-08-03 21:37:31 +10:00
Quantum
f9977332a6 [client] egl: convert desktop to use desktop_rects 2021-08-03 21:37:31 +10:00
Quantum
2dca056526 [client] egl: refactor damage mesh generation into desktop_rects
This mesh will later be used to render only damaged portions of the desktop.
We also moved the coordinate transformation for damage overlay into a matrix
and computed by the shader.
2021-08-03 21:37:31 +10:00
Quantum
dd31a7ef93 [client] egl: clean up splash background shader 2021-08-03 21:12:46 +10:00
Quantum
a25c93b28e [client] wayland: document wlroots viewport handling workaround
Add a comment so we don't forget and remove the "useless" code.
2021-08-03 21:12:25 +10:00
arcnmx
c0aec7d8f4 [host] nvfbc: disable pointerThread when unused
fixes log spam that would occur when decoupleCursor=false
2021-08-03 21:05:39 +10:00
Geoffrey McRae
03ed8b7304 [client] x11: added code to calibrate a delay for presentation
XPresent doesn't give us the time before presentation, but the time just
after. This code calculates and calibrates a delay to sleep for before
signaling the wait event for render when using jitRender
2021-08-03 07:51:03 +10:00
Geoffrey McRae
504bf02855 [client] x11: provide an empty rect region to XPresentPixmap
Providing None informs the comppositor that there is full screen damage,
instead we provide an empty rectangle to prevent this behaviour.
2021-08-03 07:49:59 +10:00
Geoffrey McRae
4d9ab81ef4 [client] egl: assert the update provdided is a dmabuf 2021-08-03 04:03:37 +10:00
Geoffrey McRae
f3413815a9 [client] egl: re-implement DMABUF (untested) 2021-08-03 03:59:03 +10:00
Netboy3
bae19cb130 [doc] install: Add Overlay Mode section 2021-08-03 01:12:35 +10:00
Netboy3
db501f689f [doc] install: Add $XDG_CONFIG_HOME/looking-glass/client.ini
* Add $XDG_CONFIG_HOME/looking-glass/client.ini to the list of
  config files
* Add a paragraph explaining config load and merge order
2021-08-03 01:12:35 +10:00
Netboy3
b8561cab0a [doc] install: Add win:jitRender command line option 2021-08-03 01:12:35 +10:00
Geoffrey McRae
50f9baedba [client] x11: remove junk code from evaluation 2021-08-03 01:11:46 +10:00
Quantum
5d5e4ede1a [client] egl: use new EGL damage count semantics
After the damage queue PR, EGL damage count 0 means no change, and -1 means
invalidate the entire window. However, several other places have different
semantics, and we are not handling them correctly:

1. KVMFR uses 0 to signal invalidating the entire frame, so if we receive 0
   rectangles in egl_on_frame, we should set damage count to -1.
2. The damage overlay treated 0 as full damage, which is now incorrect. This
   is fixed, and now it treats 0 as no update, and -1 as full damage.
2021-08-03 00:57:32 +10:00
Quantum
8d78a5aa95 [commit] wayland: invalidate window on scale changes
This makes it not require new frames for new scale to be properly applied.
2021-08-03 00:57:04 +10:00
Geoffrey McRae
14839dc54e [client] egl: there should only ever be a single sync object 2021-08-03 00:47:59 +10:00
Geoffrey McRae
7912d268e9 [doc] add libxpresent-dev as a new build dep for the client 2021-08-03 00:04:05 +10:00
Geoffrey McRae
8907a990a1 [github] add libxpresent-dev as a new build dep 2021-08-03 00:03:44 +10:00
Geoffrey McRae
891ee3e789 [client] x11: make use of the x11 present extension for jitRender
This implementation is a bit dodgy and needs some work but is currently
functional. Consider this feature highly experiemental under X11.
2021-08-02 23:59:26 +10:00
Geoffrey McRae
037788f562 [client] egl: do not set ops, this is done in texture.c 2021-08-02 23:42:46 +10:00
Geoffrey McRae
13d9c84dc9 [client] egl: replace monolithic EGLTexture with modular version
The way things were handled in EGLTexture is not only very hard to
follow, but broken. This change set breaks up EGLTexture into a modular
design making it easier to implement the various versions.

Note that DMABUF is currently broken and needs to be re-implemented.
2021-08-02 23:37:33 +10:00
Geoffrey McRae
e23144aecd [client] overlay: add new needs_render for realtime overlays 2021-08-01 21:13:59 +10:00
Quantum
23c77e8508 [client] egl: use a lock for desktop damage to eliminate all races
There used to be a possible race when a bunch of rectangle is appended, but
the total count is not updated before it's read. Using a lock eliminates
all such races.
2021-08-01 19:54:56 +10:00
Quantum
f08e2ece93 [client] wayland: implement stopWaitFrame 2021-08-01 19:54:28 +10:00
Quantum
f64310320a [client] ds: add stopWaitFrame to terminate waitFrame early
This is used on exit to unblock the render thread.
2021-08-01 19:54:28 +10:00
Geoffrey McRae
695b7b793c [client] main: allow 'catchup' when using jitRender 2021-08-01 19:32:01 +10:00
Quantum
986f92d0db [client] main: use skipFrame and signal frameEvent
We need to signal frameEvent to render the first frame. Otherwise, wayland
fails to configure the window, as configuration happens in EGL swap.
2021-08-01 18:54:55 +10:00
Quantum
2e4614cbc4 [client] wayland: make waitFrame work when not rendering
Implement skipFrame and do various things to wake waitFrame.
2021-08-01 18:54:55 +10:00
Quantum
16aa04d539 [client] ds: add skipFrame method to interface
If this exists, it should be called when waitFrame returns but we don't
wish to render.
2021-08-01 18:54:55 +10:00
Geoffrey McRae
9d95154b85 [client] main: make use of new needs_render call 2021-08-01 18:44:35 +10:00
Geoffrey McRae
f8e1ab8f31 [client] renderers: add new needs_render method to the interface
With jitRender the renderer needs to tell the main application if it
needs to be rendererd, such as during the initial splash screen fade
out.
2021-08-01 18:18:08 +10:00
Geoffrey McRae
2d74c93232 [client] main: alerts and overlay toggles should invalidate the window 2021-08-01 18:04:43 +10:00
Geoffrey McRae
17687fdea3 [client] main: only render if there is actually something to render 2021-08-01 17:39:52 +10:00
Quantum
77b3d45e0e [client] ds: change signalNextFrame to waitFrame 2021-08-01 17:29:15 +10:00
Quantum
37196f1f0e [client] config: disable JIT rendering by default
Without configuring Wayland compositors to send frame callbacks as late as
possible, JIT rendering can increase latency by more than one frame.

For example, by default, sway asks applications to render right after a
vblank, and does its own composition right after a vblank, resulting in
~2 frame's worth of latency. If max_render_time is set on the output,
it composes that many milliseconds before the vblank, losing ~1 frame's
worth of latency. If max_render_time is set on the window also, the frame
callback is sent that many milliseconds before composition, and we achieve
perfectly low latency.

Therefore, out of the box, JIT rendering should not be enabled, as manual
compositor configuration is required for optimal results.

For reference, the following sway settings results in the best latency:

    output <insert output name> max_render_time 1
    for_window [app_id="looking-glass-client"] max_render_time 1

This reverts commit 3baed05728.
2021-08-01 17:29:00 +10:00
Quantum
b3b71d6f02 [client] egl: fix cursor handling when invalidating
If we invalidate the window, we used to not update this->cursorLast, and
this causes us to lose track of the cursor. Now we update this->cursorLast
unconditionally, and this fixes the issue.
2021-08-01 17:28:40 +10:00
Geoffrey McRae
90b90e667a [common] event: lgResetEvent should return the last state of the event 2021-08-01 17:14:58 +10:00
Geoffrey McRae
a094fb8104 [common] events/linux: fix failure to call pthread_cond_broadcast 2021-08-01 17:13:31 +10:00
Quantum
96bcfff28b [client] wayland: use zxdg_output_manager_v1 version 3 if possible
Version 3 does not send xdg_output.done events, instead guaranteeing that
all xdg_output.* events are sent before wl_output.done. This saves us from
doing the work twice.
2021-08-01 13:05:41 +10:00
Quantum
0ad469178a [client] wayland: make it possible to disable fractional scaling
The method used is not guaranteed to work on all Wayland compositors,
so offer a way out. We need to support it anyways in case xdg_output
or wp_viewporter protocols are not available.
2021-08-01 13:05:41 +10:00
Quantum
b3ca872cef [client] wayland: improve fractional scale handling
Currently, we scale the desktop up to the next largest integer, and rely on
the wayland compositor to scale it back down to the correct size.
This is obviously undesirable.

In this commit, we attempt to detect the actual fractional scaling by finding
the current active mode in wl_output, and dividing it by the logical screen
size reported by xdg_output, taking into consideration screen rotation.

We then use wp_viewporter to set the exact buffer and viewport sizes if
fractional scaling is needed.
2021-08-01 13:05:41 +10:00
Quantum
3baed05728 [client] config: enable JIT rendering by default 2021-08-01 12:38:17 +10:00
Quantum
aed370c7ce [client] main: implement just-in-time render mode
When requested, JIT render mode will be used if the display server supports it.
Otherwise, a warning is generated instead.

This essentially uses the signalNextFrame logic for imgui, but for everything.
We automatically enable this mode when overlay is on.

Currently, this exposes some damage tracking bugs in the EGL renderer.
2021-08-01 12:24:19 +10:00
Quantum
b9a7ce17fe [client] egl: use queue of damages
This prevents damage from being overwritten when frames are received
faster than could be rendered.

This implementation cycles between two queues, removing all need for
memory allocation.
2021-08-01 12:11:54 +10:00
Netboy3
2e0f765190 [doc] install: Update overlay mode keybinding 2021-08-01 12:11:02 +10:00
Geoffrey McRae
66df99f5fd [client] imgui: don't free the IniFilename until imgui destruction 2021-08-01 00:55:36 +10:00
Quantum
7c3c68b84b [client] imgui: put imgui.ini in $XDG_CONFIG_DIR/looking-glass 2021-07-31 21:19:26 +10:00
Quantum
6109067275 [client] config: load config file based on new lgConfigDir() 2021-07-31 21:11:09 +10:00
Quantum
3369536cb8 [common] paths: add library to manage platform-specific paths
This abstracts away stuff like XDG base directory specification.
Currently, this is implemented for Linux only.
2021-07-31 21:11:09 +10:00
Geoffrey McRae
fcbd255e99 [client] app: allow overlay exit with KEY_ESC 2021-07-31 20:51:38 +10:00
Geoffrey McRae
f49948506b [client] move imgui input state reset to it's own core function 2021-07-31 20:51:08 +10:00
Geoffrey McRae
a11a20411b [client] overlay: disable the imgui demo window 2021-07-31 20:32:06 +10:00
Geoffrey McRae
5e2f1b3fac [client] overlay/help: do not always show when in overlay mode 2021-07-31 20:30:52 +10:00
Geoffrey McRae
75a14b8b45 [client] overlay/graphs: allow the window position and size to be saved
This change allows the window position and size to be changed, and
saved/loaded by imgui. Additionally the plots will now scale to the
window size.
2021-07-31 20:21:34 +10:00
Geoffrey McRae
b0c1714777 [client] overlay/fps: allow the window posisiton to be saved 2021-07-31 20:21:17 +10:00
Geoffrey McRae
361ead59d3 [client] overlay/help: use the interactive argument 2021-07-31 19:14:58 +10:00
Geoffrey McRae
41c5688fca [client] overlay: let the overlay know if it's in interactive mode
This also removes the need for the flags member as the overlay can just
opt to not render if it's not in interactive mode.
2021-07-31 19:11:40 +10:00
Quantum
c8dc037e94 [client] imgui: use signalNextFrame if possible
This allows imgui to be rendered at screen refresh rate for supported display
server backends.
2021-07-31 19:05:21 +10:00
Quantum
a213ee960a [client] wayland: implement signalNextFrame with frame callbacks 2021-07-31 19:05:21 +10:00
Quantum
3043296e52 [client] ds: add optional method signalNextFrame(LGEvent)
This method takes an LGEvent and signals it when the next frame should be
rendered in time for the next vblank.

We will be using this to render imgui at screen refresh rate, but this could
potentially be used later to implement a better form of vsync for supported
display servers.

This must be invoked before swapping buffers.
2021-07-31 19:05:21 +10:00
Geoffrey McRae
be9a16e8a2 [git] add libxcursor-dev to the git workflow 2021-07-31 18:26:29 +10:00
Geoffrey McRae
5e2dd589a1 [client] x11: implement cursor support for the imgui overlay
This commit adds libxcursor to the dependencies for X11.
2021-07-31 18:23:05 +10:00
Geoffrey McRae
9959578cbe [client] app: prevent buttons/keys from being held when focus is lost 2021-07-31 16:40:14 +10:00
Geoffrey McRae
971e91238a [client] main: bump the minimum fps to 60fps when showing the overlay 2021-07-31 16:19:55 +10:00
Geoffrey McRae
3143dc1e84 [client] x11: call app_handleWheelMotion for imgui scroll interaction 2021-07-31 16:12:52 +10:00
Geoffrey McRae
8898496eba [client] imgui: respect WantCaptureKeyboard
If the overlay is active and imgui has set WantCaptureKeyboard we should
ignore the escape key sequences.
2021-07-31 15:55:58 +10:00
Geoffrey McRae
7eb00bd24c [client] app: update imgui key release when guest input is disabled 2021-07-31 15:41:21 +10:00
Geoffrey McRae
a098bab114 [client] overlay: allow the fps and graph windows to be moved 2021-07-31 15:35:53 +10:00
Geoffrey McRae
7f6fd02d06 [client] x11: Add event handling for ungrabed mouse press/release
The imgui overlay requires input even if the display is not captured and
operating in raw mode. XInput2 correctly only sends
XI_Press/ReleaseButton events if the device has not been captured, as
such it's safe to handle both raw and non raw buttons events at the same
time.
2021-07-31 15:23:31 +10:00
Quantum
75e57baf6c [client] config: optionally load config from XDG_CONFIG_HOME
We look for the client config in $XDG_CONFIG_HOME/looking-glass/client.ini.
This is done because it's more conventional, and also allows us to add
additional configuration files, e.g. for the host.

We fallback to $HOME/.config as is standard, and then as a last resort use
getpwuid(getuid())->pw_dir. This is also recommended by the getpwuid manpage:

> An application that wants to determine its user's home directory should
> inspect the value of HOME (rather than the value getpwuid(getuid())->pw_dir)
> since this allows the user to modify their notion of "the home directory"
> during a login session.
2021-07-31 15:06:59 +10:00
Quantum
68d8d95266 [client] config: do not attempt to load non-files as config
Currently, we load /etc/looking-glass-client.ini and/or
~/.config/looking-glass-client.ini as long as they exist, even if they are
not files. We should only load them if they are files.
2021-07-31 15:06:37 +10:00
Quantum
7d78cba38c [client] client: release escape key on focus loss
This prevents the escape key from being treated as held down indefinitely when
losing focus while holding the escape key.
2021-07-31 15:02:57 +10:00
Quantum
7801575d99 [host] nvfbc: log error codes for various errors
This makes troubleshooting easier.

We also switch to use %d instead of 0x%x because all error codes in nvfbc.h
are negative decimal numbers.
2021-07-31 15:02:39 +10:00
Quantum
1104bd821b [common] option: make options case insensitive
It is not obvious that [DXGI] and [dxgi] are different sections in the ini file,
and only the latter works. We should just eliminate the difference.
2021-07-31 15:00:49 +10:00
Quantum
b8b70e772e [client] config: print error when app:configFile is not a valid file
This makes it clear to the user that app:configFile is at fault.
2021-07-31 14:59:00 +10:00
Quantum
64c906b801 [client] main: prevent the user from launching looking glass as setuid
We don't want to encourage craziness of people making the client suid to
bypass permission issues on the shm file.

Note: I see no evidence of this happening in the wild, but let's be
proactive.
2021-07-31 14:58:48 +10:00
Quantum
72ccd44681 [client] wayland: pass mouse wheel motion events to imgui 2021-07-31 14:58:35 +10:00
Quantum
7ca5e14938 [client] imgui: implement mouse wheel motion support
The display server should call app_handleWheelMotion as necessary.
2021-07-31 14:58:35 +10:00
Quantum
6b6b3b724a [client] wayland: reject horizontal scroll events
Currently, we handle horizontal scroll events as if they are vertical scrolls.
This is not correct and we should instead reject them.
2021-07-31 14:57:56 +10:00
Quantum
86b50cc8ab [client] imgui: initialize keymap
This allows ImGui to understand some of the basic key presses.

Also moved the include guard from kb.c to kb.h where it actually makes sense.
2021-07-31 14:57:41 +10:00
Quantum
10a27e7a27 [client] config: automatically update embedded copyright string
The refresh-copyright script now automatically updates the copyright string
embedded in config.c. In order to achieve this, refresh-copyright gained the
ability to reflow text as the situation needs.
2021-07-31 14:57:24 +10:00
Quantum
edabd1bae7 [client] imgui: remove no longer used overlay_utils.cpp 2021-07-31 14:56:49 +10:00
Quantum
f1b1da60ea [client] imgui: improve method for high DPI
We now give ImGui the true logical size of the window and tell it to scale
the framebuffer. To fix the blurry fonts, we continue to load fonts at the
scale necessary for the DPI and use FontGlobalScale to shrink the fonts back
to the logical size. The font rectangle is then expanded by the framebuffer
scaling, resulting in good text rendering.

This method has the advantage of not messing up the sizes of resizable
overlays when moving across monitors.
2021-07-31 14:56:49 +10:00
Quantum
0402dd521a [client] imgui: ask display server to display cursors 2021-07-31 14:56:04 +10:00
Quantum
11a5864969 [client] wayland: implement support for other cursors 2021-07-31 14:56:04 +10:00
Quantum
da28db2ca4 [client] ds: change showPointer to setPointer for more cursors
Currently everything only supports LG_POINTER_NONE and LG_POINTER_SQUARE.
2021-07-31 14:56:04 +10:00
Quantum
c991de7ccd [client] imgui: improved overlay input handling
1. Overlay always releases confines.
2. Overlay turns off mouse sync with guest.
2021-07-31 14:54:53 +10:00
Netboy3
d9a3b6523c [doc] install: Update client keybindings and command line options 2021-07-31 14:54:40 +10:00
Quantum
ea2651e39b [client] imgui: darken background when rendering overlays
This replaces the alert that says whether the overlay is enabled or not.

Also, we now always display the help menu in overlay mode.
2021-07-29 19:10:15 +10:00
Quantum
a980cd9406 [client] egl: log when EGL_EXT_image_dma_buf_import is unavailable 2021-07-29 17:03:44 +10:00
Quantum
1c58b3a087 [client] imgui: implement keyboard input forwarding and capture 2021-07-29 17:02:59 +10:00
Quantum
065d90c3f7 [client] imgui: add mode to forward mouse input to imgui 2021-07-29 17:02:59 +10:00
Quantum
6c64965703 [client] egl: make functions that do not need linking static 2021-07-29 16:54:56 +10:00
Quantum
134829cbf2 [client] imgui: make graph y-axis configurable
The default of [0, 50] makes sense for FPS/UPS graphs, but does not for
things like the import graph. The latter should not take more than 5 ms
for sure.

This commit allows the min/max y-axis value to be specified when registering
the graph.
2021-07-29 15:54:15 +10:00
Quantum
aff3bff8b0 [common] framebuffer: fix copy when source and dest pitch differs
We used to increment the source buffer index by width * bpp, not by pitch.
This is incorrect and the root cause behind #670.

This is a regression that appeared in 196050bd23.
2021-07-29 10:18:35 +10:00
Quantum
2ea84cd07e [common] ivshmem: use consistent device numbering on Windows
We now enumerate all IVSHMEM devices, sort them based on PCI bus, slot,
and function numbers, then index from the resulting order. This should
be consistent across boots.

To help the user identify the correct IVSHMEM device, we also print the
list of IVSHMEM devices on startup.
2021-07-29 10:17:33 +10:00
Quantum
996b9e7e7b [common] time: fully implement all functionality for windows
We implement nanotime by converting QueryPerformanceTimer output with
floating point arithmetic. This is necessary to preserve precision on
platforms where each tick is not an integer number of nanoseconds.

Furthermore, struct timespec is included C11 and appears to be supported
on Windows, so we no longer need to #ifdef it out.
2021-07-29 10:16:52 +10:00
Quantum
009ae02e32 [client] egl: add graph tracking time taken to import frame
This tracks the time taken to load the frame buffer into an OpenGL texture.
2021-07-29 10:08:52 +10:00
arcnmx
552a37122a [host] app: add throttleFPS option 2021-07-28 02:47:24 +10:00
Quantum
6ed1f4662d [client] imgui: report failed font atlas building
Also removed the inefficient code to build the default atlas.
2021-07-26 17:08:02 +10:00
Geoffrey McRae
5f5f497cbd [host/common] windows: provide delayExecution via nsleep
This change moves this platform specific sleep to `common` as the OS
agnostic `nsleep` function.

Ref PR #661
2021-07-26 16:36:56 +10:00
Quantum
120fe63c0f [client] egl: keep x/y coordinates of cursor rectangle non-negative
This shifts the rectangles upwards instead of cutting off the rectangles,
but it keeps the code simple.
2021-07-26 07:56:46 +10:00
Quantum
181b165a4b [client] egl: generate correct cursor damage with cursor rotation 2021-07-25 17:35:29 +10:00
Geoffrey McRae
dafd7e7b42 [host] app: when running in sync mode send frame repeats as needed 2021-07-25 17:10:51 +10:00
Quantum
d9cdc8d26c [client] egl: rotate damage rectangles according to client side rotation 2021-07-25 16:56:48 +10:00
Quantum
d0722349e6 [client] egl: make damage overlay support rendering rotated rectangles 2021-07-25 16:56:48 +10:00
Geoffrey McRae
2ef80a5d34 [client] overlay: cosmetic changes to the overlay display 2021-07-25 16:49:25 +10:00
Quantum
e7761abf3c [client] wayland: invalidate window on configure
This avoids sending potentially meaningless damage values after a surface
configuration event.
2021-07-25 15:36:44 +10:00
Geoffrey McRae
3905834807 [client] x11: call app_invalidateWindow on expose 2021-07-25 15:30:56 +10:00
Geoffrey McRae
60a58d4d8d [client] all: make it possible to signal full window invalidation
Now that we are drawing with damage rects, when the window is hidden and
then exposed the window may not get fully redrawn. This provides
`app_invalidateWindow` for the display server backend to call when the
screen needs a full redraw.
2021-07-25 15:29:29 +10:00
Geoffrey McRae
8c2a77e84e [client] don't skip the first frame if we do not yet have a frame
A frame serial of `0` is valid and will happen if either the host app
has just started, or the serial has overflowed.
2021-07-25 15:10:40 +10:00
Geoffrey McRae
e35facbb15 [spice] bump PureSpice version 2021-07-25 15:10:40 +10:00
Quantum
8528969efd [host] nvfbc: clamp damage rectangles to screen size 2021-07-25 14:34:10 +10:00
Geoffrey McRae
9dffde74b2 [client] main: skip duplicate frames
When a new client connects to our session the host will repeat the last
valid frame for the new client. This change will detect this and skip
the duplicated frame.
2021-07-25 13:46:48 +10:00
Geoffrey McRae
b39f38350f [common] kvmfr: add a new frameSerial field to the KVMFRFrame struct
This new field is used so that when a new client connects an already
connected client can identify any repeated frame that is sent and ignore
it.
2021-07-25 13:46:48 +10:00
Quantum
f4daa9f527 [all] cmake: fix OptimizeForNative
1. Correctly detect ON state so that host is not built as -march=native by
   default.
2. Merge OFF and GENERIC options as we need at least -march=nehalem to build
   properly.
2021-07-25 13:46:13 +10:00
arcnmx
2c745db544 [host] windows: use CMAKE_DLLTOOL if available
cmake automatically finds dlltool as of version 3.16
2021-07-24 14:52:32 +10:00
arcnmx
3b37898eb2 [all] use cmake FindPkgConfig IMPORTED_TARGETs 2021-07-24 12:35:48 +10:00
arcnmx
aa2ea05af9 [client] removed unused GMP dependency 2021-07-24 12:35:48 +10:00
arcnmx
7316c1c46c [all] include OPTIMIZE_FOR_NATIVE in feature summary 2021-07-24 12:35:17 +10:00
arcnmx
38cb348201 [all] make OPTIMIZE_FOR_NATIVE a tristate option 2021-07-24 12:35:17 +10:00
arcnmx
be664c49c8 [all] cmake: use -march=x86-64-v2 when it becomes available
Moves the logic in 0525515 to a common cmake include, and applies it to
the other binaries.
2021-07-24 12:35:17 +10:00
Quantum
f09738678e [client] fonts: remove font rendering machinery 2021-07-23 20:18:12 +10:00
Quantum
0c35d9b057 [client] opengl: remove font management 2021-07-23 20:18:12 +10:00
Quantum
cb9774bbd2 [client] egl: remove font management 2021-07-23 20:18:12 +10:00
Quantum
dd0edc1394 [client] renderers: remove alert handling 2021-07-23 20:18:12 +10:00
Quantum
be44249c05 [client] imgui: converted alerts to use imgui 2021-07-23 20:18:12 +10:00
Quantum
efb5019176 [client] overlay/help: switch to using ImGui's table facility 2021-07-23 18:04:05 +10:00
Quantum
5153d35bb5 [client] renderer: remove on_help from renderer interface 2021-07-23 18:04:05 +10:00
Quantum
036f16b9ef [client] imgui: convert help overlay to use imgui 2021-07-23 18:04:05 +10:00
Quantum
436986d182 [client] imgui: make UI font and size configurable 2021-07-23 16:07:42 +10:00
Quantum
5d053128ac [client] imgui: use improved high DPI rendering
This actually makes imgui render at a higher resolution, avoiding scaling
and resulting blurriness.
2021-07-23 16:07:42 +10:00
Quantum
b5c5ecc074 [client] imgui: support high DPI by scaling framebuffer
This allows overlays to render at correct positions on high DPI displays.
2021-07-23 16:07:42 +10:00
Quantum
56308fcbd1 [client] overlay: use utility function to get ImGui rectangle 2021-07-23 15:54:18 +10:00
Quantum
628bdab21b [client] overlay: remove space checking
Every overlay is now guaranteed to be able to write MAX_OVERLAY_RECTS rects,
and running out of space is now an error.
2021-07-23 15:54:18 +10:00
Quantum
df0397b10b [client] imgui: track last rectangles for overlays
This is necessary in case overlays change size. When this happens, we must
damage the larger of the overlays' rectangles this frame and last frame.
This erases the overlay from where it is no longer appears.

In order to do this, we must keep track of the rectangles for every overlay
with no exception. We cannot short-circuit the generation of rectangles if
we run out of buffer space, and we must allocate space for MAX_OVERLAY_RECTS
rectangles for every frame. Otherwise, we will not know where to erase the
overlay if it disappears.
2021-07-23 15:54:18 +10:00
Quantum
334bfeecea [client] egl: correctly handle mixing imgui and non-imgui overlays
This allows the full frame to be damaged when both types are visible.
2021-07-23 15:53:59 +10:00
Quantum
3cf0257f34 [host] windows: do not complain about failed timer destruction at exit
When our window is destroyed, our timers are also destroyed. This causes our
attempt at destruction to fail. Instead, set MessageHWND to NULL in the
WM_DESTROY handler and don't try destroying the timers if the window is gone.
2021-07-23 15:52:45 +10:00
Quantum
6382fc11af [client] ci: run all build combinations to completion
This is desirable because certain uncommon configurations like libdecor or
clang may break, and this shouldn't stop us from seeing if unrelated changes
pass.
2021-07-23 15:52:22 +10:00
Quantum
b26067b0a0 [client] wayland: use new libdecor pkgconfig package name 2021-07-23 15:52:05 +10:00
Quantum
947eac52f6 [client] renderers: treat -1 as full damage and 0 as no overlay
This makes everything consistent.
2021-07-22 18:57:34 +10:00
Quantum
4c60409aaf [host] windows: use WM_CLOSE to signal window destruction
DestroyWindow can only be invoked on the thread that created the window.
All other threads must use WM_CLOSE or another message to signal tell the
window to destroy itself.
2021-07-22 18:55:11 +10:00
Geoffrey McRae
eb5c588af9 [client] overlay: call igEnd before return 2021-07-22 18:38:32 +10:00
Geoffrey McRae
3b6ad957e3 [client] overlay: increment totalRects 2021-07-22 18:38:00 +10:00
Geoffrey McRae
4acbf2e9a0 [client] overlay: rework the interface to avoid possible race conditions 2021-07-22 18:33:50 +10:00
Geoffrey McRae
50f7a1a99c [client] overlay: properly free the graphs before ll free 2021-07-22 18:32:28 +10:00
Quantum
515f08d2da [client] egl: transform overlay damage coordinates
EGL uses bottom-up y-coordinate while we use top-down.
2021-07-22 18:32:18 +10:00
Quantum
58ab77d237 [client] egl: avoid overflowing damage buffer
We allocate 10 rectangles for overlay damage as agreed on Discord.
2021-07-22 18:32:18 +10:00
Geoffrey McRae
fdbdf6f167 [client] app: implement new overlay rendering framework
This change set implements a framework for overlays to be registered
that make use of ImGui. See `overlay/fps` for a simple implementation
example.
2021-07-22 17:27:30 +10:00
Geoffrey McRae
30c4a4786b [client] overlay: cosmetic, fix typo 2021-07-22 14:50:10 +10:00
Geoffrey McRae
a34d3bbab4 [client] overlay: windowRects is not an array of pointers 2021-07-22 14:48:08 +10:00
Geoffrey McRae
2310920e79 [client] overlay: added new interface for overlay windows using ImGui 2021-07-22 14:42:54 +10:00
Quantum
85f34602f4 [client] wayland: do not leak wp_presentation_feedback objects 2021-07-22 13:12:47 +10:00
Quantum
4b016b441c [client] ci: test libdecor build as part of matrix
We source libdecor packages from a PPA.
2021-07-22 13:12:28 +10:00
Quantum
27e3be3778 [client] egl: free desktop damage after rendering 2021-07-22 13:12:07 +10:00
Quantum
4954687a52 [client] ci: remove SDL dependencies and -DENABLE_SDL=ON 2021-07-22 12:04:29 +10:00
Quantum
bb60107a3b [client] wayland: dispatch events through libdecor when using it
Currently, we dispatch the events on the wayland display server ourselves.
This is fine when using the cairo backend of libdecor, as it does the same
thign we do, but other backends may require other things to be dispatched.

This commit lets libdecor dispatch events instead through libdecor_get_fd
and libdecor_dispatch, which should hopefully makes things less sketchy.
2021-07-22 12:03:55 +10:00
Quantum
ed18ead1ff [client] ci: fix clang build
Our code to generate object files from shaders with the linker seems to
depend on GNU ld. More accurately, it works with lld, but we must specify
the correct object format via -m, which is very difficult to detect in a
satisfactory manner. Therefore we simply force use of GNU ld.
2021-07-22 11:38:20 +10:00
Quantum
e58506f1a5 [all] copyright: refresh copyright notice for .nsi and .rc files 2021-07-21 18:19:46 +10:00
Geoffrey McRae
08293c8721 [egl] damage: only update the damage vbo if there was a new frame 2021-07-21 17:29:46 +10:00
Geoffrey McRae
6389a06903 [client] main: let the renderer know if it's rendering a whole new frame
While the renderer can internally track this it would be better to
simply provide this information to the renderer directly so it can make
better decisions on how best to update the screen.
2021-07-21 17:26:48 +10:00
Geoffrey McRae
8cf444ef31 [client] main: sync to the ups if the ups exceeds minFrameTime 2021-07-21 16:56:49 +10:00
Geoffrey McRae
1c8af28f26 Revert "[client] main: increase the ups/fps update rate to once per 100ms"
This reverts commit b877bab48f.
There is no need for this anymore after the prior commit which removed
this faulty implementation
2021-07-21 16:50:49 +10:00
Geoffrey McRae
9b472d62a9 [client] main: remove/fix faulty upsTime code
The implementation was flawed, instead we just update the presentation
time if there was not an early wakeup.
2021-07-21 16:47:37 +10:00
Quantum
f5dfc264ba [host] windows: trick MinGW into not using memcpy from ntdll
MinGW seems to decide at random whether it wants to use memcpy from
mscvrt.dll or ntdll.dll. Currently, on Debian buster, ntdll.dll is chosen,
while on sid, mscvrt.dll is chosen.

This commit declares a new .def file for ntdll containing only the
functions we want to link from ntdll.dll, and generates ntdll.a from it
with dlltool. This way, MinGW will never be tempted to link functions
like memcpy from ntdll.dll.
2021-07-21 15:45:59 +10:00
Quantum
8a70efafb5 [host] windows: use system thread pool to wait for exit event
This is much less annoying than creating our own thread just to wait on
a single event, then creating another event to signal that thread to exit.
2021-07-21 14:13:31 +10:00
Quantum
b1c26aaa95 [host] windows: log MsgWaitForMultipleObjects errors
This function is sometimes flaky and may fail for no apparent reason,
see https://stackoverflow.com/q/3945003. This has also been experienced
during the development of #610.

This commit adds logging so we may see if it ever fails for no reason
and work out some way to fix it.
2021-07-21 12:33:36 +10:00
Quantum
1a88996c47 [client] opengl: don't include <GL/glx.h>
The OpenGL renderer backend should not depend on any particular
implementation of OpenGL, as it's used by both X11 and Wayland.
2021-07-21 12:33:01 +10:00
Quantum
3400c2c141 [host] windows: add version resource
This allows friendly names and versions to be displayed in task manager
and other applications.
2021-07-21 12:32:41 +10:00
Quantum
80bc9604ba [host] windows: fix graceful exit
We were using an auto-reset event to signal the mousehook exit. This was
fine when there was only one thread, but with the addition of the update
thread, only one thread is signaled, causing the wait to last forever.

The fix is switching to a manual reset event and call ResetEvent after
the threads have exited.
2021-07-21 12:32:14 +10:00
Quantum
669148bca0 [host] service: respond to exit request while sleeping
Instead of Sleep, we can use WaitForSingleObject with a timeout to be
signalled when the service is exiting.
2021-07-21 12:30:27 +10:00
Quantum
26df3579a3 [host] windows/delay: cast to LONGLONG instead of int
The type of the QuadPart member of the LARGE_INTEGER union is actually
LONGLONG, so we should cast to LONGLONG instead of int.
This avoids truncation should (ms * 10000.0f) exceed 2^31-1.
2021-07-21 12:29:29 +10:00
Geoffrey McRae
3c0ebd54ec [client] main: improve frame rate sync lock with the guest
If the guest is not sending frames at a constant rate, the minimum FPS
timeout may expire drawing an additional frame. This change calculates
the average ups frame time over the past 100ms and adds this to the
timeout value allowing this value to be dynamic.
2021-07-20 15:41:56 +10:00
Geoffrey McRae
b877bab48f [client] main: increase the ups/fps update rate to once per 100ms
This is so that we can use the ups information in the following commit.
2021-07-20 15:40:57 +10:00
Geoffrey McRae
e6e07e8f3f [client] main: use the monotonic clock to calculate the ups/fps
The accumulated time is not the best way to do this as the timer
function callback may not be exactly every 1000ms, by using the
monotonic clock we will get more accurate results.
2021-07-20 15:40:51 +10:00
Quantum
3f7261d7d9 [client] wayland: do not crash when presentation time is unavailable
We don't need the presentation time protocol, so fail gracefully when
it's unavailable.
2021-07-20 14:23:08 +10:00
Quantum
26f16a3734 [common] windows: declare WINVER and _WIN32_WINNT
This is done for consistency with the Windows-specific portions of the host.
2021-07-20 12:45:29 +10:00
Quantum
88fc1a6d24 [host] windows: directly invoke ChangeWindowMessageFilterEx
This requires bumping the minimum Windows version to Windows 7, but I
don't think we care about Vista anyways.
2021-07-20 12:16:49 +10:00
Quantum
2fc1d3cae6 [host] windows: fix resource compilation dependencies
It used to be the case that when updating app.manifest, the resource file
it not automatically rebuilt. This made it a headache to update the manifest.
We set OBJECT_DEPENDS so that cmake knows to make the res file depend on
app.manifest and icon.ico.

This commit is based on PR #579 and should be rebased on it after it's merged.
2021-07-20 12:14:39 +10:00
Quantum
28a67cad0d [host] windows: avoid compiling .rc file twice
a4f5ce08b9 has a regression that caused
the .rc file to be compiled twice. We do not want the version that's
added into the .a file.
2021-07-20 12:14:04 +10:00
Quantum
2bb0602ebb [common] windebug: remove custom-rolled Windows 8 detector
Just use <versionhelpers.h> from Windows SDK.
2021-07-20 11:34:57 +10:00
Quantum
d82333519c [host] dxgi: use SDK versionhelpers.h to test for Windows version
Also, changed logic so that Windows versions before 8 is not treated as 10.
2021-07-20 11:34:57 +10:00
Quantum
5421bd8b1d [host] windows: declare Windows 10 support in manifest
This allows IsWindows10OrGreater from <versionhelpers.h> to work.
2021-07-20 11:34:57 +10:00
Quantum
f0c7e9bdfa [host] windows: declare high DPI support in manifest
This is the normal way of doing things, and it's easier and less sketchy
than GetProcAddress.
2021-07-20 11:34:45 +10:00
Quantum
0525515bee [host] cmake: use -march=x86-64-v2 when it becomes available
GCC 11 will support x86_64 micro-architecture feature levels.
What we really want to support is nehalem or newer, which is x86-64-v2,
and specifying this instead of nehalem means that we are not tuning for
nehalem specifically.
2021-07-20 11:33:23 +10:00
Quantum
f5ad14b109 [host] windows: directly link D3DKMTSetProcessSchedulingPriorityClass
This function is available since Windows Vista and can therefore be used
directly without going through GetProcAddress. Unfortunately, MinGW does
not have d3dkmthk.h, but we can declare the prototype ourselves and link
against gdi32.dll.
2021-07-20 11:32:41 +10:00
Quantum
323d321a77 [host] windows: correctly declare WINVER and _WIN32_WINNT
We wanted to target Windows Vista, which is 0x0600 not 0x6000.
0x6000 is in fact larger than Windows 10 which is 0x0a00.
2021-07-20 11:32:15 +10:00
Quantum
56833edae7 [host] delay: directly link against ntdll.dll
There is no need to LoadLibrary and GetProcAddress to get pointers to
NtDelayExecution or NtSetTimerResolution. These functions don't have
prototypes in any SDK header, but they are exported in ntdll.dll and
we can simply declare the prototype and link ntdll.

There is also no chance that the functions do not exist: I checked an
old install of Windows NT 4.0 and both of these functions exist.

Also used NtSetTimerResolution instead of ZeSetTimerResolution for
consistency (they are the same).

Also changed system timer resolution log message units to μs with
one decimal digit for readability. This is the actual amount of
precision available to us.
2021-07-20 11:30:12 +10:00
Quantum
d57b5a320e [host] service: fix adjustPriv return value
When OpenProcessToken fails, the function returned -1, which would be true
when converted to bool. This is wrong, and it should be returning false.
2021-07-20 11:29:32 +10:00
Quantum
563ad18f4e [client] egl: improve cursor damage logic
1. Use atomics and return exact cursor positions from egl_cursor_render
   to avoid race conditions between cursor render and update.
2. Instead of messing with lastCursorValid in various overlays, simply use
   the hasOverlay/hadOverlay logic for cursor damage. This simplifies the
   logic greatly.

As a result, I believe all cursor-related artifacts are fixed.

Note to reviewer: as atomic_init and atomic_store are implemented as macros,
it is currently not possible to pass structs as compound literals due to the
comma being interpreted as an argument separator by the preprocessor.
2021-07-20 11:29:13 +10:00
Quantum
b4dc021381 [host] service: pass CREATE_UNICODE_ENVIRONMENT unconditionally
According to MSDN documentation for CreateEnvironmentBlock, "[i]f the
environment block is passed to CreateProcessAsUser, you must also
specify the CREATE_UNICODE_ENVIRONMENT flag."

Also pass DETACHED_PROCESS because the host is a GUI application and
doesn't use the console.
2021-07-20 11:27:16 +10:00
Quantum
ebda52b18b [host] service: use SYSTEM token attached to the current process
Since with the service, we are already running as SYSTEM, we don't need
to use dupeSystemProcessToken to get the token for SYSTEM. This removes
the need for having SeDebugPrivilege, SeTcbPrivilege, and
SeAssignPrimaryTokenPrivilege, or otherwise doing sketchy things.

Furthermore, we now only open the token with the privileges we actually
need.
2021-07-20 11:27:03 +10:00
Quantum
16ee1a825c [host] windows: use event to gracefully signal exit
This allows the process to be terminated without resorting to
TerminateProcess. With some fixes, this allows the notification icon to be
removed when the service is restarted.

Furthermore, instead of sending WM_DESTROY to fool the window into believing
it's being destroyed, we actually call DestroyWindow now.
2021-07-20 11:26:49 +10:00
arcnmx
a4f5ce08b9 [host] link resource directly to exe 2021-07-20 11:25:55 +10:00
arcnmx
aa41e4d2ce Revert "Revert "[host] avoid manual windres command""
This reverts commit cd10e02862.
2021-07-20 11:25:55 +10:00
Quantum
b8effaf42c [client] egl: use glGetError and codes for errors in gl* functions
We used to use DEBUG_EGL_ERROR for gl* functions, which just yields
EGL_SUCCESS even when there are errors.
2021-07-19 19:36:46 +10:00
Quantum
0cbc529640 [client] ds: refactor common EGL swap with damage logic
This commit creates a new utility library, eglutil.h, which contains code
to detect and use EGL_KHR_swap_buffers_with_damage or its EXT equivalent.

This logic used to be duplicated between the X11 and Wayland display servers,
which is not ideal.
2021-07-19 19:35:52 +10:00
Quantum
a8d4668c4d [client] ci: do not install libglew-dev
We removed the GLEW dependency, so we shouldn't be installing it.
2021-07-19 19:34:04 +10:00
Geoffrey McRae
2de9912ac0 [client] egl: EGL_KHR_swap_buffers_with_damage returns EGLBoolean 2021-07-19 13:32:56 +10:00
Geoffrey McRae
2038517861 [common] linux: review and fix event logic, events should not be counted 2021-07-19 13:19:59 +10:00
Geoffrey McRae
33bf668697 [client] app: correct FPS to use an actual per second value 2021-07-19 10:58:40 +10:00
Geoffrey McRae
2736e37e4a [common] timer: fix timespec parameters when interval >= 1000 2021-07-19 10:58:10 +10:00
Geoffrey McRae
e4e1451eaa [client] app: give some transparency to the new FPS window 2021-07-18 21:22:16 +10:00
Quantum
7c872d2d9e [client] egl: properly use OpenGL ES
Instead of using the desktop <GL/gl.h>, we properly use the OpenGL ES 3.x
headers. Also, we now use GL_EXT_buffer_storage for MAP_PERSISTENT_BIT_EXT
and MAP_COHERENT_BIT_EXT as the core versions are only available in desktop
OpenGL 4.4. Similarly, we need GL_EXT_texture_format_BGRA8888 for GL_BGRA_EXT
as GL_BGRA is desktop-only.
2021-07-18 20:44:32 +10:00
Geoffrey McRae
ab31040d5f [client] all: use imgui for FPS/UPS display 2021-07-18 20:43:17 +10:00
Geoffrey McRae
45e1b5bce0 [common] ringbuffer: add pre value overwrite callback
This allows us to set a callback to read a value out before it's about
to be overwritten which can be useful for things like calculating a
running average.
2021-07-18 20:42:29 +10:00
Quantum
42d8f31eba [client] imgui: use ES 3.0 and remove glew dependency
If we specifically tell ImGui's OpenGL 3.x backend to target OpenGL ES 3.0,
then no extension loader is necessary.
2021-07-18 18:50:12 +10:00
Quantum
c6a6230a56 [client] egl: revert "only copy damaged areas when using dmabuf"
This reverts commit a14de25661.
Frame is sometimes incorrect.
2021-07-18 18:48:35 +10:00
Quantum
a14de25661 [client] egl: only copy damaged areas when using dmabuf
This speeds up the copy significantly when the frame only has small
amount of damage.
2021-07-18 17:41:29 +10:00
Quantum
09893fd728 [client] wayland: display swap to photon latency graph 2021-07-18 16:15:10 +10:00
Quantum
e87bc3a83e [client] wayland: measure presentation time 2021-07-18 16:15:10 +10:00
Quantum
6da9428d85 [client] imgui: use struct for graph metrics instead of array
This allows the members to be named, making the code much easier to read.
2021-07-18 16:15:10 +10:00
Quantum
15bc6a1509 [client] imgui: support registering new graphs
Currently, our struct ll doesn't support removing elements from the middle,
so we will not be removing anything for now.
2021-07-18 16:15:10 +10:00
Quantum
a4bf3c8088 [client] egl: use debug context and make it configurable
The boolean option egl:debug is used to control whether we want debug
output or not. This defaults to true to aid in debugging.
2021-07-18 15:55:50 +10:00
Quantum
6472c28473 [client] egl: upload damage vertices with glBufferSubData
This avoids the issues surrounding glMapBuffer and glMapBufferRange in
OpenGL ES.
2021-07-18 15:55:27 +10:00
Quantum
f49f2af6cd [client] egl: implement error reporting callback
This reports useful information from OpenGL on supported platforms.
2021-07-18 13:54:16 +10:00
Quantum
061b9ba6c2 [client] egl: use vertex array objects in model.c
This eliminates the need to bind the buffers and set up the vertices on
every frame.
2021-07-18 11:58:47 +10:00
Quantum
d4f8426ae4 [client] egl: correctly clean up vertex buffer objects
The code used to use hasBuffer, which was never set to true, so buffer
objects were always leaked instead.
2021-07-18 11:58:47 +10:00
Quantum
ad974cfa0a [common] dpi: remove no longer used library 2021-07-18 10:50:57 +10:00
Quantum
e1fae8927f [common] kvmfr: remove mouseScalingPercent which is no longer used
This used to contain DPI information, but is no longer used.
2021-07-18 10:50:57 +10:00
Quantum
9ab85fd0b8 [host] capture: stop sending DPI information
The client doesn't need DPI information anymore, so there is no point
fetching it.
2021-07-18 10:50:57 +10:00
Quantum
e0c9a71cd8 [client] spice: remove dpi which is no longer used
DPI was originally added to workaround cursor movement scaling, but since
due to changes with mouse handling, it's no longer required.
2021-07-18 10:50:57 +10:00
Geoffrey McRae
d44d87ee7f [client] spice: update to use the new epoll version of PureSpice 2021-07-18 10:48:56 +10:00
Quantum
2b3f31700b [client] egl: implement frame damage display 2021-07-18 10:41:50 +10:00
Quantum
12cb3e512f [client] util: add function for merging overlapping rectangles
This will be used to merge overlapping rectangles in order to avoid copying
the same rectangles multiple times.
2021-07-18 10:41:50 +10:00
Tudor Brindus
92706caddc [common]: move array length into a common helper
Since it is more generally useful, and less cryptic this way.
2021-07-18 10:41:50 +10:00
Quantum
893b2500c2 [host] nvfbc: copy damaged areas only
This commit tracks the damage made to the framebuffer and only updates those
areas. Damage is tracked directly with NvFBC provided diffmaps.
2021-07-18 10:41:50 +10:00
Quantum
ef2da1902e [common] framebuffer: allow custom framebuffer write implementations
This is helpful for only copying damaged areas.
2021-07-18 10:41:50 +10:00
Quantum
9ce4990793 [host] capture: pass frameIndex to capture backends
This allows capture backends to track damage made to each frame.
2021-07-18 10:41:50 +10:00
Tudor Brindus
f274bec8fc [host] dxgi: compute damage rectangles from moved rectangles
This is untested in that I don't have a Windows 8 VM where move rects
are supplied, but seems sound.
2021-07-18 10:41:50 +10:00
Quantum
00eb26a34f [client] egl: do not use damage when overlays are visible
This allows the overlays to show up correctly.
2021-07-18 10:41:50 +10:00
Quantum
e42747f4e3 [host] nvfbc: better algorithm for merging adjacent regions
Use a proper disjoint-set to give a more accurate result.
2021-07-18 10:41:50 +10:00
Quantum
5ed3301cf5 [host] nvfbc: merge adjacent changed regions
For adjacent changed regions, we actually use the bounding box for the
entire polygon. This may result in more area being damaged than strictly
necessary, but is nevertheless desirable since it reduces the number of
rectangles.
2021-07-18 10:41:50 +10:00
Quantum
442ab318fd [client] egl: use desktop frame damage information 2021-07-18 10:41:50 +10:00
Quantum
6b16bb3ea1 [host] nvfbc: populate damage rectangles 2021-07-18 10:41:50 +10:00
Tudor Brindus
d7f9afb3ba [host] dxgi: populate damage rectangles
Co-Authored-By: Quantum <quantum2048@gmail.com>
2021-07-18 10:41:50 +10:00
Quantum
80ab4b5393 [host] capture: add damage rectangles to capture interface 2021-07-18 10:41:50 +10:00
Quantum
69b20aee05 [common] kvmfr: add damage rectangles to the protocol
Co-Authored-By: Tudor Brindus <me@tbrindus.ca>
2021-07-18 10:41:50 +10:00
Quantum
38a018ebfa [doc] build: remove mentions of SDL 2021-07-18 10:34:41 +10:00
Quantum
6695ca3f34 [client] ds: remove SDL display server 2021-07-18 10:34:41 +10:00
Quantum
eb357fa58a [client] egl: use glCopyTexSubImage2D to copy when using DMA
This removes the need for the driver to allocate texture storage and deleting
the previous allocation on every frame update.
2021-07-18 10:34:21 +10:00
Quantum
e32494f684 [client] ci: fix clang build by using clang++ 2021-07-18 10:32:27 +10:00
Quantum
39ec32b2ef [client] cimgui: build as static library 2021-07-18 10:31:45 +10:00
Geoffrey McRae
d8b37a8d81 [git] update workflow to include new libglew-dev dependency 2021-07-18 10:03:30 +10:00
Geoffrey McRae
df2f623599 [client] app: add keybind to toggle frame timing information display 2021-07-18 09:59:37 +10:00
Geoffrey McRae
73357988e6 [client] main: don't trigger redraws if the video feed is disabled
If the video feed is disabled we are not drawing the cursor, so don't
cause needless redraws
2021-07-17 21:09:51 +10:00
Geoffrey McRae
03c247a9ff [client] x11: make use of eglSwapBuffersWithDamage if it's available 2021-07-17 21:09:51 +10:00
Geoffrey McRae
092ce61908 [client] main: copy & release KVMFR messages sooner
The renderer may take time to process the cursor update due to various
internal factors, as such it's best we copy the data and mark the
message as done ASAP. This prevents the host from filling up the queue
as easily when a high dpi mouse is in use.
2021-07-17 21:09:51 +10:00
Geoffrey McRae
b9d7674b20 [client] imgui: calculate min/max/avg/fps and add them to the plots 2021-07-17 21:09:51 +10:00
Geoffrey McRae
8e3df5a38f [client] main: dont push an invalid value into the timings buffers 2021-07-17 21:09:51 +10:00
Geoffrey McRae
23f9855768 [common] ringbuffer: add forEach iterator 2021-07-17 21:09:51 +10:00
Geoffrey McRae
2e76c874cc [client] app: initial imgui frame timings 2021-07-17 21:09:51 +10:00
Geoffrey McRae
41403286d1 [common] ringbuffer: add getLength method 2021-07-17 21:09:51 +10:00
Geoffrey McRae
c3bc5fb0ff [client] app: collect render and frame timing information 2021-07-17 21:09:51 +10:00
Geoffrey McRae
94ae9a95d7 [common] added new ringbuffer helper for metrics collection 2021-07-17 21:09:51 +10:00
Geoffrey McRae
bcffd70270 [client] app: init/destroy imgui context on run/shutdown 2021-07-17 21:09:51 +10:00
Geoffrey McRae
f08163fd72 [client] imgui: added imgui to the client and OpenGL/EGL renderers 2021-07-17 21:09:51 +10:00
Geoffrey McRae
c9d469fb91 [repos] added cimgui @ version 1.82 2021-07-17 21:09:51 +10:00
Jonathan Rubenstein
25c88a1c6c [doc] faq: Correct misinformed question about mouse warp issue 2021-07-17 15:20:22 +10:00
Geoffrey McRae
7decb58bf7 [host] windows: fix build on Linux due to case sensitive filenames 2021-07-17 15:05:32 +10:00
Geoffrey McRae
d1ec19b30b [host] windows: fix delayExecution order of magnitude bug 2021-07-17 15:02:58 +10:00
Geoffrey McRae
74468cf799 [host] windows: remove accidental addition of some junk 2021-07-17 15:02:36 +10:00
Geoffrey McRae
411a6b1e49 [host] windows: add delayExecution function for more accurate sleeps
This change not only exposes and allows use of NtDelayExecution, but
also moves the code to set the system timer resolution.
2021-07-17 14:55:22 +10:00
Geoffrey McRae
e228165ff9 [host] windows: fix system timer resolution message units
ZwSetTimerResolution works in units of 100ns
2021-07-17 14:31:31 +10:00
Geoffrey McRae
d615514799 [host] windows: do not callback from the mouse hook context
The windows hook WH_MOUSE_LL is called in such a way that any delay in
processing causes a system wide stall. This change spawns an extra
thread which waits on an event set by the hook which is then used to
call the callback with an artifical limit of 1000Hz.
2021-07-17 14:03:52 +10:00
Jonathan Rubenstein
ed717351cf [doc] faq: Partial revert of fa871e9 to include registry hack
Expected to be fixed in B5
2021-07-16 00:47:22 +10:00
Jonathan Rubenstein
4658244686 [doc] build: Revise client build dependencies for B4 2021-07-16 00:47:22 +10:00
Quantum
48ae5c69f4 [client] wayland: fix typo in warp usage
The unwanted ! was introduced in 4b99bba200.
This basically caused warp to never be used.
2021-07-15 08:40:36 +10:00
Geoffrey McRae
4d065d577b [obs] call debug_init() to initialize debug print
Fixes a segfault reported in discord, DEBUG_* macros are not available
until `debug_init()` has been called as of commit
1effd5fddc
2021-07-13 07:42:25 +10:00
322 changed files with 47253 additions and 8117 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.
docs/ jrubcop@gmail.com

View File

@@ -4,15 +4,23 @@ jobs:
client:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
cc: [gcc, clang]
build_type:
- Release
- Debug
compiler:
- {cc: gcc, cxx: g++}
- {cc: clang, cxx: clang++}
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: Install PipeWire repository
run: |
echo 'deb [trusted=yes] https://pipewire-ubuntu.quantum5.workers.dev ./' | sudo tee /etc/apt/sources.list.d/pipewire.list
- name: Update apt
run: |
sudo apt-get update
@@ -20,26 +28,41 @@ jobs:
run: |
sudo apt-get install \
binutils-dev \
libsdl2-dev libsdl2-ttf-dev \
libspice-protocol-dev nettle-dev \
libx11-dev libxss-dev libxi-dev \
wayland-protocols
libgl-dev libgles-dev \
libx11-dev libxss-dev libxi-dev libxinerama-dev libxcursor-dev libxpresent-dev \
libwayland-dev libxkbcommon-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
env:
CC: /usr/bin/${{ matrix.cc }}
CC: /usr/bin/${{ matrix.compiler.cc }}
CXX: /usr/bin/${{ matrix.compiler.cxx }}
run: |
mkdir client/build
cd client/build
cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DENABLE_SDL=ON ..
cmake \
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DCMAKE_LINKER:FILEPATH=/usr/bin/ld \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DENABLE_LIBDECOR=${{ matrix.wayland_shell == 'libdecor' }} \
..
- name: Build client
run: |
cd client/build
make -j$(nproc)
- name: Checking help spelling
run: ./client/build/looking-glass-client --help | ./doc/lgspell.py
- name: Check GL function calls
if: matrix.compiler.cc == 'clang'
run: WAYLAND_SHELL='${{ matrix.wayland_shell }}' ./gl-check
module:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Build kernel module
@@ -50,15 +73,19 @@ jobs:
host-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Install PipeWire repository
run: |
echo 'deb [trusted=yes] https://pipewire-ubuntu.quantum5.workers.dev ./' | sudo tee /etc/apt/sources.list.d/pipewire.list
- name: Update apt
run: |
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 libxcb-xfixes0-dev \
libpipewire-0.3-dev
- name: Configure Linux host
run: |
mkdir host/build
@@ -72,7 +99,7 @@ jobs:
host-windows-cross:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Update apt
@@ -94,11 +121,16 @@ 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
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Configure Windows host for native MinGW-w64
@@ -113,7 +145,7 @@ jobs:
- name: Build Windows host installer
run: |
cd host\build
makensis platform\Windows\installer.nsi
makensis -DBUILD_32BIT platform\Windows\installer.nsi
obs:
runs-on: ubuntu-latest
@@ -121,7 +153,7 @@ jobs:
matrix:
cc: [gcc, clang]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Update apt
@@ -139,3 +171,21 @@ jobs:
run: |
cd obs/build
make -j$(nproc)
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
with:
submodules: recursive
- name: Update apt
run: |
sudo apt-get update
- name: Install docs dependencies
run: |
sudo apt-get install python3-sphinx python3-sphinx-rtd-theme
sudo pip3 install sphinxcontrib-spelling
- name: Build docs
run: |
cd doc
make dirhtml SPHINXOPTS='-b spelling -W' -j$(nproc)

2
.gitignore vendored
View File

@@ -8,3 +8,5 @@ module/modules.order
*.o
*.exe
*/build
__pycache__
*.py[co]

9
.gitmodules vendored
View File

@@ -4,3 +4,12 @@
[submodule "repos/PureSpice"]
path = repos/PureSpice
url = https://github.com/gnif/PureSpice
[submodule "repos/cimgui"]
path = repos/cimgui
url = https://github.com/cimgui/cimgui.git
[submodule "repos/wayland-protocols"]
path = repos/wayland-protocols
url = https://gitlab.freedesktop.org/wayland/wayland-protocols.git
[submodule "repos/nanosvg"]
path = repos/nanosvg
url = https://github.com/memononen/nanosvg.git

14
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)
@@ -53,3 +53,15 @@ orcephrye <drakethebanditi@yahoo.com> (orcephrye)
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)

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,21 +1,31 @@
cmake_minimum_required(VERSION 3.0)
project(looking-glass-client C)
project(looking-glass-client C CXX)
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
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"
)
endif()
list(APPEND CMAKE_MODULE_PATH "${PROJECT_TOP}/cmake/" "${PROJECT_SOURCE_DIR}/cmake/")
include(CheckSubmodule)
include(GNUInstallDirs)
include(CheckCCompilerFlag)
include(FeatureSummary)
option(OPTIMIZE_FOR_NATIVE "Build with -march=native" ON)
if(OPTIMIZE_FOR_NATIVE)
CHECK_C_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
if(COMPILER_SUPPORTS_MARCH_NATIVE)
add_compile_options("-march=native")
endif()
else()
add_compile_options("-march=nehalem" "-mtune=generic")
endif()
set(OPTIMIZE_FOR_NATIVE_DEFAULT ON)
include(OptimizeForNative) # option(OPTIMIZE_FOR_NATIVE)
include(UninstallTarget)
find_package(PkgConfig)
pkg_check_modules(FONTCONFIG REQUIRED IMPORTED_TARGET fontconfig)
option(ENABLE_OPENGL "Enable the OpenGL renderer" ON)
add_feature_info(ENABLE_OPENGL ENABLE_OPENGL "Legacy OpenGL renderer.")
@@ -32,9 +42,6 @@ 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_SDL "Build with SDL support" OFF)
add_feature_info(ENABLE_SDL ENABLE_SDL "SDL support.")
option(ENABLE_X11 "Build with X11 support" ON)
add_feature_info(ENABLE_X11 ENABLE_X11 "X11 support.")
@@ -44,8 +51,14 @@ 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_SDL AND NOT ENABLE_X11 AND NOT ENABLE_WAYLAND)
message(FATAL_ERROR "One of ENABLE_SDL, ENABLE_X11, or ENABLE_WAYLAND must be on")
option(ENABLE_PIPEWIRE "Build with PipeWire audio output support" ON)
add_feature_info(ENABLE_PIPEWIRE ENABLE_PIPEWIRE "PipeWire audio support.")
option(ENABLE_PULSEAUDIO "Build with PulseAudio audio output support" ON)
add_feature_info(ENABLE_PULSEAUDIO ENABLE_PULSEAUDIO "PulseAudio audio support.")
if (NOT ENABLE_X11 AND NOT ENABLE_WAYLAND)
message(FATAL_ERROR "Either ENABLE_X11 or ENABLE_WAYLAND must be on")
endif()
add_compile_options(
@@ -53,6 +66,7 @@ add_compile_options(
"-Wextra"
"-Wno-sign-compare"
"-Wno-unused-parameter"
"$<$<COMPILE_LANGUAGE:C>:-Wstrict-prototypes>"
"$<$<C_COMPILER_ID:GNU>:-Wimplicit-fallthrough=2>"
"-Werror"
"-Wfatal-errors"
@@ -83,68 +97,97 @@ if(ENABLE_UBSAN)
set(EXE_FLAGS "${EXE_FLAGS} -fsanitize=undefined")
endif()
find_package(GMP)
add_definitions(-D ATOMIC_LOCKING)
add_definitions(-D GL_GLEXT_PROTOTYPES)
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
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
${GMP_INCLUDE_DIR}
${PROJECT_TOP}
${PROJECT_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}
${CMAKE_BINARY_DIR}/include
${PROJECT_TOP}/repos/nanosvg/src
)
link_libraries(
${GMP_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/lg-renderer.c
src/ll.c
src/util.c
src/clipboard.c
src/kb.c
src/egl_dynprocs.c
${CMAKE_BINARY_DIR}/version.c
src/main.c
src/core.c
src/app.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/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_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")
add_subdirectory("${PROJECT_TOP}/repos/cimgui" "${CMAKE_BINARY_DIR}/cimgui" EXCLUDE_FROM_ALL)
add_subdirectory(displayservers)
add_subdirectory(renderers)
add_subdirectory(fonts)
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}
lg_common
displayservers
lgmp
purespice
renderers
fonts
${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)
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)

View File

@@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 3.0)
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.0)
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,565 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "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"
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;
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 (!(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;
}
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 bool pipewire_init(void)
{
pw_init(NULL, NULL);
pw.loop = pw_loop_new(NULL);
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);
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);
pw.playback.stream = pw_stream_new_simple(
pw.loop,
"Looking Glass",
pw_properties_new(
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
),
&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 size_t pipewire_playbackLatency(void)
{
struct pw_time time = { 0 };
pw_thread_loop_lock(pw.thread);
#if PW_CHECK_VERSION(0, 3, 50)
if (pw_stream_get_time_n(pw.playback.stream, &time, sizeof(time)) < 0)
#else
if (pw_stream_get_time(pw.playback.stream, &time) < 0)
#endif
DEBUG_ERROR("pw_stream_get_time failed");
pw_thread_loop_unlock(pw.thread);
return time.delay + time.queued / pw.playback.stride;
}
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;
pw_thread_loop_lock(pw.thread);
pw.record.stream = pw_stream_new_simple(
pw.loop,
"Looking Glass",
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
),
&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",
.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.0)
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-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "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,19 +0,0 @@
# Try to find the GMP librairies
# GMP_FOUND - system has GMP lib
# GMP_INCLUDE_DIR - the GMP include directory
# GMP_LIBRARIES - Libraries needed to use GMP
if (GMP_INCLUDE_DIR AND GMP_LIBRARIES)
# Already in cache, be silent
set(GMP_FIND_QUIETLY TRUE)
endif (GMP_INCLUDE_DIR AND GMP_LIBRARIES)
find_path(GMP_INCLUDE_DIR NAMES gmp.h )
find_library(GMP_LIBRARIES NAMES gmp libgmp )
find_library(GMPXX_LIBRARIES NAMES gmpxx libgmpxx )
MESSAGE(STATUS "GMP libs: " ${GMP_LIBRARIES} " " ${GMPXX_LIBRARIES} )
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GMP DEFAULT_MSG GMP_INCLUDE_DIR GMP_LIBRARIES)
mark_as_advanced(GMP_INCLUDE_DIR GMP_LIBRARIES)

View File

@@ -27,11 +27,6 @@ if (ENABLE_X11)
add_displayserver(X11)
endif()
# SDL must be last as it's the fallback implemntation
if (ENABLE_SDL)
add_displayserver(SDL)
endif()
list(REMOVE_AT DISPLAYSERVERS 0)
list(REMOVE_AT DISPLAYSERVERS_LINK 0)

View File

@@ -1,24 +0,0 @@
cmake_minimum_required(VERSION 3.0)
project(displayserver_SDL LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(DISPLAYSERVER_SDL_PKGCONFIG REQUIRED
sdl2
)
add_library(displayserver_SDL STATIC
sdl.c
)
target_link_libraries(displayserver_SDL
${DISPLAYSERVER_SDL_PKGCONFIG_LIBRARIES}
${DISPLAYSERVER_SDL_OPT_PKGCONFIG_LIBRARIES}
lg_common
)
target_include_directories(displayserver_SDL
PRIVATE
src
${DISPLAYSERVER_SDL_PKGCONFIG_INCLUDE_DIRS}
${DISPLAYSERVER_SDL_OPT_PKGCONFIG_INCLUDE_DIRS}
)

View File

@@ -1,197 +0,0 @@
/**
* Looking Glass
* Copyright (C) 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 <linux/input.h>
#include <SDL2/SDL.h>
//FIXME: this should be made static once config.c is no longer using SDL
//scancodes
uint32_t sdl_to_xfree86[SDL_NUM_SCANCODES] =
{
[SDL_SCANCODE_UNKNOWN] /* = USB 0 */ = KEY_RESERVED,
[SDL_SCANCODE_A] /* = USB 4 */ = KEY_A,
[SDL_SCANCODE_B] /* = USB 5 */ = KEY_B,
[SDL_SCANCODE_C] /* = USB 6 */ = KEY_C,
[SDL_SCANCODE_D] /* = USB 7 */ = KEY_D,
[SDL_SCANCODE_E] /* = USB 8 */ = KEY_E,
[SDL_SCANCODE_F] /* = USB 9 */ = KEY_F,
[SDL_SCANCODE_G] /* = USB 10 */ = KEY_G,
[SDL_SCANCODE_H] /* = USB 11 */ = KEY_H,
[SDL_SCANCODE_I] /* = USB 12 */ = KEY_I,
[SDL_SCANCODE_J] /* = USB 13 */ = KEY_J,
[SDL_SCANCODE_K] /* = USB 14 */ = KEY_K,
[SDL_SCANCODE_L] /* = USB 15 */ = KEY_L,
[SDL_SCANCODE_M] /* = USB 16 */ = KEY_M,
[SDL_SCANCODE_N] /* = USB 17 */ = KEY_N,
[SDL_SCANCODE_O] /* = USB 18 */ = KEY_O,
[SDL_SCANCODE_P] /* = USB 19 */ = KEY_P,
[SDL_SCANCODE_Q] /* = USB 20 */ = KEY_Q,
[SDL_SCANCODE_R] /* = USB 21 */ = KEY_R,
[SDL_SCANCODE_S] /* = USB 22 */ = KEY_S,
[SDL_SCANCODE_T] /* = USB 23 */ = KEY_T,
[SDL_SCANCODE_U] /* = USB 24 */ = KEY_U,
[SDL_SCANCODE_V] /* = USB 25 */ = KEY_V,
[SDL_SCANCODE_W] /* = USB 26 */ = KEY_W,
[SDL_SCANCODE_X] /* = USB 27 */ = KEY_X,
[SDL_SCANCODE_Y] /* = USB 28 */ = KEY_Y,
[SDL_SCANCODE_Z] /* = USB 29 */ = KEY_Z,
[SDL_SCANCODE_1] /* = USB 30 */ = KEY_1,
[SDL_SCANCODE_2] /* = USB 31 */ = KEY_2,
[SDL_SCANCODE_3] /* = USB 32 */ = KEY_3,
[SDL_SCANCODE_4] /* = USB 33 */ = KEY_4,
[SDL_SCANCODE_5] /* = USB 34 */ = KEY_5,
[SDL_SCANCODE_6] /* = USB 35 */ = KEY_6,
[SDL_SCANCODE_7] /* = USB 36 */ = KEY_7,
[SDL_SCANCODE_8] /* = USB 37 */ = KEY_8,
[SDL_SCANCODE_9] /* = USB 38 */ = KEY_9,
[SDL_SCANCODE_0] /* = USB 39 */ = KEY_0,
[SDL_SCANCODE_RETURN] /* = USB 40 */ = KEY_ENTER,
[SDL_SCANCODE_ESCAPE] /* = USB 41 */ = KEY_ESC,
[SDL_SCANCODE_BACKSPACE] /* = USB 42 */ = KEY_BACKSPACE,
[SDL_SCANCODE_TAB] /* = USB 43 */ = KEY_TAB,
[SDL_SCANCODE_SPACE] /* = USB 44 */ = KEY_SPACE,
[SDL_SCANCODE_MINUS] /* = USB 45 */ = KEY_MINUS,
[SDL_SCANCODE_EQUALS] /* = USB 46 */ = KEY_EQUAL,
[SDL_SCANCODE_LEFTBRACKET] /* = USB 47 */ = KEY_LEFTBRACE,
[SDL_SCANCODE_RIGHTBRACKET] /* = USB 48 */ = KEY_RIGHTBRACE,
[SDL_SCANCODE_BACKSLASH] /* = USB 49 */ = KEY_BACKSLASH,
[SDL_SCANCODE_SEMICOLON] /* = USB 51 */ = KEY_SEMICOLON,
[SDL_SCANCODE_APOSTROPHE] /* = USB 52 */ = KEY_APOSTROPHE,
[SDL_SCANCODE_GRAVE] /* = USB 53 */ = KEY_GRAVE,
[SDL_SCANCODE_COMMA] /* = USB 54 */ = KEY_COMMA,
[SDL_SCANCODE_PERIOD] /* = USB 55 */ = KEY_DOT,
[SDL_SCANCODE_SLASH] /* = USB 56 */ = KEY_SLASH,
[SDL_SCANCODE_CAPSLOCK] /* = USB 57 */ = KEY_CAPSLOCK,
[SDL_SCANCODE_F1] /* = USB 58 */ = KEY_F1,
[SDL_SCANCODE_F2] /* = USB 59 */ = KEY_F2,
[SDL_SCANCODE_F3] /* = USB 60 */ = KEY_F3,
[SDL_SCANCODE_F4] /* = USB 61 */ = KEY_F4,
[SDL_SCANCODE_F5] /* = USB 62 */ = KEY_F5,
[SDL_SCANCODE_F6] /* = USB 63 */ = KEY_F6,
[SDL_SCANCODE_F7] /* = USB 64 */ = KEY_F7,
[SDL_SCANCODE_F8] /* = USB 65 */ = KEY_F8,
[SDL_SCANCODE_F9] /* = USB 66 */ = KEY_F9,
[SDL_SCANCODE_F10] /* = USB 67 */ = KEY_F10,
[SDL_SCANCODE_F11] /* = USB 68 */ = KEY_F11,
[SDL_SCANCODE_F12] /* = USB 69 */ = KEY_F12,
[SDL_SCANCODE_PRINTSCREEN] /* = USB 70 */ = KEY_SYSRQ,
[SDL_SCANCODE_SCROLLLOCK] /* = USB 71 */ = KEY_SCROLLLOCK,
[SDL_SCANCODE_PAUSE] /* = USB 72 */ = KEY_PAUSE,
[SDL_SCANCODE_INSERT] /* = USB 73 */ = KEY_INSERT,
[SDL_SCANCODE_HOME] /* = USB 74 */ = KEY_HOME,
[SDL_SCANCODE_PAGEUP] /* = USB 75 */ = KEY_PAGEUP,
[SDL_SCANCODE_DELETE] /* = USB 76 */ = KEY_DELETE,
[SDL_SCANCODE_END] /* = USB 77 */ = KEY_END,
[SDL_SCANCODE_PAGEDOWN] /* = USB 78 */ = KEY_PAGEDOWN,
[SDL_SCANCODE_RIGHT] /* = USB 79 */ = KEY_RIGHT,
[SDL_SCANCODE_LEFT] /* = USB 80 */ = KEY_LEFT,
[SDL_SCANCODE_DOWN] /* = USB 81 */ = KEY_DOWN,
[SDL_SCANCODE_UP] /* = USB 82 */ = KEY_UP,
[SDL_SCANCODE_NUMLOCKCLEAR] /* = USB 83 */ = KEY_NUMLOCK,
[SDL_SCANCODE_KP_DIVIDE] /* = USB 84 */ = KEY_KPSLASH,
[SDL_SCANCODE_KP_MULTIPLY] /* = USB 85 */ = KEY_KPASTERISK,
[SDL_SCANCODE_KP_MINUS] /* = USB 86 */ = KEY_KPMINUS,
[SDL_SCANCODE_KP_PLUS] /* = USB 87 */ = KEY_KPPLUS,
[SDL_SCANCODE_KP_ENTER] /* = USB 88 */ = KEY_KPENTER,
[SDL_SCANCODE_KP_1] /* = USB 89 */ = KEY_KP1,
[SDL_SCANCODE_KP_2] /* = USB 90 */ = KEY_KP2,
[SDL_SCANCODE_KP_3] /* = USB 91 */ = KEY_KP3,
[SDL_SCANCODE_KP_4] /* = USB 92 */ = KEY_KP4,
[SDL_SCANCODE_KP_5] /* = USB 93 */ = KEY_KP5,
[SDL_SCANCODE_KP_6] /* = USB 94 */ = KEY_KP6,
[SDL_SCANCODE_KP_7] /* = USB 95 */ = KEY_KP7,
[SDL_SCANCODE_KP_8] /* = USB 96 */ = KEY_KP8,
[SDL_SCANCODE_KP_9] /* = USB 97 */ = KEY_KP9,
[SDL_SCANCODE_KP_0] /* = USB 98 */ = KEY_KP0,
[SDL_SCANCODE_KP_PERIOD] /* = USB 99 */ = KEY_KPDOT,
[SDL_SCANCODE_NONUSBACKSLASH] /* = USB 100 */ = KEY_102ND,
[SDL_SCANCODE_APPLICATION] /* = USB 101 */ = KEY_COMPOSE,
[SDL_SCANCODE_POWER] /* = USB 102 */ = KEY_POWER,
[SDL_SCANCODE_KP_EQUALS] /* = USB 103 */ = KEY_KPEQUAL,
[SDL_SCANCODE_F13] /* = USB 104 */ = KEY_CONFIG,
[SDL_SCANCODE_F14] /* = USB 105 */ = KEY_F14,
[SDL_SCANCODE_F15] /* = USB 106 */ = KEY_F15,
[SDL_SCANCODE_F16] /* = USB 107 */ = KEY_F16,
[SDL_SCANCODE_F17] /* = USB 108 */ = KEY_F17,
[SDL_SCANCODE_F18] /* = USB 109 */ = KEY_F18,
[SDL_SCANCODE_F19] /* = USB 110 */ = KEY_F19,
[SDL_SCANCODE_F20] /* = USB 111 */ = KEY_F20,
[SDL_SCANCODE_HELP] /* = USB 117 */ = KEY_HELP,
[SDL_SCANCODE_MENU] /* = USB 118 */ = KEY_MENU,
[SDL_SCANCODE_STOP] /* = USB 120 */ = KEY_CANCEL,
[SDL_SCANCODE_AGAIN] /* = USB 121 */ = KEY_AGAIN,
[SDL_SCANCODE_UNDO] /* = USB 122 */ = KEY_UNDO,
[SDL_SCANCODE_CUT] /* = USB 123 */ = KEY_CUT,
[SDL_SCANCODE_COPY] /* = USB 124 */ = KEY_COPY,
[SDL_SCANCODE_PASTE] /* = USB 125 */ = KEY_PASTE,
[SDL_SCANCODE_FIND] /* = USB 126 */ = KEY_FIND,
[SDL_SCANCODE_MUTE] /* = USB 127 */ = KEY_MUTE,
[SDL_SCANCODE_VOLUMEUP] /* = USB 128 */ = KEY_VOLUMEUP,
[SDL_SCANCODE_VOLUMEDOWN] /* = USB 129 */ = KEY_VOLUMEDOWN,
[SDL_SCANCODE_KP_COMMA] /* = USB 133 */ = KEY_KPCOMMA,
[SDL_SCANCODE_INTERNATIONAL1] /* = USB 135 */ = KEY_RO,
[SDL_SCANCODE_INTERNATIONAL2] /* = USB 136 */ = KEY_KATAKANAHIRAGANA,
[SDL_SCANCODE_INTERNATIONAL3] /* = USB 137 */ = KEY_YEN,
[SDL_SCANCODE_INTERNATIONAL4] /* = USB 138 */ = KEY_HENKAN,
[SDL_SCANCODE_INTERNATIONAL5] /* = USB 139 */ = KEY_MUHENKAN,
[SDL_SCANCODE_LANG1] /* = USB 144 */ = KEY_HANGEUL,
[SDL_SCANCODE_LANG2] /* = USB 145 */ = KEY_HANJA,
[SDL_SCANCODE_SYSREQ] /* = USB 154 */ = KEY_RIGHTSHIFT,
[SDL_SCANCODE_CANCEL] /* = USB 155 */ = KEY_STOP,
[SDL_SCANCODE_KP_LEFTPAREN] /* = USB 182 */ = KEY_KPLEFTPAREN,
[SDL_SCANCODE_KP_RIGHTPAREN] /* = USB 183 */ = KEY_KPRIGHTPAREN,
[SDL_SCANCODE_KP_PLUSMINUS] /* = USB 215 */ = KEY_KPPLUSMINUS,
[SDL_SCANCODE_LCTRL] /* = USB 224 */ = KEY_LEFTCTRL,
[SDL_SCANCODE_LSHIFT] /* = USB 225 */ = KEY_LEFTSHIFT,
[SDL_SCANCODE_LALT] /* = USB 226 */ = KEY_LEFTALT,
[SDL_SCANCODE_LGUI] /* = USB 227 */ = KEY_LEFTMETA,
[SDL_SCANCODE_RCTRL] /* = USB 228 */ = KEY_RIGHTCTRL,
[SDL_SCANCODE_RSHIFT] /* = USB 229 */ = KEY_RIGHTSHIFT,
[SDL_SCANCODE_RALT] /* = USB 230 */ = KEY_RIGHTALT,
[SDL_SCANCODE_RGUI] /* = USB 231 */ = KEY_RIGHTMETA,
[SDL_SCANCODE_MODE] /* = USB 257 */ = KEY_ZENKAKUHANKAKU,
[SDL_SCANCODE_AUDIONEXT] /* = USB 258 */ = KEY_NEXTSONG,
[SDL_SCANCODE_AUDIOPREV] /* = USB 259 */ = KEY_PREVIOUSSONG,
[SDL_SCANCODE_AUDIOSTOP] /* = USB 260 */ = KEY_STOPCD,
[SDL_SCANCODE_AUDIOPLAY] /* = USB 261 */ = KEY_PLAYPAUSE,
[SDL_SCANCODE_MEDIASELECT] /* = USB 263 */ = KEY_MEDIA,
[SDL_SCANCODE_WWW] /* = USB 264 */ = KEY_WWW,
[SDL_SCANCODE_MAIL] /* = USB 265 */ = KEY_MAIL,
[SDL_SCANCODE_CALCULATOR] /* = USB 266 */ = KEY_CALC,
[SDL_SCANCODE_COMPUTER] /* = USB 267 */ = KEY_COMPUTER,
[SDL_SCANCODE_AC_SEARCH] /* = USB 268 */ = KEY_SEARCH,
[SDL_SCANCODE_AC_HOME] /* = USB 269 */ = KEY_HOMEPAGE,
[SDL_SCANCODE_AC_BACK] /* = USB 270 */ = KEY_BACK,
[SDL_SCANCODE_AC_FORWARD] /* = USB 271 */ = KEY_FORWARD,
[SDL_SCANCODE_AC_REFRESH] /* = USB 273 */ = KEY_REFRESH,
[SDL_SCANCODE_AC_BOOKMARKS] /* = USB 274 */ = KEY_BOOKMARKS,
[SDL_SCANCODE_BRIGHTNESSDOWN] /* = USB 275 */ = KEY_BRIGHTNESSDOWN,
[SDL_SCANCODE_BRIGHTNESSUP] /* = USB 276 */ = KEY_BRIGHTNESSUP,
[SDL_SCANCODE_DISPLAYSWITCH] /* = USB 277 */ = KEY_SWITCHVIDEOMODE,
[SDL_SCANCODE_KBDILLUMTOGGLE] /* = USB 278 */ = KEY_KBDILLUMTOGGLE,
[SDL_SCANCODE_KBDILLUMDOWN] /* = USB 279 */ = KEY_KBDILLUMDOWN,
[SDL_SCANCODE_KBDILLUMUP] /* = USB 280 */ = KEY_KBDILLUMUP,
[SDL_SCANCODE_EJECT] /* = USB 281 */ = KEY_EJECTCD,
[SDL_SCANCODE_SLEEP] /* = USB 282 */ = KEY_SLEEP,
[SDL_SCANCODE_APP1] /* = USB 283 */ = KEY_PROG1,
[SDL_SCANCODE_APP2] /* = USB 284 */ = KEY_PROG2,
[SDL_SCANCODE_AUDIOREWIND] /* = USB 285 */ = KEY_REWIND,
[SDL_SCANCODE_AUDIOFASTFORWARD] /* = USB 286 */ = KEY_FASTFORWARD,
};

View File

@@ -1,561 +0,0 @@
/**
* Looking Glass
* Copyright (C) 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 "interface/displayserver.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_syswm.h>
#ifdef ENABLE_EGL
#include <EGL/eglext.h>
#endif
#if defined(SDL_VIDEO_DRIVER_WAYLAND)
#include <wayland-egl.h>
#endif
#include "app.h"
#include "kb.h"
#include "egl_dynprocs.h"
#include "common/types.h"
#include "common/debug.h"
#include "util.h"
struct SDLDSState
{
SDL_Window * window;
SDL_Cursor * cursor;
EGLNativeWindowType wlDisplay;
bool keyboardGrabbed;
bool pointerGrabbed;
bool exiting;
};
static struct SDLDSState sdl;
/* forwards */
static int sdlEventFilter(void * userdata, SDL_Event * event);
static void sdlSetup(void)
{
}
static bool sdlProbe(void)
{
return true;
}
static bool sdlEarlyInit(void)
{
return true;
}
static bool sdlInit(const LG_DSInitParams params)
{
memset(&sdl, 0, sizeof(sdl));
// Allow screensavers for now: we will enable and disable as needed.
SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1");
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
DEBUG_ERROR("SDL_Init Failed");
return false;
}
#ifdef ENABLE_OPENGL
if (params.opengl)
{
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER , 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE , 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE , 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE , 8);
}
#endif
sdl.window = SDL_CreateWindow(
params.title,
params.center ? SDL_WINDOWPOS_CENTERED : params.x,
params.center ? SDL_WINDOWPOS_CENTERED : params.y,
params.w,
params.h,
(
SDL_WINDOW_HIDDEN |
(params.resizable ? SDL_WINDOW_RESIZABLE : 0) |
(params.borderless ? SDL_WINDOW_BORDERLESS : 0) |
(params.maximize ? SDL_WINDOW_MAXIMIZED : 0) |
(params.opengl ? SDL_WINDOW_OPENGL : 0)
)
);
if (sdl.window == NULL)
{
DEBUG_ERROR("Could not create an SDL window: %s\n", SDL_GetError());
goto fail_init;
}
const uint8_t data[4] = {0xf, 0x9, 0x9, 0xf};
const uint8_t mask[4] = {0xf, 0xf, 0xf, 0xf};
sdl.cursor = SDL_CreateCursor(data, mask, 8, 4, 4, 0);
SDL_SetCursor(sdl.cursor);
SDL_ShowWindow(sdl.window);
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
if (params.fullscreen)
SDL_SetWindowFullscreen(sdl.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
if (!params.center)
SDL_SetWindowPosition(sdl.window, params.x, params.y);
// ensure mouse acceleration is identical in server mode
SDL_SetHintWithPriority(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1", SDL_HINT_OVERRIDE);
SDL_SetEventFilter(sdlEventFilter, NULL);
return true;
fail_init:
SDL_Quit();
return false;
}
static void sdlStartup(void)
{
}
static void sdlShutdown(void)
{
}
static void sdlFree(void)
{
SDL_DestroyWindow(sdl.window);
if (sdl.cursor)
SDL_FreeCursor(sdl.cursor);
if (sdl.window)
SDL_DestroyWindow(sdl.window);
SDL_Quit();
}
static bool sdlGetProp(LG_DSProperty prop, void * ret)
{
return false;
}
#ifdef ENABLE_EGL
static EGLDisplay sdlGetEGLDisplay(void)
{
SDL_SysWMinfo wminfo;
SDL_VERSION(&wminfo.version);
if (!SDL_GetWindowWMInfo(sdl.window, &wminfo))
{
DEBUG_ERROR("SDL_GetWindowWMInfo failed");
return EGL_NO_DISPLAY;
}
EGLNativeDisplayType native;
EGLenum platform;
switch(wminfo.subsystem)
{
case SDL_SYSWM_X11:
native = (EGLNativeDisplayType)wminfo.info.x11.display;
platform = EGL_PLATFORM_X11_KHR;
break;
#if defined(SDL_VIDEO_DRIVER_WAYLAND)
case SDL_SYSWM_WAYLAND:
native = (EGLNativeDisplayType)wminfo.info.wl.display;
platform = EGL_PLATFORM_WAYLAND_KHR;
break;
#endif
default:
DEBUG_ERROR("Unsupported subsystem");
return EGL_NO_DISPLAY;
}
const char *early_exts = eglQueryString(NULL, EGL_EXTENSIONS);
if (util_hasGLExt(early_exts, "EGL_KHR_platform_base") &&
g_egl_dynProcs.eglGetPlatformDisplay)
{
DEBUG_INFO("Using eglGetPlatformDisplay");
return g_egl_dynProcs.eglGetPlatformDisplay(platform, native, NULL);
}
if (util_hasGLExt(early_exts, "EGL_EXT_platform_base") &&
g_egl_dynProcs.eglGetPlatformDisplayEXT)
{
DEBUG_INFO("Using eglGetPlatformDisplayEXT");
return g_egl_dynProcs.eglGetPlatformDisplayEXT(platform, native, NULL);
}
DEBUG_INFO("Using eglGetDisplay");
return eglGetDisplay(native);
}
static EGLNativeWindowType sdlGetEGLNativeWindow(void)
{
SDL_SysWMinfo wminfo;
SDL_VERSION(&wminfo.version);
if (!SDL_GetWindowWMInfo(sdl.window, &wminfo))
{
DEBUG_ERROR("SDL_GetWindowWMInfo failed");
return 0;
}
switch(wminfo.subsystem)
{
case SDL_SYSWM_X11:
return (EGLNativeWindowType)wminfo.info.x11.window;
#if defined(SDL_VIDEO_DRIVER_WAYLAND)
case SDL_SYSWM_WAYLAND:
{
if (sdl.wlDisplay)
return sdl.wlDisplay;
int width, height;
SDL_GetWindowSize(sdl.window, &width, &height);
sdl.wlDisplay = (EGLNativeWindowType)wl_egl_window_create(
wminfo.info.wl.surface, width, height);
return sdl.wlDisplay;
}
#endif
default:
DEBUG_ERROR("Unsupported subsystem");
return 0;
}
}
static void sdlEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count)
{
eglSwapBuffers(display, surface);
}
#endif //ENABLE_EGL
#ifdef ENABLE_OPENGL
static LG_DSGLContext sdlGLCreateContext(void)
{
return (LG_DSGLContext)SDL_GL_CreateContext(sdl.window);
}
static void sdlGLDeleteContext(LG_DSGLContext context)
{
SDL_GL_DeleteContext((SDL_GLContext)context);
}
static void sdlGLMakeCurrent(LG_DSGLContext context)
{
SDL_GL_MakeCurrent(sdl.window, (SDL_GLContext)context);
}
static void sdlGLSetSwapInterval(int interval)
{
SDL_GL_SetSwapInterval(interval);
}
static void sdlGLSwapBuffers(void)
{
SDL_GL_SwapWindow(sdl.window);
}
#endif //ENABLE_OPENGL
static int sdlEventFilter(void * userdata, SDL_Event * event)
{
switch(event->type)
{
case SDL_QUIT:
app_handleCloseEvent();
break;
case SDL_MOUSEMOTION:
// stop motion events during the warp out of the window
if (sdl.exiting)
break;
app_updateCursorPos(event->motion.x, event->motion.y);
app_handleMouseRelative(event->motion.xrel, event->motion.yrel,
event->motion.xrel, event->motion.yrel);
break;
case SDL_MOUSEBUTTONDOWN:
{
int button = event->button.button;
if (button > 3)
button += 2;
app_handleButtonPress(button);
break;
}
case SDL_MOUSEBUTTONUP:
{
int button = event->button.button;
if (button > 3)
button += 2;
app_handleButtonRelease(button);
break;
}
case SDL_MOUSEWHEEL:
{
int button = event->wheel.y > 0 ? 4 : 5;
app_handleButtonPress(button);
app_handleButtonRelease(button);
break;
}
case SDL_KEYDOWN:
{
SDL_Scancode sc = event->key.keysym.scancode;
app_handleKeyPress(sdl_to_xfree86[sc]);
break;
}
case SDL_KEYUP:
{
SDL_Scancode sc = event->key.keysym.scancode;
app_handleKeyRelease(sdl_to_xfree86[sc]);
break;
}
case SDL_WINDOWEVENT:
switch(event->window.event)
{
case SDL_WINDOWEVENT_ENTER:
app_handleEnterEvent(true);
break;
case SDL_WINDOWEVENT_LEAVE:
sdl.exiting = false;
app_handleEnterEvent(false);
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
app_handleFocusEvent(true);
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
app_handleFocusEvent(false);
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
case SDL_WINDOWEVENT_RESIZED:
{
struct Border border;
SDL_GetWindowBordersSize(
sdl.window,
&border.top,
&border.left,
&border.bottom,
&border.right
);
app_handleResizeEvent(
event->window.data1,
event->window.data2,
1,
border);
break;
}
case SDL_WINDOWEVENT_MOVED:
app_updateWindowPos(event->window.data1, event->window.data2);
break;
case SDL_WINDOWEVENT_CLOSE:
app_handleCloseEvent();
break;
}
break;
}
return 0;
}
static void sdlShowPointer(bool show)
{
SDL_ShowCursor(show ? SDL_ENABLE : SDL_DISABLE);
}
static void sdlGrabPointer(void)
{
SDL_SetWindowGrab(sdl.window, SDL_TRUE);
SDL_SetRelativeMouseMode(SDL_TRUE);
sdl.pointerGrabbed = true;
}
static void sdlUngrabPointer(void)
{
SDL_SetWindowGrab(sdl.window, SDL_FALSE);
SDL_SetRelativeMouseMode(SDL_FALSE);
sdl.pointerGrabbed = false;
}
static void sdlGrabKeyboard(void)
{
if (sdl.pointerGrabbed)
SDL_SetWindowGrab(sdl.window, SDL_FALSE);
else
{
DEBUG_WARN("SDL does not support grabbing only the keyboard, grabbing all");
sdl.pointerGrabbed = true;
}
SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1");
SDL_SetWindowGrab(sdl.window, SDL_TRUE);
sdl.keyboardGrabbed = true;
}
static void sdlUngrabKeyboard(void)
{
SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "0");
SDL_SetWindowGrab(sdl.window, SDL_FALSE);
if (sdl.pointerGrabbed)
SDL_SetWindowGrab(sdl.window, SDL_TRUE);
sdl.keyboardGrabbed = false;
}
static void sdlWarpPointer(int x, int y, bool exiting)
{
if (sdl.exiting)
return;
sdl.exiting = exiting;
// if exiting turn off relative mode
if (exiting)
SDL_SetRelativeMouseMode(SDL_FALSE);
// issue the warp
SDL_WarpMouseInWindow(sdl.window, x, y);
}
static void sdlRealignPointer(void)
{
app_handleMouseRelative(0.0, 0.0, 0.0, 0.0);
}
static bool sdlIsValidPointerPos(int x, int y)
{
const int displays = SDL_GetNumVideoDisplays();
for(int i = 0; i < displays; ++i)
{
SDL_Rect r;
SDL_GetDisplayBounds(i, &r);
if ((x >= r.x && x < r.x + r.w) &&
(y >= r.y && y < r.y + r.h))
return true;
}
return false;
}
static void sdlInhibitIdle(void)
{
SDL_DisableScreenSaver();
}
static void sdlUninhibitIdle(void)
{
SDL_EnableScreenSaver();
}
static void sdlWait(unsigned int time)
{
SDL_WaitEventTimeout(NULL, time);
}
static void sdlSetWindowSize(int x, int y)
{
SDL_SetWindowSize(sdl.window, x, y);
}
static void sdlSetFullscreen(bool fs)
{
SDL_SetWindowFullscreen(sdl.window, fs ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
}
static bool sdlGetFullscreen(void)
{
return (SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
}
static void sdlMinimize(void)
{
SDL_MinimizeWindow(sdl.window);
}
struct LG_DisplayServerOps LGDS_SDL =
{
.setup = sdlSetup,
.probe = sdlProbe,
.earlyInit = sdlEarlyInit,
.init = sdlInit,
.startup = sdlStartup,
.shutdown = sdlShutdown,
.free = sdlFree,
.getProp = sdlGetProp,
#ifdef ENABLE_EGL
.getEGLDisplay = sdlGetEGLDisplay,
.getEGLNativeWindow = sdlGetEGLNativeWindow,
.eglSwapBuffers = sdlEGLSwapBuffers,
#endif
#ifdef ENABLE_OPENGL
.glCreateContext = sdlGLCreateContext,
.glDeleteContext = sdlGLDeleteContext,
.glMakeCurrent = sdlGLMakeCurrent,
.glSetSwapInterval = sdlGLSetSwapInterval,
.glSwapBuffers = sdlGLSwapBuffers,
#endif
.showPointer = sdlShowPointer,
.grabPointer = sdlGrabPointer,
.ungrabPointer = sdlUngrabPointer,
.capturePointer = sdlGrabPointer,
.uncapturePointer = sdlUngrabPointer,
.grabKeyboard = sdlGrabKeyboard,
.ungrabKeyboard = sdlUngrabKeyboard,
.warpPointer = sdlWarpPointer,
.realignPointer = sdlRealignPointer,
.isValidPointerPos = sdlIsValidPointerPos,
.inhibitIdle = sdlInhibitIdle,
.uninhibitIdle = sdlUninhibitIdle,
.wait = sdlWait,
.setWindowSize = sdlSetWindowSize,
.setFullscreen = sdlSetFullscreen,
.getFullscreen = sdlGetFullscreen,
.minimize = sdlMinimize,
/* SDL does not have clipboard support */
.cbInit = NULL,
};

View File

@@ -2,91 +2,100 @@ cmake_minimum_required(VERSION 3.0)
project(displayserver_Wayland LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(DISPLAYSERVER_Wayland_PKGCONFIG REQUIRED
wayland-client
pkg_check_modules(DISPLAYSERVER_Wayland REQUIRED IMPORTED_TARGET
wayland-client
wayland-cursor
xkbcommon
)
#pkg_check_modules(DISPLAYSERVER_Wayland_OPT_PKGCONFIG
#)
set(DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES "")
set(displayserver_Wayland_SHELL_SRC "")
if (ENABLE_LIBDECOR)
pkg_check_modules(DISPLAYSERVER_Wayland_LIBDECOR REQUIRED
libdecor-0.1
)
list(APPEND DISPLAYSERVER_Wayland_PKGCONFIG_LIBRARIES ${DISPLAYSERVER_Wayland_LIBDECOR_LIBRARIES})
list(APPEND DISPLAYSERVER_Wayland_PKGCONFIG_INCLUDE_DIRS ${DISPLAYSERVER_Wayland_LIBDECOR_INCLUDE_DIRS})
list(APPEND displayserver_Wayland_SHELL_SRC shell_libdecor.c)
add_definitions(-D 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)
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
state.c
registry.c
wayland.c
window.c
${displayserver_Wayland_SHELL_SRC}
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
${displayserver_Wayland_SHELL_SRC}
)
target_link_libraries(displayserver_Wayland
${DISPLAYSERVER_Wayland_PKGCONFIG_LIBRARIES}
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES}
lg_common
PkgConfig::DISPLAYSERVER_Wayland
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES}
lg_common
)
target_include_directories(displayserver_Wayland
PRIVATE
src
${DISPLAYSERVER_Wayland_PKGCONFIG_INCLUDE_DIRS}
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_INCLUDE_DIRS}
PRIVATE
src
)
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_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)
add_custom_command(OUTPUT "${output_file}.c"
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${protocol_file}" "${output_file}.c"
DEPENDS "${protocol_file}"
VERBATIM)
target_sources(displayserver_Wayland PRIVATE "${output_file}.h" "${output_file}.c")
target_sources(displayserver_Wayland PRIVATE "${output_file}.h" "${output_file}.c")
endmacro()
set(WAYLAND_PROTOCOLS_BASE "${PROJECT_TOP}/repos/wayland-protocols")
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/wayland")
include_directories("${CMAKE_BINARY_DIR}/wayland")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-client-protocol")
"${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-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_PROTOCOLS_BASE}/stable/presentation-time/presentation-time.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-presentation-time-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_PROTOCOLS_BASE}/stable/viewporter/viewporter.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-viewporter-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_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/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol")
"${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/idle-inhibit/idle-inhibit-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-idle-inhibit-unstable-v1-client-protocol")
"${WAYLAND_PROTOCOLS_BASE}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-pointer-constraints-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-idle-inhibit-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-output/xdg-output-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-output-unstable-v1-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/staging/xdg-activation/xdg-activation-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-activation-v1-client-protocol")

View File

@@ -0,0 +1,71 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"
#include <stdbool.h>
#include <wayland-client.h>
#include "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 (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -20,7 +20,6 @@
#include "wayland.h"
#include <assert.h>
#include <stdbool.h>
#include <string.h>
@@ -299,7 +298,7 @@ static void dataDeviceHandleEnter(void * data, struct wl_data_device * device,
uint32_t serial, struct wl_surface * surface, wl_fixed_t sxW, wl_fixed_t syW,
struct wl_data_offer * offer)
{
assert(wlCb.dndOffer == NULL);
DEBUG_ASSERT(wlCb.dndOffer == NULL);
wlCb.dndOffer = offer;
struct DataOffer * extra = wl_data_offer_get_user_data(offer);
@@ -447,7 +446,7 @@ void waylandCBRequest(LG_ClipboardData type)
wl_data_offer_receive(wlCb.offer, wlCb.mimetypes[type], fds[1]);
close(fds[1]);
struct ClipboardRead * data = malloc(sizeof(struct ClipboardRead));
struct ClipboardRead * data = malloc(sizeof(*data));
if (!data)
{
DEBUG_ERROR("Failed to allocate memory to read clipboard");
@@ -513,13 +512,20 @@ 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)
{
struct WCBTransfer * transfer = (struct WCBTransfer *) data;
if (containsMimetype(transfer->mimetypes, mimetype))
{
struct ClipboardWrite * data = malloc(sizeof(struct ClipboardWrite));
struct ClipboardWrite * data = malloc(sizeof(*data));
if (!data)
{
DEBUG_ERROR("Out of memory trying to allocate ClipboardWrite");
@@ -548,14 +554,15 @@ static void dataSourceHandleCancelled(void * data,
}
static const struct wl_data_source_listener dataSourceListener = {
.send = dataSourceHandleSend,
.target = dataSourceHandleTarget,
.send = dataSourceHandleSend,
.cancelled = dataSourceHandleCancelled,
};
static void waylandCBReplyFn(void * opaque, LG_ClipboardData type,
uint8_t * data, uint32_t size)
{
struct WCBTransfer * transfer = malloc(sizeof(struct WCBTransfer));
struct WCBTransfer * transfer = malloc(sizeof(*transfer));
if (!transfer)
{
DEBUG_ERROR("Out of memory when allocating WCBTransfer");

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -38,7 +38,7 @@ static const uint32_t cursorBitmap[] = {
0x000000, 0x000000, 0x000000, 0x000000,
};
static struct wl_buffer * createCursorBuffer(void)
static struct wl_buffer * createSquareCursorBuffer(void)
{
int fd = memfd_create("lg-cursor", 0);
if (fd < 0)
@@ -74,6 +74,68 @@ fail:
return result;
}
static bool loadThemedCursor(const char * name, struct wl_surface ** surface,
struct Point * hotspot)
{
struct wl_cursor * cursor = wl_cursor_theme_get_cursor(wlWm.cursorTheme, name);
if (!cursor)
return false;
struct wl_buffer * buffer = wl_cursor_image_get_buffer(cursor->images[0]);
if (!buffer)
return false;
*surface = wl_compositor_create_surface(wlWm.compositor);
if (!*surface)
return NULL;
wl_surface_attach(*surface, buffer, 0, 0);
wl_surface_set_buffer_scale(*surface, wlWm.cursorScale);
wl_surface_commit(*surface);
*hotspot = (struct Point) {
.x = cursor->images[0]->hotspot_x,
.y = cursor->images[0]->hotspot_y,
};
return true;
}
static const char ** nameLists[LG_POINTER_COUNT] = {
[LG_POINTER_ARROW ] = (const char *[]) { "left_ptr", "arrow", NULL },
[LG_POINTER_INPUT ] = (const char *[]) { "text", "xterm", "ibeam", NULL },
[LG_POINTER_MOVE ] = (const char *[]) {
"move", "4498f0e0c1937ffe01fd06f973665830", "9081237383d90e509aa00f00170e968f", NULL
},
[LG_POINTER_RESIZE_NS ] = (const char *[]) {
"sb_v_double_arrow", "size_ver", "v_double_arrow",
"2870a09082c103050810ffdffffe0204", "00008160000006810000408080010102", NULL
},
[LG_POINTER_RESIZE_EW ] = (const char *[]) {
"sb_h_double_arrow", "size_hor", "h_double_arrow",
"14fef782d02440884392942c11205230", "028006030e0e7ebffc7f7070c0600140", NULL
},
[LG_POINTER_RESIZE_NESW] = (const char *[]) {
"fd_double_arrow", "size_bdiag", "fcf1c3c7cd4491d801f1e1c78f100000", NULL
},
[LG_POINTER_RESIZE_NWSE] = (const char *[]) {
"bd_double_arrow", "size_fdiag", "c7088f0f3e6c8088236ef8e1e3e70000", NULL
},
[LG_POINTER_HAND ] = (const char *[]) {
"hand", "pointing_hand", "hand1", "hand2", "pointer",
"e29285e634086352946a0e7090d73106", "9d800788f1b08800ae810202380a0822", NULL
},
[LG_POINTER_NOT_ALLOWED] = (const char *[]) { "crossed_circle", "not-allowed", NULL },
};
static void reloadCursors(void)
{
if (wlWm.cursorTheme)
for (LG_DSPointer pointer = LG_POINTER_ARROW; pointer < LG_POINTER_COUNT; ++pointer)
for (const char ** names = nameLists[pointer]; *names; ++names)
if (loadThemedCursor(*names, wlWm.cursors + pointer, wlWm.cursorHot + pointer))
break;
}
bool waylandCursorInit(void)
{
if (!wlWm.compositor)
@@ -82,27 +144,79 @@ bool waylandCursorInit(void)
return false;
}
wlWm.cursorBuffer = createCursorBuffer();
if (wlWm.cursorBuffer)
wlWm.cursorSquareBuffer = createSquareCursorBuffer();
if (wlWm.cursorSquareBuffer)
{
wlWm.cursor = wl_compositor_create_surface(wlWm.compositor);
wl_surface_attach(wlWm.cursor, wlWm.cursorBuffer, 0, 0);
wl_surface_commit(wlWm.cursor);
wlWm.cursors[LG_POINTER_SQUARE] = wl_compositor_create_surface(wlWm.compositor);
wl_surface_attach(wlWm.cursors[LG_POINTER_SQUARE], wlWm.cursorSquareBuffer, 0, 0);
wl_surface_commit(wlWm.cursors[LG_POINTER_SQUARE]);
}
wlWm.cursorThemeName = getenv("XCURSOR_THEME");
wlWm.cursorSize = 24;
const char * cursorSizeEnv = getenv("XCURSOR_SIZE");
if (cursorSizeEnv)
{
int size = atoi(cursorSizeEnv);
if (size)
wlWm.cursorSize = size;
}
wlWm.cursorTheme = wl_cursor_theme_load(wlWm.cursorThemeName, wlWm.cursorSize, wlWm.shm);
wlWm.cursorScale = 1;
reloadCursors();
return true;
}
void waylandCursorFree(void)
{
if (wlWm.cursor)
wl_surface_destroy(wlWm.cursor);
if (wlWm.cursorBuffer)
wl_buffer_destroy(wlWm.cursorBuffer);
for (int i = 0; i < LG_POINTER_COUNT; ++i)
if (wlWm.cursors[i])
wl_surface_destroy(wlWm.cursors[i]);
if (wlWm.cursorTheme)
wl_cursor_theme_destroy(wlWm.cursorTheme);
if (wlWm.cursorSquareBuffer)
wl_buffer_destroy(wlWm.cursorSquareBuffer);
}
void waylandShowPointer(bool show)
void waylandCursorScaleChange(void)
{
wlWm.showPointer = show;
wl_pointer_set_cursor(wlWm.pointer, wlWm.pointerEnterSerial, show ? wlWm.cursor : NULL, 0, 0);
int newScale = ceil(wl_fixed_to_double(wlWm.scale));
if (newScale == wlWm.cursorScale)
return;
struct wl_cursor_theme * new = wl_cursor_theme_load(wlWm.cursorThemeName,
wlWm.cursorSize * newScale, wlWm.shm);
if (!new)
return;
struct wl_surface * old[LG_POINTER_COUNT];
memcpy(old, wlWm.cursors, sizeof(old));
memset(wlWm.cursors, 0, sizeof(wlWm.cursors));
if (wlWm.cursorTheme)
wl_cursor_theme_destroy(wlWm.cursorTheme);
wlWm.cursorTheme = new;
wlWm.cursorScale = newScale;
reloadCursors();
waylandSetPointer(wlWm.cursorId);
for (int i = 0; i < LG_POINTER_COUNT; ++i)
if (old[i])
wl_surface_destroy(old[i]);
}
void waylandSetPointer(LG_DSPointer pointer)
{
wlWm.cursorId = pointer;
wlWm.cursor = wlWm.cursors[pointer];
wlWm.cursorHotX = wlWm.cursorHot[pointer].x;
wlWm.cursorHotY = wlWm.cursorHot[pointer].y;
if (wlWm.pointer)
wl_pointer_set_cursor(wlWm.pointer, wlWm.pointerEnterSerial, wlWm.cursor, wlWm.cursorHotX, wlWm.cursorHotY);
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL)
#include "egl_dynprocs.h"
#include "eglutil.h"
bool waylandEGLInit(int w, int h)
{
@@ -71,62 +72,63 @@ EGLDisplay waylandGetEGLDisplay(void)
void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count)
{
if (!wlWm.eglSwapWithDamageInit)
if (!wlWm.swapWithDamage.init)
{
const char *exts = eglQueryString(display, EGL_EXTENSIONS);
wlWm.eglSwapWithDamageInit = true;
if (wl_proxy_get_version((struct wl_proxy *) wlWm.surface) < 4)
{
DEBUG_INFO("Swapping buffers with damage: not supported, need wl_compositor v4");
else if (util_hasGLExt(exts, "EGL_KHR_swap_buffers_with_damage") && g_egl_dynProcs.eglSwapBuffersWithDamageKHR)
{
wlWm.eglSwapWithDamage = g_egl_dynProcs.eglSwapBuffersWithDamageKHR;
DEBUG_INFO("Using EGL_KHR_swap_buffers_with_damage");
}
else if (util_hasGLExt(exts, "EGL_EXT_swap_buffers_with_damage") && g_egl_dynProcs.eglSwapBuffersWithDamageEXT)
{
wlWm.eglSwapWithDamage = g_egl_dynProcs.eglSwapBuffersWithDamageEXT;
DEBUG_INFO("Using EGL_EXT_swap_buffers_with_damage");
swapWithDamageDisable(&wlWm.swapWithDamage);
}
else
DEBUG_INFO("Swapping buffers with damage: not supported");
swapWithDamageInit(&wlWm.swapWithDamage, display);
}
if (wlWm.eglSwapWithDamage && count)
{
if (count * 4 > wlWm.eglDamageRectCount)
{
free(wlWm.eglDamageRects);
wlWm.eglDamageRects = malloc(sizeof(EGLint) * count * 4);
if (!wlWm.eglDamageRects)
DEBUG_FATAL("Out of memory");
wlWm.eglDamageRectCount = count * 4;
}
for (int i = 0; i < count; ++i)
{
wlWm.eglDamageRects[i*4+0] = damage[i].x;
wlWm.eglDamageRects[i*4+1] = damage[i].y;
wlWm.eglDamageRects[i*4+2] = damage[i].w;
wlWm.eglDamageRects[i*4+3] = damage[i].h;
}
wlWm.eglSwapWithDamage(display, surface, wlWm.eglDamageRects, count);
}
else
eglSwapBuffers(display, surface);
waylandPresentationFrame();
swapWithDamage(&wlWm.swapWithDamage, display, surface, damage, count);
if (wlWm.needsResize)
{
wl_egl_window_resize(wlWm.eglWindow, wlWm.width * wlWm.scale, wlWm.height * wlWm.scale, 0, 0);
wl_surface_set_buffer_scale(wlWm.surface, wlWm.scale);
bool skipResize = false;
wl_egl_window_resize(wlWm.eglWindow, wl_fixed_to_int(wlWm.width * wlWm.scale),
wl_fixed_to_int(wlWm.height * wlWm.scale), 0, 0);
if (wlWm.width == 0 || wlWm.height == 0)
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);
}
else
{
if (wlWm.viewport)
{
// Clearing the source and destination rectangles should happen in wp_viewport_destroy.
// However, wlroots does not clear the rectangle until fixed in 456c6e22 (2021-08-02).
// This should be kept to work around old versions of wlroots.
wl_fixed_t clear = wl_fixed_from_int(-1);
wp_viewport_set_source(wlWm.viewport, clear, clear, clear, clear);
wp_viewport_set_destination(wlWm.viewport, -1, -1);
wp_viewport_destroy(wlWm.viewport);
wlWm.viewport = NULL;
}
wl_surface_set_buffer_scale(wlWm.surface, wl_fixed_to_int(wlWm.scale));
}
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
wl_region_add(region, 0, 0, wlWm.width, wlWm.height);
wl_surface_set_opaque_region(wlWm.surface, region);
wl_region_destroy(region);
app_handleResizeEvent(wlWm.width, wlWm.height, wlWm.scale, (struct Border) {0, 0, 0, 0});
wlWm.needsResize = false;
app_handleResizeEvent(wlWm.width, wlWm.height, wl_fixed_to_double(wlWm.scale),
(struct Border) {0, 0, 0, 0});
app_invalidateWindow(true);
waylandStopWaitFrame();
wlWm.needsResize = skipResize;
}
waylandShellAckConfigureIfNeeded();

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 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 (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -20,11 +20,14 @@
#include "wayland.h"
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-client.h>
#include <xkbcommon/xkbcommon.h>
#include "app.h"
#include "common/debug.h"
@@ -52,7 +55,7 @@ static void pointerEnterHandler(void * data, struct wl_pointer * pointer,
wlWm.pointerInSurface = true;
app_handleEnterEvent(true);
wl_pointer_set_cursor(pointer, serial, wlWm.showPointer ? wlWm.cursor : NULL, 0, 0);
wl_pointer_set_cursor(pointer, serial, wlWm.cursor, wlWm.cursorHotX, wlWm.cursorHotY);
wlWm.pointerEnterSerial = serial;
wlWm.cursorX = wl_fixed_to_double(sxW);
@@ -85,11 +88,15 @@ static void pointerLeaveHandler(void * data, struct wl_pointer * pointer,
static void pointerAxisHandler(void * data, struct wl_pointer * pointer,
uint32_t serial, uint32_t axis, wl_fixed_t value)
{
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
return;
int button = value > 0 ?
5 /* SPICE_MOUSE_BUTTON_DOWN */ :
4 /* SPICE_MOUSE_BUTTON_UP */;
app_handleButtonPress(button);
app_handleButtonRelease(button);
app_handleWheelMotion(wl_fixed_to_double(value) / 15.0);
}
static int mapWaylandToSpiceButton(uint32_t button)
@@ -155,9 +162,64 @@ static const struct zwp_relative_pointer_v1_listener relativePointerListener = {
static void keyboardKeymapHandler(void * data, struct wl_keyboard * keyboard,
uint32_t format, int fd, uint32_t size)
{
if (!wlWm.xkb)
goto done;
if (wlWm.keymap)
{
xkb_keymap_unref(wlWm.keymap);
wlWm.keymap = NULL;
}
if (wlWm.xkbState)
{
xkb_state_unref(wlWm.xkbState);
wlWm.xkbState = NULL;
}
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
{
DEBUG_WARN("Unsupported keymap format, keyboard input will not work: %d", format);
goto done;
}
char * map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (map == MAP_FAILED)
{
DEBUG_ERROR("Failed to mmap keymap: %s", strerror(errno));
goto done;
}
wlWm.keymap = xkb_keymap_new_from_string(wlWm.xkb, map,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!wlWm.keymap)
DEBUG_WARN("Failed to load keymap, keyboard input will not work");
munmap(map, size);
if (wlWm.keymap)
{
wlWm.xkbState = xkb_state_new(wlWm.keymap);
if (!wlWm.xkbState)
DEBUG_WARN("Failed to create xkb_state");
}
done:
close(fd);
}
static int getCharcode(uint32_t 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)
{
@@ -170,7 +232,7 @@ static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
uint32_t * key;
wl_array_for_each(key, keys)
app_handleKeyPress(*key);
app_handleKeyPress(*key, getCharcode(*key));
}
static void keyboardLeaveHandler(void * data, struct wl_keyboard * keyboard,
@@ -191,16 +253,44 @@ static void keyboardKeyHandler(void * data, struct wl_keyboard * keyboard,
return;
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
app_handleKeyPress(key);
app_handleKeyPress(key, getCharcode(key));
else
app_handleKeyRelease(key);
app_handleKeyRelease(key, getCharcode(key));
if (!wlWm.xkbState || !app_isOverlayMode() || state != WL_KEYBOARD_KEY_STATE_PRESSED)
return;
key += 8; // xkb scancode is evdev scancode + 8
int size = xkb_state_key_get_utf8(wlWm.xkbState, key, NULL, 0);
if (size <= 0)
return;
char buffer[size + 1];
xkb_state_key_get_utf8(wlWm.xkbState, key, buffer, size + 1);
app_handleKeyboardTyped(buffer);
}
static void keyboardModifiersHandler(void * data,
struct wl_keyboard * keyboard, uint32_t serial, uint32_t modsDepressed,
uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
{
// Do nothing.
if (!wlWm.xkbState)
return;
xkb_state_update_mask(wlWm.xkbState, modsDepressed, modsLatched, modsLocked, 0, 0, group);
app_handleKeyboardModifiers(
xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0,
xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) > 0,
xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) > 0,
xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) > 0
);
app_handleKeyboardLEDs(
xkb_state_led_name_is_active(wlWm.xkbState, XKB_LED_NAME_NUM) > 0,
xkb_state_led_name_is_active(wlWm.xkbState, XKB_LED_NAME_CAPS) > 0,
xkb_state_led_name_is_active(wlWm.xkbState, XKB_LED_NAME_SCROLL) > 0
);
}
static const struct wl_keyboard_listener keyboardListener = {
@@ -211,20 +301,53 @@ static const struct wl_keyboard_listener keyboardListener = {
.modifiers = keyboardModifiersHandler,
};
static void waylandCleanUpPointer(void)
{
INTERLOCKED_SECTION(wlWm.confineLock, {
if (wlWm.lockedPointer)
{
zwp_locked_pointer_v1_destroy(wlWm.lockedPointer);
wlWm.lockedPointer = NULL;
}
if (wlWm.confinedPointer)
{
zwp_confined_pointer_v1_destroy(wlWm.confinedPointer);
wlWm.confinedPointer = NULL;
}
});
if (wlWm.relativePointer)
{
zwp_relative_pointer_v1_destroy(wlWm.relativePointer);
wlWm.relativePointer = NULL;
}
wl_pointer_destroy(wlWm.pointer);
wlWm.pointer = NULL;
}
// Seat-handling listeners.
static void handlePointerCapability(uint32_t capabilities)
{
bool hasPointer = capabilities & WL_SEAT_CAPABILITY_POINTER;
if (!hasPointer && wlWm.pointer)
{
wl_pointer_destroy(wlWm.pointer);
wlWm.pointer = NULL;
}
waylandCleanUpPointer();
else if (hasPointer && !wlWm.pointer)
{
wlWm.pointer = wl_seat_get_pointer(wlWm.seat);
wl_pointer_add_listener(wlWm.pointer, &pointerListener, NULL);
waylandSetPointer(wlWm.cursorId);
if (wlWm.warpSupport)
{
wlWm.relativePointer =
zwp_relative_pointer_manager_v1_get_relative_pointer(
wlWm.relativePointerManager, wlWm.pointer);
zwp_relative_pointer_v1_add_listener(wlWm.relativePointer,
&relativePointerListener, NULL);
}
}
}
@@ -289,18 +412,13 @@ bool waylandInputInit(void)
DEBUG_WARN("zwp_keyboard_shortcuts_inhibit_manager_v1 not exported by "
"compositor, keyboard will not be grabbed");
wlWm.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!wlWm.xkb)
DEBUG_WARN("Failed to initialize xkb, keyboard input will not work");
wl_seat_add_listener(wlWm.seat, &seatListener, NULL);
wl_display_roundtrip(wlWm.display);
if (wlWm.warpSupport)
{
wlWm.relativePointer =
zwp_relative_pointer_manager_v1_get_relative_pointer(
wlWm.relativePointerManager, wlWm.pointer);
zwp_relative_pointer_v1_add_listener(wlWm.relativePointer,
&relativePointerListener, NULL);
}
LG_LOCK_INIT(wlWm.confineLock);
return true;
@@ -310,9 +428,25 @@ void waylandInputFree(void)
{
waylandUngrabPointer();
LG_LOCK_FREE(wlWm.confineLock);
wl_pointer_destroy(wlWm.pointer);
wl_keyboard_destroy(wlWm.keyboard);
if (wlWm.pointer)
waylandCleanUpPointer();
// The only legal way the keyboard can be null is if it never existed.
// When unplugged, the compositor must have an inert object.
if (wlWm.keyboard)
wl_keyboard_destroy(wlWm.keyboard);
wl_seat_destroy(wlWm.seat);
if (wlWm.xkbState)
xkb_state_unref(wlWm.xkbState);
if (wlWm.keymap)
xkb_keymap_unref(wlWm.keymap);
if (wlWm.xkb)
xkb_context_unref(wlWm.xkb);
}
void waylandGrabPointer(void)
@@ -331,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,
@@ -340,16 +474,19 @@ void waylandGrabPointer(void)
});
}
void waylandUngrabPointer(void)
inline static void internalUngrabPointer(bool lock)
{
INTERLOCKED_SECTION(wlWm.confineLock,
if (lock)
LG_LOCK(wlWm.confineLock);
if (wlWm.confinedPointer)
{
if (wlWm.confinedPointer)
{
zwp_confined_pointer_v1_destroy(wlWm.confinedPointer);
wlWm.confinedPointer = NULL;
}
});
zwp_confined_pointer_v1_destroy(wlWm.confinedPointer);
wlWm.confinedPointer = NULL;
}
if (lock)
LG_UNLOCK(wlWm.confineLock);
if (!wlWm.warpSupport)
{
@@ -367,6 +504,11 @@ void waylandUngrabPointer(void)
}
}
void waylandUngrabPointer(void)
{
internalUngrabPointer(true);
}
void waylandCapturePointer(void)
{
if (!wlWm.warpSupport)
@@ -407,10 +549,8 @@ void waylandUncapturePointer(void)
* - if the user has opted to use captureInputOnly mode.
*/
if (!wlWm.warpSupport || !app_isFormatValid() || app_isCaptureOnlyMode())
{
waylandUngrabPointer();
}
else
internalUngrabPointer(false);
else if (wlWm.pointer)
{
wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,
@@ -444,7 +584,7 @@ void waylandWarpPointer(int x, int y, bool exiting)
INTERLOCKED_SECTION(wlWm.confineLock,
{
if (!wlWm.lockedPointer)
if (wlWm.lockedPointer)
{
LG_UNLOCK(wlWm.confineLock);
return;
@@ -487,7 +627,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

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -27,29 +27,63 @@
#include "common/debug.h"
static void outputGeometryHandler(void * data, struct wl_output * output, int32_t x, int32_t y,
static void outputUpdateScale(struct WaylandOutput * node)
{
wl_fixed_t original = node->scale;
if (!wlWm.useFractionalScale || !wlWm.viewporter || !node->logicalWidth)
node->scale = wl_fixed_from_int(node->scaleInt);
else
{
int32_t modeWidth = node->modeRotate ? node->modeHeight : node->modeWidth;
node->scale = wl_fixed_from_double(1.0 * modeWidth / node->logicalWidth);
}
if (original != node->scale)
waylandWindowUpdateScale();
}
static void outputGeometryHandler(void * opaque, struct wl_output * output, int32_t x, int32_t y,
int32_t physical_width, int32_t physical_height, int32_t subpixel, const char * make,
const char * model, int32_t output_transform)
{
// Do nothing.
struct WaylandOutput * node = opaque;
switch (output_transform)
{
case WL_OUTPUT_TRANSFORM_90:
case WL_OUTPUT_TRANSFORM_270:
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
node->modeRotate = true;
break;
default:
node->modeRotate = false;
}
}
static void outputModeHandler(void * data, struct wl_output * wl_output, uint32_t flags,
static void outputModeHandler(void * opaque, struct wl_output * wl_output, uint32_t flags,
int32_t width, int32_t height, int32_t refresh)
{
// Do nothing.
if (!(flags & WL_OUTPUT_MODE_CURRENT))
return;
struct WaylandOutput * node = opaque;
node->modeWidth = width;
node->modeHeight = height;
}
static void outputDoneHandler(void * data, struct wl_output * output)
static void outputDoneHandler(void * opaque, struct wl_output * output)
{
// Do nothing.
struct WaylandOutput * node = opaque;
outputUpdateScale(node);
}
static void outputScaleHandler(void * opaque, struct wl_output * output, int32_t scale)
{
struct WaylandOutput * node = opaque;
node->scale = scale;
waylandWindowUpdateScale();
node->scaleInt = scale;
}
static const struct wl_output_listener outputListener = {
@@ -59,6 +93,45 @@ static const struct wl_output_listener outputListener = {
.scale = outputScaleHandler,
};
static void xdgOutputLogicalPositionHandler(void * opaque, struct zxdg_output_v1 * xdgOutput,
int32_t x, int32_t y)
{
// Do nothing.
}
static void xdgOutputLogicalSizeHandler(void * opaque, struct zxdg_output_v1 * xdgOutput,
int32_t width, int32_t height)
{
struct WaylandOutput * node = opaque;
node->logicalWidth = width;
node->logicalHeight = height;
}
static void xdgOutputDoneHandler(void * opaque, struct zxdg_output_v1 * xdgOutput)
{
struct WaylandOutput * node = opaque;
outputUpdateScale(node);
}
static void xdgOutputNameHandler(void * opaque, struct zxdg_output_v1 * xdgOutput, const char * name)
{
// Do nothing.
}
static void xdgOutputDescriptionHandler(void * opaque, struct zxdg_output_v1 * xdgOutput,
const char * description)
{
// Do nothing.
}
static const struct zxdg_output_v1_listener xdgOutputListener = {
.logical_position = xdgOutputLogicalPositionHandler,
.logical_size = xdgOutputLogicalSizeHandler,
.done = xdgOutputDoneHandler,
.name = xdgOutputNameHandler,
.description = xdgOutputDescriptionHandler,
};
bool waylandOutputInit(void)
{
wl_list_init(&wlWm.outputs);
@@ -73,6 +146,8 @@ void waylandOutputFree(void)
{
if (node->version >= 3)
wl_output_release(node->output);
if (node->xdgOutput)
zxdg_output_v1_destroy(node->xdgOutput);
wl_list_remove(&node->link);
free(node);
}
@@ -80,13 +155,14 @@ void waylandOutputFree(void)
void waylandOutputBind(uint32_t name, uint32_t version)
{
struct WaylandOutput * node = malloc(sizeof(struct WaylandOutput));
struct WaylandOutput * node = calloc(1, sizeof(struct WaylandOutput));
if (!node)
return;
if (version < 2)
{
DEBUG_WARN("wl_output version too old: expected >= 2, got %d", version);
free(node);
return;
}
@@ -103,6 +179,13 @@ void waylandOutputBind(uint32_t name, uint32_t version)
return;
}
if (wlWm.xdgOutputManager)
{
node->xdgOutput = zxdg_output_manager_v1_get_xdg_output(wlWm.xdgOutputManager, node->output);
if (node->xdgOutput)
zxdg_output_v1_add_listener(node->xdgOutput, &xdgOutputListener, node);
}
wl_output_add_listener(node->output, &outputListener, node);
wl_list_insert(&wlWm.outputs, &node->link);
}
@@ -117,6 +200,8 @@ void waylandOutputTryUnbind(uint32_t name)
{
if (node->version >= 3)
wl_output_release(node->output);
if (node->xdgOutput)
zxdg_output_v1_destroy(node->xdgOutput);
wl_list_remove(&node->link);
free(node);
break;
@@ -124,7 +209,7 @@ void waylandOutputTryUnbind(uint32_t name)
}
}
int32_t waylandOutputGetScale(struct wl_output * output)
wl_fixed_t waylandOutputGetScale(struct wl_output * output)
{
struct WaylandOutput * node;
@@ -133,4 +218,3 @@ int32_t waylandOutputGetScale(struct wl_output * output)
return node->scale;
return 0;
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -30,8 +30,13 @@
#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)
@@ -40,6 +45,7 @@ static void waylandDisplayCallback(uint32_t events, void * opaque)
wl_display_read_events(wlWm.display);
wl_display_dispatch_pending(wlWm.display);
}
#endif
bool waylandPollInit(void)
{
@@ -55,21 +61,27 @@ 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;
}
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;
@@ -77,21 +89,29 @@ void waylandWait(unsigned int time)
{
if (errno != EINTR)
DEBUG_INFO("epoll failed: %s", strerror(errno));
#ifndef ENABLE_LIBDECOR
wl_display_cancel_read(wlWm.display);
#endif
return;
}
#ifndef ENABLE_LIBDECOR
bool sawDisplay = false;
#endif
for (int i = 0; i < count; ++i) {
struct WaylandPoll * poll = events[i].data.ptr;
if (!poll->removed)
poll->callback(events[i].events, poll->opaque);
#ifndef ENABLE_LIBDECOR
if (poll->fd == wlWm.displayFd)
sawDisplay = true;
#endif
}
#ifndef ENABLE_LIBDECOR
if (!sawDisplay)
wl_display_cancel_read(wlWm.display);
#endif
INTERLOCKED_SECTION(wlWm.pollFreeLock,
{
@@ -115,7 +135,7 @@ static void waylandPollRemoveNode(struct WaylandPoll * node)
bool waylandPollRegister(int fd, WaylandPollCallback callback, void * opaque, uint32_t events)
{
struct WaylandPoll * node = malloc(sizeof(struct WaylandPoll));
struct WaylandPoll * node = malloc(sizeof(*node));
if (!node)
return false;

View File

@@ -0,0 +1,120 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define _GNU_SOURCE
#include "wayland.h"
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <wayland-client.h>
#include "common/debug.h"
#include "common/time.h"
struct FrameData
{
struct timespec sent;
};
static void presentationClockId(void * data,
struct wp_presentation * presentation, uint32_t clkId)
{
wlWm.clkId = clkId;
}
static const struct wp_presentation_listener presentationListener = {
.clock_id = presentationClockId,
};
static void presentationFeedbackSyncOutput(void * data,
struct wp_presentation_feedback * feedback, struct wl_output * output)
{
// Do nothing.
}
static void presentationFeedbackPresented(void * opaque,
struct wp_presentation_feedback * feedback, uint32_t tvSecHi, uint32_t tvSecLo,
uint32_t tvNsec, uint32_t refresh, uint32_t seqHi, uint32_t seqLo, uint32_t flags)
{
struct FrameData * data = opaque;
struct timespec present = {
.tv_sec = (uint64_t) tvSecHi << 32 | tvSecLo,
.tv_nsec = tvNsec,
};
struct timespec delta;
tsDiff(&delta, &present, &data->sent);
ringbuffer_push(wlWm.photonTimings, &(float){ delta.tv_sec + delta.tv_nsec * 1e-6f });
free(data);
wp_presentation_feedback_destroy(feedback);
}
static void presentationFeedbackDiscarded(void * data,
struct wp_presentation_feedback * feedback)
{
free(data);
wp_presentation_feedback_destroy(feedback);
}
static const struct wp_presentation_feedback_listener presentationFeedbackListener = {
.sync_output = presentationFeedbackSyncOutput,
.presented = presentationFeedbackPresented,
.discarded = presentationFeedbackDiscarded,
};
bool waylandPresentationInit(void)
{
if (wlWm.presentation)
{
wlWm.photonTimings = ringbuffer_new(256, sizeof(float));
wlWm.photonGraph = app_registerGraph("PHOTON", wlWm.photonTimings,
0.0f, 30.0f, NULL);
wp_presentation_add_listener(wlWm.presentation, &presentationListener, NULL);
}
return true;
}
void waylandPresentationFree(void)
{
if (!wlWm.presentation)
return;
wp_presentation_destroy(wlWm.presentation);
app_unregisterGraph(wlWm.photonGraph);
ringbuffer_free(&wlWm.photonTimings);
}
void waylandPresentationFrame(void)
{
if (!wlWm.presentation)
return;
struct FrameData * data = malloc(sizeof(*data));
if (clock_gettime(wlWm.clkId, &data->sent))
{
DEBUG_ERROR("clock_gettime failed: %s\n", strerror(errno));
free(data);
}
struct wp_presentation_feedback * feedback = wp_presentation_feedback(wlWm.presentation, wlWm.surface);
wp_presentation_feedback_add_listener(feedback, &presentationFeedbackListener, data);
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -47,6 +47,12 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
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);
else if (!strcmp(interface, wp_viewporter_interface.name))
wlWm.viewporter = wl_registry_bind(wlWm.registry, name,
&wp_viewporter_interface, 1);
else if (!strcmp(interface, zwp_relative_pointer_manager_v1_interface.name))
wlWm.relativePointerManager = wl_registry_bind(wlWm.registry, name,
&zwp_relative_pointer_manager_v1_interface, 1);
@@ -62,6 +68,13 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
else if (!strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name))
wlWm.idleInhibitManager = wl_registry_bind(wlWm.registry, name,
&zwp_idle_inhibit_manager_v1_interface, 1);
else if (!strcmp(interface, zxdg_output_manager_v1_interface.name) && version >= 2)
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);
}
static void registryGlobalRemoveHandler(void * data,

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -20,8 +20,10 @@
#include "wayland.h"
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <sys/epoll.h>
#include <libdecor.h>
#include <wayland-client.h>
@@ -49,27 +51,32 @@ static void libdecorHandleError(struct libdecor * context, enum libdecor_error e
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)) {
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;
struct libdecor_state * state = libdecor_state_new(wlWm.width, wlWm.height);
libdecor_frame_commit(frame, state, wlWm.configured ? NULL : configuration);
libdecor_state_free(state);
if (wlWm.configured)
{
wlWm.needsResize = true;
wlWm.resizeSerial = configuration->serial;
}
else
wlWm.configured = true;
wlWm.needsResize = true;
wlWm.resizeSerial = configuration->serial;
app_invalidateWindow(true);
waylandStopWaitFrame();
}
static void libdecorFrameClose(struct libdecor_frame * frame, void * opaque)
@@ -94,7 +101,12 @@ static struct libdecor_frame_interface libdecorFrameListener = {
};
#pragma GCC diagnostic pop
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless)
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);
@@ -103,9 +115,19 @@ bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool b
libdecor_frame_set_title(wlWm.libdecorFrame, title);
libdecor_frame_map(wlWm.libdecorFrame);
while (!wlWm.configured)
wl_display_roundtrip(wlWm.display);
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;
}
@@ -124,6 +146,8 @@ void waylandSetFullscreen(bool 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)
@@ -135,3 +159,20 @@ 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,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -46,6 +46,8 @@ static void xdgSurfaceConfigure(void * data, struct xdg_surface * xdgSurface,
{
wlWm.needsResize = true;
wlWm.resizeSerial = serial;
app_invalidateWindow(true);
waylandStopWaitFrame();
}
else
{
@@ -63,15 +65,30 @@ static const struct xdg_surface_listener xdgSurfaceListener = {
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.width = width;
wlWm.height = height;
wlWm.fullscreen = false;
wlWm.floating = true;
enum xdg_toplevel_state * state;
wl_array_for_each(state, states)
{
if (*state == XDG_TOPLEVEL_STATE_FULLSCREEN)
switch (*state)
{
case XDG_TOPLEVEL_STATE_FULLSCREEN:
wlWm.fullscreen = true;
// fallthrough
case XDG_TOPLEVEL_STATE_MAXIMIZED:
case XDG_TOPLEVEL_STATE_TILED_LEFT:
case XDG_TOPLEVEL_STATE_TILED_RIGHT:
case XDG_TOPLEVEL_STATE_TILED_TOP:
case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
wlWm.floating = false;
break;
default:
break;
}
}
}
@@ -85,7 +102,7 @@ static const struct xdg_toplevel_listener xdgToplevelListener = {
.close = xdgToplevelClose,
};
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless)
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
{
if (!wlWm.xdgWmBase)
{
@@ -151,3 +168,17 @@ void waylandMinimize(void)
{
xdg_toplevel_set_minimized(wlWm.xdgToplevel);
}
void waylandShellResize(int w, int h)
{
if (!wlWm.floating)
return;
wlWm.width = w;
wlWm.height = h;
xdg_surface_set_window_geometry(wlWm.xdgSurface, 0, 0, w, h);
wlWm.needsResize = true;
app_invalidateWindow(true);
waylandStopWaitFrame();
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 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 (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -37,6 +37,13 @@ static struct Option waylandOptions[] =
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
{
.module = "wayland",
.name = "fractionScale",
.description = "Enable fractional scale",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
{0}
};
@@ -66,7 +73,8 @@ static bool waylandInit(const LG_DSInitParams params)
memset(&wlWm, 0, sizeof(wlWm));
wl_list_init(&wlWm.surfaceOutputs);
wlWm.warpSupport = option_get_bool("wayland", "warpSupport");
wlWm.warpSupport = option_get_bool("wayland", "warpSupport");
wlWm.useFractionalScale = option_get_bool("wayland", "fractionScale");
wlWm.display = wl_display_connect(NULL);
wlWm.width = params.w;
@@ -81,21 +89,27 @@ static bool waylandInit(const LG_DSInitParams params)
if (!waylandRegistryInit())
return false;
if (!waylandActivationInit())
return false;
if (!waylandIdleInit())
return false;
if (!waylandPresentationInit())
return false;
if (!waylandCursorInit())
return false;
if (!waylandInputInit())
return false;
if (!waylandWindowInit(params.title, params.fullscreen, params.maximize, params.borderless))
if (!waylandWindowInit(params.title, params.fullscreen, params.maximize, params.borderless, params.resizable))
return false;
if (!waylandEGLInit(params.w, params.h))
return false;
if (!waylandCursorInit())
return false;
#ifdef ENABLE_OPENGL
if (params.opengl && !waylandOpenGLInit())
return false;
@@ -119,6 +133,7 @@ static void waylandFree(void)
{
waylandIdleFree();
waylandWindowFree();
waylandPresentationFree();
waylandInputFree();
waylandOutputFree();
waylandRegistryFree();
@@ -139,6 +154,7 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
struct LG_DisplayServerOps LGDS_Wayland =
{
.name = "Wayland",
.setup = waylandSetup,
.probe = waylandProbe,
.earlyInit = waylandEarlyInit,
@@ -161,8 +177,11 @@ struct LG_DisplayServerOps LGDS_Wayland =
.glSetSwapInterval = waylandGLSetSwapInterval,
.glSwapBuffers = waylandGLSwapBuffers,
#endif
.waitFrame = waylandWaitFrame,
.skipFrame = waylandSkipFrame,
.stopWaitFrame = waylandStopWaitFrame,
.guestPointerUpdated = waylandGuestPointerUpdated,
.showPointer = waylandShowPointer,
.setPointer = waylandSetPointer,
.grabPointer = waylandGrabPointer,
.ungrabPointer = waylandUngrabPointer,
.capturePointer = waylandCapturePointer,
@@ -172,6 +191,7 @@ struct LG_DisplayServerOps LGDS_Wayland =
.warpPointer = waylandWarpPointer,
.realignPointer = waylandRealignPointer,
.isValidPointerPos = waylandIsValidPointerPos,
.requestActivation = waylandActivationRequestActivation,
.inhibitIdle = waylandInhibitIdle,
.uninhibitIdle = waylandUninhibitIdle,
.wait = waylandWait,

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -22,24 +22,32 @@
#include <sys/types.h>
#include <wayland-client.h>
#include <wayland-cursor.h>
#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL)
# include <wayland-egl.h>
# include <EGL/egl.h>
# include <EGL/eglext.h>
# include "eglutil.h"
#endif
#include "app.h"
#include "egl_dynprocs.h"
#include "common/locking.h"
#include "common/countedbuffer.h"
#include "common/ringbuffer.h"
#include "interface/displayserver.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);
@@ -55,8 +63,15 @@ struct WaylandPoll
struct WaylandOutput
{
uint32_t name;
int32_t scale;
wl_fixed_t scale;
int32_t scaleInt;
int32_t logicalWidth;
int32_t logicalHeight;
int32_t modeWidth;
int32_t modeHeight;
bool modeRotate;
struct wl_output * output;
struct zxdg_output_v1 * xdgOutput;
uint32_t version;
struct wl_list link;
};
@@ -74,6 +89,10 @@ enum EGLSwapWithDamageState {
SWAP_WITH_DAMAGE_EXT,
};
struct xkb_context;
struct xkb_keymap;
struct xkb_state;
struct WaylandDSState
{
bool pointerGrabbed;
@@ -88,9 +107,12 @@ struct WaylandDSState
struct wl_shm * shm;
struct wl_compositor * compositor;
int32_t width, height, scale;
int32_t width, height;
wl_fixed_t scale;
bool fractionalScale;
bool needsResize;
bool fullscreen;
bool floating;
uint32_t resizeSerial;
bool configured;
bool warpSupport;
@@ -98,10 +120,7 @@ struct WaylandDSState
#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL)
struct wl_egl_window * eglWindow;
bool eglSwapWithDamageInit;
eglSwapBuffersWithDamageKHR_t eglSwapWithDamage;
EGLint * eglDamageRects;
int eglDamageRectCount;
struct SwapWithDamageData swapWithDamage;
#endif
#ifdef ENABLE_OPENGL
@@ -110,6 +129,11 @@ struct WaylandDSState
EGLSurface glSurface;
#endif
struct wp_presentation * presentation;
clockid_t clkId;
RingBuffer photonTimings;
GraphHandle photonGraph;
#ifdef ENABLE_LIBDECOR
struct libdecor * libdecor;
struct libdecor_frame * libdecorFrame;
@@ -121,8 +145,17 @@ struct WaylandDSState
struct zxdg_toplevel_decoration_v1 * xdgToplevelDecoration;
#endif
struct wl_surface * cursor;
struct wl_buffer * cursorBuffer;
const char * cursorThemeName;
int cursorSize;
int cursorScale;
struct wl_cursor_theme * cursorTheme;
struct wl_buffer * cursorSquareBuffer;
struct wl_surface * cursors[LG_POINTER_COUNT];
struct Point cursorHot[LG_POINTER_COUNT];
LG_DSPointer cursorId;
struct wl_surface * cursor;
int cursorHotX;
int cursorHotY;
struct wl_data_device_manager * dataDeviceManager;
@@ -132,6 +165,9 @@ struct WaylandDSState
struct zwp_keyboard_shortcuts_inhibit_manager_v1 * keyboardInhibitManager;
struct zwp_keyboard_shortcuts_inhibitor_v1 * keyboardInhibitor;
uint32_t keyboardEnterSerial;
struct xkb_context * xkb;
struct xkb_state * xkbState;
struct xkb_keymap * keymap;
struct wl_pointer * pointer;
struct zwp_relative_pointer_manager_v1 * relativePointerManager;
@@ -146,8 +182,16 @@ 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;
struct wl_list outputs; // WaylandOutput::link
struct wl_list surfaceOutputs; // SurfaceOutput::link
bool useFractionalScale;
LGEvent * frameEvent;
struct wl_list poll; // WaylandPoll::link
struct wl_list pollFree; // WaylandPoll::link
@@ -191,6 +235,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);
@@ -201,7 +250,8 @@ void waylandCBInvalidate(void);
// cursor module
bool waylandCursorInit(void);
void waylandCursorFree(void);
void waylandShowPointer(bool show);
void waylandSetPointer(LG_DSPointer pointer);
void waylandCursorScaleChange(void);
// gl module
#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL)
@@ -247,7 +297,7 @@ bool waylandOutputInit(void);
void waylandOutputFree(void);
void waylandOutputBind(uint32_t name, uint32_t version);
void waylandOutputTryUnbind(uint32_t name);
int32_t waylandOutputGetScale(struct wl_output * output);
wl_fixed_t waylandOutputGetScale(struct wl_output * output);
// poll module
bool waylandPollInit(void);
@@ -255,20 +305,29 @@ void waylandWait(unsigned int time);
bool waylandPollRegister(int fd, WaylandPollCallback callback, void * opaque, uint32_t events);
bool waylandPollUnregister(int fd);
// presentation module
bool waylandPresentationInit(void);
void waylandPresentationFrame(void);
void waylandPresentationFree(void);
// registry module
bool waylandRegistryInit(void);
void waylandRegistryFree(void);
// shell module
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless);
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 waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable);
void waylandWindowFree(void);
void waylandWindowUpdateScale(void);
void waylandSetWindowSize(int x, int y);
bool waylandIsValidPointerPos(int x, int y);
bool waylandWaitFrame(void);
void waylandSkipFrame(void);
void waylandStopWaitFrame(void);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -27,17 +27,18 @@
#include "app.h"
#include "common/debug.h"
#include "common/event.h"
// Surface-handling listeners.
void waylandWindowUpdateScale(void)
{
int32_t maxScale = 0;
wl_fixed_t maxScale = 0;
struct SurfaceOutput * node;
wl_list_for_each(node, &wlWm.surfaceOutputs, link)
{
int32_t scale = waylandOutputGetScale(node->output);
wl_fixed_t scale = waylandOutputGetScale(node->output);
if (scale > maxScale)
maxScale = scale;
}
@@ -45,13 +46,23 @@ void waylandWindowUpdateScale(void)
if (maxScale)
{
wlWm.scale = maxScale;
wlWm.fractionalScale = wl_fixed_from_int(wl_fixed_to_int(maxScale)) != maxScale;
wlWm.needsResize = true;
waylandCursorScaleChange();
app_invalidateWindow(true);
waylandStopWaitFrame();
}
}
static void wlSurfaceEnterHandler(void * data, struct wl_surface * surface, struct wl_output * output)
{
struct SurfaceOutput * node = malloc(sizeof(struct SurfaceOutput));
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();
@@ -74,9 +85,17 @@ static const struct wl_surface_listener wlSurfaceListener = {
.leave = wlSurfaceLeaveHandler,
};
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless)
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
{
wlWm.scale = 1;
wlWm.scale = wl_fixed_from_int(1);
wlWm.frameEvent = lgCreateEvent(true, 0);
if (!wlWm.frameEvent)
{
DEBUG_ERROR("Failed to initialize event for waitFrame");
return false;
}
lgSignalEvent(wlWm.frameEvent);
if (!wlWm.compositor)
{
@@ -93,7 +112,7 @@ bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool
wl_surface_add_listener(wlWm.surface, &wlSurfaceListener, NULL);
if (!waylandShellInit(title, fullscreen, maximize, borderless))
if (!waylandShellInit(title, fullscreen, maximize, borderless, resizable))
return false;
wl_surface_commit(wlWm.surface);
@@ -103,14 +122,47 @@ bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool
void waylandWindowFree(void)
{
wl_surface_destroy(wlWm.surface);
lgFreeEvent(wlWm.frameEvent);
}
void waylandSetWindowSize(int x, int y)
{
// FIXME: implement.
waylandShellResize(x, y);
}
bool waylandIsValidPointerPos(int x, int y)
{
return x >= 0 && x < wlWm.width && y >= 0 && y < wlWm.height;
}
static void frameHandler(void * opaque, struct wl_callback * callback, unsigned int data)
{
lgSignalEvent(wlWm.frameEvent);
wl_callback_destroy(callback);
}
static const struct wl_callback_listener frame_listener = {
.done = frameHandler,
};
bool waylandWaitFrame(void)
{
lgWaitEvent(wlWm.frameEvent, TIMEOUT_INFINITE);
struct wl_callback * callback = wl_surface_frame(wlWm.surface);
if (callback)
wl_callback_add_listener(callback, &frame_listener, NULL);
return false;
}
void waylandSkipFrame(void)
{
// If we decided to not render, we must commit the surface so that the callback is registered.
wl_surface_commit(wlWm.surface);
}
void waylandStopWaitFrame(void)
{
lgSignalEvent(wlWm.frameEvent);
}

View File

@@ -2,29 +2,31 @@ cmake_minimum_required(VERSION 3.0)
project(displayserver_X11 LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(DISPLAYSERVER_X11_PKGCONFIG REQUIRED
x11
xi
xfixes
xscrnsaver
xinerama
pkg_check_modules(DISPLAYSERVER_X11 REQUIRED IMPORTED_TARGET
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
)
add_definitions(-D GLX_GLXEXT_PROTOTYPES)
target_link_libraries(displayserver_X11
${DISPLAYSERVER_X11_PKGCONFIG_LIBRARIES}
lg_common
PkgConfig::DISPLAYSERVER_X11
lg_common
)
target_include_directories(displayserver_X11
PRIVATE
src
${DISPLAYSERVER_X11_PKGCONFIG_INCLUDE_DIRS}
PRIVATE
src
)

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 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 (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -22,16 +22,23 @@
#define _H_X11DS_ATOMS_
#define DEF_ATOMS() \
DEF_ATOM(_NET_SUPPORTING_WM_CHECK, True) \
DEF_ATOM(_NET_SUPPORTED, True) \
DEF_ATOM(_NET_WM_NAME, True) \
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 (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -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);
@@ -153,7 +151,13 @@ static void x11CBReplyFn(void * opaque, LG_ClipboardData type,
static void x11CBSelectionRequest(const XSelectionRequestEvent e)
{
XEvent * s = (XEvent *)malloc(sizeof(XEvent));
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 (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -28,7 +28,7 @@
bool x11CBEventThread(const XEvent xe);
bool x11CBInit();
bool x11CBInit(void);
void x11CBNotice(LG_ClipboardData type);
void x11CBRelease(void);
void x11CBRequest(LG_ClipboardData type);

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -21,22 +21,62 @@
#ifndef _H_X11DS_X11_
#define _H_X11DS_X11_
#include <stdatomic.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xfixes.h>
#include <GL/glx.h>
#include "interface/displayserver.h"
#include "common/thread.h"
#include "common/types.h"
enum Modifiers
{
MOD_CTRL_LEFT = 0,
MOD_CTRL_RIGHT,
MOD_SHIFT_LEFT,
MOD_SHIFT_RIGHT,
MOD_ALT_LEFT,
MOD_ALT_RIGHT,
MOD_SUPER_LEFT,
MOD_SUPER_RIGHT,
};
#define MOD_COUNT (MOD_SUPER_RIGHT + 1)
struct X11DSState
{
Display * display;
Window window;
XVisualInfo * visual;
int xinputOp;
int minKeycode, maxKeycode;
int symsPerKeycode;
KeySym * keysyms;
//Extended Window Manager Hints
//ref: https://specifications.freedesktop.org/wm-spec/latest/
bool ewmhSupport;
bool ewmhHasFocusEvent;
_Atomic(uint64_t) lastWMEvent;
bool invalidateAll;
int xpresentOp;
bool jitRender;
_Atomic(uint64_t) presentMsc, presentUst;
uint32_t presentSerial;
Pixmap presentPixmap;
XserverRegion presentRegion;
LGEvent * frameEvent;
LGThread * eventThread;
int xinputOp;
int pointerDev;
int keyboardDev;
int xValuator;
@@ -51,8 +91,11 @@ struct X11DSState
struct Rect rect;
struct Border border;
Cursor blankCursor;
Cursor squareCursor;
Cursor cursors[LG_POINTER_COUNT];
XIM xim;
XIC xic;
bool modifiers[MOD_COUNT];
// XFixes vars
int eventBase;

View File

@@ -1,41 +0,0 @@
cmake_minimum_required(VERSION 3.0)
project(fonts LANGUAGES C)
set(FONT_H "${CMAKE_BINARY_DIR}/include/dynamic/fonts.h")
set(FONT_C "${CMAKE_BINARY_DIR}/src/fonts.c")
file(WRITE ${FONT_H} "#include \"interface/font.h\"\n\n")
file(APPEND ${FONT_H} "extern LG_Font * LG_Fonts[];\n\n")
file(WRITE ${FONT_C} "#include \"interface/font.h\"\n\n")
file(APPEND ${FONT_C} "#include <stddef.h>\n\n")
set(FONTS "_")
set(FONTS_LINK "_")
function(add_font name)
set(FONTS "${FONTS};${name}" PARENT_SCOPE)
set(FONTS_LINK "${FONTS_LINK};font_${name}" PARENT_SCOPE)
add_subdirectory(${name})
endfunction()
# Add/remove fonts here!
add_font(freetype)
list(REMOVE_AT FONTS 0)
list(REMOVE_AT FONTS_LINK 0)
list(LENGTH FONTS FONT_COUNT)
file(APPEND ${FONT_H} "#define LG_FONT_COUNT ${FONT_COUNT}\n")
foreach(font ${FONTS})
file(APPEND ${FONT_C} "extern LG_Font LGF_${font};\n")
endforeach()
file(APPEND ${FONT_C} "\nconst LG_Font * LG_Fonts[] =\n{\n")
foreach(font ${FONTS})
file(APPEND ${FONT_C} " &LGF_${font},\n")
endforeach()
file(APPEND ${FONT_C} " NULL\n};\n\n")
add_library(fonts STATIC ${FONT_C})
target_link_libraries(fonts ${FONTS_LINK})

View File

@@ -1,26 +0,0 @@
cmake_minimum_required(VERSION 3.0)
project(font_freetype LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(FONT_FREETYPE_PKGCONFIG REQUIRED
freetype2
fontconfig
)
add_library(font_freetype STATIC
src/freetype.c
)
target_link_libraries(font_freetype
${FONT_FREETYPE_PKGCONFIG_LIBRARIES}
lg_common
)
target_include_directories(font_freetype
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
src
${FONT_FREETYPE_PKGCONFIG_INCLUDE_DIRS}
)

View File

@@ -1,325 +0,0 @@
/**
* Looking Glass
* Copyright (C) 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 <stdlib.h>
#include <stdbool.h>
#include "interface/font.h"
#include "common/debug.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include <fontconfig/fontconfig.h>
static int g_initCount = 0;
static FcConfig * g_fontConfig = NULL;
static FT_Library g_ft;
struct Inst
{
FT_Face face;
unsigned int height;
};
static bool lgf_freetype_create(LG_FontObj * opaque, const char * font_name, unsigned int size)
{
bool ret = false;
if (g_initCount == 0)
{
if (FT_Init_FreeType(&g_ft))
{
DEBUG_ERROR("FT_Init_FreeType Failed");
goto fail;
}
g_fontConfig = FcInitLoadConfigAndFonts();
if (!g_fontConfig)
{
DEBUG_ERROR("FcInitLoadConfigAndFonts Failed");
goto fail_init;
}
}
*opaque = malloc(sizeof(struct Inst));
if (!*opaque)
{
DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct Inst));
goto fail_config;
}
memset(*opaque, 0, sizeof(struct Inst));
struct Inst * this = (struct Inst *)*opaque;
if (!font_name)
font_name = "FreeMono";
FcPattern * pat = FcNameParse((const FcChar8*)font_name);
if (!pat)
{
DEBUG_ERROR("FCNameParse failed");
goto fail_opaque;
}
FcConfigSubstitute(g_fontConfig, pat, FcMatchPattern);
FcDefaultSubstitute(pat);
FcResult result;
FcChar8 * file = NULL;
FcPattern * match = FcFontMatch(g_fontConfig, pat, &result);
if (!match)
{
DEBUG_ERROR("FcFontMatch Failed");
goto fail_parse;
}
if (FcPatternGetString(match, FC_FILE, 0, &file) == FcResultMatch)
{
if (FT_New_Face(g_ft, (char *) file, 0, &this->face))
{
DEBUG_ERROR("FT_New_Face Failed");
goto fail_match;
}
if (FT_Select_Charmap(this->face, ft_encoding_unicode))
{
DEBUG_ERROR("FT_Select_Charmap failed");
FT_Done_Face(this->face);
goto fail_match;
}
FT_Set_Pixel_Sizes(this->face, 0, size);
this->height = size;
}
else
{
DEBUG_ERROR("Failed to locate the requested font: %s", font_name);
goto fail_match;
}
++g_initCount;
ret = true;
fail_match:
FcPatternDestroy(match);
fail_parse:
FcPatternDestroy(pat);
if (ret)
return true;
fail_opaque:
free(this);
*opaque = NULL;
fail_config:
if (g_initCount == 0)
{
FcConfigDestroy(g_fontConfig);
g_fontConfig = NULL;
}
fail_init:
if (g_initCount == 0)
FT_Done_FreeType(g_ft);
fail:
return false;
}
static void lgf_freetype_destroy(LG_FontObj opaque)
{
struct Inst * this = (struct Inst *)opaque;
if (this->face)
FT_Done_Face(this->face);
free(this);
if (--g_initCount == 0)
{
FcConfigDestroy(g_fontConfig);
g_fontConfig = NULL;
FT_Done_FreeType(g_ft);
}
}
// A very simple UTF-8 decoder that assumes the input is valid.
static unsigned int utf8_decode(const char * str)
{
const unsigned char * ptr = (const unsigned char *) str;
// Handle the 4 byte case: 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx.
if ((*ptr & 0xf8) == 0xf0)
return (ptr[0] & 0x07) << 18 | (ptr[1] & 0x3f) << 12 | (ptr[2] & 0x3f) << 6 | (ptr[3] & 0x3f);
// Handle the 3 byte case: 1110 xxxx 10xx xxxx 10xx xxxx.
else if ((*ptr & 0xf0) == 0xe0)
return (ptr[0] & 0x0f) << 12 | (ptr[1] & 0x3f) << 6 | (ptr[2] & 0x3f);
// Handle the 2 byte case: 110x xxxx 10xx xxxx.
else if ((*ptr & 0xe0) == 0xc0)
return (ptr[0] & 0x1f) << 6 | (ptr[1] & 0x3f);
// Everything else is the 1 byte case.
else
return *ptr;
}
// Return the length of the current UTF-8 character. Assumes the input is valid.
static unsigned int utf8_advance(const char * str)
{
const unsigned char * ptr = (const unsigned char *) str;
// 4 byte case starts with 1111 0xxx.
if ((*ptr & 0xf8) == 0xf0)
return 4;
// 3 byte case starts with 1110 xxxx.
else if ((*ptr & 0xf0) == 0xe0)
return 3;
// 2 byte case starts with 110x xxxx.
else if ((*ptr & 0xe0) == 0xc0)
return 2;
// Everything else is the 1 byte case.
else
return 1;
}
static LG_FontBitmap * lgf_freetype_render(LG_FontObj opaque, unsigned int fg_color, const char * text)
{
struct Inst * this = (struct Inst *)opaque;
int width = 0;
int row = 0;
int rowWidth = 0;
int topAscend = 0;
int bottomDescend = 0;
for (const char * ptr = text; *ptr; ptr += utf8_advance(ptr))
{
unsigned int ch = utf8_decode(ptr);
if (ch == '\n')
{
if (!ptr[1])
break;
if (rowWidth > width)
width = rowWidth;
rowWidth = bottomDescend = 0;
++row;
}
else if (FT_Load_Char(this->face, ch, FT_LOAD_RENDER))
{
DEBUG_ERROR("Failed to load character: %c", *ptr);
return NULL;
}
else
{
FT_GlyphSlot glyph = this->face->glyph;
rowWidth += glyph->advance.x / 64;
int descend = glyph->bitmap.rows - glyph->bitmap_top;
if (descend > bottomDescend)
bottomDescend = descend;
if (row == 0 && glyph->bitmap_top > topAscend)
topAscend = glyph->bitmap_top;
}
}
if (rowWidth > width)
width = rowWidth;
int height = topAscend + this->height * row + bottomDescend;
uint32_t * pixels = calloc(width * height, sizeof(uint32_t));
if (!pixels)
{
DEBUG_ERROR("Failed to allocate memory for font pixels");
return NULL;
}
int baseline = topAscend;
int x = 0;
unsigned int r = (fg_color & 0xff000000) >> 24;
unsigned int g = (fg_color & 0x00ff0000) >> 16;
unsigned int b = (fg_color & 0x0000ff00) >> 8;
uint32_t color = (r << 0) | (g << 8) | (b << 16);
for (const char * ptr = text; *ptr; ptr += utf8_advance(ptr))
{
unsigned int ch = utf8_decode(ptr);
if (ch == '\n')
{
baseline += this->height;
x = 0;
}
else if (FT_Load_Char(this->face, ch, FT_LOAD_RENDER))
{
DEBUG_ERROR("Failed to load character: U+%x", ch);
return NULL;
}
else
{
FT_GlyphSlot glyph = this->face->glyph;
int start = baseline - glyph->bitmap_top;
int pitch = width;
if (glyph->bitmap.pitch < 0)
{
start += glyph->bitmap.rows - 1;
pitch = -pitch;
}
for (int i = 0; i < glyph->bitmap.rows; ++i)
for (int j = 0; j < glyph->bitmap.width; ++j)
pixels[(start + i) * pitch + x + j + glyph->bitmap_left] = color |
(uint32_t)glyph->bitmap.buffer[i * glyph->bitmap.pitch + j] << 24;
x += glyph->advance.x / 64;
}
}
LG_FontBitmap * out = malloc(sizeof(LG_FontBitmap));
if (!out)
{
free(pixels);
DEBUG_ERROR("Failed to allocate memory for font bitmap");
return NULL;
}
out->width = width;
out->height = height;
out->bpp = 4;
out->pixels = (uint8_t *) pixels;
return out;
}
static void lgf_freetype_release(LG_FontObj opaque, LG_FontBitmap * font)
{
LG_FontBitmap * bitmap = (LG_FontBitmap *)font;
free(bitmap->pixels);
free(bitmap);
}
struct LG_Font LGF_freetype =
{
.name = "freetype",
.create = lgf_freetype_create,
.destroy = lgf_freetype_destroy,
.render = lgf_freetype_render,
.release = lgf_freetype_release
};

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -24,8 +24,10 @@
#include <stdbool.h>
#include <linux/input.h>
#include "common/ringbuffer.h"
#include "common/types.h"
#include "interface/displayserver.h"
#include "interface/overlay.h"
typedef enum LG_MsgAlert
{
@@ -41,9 +43,11 @@ bool app_inputEnabled(void);
bool app_isCaptureMode(void);
bool app_isCaptureOnlyMode(void);
bool app_isFormatValid(void);
bool app_isOverlayMode(void);
void app_updateCursorPos(double x, double y);
void app_updateWindowPos(int x, int y);
void app_handleResizeEvent(int w, int h, double scale, const struct Border border);
void app_invalidateWindow(bool full);
void app_handleMouseRelative(double normx, double normy,
double rawx, double rawy);
@@ -53,8 +57,12 @@ void app_resyncMouseBasic(void);
void app_handleButtonPress(int button);
void app_handleButtonRelease(int button);
void app_handleKeyPress(int scancode);
void app_handleKeyRelease(int scancode);
void app_handleWheelMotion(double motion);
void app_handleKeyboardTyped(const char * typed);
void app_handleKeyPress(int scancode, int charcode);
void app_handleKeyRelease(int scancode, int charcode);
void app_handleKeyboardModifiers(bool ctrl, bool shift, bool alt, bool super);
void app_handleKeyboardLEDs(bool numLock, bool capsLock, bool scrollLock);
void app_handleEnterEvent(bool entered);
void app_handleFocusEvent(bool focused);
void app_handleCloseEvent(void);
@@ -78,6 +86,44 @@ void app_glSetSwapInterval(int interval);
void app_glSwapBuffers(void);
#endif
#define MAX_OVERLAY_RECTS 10
void app_registerOverlay(const struct LG_OverlayOps * ops, const void * params);
void app_initOverlays(void);
void app_setOverlay(bool enable);
bool app_overlayNeedsRender(void);
/**
* render the overlay
* returns:
* -1 for full output damage
* 0 for no overlay
* >0 number of rects written into rects
*/
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, 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);
void app_overlayConfigRegisterTab(const char * title,
void (*callback)(void * udata, int * id), void * udata);
void app_clipboardRelease(void);
void app_clipboardNotifyTypes(const LG_ClipboardData types[], int count);
void app_clipboardNotifySize(const LG_ClipboardData type, size_t size);
@@ -92,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
@@ -116,14 +175,20 @@ void app_releaseKeybind(KeybindHandle * handle);
*/
void app_releaseAllKeybinds(void);
/**
* Changes whether the help message is displayed or not.
*/
void app_showHelp(bool show);
bool app_guestIsLinux(void);
bool app_guestIsWindows(void);
bool app_guestIsOSX(void);
bool app_guestIsBSD(void);
bool app_guestIsOther(void);
/**
* Changes whether the FPS is displayed or not.
* Enable/disable the LG display
*/
void app_showFPS(bool showFPS);
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 (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -23,22 +23,23 @@
#ifdef ENABLE_EGL
#include <EGL/egl.h>
#include <GL/gl.h>
typedef EGLDisplay (*eglGetPlatformDisplayEXT_t)(EGLenum platform,
void *native_display, const EGLint *attrib_list);
typedef void (*eglSwapBuffersWithDamageKHR_t)(EGLDisplay dpy,
EGLSurface surface, const EGLint *rects, EGLint n_rects);
typedef void (*glEGLImageTargetTexture2DOES_t)(GLenum target,
GLeglImageOES image);
#include <EGL/eglext.h>
#undef GL_KHR_debug
#include <GLES3/gl3.h>
#include <GLES2/gl2ext.h>
struct EGLDynProcs
{
eglGetPlatformDisplayEXT_t eglGetPlatformDisplay;
eglGetPlatformDisplayEXT_t eglGetPlatformDisplayEXT;
eglSwapBuffersWithDamageKHR_t eglSwapBuffersWithDamageKHR;
eglSwapBuffersWithDamageKHR_t eglSwapBuffersWithDamageEXT;
glEGLImageTargetTexture2DOES_t glEGLImageTargetTexture2DOES;
PFNEGLGETPLATFORMDISPLAYPROC eglGetPlatformDisplay;
PFNEGLGETPLATFORMDISPLAYPROC eglGetPlatformDisplayEXT;
PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC eglSwapBuffersWithDamageKHR;
PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC eglSwapBuffersWithDamageEXT;
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallback;
PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallbackKHR;
PFNGLBUFFERSTORAGEEXTPROC glBufferStorageEXT;
PFNEGLCREATEIMAGEPROC eglCreateImage;
PFNEGLDESTROYIMAGEPROC eglDestroyImage;
};
extern struct EGLDynProcs g_egl_dynProcs;

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -18,18 +18,24 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#ifndef _H_LG_GLUTIL_
#define _H_LG_GLUTIL_
#include <stdbool.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "interface/font.h"
#include "common/types.h"
typedef struct EGL_FPS EGL_FPS;
struct SwapWithDamageData
{
bool init;
PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC func;
};
bool egl_fps_init(EGL_FPS ** fps, const LG_Font * font, LG_FontObj fontObj);
void egl_fps_free(EGL_FPS ** fps);
void swapWithDamageInit(struct SwapWithDamageData * data, EGLDisplay display);
void swapWithDamageDisable(struct SwapWithDamageData * data);
void swapWithDamage(struct SwapWithDamageData * data, EGLDisplay display, EGLSurface surface,
const struct Rect * damage, int count);
void egl_fps_set_display(EGL_FPS * fps, bool display);
void egl_fps_set_font (EGL_FPS * fps, LG_Font * fontObj);
void egl_fps_update(EGL_FPS * fps, const float avgUPS, const float avgFPS);
void egl_fps_render(EGL_FPS * fps, const float scaleX, const float scaleY);
#endif

View File

@@ -0,0 +1,50 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_LG_GL_DYNPROCS_
#define _H_LG_GL_DYNPROCS_
#ifdef ENABLE_OPENGL
#include <GL/gl.h>
#include <GL/glext.h>
struct GLDynProcs
{
PFNGLGENBUFFERSPROC glGenBuffers;
PFNGLBINDBUFFERPROC glBindBuffer;
PFNGLBUFFERDATAPROC glBufferData;
PFNGLBUFFERSUBDATAPROC glBufferSubData;
PFNGLDELETEBUFFERSPROC glDeleteBuffers;
PFNGLISSYNCPROC glIsSync;
PFNGLFENCESYNCPROC glFenceSync;
PFNGLCLIENTWAITSYNCPROC glClientWaitSync;
PFNGLDELETESYNCPROC glDeleteSync;
PFNGLGENERATEMIPMAPPROC glGenerateMipmap;
};
extern struct GLDynProcs g_gl_dynProcs;
void gl_dynProcsInit(void);
#else
#define gl_dynProcsInit(...)
#endif
#endif // _H_LG_GL_DYNPROCS_

View File

@@ -0,0 +1,89 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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 (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -24,6 +24,7 @@
#include <stdbool.h>
#include <EGL/egl.h>
#include "common/types.h"
#include "common/debug.h"
typedef enum LG_ClipboardData
{
@@ -62,6 +63,24 @@ enum LG_DSWarpSupport
LG_DS_WARP_SCREEN,
};
typedef enum LG_DSPointer
{
LG_POINTER_NONE = 0,
LG_POINTER_SQUARE,
LG_POINTER_ARROW,
LG_POINTER_INPUT,
LG_POINTER_MOVE,
LG_POINTER_RESIZE_NS,
LG_POINTER_RESIZE_EW,
LG_POINTER_RESIZE_NESW,
LG_POINTER_RESIZE_NWSE,
LG_POINTER_HAND,
LG_POINTER_NOT_ALLOWED,
}
LG_DSPointer;
#define LG_POINTER_COUNT (LG_POINTER_NOT_ALLOWED + 1)
typedef struct LG_DSInitParams
{
const char * title;
@@ -74,6 +93,10 @@ typedef struct LG_DSInitParams
// if true the renderer requires an OpenGL context
bool opengl;
// x11 needs to know if this is in use so we can decide to setup for
// presentation times
bool jitRender;
}
LG_DSInitParams;
@@ -83,8 +106,12 @@ typedef void (* LG_ClipboardReplyFn)(void * opaque, const LG_ClipboardData type,
typedef struct LG_DSGLContext
* LG_DSGLContext;
typedef struct LGEvent LGEvent;
struct LG_DisplayServerOps
{
const char * name;
/* called before options are parsed, useful for registering options */
void (*setup)(void);
@@ -98,13 +125,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
@@ -129,17 +156,30 @@ struct LG_DisplayServerOps
void (*glSwapBuffers)(void);
#endif
/* Waits for a good time to render the next frame in time for the next vblank.
* This is optional and a display server may choose to not implement it.
*
* return true to force the frame to be rendered, this is used by X11 for
* calibration */
bool (*waitFrame)(void);
/* This must be called when waitFrame returns, but no frame is actually rendered. */
void (*skipFrame)(void);
/* This is used to interrupt waitFrame. */
void (*stopWaitFrame)(void);
/* dm specific cursor implementations */
void (*guestPointerUpdated)(double x, double y, double localX, double localY);
void (*showPointer)(bool show);
void (*grabKeyboard)();
void (*ungrabKeyboard)();
void (*setPointer)(LG_DSPointer pointer);
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);
/* exiting = true if the warp is to leave the window */
void (*warpPointer)(int x, int y, bool exiting);
@@ -147,14 +187,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);
@@ -173,49 +216,50 @@ struct LG_DisplayServerOps
};
#ifdef ENABLE_EGL
#define ASSERT_EGL_FN(x) assert(x);
#define ASSERT_EGL_FN(x) DEBUG_ASSERT(x)
#else
#define ASSERT_EGL_FN(x)
#endif
#ifdef ENABLE_OPENGL
#define ASSERT_OPENGL_FN(x) assert(x)
#define ASSERT_OPENGL_FN(x) DEBUG_ASSERT(x)
#else
#define ASSERT_OPENGL_FN(x)
#endif
#define ASSERT_LG_DS_VALID(x) \
assert((x)->setup ); \
assert((x)->probe ); \
assert((x)->earlyInit ); \
assert((x)->init ); \
assert((x)->startup ); \
assert((x)->shutdown ); \
assert((x)->free ); \
assert((x)->getProp ); \
ASSERT_EGL_FN((x)->getEGLDisplay ); \
ASSERT_EGL_FN((x)->getEGLNativeWindow ); \
ASSERT_EGL_FN((x)->eglSwapBuffers ); \
DEBUG_ASSERT((x)->setup ); \
DEBUG_ASSERT((x)->probe ); \
DEBUG_ASSERT((x)->earlyInit); \
DEBUG_ASSERT((x)->init ); \
DEBUG_ASSERT((x)->startup ); \
DEBUG_ASSERT((x)->shutdown ); \
DEBUG_ASSERT((x)->free ); \
DEBUG_ASSERT((x)->getProp ); \
ASSERT_EGL_FN((x)->getEGLDisplay ); \
ASSERT_EGL_FN((x)->getEGLNativeWindow); \
ASSERT_EGL_FN((x)->eglSwapBuffers ); \
ASSERT_OPENGL_FN((x)->glCreateContext ); \
ASSERT_OPENGL_FN((x)->glDeleteContext ); \
ASSERT_OPENGL_FN((x)->glMakeCurrent ); \
ASSERT_OPENGL_FN((x)->glSetSwapInterval); \
ASSERT_OPENGL_FN((x)->glSwapBuffers ); \
assert((x)->guestPointerUpdated); \
assert((x)->showPointer ); \
assert((x)->grabPointer ); \
assert((x)->ungrabPointer ); \
assert((x)->capturePointer ); \
assert((x)->uncapturePointer ); \
assert((x)->warpPointer ); \
assert((x)->realignPointer ); \
assert((x)->isValidPointerPos ); \
assert((x)->inhibitIdle ); \
assert((x)->uninhibitIdle ); \
assert((x)->wait ); \
assert((x)->setWindowSize ); \
assert((x)->setFullscreen ); \
assert((x)->getFullscreen ); \
assert((x)->minimize );
DEBUG_ASSERT(!(x)->waitFrame == !(x)->stopWaitFrame); \
DEBUG_ASSERT((x)->guestPointerUpdated); \
DEBUG_ASSERT((x)->setPointer ); \
DEBUG_ASSERT((x)->grabPointer ); \
DEBUG_ASSERT((x)->ungrabPointer ); \
DEBUG_ASSERT((x)->capturePointer ); \
DEBUG_ASSERT((x)->uncapturePointer ); \
DEBUG_ASSERT((x)->warpPointer ); \
DEBUG_ASSERT((x)->realignPointer ); \
DEBUG_ASSERT((x)->isValidPointerPos ); \
DEBUG_ASSERT((x)->inhibitIdle ); \
DEBUG_ASSERT((x)->uninhibitIdle ); \
DEBUG_ASSERT((x)->wait ); \
DEBUG_ASSERT((x)->setWindowSize ); \
DEBUG_ASSERT((x)->setFullscreen ); \
DEBUG_ASSERT((x)->getFullscreen ); \
DEBUG_ASSERT((x)->minimize );
#endif

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 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,86 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_I_OVERLAY_
#define _H_I_OVERLAY_
#include <stdbool.h>
#include "common/types.h"
#define TICK_RATE 25
struct LG_OverlayOps
{
/* internal name of the overlay for debugging */
const char * name;
/* called very early to allow for option registration, optional */
void (*earlyInit)(void);
/* called when the overlay is registered */
bool (*init)(void ** udata, const void * params);
/* final free */
void (*free)(void * udata);
/* return true if realtime rendering is required when in jitRender mode
* 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
* mode.
*
* `windowRects` is an array of window rects that were rendered using screen
* coordinates. Will be `NULL` if the information is not required.
*
* `maxRects` is the length of `windowRects`, or 0 if `windowRects` is `NULL`
*
* returns the number of rects written to `windowRects`, or -1 if there is not
* enough room left.
*/
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 */
};
#define ASSERT_LG_OVERLAY_VALID(x) \
DEBUG_ASSERT((x)->name ); \
DEBUG_ASSERT((x)->init ); \
DEBUG_ASSERT((x)->free ); \
DEBUG_ASSERT((x)->render);
#endif

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -27,26 +27,26 @@
#include "common/framebuffer.h"
#define IS_LG_RENDERER_VALID(x) \
((x)->get_name && \
(x)->create && \
(x)->initialize && \
(x)->deinitialize && \
(x)->on_restart && \
(x)->on_resize && \
(x)->on_mouse_shape && \
(x)->on_mouse_event && \
(x)->on_alert && \
(x)->on_help && \
(x)->on_show_fps && \
(x)->render_startup && \
(x)->render && \
(x)->update_fps)
((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
{
// TTF_Font * font;
// TTF_Font * alertFont;
bool quickSplash;
bool quickSplash;
}
LG_RendererParams;
@@ -71,9 +71,11 @@ LG_RendererRotate;
typedef struct LG_RendererFormat
{
FrameType type; // frame type
unsigned int width; // image width
unsigned int height; // image height
FrameType type; // frame type
unsigned int screenWidth; // actual width of the host
unsigned int screenHeight; // actual height of the host
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)
@@ -99,49 +101,103 @@ typedef enum LG_RendererCursor
}
LG_RendererCursor;
// returns the friendly name of the renderer
typedef const char * (* LG_RendererGetName)();
typedef struct LG_Renderer LG_Renderer;
// called pre-creation to allow the renderer to register any options it might have
typedef void (* LG_RendererSetup)();
typedef struct LG_RendererOps
{
/* returns the friendly name of the renderer */
const char * (*getName)(void);
typedef bool (* LG_RendererCreate )(void ** opaque, const LG_RendererParams params, bool * needsOpenGL);
typedef bool (* LG_RendererInitialize )(void * opaque);
typedef void (* LG_RendererDeInitialize )(void * opaque);
typedef bool (* LG_RendererSupports )(void * opaque, LG_RendererSupport support);
typedef void (* LG_RendererOnRestart )(void * opaque);
typedef void (* LG_RendererOnResize )(void * opaque, const int width, const int height, const double scale, const LG_RendererRect destRect, LG_RendererRotate rotate);
typedef bool (* LG_RendererOnMouseShape )(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data);
typedef bool (* LG_RendererOnMouseEvent )(void * opaque, const bool visible , const int x, const int y);
typedef bool (* LG_RendererOnFrameFormat)(void * opaque, const LG_RendererFormat format, bool useDMA);
typedef bool (* LG_RendererOnFrame )(void * opaque, const FrameBuffer * frame, int dmaFD);
typedef void (* LG_RendererOnAlert )(void * opaque, const LG_MsgAlert alert, const char * message, bool ** closeFlag);
typedef void (* LG_RendererOnHelp )(void * opaque, const char * message);
typedef void (* LG_RendererOnShowFPS )(void * opaque, bool showFPS);
typedef bool (* LG_RendererRenderStartup)(void * opaque);
typedef bool (* LG_RendererRender )(void * opaque, LG_RendererRotate rotate);
typedef void (* LG_RendererUpdateFPS )(void * opaque, const float avgUPS, const float avgFPS);
/* called pre-creation to allow the renderer to register any options it may
* have */
void (*setup)(void);
/* creates an instance of the renderer
* Context: lg_run */
bool (*create)(LG_Renderer ** renderer, const LG_RendererParams params,
bool * needsOpenGL);
/* initializes the renderer for use
* Context: lg_run */
bool (*initialize)(LG_Renderer * renderer);
/* deinitializes & frees the renderer
* Context: lg_run & renderThread */
void (*deinitialize)(LG_Renderer * renderer);
/* returns true if the specified feature is supported
* Context: renderThread */
bool (*supports)(LG_Renderer * renderer, LG_RendererSupport support);
/* called when the renderer is to reset it's state
* Context: lg_run & frameThread */
void (*onRestart)(LG_Renderer * renderer);
/* called when the viewport has been resized
* Context: renderThrtead */
void (*onResize)(LG_Renderer * renderer, const int width, const int height,
const double scale, const LG_RendererRect destRect,
LG_RendererRotate rotate);
/* called when the mouse shape has changed
* Context: cursorThread */
bool (*onMouseShape)(LG_Renderer * renderer, const LG_RendererCursor cursor,
const int width, const int height, const int pitch, const uint8_t * data);
/* called when the mouse has moved or changed visibillity
* Context: cursorThread */
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 */
bool (*onFrameFormat)(LG_Renderer * renderer,
const LG_RendererFormat format);
/* called when there is a new frame
* Context: frameThread */
bool (*onFrame)(LG_Renderer * renderer, const FrameBuffer * frame, int dmaFD,
const FrameDamageRect * damage, int damageCount);
/* called when the rederer is to startup
* Context: renderThread */
bool (*renderStartup)(LG_Renderer * renderer, bool useDMA);
/* 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;
typedef struct LG_Renderer
{
LG_RendererGetName get_name;
LG_RendererSetup setup;
LG_RendererCreate create;
LG_RendererInitialize initialize;
LG_RendererDeInitialize deinitialize;
LG_RendererSupports supports;
LG_RendererOnRestart on_restart;
LG_RendererOnResize on_resize;
LG_RendererOnMouseShape on_mouse_shape;
LG_RendererOnMouseEvent on_mouse_event;
LG_RendererOnFrameFormat on_frame_format;
LG_RendererOnFrame on_frame;
LG_RendererOnAlert on_alert;
LG_RendererOnHelp on_help;
LG_RendererOnShowFPS on_show_fps;
LG_RendererRenderStartup render_startup;
LG_RendererRender render;
LG_RendererUpdateFPS update_fps;
LG_RendererOps ops;
}
LG_Renderer;

View File

@@ -1,33 +0,0 @@
/**
* Looking Glass
* Copyright (C) 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 <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);
void ll_reset (struct ll * list);
bool ll_walk (struct ll * list, void ** data);

View File

@@ -0,0 +1,49 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_LG_OVERLAY_UTILS_
#define _H_LG_OVERLAY_UTILS_
#include <stdbool.h>
#include "common/types.h"
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 (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -24,6 +24,7 @@
#include <stdlib.h>
#include <stdbool.h>
#include "common/types.h"
#include "common/util.h"
// reads the specified file into a new buffer
// the callee must free the buffer
@@ -42,4 +43,8 @@ static inline double util_clamp(double x, double min, double max)
return x;
}
bool util_initUIFonts(void);
void util_freeUIFonts(void);
char * util_getUIFont(const char * fontName);
#endif

View File

@@ -5,7 +5,7 @@ set(RENDERER_H "${CMAKE_BINARY_DIR}/include/dynamic/renderers.h")
set(RENDERER_C "${CMAKE_BINARY_DIR}/src/renderers.c")
file(WRITE ${RENDERER_H} "#include \"interface/renderer.h\"\n\n")
file(APPEND ${RENDERER_H} "extern LG_Renderer * LG_Renderers[];\n\n")
file(APPEND ${RENDERER_H} "extern LG_RendererOps * LG_Renderers[];\n\n")
file(WRITE ${RENDERER_C} "#include \"interface/renderer.h\"\n\n")
file(APPEND ${RENDERER_C} "#include <stddef.h>\n\n")
@@ -33,10 +33,10 @@ list(LENGTH RENDERERS RENDERER_COUNT)
file(APPEND ${RENDERER_H} "#define LG_RENDERER_COUNT ${RENDERER_COUNT}\n")
foreach(renderer ${RENDERERS})
file(APPEND ${RENDERER_C} "extern LG_Renderer LGR_${renderer};\n")
file(APPEND ${RENDERER_C} "extern LG_RendererOps LGR_${renderer};\n")
endforeach()
file(APPEND ${RENDERER_C} "\nconst LG_Renderer * LG_Renderers[] =\n{\n")
file(APPEND ${RENDERER_C} "\nconst LG_RendererOps * LG_Renderers[] =\n{\n")
foreach(renderer ${RENDERERS})
file(APPEND ${RENDERER_C} " &LGR_${renderer},\n")
endforeach()

View File

@@ -1,72 +1,116 @@
cmake_minimum_required(VERSION 3.0)
project(renderer_EGL LANGUAGES C)
project(renderer_EGL LANGUAGES C CXX)
find_package(PkgConfig)
pkg_check_modules(RENDERER_EGL_PKGCONFIG REQUIRED
egl
gl
pkg_check_modules(RENDERER_EGL REQUIRED IMPORTED_TARGET
egl
gl
)
pkg_check_modules(RENDERER_EGL_OPT_PKGCONFIG
wayland-egl
pkg_check_modules(RENDERER_EGL_OPT IMPORTED_TARGET
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}).")
else()
message(STATUS "Using awk: ${AWK}")
endif()
include(MakeObject)
make_object(
EGL_SHADER
shader/desktop.vert
shader/desktop_rgb.frag
shader/cursor.vert
shader/cursor_rgb.frag
shader/cursor_mono.frag
shader/fps.vert
shader/fps.frag
shader/fps_bg.frag
shader/help.vert
shader/help.frag
shader/help_bg.frag
shader/alert.vert
shader/alert.frag
shader/alert_bg.frag
shader/splash_bg.vert
shader/splash_bg.frag
shader/splash_logo.vert
shader/splash_logo.frag
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()
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)
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/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
)
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.c
model.c
desktop.c
cursor.c
fps.c
help.c
draw.c
splash.c
alert.c
${EGL_SHADER_OBJS}
"${EGL_SHADER_INCS}/desktop_rgb.def.h"
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_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
${RENDERER_EGL_PKGCONFIG_LIBRARIES}
${RENDERER_EGL_OPT_PKGCONFIG_LIBRARIES}
lg_common
fonts
PkgConfig::RENDERER_EGL
lg_common
cimgui
)
if(RENDERER_EGL_OPT_FOUND)
target_link_libraries(renderer_EGL
PkgConfig::RENDERER_EGL_OPT
)
endif()
target_include_directories(renderer_EGL
PRIVATE
src
${EGL_SHADER_INCS}
${RENDERER_EGL_PKGCONFIG_INCLUDE_DIRS}
${RENDERER_EGL_OPT_PKGCONFIG_INCLUDE_DIRS}
PRIVATE
src
${EGL_SHADER_INCS}
)

View File

@@ -1,227 +0,0 @@
/**
* Looking Glass
* Copyright (C) 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 "alert.h"
#include "common/debug.h"
#include "common/locking.h"
#include "texture.h"
#include "shader.h"
#include "model.h"
#include <stdlib.h>
#include <string.h>
// these headers are auto generated by cmake
#include "alert.vert.h"
#include "alert.frag.h"
#include "alert_bg.frag.h"
struct EGL_Alert
{
const LG_Font * font;
LG_FontObj fontObj;
EGL_Texture * texture;
EGL_Shader * shader;
EGL_Shader * shaderBG;
EGL_Model * model;
LG_Lock lock;
bool update;
LG_FontBitmap * bmp;
bool ready;
float width , height ;
float bgWidth, bgHeight;
float r, g, b, a;
// uniforms
GLint uScreen , uSize;
GLint uScreenBG, uSizeBG, uColorBG;
};
bool egl_alert_init(EGL_Alert ** alert, const LG_Font * font, LG_FontObj fontObj)
{
*alert = (EGL_Alert *)malloc(sizeof(EGL_Alert));
if (!*alert)
{
DEBUG_ERROR("Failed to malloc EGL_Alert");
return false;
}
memset(*alert, 0, sizeof(EGL_Alert));
(*alert)->font = font;
(*alert)->fontObj = fontObj;
LG_LOCK_INIT((*alert)->lock);
if (!egl_texture_init(&(*alert)->texture, NULL))
{
DEBUG_ERROR("Failed to initialize the alert texture");
return false;
}
if (!egl_shader_init(&(*alert)->shader))
{
DEBUG_ERROR("Failed to initialize the alert shader");
return false;
}
if (!egl_shader_init(&(*alert)->shaderBG))
{
DEBUG_ERROR("Failed to initialize the alert bg shader");
return false;
}
if (!egl_shader_compile((*alert)->shader,
b_shader_alert_vert, b_shader_alert_vert_size,
b_shader_alert_frag, b_shader_alert_frag_size))
{
DEBUG_ERROR("Failed to compile the alert shader");
return false;
}
if (!egl_shader_compile((*alert)->shaderBG,
b_shader_alert_vert , b_shader_alert_vert_size,
b_shader_alert_bg_frag, b_shader_alert_bg_frag_size))
{
DEBUG_ERROR("Failed to compile the alert shader");
return false;
}
(*alert)->uSize = egl_shader_get_uniform_location((*alert)->shader , "size" );
(*alert)->uScreen = egl_shader_get_uniform_location((*alert)->shader , "screen");
(*alert)->uSizeBG = egl_shader_get_uniform_location((*alert)->shaderBG, "size" );
(*alert)->uScreenBG = egl_shader_get_uniform_location((*alert)->shaderBG, "screen");
(*alert)->uColorBG = egl_shader_get_uniform_location((*alert)->shaderBG, "color" );
if (!egl_model_init(&(*alert)->model))
{
DEBUG_ERROR("Failed to initialize the alert model");
return false;
}
egl_model_set_default((*alert)->model);
egl_model_set_texture((*alert)->model, (*alert)->texture);
return true;
}
void egl_alert_free(EGL_Alert ** alert)
{
if (!*alert)
return;
egl_texture_free(&(*alert)->texture );
egl_shader_free (&(*alert)->shader );
egl_shader_free (&(*alert)->shaderBG);
egl_model_free (&(*alert)->model );
free(*alert);
*alert = NULL;
}
void egl_alert_set_color(EGL_Alert * alert, const uint32_t color)
{
alert->r = (1.0f / 0xff) * ((color >> 24) & 0xFF);
alert->g = (1.0f / 0xff) * ((color >> 16) & 0xFF);
alert->b = (1.0f / 0xff) * ((color >> 8) & 0xFF);
alert->a = (1.0f / 0xff) * ((color >> 0) & 0xFF);
}
void egl_alert_set_text (EGL_Alert * alert, const char * str)
{
LG_LOCK(alert->lock);
alert->bmp = alert->font->render(alert->fontObj, 0xffffff00, str);
if (!alert->bmp)
{
alert->update = false;
LG_UNLOCK(alert->lock);
DEBUG_ERROR("Failed to render alert text");
return;
}
alert->update = true;
LG_UNLOCK(alert->lock);
}
void egl_alert_set_font(EGL_Alert * alert, LG_Font * fontObj)
{
LG_LOCK(alert->lock);
alert->fontObj = fontObj;
LG_UNLOCK(alert->lock);
}
void egl_alert_render(EGL_Alert * alert, const float scaleX, const float scaleY)
{
if (alert->update)
{
LG_LOCK(alert->lock);
egl_texture_setup(
alert->texture,
EGL_PF_BGRA,
alert->bmp->width ,
alert->bmp->height,
alert->bmp->width * alert->bmp->bpp,
false,
false
);
egl_texture_update(alert->texture, alert->bmp->pixels);
alert->width = alert->bgWidth = alert->bmp->width;
alert->height = alert->bgHeight = alert->bmp->height;
if (alert->bgWidth < 200)
alert->bgWidth = 200;
alert->bgHeight += 4;
alert->ready = true;
alert->font->release(alert->fontObj, alert->bmp);
alert->update = false;
alert->bmp = NULL;
LG_UNLOCK(alert->lock);
}
if (!alert->ready)
return;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// render the background first
egl_shader_use(alert->shaderBG);
glUniform2f(alert->uScreenBG, scaleX , scaleY );
glUniform2i(alert->uSizeBG , alert->bgWidth, alert->bgHeight);
glUniform4f(alert->uColorBG , alert->r, alert->g, alert->b, alert->a);
egl_model_render(alert->model);
// render the texture over the background
egl_shader_use(alert->shader);
glUniform2f(alert->uScreen, scaleX , scaleY );
glUniform2i(alert->uSize , alert->width, alert->height);
egl_model_render(alert->model);
glDisable(GL_BLEND);
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -26,7 +26,9 @@
#include "texture.h"
#include "shader.h"
#include "model.h"
#include "util.h"
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>
@@ -40,10 +42,21 @@ struct CursorTex
struct EGL_Texture * texture;
struct EGL_Shader * shader;
GLuint uMousePos;
GLuint uScale;
GLuint uRotate;
GLuint uCBMode;
};
struct CursorPos
{
float x, y;
};
struct CursorSize
{
float w, h;
};
struct EGL_Cursor
{
LG_Lock lock;
@@ -57,103 +70,110 @@ struct EGL_Cursor
// cursor state
bool visible;
float x, y, w, h;
LG_RendererRotate rotate;
int cbMode;
_Atomic(struct CursorPos) pos;
_Atomic(struct CursorPos) hs;
_Atomic(struct CursorSize) size;
_Atomic(float) scale;
struct CursorTex norm;
struct CursorTex mono;
struct EGL_Model * model;
};
static bool egl_cursor_tex_init(struct CursorTex * t,
static bool cursorTexInit(struct CursorTex * t,
const char * vertex_code , size_t vertex_size,
const char * fragment_code, size_t fragment_size)
{
if (!egl_texture_init(&t->texture, NULL))
if (!egl_textureInit(&t->texture, NULL, EGL_TEXTYPE_BUFFER))
{
DEBUG_ERROR("Failed to initialize the cursor texture");
return false;
}
if (!egl_shader_init(&t->shader))
if (!egl_shaderInit(&t->shader))
{
DEBUG_ERROR("Failed to initialize the cursor shader");
return false;
}
if (!egl_shader_compile(t->shader,
if (!egl_shaderCompile(t->shader,
vertex_code, vertex_size, fragment_code, fragment_size))
{
DEBUG_ERROR("Failed to compile the cursor shader");
return false;
}
t->uMousePos = egl_shader_get_uniform_location(t->shader, "mouse" );
t->uRotate = egl_shader_get_uniform_location(t->shader, "rotate");
t->uCBMode = egl_shader_get_uniform_location(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 egl_cursor_tex_uniforms(EGL_Cursor * cursor, struct CursorTex * t, bool mono)
static inline void setCursorTexUniforms(EGL_Cursor * cursor,
struct CursorTex * t, bool mono, float x, float y,
float w, float h, float scale)
{
if (mono)
{
glUniform4f(t->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h / 2);
glUniform1i(t->uRotate , cursor->rotate);
glUniform1i(t->uCBMode , cursor->cbMode);
}
else
{
glUniform4f(t->uMousePos, cursor->x, cursor->y, cursor->w, cursor->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 egl_cursor_tex_free(struct CursorTex * t)
static void cursorTexFree(struct CursorTex * t)
{
egl_texture_free(&t->texture);
egl_shader_free (&t->shader );
egl_textureFree(&t->texture);
egl_shaderFree (&t->shader );
};
bool egl_cursor_init(EGL_Cursor ** cursor)
bool egl_cursorInit(EGL_Cursor ** cursor)
{
*cursor = (EGL_Cursor *)malloc(sizeof(EGL_Cursor));
*cursor = malloc(sizeof(**cursor));
if (!*cursor)
{
DEBUG_ERROR("Failed to malloc EGL_Cursor");
return false;
}
memset(*cursor, 0, sizeof(EGL_Cursor));
memset(*cursor, 0, sizeof(**cursor));
LG_LOCK_INIT((*cursor)->lock);
if (!egl_cursor_tex_init(&(*cursor)->norm,
if (!cursorTexInit(&(*cursor)->norm,
b_shader_cursor_vert , b_shader_cursor_vert_size,
b_shader_cursor_rgb_frag, b_shader_cursor_rgb_frag_size))
return false;
if (!egl_cursor_tex_init(&(*cursor)->mono,
if (!cursorTexInit(&(*cursor)->mono,
b_shader_cursor_vert , b_shader_cursor_vert_size,
b_shader_cursor_mono_frag, b_shader_cursor_mono_frag_size))
return false;
if (!egl_model_init(&(*cursor)->model))
if (!egl_modelInit(&(*cursor)->model))
{
DEBUG_ERROR("Failed to initialize the cursor model");
return false;
}
egl_model_set_default((*cursor)->model);
egl_modelSetDefault((*cursor)->model, true);
(*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)->hs , hs );
atomic_init(&(*cursor)->size , size);
atomic_init(&(*cursor)->scale, 1.0f);
return true;
}
void egl_cursor_free(EGL_Cursor ** cursor)
void egl_cursorFree(EGL_Cursor ** cursor)
{
if (!*cursor)
return;
@@ -162,15 +182,15 @@ void egl_cursor_free(EGL_Cursor ** cursor)
if ((*cursor)->data)
free((*cursor)->data);
egl_cursor_tex_free(&(*cursor)->norm);
egl_cursor_tex_free(&(*cursor)->mono);
egl_model_free(&(*cursor)->model);
cursorTexFree(&(*cursor)->norm);
cursorTexFree(&(*cursor)->mono);
egl_modelFree(&(*cursor)->model);
free(*cursor);
*cursor = NULL;
}
bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type,
bool egl_cursorSetShape(EGL_Cursor * cursor, const LG_RendererCursor type,
const int width, const int height, const int stride, const uint8_t * data)
{
LG_LOCK(cursor->lock);
@@ -186,7 +206,7 @@ bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type,
if (cursor->data)
free(cursor->data);
cursor->data = (uint8_t *)malloc(size);
cursor->data = malloc(size);
if (!cursor->data)
{
DEBUG_ERROR("Failed to malloc buffer for cursor shape");
@@ -203,33 +223,32 @@ bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type,
return true;
}
void egl_cursor_set_size(EGL_Cursor * cursor, const float w, const float h)
void egl_cursorSetSize(EGL_Cursor * cursor, const float w, const float h)
{
cursor->w = w;
cursor->h = h;
struct CursorSize size = { .w = w, .h = h };
atomic_store(&cursor->size, size);
}
void egl_cursor_set_state(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;
cursor->x = x;
cursor->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_cursor_get_state(EGL_Cursor * cursor, int width, int height) {
return (struct CursorState) {
.visible = cursor->visible,
.rect.x = (cursor->x * width + width) / 2,
.rect.y = (-cursor->y * height + height) / 2 - cursor->h * height,
.rect.w = cursor->w * width + 2,
.rect.h = cursor->h * height + 2,
};
}
void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate)
struct CursorState egl_cursorRender(EGL_Cursor * cursor,
LG_RendererRotate rotate, int width, int height)
{
if (!cursor->visible)
return;
return (struct CursorState) { .visible = false };
if (cursor->update)
{
@@ -241,22 +260,43 @@ void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate)
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, sizeof(xor[0]));
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true);
}
// fall through
case LG_CURSOR_COLOR:
{
egl_texture_setup(cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->stride, false, false);
egl_texture_update(cursor->norm.texture, data);
egl_model_set_texture(cursor->model, cursor->norm.texture);
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
cursor->width, cursor->height, 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);
@@ -265,14 +305,17 @@ void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate)
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_texture_setup (cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false, false);
egl_texture_setup (cursor->mono.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false, false);
egl_texture_update(cursor->norm.texture, (uint8_t *)and);
egl_texture_update(cursor->mono.texture, (uint8_t *)xor);
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
cursor->width, cursor->height, sizeof(and[0]));
egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA,
cursor->width, cursor->height, sizeof(xor[0]));
egl_textureUpdate(cursor->norm.texture, (uint8_t *)and, true);
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true);
break;
}
}
@@ -281,42 +324,108 @@ void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate)
cursor->rotate = rotate;
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,
};
switch (rotate)
{
case LG_ROTATE_0:
state.rect.x = (pos.x * width + width) / 2;
state.rect.y = (-pos.y * height + height) / 2 - size.h * height;
state.rect.w = size.w * width + 3;
state.rect.h = size.h * height + 3;
break;
case LG_ROTATE_90:
state.rect.x = (-pos.y * width + width) / 2 - size.h * width;
state.rect.y = (-pos.x * height + height) / 2 - size.w * height;
state.rect.w = size.h * width + 3;
state.rect.h = size.w * height + 3;
break;
case LG_ROTATE_180:
state.rect.x = (-pos.x * width + width) / 2 - size.w * width;
state.rect.y = (pos.y * height + height) / 2;
state.rect.w = size.w * width + 3;
state.rect.h = size.h * height + 3;
break;
case LG_ROTATE_270:
state.rect.x = (pos.y * width + width) / 2;
state.rect.y = (pos.x * height + height) / 2;
state.rect.w = size.h * width + 3;
state.rect.h = size.w * height + 3;
break;
default:
DEBUG_UNREACHABLE();
}
state.rect.x = max(0, state.rect.x - 1);
state.rect.y = max(0, state.rect.y - 1);
glEnable(GL_BLEND);
switch(cursor->type)
{
case LG_CURSOR_MONOCHROME:
{
egl_shader_use(cursor->norm.shader);
egl_cursor_tex_uniforms(cursor, &cursor->norm, true);;
egl_shaderUse(cursor->norm.shader);
setCursorTexUniforms(cursor, &cursor->norm, true, pos.x, pos.y,
size.w, size.h, scale);
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
egl_model_set_texture(cursor->model, cursor->norm.texture);
egl_model_render(cursor->model);
egl_modelSetTexture(cursor->model, cursor->norm.texture);
egl_modelRender(cursor->model);
egl_shader_use(cursor->mono.shader);
egl_cursor_tex_uniforms(cursor, &cursor->mono, true);;
egl_shaderUse(cursor->mono.shader);
setCursorTexUniforms(cursor, &cursor->mono, true, pos.x, pos.y,
size.w, size.h, scale);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
egl_model_set_texture(cursor->model, cursor->mono.texture);
egl_model_render(cursor->model);
break;
}
case LG_CURSOR_COLOR:
{
egl_shader_use(cursor->norm.shader);
egl_cursor_tex_uniforms(cursor, &cursor->norm, false);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
egl_model_render(cursor->model);
egl_modelSetTexture(cursor->model, cursor->mono.texture);
egl_modelRender(cursor->model);
break;
}
case LG_CURSOR_MASKED_COLOR:
{
egl_shader_use(cursor->mono.shader);
egl_cursor_tex_uniforms(cursor, &cursor->mono, false);
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_model_render(cursor->model);
egl_modelSetTexture(cursor->model, cursor->mono.texture);
egl_modelRender(cursor->model);
break;
}
case LG_CURSOR_COLOR:
{
egl_shaderUse(cursor->norm.shader);
setCursorTexUniforms(cursor, &cursor->norm, false, pos.x, pos.y,
size.w, size.h, scale);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
egl_modelSetTexture(cursor->model, cursor->norm.texture);
egl_modelRender(cursor->model);
break;
}
}
glDisable(GL_BLEND);
return state;
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -22,6 +22,7 @@
#include <stdbool.h>
#include "egl.h"
#include "interface/renderer.h"
typedef struct EGL_Cursor EGL_Cursor;
@@ -31,10 +32,10 @@ struct CursorState {
struct Rect rect;
};
bool egl_cursor_init(EGL_Cursor ** cursor);
void egl_cursor_free(EGL_Cursor ** cursor);
bool egl_cursorInit(EGL_Cursor ** cursor);
void egl_cursorFree(EGL_Cursor ** cursor);
bool egl_cursor_set_shape(
bool egl_cursorSetShape(
EGL_Cursor * cursor,
const LG_RendererCursor type,
const int width,
@@ -42,11 +43,12 @@ bool egl_cursor_set_shape(
const int stride,
const uint8_t * data);
void egl_cursor_set_size(EGL_Cursor * cursor, const float x, const float y);
void egl_cursorSetSize(EGL_Cursor * cursor, const float x, const float y);
void egl_cursor_set_state(EGL_Cursor * cursor, const bool visible,
const float x, const float y);
void egl_cursorSetScale(EGL_Cursor * cursor, const float scale);
struct CursorState egl_cursor_get_state(EGL_Cursor * cursor, int width, int height);
void egl_cursorSetState(EGL_Cursor * cursor, const bool visible,
const float x, const float y, const float hx, const float hy);
void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate);
struct CursorState egl_cursorRender(EGL_Cursor * cursor,
LG_RendererRotate rotate, int width, int height);

View File

@@ -0,0 +1,156 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "damage.h"
#include "common/debug.h"
#include "common/KVMFR.h"
#include "common/locking.h"
#include "app.h"
#include "desktop_rects.h"
#include "shader.h"
#include "cimgui.h"
#include <stdlib.h>
#include <string.h>
// these headers are auto generated by cmake
#include "damage.vert.h"
#include "damage.frag.h"
struct EGL_Damage
{
EGL_Shader * shader;
EGL_DesktopRects * mesh;
GLfloat transform[6];
bool show;
int width , height;
float translateX, translateY;
float scaleX , scaleY;
LG_RendererRotate rotate;
// uniforms
GLint uTransform;
};
void egl_damageConfigUI(EGL_Damage * damage)
{
igCheckbox("Show damage overlay", &damage->show);
}
bool egl_damageInit(EGL_Damage ** damage)
{
*damage = malloc(sizeof(**damage));
if (!*damage)
{
DEBUG_ERROR("Failed to malloc EGL_Damage");
return false;
}
memset(*damage, 0, sizeof(EGL_Damage));
if (!egl_shaderInit(&(*damage)->shader))
{
DEBUG_ERROR("Failed to initialize the damage shader");
return false;
}
if (!egl_shaderCompile((*damage)->shader,
b_shader_damage_vert, b_shader_damage_vert_size,
b_shader_damage_frag, b_shader_damage_frag_size))
{
DEBUG_ERROR("Failed to compile the damage shader");
return false;
}
if (!egl_desktopRectsInit(&(*damage)->mesh, KVMFR_MAX_DAMAGE_RECTS))
{
DEBUG_ERROR("Failed to initialize the mesh");
return false;
}
(*damage)->uTransform = egl_shaderGetUniform((*damage)->shader, "transform");
return true;
}
void egl_damageFree(EGL_Damage ** damage)
{
if (!*damage)
return;
egl_desktopRectsFree(&(*damage)->mesh);
egl_shaderFree(&(*damage)->shader);
free(*damage);
*damage = NULL;
}
static void update_matrix(EGL_Damage * damage)
{
egl_desktopRectsMatrix(damage->transform, damage->width, damage->height,
damage->translateX, damage->translateY, damage->scaleX, damage->scaleY, damage->rotate);
}
void egl_damageSetup(EGL_Damage * damage, int width, int height)
{
damage->width = width;
damage->height = height;
update_matrix(damage);
}
void egl_damageResize(EGL_Damage * damage, float translateX, float translateY,
float scaleX, float scaleY)
{
damage->translateX = translateX;
damage->translateY = translateY;
damage->scaleX = scaleX;
damage->scaleY = scaleY;
update_matrix(damage);
}
bool egl_damageRender(EGL_Damage * damage, LG_RendererRotate rotate, const struct DesktopDamage * data)
{
if (!damage->show)
return false;
if (rotate != damage->rotate)
{
damage->rotate = rotate;
update_matrix(damage);
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
egl_shaderUse(damage->shader);
glUniformMatrix3x2fv(damage->uTransform, 1, GL_FALSE, damage->transform);
if (data && data->count != 0)
egl_desktopRectsUpdate(damage->mesh, (const struct DamageRects *) data,
damage->width, damage->height);
egl_desktopRectsRender(damage->mesh);
glDisable(GL_BLEND);
return true;
}

View File

@@ -0,0 +1,44 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <stdbool.h>
#include "common/KVMFR.h"
#include "interface/renderer.h"
#include "desktop_rects.h"
struct DesktopDamage
{
int count;
FrameDamageRect rects[KVMFR_MAX_DAMAGE_RECTS];
};
typedef struct EGL_Damage EGL_Damage;
bool egl_damageInit(EGL_Damage ** damage);
void egl_damageFree(EGL_Damage ** damage);
void egl_damageConfigUI(EGL_Damage * damage);
void egl_damageSetup(EGL_Damage * damage, int width, int height);
void egl_damageResize(EGL_Damage * damage, float translateX, float translateY,
float scaleX, float scaleY);
bool egl_damageRender(EGL_Damage * damage, LG_RendererRotate rotate,
const struct DesktopDamage * data);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -22,11 +22,13 @@
#include "common/debug.h"
#include "common/option.h"
#include "common/locking.h"
#include "common/array.h"
#include "app.h"
#include "texture.h"
#include "shader.h"
#include "model.h"
#include "desktop_rects.h"
#include "cimgui.h"
#include <stdlib.h>
#include <string.h>
@@ -36,31 +38,36 @@
#include "desktop_rgb.frag.h"
#include "desktop_rgb.def.h"
#include "postprocess.h"
#include "filters.h"
struct DesktopShader
{
EGL_Shader * shader;
GLint uDesktopPos;
GLint uTransform;
GLint uDesktopSize;
GLint uRotate;
GLint uScaleAlgo;
GLint uNV, uNVGain;
GLint uNVGain;
GLint uCBMode;
};
struct EGL_Desktop
{
EGL * egl;
EGLDisplay * display;
EGL_Texture * texture;
struct DesktopShader * shader; // the active shader
EGL_Model * model;
struct DesktopShader shader;
EGL_DesktopRects * mesh;
CountedBuffer * matrix;
// internals
int width, height;
LG_RendererRotate rotate;
// shader instances
struct DesktopShader shader_generic;
bool useSpice;
int spiceWidth, spiceHeight;
EGL_Texture * spiceTexture;
// scale algorithm
int scaleAlgo;
@@ -71,59 +78,65 @@ struct EGL_Desktop
// colorblind mode
int cbMode;
bool useDMA;
LG_RendererFormat format;
EGL_PostProcess * pp;
_Atomic(bool) processFrame;
};
// forwards
void egl_desktop_toggle_nv(int key, void * opaque);
void egl_desktop_toggle_scale_algo(int key, void * opaque);
void toggleNV(int key, void * opaque);
static bool egl_init_desktop_shader(
static bool egl_initDesktopShader(
struct DesktopShader * shader,
const char * vertex_code , size_t vertex_size,
const char * fragment_code, size_t fragment_size
)
{
if (!egl_shader_init(&shader->shader))
if (!egl_shaderInit(&shader->shader))
return false;
if (!egl_shader_compile(shader->shader,
if (!egl_shaderCompile(shader->shader,
vertex_code , vertex_size,
fragment_code, fragment_size))
{
return false;
}
shader->uDesktopPos = egl_shader_get_uniform_location(shader->shader, "position" );
shader->uDesktopSize = egl_shader_get_uniform_location(shader->shader, "size" );
shader->uRotate = egl_shader_get_uniform_location(shader->shader, "rotate" );
shader->uScaleAlgo = egl_shader_get_uniform_location(shader->shader, "scaleAlgo");
shader->uNV = egl_shader_get_uniform_location(shader->shader, "nv" );
shader->uNVGain = egl_shader_get_uniform_location(shader->shader, "nvGain" );
shader->uCBMode = egl_shader_get_uniform_location(shader->shader, "cbMode" );
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" );
return true;
}
bool egl_desktop_init(EGL_Desktop ** desktop, EGLDisplay * display)
bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
bool useDMA, int maxRects)
{
*desktop = (EGL_Desktop *)malloc(sizeof(EGL_Desktop));
if (!*desktop)
EGL_Desktop * desktop = calloc(1, sizeof(EGL_Desktop));
if (!desktop)
{
DEBUG_ERROR("Failed to malloc EGL_Desktop");
return false;
}
*desktop_ = desktop;
memset(*desktop, 0, sizeof(EGL_Desktop));
(*desktop)->display = display;
desktop->egl = egl;
desktop->display = display;
if (!egl_texture_init(&(*desktop)->texture, display))
if (!egl_textureInit(&desktop->texture, display,
useDMA ? EGL_TEXTYPE_DMABUF : EGL_TEXTYPE_FRAMEBUFFER))
{
DEBUG_ERROR("Failed to initialize the desktop texture");
return false;
}
if (!egl_init_desktop_shader(
&(*desktop)->shader_generic,
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))
{
@@ -131,27 +144,41 @@ bool egl_desktop_init(EGL_Desktop ** desktop, EGLDisplay * display)
return false;
}
if (!egl_model_init(&(*desktop)->model))
if (!egl_desktopRectsInit(&desktop->mesh, maxRects))
{
DEBUG_ERROR("Failed to initialize the desktop model");
DEBUG_ERROR("Failed to initialize the desktop mesh");
return false;
}
egl_model_set_default((*desktop)->model);
egl_model_set_texture((*desktop)->model, (*desktop)->texture);
desktop->matrix = countedBufferNew(6 * sizeof(GLfloat));
if (!desktop->matrix)
{
DEBUG_ERROR("Failed to allocate the desktop matrix buffer");
return false;
}
app_registerKeybind(KEY_N, egl_desktop_toggle_nv, *desktop, "Toggle night vision mode");
app_registerKeybind(KEY_S, egl_desktop_toggle_scale_algo, *desktop, "Toggle scale algorithm");
app_registerKeybind(0, 'N', toggleNV, desktop,
"Toggle night vision mode");
(*desktop)->nvMax = option_get_int("egl", "nvGainMax");
(*desktop)->nvGain = option_get_int("egl", "nvGain" );
(*desktop)->cbMode = option_get_int("egl", "cbMode" );
(*desktop)->scaleAlgo = option_get_int("egl", "scale" );
desktop->nvMax = option_get_int("egl", "nvGainMax");
desktop->nvGain = option_get_int("egl", "nvGain" );
desktop->cbMode = option_get_int("egl", "cbMode" );
desktop->scaleAlgo = option_get_int("egl", "scale" );
desktop->useDMA = useDMA;
if (!egl_postProcessInit(&desktop->pp))
{
DEBUG_ERROR("Failed to initialize the post process manager");
return false;
}
egl_postProcessAdd(desktop->pp, &egl_filterDownscaleOps);
egl_postProcessAdd(desktop->pp, &egl_filterFFXCASOps );
egl_postProcessAdd(desktop->pp, &egl_filterFFXFSR1Ops );
return true;
}
void egl_desktop_toggle_nv(int key, void * opaque)
void toggleNV(int key, void * opaque)
{
EGL_Desktop * desktop = (EGL_Desktop *)opaque;
if (desktop->nvGain++ == desktop->nvMax)
@@ -160,24 +187,11 @@ void egl_desktop_toggle_nv(int key, void * opaque)
if (desktop->nvGain == 0) app_alert(LG_ALERT_INFO, "NV Disabled");
else if (desktop->nvGain == 1) app_alert(LG_ALERT_INFO, "NV Enabled");
else app_alert(LG_ALERT_INFO, "NV Gain + %d", desktop->nvGain - 1);
app_invalidateWindow(true);
}
static const char * egl_desktop_scale_algo_name(int algorithm)
{
switch (algorithm)
{
case EGL_SCALE_AUTO:
return "Automatic (downscale: linear, upscale: nearest)";
case EGL_SCALE_NEAREST:
return "Nearest";
case EGL_SCALE_LINEAR:
return "Linear";
default:
return "(unknown)";
}
}
bool egl_desktop_scale_validate(struct Option * opt, const char ** error)
bool egl_desktopScaleValidate(struct Option * opt, const char ** error)
{
if (opt->value.x_int >= 0 && opt->value.x_int < EGL_SCALE_MAX)
return true;
@@ -186,52 +200,84 @@ bool egl_desktop_scale_validate(struct Option * opt, const char ** error)
return false;
}
void egl_desktop_toggle_scale_algo(int key, void * opaque)
{
EGL_Desktop * desktop = (EGL_Desktop *)opaque;
if (++desktop->scaleAlgo == EGL_SCALE_MAX)
desktop->scaleAlgo = 0;
app_alert(LG_ALERT_INFO, "Scale Algorithm %d: %s", desktop->scaleAlgo,
egl_desktop_scale_algo_name(desktop->scaleAlgo));
}
void egl_desktop_free(EGL_Desktop ** desktop)
void egl_desktopFree(EGL_Desktop ** desktop)
{
if (!*desktop)
return;
egl_texture_free(&(*desktop)->texture );
egl_shader_free (&(*desktop)->shader_generic.shader);
egl_model_free (&(*desktop)->model );
egl_textureFree (&(*desktop)->texture );
egl_textureFree (&(*desktop)->spiceTexture );
egl_shaderFree (&(*desktop)->shader.shader);
egl_desktopRectsFree(&(*desktop)->mesh );
countedBufferRelease(&(*desktop)->matrix );
egl_postProcessFree(&(*desktop)->pp);
free(*desktop);
*desktop = NULL;
}
bool egl_desktop_setup(EGL_Desktop * desktop, const LG_RendererFormat format, bool useDMA)
static const char * algorithmNames[EGL_SCALE_MAX] = {
[EGL_SCALE_AUTO] = "Automatic (downscale: linear, upscale: nearest)",
[EGL_SCALE_NEAREST] = "Nearest",
[EGL_SCALE_LINEAR] = "Linear",
};
void egl_desktopConfigUI(EGL_Desktop * desktop)
{
igText("Scale algorithm:");
igPushItemWidth(igGetWindowWidth() - igGetStyle()->WindowPadding.x * 2);
if (igBeginCombo("##scale", algorithmNames[desktop->scaleAlgo], 0))
{
for (int i = 0; i < EGL_SCALE_MAX; ++i)
{
bool selected = i == desktop->scaleAlgo;
if (igSelectable_Bool(algorithmNames[i], selected, 0,
(ImVec2) { 0.0f, 0.0f }))
desktop->scaleAlgo = i;
if (selected)
igSetItemDefaultFocus();
}
igEndCombo();
}
igPopItemWidth();
igText("Night vision mode:");
igSameLine(0.0f, -1.0f);
igPushItemWidth(igGetWindowWidth() - igGetCursorPosX() - igGetStyle()->WindowPadding.x);
const char * format;
switch (desktop->nvGain)
{
case 0: format = "off"; break;
case 1: format = "on"; break;
default: format = "gain: %d";
}
igSliderInt("##nvgain", &desktop->nvGain, 0, desktop->nvMax, format, 0);
igPopItemWidth();
}
bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
{
memcpy(&desktop->format, &format, sizeof(LG_RendererFormat));
enum EGL_PixelFormat pixFmt;
switch(format.type)
{
case FRAME_TYPE_BGRA:
pixFmt = EGL_PF_BGRA;
desktop->shader = &desktop->shader_generic;
break;
case FRAME_TYPE_RGBA:
pixFmt = EGL_PF_RGBA;
desktop->shader = &desktop->shader_generic;
break;
case FRAME_TYPE_RGBA10:
pixFmt = EGL_PF_RGBA10;
desktop->shader = &desktop->shader_generic;
break;
case FRAME_TYPE_RGBA16F:
pixFmt = EGL_PF_RGBA16F;
desktop->shader = &desktop->shader_generic;
break;
default:
@@ -239,17 +285,15 @@ bool egl_desktop_setup(EGL_Desktop * desktop, const LG_RendererFormat format, bo
return false;
}
desktop->width = format.width;
desktop->height = format.height;
desktop->width = format.frameWidth;
desktop->height = format.frameHeight;
if (!egl_texture_setup(
if (!egl_textureSetup(
desktop->texture,
pixFmt,
format.width,
format.height,
format.pitch,
true, // streaming texture
useDMA
format.frameWidth,
format.frameHeight,
format.pitch
))
{
DEBUG_ERROR("Failed to setup the desktop texture");
@@ -259,48 +303,128 @@ bool egl_desktop_setup(EGL_Desktop * desktop, const LG_RendererFormat format, bo
return true;
}
bool egl_desktop_update(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd)
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd,
const FrameDamageRect * damageRects, int damageRectsCount)
{
if (dmaFd >= 0)
if (desktop->useDMA && dmaFd >= 0)
{
if (!egl_texture_update_from_dma(desktop->texture, frame, dmaFd))
if (egl_textureUpdateFromDMA(desktop->texture, frame, dmaFd))
{
atomic_store(&desktop->processFrame, true);
return true;
}
DEBUG_WARN("DMA update failed, disabling DMABUF imports");
const char * vendor = (const char *)glGetString(GL_VENDOR);
if (strstr(vendor, "NVIDIA"))
{
DEBUG_WARN("NVIDIA's DMABUF support is incomplete, please direct your complaints to NVIDIA");
DEBUG_WARN("This is not a bug in Looking Glass");
}
desktop->useDMA = false;
const char * gl_exts = (const char *)glGetString(GL_EXTENSIONS);
if (!util_hasGLExt(gl_exts, "GL_EXT_buffer_storage"))
{
DEBUG_ERROR("GL_EXT_buffer_storage is needed to use EGL backend");
return false;
}
else
{
if (!egl_texture_update_from_frame(desktop->texture, frame))
}
egl_textureFree(&desktop->texture);
if (!egl_textureInit(&desktop->texture, desktop->display,
EGL_TEXTYPE_FRAMEBUFFER))
{
DEBUG_ERROR("Failed to initialize the desktop texture");
return false;
}
if (!egl_desktopSetup(desktop, desktop->format))
return false;
}
if (egl_textureUpdateFromFrame(desktop->texture, frame,
damageRects, damageRectsCount))
{
atomic_store(&desktop->processFrame, true);
return true;
}
return false;
}
void egl_desktopResize(EGL_Desktop * desktop, int width, int height)
{
atomic_store(&desktop->processFrame, true);
}
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)
{
EGL_Texture * tex;
int width, height;
if (desktop->useSpice)
{
tex = desktop->spiceTexture;
width = desktop->spiceWidth;
height = desktop->spiceHeight;
}
else
{
tex = desktop->texture;
width = desktop->width;
height = desktop->height;
}
if (outputWidth == 0 && outputHeight == 0)
DEBUG_FATAL("outputWidth || outputHeight == 0");
enum EGL_TexStatus status;
if ((status = egl_texture_process(desktop->texture)) != EGL_TEX_STATUS_OK)
if ((status = egl_textureProcess(tex)) != EGL_TEX_STATUS_OK)
{
if (status != EGL_TEX_STATUS_NOTREADY)
DEBUG_ERROR("Failed to process the desktop texture");
}
return true;
}
bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y,
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
LG_RendererRotate rotate)
{
if (!desktop->shader)
return false;
int scaleAlgo = EGL_SCALE_NEAREST;
egl_desktopRectsMatrix((float *)desktop->matrix->data,
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, tex, desktop->mesh,
width, height, outputWidth, outputHeight);
unsigned int finalSizeX, finalSizeY;
GLuint texture = egl_postProcessGetOutput(desktop->pp,
&finalSizeX, &finalSizeY);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
egl_resetViewport(desktop->egl);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, tex->sampler);
if (finalSizeX > width || finalSizeY > height)
scaleType = EGL_DESKTOP_DOWNSCALE;
switch (desktop->scaleAlgo)
{
case EGL_SCALE_AUTO:
switch (scaleType)
{
case EGL_DESKTOP_NOSCALE:
case EGL_DESKTOP_UPSCALE:
scaleAlgo = EGL_SCALE_NEAREST;
break;
case EGL_DESKTOP_NOSCALE:
case EGL_DESKTOP_DOWNSCALE:
scaleAlgo = EGL_SCALE_LINEAR;
break;
@@ -311,22 +435,96 @@ bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y,
scaleAlgo = desktop->scaleAlgo;
}
const struct DesktopShader * shader = desktop->shader;
egl_shader_use(shader->shader);
glUniform4f(shader->uDesktopPos , x, y, scaleX, scaleY);
glUniform1i(shader->uRotate , rotate);
glUniform1i(shader->uScaleAlgo , scaleAlgo);
glUniform2f(shader->uDesktopSize, desktop->width, desktop->height);
if (desktop->nvGain)
const struct DesktopShader * shader = &desktop->shader;
EGL_Uniform uniforms[] =
{
glUniform1i(shader->uNV, 1);
glUniform1f(shader->uNVGain, (float)desktop->nvGain);
}
else
glUniform1i(shader->uNV, 0);
{
.type = EGL_UNIFORM_TYPE_1I,
.location = shader->uScaleAlgo,
.i = { scaleAlgo },
},
{
.type = EGL_UNIFORM_TYPE_2F,
.location = shader->uDesktopSize,
.f = { width, height },
},
{
.type = EGL_UNIFORM_TYPE_M3x2FV,
.location = shader->uTransform,
.m.transpose = GL_FALSE,
.m.v = desktop->matrix
},
{
.type = EGL_UNIFORM_TYPE_1F,
.location = shader->uNVGain,
.f = { (float)desktop->nvGain }
},
{
.type = EGL_UNIFORM_TYPE_1I,
.location = shader->uCBMode,
.f = { desktop->cbMode }
}
};
glUniform1i(shader->uCBMode, desktop->cbMode);
egl_model_render(desktop->model);
egl_shaderSetUniforms(shader->shader, uniforms, ARRAY_LENGTH(uniforms));
egl_shaderUse(shader->shader);
egl_desktopRectsRender(desktop->mesh);
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 * 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(; y < height; ++y)
egl_textureUpdateRect(desktop->spiceTexture,
x, y, width, 1, 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, stride, data, topDown);
atomic_store(&desktop->processFrame, true);
}
void egl_desktopSpiceShow(EGL_Desktop * desktop, bool show)
{
desktop->useSpice = show;
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -22,7 +22,8 @@
#include <stdbool.h>
#include "interface/renderer.h"
#include "egl.h"
#include "desktop_rects.h"
typedef struct EGL_Desktop EGL_Desktop;
@@ -34,13 +35,25 @@ enum EGL_DesktopScaleType
};
struct Option;
bool egl_desktop_scale_validate(struct Option * opt, const char ** error);
bool egl_desktopScaleValidate(struct Option * opt, const char ** error);
bool egl_desktop_init(EGL_Desktop ** desktop, EGLDisplay * display);
void egl_desktop_free(EGL_Desktop ** desktop);
bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop, EGLDisplay * display,
bool useDMA, int maxRects);
void egl_desktopFree(EGL_Desktop ** desktop);
bool egl_desktop_setup (EGL_Desktop * desktop, const LG_RendererFormat format, bool useDMA);
bool egl_desktop_update(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd);
bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y,
void egl_desktopConfigUI(EGL_Desktop * desktop);
bool egl_desktopSetup (EGL_Desktop * desktop, const LG_RendererFormat format);
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd,
const FrameDamageRect * damageRects, int damageRectsCount);
void egl_desktopResize(EGL_Desktop * desktop, int width, int height);
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);
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

@@ -0,0 +1,305 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "desktop_rects.h"
#include "common/debug.h"
#include "common/KVMFR.h"
#include "common/locking.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <GLES3/gl3.h>
#include "util.h"
struct EGL_DesktopRects
{
GLfloat * lastVertices;
int lastVerticesCount;
int lastVerticesSize;
GLuint buffers[2];
GLuint vao;
int count;
int maxCount;
};
bool egl_desktopRectsInit(EGL_DesktopRects ** rects_, int maxCount)
{
EGL_DesktopRects * rects = malloc(sizeof(*rects));
if (!rects)
{
DEBUG_ERROR("Failed to malloc EGL_DesktopRects");
return false;
}
*rects_ = rects;
memset(rects, 0, sizeof(*rects));
glGenVertexArrays(1, &rects->vao);
glBindVertexArray(rects->vao);
glGenBuffers(2, rects->buffers);
glBindBuffer(GL_ARRAY_BUFFER, rects->buffers[0]);
glBufferData(GL_ARRAY_BUFFER, maxCount * 8 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER, 0);
GLushort indices[maxCount * 6];
for (int i = 0; i < maxCount; ++i)
{
indices[6 * i + 0] = 4 * i + 0;
indices[6 * i + 1] = 4 * i + 1;
indices[6 * i + 2] = 4 * i + 2;
indices[6 * i + 3] = 4 * i + 0;
indices[6 * i + 4] = 4 * i + 2;
indices[6 * i + 5] = 4 * i + 3;
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rects->buffers[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof indices, indices, GL_STATIC_DRAW);
glBindVertexArray(0);
rects->count = 0;
rects->maxCount = maxCount;
return true;
}
void egl_desktopRectsFree(EGL_DesktopRects ** rects_)
{
EGL_DesktopRects * rects = *rects_;
if (!rects)
return;
glDeleteVertexArrays(1, &rects->vao);
glDeleteBuffers(2, rects->buffers);
free(rects->lastVertices);
free(rects);
*rects_ = NULL;
}
inline static void rectToVertices(GLfloat * vertex, const FrameDamageRect * rect)
{
vertex[0] = rect->x;
vertex[1] = rect->y;
vertex[2] = rect->x + rect->width;
vertex[3] = rect->y;
vertex[4] = rect->x + rect->width;
vertex[5] = rect->y + rect->height;
vertex[6] = rect->x;
vertex[7] = rect->y + rect->height;
}
void egl_desktopRectsUpdate(EGL_DesktopRects * rects, const struct DamageRects * data,
int width, int height)
{
if (data && data->count == 0)
{
rects->count = 0;
return;
}
const int count = (!data || data->count < 0 ? 1 : data->count) * 8;
GLfloat vertices[count];
if (!data || data->count < 0)
{
FrameDamageRect full = {
.x = 0, .y = 0, .width = width, .height = height,
};
rects->count = 1;
rectToVertices(vertices, &full);
}
else
{
rects->count = data->count;
DEBUG_ASSERT(rects->count <= rects->maxCount);
for (int i = 0; i < rects->count; ++i)
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);
}
static void desktopToGLSpace(double matrix[6], int width, int height, double translateX,
double translateY, double scaleX, double scaleY, LG_RendererRotate rotate)
{
switch (rotate)
{
case LG_ROTATE_0:
matrix[0] = 2.0 * scaleX / width;
matrix[1] = 0.0;
matrix[2] = 0.0;
matrix[3] = -2.0 * scaleY / height;
matrix[4] = translateX - scaleX;
matrix[5] = translateY + scaleY;
return;
case LG_ROTATE_90:
matrix[0] = 0.0;
matrix[1] = -2.0 * scaleY / width;
matrix[2] = -2.0 * scaleX / height;
matrix[3] = 0.0;
matrix[4] = translateX + scaleX;
matrix[5] = translateY + scaleY;
return;
case LG_ROTATE_180:
matrix[0] = -2.0 * scaleX / width;
matrix[1] = 0.0;
matrix[2] = 0.0;
matrix[3] = 2.0 * scaleY / height;
matrix[4] = translateX + scaleX;
matrix[5] = translateY - scaleY;
return;
case LG_ROTATE_270:
matrix[0] = 0.0;
matrix[1] = 2.0 * scaleY / width;
matrix[2] = 2.0 * scaleX / height;
matrix[3] = 0.0;
matrix[4] = translateX - scaleX;
matrix[5] = translateY - scaleY;
}
}
void egl_desktopRectsMatrix(float matrix[6], int width, int height, float translateX,
float translateY, float scaleX, float scaleY, LG_RendererRotate rotate)
{
double temp[6];
desktopToGLSpace(temp, width, height, translateX, translateY, scaleX, scaleY, rotate);
for (int i = 0; i < 6; ++i)
matrix[i] = temp[i];
}
void egl_desktopToScreenMatrix(double matrix[6], int frameWidth, int frameHeight,
double translateX, double translateY, double scaleX, double scaleY, LG_RendererRotate rotate,
double windowWidth, double windowHeight)
{
desktopToGLSpace(matrix, frameWidth, frameHeight, translateX, translateY, scaleX, scaleY, rotate);
double hw = windowWidth / 2;
double hh = windowHeight / 2;
matrix[0] *= hw;
matrix[1] *= hh;
matrix[2] *= hw;
matrix[3] *= hh;
matrix[4] = matrix[4] * hw + hw;
matrix[5] = matrix[5] * hh + hh;
}
inline static void matrixMultiply(const double matrix[6], double * nx, double * ny, double x, double y)
{
*nx = matrix[0] * x + matrix[2] * y + matrix[4];
*ny = matrix[1] * x + matrix[3] * y + matrix[5];
}
struct Rect egl_desktopToScreen(const double matrix[6], const struct FrameDamageRect * rect)
{
double x1, y1, x2, y2;
matrixMultiply(matrix, &x1, &y1, rect->x, rect->y);
matrixMultiply(matrix, &x2, &y2, rect->x + rect->width, rect->y + rect->height);
int x3 = min(x1, x2);
int y3 = min(y1, y2);
return (struct Rect) {
.x = x3,
.y = y3,
.w = ceil(max(x1, x2)) - x3,
.h = ceil(max(y1, y2)) - y3,
};
}
void egl_screenToDesktopMatrix(double matrix[6], int frameWidth, int frameHeight,
double translateX, double translateY, double scaleX, double scaleY, LG_RendererRotate rotate,
double windowWidth, double windowHeight)
{
double inverted[6] = {0};
egl_desktopToScreenMatrix(inverted, frameWidth, frameHeight, translateX, translateY,
scaleX, scaleY, rotate, windowWidth, windowHeight);
double det = inverted[0] * inverted[3] - inverted[1] * inverted[2];
matrix[0] = inverted[3] / det;
matrix[1] = -inverted[1] / det;
matrix[2] = -inverted[2] / det;
matrix[3] = inverted[0] / det;
matrix[4] = (inverted[2] * inverted[5] - inverted[3] * inverted[4]) / det;
matrix[5] = (inverted[1] * inverted[4] - inverted[0] * inverted[5]) / det;
}
bool egl_screenToDesktop(struct FrameDamageRect * output, const double matrix[6],
const struct Rect * rect, int width, int height)
{
double x1, y1, x2, y2;
matrixMultiply(matrix, &x1, &y1, rect->x - 1, rect->y - 1);
matrixMultiply(matrix, &x2, &y2, rect->x + rect->w + 1, rect->y + rect->h + 1);
int x3 = min(x1, x2);
int y3 = min(y1, y2);
int x4 = ceil(max(x1, x2));
int y4 = ceil(max(y1, y2));
if (x4 < 0 || y4 < 0 || x3 >= width || y3 >= height)
return false;
output->x = max(x3, 0);
output->y = max(y3, 0);
output->width = min(width, x4) - output->x;
output->height = min(height, y4) - output->y;
return true;
}
void egl_desktopRectsRender(EGL_DesktopRects * rects)
{
if (!rects->count)
return;
glBindVertexArray(rects->vao);
glDrawElements(GL_TRIANGLES, 6 * rects->count, GL_UNSIGNED_SHORT, NULL);
glBindVertexArray(0);
}

View File

@@ -0,0 +1,53 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <stdbool.h>
#include "common/types.h"
#include "interface/renderer.h"
struct DamageRects
{
int count;
FrameDamageRect rects[];
};
typedef struct EGL_DesktopRects EGL_DesktopRects;
bool egl_desktopRectsInit(EGL_DesktopRects ** rects, int maxCount);
void egl_desktopRectsFree(EGL_DesktopRects ** rects);
void egl_desktopRectsMatrix(float matrix[6], int width, int height, float translateX,
float translateY, float scaleX, float scaleY, LG_RendererRotate rotate);
void egl_desktopToScreenMatrix(double matrix[6], int frameWidth, int frameHeight,
double translateX, double translateY, double scaleX, double scaleY, LG_RendererRotate rotate,
double windowWidth, double windowHeight);
struct Rect egl_desktopToScreen(const double matrix[6], const struct FrameDamageRect * rect);
void egl_screenToDesktopMatrix(double matrix[6], int frameWidth, int frameHeight,
double translateX, double translateY, double scaleX, double scaleY, LG_RendererRotate rotate,
double windowWidth, double windowHeight);
bool egl_screenToDesktop(struct FrameDamageRect * output, const double matrix[6],
const struct Rect * rect, int width, int height);
void egl_desktopRectsUpdate(EGL_DesktopRects * rects, const struct DamageRects * data,
int width, int height);
void egl_desktopRectsRender(EGL_DesktopRects * rects);

View File

@@ -1,67 +0,0 @@
/**
* Looking Glass
* Copyright (C) 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_draw_torus(EGL_Model * model, unsigned int pts, float x, float y, float inner, float outer)
{
GLfloat * v = (GLfloat *)malloc(sizeof(GLfloat) * (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_model_add_verticies(model, v, NULL, (pts + 1) * 2);
free(v);
}
void egl_draw_torus_arc(EGL_Model * model, unsigned int pts, float x, float y, float inner, float outer, float s, float e)
{
GLfloat * v = (GLfloat *)malloc(sizeof(GLfloat) * (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_model_add_verticies(model, v, NULL, (pts + 1) * 2);
free(v);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -20,7 +20,5 @@
#pragma once
#include "model.h"
void egl_draw_torus (EGL_Model * model, unsigned int pts, float x, float y, float inner, float outer);
void egl_draw_torus_arc(EGL_Model * model, unsigned int pts, float x, float y, float inner, float outer, float s, float e);
typedef struct Inst EGL;
void egl_resetViewport(EGL * egl);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -19,7 +19,7 @@
*/
#include "egldebug.h"
#include <GL/gl.h>
#include <GLES3/gl3.h>
#include <EGL/egl.h>
const char * egl_getErrorStr(void)
@@ -44,3 +44,17 @@ const char * egl_getErrorStr(void)
default : return "UNKNOWN";
}
}
const char * gl_getErrorStr(void)
{
switch (glGetError())
{
case GL_NO_ERROR : return "GL_NO_ERROR";
case GL_INVALID_ENUM : return "GL_INVALID_ENUM";
case GL_INVALID_VALUE : return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION : return "GL_INVALID_OPERATION";
case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION";
case GL_OUT_OF_MEMORY : return "GL_OUT_OF_MEMORY";
default : return "UNKNOWN";
}
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -21,9 +21,16 @@
#include "common/debug.h"
const char * egl_getErrorStr(void);
const char * gl_getErrorStr(void);
#define DEBUG_EGL_WARN(fmt, ...) \
DEBUG_WARN(fmt " (%s)", ##__VA_ARGS__, egl_getErrorStr())
#define DEBUG_EGL_ERROR(fmt, ...) \
DEBUG_ERROR(fmt " (%s)", ##__VA_ARGS__, egl_getErrorStr())
#define DEBUG_GL_WARN(fmt, ...) \
DEBUG_WARN(fmt " (%s)", ##__VA_ARGS__, gl_getErrorStr())
#define DEBUG_GL_ERROR(fmt, ...) \
DEBUG_ERROR(fmt " (%s)", ##__VA_ARGS__, gl_getErrorStr())

View File

@@ -0,0 +1,74 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <stddef.h>
typedef enum EGL_TexType
{
EGL_TEXTYPE_BUFFER,
EGL_TEXTYPE_BUFFER_MAP,
EGL_TEXTYPE_BUFFER_STREAM,
EGL_TEXTYPE_FRAMEBUFFER,
EGL_TEXTYPE_DMABUF
}
EGL_TexType;
typedef enum EGL_PixelFormat
{
EGL_PF_RGBA,
EGL_PF_BGRA,
EGL_PF_RGBA10,
EGL_PF_RGBA16F
}
EGL_PixelFormat;
typedef enum EGL_TexStatus
{
EGL_TEX_STATUS_NOTREADY,
EGL_TEX_STATUS_OK,
EGL_TEX_STATUS_ERROR
}
EGL_TexStatus;
typedef struct EGL_TexSetup
{
/* the pixel format of the texture */
EGL_PixelFormat pixFmt;
/* the width of the texture in pixels */
size_t width;
/* the height of the texture in pixels */
size_t height;
/* the stide of the texture in bytes */
size_t stride;
}
EGL_TexSetup;
typedef enum EGL_FilterType
{
EGL_FILTER_TYPE_EFFECT,
EGL_FILTER_TYPE_UPSCALE,
EGL_FILTER_TYPE_DOWNSCALE
}
EGL_FilterType;

View File

@@ -0,0 +1,49 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ffx.h"
#include <stdlib.h>
#include <math.h>
#define A_CPU
#define A_RESTRICT
#define A_STATIC inline static
#include "shader/ffx_a.h"
#include "shader/ffx_cas.h"
#include "shader/ffx_fsr1.h"
void ffxCasConst(uint32_t consts[8], float sharpness, float inputX, float inputY,
float outputX, float outputY)
{
CasSetup(consts + 0, consts + 4, sharpness, inputX, inputY, outputX, outputY);
}
void ffxFsrEasuConst(uint32_t consts[16], float viewportX, float viewportY,
float inputX, float inputY, float outputX, float outputY)
{
FsrEasuCon(consts + 0, consts + 4, consts + 8, consts + 12, viewportX, viewportY,
inputX, inputY, outputX, outputY);
}
void ffxFsrRcasConst(uint32_t consts[4], float sharpness)
{
FsrRcasCon(consts, sharpness);
}

View File

@@ -0,0 +1,28 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <stdint.h>
void ffxCasConst(uint32_t consts[8], float sharpness, float inputX, float inputY,
float outputX, float outputY);
void ffxFsrEasuConst(uint32_t consts[16], float viewportX, float viewportY,
float inputX, float inputY, float outputX, float outputY);
void ffxFsrRcasConst(uint32_t consts[4], float sharpness);

View File

@@ -0,0 +1,30 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "filter.h"
void egl_filterRectsRender(EGL_Shader * shader, EGL_FilterRects * rects)
{
glUniformMatrix3x2fv(egl_shaderGetUniform(shader, "transform"),
1, GL_FALSE, rects->matrix);
glUniform2f(egl_shaderGetUniform(shader, "desktopSize"),
rects->width, rects->height);
egl_desktopRectsRender(rects->rects);
}

View File

@@ -0,0 +1,171 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include "util.h"
#include "shader.h"
#include "egltypes.h"
#include "desktop_rects.h"
#include "model.h"
#include <string.h>
typedef struct EGL_FilterRects
{
EGL_DesktopRects * rects;
GLfloat * matrix;
int width, height;
}
EGL_FilterRects;
typedef struct EGL_Filter EGL_Filter;
typedef struct EGL_FilterOps
{
/* the identifier of this filter */
const char * id;
/* the friendly name of this filter */
const char * name;
/* the type of this filter */
EGL_FilterType type;
/* early initialization for registration of options */
void (*earlyInit)(void);
/* initialize the filter */
bool (*init)(EGL_Filter ** filter);
/* free the filter */
void (*free)(EGL_Filter * filter);
/* render any imgui config
* Returns true if a redraw is required */
bool (*imguiConfig)(EGL_Filter * filter);
/* writes filter state to options */
void (*saveState)(EGL_Filter * filter);
/* reads filter state from options */
void (*loadState)(EGL_Filter * filter);
/* set the input format of the filter */
bool (*setup)(EGL_Filter * filter, enum EGL_PixelFormat pixFmt,
unsigned int width, unsigned int height);
/* 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 */
void (*getOutputRes)(EGL_Filter * filter,
unsigned int *x, unsigned int *y);
/* prepare the shader for use
* A filter can return false to bypass it */
bool (*prepare)(EGL_Filter * filter);
/* runs the filter on the provided texture
* returns the processed texture as the output */
GLuint (*run)(EGL_Filter * filter, EGL_FilterRects * rects,
GLuint texture);
/* called when the filter output is no loger needed so it can release memory
* this is optional */
void (*release)(EGL_Filter * filter);
}
EGL_FilterOps;
typedef struct EGL_Filter
{
EGL_FilterOps ops;
}
EGL_Filter;
static inline bool egl_filterInit(const EGL_FilterOps * ops, EGL_Filter ** filter)
{
if (!ops->init(filter))
return false;
memcpy(&(*filter)->ops, ops, sizeof(*ops));
return true;
}
static inline void egl_filterFree(EGL_Filter ** filter)
{
(*filter)->ops.free(*filter);
*filter = NULL;
}
static inline bool egl_filterImguiConfig(EGL_Filter * filter)
{
return filter->ops.imguiConfig(filter);
}
static inline void egl_filterSaveState(EGL_Filter * filter)
{
filter->ops.saveState(filter);
}
static inline void egl_filterLoadState(EGL_Filter * filter)
{
filter->ops.loadState(filter);
}
static inline bool egl_filterSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
return filter->ops.setup(filter, pixFmt, width, height);
}
static inline void egl_filterSetOutputResHint(EGL_Filter * filter,
unsigned int x, unsigned int y)
{
if (filter->ops.setOutputResHint)
filter->ops.setOutputResHint(filter, x, y);
}
static inline void egl_filterGetOutputRes(EGL_Filter * filter,
unsigned int *x, unsigned int *y)
{
return filter->ops.getOutputRes(filter, x, y);
}
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)
{
return filter->ops.run(filter, rects, texture);
}
static inline void egl_filterRelease(EGL_Filter * filter)
{
if (filter->ops.release)
filter->ops.release(filter);
}
void egl_filterRectsRender(EGL_Shader * shader, EGL_FilterRects * rects);

View File

@@ -0,0 +1,442 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "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 "downscale.frag.h"
#include "downscale_lanczos2.frag.h"
#include "downscale_linear.frag.h"
typedef enum
{
DOWNSCALE_NEAREST = 0,
DOWNSCALE_LINEAR,
DOWNSCALE_LANCZOS2,
}
DownscaleFilter;
#define DOWNSCALE_COUNT (DOWNSCALE_LANCZOS2 + 1)
const char *filterNames[DOWNSCALE_COUNT] = {
"Nearest pixel",
"Linear",
"Lanczos",
};
typedef struct EGL_FilterDownscale
{
EGL_Filter base;
bool enable;
EGL_Shader * nearest;
EGL_Uniform uNearest;
EGL_Shader * linear;
EGL_Shader * lanczos2;
DownscaleFilter filter;
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
float pixelSize;
float vOffset, hOffset;
bool prepared;
EGL_Framebuffer * fb;
GLuint sampler[2];
}
EGL_FilterDownscale;
static void egl_filterDownscaleEarlyInit(void)
{
static struct Option options[] =
{
{
.module = "eglFilter",
.name = "downscale",
.description = "Enable downscaling",
.preset = true,
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
{
.module = "eglFilter",
.name = "downscalePixelSize",
.description = "Downscale filter pixel size",
.preset = true,
.type = OPTION_TYPE_FLOAT,
.value.x_float = 2.0f
},
{
.module = "eglFilter",
.name = "downscaleHOffset",
.description = "Downscale filter horizontal offset",
.preset = true,
.type = OPTION_TYPE_FLOAT,
.value.x_float = 0.0f
},
{
.module = "eglFilter",
.name = "downscaleVOffset",
.description = "Downscale filter vertical offset",
.preset = true,
.type = OPTION_TYPE_FLOAT,
.value.x_float = 0.0f
},
{
.module = "eglFilter",
.name = "downscaleFilter",
.description = "Downscale filter type",
.preset = true,
.type = OPTION_TYPE_INT,
.value.x_int = 0
},
{ 0 }
};
option_register(options);
}
static void egl_filterDownscaleSaveState(EGL_Filter * filter)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
option_set_bool ("eglFilter", "downscale", this->enable);
option_set_float("eglFilter", "downscalePixelSize", this->pixelSize);
option_set_float("eglFilter", "downscaleHOffset", this->vOffset);
option_set_float("eglFilter", "downscaleVOffset", this->hOffset);
option_set_int ("eglFilter", "downscaleFilter", this->filter);
}
static void egl_filterDownscaleLoadState(EGL_Filter * filter)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
this->enable = option_get_bool ("eglFilter", "downscale");
this->pixelSize = option_get_float("eglFilter", "downscalePixelSize");
this->vOffset = option_get_float("eglFilter", "downscaleHOffset");
this->hOffset = option_get_float("eglFilter", "downscaleVOffset");
this->filter = option_get_int ("eglFilter", "downscaleFilter");
if (this->filter < 0 || this->filter >= DOWNSCALE_COUNT)
this->filter = 0;
this->prepared = false;
}
static bool egl_filterDownscaleInit(EGL_Filter ** filter)
{
EGL_FilterDownscale * this = calloc(1, sizeof(*this));
if (!this)
{
DEBUG_ERROR("Failed to allocate ram");
return false;
}
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");
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);
egl_filterDownscaleLoadState(&this->base);
*filter = &this->base;
return true;
error_shader:
egl_shaderFree(&this->nearest);
egl_shaderFree(&this->linear);
egl_shaderFree(&this->lanczos2);
error_this:
free(this);
return false;
}
static void egl_filterDownscaleFree(EGL_Filter * filter)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
egl_shaderFree(&this->nearest);
egl_shaderFree(&this->linear);
egl_shaderFree(&this->lanczos2);
egl_framebufferFree(&this->fb);
glDeleteSamplers(ARRAY_LENGTH(this->sampler), this->sampler);
free(this);
}
static bool egl_filterDownscaleImguiConfig(EGL_Filter * filter)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
bool redraw = false;
bool enable = this->enable;
igCheckbox("Enable", &enable);
if (enable != this->enable)
{
this->enable = enable;
redraw = true;
}
if (igBeginCombo("Filter", filterNames[this->filter], 0))
{
for (int i = 0; i < DOWNSCALE_COUNT; ++i)
{
bool selected = i == this->filter;
if (igSelectable_Bool(filterNames[i], selected, 0, (ImVec2) { 0.0f, 0.0f }))
{
redraw = true;
this->filter = i;
}
if (selected)
igSetItemDefaultFocus();
}
igEndCombo();
}
float pixelSize = this->pixelSize;
igInputFloat("Pixel size", &pixelSize, 0.1f, 1.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal);
pixelSize = util_clamp(pixelSize, 1.0f, 10.0f);
igSliderFloat("##pixelsize", &pixelSize, 1.0f, 10.0f, "%.2f",
ImGuiSliderFlags_Logarithmic | ImGuiSliderFlags_NoInput);
igText("Resolution: %dx%d", this->width, this->height);
if (pixelSize != this->pixelSize)
{
this->pixelSize = pixelSize;
redraw = true;
}
switch (this->filter)
{
case DOWNSCALE_NEAREST:
{
float vOffset = this->vOffset;
igSliderFloat("V-Offset", &vOffset, -2, 2, NULL, 0);
if (vOffset != this->vOffset)
{
this->vOffset = vOffset;
redraw = true;
}
float hOffset = this->hOffset;
igSliderFloat("H-Offset", &hOffset, -2, 2, NULL, 0);
if (hOffset != this->hOffset)
{
this->hOffset = hOffset;
redraw = true;
}
break;
}
default:
break;
}
if (redraw)
this->prepared = false;
return redraw;
}
static bool egl_filterDownscaleSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
width = (float)width / this->pixelSize;
height = (float)height / this->pixelSize;
if (!this->enable)
return false;
if (this->prepared &&
pixFmt == this->pixFmt &&
this->width == width &&
this->height == height)
return this->pixelSize > 1.0f;
if (!egl_framebufferSetup(this->fb, pixFmt, width, height))
return false;
this->pixFmt = pixFmt;
this->width = width;
this->height = height;
this->prepared = false;
return this->pixelSize > 1.0f;
}
static void egl_filterDownscaleGetOutputRes(EGL_Filter * filter,
unsigned int *width, unsigned int *height)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
*width = this->width;
*height = this->height;
}
static bool egl_filterDownscalePrepare(EGL_Filter * filter)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
if (this->prepared)
return true;
switch (this->filter)
{
case DOWNSCALE_NEAREST:
this->uNearest.f[0] = this->pixelSize;
this->uNearest.f[1] = this->vOffset;
this->uNearest.f[2] = this->hOffset;
egl_shaderSetUniforms(this->nearest, &this->uNearest, 1);
break;
default:
break;
}
this->prepared = true;
return true;
}
static GLuint egl_filterDownscaleRun(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
{
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
egl_framebufferBind(this->fb);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
EGL_Shader * shader;
switch (this->filter)
{
case DOWNSCALE_NEAREST:
glBindSampler(0, this->sampler[0]);
shader = this->nearest;
break;
case DOWNSCALE_LINEAR:
glBindSampler(0, this->sampler[1]);
shader = this->linear;
break;
case DOWNSCALE_LANCZOS2:
glBindSampler(0, this->sampler[0]);
shader = this->lanczos2;
break;
default:
DEBUG_UNREACHABLE();
}
egl_shaderUse(shader);
egl_filterRectsRender(shader, rects);
return egl_framebufferGetTexture(this->fb);
}
EGL_FilterOps egl_filterDownscaleOps =
{
.id = "downscale",
.name = "Downscaler",
.type = EGL_FILTER_TYPE_DOWNSCALE,
.earlyInit = egl_filterDownscaleEarlyInit,
.init = egl_filterDownscaleInit,
.free = egl_filterDownscaleFree,
.imguiConfig = egl_filterDownscaleImguiConfig,
.saveState = egl_filterDownscaleSaveState,
.loadState = egl_filterDownscaleLoadState,
.setup = egl_filterDownscaleSetup,
.getOutputRes = egl_filterDownscaleGetOutputRes,
.prepare = egl_filterDownscalePrepare,
.run = egl_filterDownscaleRun
};

View File

@@ -0,0 +1,297 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "filter.h"
#include "framebuffer.h"
#include "common/countedbuffer.h"
#include "common/debug.h"
#include "common/option.h"
#include "cimgui.h"
#include "ffx.h"
#include "basic.vert.h"
#include "ffx_cas.frag.h"
typedef struct EGL_FilterFFXCAS
{
EGL_Filter base;
EGL_Shader * shader;
bool enable;
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
float sharpness;
CountedBuffer * consts;
bool prepared;
EGL_Framebuffer * fb;
GLuint sampler;
}
EGL_FilterFFXCAS;
static void egl_filterFFXCASEarlyInit(void)
{
static struct Option options[] =
{
{
.module = "eglFilter",
.name = "ffxCAS",
.description = "AMD FidelityFX CAS",
.preset = true,
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
{
.module = "eglFilter",
.name = "ffxCASSharpness",
.description = "AMD FidelityFX CAS Sharpness",
.preset = true,
.type = OPTION_TYPE_FLOAT,
.value.x_float = 0.0f
},
{ 0 }
};
option_register(options);
}
static void casUpdateConsts(EGL_FilterFFXCAS * this)
{
ffxCasConst((uint32_t *) this->consts->data, this->sharpness,
this->width, this->height,
this->width, this->height);
}
static void egl_filterFFXCASSaveState(EGL_Filter * filter)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
option_set_bool ("eglFilter", "ffxCAS", this->enable);
option_set_float("eglFilter", "ffxCASSharpness", this->sharpness);
}
static void egl_filterFFXCASLoadState(EGL_Filter * filter)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
this->enable = option_get_bool ("eglFilter", "ffxCAS");
this->sharpness = option_get_float("eglFilter", "ffxCASSharpness");
}
static bool egl_filterFFXCASInit(EGL_Filter ** filter)
{
EGL_FilterFFXCAS * this = calloc(1, sizeof(*this));
if (!this)
{
DEBUG_ERROR("Failed to allocate ram");
return false;
}
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)
{
DEBUG_ERROR("Failed to allocate consts buffer");
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))
{
DEBUG_ERROR("Failed to initialize the framebuffer");
goto error_consts;
}
glGenSamplers(1, &this->sampler);
glSamplerParameteri(this->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(this->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
*filter = &this->base;
return true;
error_consts:
countedBufferRelease(&this->consts);
error_shader:
egl_shaderFree(&this->shader);
error_this:
free(this);
return false;
}
static void egl_filterFFXCASFree(EGL_Filter * filter)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
egl_shaderFree(&this->shader);
countedBufferRelease(&this->consts);
egl_framebufferFree(&this->fb);
glDeleteSamplers(1, &this->sampler);
free(this);
}
static bool egl_filterFFXCASImguiConfig(EGL_Filter * filter)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
bool redraw = false;
bool cas = this->enable;
float casSharpness = this->sharpness;
igCheckbox("Enabled", &cas);
if (cas != this->enable)
{
this->enable = cas;
redraw = true;
}
igText("Sharpness:");
igSameLine(0.0f, -1.0f);
igPushItemWidth(igGetWindowWidth() - igGetCursorPosX() -
igGetStyle()->WindowPadding.x);
igSliderFloat("##casSharpness", &casSharpness, 0.0f, 1.0f, NULL, 0);
casSharpness = util_clamp(casSharpness, 0.0f, 1.0f);
if (igIsItemHovered(ImGuiHoveredFlags_None))
igSetTooltip("Ctrl+Click to enter a value");
igPopItemWidth();
if (casSharpness != this->sharpness)
{
// enable CAS if the sharpness was changed
if (!cas)
{
cas = true;
this->enable = true;
}
this->sharpness = casSharpness;
casUpdateConsts(this);
redraw = true;
}
if (redraw)
this->prepared = false;
return redraw;
}
static bool egl_filterFFXCASSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
if (!this->enable)
return false;
if (pixFmt == this->pixFmt && this->width == width && this->height == height)
return true;
if (!egl_framebufferSetup(this->fb, pixFmt, width, height))
return false;
this->pixFmt = pixFmt;
this->width = width;
this->height = height;
this->prepared = false;
casUpdateConsts(this);
return true;
}
static void egl_filterFFXCASGetOutputRes(EGL_Filter * filter,
unsigned int *width, unsigned int *height)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
*width = this->width;
*height = this->height;
}
static bool egl_filterFFXCASPrepare(EGL_Filter * filter)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
if (this->prepared)
return true;
this->prepared = true;
return true;
}
static GLuint egl_filterFFXCASRun(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
egl_framebufferBind(this->fb);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->shader);
egl_filterRectsRender(this->shader, rects);
return egl_framebufferGetTexture(this->fb);
}
EGL_FilterOps egl_filterFFXCASOps =
{
.id = "ffxCAS",
.name = "AMD FidelityFX CAS",
.type = EGL_FILTER_TYPE_EFFECT,
.earlyInit = egl_filterFFXCASEarlyInit,
.init = egl_filterFFXCASInit,
.free = egl_filterFFXCASFree,
.imguiConfig = egl_filterFFXCASImguiConfig,
.saveState = egl_filterFFXCASSaveState,
.loadState = egl_filterFFXCASLoadState,
.setup = egl_filterFFXCASSetup,
.getOutputRes = egl_filterFFXCASGetOutputRes,
.prepare = egl_filterFFXCASPrepare,
.run = egl_filterFFXCASRun
};

View File

@@ -0,0 +1,440 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "filter.h"
#include "framebuffer.h"
#include "common/array.h"
#include "common/countedbuffer.h"
#include "common/debug.h"
#include "common/option.h"
#include "cimgui.h"
#include "ffx.h"
#include "basic.vert.h"
#include "ffx_fsr1_easu.frag.h"
#include "ffx_fsr1_rcas.frag.h"
typedef struct EGL_FilterFFXFSR1
{
EGL_Filter base;
EGL_Shader * easu, * rcas;
bool enable, active;
float sharpness;
CountedBuffer * consts;
EGL_Uniform easuUniform[2], rcasUniform;
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
unsigned int inWidth, inHeight;
bool sizeChanged;
bool prepared;
EGL_Framebuffer * easuFb, * rcasFb;
GLuint sampler;
}
EGL_FilterFFXFSR1;
static void egl_filterFFXFSR1EarlyInit(void)
{
static struct Option options[] =
{
{
.module = "eglFilter",
.name = "ffxFSR",
.description = "AMD FidelityFX FSR",
.preset = true,
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
{
.module = "eglFilter",
.name = "ffxFSRSharpness",
.description = "AMD FidelityFX FSR Sharpness",
.preset = true,
.type = OPTION_TYPE_FLOAT,
.value.x_float = 1.0f
},
{ 0 }
};
option_register(options);
}
static void rcasUpdateUniform(EGL_FilterFFXFSR1 * this)
{
ffxFsrRcasConst(this->rcasUniform.ui, 2.0f - this->sharpness * 2.0f);
}
static void egl_filterFFXFSR1SaveState(EGL_Filter * filter)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
option_set_bool ("eglFilter", "ffxFSR", this->enable);
option_set_float("eglFilter", "ffxFSRSharpness", this->sharpness);
}
static void egl_filterFFXFSR1LoadState(EGL_Filter * filter)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
this->enable = option_get_bool ("eglFilter", "ffxFSR");
this->sharpness = option_get_float("eglFilter", "ffxFSRSharpness");
}
static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
{
EGL_FilterFFXFSR1 * this = calloc(1, sizeof(*this));
if (!this)
{
DEBUG_ERROR("Failed to allocate ram");
return false;
}
if (!egl_shaderInit(&this->easu))
{
DEBUG_ERROR("Failed to initialize the Easu shader");
goto error_this;
}
if (!egl_shaderInit(&this->rcas))
{
DEBUG_ERROR("Failed to initialize the Rcas shader");
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)
)
{
DEBUG_ERROR("Failed to compile the Rcas shader");
goto error_rcas;
}
this->consts = countedBufferNew(16 * sizeof(GLuint));
if (!this->consts)
{
DEBUG_ERROR("Failed to allocate consts buffer");
goto error_rcas;
}
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);
if (!egl_framebufferInit(&this->easuFb))
{
DEBUG_ERROR("Failed to initialize the Easu framebuffer");
goto error_consts;
}
if (!egl_framebufferInit(&this->rcasFb))
{
DEBUG_ERROR("Failed to initialize the Rcas framebuffer");
goto error_easuFb;
}
glGenSamplers(1, &this->sampler);
glSamplerParameteri(this->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(this->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
*filter = &this->base;
return true;
error_easuFb:
egl_framebufferFree(&this->rcasFb);
error_consts:
countedBufferRelease(&this->consts);
error_rcas:
egl_shaderFree(&this->rcas);
error_esau:
egl_shaderFree(&this->easu);
error_this:
free(this);
return false;
}
static void egl_filterFFXFSR1Free(EGL_Filter * filter)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
egl_shaderFree(&this->easu);
egl_shaderFree(&this->rcas);
countedBufferRelease(&this->consts);
egl_framebufferFree(&this->easuFb);
egl_framebufferFree(&this->rcasFb);
glDeleteSamplers(1, &this->sampler);
free(this);
}
static bool egl_filterFFXFSR1ImguiConfig(EGL_Filter * filter)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
bool redraw = false;
bool enable = this->enable;
float sharpness = this->sharpness;
igCheckbox("Enabled", &enable);
if (enable != this->enable)
{
this->enable = enable;
redraw = true;
}
if (this->active)
{
double dimScale = (double) this->width / this->inWidth;
const char * name;
if (dimScale < 1.29)
name = "better than Ultra Quality";
else if (dimScale < 1.31)
name = "Ultra Quality";
else if (dimScale < 1.4)
name = "slightly worse than Ultra Quality";
else if (dimScale < 1.49)
name = "slightly better than Quality";
else if (dimScale < 1.51)
name = "Quality";
else if (dimScale < 1.6)
name = "slightly worse than Quality";
else if (dimScale < 1.69)
name = "slightly better than Balanced";
else if (dimScale < 1.71)
name = "Balanced";
else if (dimScale < 1.85)
name = "slightly worse than Balanced";
else if (dimScale < 1.99)
name = "slightly better than Performance";
else if (dimScale < 2.01)
name = "Performance";
else
name = "worse than Performance";
igText("Equivalent quality mode: %s%s", name, this->enable ? "" : ", inactive");
}
else
igText("Equivalent quality mode: not upscaling, inactive");
if (igIsItemHovered(ImGuiHoveredFlags_None))
{
igBeginTooltip();
igText(
"Equivalent quality mode is decided by the resolution in the guest VM or the output\n"
"of the previous filter in the chain.\n\n"
"Here are the input resolutions needed for each quality mode at current window size:\n"
);
if (igBeginTable("Resolutions", 2, 0, (ImVec2) { 0.0f, 0.0f }, 0.0f))
{
igTableNextColumn();
igText("Ultra Quality");
igTableNextColumn();
igText("%.0fx%.0f", this->width / 1.3, this->height / 1.3);
igTableNextColumn();
igText("Quality");
igTableNextColumn();
igText("%.0fx%.0f", this->width / 1.5, this->height / 1.5);
igTableNextColumn();
igText("Balanced");
igTableNextColumn();
igText("%.0fx%.0f", this->width / 1.7, this->height / 1.7);
igTableNextColumn();
igText("Performance");
igTableNextColumn();
igText("%.0fx%.0f", this->width / 2.0, this->height / 2.0);
igEndTable();
}
igEndTooltip();
}
igText("Sharpness:");
igSameLine(0.0f, -1.0f);
igPushItemWidth(igGetWindowWidth() - igGetCursorPosX() -
igGetStyle()->WindowPadding.x);
igSliderFloat("##fsr1Sharpness", &sharpness, 0.0f, 1.0f, NULL, 0);
sharpness = util_clamp(sharpness, 0.0f, 1.0f);
if (igIsItemHovered(ImGuiHoveredFlags_None))
igSetTooltip("Ctrl+Click to enter a value");
igPopItemWidth();
if (sharpness != this->sharpness)
{
// enable FSR1 if the sharpness was changed
if (!enable)
{
enable = true;
this->enable = true;
}
this->sharpness = sharpness;
rcasUpdateUniform(this);
redraw = true;
}
if (redraw)
this->prepared = false;
return redraw;
}
static void egl_filterFFXFSR1SetOutputResHint(EGL_Filter * filter,
unsigned int width, unsigned int height)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
if (this->width == width && this->height == height)
return;
this->width = width;
this->height = height;
this->sizeChanged = true;
this->prepared = false;
}
static bool egl_filterFFXFSR1Setup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
if (!this->enable)
return false;
this->active = this->width > width && this->height > height;
if (!this->active)
return false;
if (pixFmt == this->pixFmt && !this->sizeChanged &&
width == this->inWidth && height == this->inHeight)
return true;
if (!egl_framebufferSetup(this->easuFb, pixFmt, this->width, this->height))
return false;
if (!egl_framebufferSetup(this->rcasFb, pixFmt, this->width, this->height))
return false;
this->inWidth = width;
this->inHeight = height;
this->sizeChanged = false;
this->pixFmt = pixFmt;
this->prepared = false;
this->easuUniform[1].f[0] = this->width;
this->easuUniform[1].f[1] = this->height;
ffxFsrEasuConst((uint32_t *)this->consts->data, this->inWidth, this->inHeight,
this->inWidth, this->inHeight, this->width, this->height);
return true;
}
static void egl_filterFFXFSR1GetOutputRes(EGL_Filter * filter,
unsigned int *width, unsigned int *height)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
*width = this->width;
*height = this->height;
}
static bool egl_filterFFXFSR1Prepare(EGL_Filter * filter)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
if (!this->active)
return false;
if (this->prepared)
return true;
egl_shaderSetUniforms(this->easu, this->easuUniform, ARRAY_LENGTH(this->easuUniform));
egl_shaderSetUniforms(this->rcas, &this->rcasUniform, 1);
this->prepared = true;
return true;
}
static GLuint egl_filterFFXFSR1Run(EGL_Filter * filter,
EGL_FilterRects * rects, GLuint texture)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
// pass 1, Easu
egl_framebufferBind(this->easuFb);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->easu);
egl_filterRectsRender(this->easu, rects);
texture = egl_framebufferGetTexture(this->easuFb);
// pass 2, Rcas
egl_framebufferBind(this->rcasFb);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->rcas);
egl_filterRectsRender(this->rcas, rects);
texture = egl_framebufferGetTexture(this->rcasFb);
return texture;
}
EGL_FilterOps egl_filterFFXFSR1Ops =
{
.id = "ffxFSR1",
.name = "AMD FidelityFX FSR",
.type = EGL_FILTER_TYPE_UPSCALE,
.earlyInit = egl_filterFFXFSR1EarlyInit,
.init = egl_filterFFXFSR1Init,
.free = egl_filterFFXFSR1Free,
.imguiConfig = egl_filterFFXFSR1ImguiConfig,
.saveState = egl_filterFFXFSR1SaveState,
.loadState = egl_filterFFXFSR1LoadState,
.setup = egl_filterFFXFSR1Setup,
.setOutputResHint = egl_filterFFXFSR1SetOutputResHint,
.getOutputRes = egl_filterFFXFSR1GetOutputRes,
.prepare = egl_filterFFXFSR1Prepare,
.run = egl_filterFFXFSR1Run
};

View File

@@ -0,0 +1,25 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
extern EGL_FilterOps egl_filterDownscaleOps;
extern EGL_FilterOps egl_filterFFXCASOps;
extern EGL_FilterOps egl_filterFFXFSR1Ops;

View File

@@ -1,211 +0,0 @@
/**
* Looking Glass
* Copyright (C) 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 "fps.h"
#include "common/debug.h"
#include "texture.h"
#include "shader.h"
#include "model.h"
#include <stdlib.h>
#include <string.h>
// these headers are auto generated by cmake
#include "fps.vert.h"
#include "fps.frag.h"
#include "fps_bg.frag.h"
struct EGL_FPS
{
const LG_Font * font;
LG_FontObj fontObj;
EGL_Texture * texture;
EGL_Shader * shader;
EGL_Shader * shaderBG;
EGL_Model * model;
bool display;
bool ready;
int iwidth, iheight;
float width, height;
// uniforms
GLint uScreen , uSize;
GLint uScreenBG, uSizeBG;
};
bool egl_fps_init(EGL_FPS ** fps, const LG_Font * font, LG_FontObj fontObj)
{
*fps = (EGL_FPS *)malloc(sizeof(EGL_FPS));
if (!*fps)
{
DEBUG_ERROR("Failed to malloc EGL_FPS");
return false;
}
memset(*fps, 0, sizeof(EGL_FPS));
(*fps)->font = font;
(*fps)->fontObj = fontObj;
if (!egl_texture_init(&(*fps)->texture, NULL))
{
DEBUG_ERROR("Failed to initialize the fps texture");
return false;
}
if (!egl_shader_init(&(*fps)->shader))
{
DEBUG_ERROR("Failed to initialize the fps shader");
return false;
}
if (!egl_shader_init(&(*fps)->shaderBG))
{
DEBUG_ERROR("Failed to initialize the fps bg shader");
return false;
}
if (!egl_shader_compile((*fps)->shader,
b_shader_fps_vert, b_shader_fps_vert_size,
b_shader_fps_frag, b_shader_fps_frag_size))
{
DEBUG_ERROR("Failed to compile the fps shader");
return false;
}
if (!egl_shader_compile((*fps)->shaderBG,
b_shader_fps_vert , b_shader_fps_vert_size,
b_shader_fps_bg_frag, b_shader_fps_bg_frag_size))
{
DEBUG_ERROR("Failed to compile the fps shader");
return false;
}
(*fps)->uSize = egl_shader_get_uniform_location((*fps)->shader , "size" );
(*fps)->uScreen = egl_shader_get_uniform_location((*fps)->shader , "screen");
(*fps)->uSizeBG = egl_shader_get_uniform_location((*fps)->shaderBG, "size" );
(*fps)->uScreenBG = egl_shader_get_uniform_location((*fps)->shaderBG, "screen");
if (!egl_model_init(&(*fps)->model))
{
DEBUG_ERROR("Failed to initialize the fps model");
return false;
}
egl_model_set_default((*fps)->model);
egl_model_set_texture((*fps)->model, (*fps)->texture);
return true;
}
void egl_fps_free(EGL_FPS ** fps)
{
if (!*fps)
return;
egl_texture_free(&(*fps)->texture );
egl_shader_free (&(*fps)->shader );
egl_shader_free (&(*fps)->shaderBG);
egl_model_free (&(*fps)->model );
free(*fps);
*fps = NULL;
}
void egl_fps_set_display(EGL_FPS * fps, bool display)
{
fps->display = display;
}
void egl_fps_set_font(EGL_FPS * fps, LG_Font * fontObj)
{
fps->fontObj = fontObj;
}
void egl_fps_update(EGL_FPS * fps, const float avgFPS, const float renderFPS)
{
if (!fps->display)
return;
char str[128];
snprintf(str, sizeof(str), "UPS: %8.4f, FPS: %8.4f", avgFPS, renderFPS);
LG_FontBitmap * bmp = fps->font->render(fps->fontObj, 0xffffff00, str);
if (!bmp)
{
DEBUG_ERROR("Failed to render fps text");
return;
}
if (fps->iwidth != bmp->width || fps->iheight != bmp->height)
{
fps->iwidth = bmp->width;
fps->iheight = bmp->height;
fps->width = (float)bmp->width;
fps->height = (float)bmp->height;
egl_texture_setup(
fps->texture,
EGL_PF_BGRA,
bmp->width ,
bmp->height,
bmp->width * bmp->bpp,
false,
false
);
}
egl_texture_update
(
fps->texture,
bmp->pixels
);
fps->ready = true;
fps->font->release(fps->fontObj, bmp);
}
void egl_fps_render(EGL_FPS * fps, const float scaleX, const float scaleY)
{
if (!fps->display || !fps->ready)
return;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// render the background first
egl_shader_use(fps->shaderBG);
glUniform2f(fps->uScreenBG, scaleX , scaleY );
glUniform2f(fps->uSizeBG , fps->width, fps->height);
egl_model_render(fps->model);
// render the texture over the background
egl_shader_use(fps->shader);
glUniform2f(fps->uScreen, scaleX , scaleY );
glUniform2f(fps->uSize , fps->width, fps->height);
egl_model_render(fps->model);
glDisable(GL_BLEND);
}

View File

@@ -0,0 +1,108 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "framebuffer.h"
#include "texture.h"
#include <stdlib.h>
#include "common/debug.h"
struct EGL_Framebuffer
{
GLuint fbo;
EGL_Texture * tex;
};
bool egl_framebufferInit(EGL_Framebuffer ** fb)
{
EGL_Framebuffer * this = calloc(1, sizeof(*this));
if (!this)
{
DEBUG_ERROR("Failed to allocate ram");
return false;
}
if (!egl_textureInit(&this->tex, NULL, EGL_TEXTYPE_BUFFER))
{
DEBUG_ERROR("Failed to initialize the texture");
return false;
}
glGenFramebuffers(1, &this->fbo);
*fb = this;
return true;
}
void egl_framebufferFree(EGL_Framebuffer ** fb)
{
EGL_Framebuffer * this = *fb;
egl_textureFree(&this->tex);
free(this);
*fb = NULL;
}
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))
{
DEBUG_ERROR("Failed to setup the texture");
return false;
}
GLuint tex;
egl_textureGet(this->tex, &tex, NULL, NULL);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, this->fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, tex, 0);
glDrawBuffers(1, &(GLenum){GL_COLOR_ATTACHMENT0});
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
DEBUG_ERROR("Failed to setup the framebuffer: 0x%x", status);
return false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return true;
}
void egl_framebufferBind(EGL_Framebuffer * this)
{
glBindFramebuffer(GL_FRAMEBUFFER, this->fbo);
glViewport(0, 0, this->tex->format.width, this->tex->format.height);
}
GLuint egl_framebufferGetTexture(EGL_Framebuffer * this)
{
GLuint output;
egl_textureGet(this->tex, &output, NULL, NULL);
return output;
}

View File

@@ -0,0 +1,35 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include "texture.h"
typedef struct EGL_Framebuffer EGL_Framebuffer;
bool egl_framebufferInit(EGL_Framebuffer ** fb);
void egl_framebufferFree(EGL_Framebuffer ** fb);
bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt,
unsigned int width, unsigned int height);
void egl_framebufferBind(EGL_Framebuffer * this);
GLuint egl_framebufferGetTexture(EGL_Framebuffer * this);

View File

@@ -0,0 +1,13 @@
BEGIN { FS="\"" }
function process(line, second) {
if (line ~ /^#include[ \t]*".+"[ \t\r]*$/) {
while (getline < second) {
process($0, $2)
}
} else {
print line
}
}
{ process($0, $2) }

View File

@@ -1,215 +0,0 @@
/**
* Looking Glass
* Copyright (C) 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 "help.h"
#include "common/debug.h"
#include "texture.h"
#include "shader.h"
#include "model.h"
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>
// these headers are auto generated by cmake
#include "help.vert.h"
#include "help.frag.h"
#include "help_bg.frag.h"
struct EGL_Help
{
const LG_Font * font;
LG_FontObj fontObj;
EGL_Texture * texture;
EGL_Shader * shader;
EGL_Shader * shaderBG;
EGL_Model * model;
_Atomic(LG_FontBitmap *) bmp;
bool shouldRender;
int iwidth, iheight;
float width, height;
// uniforms
GLint uScreen , uSize;
GLint uScreenBG, uSizeBG;
};
bool egl_help_init(EGL_Help ** help, const LG_Font * font, LG_FontObj fontObj)
{
*help = (EGL_Help *)malloc(sizeof(EGL_Help));
if (!*help)
{
DEBUG_ERROR("Failed to malloc EGL_Help");
return false;
}
memset(*help, 0, sizeof(EGL_Help));
(*help)->font = font;
(*help)->fontObj = fontObj;
if (!egl_texture_init(&(*help)->texture, NULL))
{
DEBUG_ERROR("Failed to initialize the help texture");
return false;
}
if (!egl_shader_init(&(*help)->shader))
{
DEBUG_ERROR("Failed to initialize the help shader");
return false;
}
if (!egl_shader_init(&(*help)->shaderBG))
{
DEBUG_ERROR("Failed to initialize the help bg shader");
return false;
}
if (!egl_shader_compile((*help)->shader,
b_shader_help_vert, b_shader_help_vert_size,
b_shader_help_frag, b_shader_help_frag_size))
{
DEBUG_ERROR("Failed to compile the help shader");
return false;
}
if (!egl_shader_compile((*help)->shaderBG,
b_shader_help_vert , b_shader_help_vert_size,
b_shader_help_bg_frag, b_shader_help_bg_frag_size))
{
DEBUG_ERROR("Failed to compile the help shader");
return false;
}
(*help)->uSize = egl_shader_get_uniform_location((*help)->shader , "size" );
(*help)->uScreen = egl_shader_get_uniform_location((*help)->shader , "screen");
(*help)->uSizeBG = egl_shader_get_uniform_location((*help)->shaderBG, "size" );
(*help)->uScreenBG = egl_shader_get_uniform_location((*help)->shaderBG, "screen");
if (!egl_model_init(&(*help)->model))
{
DEBUG_ERROR("Failed to initialize the fps model");
return false;
}
egl_model_set_default((*help)->model);
egl_model_set_texture((*help)->model, (*help)->texture);
atomic_init(&(*help)->bmp, NULL);
return true;
}
void egl_help_free(EGL_Help ** help)
{
if (!*help)
return;
egl_texture_free(&(*help)->texture );
egl_shader_free (&(*help)->shader );
egl_shader_free (&(*help)->shaderBG);
egl_model_free (&(*help)->model );
free(*help);
*help = NULL;
}
void egl_help_set_text(EGL_Help * help, const char * help_text)
{
LG_FontBitmap * bmp = NULL;
if (help_text)
{
bmp = help->font->render(help->fontObj, 0xffffff00, help_text);
if (!bmp)
DEBUG_ERROR("Failed to render help text");
} else
help->shouldRender = false;
bmp = atomic_exchange(&help->bmp, bmp);
if (bmp)
{
help->font->release(help->fontObj, bmp);
}
}
void egl_help_set_font(EGL_Help * help, LG_FontObj fontObj)
{
help->fontObj = fontObj;
}
void egl_help_render(EGL_Help * help, const float scaleX, const float scaleY)
{
LG_FontBitmap * bmp = atomic_exchange(&help->bmp, NULL);
if (bmp)
{
if (help->iwidth != bmp->width || help->iheight != bmp->height)
{
help->iwidth = bmp->width;
help->iheight = bmp->height;
help->width = (float)bmp->width;
help->height = (float)bmp->height;
egl_texture_setup(
help->texture,
EGL_PF_BGRA,
bmp->width ,
bmp->height,
bmp->width * bmp->bpp,
false,
false
);
}
egl_texture_update
(
help->texture,
bmp->pixels
);
help->shouldRender = true;
help->font->release(help->fontObj, bmp);
}
if (!help->shouldRender)
return;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// render the background first
egl_shader_use(help->shaderBG);
glUniform2f(help->uScreenBG, scaleX , scaleY );
glUniform2f(help->uSizeBG , help->width, help->height);
egl_model_render(help->model);
// render the texture over the background
egl_shader_use(help->shader);
glUniform2f(help->uScreen, scaleX , scaleY );
glUniform2f(help->uSize , help->width, help->height);
egl_model_render(help->model);
glDisable(GL_BLEND);
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -23,12 +23,11 @@
#include "texture.h"
#include "common/debug.h"
#include "ll.h"
#include "common/ll.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
struct EGL_Model
{
@@ -37,8 +36,8 @@ struct EGL_Model
size_t vertexCount;
bool finish;
bool hasBuffer;
GLuint buffer;
GLuint vao;
EGL_Shader * shader;
EGL_Texture * texture;
@@ -53,23 +52,23 @@ struct FloatList
void update_uniform_bindings(EGL_Model * model);
bool egl_model_init(EGL_Model ** model)
bool egl_modelInit(EGL_Model ** model)
{
*model = (EGL_Model *)malloc(sizeof(EGL_Model));
*model = malloc(sizeof(**model));
if (!*model)
{
DEBUG_ERROR("Failed to malloc EGL_Model");
return false;
}
memset(*model, 0, sizeof(EGL_Model));
memset(*model, 0, sizeof(**model));
(*model)->verticies = ll_new();
return true;
}
void egl_model_free(EGL_Model ** model)
void egl_modelFree(EGL_Model ** model)
{
if (!*model)
return;
@@ -83,14 +82,17 @@ void egl_model_free(EGL_Model ** model)
}
ll_free((*model)->verticies);
if ((*model)->hasBuffer)
if ((*model)->buffer)
glDeleteBuffers(1, &(*model)->buffer);
if ((*model)->vao)
glDeleteVertexArrays(1, &(*model)->vao);
free(*model);
*model = NULL;
}
void egl_model_set_default(EGL_Model * model)
void egl_modelSetDefault(EGL_Model * model, bool flipped)
{
static const GLfloat square[] =
{
@@ -100,7 +102,15 @@ void egl_model_set_default(EGL_Model * model)
1.0f, 1.0f, 0.0f
};
static const GLfloat uvs[] =
static const GLfloat uvsNormal[] =
{
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f
};
static const GLfloat uvsFlipped[] =
{
0.0f, 1.0f,
1.0f, 1.0f,
@@ -108,16 +118,37 @@ void egl_model_set_default(EGL_Model * model)
1.0f, 0.0f
};
egl_model_add_verticies(model, square, uvs, 4);
egl_modelAddVerts(model, square, flipped ? uvsFlipped : uvsNormal, 4);
}
void egl_model_add_verticies(EGL_Model * model, const GLfloat * verticies, const GLfloat * uvs, const size_t count)
void egl_modelAddVerts(EGL_Model * model, const GLfloat * verticies, const GLfloat * uvs, const size_t count)
{
struct FloatList * fl = (struct FloatList *)malloc(sizeof(struct FloatList));
struct FloatList * fl = malloc(sizeof(*fl));
if (!fl)
{
DEBUG_ERROR("out of memory");
return;
}
fl->count = count;
fl->v = (GLfloat *)malloc(sizeof(GLfloat) * count * 3);
fl->u = (GLfloat *)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)
@@ -130,16 +161,21 @@ void egl_model_add_verticies(EGL_Model * model, const GLfloat * verticies, const
model->vertexCount += count;
}
void egl_model_render(EGL_Model * model)
void egl_modelRender(EGL_Model * model)
{
if (!model->vertexCount)
return;
if (model->rebuild)
{
if (model->hasBuffer)
if (model->buffer)
glDeleteBuffers(1, &model->buffer);
if (!model->vao)
glGenVertexArrays(1, &model->vao);
glBindVertexArray(model->vao);
/* create a buffer large enough */
glGenBuffers(1, &model->buffer);
glBindBuffer(GL_ARRAY_BUFFER, model->buffer);
@@ -149,59 +185,64 @@ void egl_model_render(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);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(GLfloat) * model->vertexCount * 3));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
model->rebuild = false;
}
/* bind the model buffer and setup the pointers */
glBindBuffer(GL_ARRAY_BUFFER, model->buffer);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(GLfloat) * model->vertexCount * 3));
glBindVertexArray(model->vao);
if (model->shader)
egl_shader_use(model->shader);
egl_shaderUse(model->shader);
if (model->texture)
egl_texture_bind(model->texture);
egl_textureBind(model->texture);
/* 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);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindVertexArray(0);
glUseProgram(0);
}
void egl_model_set_shader(EGL_Model * model, EGL_Shader * shader)
void egl_modelSetShader(EGL_Model * model, EGL_Shader * shader)
{
model->shader = shader;
update_uniform_bindings(model);
}
void egl_model_set_texture(EGL_Model * model, EGL_Texture * texture)
void egl_modelSetTexture(EGL_Model * model, EGL_Texture * texture)
{
model->texture = texture;
update_uniform_bindings(model);
@@ -212,6 +253,5 @@ void update_uniform_bindings(EGL_Model * model)
if (!model->shader || !model->texture)
return;
const int count = egl_texture_count(model->texture);
egl_shader_associate_textures(model->shader, count);
egl_shaderAssocTextures(model->shader, 1);
}

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -24,16 +24,17 @@
#include "shader.h"
#include "texture.h"
#include <GL/gl.h>
#include <GLES3/gl3.h>
typedef struct EGL_Model EGL_Model;
typedef struct EGL_Texture EGL_Texture;
bool egl_model_init(EGL_Model ** model);
void egl_model_free(EGL_Model ** model);
bool egl_modelInit(EGL_Model ** model);
void egl_modelFree(EGL_Model ** model);
void egl_model_set_default (EGL_Model * model);
void egl_model_add_verticies(EGL_Model * model, const GLfloat * verticies, const GLfloat * uvs, const size_t count);
void egl_model_set_shader (EGL_Model * model, EGL_Shader * shader);
void egl_model_set_texture (EGL_Model * model, EGL_Texture * texture);
void egl_modelSetDefault (EGL_Model * model, bool flipped);
void egl_modelAddVerts(EGL_Model * model, const GLfloat * verticies, const GLfloat * uvs, const size_t count);
void egl_modelSetShader (EGL_Model * model, EGL_Shader * shader);
void egl_modelSetTexture (EGL_Model * model, EGL_Texture * texture);
void egl_model_render(EGL_Model * model);
void egl_modelRender(EGL_Model * model);

View File

@@ -0,0 +1,668 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define _GNU_SOURCE
#include "postprocess.h"
#include "filters.h"
#include "app.h"
#include "cimgui.h"
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include <stdatomic.h>
#include <sys/stat.h>
#include "common/debug.h"
#include "common/array.h"
#include "common/option.h"
#include "common/paths.h"
#include "common/stringlist.h"
#include "common/stringutils.h"
#include "common/vector.h"
static const EGL_FilterOps * EGL_Filters[] =
{
&egl_filterDownscaleOps,
&egl_filterFFXFSR1Ops,
&egl_filterFFXCASOps
};
struct EGL_PostProcess
{
Vector filters;
GLuint output;
unsigned int outputX, outputY;
_Atomic(bool) modified;
EGL_DesktopRects * rects;
StringList presets;
char * presetDir;
int activePreset;
char presetEdit[128];
char * presetError;
};
void egl_postProcessEarlyInit(void)
{
static struct Option options[] =
{
{
.module = "eglFilter",
.name = "order",
.description = "The order of filters to use",
.preset = true,
.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();
}
static void loadPreset(struct EGL_PostProcess * this, const char * name);
static void loadPresetList(struct EGL_PostProcess * this)
{
DIR * dir = NULL;
alloc_sprintf(&this->presetDir, "%s/presets", lgConfigDir());
if (!this->presetDir)
{
DEBUG_ERROR("Failed to allocate memory for presets");
return;
}
if (mkdir(this->presetDir, S_IRWXU) < 0 && errno != EEXIST)
{
DEBUG_ERROR("Failed to create presets directory: %s", this->presetDir);
goto fail;
}
dir = opendir(this->presetDir);
if (!dir)
{
DEBUG_ERROR("Failed to open presets directory: %s", this->presetDir);
goto fail;
}
this->presets = stringlist_new(true);
if (!this->presets)
{
DEBUG_ERROR("Failed to allocate memory for preset list");
goto fail;
}
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)
continue;
DEBUG_INFO("Found preset: %s", entry->d_name);
char * name = strdup(entry->d_name);
if (!name)
{
DEBUG_ERROR("Failed to allocate memory");
goto fail;
}
stringlist_push(this->presets, name);
if (preset && strcmp(preset, name) == 0)
this->activePreset = stringlist_count(this->presets) - 1;
}
closedir(dir);
if (preset)
{
if (this->activePreset > -1)
loadPreset(this, preset);
else
DEBUG_WARN("egl:preset '%s' does not exist", preset);
}
return;
fail:
free(this->presetDir);
this->presetDir = NULL;
if (dir)
closedir(dir);
if (this->presets)
stringlist_free(&this->presets);
}
static void presetError(struct EGL_PostProcess * this, char * message)
{
free(this->presetError);
this->presetError = message;
}
static bool savePreset(struct EGL_PostProcess * this, const char * name)
{
EGL_Filter * filter;
vector_forEach(filter, &this->filters)
egl_filterSaveState(filter);
size_t orderLen = 0;
vector_forEach(filter, &this->filters)
orderLen += strlen(filter->ops.id) + 1;
char order[orderLen];
char * p = order;
vector_forEach(filter, &this->filters)
{
strcpy(p, filter->ops.id);
p += strlen(filter->ops.id);
*p++ = ';';
}
if (p > order)
p[-1] = '\0';
option_set_string("eglFilter", "order", order);
char * path;
alloc_sprintf(&path, "%s/%s", this->presetDir, name);
if (!path)
{
DEBUG_ERROR("Failed to allocate memory");
return false;
}
FILE * file = fopen(path, "w");
if (!file)
{
const char * strError = strerror(errno);
DEBUG_ERROR("Failed to open preset \"%s\" for writing: %s", name, strError);
free(path);
char * error;
alloc_sprintf(&error, "Failed to save preset: %s\nError: %s", name, strError);
if (error)
presetError(this, error);
return false;
}
free(path);
DEBUG_INFO("Saving preset: %s", name);
option_dump_preset(file);
fclose(file);
return true;
}
static int stringListIndex(StringList list, const char * str)
{
unsigned int count = stringlist_count(list);
for (unsigned int i = 0; i < count; ++i)
if (strcmp(stringlist_at(list, i), str) == 0)
return i;
return INT_MAX;
}
static int compareFilterOrder(const void * a_, const void * b_, void * opaque)
{
const EGL_Filter * a = *(const EGL_Filter **)a_;
const EGL_Filter * b = *(const EGL_Filter **)b_;
StringList order = opaque;
return stringListIndex(order, a->ops.id) - stringListIndex(order, b->ops.id);
}
static void reorderFilters(struct EGL_PostProcess * this)
{
StringList order = stringlist_new(false);
if (!order)
{
DEBUG_ERROR("Failed to allocate memory");
return;
}
char * orderStr = strdup(option_get_string("eglFilter", "order"));
if (!orderStr)
{
DEBUG_ERROR("Failed to allocate memory");
stringlist_free(&order);
return;
}
char * p = orderStr;
while (*p)
{
stringlist_push(order, p);
char * end = strchr(p, ';');
if (!end)
break;
*end = '\0';
p = end + 1;
}
qsort_r(vector_data(&this->filters), vector_size(&this->filters),
sizeof(EGL_Filter *), compareFilterOrder, order);
stringlist_free(&order);
free(orderStr);
}
static void loadPreset(struct EGL_PostProcess * this, const char * name)
{
char * path;
alloc_sprintf(&path, "%s/%s", this->presetDir, name);
if (!path)
{
DEBUG_ERROR("Failed to allocate memory");
return;
}
if (!option_load(path))
{
DEBUG_ERROR("Failed to load preset: %s", name);
free(path);
char * error;
alloc_sprintf(&error, "Failed to load preset: %s", name);
if (error)
presetError(this, error);
return;
}
free(path);
DEBUG_INFO("Loading preset: %s", name);
EGL_Filter * filter;
vector_forEach(filter, &this->filters)
egl_filterLoadState(filter);
reorderFilters(this);
}
static void savePresetAs(struct EGL_PostProcess * this)
{
if (!savePreset(this, this->presetEdit))
return;
for (unsigned i = 0; i < stringlist_count(this->presets); ++i)
{
DEBUG_INFO("Saw preset: %s", stringlist_at(this->presets, i));
if (strcmp(stringlist_at(this->presets, i), this->presetEdit) == 0)
{
this->activePreset = i;
return;
}
}
this->activePreset = stringlist_push(this->presets, strdup(this->presetEdit));
}
static void deletePreset(struct EGL_PostProcess * this)
{
char * path;
alloc_sprintf(&path, "%s/%s", this->presetDir,
stringlist_at(this->presets, this->activePreset));
if (!path)
{
DEBUG_ERROR("Failed to allocate memory");
return;
}
unlink(path);
free(path);
stringlist_remove(this->presets, this->activePreset);
if (this->activePreset >= stringlist_count(this->presets))
this->activePreset = stringlist_count(this->presets) - 1;
}
static bool presetsUI(struct EGL_PostProcess * this)
{
if (!this->presets)
return false;
bool redraw = false;
const char * active = "<none>";
if (this->activePreset >= 0)
active = stringlist_at(this->presets, this->activePreset);
if (igBeginCombo("Preset name", active, 0))
{
for (unsigned i = 0; i < stringlist_count(this->presets); ++i)
{
bool selected = i == this->activePreset;
if (igSelectable_Bool(stringlist_at(this->presets, i), selected, 0,
(ImVec2) { 0.0f, 0.0f }))
{
this->activePreset = i;
redraw = true;
loadPreset(this, stringlist_at(this->presets, this->activePreset));
}
if (selected)
igSetItemDefaultFocus();
}
igEndCombo();
}
if (igIsItemHovered(ImGuiHoveredFlags_None))
igSetTooltip("Selecting a preset will load it");
if (igButton("Save preset", (ImVec2) { 0.0f, 0.0f }))
{
if (this->activePreset >= 0)
savePreset(this, stringlist_at(this->presets, this->activePreset));
else
presetError(this, strdup("You must select a preset to save."));
}
if (igIsItemHovered(ImGuiHoveredFlags_None) && this->activePreset >= 0)
igSetTooltip("This will overwrite the preset named: %s",
stringlist_at(this->presets, this->activePreset));
igSameLine(0.0f, -1.0f);
if (igButton("Save preset as...", (ImVec2) { 0.0f, 0.0f }))
{
this->presetEdit[0] = '\0';
igOpenPopup_Str("Save preset as...", ImGuiPopupFlags_None);
}
igSameLine(0.0f, -1.0f);
if (igButton("Delete preset", (ImVec2) { 0.0f, 0.0f }) && this->activePreset >= 0)
deletePreset(this);
if (igBeginPopupModal("Save preset as...", NULL, ImGuiWindowFlags_AlwaysAutoResize))
{
igText("Enter a name for the new preset:");
if (!igIsAnyItemActive())
igSetKeyboardFocusHere(0);
if (igInputText("##name", this->presetEdit, sizeof(this->presetEdit),
ImGuiInputTextFlags_EnterReturnsTrue, NULL, NULL))
{
savePresetAs(this);
igCloseCurrentPopup();
}
if (igButton("Save", (ImVec2) { 0.0f, 0.0f }))
{
savePresetAs(this);
igCloseCurrentPopup();
}
igSameLine(0.0f, -1.0f);
if (igButton("Cancel", (ImVec2) { 0.0f, 0.0f }))
igCloseCurrentPopup();
igEndPopup();
}
if (this->presetError)
igOpenPopup_Str("Preset error", ImGuiPopupFlags_None);
if (igBeginPopupModal("Preset error", NULL, ImGuiWindowFlags_AlwaysAutoResize))
{
igText("%s", this->presetError);
if (!igIsAnyItemActive())
igSetKeyboardFocusHere(0);
if (igButton("OK", (ImVec2) { 0.0f, 0.0f }))
{
free(this->presetError);
this->presetError = NULL;
igCloseCurrentPopup();
}
igEndPopup();
}
return redraw;
}
static void drawDropTarget(void)
{
igPushStyleColor_Vec4(ImGuiCol_Separator, (ImVec4) { 1.0f, 1.0f, 0.0f, 1.0f });
igSeparator();
igPopStyleColor(1);
}
static void configUI(void * opaque, int * id)
{
struct EGL_PostProcess * this = opaque;
bool redraw = false;
redraw |= presetsUI(this);
igSeparator();
static size_t mouseIdx = -1;
static bool moving = false;
static size_t moveIdx = 0;
bool doMove = false;
ImVec2 window, pos;
igGetWindowPos(&window);
igGetMousePos(&pos);
EGL_Filter ** filters = vector_data(&this->filters);
size_t count = vector_size(&this->filters);
for (size_t i = 0; i < count; ++i)
{
EGL_Filter * filter = filters[i];
if (moving && mouseIdx < moveIdx && i == mouseIdx)
drawDropTarget();
igPushID_Ptr(filter);
bool draw = igCollapsingHeader_BoolPtr(filter->ops.name, NULL, 0);
if (igIsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
mouseIdx = i;
bool active = igIsItemActive();
if (draw)
redraw |= egl_filterImguiConfig(filter);
igPopID();
if (moving)
{
if (!igIsMouseDragging(ImGuiMouseButton_Left, -1.0f))
{
moving = false;
doMove = true;
}
}
else
if (active && igIsMouseDragging(ImGuiMouseButton_Left, -1.0f))
{
moveIdx = mouseIdx;
moving = true;
}
if (moving && mouseIdx > moveIdx && i == mouseIdx)
drawDropTarget();
}
if (moving)
{
igSetMouseCursor(ImGuiMouseCursor_Hand);
igSetTooltip(filters[moveIdx]->ops.name);
}
if (doMove)
{
EGL_Filter * tmp = filters[moveIdx];
if (mouseIdx > moveIdx) // moving down
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 *));
filters[mouseIdx] = tmp;
}
if (redraw)
{
atomic_store(&this->modified, true);
app_invalidateWindow(true);
}
}
bool egl_postProcessInit(EGL_PostProcess ** pp)
{
EGL_PostProcess * this = calloc(1, sizeof(*this));
if (!this)
{
DEBUG_ERROR("Failed to allocate memory");
return false;
}
if (!vector_create(&this->filters, sizeof(EGL_Filter *), ARRAY_LENGTH(EGL_Filters)))
{
DEBUG_ERROR("Failed to allocate the filter list");
goto error_this;
}
if (!egl_desktopRectsInit(&this->rects, 1))
{
DEBUG_ERROR("Failed to initialize the desktop rects");
goto error_filters;
}
loadPresetList(this);
reorderFilters(this);
app_overlayConfigRegisterTab("EGL Filters", configUI, this);
*pp = this;
return true;
error_filters:
vector_destroy(&this->filters);
error_this:
free(this);
return false;
}
void egl_postProcessFree(EGL_PostProcess ** pp)
{
if (!*pp)
return;
EGL_PostProcess * this = *pp;
EGL_Filter ** filter;
vector_forEachRef(filter, &this->filters)
egl_filterFree(filter);
vector_destroy(&this->filters);
free(this->presetDir);
if (this->presets)
stringlist_free(&this->presets);
egl_desktopRectsFree(&this->rects);
free(this->presetError);
free(this);
*pp = NULL;
}
bool egl_postProcessAdd(EGL_PostProcess * this, const EGL_FilterOps * ops)
{
EGL_Filter * filter;
if (!egl_filterInit(ops, &filter))
return false;
vector_push(&this->filters, &filter);
return true;
}
bool egl_postProcessConfigModified(EGL_PostProcess * this)
{
return atomic_load(&this->modified);
}
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
EGL_DesktopRects * rects, int desktopWidth, int desktopHeight,
unsigned int targetX, unsigned int targetY)
{
if (targetX == 0 && targetY == 0)
DEBUG_FATAL("targetX || targetY == 0");
EGL_Filter * lastFilter = NULL;
unsigned int sizeX, sizeY;
GLuint texture;
if (egl_textureGet(tex, &texture, &sizeX, &sizeY) != EGL_TEX_STATUS_OK)
return false;
if (atomic_exchange(&this->modified, false))
{
rects = this->rects;
egl_desktopRectsUpdate(rects, NULL, desktopWidth, desktopHeight);
}
GLfloat matrix[6];
egl_desktopRectsMatrix(matrix, desktopWidth, desktopHeight, 0.0f, 0.0f,
1.0f, 1.0f, LG_ROTATE_0);
EGL_FilterRects filterRects = {
.rects = rects,
.matrix = matrix,
.width = desktopWidth,
.height = desktopHeight,
};
EGL_Filter * filter;
vector_forEach(filter, &this->filters)
{
egl_filterSetOutputResHint(filter, targetX, targetY);
if (!egl_filterSetup(filter, tex->format.pixFmt, sizeX, sizeY) ||
!egl_filterPrepare(filter))
continue;
texture = egl_filterRun(filter, &filterRects, texture);
egl_filterGetOutputRes(filter, &sizeX, &sizeY);
if (lastFilter)
egl_filterRelease(lastFilter);
lastFilter = filter;
}
this->output = texture;
this->outputX = sizeX;
this->outputY = sizeY;
return true;
}
GLuint egl_postProcessGetOutput(EGL_PostProcess * this,
unsigned int * outputX, unsigned int * outputY)
{
*outputX = this->outputX;
*outputY = this->outputY;
return this->output;
}

View File

@@ -0,0 +1,47 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include "desktop_rects.h"
#include "filter.h"
#include "texture.h"
typedef struct EGL_PostProcess EGL_PostProcess;
void egl_postProcessEarlyInit(void);
bool egl_postProcessInit(EGL_PostProcess ** pp);
void egl_postProcessFree(EGL_PostProcess ** pp);
/* create and add a filter to this processor */
bool egl_postProcessAdd(EGL_PostProcess * this, const EGL_FilterOps * ops);
/* returns true if the configuration was modified since the last run */
bool egl_postProcessConfigModified(EGL_PostProcess * this);
/* apply the filters to the supplied texture
* 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);
GLuint egl_postProcessGetOutput(EGL_PostProcess * this,
unsigned int * outputX, unsigned int * outputY);

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -30,34 +30,41 @@ struct EGL_Shader
{
bool hasShader;
GLuint shader;
EGL_Uniform * uniforms;
int uniformCount;
int uniformUsed;
};
bool egl_shader_init(EGL_Shader ** this)
bool egl_shaderInit(EGL_Shader ** this)
{
*this = (EGL_Shader *)malloc(sizeof(EGL_Shader));
*this = calloc(1, sizeof(EGL_Shader));
if (!*this)
{
DEBUG_ERROR("Failed to malloc EGL_Shader");
return false;
}
memset(*this, 0, sizeof(EGL_Shader));
return true;
}
void egl_shader_free(EGL_Shader ** this)
void egl_shaderFree(EGL_Shader ** shader)
{
if (!*this)
EGL_Shader * this = *shader;
if (!this)
return;
if ((*this)->hasShader)
glDeleteProgram((*this)->shader);
if (this->hasShader)
glDeleteProgram(this->shader);
free(*this);
*this = NULL;
egl_shaderFreeUniforms(this);
free(this->uniforms);
free(this);
*shader = NULL;
}
bool egl_shader_load(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)
{
char * vertex_code, * fragment_code;
size_t vertex_size, fragment_size;
@@ -79,13 +86,14 @@ bool egl_shader_load(EGL_Shader * this, const char * vertex_file, const char * f
DEBUG_INFO("Loaded fragment shader: %s", fragment_file);
bool ret = egl_shader_compile(this, vertex_code, vertex_size, fragment_code, fragment_size);
bool ret = egl_shaderCompile(this, vertex_code, vertex_size, fragment_code, fragment_size);
free(vertex_code);
free(fragment_code);
return ret;
}
bool egl_shader_compile(EGL_Shader * this, const char * vertex_code, size_t vertex_size, const char * fragment_code, size_t fragment_size)
bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
size_t vertex_size, const char * fragment_code, size_t fragment_size)
{
if (this->hasShader)
{
@@ -111,10 +119,15 @@ bool egl_shader_compile(EGL_Shader * this, const char * vertex_code, size_t vert
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);
@@ -137,10 +150,15 @@ bool egl_shader_compile(EGL_Shader * this, const char * vertex_code, size_t vert
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);
@@ -186,15 +204,286 @@ bool egl_shader_compile(EGL_Shader * this, const char * vertex_code, size_t vert
return true;
}
void egl_shader_use(EGL_Shader * this)
void egl_shaderSetUniforms(EGL_Shader * this, EGL_Uniform * uniforms, int count)
{
egl_shaderFreeUniforms(this);
if (count > this->uniformCount)
{
free(this->uniforms);
this->uniforms = malloc(sizeof(*this->uniforms) * count);
if (!this->uniforms)
{
DEBUG_ERROR("out of memory");
return;
}
this->uniformCount = count;
}
this->uniformUsed = count;
memcpy(this->uniforms, uniforms, sizeof(*this->uniforms) * count);
for(int i = 0; i < this->uniformUsed; ++i)
{
switch(this->uniforms[i].type)
{
case EGL_UNIFORM_TYPE_1FV:
case EGL_UNIFORM_TYPE_2FV:
case EGL_UNIFORM_TYPE_3FV:
case EGL_UNIFORM_TYPE_4FV:
case EGL_UNIFORM_TYPE_1IV:
case EGL_UNIFORM_TYPE_2IV:
case EGL_UNIFORM_TYPE_3IV:
case EGL_UNIFORM_TYPE_4IV:
case EGL_UNIFORM_TYPE_1UIV:
case EGL_UNIFORM_TYPE_2UIV:
case EGL_UNIFORM_TYPE_3UIV:
case EGL_UNIFORM_TYPE_4UIV:
countedBufferAddRef(this->uniforms[i].v);
break;
case EGL_UNIFORM_TYPE_M2FV:
case EGL_UNIFORM_TYPE_M3FV:
case EGL_UNIFORM_TYPE_M4FV:
case EGL_UNIFORM_TYPE_M2x3FV:
case EGL_UNIFORM_TYPE_M3x2FV:
case EGL_UNIFORM_TYPE_M2x4FV:
case EGL_UNIFORM_TYPE_M4x2FV:
case EGL_UNIFORM_TYPE_M3x4FV:
case EGL_UNIFORM_TYPE_M4x3FV:
countedBufferAddRef(this->uniforms[i].m.v);
break;
default:
break;
}
}
};
void egl_shaderFreeUniforms(EGL_Shader * this)
{
for(int i = 0; i < this->uniformUsed; ++i)
{
switch(this->uniforms[i].type)
{
case EGL_UNIFORM_TYPE_1FV:
case EGL_UNIFORM_TYPE_2FV:
case EGL_UNIFORM_TYPE_3FV:
case EGL_UNIFORM_TYPE_4FV:
case EGL_UNIFORM_TYPE_1IV:
case EGL_UNIFORM_TYPE_2IV:
case EGL_UNIFORM_TYPE_3IV:
case EGL_UNIFORM_TYPE_4IV:
case EGL_UNIFORM_TYPE_1UIV:
case EGL_UNIFORM_TYPE_2UIV:
case EGL_UNIFORM_TYPE_3UIV:
case EGL_UNIFORM_TYPE_4UIV:
countedBufferRelease(&this->uniforms[i].v);
break;
case EGL_UNIFORM_TYPE_M2FV:
case EGL_UNIFORM_TYPE_M3FV:
case EGL_UNIFORM_TYPE_M4FV:
case EGL_UNIFORM_TYPE_M2x3FV:
case EGL_UNIFORM_TYPE_M3x2FV:
case EGL_UNIFORM_TYPE_M2x4FV:
case EGL_UNIFORM_TYPE_M4x2FV:
case EGL_UNIFORM_TYPE_M3x4FV:
case EGL_UNIFORM_TYPE_M4x3FV:
countedBufferRelease(&this->uniforms[i].m.v);
break;
default:
break;
}
}
this->uniformUsed = 0;
}
void egl_shaderUse(EGL_Shader * this)
{
if (this->hasShader)
glUseProgram(this->shader);
else
DEBUG_ERROR("Shader program has not been compiled");
for(int i = 0; i < this->uniformUsed; ++i)
{
EGL_Uniform * uniform = &this->uniforms[i];
switch(uniform->type)
{
case EGL_UNIFORM_TYPE_1F:
glUniform1f(uniform->location, uniform->f[0]);
break;
case EGL_UNIFORM_TYPE_2F:
glUniform2f(uniform->location, uniform->f[0], uniform->f[1]);
break;
case EGL_UNIFORM_TYPE_3F:
glUniform3f(uniform->location, uniform->f[0], uniform->f[1],
uniform->f[2]);
break;
case EGL_UNIFORM_TYPE_4F:
glUniform4f(uniform->location, uniform->f[0], uniform->f[1],
uniform->f[2], uniform->f[3]);
break;
case EGL_UNIFORM_TYPE_1I:
glUniform1i(uniform->location, uniform->i[0]);
break;
case EGL_UNIFORM_TYPE_2I:
glUniform2i(uniform->location, uniform->i[0], uniform->i[1]);
break;
case EGL_UNIFORM_TYPE_3I:
glUniform3i(uniform->location, uniform->i[0], uniform->i[1],
uniform->i[2]);
break;
case EGL_UNIFORM_TYPE_4I:
glUniform4i(uniform->location, uniform->i[0], uniform->i[1],
uniform->i[2], uniform->i[3]);
break;
case EGL_UNIFORM_TYPE_1UI:
glUniform1ui(uniform->location, uniform->ui[0]);
break;
case EGL_UNIFORM_TYPE_2UI:
glUniform2ui(uniform->location, uniform->ui[0], uniform->ui[1]);
break;
case EGL_UNIFORM_TYPE_3UI:
glUniform3ui(uniform->location, uniform->ui[0], uniform->ui[1],
uniform->ui[2]);
break;
case EGL_UNIFORM_TYPE_4UI:
glUniform4ui(uniform->location, uniform->ui[0], uniform->ui[1],
uniform->ui[2], uniform->ui[3]);
break;
case EGL_UNIFORM_TYPE_1FV:
glUniform1fv(uniform->location, uniform->v->size / sizeof(GLfloat),
(const GLfloat *)uniform->v->data);
break;
case EGL_UNIFORM_TYPE_2FV:
glUniform2fv(uniform->location, uniform->v->size / sizeof(GLfloat) / 2,
(const GLfloat *)uniform->v->data);
break;
case EGL_UNIFORM_TYPE_3FV:
glUniform3fv(uniform->location, uniform->v->size / sizeof(GLfloat) / 3,
(const GLfloat *)uniform->v->data);
break;
case EGL_UNIFORM_TYPE_4FV:
glUniform4fv(uniform->location, uniform->v->size / sizeof(GLfloat) / 4,
(const GLfloat *)uniform->v->data);
break;
case EGL_UNIFORM_TYPE_1IV:
glUniform1iv(uniform->location, uniform->v->size / sizeof(GLint),
(const GLint *)uniform->v->data);
break;
case EGL_UNIFORM_TYPE_2IV:
glUniform2iv(uniform->location, uniform->v->size / sizeof(GLint) / 2,
(const GLint *)uniform->v->data);
break;
case EGL_UNIFORM_TYPE_3IV:
glUniform3iv(uniform->location, uniform->v->size / sizeof(GLint) / 3,
(const GLint *)uniform->v->data);
break;
case EGL_UNIFORM_TYPE_4IV:
glUniform4iv(uniform->location, uniform->v->size / sizeof(GLint) / 4,
(const GLint *)uniform->v->data);
break;
case EGL_UNIFORM_TYPE_1UIV:
glUniform1uiv(uniform->location, uniform->v->size / sizeof(GLuint),
(const GLuint *)uniform->v->data);
break;
case EGL_UNIFORM_TYPE_2UIV:
glUniform2uiv(uniform->location, uniform->v->size / sizeof(GLuint) / 2,
(const GLuint *)uniform->v->data);
break;
case EGL_UNIFORM_TYPE_3UIV:
glUniform3uiv(uniform->location, uniform->v->size / sizeof(GLuint) / 3,
(const GLuint *)uniform->v->data);
break;
case EGL_UNIFORM_TYPE_4UIV:
glUniform4uiv(uniform->location, uniform->v->size / sizeof(GLuint) / 4,
(const GLuint *)uniform->v->data);
break;
case EGL_UNIFORM_TYPE_M2FV:
glUniformMatrix2fv(uniform->location,
uniform->v->size / sizeof(GLfloat) / 2,
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
break;
case EGL_UNIFORM_TYPE_M3FV:
glUniformMatrix3fv(uniform->location,
uniform->v->size / sizeof(GLfloat) / 3,
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
break;
case EGL_UNIFORM_TYPE_M4FV:
glUniformMatrix4fv(uniform->location,
uniform->v->size / sizeof(GLfloat) / 4,
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
break;
case EGL_UNIFORM_TYPE_M2x3FV:
glUniformMatrix2x3fv(uniform->location,
uniform->v->size / sizeof(GLfloat) / 6,
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
break;
case EGL_UNIFORM_TYPE_M3x2FV:
glUniformMatrix3x2fv(uniform->location,
uniform->v->size / sizeof(GLfloat) / 6,
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
break;
case EGL_UNIFORM_TYPE_M2x4FV:
glUniformMatrix2x4fv(uniform->location,
uniform->v->size / sizeof(GLfloat) / 8,
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
break;
case EGL_UNIFORM_TYPE_M4x2FV:
glUniformMatrix4x2fv(uniform->location,
uniform->v->size / sizeof(GLfloat) / 8,
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
break;
case EGL_UNIFORM_TYPE_M3x4FV:
glUniformMatrix3x4fv(uniform->location,
uniform->v->size / sizeof(GLfloat) / 12,
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
break;
case EGL_UNIFORM_TYPE_M4x3FV:
glUniformMatrix4x3fv(uniform->location,
uniform->v->size / sizeof(GLfloat) / 12,
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
break;
}
}
}
void egl_shader_associate_textures(EGL_Shader * this, const int count)
void egl_shaderAssocTextures(EGL_Shader * this, const int count)
{
char name[] = "sampler1";
glUseProgram(this->shader);
@@ -212,7 +501,7 @@ void egl_shader_associate_textures(EGL_Shader * this, const int count)
glUseProgram(0);
}
GLint egl_shader_get_uniform_location(EGL_Shader * this, const char * name)
GLint egl_shaderGetUniform(EGL_Shader * this, const char * name)
{
if (!this->shader)
{

View File

@@ -1,6 +1,6 @@
/**
* Looking Glass
* Copyright (C) 2017-2021 The Looking Glass Authors
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
@@ -23,16 +23,91 @@
#include <stdbool.h>
#include <stddef.h>
#include <GL/gl.h>
#include <GLES3/gl3.h>
#include "common/countedbuffer.h"
typedef struct EGL_Shader EGL_Shader;
bool egl_shader_init(EGL_Shader ** shader);
void egl_shader_free(EGL_Shader ** shader);
enum EGL_UniformType
{
EGL_UNIFORM_TYPE_1F,
EGL_UNIFORM_TYPE_2F,
EGL_UNIFORM_TYPE_3F,
EGL_UNIFORM_TYPE_4F,
EGL_UNIFORM_TYPE_1I,
EGL_UNIFORM_TYPE_2I,
EGL_UNIFORM_TYPE_3I,
EGL_UNIFORM_TYPE_4I,
EGL_UNIFORM_TYPE_1UI,
EGL_UNIFORM_TYPE_2UI,
EGL_UNIFORM_TYPE_3UI,
EGL_UNIFORM_TYPE_4UI,
bool egl_shader_load (EGL_Shader * model, const char * vertex_file, const char * fragment_file);
bool egl_shader_compile(EGL_Shader * model, const char * vertex_code, size_t vertex_size, const char * fragment_code, size_t fragment_size);
void egl_shader_use (EGL_Shader * shader);
// vectors
EGL_UNIFORM_TYPE_1FV,
EGL_UNIFORM_TYPE_2FV,
EGL_UNIFORM_TYPE_3FV,
EGL_UNIFORM_TYPE_4FV,
EGL_UNIFORM_TYPE_1IV,
EGL_UNIFORM_TYPE_2IV,
EGL_UNIFORM_TYPE_3IV,
EGL_UNIFORM_TYPE_4IV,
EGL_UNIFORM_TYPE_1UIV,
EGL_UNIFORM_TYPE_2UIV,
EGL_UNIFORM_TYPE_3UIV,
EGL_UNIFORM_TYPE_4UIV,
void egl_shader_associate_textures(EGL_Shader * shader, const int count);
GLint egl_shader_get_uniform_location(EGL_Shader * shader, const char * name);
// matrices
EGL_UNIFORM_TYPE_M2FV,
EGL_UNIFORM_TYPE_M3FV,
EGL_UNIFORM_TYPE_M4FV,
EGL_UNIFORM_TYPE_M2x3FV,
EGL_UNIFORM_TYPE_M3x2FV,
EGL_UNIFORM_TYPE_M2x4FV,
EGL_UNIFORM_TYPE_M4x2FV,
EGL_UNIFORM_TYPE_M3x4FV,
EGL_UNIFORM_TYPE_M4x3FV
};
typedef struct EGL_Uniform
{
enum EGL_UniformType type;
GLint location;
union
{
GLfloat f [4];
GLint i [4];
GLuint ui[4];
CountedBuffer * v;
struct
{
CountedBuffer * v;
GLboolean transpose;
}
m;
};
}
EGL_Uniform;
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);
bool egl_shaderCompile(EGL_Shader * model, const char * vertex_code,
size_t vertex_size, const char * fragment_code, size_t fragment_size);
void egl_shaderSetUniforms(EGL_Shader * shader, EGL_Uniform * uniforms,
int count);
void egl_shaderFreeUniforms(EGL_Shader * shader);
void egl_shaderUse(EGL_Shader * shader);
void egl_shaderAssocTextures(EGL_Shader * shader, const int count);
GLint egl_shaderGetUniform(EGL_Shader * shader, const char * name);

View File

@@ -1,12 +0,0 @@
#version 300 es
in highp vec2 uv;
in highp vec2 sz;
out highp vec4 color;
uniform sampler2D sampler1;
void main()
{
color = texelFetch(sampler1, ivec2(uv * sz), 0);
}

View File

@@ -1,24 +0,0 @@
#version 300 es
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;
uniform vec2 screen;
uniform ivec2 size;
uniform vec4 color;
out highp vec2 uv;
out highp vec2 sz;
out highp vec4 c;
void main()
{
sz = vec2(size) + 0.5;
gl_Position.xyz = vertexPosition_modelspace;
gl_Position.w = 1.0;
gl_Position.xy *= screen.xy * sz;
uv = vertexUV;
c = color;
}

View File

@@ -1,9 +0,0 @@
#version 300 es
in highp vec4 c;
out highp vec4 color;
void main()
{
color = c;
}

View File

@@ -0,0 +1,15 @@
#version 300 es
precision mediump float;
layout(location = 0) in vec2 vertex;
out vec2 fragCoord;
uniform vec2 desktopSize;
uniform mat3x2 transform;
void main()
{
vec2 pos = transform * vec3(vertex, 1.0);
gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0);
fragCoord = vertex / desktopSize;
}

View File

@@ -0,0 +1,37 @@
vec4 cbTransform(vec4 color, int cbMode)
{
float L = (17.8824000 * color.r) + (43.516100 * color.g) + (4.11935 * color.b);
float M = (03.4556500 * color.r) + (27.155400 * color.g) + (3.86714 * color.b);
float S = (00.0299566 * color.r) + (00.184309 * color.g) + (1.46709 * color.b);
float l, m, s;
if (cbMode == 1) // Protanope
{
l = 0.0f * L + 2.02344f * M + -2.52581f * S;
m = 0.0f * L + 1.0f * M + 0.0f * S;
s = 0.0f * L + 0.0f * M + 1.0f * S;
}
else if (cbMode == 2) // Deuteranope
{
l = 1.000000 * L + 0.0f * M + 0.00000 * S;
m = 0.494207 * L + 0.0f * M + 1.24827 * S;
s = 0.000000 * L + 0.0f * M + 1.00000 * S;
}
else if (cbMode == 3) // Tritanope
{
l = 1.000000 * L + 0.000000 * M + 0.0 * S;
m = 0.000000 * L + 1.000000 * M + 0.0 * S;
s = -0.395913 * L + 0.801109 * M + 0.0 * S;
}
vec4 error;
error.r = ( 0.080944447900 * l) + (-0.13050440900 * m) + ( 0.116721066 * s);
error.g = (-0.010248533500 * l) + ( 0.05401932660 * m) + (-0.113614708 * s);
error.b = (-0.000365296938 * l) + (-0.00412161469 * m) + ( 0.693511405 * s);
error.a = 0.0;
error = color - error;
color.g += (error.r * 0.7) + (error.g * 1.0);
color.b += (error.r * 0.7) + (error.b * 1.0);
return color;
}

View File

@@ -0,0 +1,32 @@
#if __VERSION__ == 300
vec4 textureGather(sampler2D tex, vec2 uv, int comp)
{
vec4 c0 = textureOffset(tex, uv, ivec2(0,1));
vec4 c1 = textureOffset(tex, uv, ivec2(1,1));
vec4 c2 = textureOffset(tex, uv, ivec2(1,0));
vec4 c3 = textureOffset(tex, uv, ivec2(0,0));
return vec4(c0[comp], c1[comp], c2[comp],c3[comp]);
}
#elif __VERSION__ < 300
vec4 textureGather(sampler2D tex, vec2 uv, int comp)
{
vec4 c3 = texture2D(tex, uv);
return vec4(c3[comp], c3[comp], c3[comp],c3[comp]);
}
#endif
#if __VERSION__ < 310
uint bitfieldExtract(uint val, int off, int size)
{
uint mask = uint((1 << size) - 1);
return uint(val >> off) & mask;
}
uint bitfieldInsert(uint a, uint b, int c, int d)
{
uint mask = ~(0xffffffffu << d) << c;
mask = ~mask;
a &= mask;
return a | (b << c);
}
#endif

View File

@@ -1,12 +1,13 @@
#version 300 es
precision mediump float;
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;
uniform vec4 mouse;
uniform lowp int rotate;
uniform int rotate;
out highp vec2 uv;
out vec2 uv;
void main()
{
@@ -38,6 +39,6 @@ void main()
gl_Position.y = muv.x;
}
gl_Position.w = 1.0;
gl_Position.zw = vec2(0.0, 1.0);
uv = vertexUV;
}

View File

@@ -1,14 +1,29 @@
#version 300 es
precision mediump float;
in highp vec2 uv;
out highp vec4 color;
in vec2 uv;
out vec4 color;
uniform sampler2D sampler1;
uniform float scale;
void main()
{
highp vec4 tmp = texture(sampler1, uv);
vec4 tmp;
if (scale > 1.0)
{
vec2 ts = vec2(textureSize(sampler1, 0));
vec2 px = (uv - (0.5 / ts)) * ts;
if (px.x < 0.0 || px.y < 0.0)
discard;
tmp = texelFetch(sampler1, ivec2(px), 0);
}
else
tmp = texture(sampler1, uv);
if (tmp.rgb == vec3(0.0, 0.0, 0.0))
discard;
color = tmp;
}

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