Compare commits

...

920 Commits

Author SHA1 Message Date
Geoffrey McRae
3df7d30cd5 [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:56:53 +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
Geoffrey McRae
789ee70674 [host] dxgi: print out the adapter details earlier
Before we try and perhaps fail to init DXGI, we should print out what
the device is so that when there is an error report we can immediately
see if the user has the QXL device attached still.
2021-07-12 19:28:13 +10:00
Geoffrey McRae
3c0616bab7 [host] dxgi: print out the output device name to aid with support 2021-07-12 19:03:02 +10:00
Geoffrey McRae
3ce3b573a3 [host] app: fix infinate loop introduced in the last commit 2021-07-12 17:35:16 +10:00
Geoffrey McRae
ce459c24ce [host] app: wait for space in the frame queue
We must always wait for space in the frame queue so that we do not
overwrite memory that is already queued and may be in use by a client.
2021-07-12 17:30:06 +10:00
Geoffrey McRae
7d0b9711bd [host] nvfbc: remove the frameEvent event and associated code
Now that the host application can run the capture interface in
synchronous mode, and NVFBC uses this mode there is no longer need for
the frameEvent.
2021-07-12 17:01:23 +10:00
Geoffrey McRae
e477663a7e [host] app: allow the capture interface to select async or sync mode
While it's correct for DXGI to use a asyncronous waitFrame model, other
capture interfaces such as NvFBC it is not correct. This change allows
the capture interface to specify which is more correct for it and moves
the waitFrame/post into the main thread if async is not desired.
2021-07-12 16:57:22 +10:00
Quantum
eb01efe0cb [host] nvfbc: do not crash when protected content is playing
We return a timeout, so that when protected content finishes playing, we
can immediately resume capture.
2021-07-11 17:54:23 +10:00
Geoffrey McRae
8db4b65dee [host] app: allocate LGMP memory for KVMFRCursor updates without shapes
This changes the host to use a seperate pool of LGMP memory for cursor
positionl updates without shape information helping to prevent
corruption of the shape entries if they are still pending. While this is
not a perfect solution it resolves the issue without making major
changes to LGMP during the RC phase we are currently in.
2021-07-11 12:52:18 +10:00
Quantum
501b270890 [host] nvfbc: optimize change detection loop
Before, we only break out of the current row when a change is detected,
and all subsequent rows are still scanned. Now we break out of the entire
loop. This should make change detection ever so slightly faster.
2021-07-11 10:15:12 +10:00
Quantum
fd8f8b2b28 [host] dxgi: correctly mention AcquireNextFrame in help text
Also fix some formatting issues.

Co-Authored-By: Tudor Brindus <me@tbrindus.ca>
2021-07-11 10:15:12 +10:00
Jonathan Rubenstein
7083b73720 [doc] faq: B4-rc2 touchups 2021-07-10 13:13:23 +10:00
Jonathan Rubenstein
511adbba68 [doc] module: dkms example has explicit "."
This works in dash, so should be portable to most shells.
Will hopefully help users not misinterpret the period as a mistake.
2021-07-10 12:43:10 +10:00
Geoffrey McRae
78b8e2a73c [host] windows: make D3DKMTSetProcessSchedulingPriorityClass global
Testing shows that `D3DKMTSetProcessSchedulingPriorityClass` has a
positive performance impact for NvFBC as well as DXGI, as such always
try to boost the priority for the windows host.
2021-07-10 12:27:30 +10:00
Geoffrey McRae
041b95507d [host] windows/nvfbc/common: strip out broken "enhanced" event logic
This so called "enhanced" event logic is completely flawed and can never
work correctly, better to strip it out and put our faith in windows to
handle the events for us.

And yes, I am fully aware I wrote the utter trash in the first place :)
2021-07-09 10:22:03 +10:00
Geoffrey McRae
bfb47a0ae4 [common] windows: update event fast path to use atomics
Due to a failure to understand atomics when this code was originally
written it has a critical flaw with the fast path where an event could
be signalled when it should not be. This change set corrects this issue
by using atomic operations.
2021-07-09 04:50:14 +10:00
Geoffrey McRae
59efa6f0ad [client] config: default minimizeOnFocusLoss to off 2021-07-08 08:49:00 +10:00
Geoffrey McRae
0acce0737f [common] debug: output to stdout and reset the the color at the end 2021-07-08 07:18:06 +10:00
Jonathan Rubenstein
e96a80dd20 [doc] faq: Correct log file tray operation 2021-07-07 23:19:54 +10:00
Jonathan Rubenstein
c626385845 [doc] index: Nuke trello and add ethereum address 2021-07-07 23:19:54 +10:00
Geoffrey McRae
54d0dc351b [common] debug: remove missed debug_print forward 2021-07-07 23:08:47 +10:00
Geoffrey McRae
1effd5fddc [common] debug: add color support to debug messages
People often miss the warnings about invalid arguments in their command
line, this last minute patch attempts to address this by making
warnings, errors, fixme's and fatal errors stand out if stdout is a TTY.
2021-07-07 23:05:46 +10:00
Geoffrey McRae
ada6ada576 [host] app: always send the cursor position to the client
If the guest VM is not showing a cursor when it starts such as on the
Windows login screen, the client never gets the current position of the
cursor, which prevents the client from attempting to send mouse
movements. This change ensures the client gets the mouse location on
startup.
2021-07-06 09:53:42 +10:00
Jonathan Rubenstein
dacd0ab844 [doc] faq/install: Move Clipboard Synch instructions to Installation 2021-07-06 07:27:16 +10:00
Jonathan Rubenstein
8c43972786 [doc] faq: Touchup 2021-07-06 07:27:16 +10:00
Jonathan Rubenstein
acac8b40cb [doc] module: Touchups 2021-07-06 07:27:16 +10:00
Jonathan Rubenstein
6f5e5aa0c2 [doc] obs: Touchup 2021-07-06 07:27:16 +10:00
Jonathan Rubenstein
13e9488667 [doc] install/troubleshooting: AppArmor instructions already in Installation 2021-07-06 07:27:16 +10:00
Jonathan Rubenstein
13e76e6db2 [doc] troubleshooting: B4 touchup 2021-07-06 07:27:16 +10:00
Jonathan Rubenstein
e2c3ffcbb4 [doc] host: Touchups and sentence flow corrections 2021-07-06 07:27:16 +10:00
Jonathan Rubenstein
733e634f4d [doc] install: Touch ups and tweaks 2021-07-06 07:27:16 +10:00
A.J. Ruckman
1a7b7ce01e [client] Fix narrowing issues with win:shrinkOnUpscale param
Conversion from the float values srcW/srcH to the int values for the client window dimensions would sometimes round down, causing the client to scale instead of matching the host's resolution.
2021-07-06 01:46:33 +10:00
Geoffrey McRae
6c545806ab [all] cmake: check for VERSION file before using git describe
In order to make it easier for package maintainers that manage LG inside
their own git repository we should use the version file if it exists
over calling git describe.
2021-07-05 10:12:01 +10:00
Geoffrey McRae
7c1e8a85cd [client] egl: fix race on resolution change
A resolution switch could cause the renderer state to become invalid as
the texture format may change while it's being rendered. This fixes this
by adding a lock around the format change and render calls to the
renderer.
2021-07-04 21:56:16 +10:00
Geoffrey McRae
82e10c1b7e [host] app: fix inverted cursor shape logic
We should only advance the pointerIndex if the buffer was not swapped
out for storage. This is to ensure that we do not overwrite cursor
memory that the client(s) may still be using.
2021-07-04 19:45:10 +10:00
Geoffrey McRae
cd10e02862 Revert "[host] avoid manual windres command"
This reverts commit d82f2e510d.
While the proposed change is more correct, it breaks the generation of
the file due to failure to locate the resource files, such as
`resources/icon.ico`.
2021-07-04 19:30:06 +10:00
Geoffrey McRae
e9b009db9f [host] app: fix issue with wrong cursor shape being sent for new clients
When a new cursor shape is provided by the capture interface we need to
retain a copy of it incase a new client connects which will not yet have
the cursor shape. The logic here was flawed causing the wrong shape to
be sent to a new client in some instances.
2021-07-04 19:04:54 +10:00
arcnmx
d82f2e510d [host] avoid manual windres command 2021-07-03 04:28:28 +10:00
arcnmx
2386781d4f [host] install cmake target 2021-07-03 03:30:53 +10:00
arcnmx
1345e92ec0 [client] install cmake target 2021-07-03 03:30:53 +10:00
Geoffrey McRae
7b95cc72ed [client] opengl: do not render a sync pending texture 2021-07-02 21:48:31 +10:00
Geoffrey McRae
203ebc73eb [client] x11/opengl: enable double buffering for OpenGL
Drawing to the front buffer directly requires special handling to
prevent seeing the draw progress (avoiding glClear, etc) and as a result
the output is quite bad unless a compositor is running. Also vsync if
enabled will not function without double buffering enabled.

As OpenGL is the legacy fallback, there are no plans to implement clean
front buffer draw support, so just enable double buffering.
2021-07-02 21:45:12 +10:00
Geoffrey McRae
d24b031fc5 [client] opengl: cosmetics 2021-07-02 20:10:25 +10:00
Geoffrey McRae
55d185ef9c [client] opengl: no need to re-bind the already bound texture 2021-07-02 19:51:22 +10:00
Geoffrey McRae
9cbc255448 [client] opengl: do not prematurely delete overlay textures
Overlay textures exist for the lifetime of the renderer and as such
should not be deleted when the resolution changes.
2021-07-02 19:50:31 +10:00
Geoffrey McRae
212cc32097 [client] opengl: deconfigure is idempotent 2021-07-02 19:50:15 +10:00
Quantum
d385b49f5f [common] runningavg: avoid integer overflows
We receive values as int64_t, but when we compute the sum, we store it as
int. This doesn't make sense as we eventually cast it to double when
computing the average. We should instead store the sum as int64_t.
2021-06-28 14:47:19 +10:00
Quantum
6419279c3c [client] wayland/clipboard: prefer text/plain over text/html
When both text/plain and text/html are available, we should prefer the plain
text as we can't paste HTML.
2021-06-26 12:49:47 +10:00
Quantum
f3a1eaf557 [client] wayland: fix full screen toggle in capture mode regression
This was fixed in 9db3cd7b and accidentally broke again in 4b99bba2.
2021-06-20 09:27:33 +10:00
Geoffrey McRae
facbb54776 [client] x11: create _NET_WM_BYPASS_COMPOSITOR if it doesn't exist
If a compositor has not yet started or is not running the atom
`_NET_WM_BYPASS_COMPOSITOR` may not have yet been created. As such we
need to create it so that if a compositor is started it will see this
propery and honour it.
2021-06-17 11:16:26 +10:00
Geoffrey McRae
2160dee23a [host] linux: implement new truncated frame support 2021-06-12 19:10:32 +10:00
Geoffrey McRae
9f25f7fced [client] main: display error when the frame has been truncated 2021-06-12 19:06:32 +10:00
Geoffrey McRae
d36c4f0e83 [host] kvmfr: allow the frame size to exceed the available memory
This change allows the host to still transmit a frame that is truncated
if the IVSHMEM size is too small to allow for a full frame.
2021-06-12 18:44:28 +10:00
Quantum
4b99bba200 [client] wayland: lock confine-related code to avoid race
This should fix the occasional Wayland protocol errors that arise when
the UI thread and the cursor thread race.

Example of error that is fixed:

    zwp_pointer_constraints_v1@11: error 1: a pointer constraint with a wl_pointer of the same wl_seat is already on this surface
2021-06-08 21:04:14 +10:00
Geoffrey McRae
f07e4839b1 [client] egl: fix dmabuf support 2021-06-08 20:51:09 +10:00
Geoffrey McRae
964403b4f8 [client] egl: tex is now an array, so check it properly for assignment 2021-06-08 20:45:37 +10:00
Geoffrey McRae
20d20f5c8a [client] egl: fix incorrect texture usage
The existing code would overwrite the texture's data even if the texture
is currently being used to render to screen. This changeset generates a
texture for each buffer preventing this invalid usage.
2021-06-08 20:41:22 +10:00
Geoffrey McRae
d20e4d1de5 [common] framebuffer: make use of framebuffer_wait 2021-06-08 16:10:34 +10:00
Geoffrey McRae
e87d2f1a20 [common] framebuffer: add additional profiling support 2021-06-08 16:08:44 +10:00
Geoffrey McRae
4a75cc3bcf [common] framebuffer: simplify the remaining calculation
The pitches match so there is no need for the added complexity of this
calculation.
2021-06-08 15:14:54 +10:00
Geoffrey McRae
196050bd23 [common] framebuffer: improve client framebuffer read performance
Extensive profiling reveals that the glibc memcpy performs up to 2x
faster then the existing SIMD implementation that was in use here. This
patch also will copy large 1MB chunks if the pitch of the source and
destination match further increasing throughput.
2021-06-08 15:08:13 +10:00
Quantum
7300d00f66 [common] option: handle ini lines without trailing newline
Before, if the last line of an ini file has no trailing \n, it is ignored.
This commit changes the parser to process such lines.
2021-06-07 18:22:19 +10:00
Geoffrey McRae
21d86dd31d [common] option: prevent ascii filter from eating newlines 2021-06-07 11:29:17 +10:00
Geoffrey McRae
44bff58aa8 [common] option: ignore non-typeable ascii characters
Under windows text editors like to default to saving in UTF8 with a byte
order mask prefix preventing proper parsing of any option that is on the
first line of the file. Since the configuration file is only intended to
be plain ascii this change ignores all non-ascii characters solving this
issue.
2021-06-07 11:25:35 +10:00
Quantum
5c00f73d23 [client] x11: use correct spelling of _NET_WM_BYPASS_COMPOSITOR
The incorrect spelling, NETWM_BYPASS_COMPOSITOR, somehow worked in the
past, but it appears to not work right now. Corrgan on Discord reported
the issue and confirmed that changing the spelling allowed the compositor
to be bypassed and the client to update faster than 60 Hz on his mixed
refresh rate setup.
2021-06-07 11:17:06 +10:00
Quantum
62c7fc2588 [common] runningavg: refresh copyright notice 2021-06-06 13:36:55 +10:00
Quantum
e6eeb2bb65 [all] check if pull request author is in AUTHORS 2021-06-06 13:17:39 +10:00
Quantum
3ca2030c22 [all] add AUTHORS file 2021-06-06 12:59:59 +10:00
Geoffrey McRae
f02d61d665 [host] dxgi: sleep until it's close to time to map
This change adds an average function to time how long it takes the GPU
to copy and map the texture, and then uses this average to sleep for 80%
of this average lowering CPU usage and potentially decreasing lock
contention.
2021-06-06 12:26:36 +10:00
Quantum
7f5f46c448 [client] wayland: offer all supported clipboard formats
This commit restructures the Wayland clipboard handling for host->VM.

Before, we select one clipboard format and buffers the data for it, to
be presented to spice when needed.

Now, we simply offer all clipboard formats supported, and only when spice
asks for the data do we actually read the wl_data_offer. The wl_data_offer
is kept around until a new offer is presented, the offer invalidated, or
when we lose keyboard focus. This is in accordance with the specification
for wl_data_device::selection, which states that:

> The data_offer is valid until a new data_offer or NULL is received or
> until the client loses keyboard focus. The client must destroy the
> previous selection data_offer, if any, upon receiving this event.

We still buffer the entire clipboard data into memory because we have no
knowledge of the clipboard data size in advance and cannot do incremental
transfers.

Furthermore, if the user performs drag-and-drop on our window, we may have
need to handle multiple wl_data_offer objects at the same time. Therefore,
instead of storing state on the global wlCb object, we instead allocate
memory and store it as user_data on the wl_data_offer. As a result, we also
handle drag-and-drop so that we can free the memory.
2021-06-06 12:20:42 +10:00
Quantum
24d0aa0c18 [all] normalize copyright on all source files 2021-06-06 11:53:05 +10:00
Quantum
c0bbc88e9a [all] add script to refresh copyright
Simply run `./refresh-copyright` in the repository to normalize the copyright
notice on all project source files.
2021-06-06 11:53:05 +10:00
Geoffrey McRae
fcf6abc7c6 [host] NvFBC/DXGI: make DXGI the default instead of the fallback
It has been detemined that a failure to init NvFBC causes a 20-30%
performance penalty on non NvFBC supported hardware (GeForce) when using
DXGI, as such reverse the order and default to using DXGI as our first
option.

If NvFBC is still desired, pr #500 added the option `app:capture` which
can be used to force NvFBC.
2021-06-06 06:14:24 +10:00
Geoffrey McRae
1d7eb50608 [host] cmake: force -O3 for release with debug builds 2021-06-06 02:25:48 +10:00
Geoffrey McRae
0d9b0bd367 [host] dxgi: increase maxTextures default to 4
Testing shows that at high frame rates the default of 3 is hampering
performance, increasing this to 4 yields a substantial performance
improvement.
2021-06-06 01:35:00 +10:00
Geoffrey McRae
87568f97eb [host] linux: add missing library to fix compilation 2021-06-04 12:44:21 +10:00
Geoffrey McRae
0b4e98881f [host] app: add os_showMessage for Linux
Linux doesn't have a generic way to show a dialog box, so we just use
DEBUG_INFO to print the message out to the console.
2021-06-04 12:39:11 +10:00
Geoffrey McRae
fb1b30b728 [host] app: calculate and report the required IVSHMEM size
One of the most common issues reported in the support channels is the
IVSHMEM size being too small. This change adds a calculation to
determine an optimal size and uses the new `os_showMessage` platform
method to display a message box to the user with the error.
2021-06-04 12:31:15 +10:00
Quantum
75a9e38e3a [client] wayland: remove sync transfer for VM->host copies
Since we are using epoll, there is no longer any need to remove O_NONBLOCK
with fcntl, and the comment is also no longer relevant.
2021-06-02 22:22:59 +10:00
Geoffrey McRae
c4468b1a0d [client] spice: update PureSpice to fix #556 2021-06-02 22:19:37 +10:00
Geoffrey McRae
b2974c38ef [client] app: cleanup clipboard option logic 2021-06-02 22:19:37 +10:00
arcnmx
b657b57016 [obs] install into plugin dir 2021-06-02 07:36:22 +10:00
arcnmx
6058a0e243 [obs] fix oob cursor writes 2021-06-01 14:37:45 +10:00
arcnmx
1462ab0cb4 [obs] fix cursor blending 2021-06-01 14:37:45 +10:00
arcnmx
186b0f1efd [obs] fix monochrome cursor crop 2021-06-01 14:37:45 +10:00
Quantum
000d91afca [client] wayland: prefer PNG over other image types
Support for non-PNG types is optional in the spice agent, so we should
avoid sending those if PNG is available.

Currently, the spice VDAgent supports only PNG and BMP formats.
2021-05-30 14:32:18 +10:00
Quantum
437bba1897 [client] wayland: update to support new spice clipboard type list 2021-05-30 12:39:00 +10:00
Geoffrey McRae
9ddfacc1a8 [client] x11: update to support new spice clipboard type list 2021-05-30 11:37:31 +10:00
Geoffrey McRae
28640295f1 [client] spice: update to support sending clipboard type list 2021-05-30 11:37:15 +10:00
Geoffrey McRae
118d9a086e [client] x11: implement win:noResize
This is the final fix for #506
2021-05-28 20:16:22 +10:00
Geoffrey McRae
77c2b895c6 [client] app: only minimize if lost focus when in full screen mode
This fixes a regression caused by the move from SDL2 which handled this
itself. We should only minimize when focus is lost if the application
was in full screen mode.
2021-05-28 19:32:26 +10:00
Geoffrey McRae
e6ed5574d5 [client] spice: update PureSpice submodule to fix agent message issues 2021-05-28 17:12:20 +10:00
Geoffrey McRae
09a7e177fa [client] core: fix use of unintalized variable
util_guestCurToLocal may not be able to provide the local position if
we do not yet know where the guest cursor is, or the destination render
rect dimensions. Acting on this when this information is unknown causes
undefined behaivour.
2021-05-28 17:00:13 +10:00
Geoffrey McRae
2e239ede3f [client] spice: update PureSpice again for more clipboard fixes 2021-05-27 21:11:43 +10:00
Geoffrey McRae
3dfd8e8dfd [client] spice: update PureSpice to fix large buffer paste bug 2021-05-27 20:48:01 +10:00
Geoffrey McRae
1e4e668c81 [client] spice: update PureSpice to fix clipboard stack overflow bug 2021-05-27 20:04:06 +10:00
Geoffrey McRae
3be645a357 [client] x11: fix BadAtom when pasting large buffers
To start a clipboard incr transfer the client has to delete the INCR
window property as the reply to the selection. This deletion generates a
property change event with the type delete, errornously triggering the
incr processing of the data. This patch corrects this by ignoring
property deletions.
2021-05-27 18:41:54 +10:00
Geoffrey McRae
95ec206b36 [client] x11: cosmetics 2021-05-27 18:41:54 +10:00
Jonathan Rubenstein
a7e4d9ec68 [doc] README.md: Correct docs link 2021-05-25 20:04:16 +10:00
Jonathan Rubenstein
86c7cfe589 [doc] Rerganize content into install and build pages 2021-05-25 19:52:08 +10:00
Jonathan Rubenstein
6da04655f0 [doc] troubleshooting: Move link to new website 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
fcc35331a3 [doc] README: Emphasis fix 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
b7765a22df [doc] index: IRC moved 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
4a2d6851b1 [all] README: Remove most info already in docs/ 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
397af46909 [doc] index: Correct spelling and add irc, trello, and donate info 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
8920d01dab [doc] module: Change AppArmor path for libvirt to include local/ 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
00c15238e8 [doc] host: Better formatting for Windows on Windows 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
75d48de905 [doc] host: Reformat build instructions for all platforms 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
d5fb1bab9a [all] README: Comment out inactive docs url 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
94ffcfba23 [doc] install: Add warning about downloading with git 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
5f20a026b2 [doc] install: Rename Running the Client to Usage
Also move relevant sentence into Usage
2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
b64fa56b26 [doc] module: Add instructions for systemd-modules-load.d 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
df869a8e6b [all] README.md: Update Documentation section with /doc 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
5fb6c73263 [doc] host: Imported from host/README.md
Placed at bottom of Technical FAQ to hide it
host/README.md removed
2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
2bee8d91b0 [module] README.md: Deleted file absorbed into /doc 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
77a4c114e9 [doc] module: Import from module/README.md 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
b2e460d19d [client] doc: Remove README.md as it has been absorbed into doc/install.rst 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
565b14f517 [doc] install: Add keybinds and full cli options from client/README.md 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
71cde7065b [doc] install: Add Keybindings from client/README.md 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
39c6b97582 [doc] install: Correct command line examples 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
211146b1a9 [doc] install: Add Command Line Options section
May in the future include up to date client options from the client
2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
d44b55440f [doc] conf: Enable fixed sidebar 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
a868370a63 [doc] install: Add 'local' to AppArmor abstraction path
This prevents system upgrades from overriding the addition
2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
19cb37dc58 [doc] obs: Fix code block for installing package
Co-authored-by: Guanzhong Chen <quantum2048@gmail.com>
2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
1e7b368329 [doc] faq: Remove virt-manager instructions 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
c66a5c526a [doc] faq: Grammar and page flow improvements 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
3972482922 [doc] install: Grammar, formatting, and page flow 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
ab5271a2f7 [doc] credits: Add real name to quantum 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
2875adc7c3 [doc] credits: Add real name to xyene 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
e24b2455d1 [doc] index: Removed "Contents:" 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
a89c7993f9 [doc] credits: Add credits 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
940269c220 [doc] conf: Remove text under logo 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
5cb40875a2 [doc] install: Update Determining Memory formula 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
83f060b895 [doc] config: Add LG logo and favicon 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
02d6fd5133 [doc] install: Remove unofficial chocolatey package tip 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
0afaf040a1 [doc] troubleshooting: Wayland supports clipboard and remove reference to ubuntu 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
9084e261a7 [doc] install: Remove 7-zip and add period. 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
e5869942ca [doc] install: Convert bootleg notes into true notes 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
9d910ec37e [doc] conf: Correct spelling of "xyene" 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
fa871e9678 [doc] faq: FAQt checking and grammar changes
No I couldn't resist the pun no matter how much I tried
2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
186038fd2e [doc] conf: Add master_doc to explicitly use index.rst
This is for compatibility with older sphinx versions
2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
9669cd5ed7 [doc] index: Add new welcome message with release version 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
91e55b9d5f [doc] Import from wiki
Imported Installation, FAQ, Troubleshooting, OBS, and Technical FAQ pages
from wiki. Edits done to content as this is no longer a wiki, but versioned
in-repo documentation
2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
5b0ca835f8 [doc] Add _build directory to new .gitignore file 2021-05-25 16:57:52 +10:00
Jonathan Rubenstein
30d2035a3e [doc] Initialize Sphinx in /doc 2021-05-25 16:57:52 +10:00
arcnmx
1a530da139 [obs] fix oob cursor writes
Allocate enough memory for the cursor data!
2021-05-23 12:28:35 +10:00
Quantum
9db3cd7b51 [client] wayland: avoid protocol error when warping in capture mode
When using locked pointer (i.e. capture mode), it doesn't make sense to
warp the cursor, so we disable it. This fixes #540.
2021-05-22 13:22:31 +10:00
Quantum
fd531bd39b [client] wayland: use util_hasGLExt for swapping buffers with damage 2021-05-19 19:37:14 +10:00
Quantum
e70cfd84fb [client] egl: use eglSwapBuffersWithDamageKHR for cursor-only updates
Instead of damaging the entire surface when rendering a cursor move,
we can use the EGL_KHR_swap_buffers_with_damage extension to only
damage the part of the window covered by the cursor. This should
reduce the cursor movement latency on Wayland.
2021-05-19 18:56:36 +10:00
Quantum
f9a9953071 [client] egl: use new util_hasGLExt helper to check extensions
We previously used strstr, which can be prone to false positives when
the name of one extension is a substring of another extension.

This commit creates the helper function util_hasGLExt, which asserts
that the substring found in extension list is bounded by either spaces
or the beginning/end of the string.
2021-05-19 15:58:54 +10:00
Quantum
edbd6f6ade [client] wayland: implement minimize operation
This commit implements minimize for both xdg-shell and libdecor
pseudo-shell. This fixes #535.
2021-05-08 10:08:37 +10:00
Jonathan Rubenstein
c6d7fb8dd0 [common] option: Reformat help and support rST tables
Corners of table have '+' added, and adds new command line flag --rst-help,
which adds some extra formatting to the make it an rST compliant table for the
in-line docs.
2021-05-06 22:32:36 +10:00
Geoffrey McRae
903cc9815f [client] x11/sdl/wayland: implement new minimize (stub for wayland)
This implements the new minimize function introduced in the last commit
for x11 and SDL. Wayland at current is just a stub and needs some
attention.
2021-05-06 22:25:38 +10:00
Geoffrey McRae
f698e4589d [client] ds: add new minimize function to the ds interface
This change is to allow the application to minimize the window on focus
loss if the user's preferences are configured to do this.
2021-05-06 22:24:42 +10:00
Geoffrey McRae
bdfb18299d [client] core: clamp the local position to the dstRect correctly
When running in a mode that is letterboxed the dstRect origin is not 0x0
2021-05-05 23:18:13 +10:00
Geoffrey McRae
9dd11b5e04 [client] main: fix failure to obey win:autoResize 2021-05-05 23:17:15 +10:00
Quantum
0f2fd84724 [client] ds: avoid util_cursorToInt when warping pointer
Using util_cursorToInt messes with the error tracking for normal movements,
and is not necessary since we are computing an absolute position on the
client window.

Instead, we should pass doubles directly to display servers and let them
decide how to best handle them. For example, XIWarpPointer accepts doubles
directly.
2021-05-04 19:02:13 +10:00
Quantum
71c9680245 [client] wayland: use locked pointer to implement capture mode
This prevents the host cursor from moving into another window in capture
mode, solving the problem of input going to an overlapping window in
capture mode, and also preventing loss of focus with focus_follows_mouse.
2021-05-04 18:17:19 +10:00
Quantum
00c2773e6b [client] ds: separate grab and capture logic
Currently, (un)grabPointer is used both for tracking/confining the mouse
in normal mode, as well as entering/exiting capture mode. This makes it
impossible to use separate cursor logic for capture mode, which is needed
to deal with overlapping windows for the Wayland backend.

This commit creates separate (un)capturePointer for entering/exiting
capture mode. There should be no behaviour changes.
2021-05-04 18:17:19 +10:00
Geoffrey McRae
7acbc57bc7 [client] x11: remove pointer motion event mask
This was added as a test when developing the overlapping window code and
is not needed/used.
2021-05-04 18:11:30 +10:00
eater
62725bcb9d [client] egl: show error for glMapBufferRange instead of glBindbuffer 2021-05-04 10:30:03 +10:00
Quantum
ee380451ca [client] wayland: synchronize host cursor position with guest
This mirrors the x11 implementation, allowing the pointer to move
correctly into overlapping windows.
2021-05-04 09:18:35 +10:00
Geoffrey McRae
d0a12f6097 [client] ds: detect when the cursor exits into an overlapping window
This adds a new method to the display server interface to allow the
application to notify the ds when there is a guest cursor position
update along with the translated local guest cursor position. This makes
it possible for the display server to keep the local cursor position in
sync with the guest cursor so that window leave events can be detected
when the cursor would move into an overlapping window.

Wayland currently just has a stub for this, and the X11 implementation
still needs some minor tweaking.
2021-05-04 06:35:36 +10:00
Geoffrey McRae
cd56321e65 [client] util: move and rename clamp to util_clamp
As this is a generic utility function it should live in `util.h`
allowing it to be used elsewhere in the project.
2021-05-04 05:14:33 +10:00
Quantum
a2d5c08460 [client] ui: add input:helpMenuDelay option
This option controls the time period (in ms) after which the help menu
appears when holding down the escape key. After this time period,
capture mode is no longer toggled.

This fixes #527.
2021-05-01 12:05:12 +10:00
A.J. Ruckman
dec4c8533c [client] win: add new option win:shrinkOnUpscale
This option constrains the client's window dimensions to the guest's resolution when 'dontUpscale' is also enabled
2021-04-29 13:35:00 +10:00
Geoffrey McRae
60feb3050c [client] config: update description for DMA as VM-Host is now supported 2021-04-29 12:56:35 +10:00
Geoffrey McRae
3c1282c92b [client] spice: put new option into the correct category 2021-04-29 12:54:13 +10:00
Geoffrey McRae
9900b263d4 [client] spice: new option spice:showCursorDot added
This option if disabled will prevent the client from showing a "dot"
cursor when the mouse is over the window, but the window is unfocused.
2021-04-29 12:50:43 +10:00
Geoffrey McRae
4c0c8f804f [client] x11: cosmetics 2021-04-29 12:26:18 +10:00
Geoffrey McRae
3912d3411c [client] x11: process clipboard events early
Due to the logic in the event loop property events may get filtered out
that were clipboard related. This changes ensures the clipboard event
handler code gets to run first avoiding this issue.
2021-04-29 12:24:23 +10:00
Geoffrey McRae
9015706fcb [client] x11: fix failure to paste clipboard into the guest
The clipboard atoms may not exist yet and as such we must create them if
this is the case. Failure to do so results in `SEL_DATA` being zero
breaking the clipboard paste mechanics
2021-04-29 12:22:54 +10:00
Quantum
ec81a353c2 [host] nvfbc: fix null dereference in mouse hook handler
Since we now let the mouse hook linger until the process is killed, the
cursor event that the hook signals may now be null, as the capture could
have stopped. If the hook fires during this time, a crash occurs.
2021-04-29 11:53:19 +10:00
Quantum
6a1ec9420e [host] service: compare SIDs directly without string conversion
Instead of converting every SID to string with ConvertSidToStringSidA
and compare it with the magical SID string for local system with strcmp,
we could instead create the local system SID and compare directly with
EqualSid.
2021-04-29 11:52:23 +10:00
Quantum
93d97424df [host] service: disable handle inheritance when spawning host
We don't actually have any handles that should be inherited, so specifying
TRUE for bInheritHandles to CreateProcessAsUserA is pointless.

Furthermore, according to MSDN, "[y]ou cannot inherit handles across
sessions," and we are spawning the host in a different session, so this
is even more pointless.
2021-04-29 11:51:41 +10:00
Quantum
2afad4e1be [host] service: disable privileges as soon as they are not needed
It is usually considered good practice to enable elevated privileges
for the shortest duration possible.
2021-04-29 11:51:30 +10:00
vroad
5d5eb47598 [client] egl: do not leak fence when using DMABUF 2021-04-25 14:32:17 +10:00
Quantum
30c3b399f2 [client] wayland: do not leak cursor wl_buffer and wl_surface 2021-04-19 17:03:25 +10:00
Quantum
a380803d37 [client] libdecor: fix gcc compile warnings-turned-errors
This commit fixes the -Wmissing-field-initializers warning, which can only
be disabled with a pragma. GCC wants us to Initialize libdecor reserved
fields, which requires knowing how many reserved fields there are.
This is an implementation detail, and so we can only disable the warning.

This also fixes -Wincompatible-pointer-types, which is an actual bug.
2021-04-19 17:03:13 +10:00
Geoffrey McRae
168d9890ae [client] spice: update the PureSpice submodule 2021-04-19 11:24:37 +10:00
Geoffrey McRae
ae36abb1ca [client] x11: fallback to a utility window if motif atom is missing
If the window manager does not support the motif hints then fallback to
creating a utility window, do not do both. A utility window is a
sub-optimal fallback as it may prevent the application being shown in
the taskbar or as a running application as has been reported on KDE.
2021-03-27 21:42:47 +11:00
Mikko Rasa
358515f4a8 [client] x11: Find and use the correct valuators for mouse axes
Using the first two valuators present in the event is incorrect.  Events
with only one valuator set, such as those sent by the Xorg evdev driver
when the mouse moved along one axis only, were being discarded.  On the
other hand, mice with multiple scroll wheels may be able to emit events
with two scroll wheel valuators set.

The XInput2 specification is light on details, but "Rel X" and "Rel Y"
appear to be the de facto standard names for the motion valuators.  If
valuators with those labels are not found, fall back to using valuators
with numbers 0 and 1.
2021-03-27 21:12:52 +11:00
Geoffrey McRae
d74307223f [client] x11: set the window center via WM_SIZE_HINTS
Due to the confusing nature of the x11 protocol, bit_gravity and
win_gravity are not what they appear to be. These do not describe the
window position but rather the pixels/subwindows when the window is
resized. Instead set the gravity via the WM_SIZE_HINTS property which
all modern window managers should respect.
2021-03-27 21:05:27 +11:00
Geoffrey McRae
5aa7a391ac [client] x11: implement center, borderless and maximized parameters 2021-03-27 11:17:48 +11:00
Geoffrey McRae
1bf9fb7d73 [client] x11: fix segfault caused by clipboard processing on shutdown 2021-03-27 10:06:57 +11:00
Geoffrey McRae
84d4c18c48 [client] x11: split out atoms into a seperate structure 2021-03-27 10:05:57 +11:00
Geoffrey McRae
2f14d64289 [client] x11: split out clipboard code for maintainabillity 2021-03-27 09:21:32 +11:00
Geoffrey McRae
ab4d7cb94b [client] x11: _NET_WM_* hints are ATOMS not CARDINAL
This fixes the fullscreen and likely borderless issue too that people
have been reporting on X11 under gnome. Thanks to tdb in discord for
spotting the error.
2021-03-25 09:29:06 +11:00
Quantum
5bfb33c739 [host] windows: re-implement open log safely
Instead of doing ShellExecute from the service, we instead get the token
of the currently logged in user, and do CreateProcessAsUserA to run
notepad with that token. This should be safe.
2021-03-22 09:06:07 +11:00
Quantum
a089c4ea32 [host] service: introduce fatal errors for ivshmem failures
Also for failure to parse command line. For these errors, restarting
with exponential backoff will not help: no amount of restarting the
service could possibly make the ivshmem device exist or larger, so
we shouldn't try.
2021-03-22 08:54:58 +11:00
Quantum
c12c6ea3c7 [host] service: retry a few times when host fails to restart
Certain users of Radeon cards have observed that the host fails to start
at boot, with D3D11CreateDevice failing with HSTATUS 0x887a0004, which
translates to "The specified device interface or feature level is not
supported on this system."

This failure results in a LG_HOST_EXIT_FAILED exit code, which the service
does not attempt to restart. The user has to manually restart the service
for the host application to work.

These users reported that the host application started fine on
B2. This strongly suggests that the fix to enable capturing the login
screen made the host application start too early during the boot process,
and the graphics driver did not have time to initialize fully.

This PR allows the service to retry a few times on LG_HOST_EXIT_FAILED,
with exponential backoff, before giving up. This should cover this bug
and other similar bugs related to the early initialization which I do not
have logs for.
2021-03-22 08:54:58 +11:00
Quantum
a4290b290b [host] cmake: link correctly against libxcb-xfixes.so
We are using <xcb/xfixes.h>, so it makes sense to link against the
xcb library instead of libXfixes.so directly.
2021-03-15 12:12:48 +11:00
Quantum
69dc598857 [host] ci: install libxcb-xfixes0-dev package
This should fix the <xcb/xfixes.h> not found error.
2021-03-15 12:12:48 +11:00
Quantum
52a445e2f5 [client] ci: correctly set CMAKE_BUILD_TYPE
A $ sign is needed for GitHub Actions to interpolate the variable.
2021-03-15 10:54:55 +11:00
Quantum
ba3243f88a [client] wayland: make input handlers aware of multiple surfaces
This prevents input handlers from breaking in presence of subsurfaces,
which are used by libdecor for client-side decorations.
2021-03-15 10:54:35 +11:00
Quantum
d6a290a31d [client] wayland: implement libdecor pseudo-shell backend
This allows decorations to be drawn on GNOME and other crazy compositors
that do not support server-side decorations.
2021-03-15 10:54:35 +11:00
Quantum
82e107af8a [client] cmake: support building with libdecor 2021-03-15 10:54:35 +11:00
Quantum
4031a862df [client] wayland: split xdg-shell handling into a module
This will allow libdecor to be added as a new pseudo-shell.
2021-03-15 10:54:35 +11:00
Quantum
854b53e28c [client] wayland: use consistent naming in poll module
Some of the functions were named WaylandEpoll*, which is inconsistent.
This commit changed them to be WaylandPoll*.
2021-03-03 16:38:23 +11:00
Quantum
fc7dd7dbb7 [common] cmake: correctly link static libbfd.a
When linking against libbfd.so, just passing libbfd.so to the compiler is
sufficient. When linking against the static version libbfd.a, however,
we must additionally link against libiberty.a and libz.a.

This commit adds a CMake helper to find the correct libraries that need
to be passed to link against libbfd correctly.
2021-03-03 16:37:34 +11:00
Quantum
b00c25a822 [common] crash: fix Linux compilation with -DENABLE_BACKTRACE=NO
Essentially, debug.h defines printTrace as an empty macro when the macro
ENABLE_BACKTRACE is not defined, breaking the compilation of crash.c.

Fixed by defining a private macro for debug.h only to avoid clashing.
2021-03-03 16:36:50 +11:00
Quantum
f2c0b8c0b4 [host] allow user to select capture backend
This commit introduces a new option, app:capture, which can be set to
either DXGI or NvFBC to force the host application to use that backend.

This is very useful for testing DXGI on Quadro cards, which would default
to running with NvFBC.
2021-02-27 17:41:44 +11:00
Quantum
54da11a206 [host] free capture interface on exit
This is needed for proper cleanup.

Freeing the capture interface also avoids a crash when using the NvFBC
backend. This is because we moved the mouse hook removal to nvfbc_free.
If nvfbc_free is not called before we start freeing LGMP memory, the
mouse hook would end up writing the cursor position into an invalid
memory address, causing an access violation.
2021-02-27 17:38:12 +11:00
Tudor Brindus
952f50eb8c [client] ds: build SDL backend in CI
SDL was made optional, and defaulted to off, in c34fe10. However, we
should still make sure it continues building.
2021-02-27 14:49:25 +11:00
Tudor Brindus
484012a90c [client] wayland: bind no-op drag-and-drop handlers
Otherwise, libwayland aborts when a drag into the Looking Glass window
occurs.
2021-02-27 14:49:06 +11:00
Tudor Brindus
97009027d1 [client] wayland: require wl_compositor v3 instead of v4
The only addition to v4 was `wl_surface_damage_buffer`, which we do not
use.

This change should allow running on more compositors (even though v4 is
already old -- 5 years now).

Ref
3384f69ecf.
2021-02-27 14:48:40 +11:00
Tudor Brindus
d6b8823dce [client] wayland: gracefully downgrade to wl_output v2
We don't necessarily need `wl_output.release`, which is the only
addition in v3.

This allows Looking Glass to run on Ubuntu 20.04 without having to go
difficult lengths to acquire newer Wayland packages. Since 20.04 is an
LTS release, this seems worthwhile for the small amount of complexity
this introduces.

Fixes
https://forum.level1techs.com/t/latest-build-allow-inhibiting-shortcuts-dialog-ubuntu/168684/6.
2021-02-27 14:48:28 +11:00
Quantum
5ad70ccc41 [common] crash/linux: generate core dump when handling SIGSEGV
Calling abort() instead of exit() will generate a core dump, allowing gdb
to be used when the client crashes. This is desirable for the following
reasons:

1. gdb can be used to inspect the call stack with far more detail than the
   our quick stack trace code, and also allows the access to the heap.
2. Our SIGSEGV handler is unable to use debug symbols for shared libraries,
   making it impossible to debug bugs involving drivers and similar.
2021-02-27 13:34:03 +11:00
Quantum
31c1452451 [client] readme: remove mention of Wayland's lack of warp
We have an implementation of Wayland cursor warping now, so there is no
need for the section saying that Wayland lacks the feature.
2021-02-26 10:29:07 +11:00
Quantum
8c6cff012d [client] readme: remove mention of SDL
SDL is default to off and its usage should not be encouraged, so
-DENABLE_SDL=ON is not mentioned as an option in the README.
2021-02-26 10:28:51 +11:00
Quantum
c34fe10f23 [client] ds: make SDL display server optional
This commit also moved the SDL dependencies into the SDL backend so that
-DENABLE_SDL=NO builds do not link against SDL.
2021-02-26 10:28:51 +11:00
Geoffrey McRae
f3b46e6d4f [client] app: delay showing the escape help for 250ms
This change prevents the help text being shown unless the escape key has
been held for a minimum of 250ms (reduced from 500ms).
2021-02-26 10:21:56 +11:00
Quantum
521ac706c1 [client] app: do not toggle capture if escapeKey is held
When users press escapeKey for a long time, they probably want to
see the help text instead of actually toggling capture. Therefore,
if the key is held down for more than 500 ms, we assume the user
wants to look at the help text and do not toggle capture mode.

500 ms seems to be a decent compromise, allowing slow presses, but
is not enough time for the user to have looked at the help text.
2021-02-26 09:59:17 +11:00
Quantum
9886c2bf40 [client] fonts: remove SDL font backend 2021-02-26 09:58:21 +11:00
Geoffrey McRae
c96ee0f786 [client] font: fix left shift runtime error due to signed shift 2021-02-26 09:54:08 +11:00
Quantum
5551420983 [client] wayland: clean up #includes in wayland.c
This was missed when splitting up wayland.c into multiple modules.
This commit also drops the useless #include <SDL2/SDL.h>, bringing
SDL removal one step closer.
2021-02-26 09:44:59 +11:00
Quantum
84e14edfe7 [client] opengl: remove dependency on SDL 2021-02-26 09:44:34 +11:00
Quantum
798a1aadb5 [client] wayland: fail gracefully when interfaces are too old
Before, we just attempt to bind, causing an obscure Wayland error.
This commit checks the interface versions and print better error
messages.
2021-02-26 09:43:38 +11:00
Quantum
1111045546 [client] freetype: implement UTF-8 handling 2021-02-26 09:43:06 +11:00
Quantum
5a075744bd [client] fonts: add freetype font rendering backend 2021-02-26 09:43:06 +11:00
Quantum
2da81e0b5a [client] display correct log path on Windows
We moved it to %ProgramData%\Looking Glass (host), but the client still
says it's in %TEMP%.
2021-02-23 20:28:48 +11:00
Quantum
389d8824e2 [client] wayland: handle output scale changes
If the scale factor of an wl_output changes while the client is running,
the maximum scale factor is not updated. This may result in incorrect
scaling.

Therefore, when the scale factor is changed, we should generate a
resize event.
2021-02-23 20:28:25 +11:00
Łukasz Kostka
0089c1252f [all] github: add build type to CI matrix 2021-02-23 20:28:02 +11:00
Geoffrey McRae
8771103abb [client] all: fix more maybe-uninitialized when -O3 is in use
Closes #475
2021-02-23 20:25:30 +11:00
Geoffrey McRae
9e2cfb9106 [client] core: fix reversed logic and document the rationale for it
During the refactor/rebase period with B3-next the conditional was
accidentally reversed. This would cause the cursor to be ungrabbed
simply when toggling capture mode instead of waiting for the cursor to
exit the window.
2021-02-23 20:19:35 +11:00
Geoffrey McRae
a8ab559de0 [client] core: increase autoCapture false exit detection sensitivity
Mouse move deltas greater then 10 are rare, let alone the 20 this code
now uses. Any movements that exceed 20 pixels will disable the exit
detection code path preventing rapid movements in FPV games from causing
the cursor to exit the window if autoCapture is enabled.
2021-02-23 20:06:44 +11:00
Geoffrey McRae
78f4249496 [client] core: remove unnecessary warp and update of cursor location
As we now are using our own backends instead of SDL, there is no longer
any need to warp back to the center of the window when in autoCapture
mode. This breaks the SDL ds backend behaviour, however as SDL is
planned to be removed this is not an issue.
2021-02-23 20:06:44 +11:00
Geoffrey McRae
9ae53aca70 [client] doc: update license output copyright date 2021-02-23 20:06:44 +11:00
Quantum
30b5287c38 [client] egl: add option to set initial scale algorithm
The option is egl:scale. The scale algorithm number is displayed on the
alert to make it easier to specify the number for the option.
2021-02-22 17:25:06 +11:00
Quantum
0512c88ea8 [client] egl: make scale algorithms toggleable
The $escape+S keybinding now cycles through the available scale algorithms.
This allows the user to switch between algorithms if the automatic detection
turns out to be problematic.

The algorithms are renumbered so that 0 can be LG_SCALE_AUTO.
2021-02-22 17:25:06 +11:00
Quantum
46758efc8f [client] cmake: build for nehalem when OPTIMIZE_FOR_NATIVE=NO
We need SSE intrinsics like _mm_stream_load_si128 which is only available
on CPUs of nehalem or newer.
2021-02-22 11:25:44 +11:00
Geoffrey McRae
6358f35cb7 [common] detect clang and disable warnings for unknown-warning-option
Clang doesn't know the warning option `format-truncation`, which GCC
falsely triggers on when calling vsnprintf. Further to this, GCC doesn't
understand the warning group `unknown-warning-option`, as such we must
disable these warnings in the cmake.
2021-02-22 01:54:46 +11:00
Geoffrey McRae
e79661a924 [client] app: fix format-truncation false positive
This is fixed by using the `valloc_sprintf` function provided in
common/stringutils.h as the functionallity is literally identical.
2021-02-22 01:39:56 +11:00
Geoffrey McRae
99fc650550 [common] fix gcc format-truncation false positive 2021-02-22 01:39:28 +11:00
Geoffrey McRae
28024de314 [client] egl: fix gcc false maybe-uninitialized positives 2021-02-22 01:38:26 +11:00
Quantum
63ec2dc01b [client] egl: destroy cached EGLImages when changing format
Reusing cached EGLImages while the frame format has changed will result
in visual artifacts. We should instead destroy the EGLImages and let
them be recreated.
2021-02-21 19:07:46 +11:00
Quantum
48be58378c [client] opengl: scale UI elements on high DPI displays
This is done by simply scaling up the rendered text bitmap.
2021-02-21 19:06:53 +11:00
Quantum
fd50426dda [client] egl: scale UI elements on high DPI displays
This is done by actually rendering the text at high DPI.
2021-02-21 19:06:53 +11:00
Quantum
89bdaec95a [client] opengl: support scaled UI for high DPI displays 2021-02-21 19:06:53 +11:00
Quantum
3ec73e2444 [client] egl: support scaled UI for high DPI displays 2021-02-21 19:06:53 +11:00
Quantum
447aedc9a3 [client] wayland: pass scale factor information to renderer 2021-02-21 19:06:53 +11:00
Quantum
b35e19fc27 [client] renderer: add interface for high DPI rendering 2021-02-21 19:06:53 +11:00
Quantum
6b26089353 [client] wayland: keep track of wl_output scale information
This will later be used to control the size of the underlying EGL buffers
in order to handle high DPI.
2021-02-21 19:06:53 +11:00
Quantum
e46cadb211 [client] egl: generate header file with #define for desktop shader
Instead of duplicating the #defines from the shader into the C code,
this commit adds a custom CMake rule that exports all the #defines
from a shader into a C header.
2021-02-21 14:20:41 +11:00
Quantum
ba6f26393f [client] egl: add framework for multiple scaling algorithms
This commit fixes the issues with the meaning of useNearest being flipped
by removing the variable and use enumerations.

We define an enumeration EGL_DesktopScaleType to express the type of scaling
needed to be performed: no scaling, upscaling, or downscaling. This is
updated when either the frame size or the viewport size changes.
Previously, we only updated the useNearest when the frame size changes.

The desktop shader can now support an enumeration of scaling algorithms,
although only nearest and linear are currently implemented.

Like before, nearest is used when not scaling or upscaling, and linear is
used when downscaling.
2021-02-21 14:20:41 +11:00
Geoffrey McRae
1fc31ba832 [module] bump version and add copyright for @quantum5 2021-02-21 14:17:22 +11:00
Quantum
7e58278858 [module] fault in pages one by one for ivshmem devices
It appears that the PCI BAR memory is slow to access with remap_pfn_range
and that it should instead be faulted in one page at a time.

The commit 5774e21965 implemented the former
behaviour and caused a performance regression in the VM->VM case.

This commit retores the old behaviour, but extends it to support mmaping
the kvmfr device directly, without going through a dmabuf.
2021-02-21 14:13:24 +11:00
296 changed files with 27926 additions and 9394 deletions

View File

@@ -4,12 +4,20 @@ jobs:
client:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
cc: [gcc, clang]
compiler:
- {cc: gcc, cxx: g++}
- {cc: clang, cxx: clang++}
wayland_shell: [xdg-shell, libdecor]
build_type: [Release, Debug]
steps:
- uses: actions/checkout@v1
with:
submodules: recursive
- name: Install libdecor PPA
run: sudo add-apt-repository ppa:christianrauch/libdecoration
if: ${{ matrix.wayland_shell == 'libdecor' }}
- name: Update apt
run: |
sudo apt-get update
@@ -17,19 +25,35 @@ 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 wayland-protocols libxkbcommon-dev \
$([ '${{ matrix.wayland_shell }}' = libdecor ] && echo 'libdecor-dev libdbus-1-dev') \
$([ '${{ matrix.compiler.cc }}' = clang ] && echo 'clang-tools')
sudo pip3 install pyenchant
- name: Configure client
env:
CC: /usr/bin/${{ matrix.compiler.cc }}
CXX: /usr/bin/${{ matrix.compiler.cxx }}
run: |
mkdir client/build
cd client/build
CC=/usr/bin/${{ matrix.cc }} cmake ..
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
@@ -53,7 +77,7 @@ jobs:
sudo apt-get update
- name: Install Linux host dependencies
run: |
sudo apt-get install binutils-dev libgl1-mesa-dev
sudo apt-get install binutils-dev libgl1-mesa-dev libxcb-xfixes0-dev
- name: Configure Linux host
run: |
mkdir host/build
@@ -108,7 +132,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
@@ -134,3 +158,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
sudo pip3 install sphinxcontrib-spelling
- name: Build docs
run: |
cd doc
make dirhtml SPHINXOPTS='-b spelling -W' -j$(nproc)

18
.github/workflows/pr-check.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: pr-check
on: pull_request
jobs:
authors:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Check AUTHORS file
run: |
user="$(curl -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' -s https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }} | jq -r .user.login)"
echo "Checking if GitHub user $user is in AUTHORS file..."
if grep -q -E '> \('"$user"'\)' AUTHORS; then
echo "$user found in AUTHORS file, all good!"
else
echo "$user not found in AUTHORS file."
echo "Please add yourself to the AUTHORS file and try again."
exit 1
fi

2
.gitignore vendored
View File

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

3
.gitmodules vendored
View File

@@ -4,3 +4,6 @@
[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

56
AUTHORS Normal file
View File

@@ -0,0 +1,56 @@
The following authors have licensed all their contributions to
the Looking Glass project under the terms detailed in LICENSE:
Geoffrey McRae <geoff@hostfission.com> (gnif)
Guanzhong Chen <quantum2048@gmail.com> (quantum5)
Tudor Brindus <me@tbrindus.ca> (Xyene)
Jonathan Rubenstein <jrubcop@gmail.com> (JJRcop)
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>
Jack Karamanian <karamanian.jack@gmail.com>
Mikko Rasa <tdb@tdb.fi> (DataBeaver)
Omar Pakker <Omar007@users.noreply.github.com> (Omar007)
Yvan da Silva <yvan.m.silva@gmail.com> (YvanDaSilva)
r4m0n <thiagoramon@gmail.com> (r4m0n)
Łukasz Kostka <lukasz.kostka@netng.pl>
A.J. Ruckman <aj@ruckman.dev> (ajruckman)
Aaron <mcd1992@users.noreply.github.com> (mcd1992)
Alam Arias <Alam.GBC@gmail.com> (alama)
Alexander Olofsson <ace@haxalot.com> (ananace)
Andrew Sheldon <asheldon55@gmail.com> (aqxa1)
Andy Chun <andy@lolc.at> (noneucat)
Arti Zirk <arti.zirk@gmail.com>
Ash <5615358+ash-hat@users.noreply.github.com> (ash-hat)
Dominik Csapak <d.csapak@proxmox.com> (flumm)
Frediano Ziglio <fziglio@redhat.com>
Luke Brown <axios350@gmail.com> (Dynamic-Gravity)
Marius Barbu <msb@avengis.com> (mbarbu)
Max Sistemich <maximilian.sistemich@rwth-aachen.de> (mafrasi2)
Michael Golisch <mgolisch@googlemail.com> (mgolisch)
Michał Zając <michal.zajac@gmail.com>
Netboy3 <1472804+netboy3@users.noreply.github.com> (netboy3)
Patrick Steinhardt <ps@pks.im>
Paul Götzinger <paul70079@gmail.com> (pagdot)
Rikard Falkeborn <rikard.falkeborn@gmail.com> (rikardfalkeborn)
Rokas Kupstys <rokups@zoho.com> (rokups)
Samuel Bowman <SamuelBowman@users.noreply.github.com> (SamuelBowman)
Txanton <txb2@live.com> (tbejos)
Tyler Watson <tyler@tw.id.au>
Xiretza <xiretza@xiretza.xyz> (Xiretza)
aspen <aspenuwu@protonmail.com> (aspenluxxxy)
camr0 <CAMR0@protonmail.com> (camr0)
chrsm <github.personal@c.chrsm.org> (chrsm)
commander kotori <cmdrkotori@gmail.com> (cmdrkotori)
eater <=@eater.me> (the-eater)
fishery <i@ovo.so> (fisherwise)
four0four <galen@shellspace.net> (four0four)
jmossman <jmossman@users.noreply.github.com> (jmossman)
jonpas <jonpas33@gmail.com> (jonpas)
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)

View File

@@ -4,64 +4,19 @@ An extremely low latency KVMFR (KVM FrameRelay) implementation for guests with
VGA PCI Passthrough.
* Project Website: https://looking-glass.io
* Getting Started: https://looking-glass.io/wiki/Installation
## Donations
I (Geoffrey McRae) am the primary developer behind this project and I have
invested thousands of hours of development time into it.
If you like this project and find it useful and would like to help out you can
support me directly 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
* Documentation: https://looking-glass.io/docs
## Documentation
** IMPORTANT **
❕❕❕ **IMPORTANT** ❕❕❕
This project contains submodules that must be checked out if building from the
git repository! If you are not a developer and just want to compile Looking
Glass please download the source archive from the website instead:
Glass, please download the source archive from the website instead:
https://looking-glass.io/downloads
Please also be sure to see the following files for more information
Note: The `README.md` files are slowly being deprecated from this project in
favor of the wiki at https://looking-glass.io/wiki, and as such the
information in these files may be dated.
Source code for the documentation can be found in the `/doc` directory.
* [client/README.md](client/README.md)
* [host/README.md](host/README.md)
* [module/README.md](module/README.md)
## Latest Version
If you would like to use the latest bleeding edge version of Looking Glass please
be aware there will be no support at this time.
Latest bleeding edge builds of the Windows host application can be obtained from:
https://looking-glass.io/downloads
# Help and support
## Web
https://forum.level1techs.com/c/software/lookingglass/142
## Discord
* Looking Glass: https://discord.gg/52SMupxkvt
* VFIO: https://discord.gg/4ahCn4c
## IRC
Join us in the #LookingGlass channel on the FreeNode network
## Trello
* https://trello.com/b/tI1Xbwsg/looking-glass
You may view this locally as HTML by running `make html` with `python3-sphinx`
installed.

View File

@@ -1,19 +1,31 @@
cmake_minimum_required(VERSION 3.0)
project(looking-glass-client C)
project(looking-glass-client C CXX)
set(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()
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.")
@@ -36,6 +48,13 @@ add_feature_info(ENABLE_X11 ENABLE_X11 "X11 support.")
option(ENABLE_WAYLAND "Build with Wayland support" ON)
add_feature_info(ENABLE_WAYLAND ENABLE_WAYLAND "Wayland support.")
option(ENABLE_LIBDECOR "Build with libdecor support" OFF)
add_feature_info(ENABLE_LIBDECOR ENABLE_LIBDECOR "libdecor support.")
if (NOT ENABLE_X11 AND NOT ENABLE_WAYLAND)
message(FATAL_ERROR "Either ENABLE_X11 or ENABLE_WAYLAND must be on")
endif()
add_compile_options(
"-Wall"
"-Wextra"
@@ -71,16 +90,8 @@ if(ENABLE_UBSAN)
set(EXE_FLAGS "${EXE_FLAGS} -fsanitize=undefined")
endif()
find_package(PkgConfig)
pkg_check_modules(PKGCONFIG REQUIRED
sdl2
)
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
@@ -92,13 +103,9 @@ add_custom_command(
include_directories(
${PROJECT_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/include
${PKGCONFIG_INCLUDE_DIRS} ${PKGCONFIG_OPT_INCLUDE_DIRS}
${GMP_INCLUDE_DIR}
)
link_libraries(
${PKGCONFIG_LIBRARIES} ${PKGCONFIG_OPT_LIBRARIES}
${GMP_LIBRARIES}
${CMAKE_DL_LIBS}
rt
m
@@ -111,34 +118,50 @@ set(SOURCES
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/gl_dynprocs.c
src/egl_dynprocs.c
src/eglutil.c
src/overlay_utils.c
src/overlay/alert.c
src/overlay/fps.c
src/overlay/graphs.c
src/overlay/help.c
src/overlay/config.c
)
# Force cimgui to build as a static library.
set(IMGUI_STATIC "yes" CACHE STRING "Build as a static library")
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_options(looking-glass-client PUBLIC ${PKGCONFIG_CFLAGS_OTHER} ${PKGCONFIG_OPT_CFLAGS_OTHER})
target_compile_definitions(looking-glass-client PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1)
target_link_libraries(looking-glass-client
${EXE_FLAGS}
PkgConfig::FONTCONFIG
lg_common
displayservers
lgmp
purespice
renderers
fonts
cimgui
)
install(PROGRAMS ${CMAKE_BINARY_DIR}/looking-glass-client DESTINATION bin/ COMPONENT binary)
install(TARGETS looking-glass-client
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT binary)
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)

View File

@@ -1,205 +0,0 @@
# Looking Glass Client
This is the Looking Glass client application that is designed to work in tandem with the Looking Glass Host application
---
## Building the Application
### Build Dependencies
* binutils-dev
* cmake
* fonts-freefont-ttf
* libsdl2-dev
* libsdl2-ttf-dev
* libspice-protocol-dev
* libfontconfig1-dev
* libx11-dev
* nettle-dev
* libxss-dev
* libxi-dev
#### Debian (and maybe Ubuntu)
apt-get install binutils-dev cmake fonts-freefont-ttf libsdl2-dev libsdl2-ttf-dev libspice-protocol-dev libfontconfig1-dev libx11-dev nettle-dev libxss-dev libxi-dev
### Building
mkdir build
cd build
cmake ../
make
Should this all go well you should be left with the file `looking-glass-client`
### Removing Wayland or X11 support
Wayland and/or X11 support can be disabled with the compile options
`ENABLE_WAYLAND` and `ENABLE_X11`, if both are specified only `SDL2` will remain
and the client will fallback to using it.
cmake ../ -DENABLE_WAYLAND=OFF
At this time, X11 is the perferred and best supported interface. Wayland is not
far behind, however it lacks some of the seamless interaction features that X11
has due to the lack of cursor warp (programmatic movement of the local cusror) on
Wayland.
---
## Usage Tips
### Key Bindings
By default Looking Glass uses the `Scroll Lock` key as the escape key for commands as well as the input capture mode toggle, this can be changed using the `-m` switch if you desire a different key.
Below are a list of current key bindings:
| Command | Description |
|-|-|
| <kbd>ScrLk</kbd> | Toggle cursor screen capture |
| <kbd>ScrLk</kbd>+<kbd>F</kbd> | Full Screen toggle |
| <kbd>ScrLk</kbd>+<kbd>V</kbd> | Video stream toggle |
| <kbd>ScrLk</kbd>+<kbd>I</kbd> | Spice keyboard & mouse enable toggle |
| <kbd>ScrLk</kbd>+<kbd>N</kbd> | Toggle night vision mode (EGL renderer only!) |
| <kbd>ScrLk</kbd>+<kbd>R</kbd> | Rotate the output clockwise by 90 degree increments |
| <kbd>ScrLk</kbd>+<kbd>Q</kbd> | Quit |
| <kbd>ScrLk</kbd>+<kbd>Insert</kbd> | Increase mouse sensitivity (in capture mode only) |
| <kbd>ScrLk</kbd>+<kbd>Del</kbd> | Decrease mouse sensitivity (in capture mode only) |
| <kbd>ScrLk</kbd>+<kbd>F1</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F1</kbd> to the guest |
| <kbd>ScrLk</kbd>+<kbd>F2</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F2</kbd> to the guest |
| <kbd>ScrLk</kbd>+<kbd>F3</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F3</kbd> to the guest |
| <kbd>ScrLk</kbd>+<kbd>F4</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F4</kbd> to the guest |
| <kbd>ScrLk</kbd>+<kbd>F5</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F5</kbd> to the guest |
| <kbd>ScrLk</kbd>+<kbd>F6</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F6</kbd> to the guest |
| <kbd>ScrLk</kbd>+<kbd>F7</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F7</kbd> to the guest |
| <kbd>ScrLk</kbd>+<kbd>F8</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F8</kbd> to the guest |
| <kbd>ScrLk</kbd>+<kbd>F9</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F9</kbd> to the guest |
| <kbd>ScrLk</kbd>+<kbd>F10</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F10</kbd> to the guest |
| <kbd>ScrLk</kbd>+<kbd>F11</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F11</kbd> to the guest |
| <kbd>ScrLk</kbd>+<kbd>F12</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F12</kbd> to the guest |
| <kbd>ScrLk</kbd>+<kbd>LWin</kbd> | Send <kbd>LWin</kbd> to the guest |
| <kbd>ScrLk</kbd>+<kbd>RWin</kbd> | Send <kbd>RWin</kbd> to the guest |
### Setting options via command line arguments
The syntax is simple: `module:name=value`, for example:
./looking-glass-client win:fullScreen=yes egl:nvGain=1
### Setting options via configuration files
By default the application will look for and load the config files in the following locations
* /etc/looking-glass-client.ini
* ~/.looking-glass-client.ini
The format of this file is the commonly known INI format, for example:
[win]
fullScreen=yes
[egl]
nvGain=1
Command line arguments will override any options loaded from the config files.
### Supported options
```
|--------------------------------------------------------------------------------------------------------------------------------------------------|
| Long | Short | Value | Description |
|--------------------------------------------------------------------------------------------------------------------------------------------------|
| app:configFile | -C | NULL | A file to read additional configuration from |
| app:renderer | -g | auto | Specify the renderer to use |
| app:license | -l | no | Show the license for this application and then terminate |
| app:cursorPollInterval | | 1000 | How often to check for a cursor update in microseconds |
| app:framePollInterval | | 1000 | How often to check for a frame update in microseconds |
| app:allowDMA | | yes | Allow direct DMA transfers if possible (VM-VM only for now) |
| app:shmFile | -f | /dev/shm/looking-glass | The path to the shared memory file, or the name of the kvmfr device to use, ie: kvmfr0 |
|--------------------------------------------------------------------------------------------------------------------------------------------------|
|---------------------------------------------------------------------------------------------------------------------------------|
| Long | Short | Value | Description |
|---------------------------------------------------------------------------------------------------------------------------------|
| win:title | | Looking Glass (client) | The window title |
| win:position | | center | Initial window position at startup |
| win:size | | 1024x768 | Initial window size at startup |
| win:autoResize | -a | no | Auto resize the window to the guest |
| win:allowResize | -n | yes | Allow the window to be manually resized |
| win:keepAspect | -r | yes | Maintain the correct aspect ratio |
| win:forceAspect | | yes | Force the window to maintain the aspect ratio |
| win:dontUpscale | | no | Never try to upscale the window |
| win:borderless | -d | no | Borderless mode |
| win:fullScreen | -F | no | Launch in fullscreen borderless mode |
| win:maximize | -T | no | Launch window maximized |
| win:minimizeOnFocusLoss | | yes | Minimize window on focus loss |
| win:fpsMin | -K | -1 | Frame rate minimum (0 = disable - not recommended, -1 = auto detect) |
| win:showFPS | -k | no | Enable the FPS & UPS display |
| win:ignoreQuit | -Q | no | Ignore requests to quit (ie: Alt+F4) |
| win:noScreensaver | -S | no | Prevent the screensaver from starting |
| win:alerts | -q | yes | Show on screen alert messages |
| win:quickSplash | | no | Skip fading out the splash screen when a connection is established |
| win:rotate | | 0 | Rotate the displayed image (0, 90, 180, 270) |
|---------------------------------------------------------------------------------------------------------------------------------|
|-----------------------------------------------------------------------------------------------------------------------------------------------|
| Long | Short | Value | Description |
|-----------------------------------------------------------------------------------------------------------------------------------------------|
| input:grabKeyboard | -G | no | Grab the keyboard in capture mode |
| input:grabKeyboardOnFocus | | no | Grab the keyboard when focused |
| input:releaseKeysOnFocusLoss | | yes | On focus loss, send key up events to guest for all held keys |
| input:escapeKey | -m | 70 = KEY_SCROLLLOCK | Specify the escape key, see <linux/input-event-codes.h> for valid values |
| input:ignoreWindowsKeys | | no | Do not pass events for the windows keys to the guest |
| input:hideCursor | -M | yes | Hide the local mouse cursor |
| input:mouseSens | | 0 | Initial mouse sensitivity when in capture mode (-9 to 9) |
| input:mouseSmoothing | | yes | Apply simple mouse smoothing when rawMouse is not in use (helps reduce aliasing) |
| input:rawMouse | | no | Use RAW mouse input when in capture mode (good for gaming) |
| input:mouseRedraw | | yes | Mouse movements trigger redraws (ignores FPS minimum) |
| input:autoCapture | | no | Try to keep the mouse captured when needed |
| input:captureOnly | | no | Only enable input via SPICE if in capture mode |
|-----------------------------------------------------------------------------------------------------------------------------------------------|
|------------------------------------------------------------------------------------------------------------------|
| Long | Short | Value | Description |
|------------------------------------------------------------------------------------------------------------------|
| spice:enable | -s | yes | Enable the built in SPICE client for input and/or clipboard support |
| spice:host | -c | 127.0.0.1 | The SPICE server host or UNIX socket |
| spice:port | -p | 5900 | The SPICE server port (0 = unix socket) |
| spice:input | | yes | Use SPICE to send keyboard and mouse input events to the guest |
| spice:clipboard | | yes | Use SPICE to syncronize the clipboard contents with the guest |
| spice:clipboardToVM | | yes | Allow the clipboard to be syncronized TO the VM |
| spice:clipboardToLocal | | yes | Allow the clipboard to be syncronized FROM the VM |
| spice:scaleCursor | -j | yes | Scale cursor input position to screen size when up/down scaled |
| spice:captureOnStart | | no | Capture mouse and keyboard on start |
| spice:alwaysShowCursor | | no | Always show host cursor |
|------------------------------------------------------------------------------------------------------------------|
|--------------------------------------------------------------------------------------------------------------|
| Long | Short | Value | Description |
|--------------------------------------------------------------------------------------------------------------|
| egl:vsync | | no | Enable vsync |
| egl:doubleBuffer | | no | Enable double buffering |
| egl:multisample | | yes | Enable Multisampling |
| egl:nvGainMax | | 1 | The maximum night vision gain |
| egl:nvGain | | 0 | The initial night vision gain at startup |
| egl:cbMode | | 0 | Color Blind Mode (0 = Off, 1 = Protanope, 2 = Deuteranope, 3 = Tritanope) |
|--------------------------------------------------------------------------------------------------------------|
|------------------------------------------------------------------------------------|
| Long | Short | Value | Description |
|------------------------------------------------------------------------------------|
| opengl:mipmap | | yes | Enable mipmapping |
| opengl:vsync | | no | Enable vsync |
| opengl:preventBuffer | | yes | Prevent the driver from buffering frames |
| opengl:amdPinnedMem | | yes | Use GL_AMD_pinned_memory if it is available |
|------------------------------------------------------------------------------------|
|-------------------------------------------------------------|
| Long | Short | Value | Description |
|-------------------------------------------------------------|
| wayland:warpSupport | | yes | Enable cursor warping |
|-------------------------------------------------------------|
```

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

@@ -5,15 +5,15 @@ function(make_object out_var)
file(RELATIVE_PATH out_f ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/${in_f}")
set(out_h "${CMAKE_CURRENT_BINARY_DIR}/${out_f}.h")
set(out_f "${CMAKE_CURRENT_BINARY_DIR}/${out_f}.o")
string(REGEX REPLACE "[/.]" "_" sym_in ${in_f})
string(REGEX REPLACE "[/.-]" "_" sym_in "${in_f}")
add_custom_command(OUTPUT ${out_f}
COMMAND ${CMAKE_LINKER} -r -b binary -o ${out_f} ${in_f}
COMMAND ${CMAKE_LINKER} -r -b binary -o ${out_f} "${in_f}"
COMMAND ${CMAKE_OBJCOPY} --rename-section .data=.rodata,CONTENTS,ALLOC,LOAD,READONLY,DATA ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_start=b_${sym_in} ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_end=b_${sym_in}_end ${out_f} ${out_f}
COMMAND ${CMAKE_OBJCOPY} --strip-symbol _binary_${sym_in}_size ${out_f} ${out_f}
DEPENDS ${in_f}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${in_f}"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Creating object from ${in_f}"
VERBATIM
@@ -32,3 +32,11 @@ function(make_object out_var)
set(${out_var}_OBJS "${result}" PARENT_SCOPE)
set(${out_var}_INCS "${result_h}" PARENT_SCOPE)
endfunction()
function(make_defines in_file out_file)
add_custom_command(OUTPUT ${out_file}
COMMAND grep "^#define" "${in_file}" > "${out_file}"
DEPENDS ${in_file}
COMMENT "Creating #defines from ${in_file}"
)
endfunction()

View File

@@ -27,9 +27,6 @@ if (ENABLE_X11)
add_displayserver(X11)
endif()
# SDL must be last as it's the fallback implemntation
add_displayserver(SDL)
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,196 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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,551 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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"
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,
params.minimizeOnFocusLoss ? "1" : "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 (strstr(early_exts, "EGL_KHR_platform_base") != NULL &&
g_egl_dynProcs.eglGetPlatformDisplay)
{
DEBUG_INFO("Using eglGetPlatformDisplay");
return g_egl_dynProcs.eglGetPlatformDisplay(platform, native, NULL);
}
if (strstr(early_exts, "EGL_EXT_platform_base") != NULL &&
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)
{
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,
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;
}
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,
.grabKeyboard = sdlGrabKeyboard,
.ungrabKeyboard = sdlUngrabKeyboard,
.warpPointer = sdlWarpPointer,
.realignPointer = sdlRealignPointer,
.isValidPointerPos = sdlIsValidPointerPos,
.inhibitIdle = sdlInhibitIdle,
.uninhibitIdle = sdlUninhibitIdle,
.wait = sdlWait,
.setWindowSize = sdlSetWindowSize,
.setFullscreen = sdlSetFullscreen,
.getFullscreen = sdlGetFullscreen,
/* SDL does not have clipboard support */
.cbInit = NULL,
};

View File

@@ -2,12 +2,25 @@ cmake_minimum_required(VERSION 3.0)
project(displayserver_Wayland LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(DISPLAYSERVER_Wayland_PKGCONFIG REQUIRED
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 IMPORTED_TARGET
libdecor-0
)
list(APPEND DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES PkgConfig::DISPLAYSERVER_Wayland_LIBDECOR)
list(APPEND displayserver_Wayland_SHELL_SRC shell_libdecor.c)
add_compile_definitions(ENABLE_LIBDECOR)
else()
list(APPEND displayserver_Wayland_SHELL_SRC shell_xdg.c)
endif()
add_library(displayserver_Wayland STATIC
clipboard.c
@@ -15,15 +28,18 @@ add_library(displayserver_Wayland STATIC
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}
PkgConfig::DISPLAYSERVER_Wayland
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES}
lg_common
)
@@ -31,8 +47,6 @@ target_link_libraries(displayserver_Wayland
target_include_directories(displayserver_Wayland
PRIVATE
src
${DISPLAYSERVER_Wayland_PKGCONFIG_INCLUDE_DIRS}
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_INCLUDE_DIRS}
)
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
@@ -58,6 +72,12 @@ include_directories("${CMAKE_BINARY_DIR}/wayland")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/stable/presentation-time/presentation-time.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-presentation-time-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/stable/viewporter/viewporter.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-viewporter-client-protocol")
wayland_generate(
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-decoration-unstable-v1-client-protocol")
@@ -73,4 +93,6 @@ wayland_generate(
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")

View File

@@ -1,26 +1,25 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"
#include <assert.h>
#include <stdbool.h>
#include <string.h>
@@ -33,6 +32,11 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "app.h"
#include "common/debug.h"
struct DataOffer {
bool isSelfCopy;
char * mimetypes[LG_CLIPBOARD_DATA_NONE];
};
static const char * textMimetypes[] =
{
"text/plain",
@@ -116,6 +120,9 @@ static bool isTextMimetype(const char * mimetype)
if (containsMimetype(textMimetypes, mimetype))
return true;
if (!strcmp(mimetype, "text/ico"))
return false;
char * text = "text/";
if (!strncmp(mimetype, text, strlen(text)))
return true;
@@ -151,27 +158,74 @@ static enum LG_ClipboardData mimetypeToCbType(const char * mimetype)
return LG_CLIPBOARD_DATA_NONE;
}
static bool isImageCbtype(enum LG_ClipboardData type)
{
switch (type)
{
case LG_CLIPBOARD_DATA_TEXT:
return false;
case LG_CLIPBOARD_DATA_PNG:
case LG_CLIPBOARD_DATA_BMP:
case LG_CLIPBOARD_DATA_TIFF:
case LG_CLIPBOARD_DATA_JPEG:
return true;
default:
DEBUG_ERROR("invalid clipboard type");
abort();
}
}
static bool hasAnyMimetype(char ** mimetypes)
{
for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
if (mimetypes[i])
return true;
return false;
}
static bool hasImageMimetype(char ** mimetypes)
{
for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
if (isImageCbtype(i) && mimetypes[i])
return true;
return false;
}
// Destination client handlers.
static void dataOfferHandleOffer(void * data, struct wl_data_offer * offer,
static void dataOfferHandleOffer(void * opaque, struct wl_data_offer * offer,
const char * mimetype)
{
enum LG_ClipboardData type = mimetypeToCbType(mimetype);
// We almost never prefer text/html, as that's used to represent rich text.
// Since we can't copy or paste rich text, we should instead prefer actual
// images or plain text.
if (type != LG_CLIPBOARD_DATA_NONE &&
(wlCb.pendingType == LG_CLIPBOARD_DATA_NONE ||
strstr(wlCb.pendingMimetype, "html")))
{
wlCb.pendingType = type;
if (wlCb.pendingMimetype)
free(wlCb.pendingMimetype);
wlCb.pendingMimetype = strdup(mimetype);
}
struct DataOffer * data = opaque;
if (!strcmp(mimetype, wlCb.lgMimetype))
wlCb.isSelfCopy = true;
{
data->isSelfCopy = true;
return;
}
enum LG_ClipboardData type = mimetypeToCbType(mimetype);
if (type == LG_CLIPBOARD_DATA_NONE)
return;
// text/html represents rich text format, which is almost never desirable when
// and should not be used when a plain text or image format is available.
if ((isImageCbtype(type) || containsMimetype(textMimetypes, mimetype)) &&
data->mimetypes[LG_CLIPBOARD_DATA_TEXT] &&
strstr(data->mimetypes[LG_CLIPBOARD_DATA_TEXT], "html"))
{
free(data->mimetypes[LG_CLIPBOARD_DATA_TEXT]);
data->mimetypes[LG_CLIPBOARD_DATA_TEXT] = NULL;
}
if (strstr(mimetype, "html") && hasImageMimetype(data->mimetypes))
return;
if (data->mimetypes[type])
return;
data->mimetypes[type] = strdup(mimetype);
}
static void dataOfferHandleSourceActions(void * data,
@@ -195,132 +249,93 @@ static const struct wl_data_offer_listener dataOfferListener = {
static void dataDeviceHandleDataOffer(void * data,
struct wl_data_device * dataDevice, struct wl_data_offer * offer)
{
wlCb.pendingType = LG_CLIPBOARD_DATA_NONE;
wlCb.isSelfCopy = false;
wl_data_offer_add_listener(offer, &dataOfferListener, NULL);
}
static void clipboardReadCancel(struct ClipboardRead * data, bool freeBuf)
{
waylandEpollUnregister(data->fd);
close(data->fd);
wl_data_offer_destroy(data->offer);
if (freeBuf)
free(data->buf);
free(data);
wlCb.currentRead = NULL;
}
static void clipboardReadCallback(uint32_t events, void * opaque)
{
struct ClipboardRead * data = opaque;
if (events & EPOLLERR)
struct DataOffer * extra = calloc(1, sizeof(struct DataOffer));
if (!extra)
{
clipboardReadCancel(data, true);
return;
}
ssize_t result = read(data->fd, data->buf + data->numRead, data->size - data->numRead);
if (result < 0)
{
DEBUG_ERROR("Failed to read from clipboard: %s", strerror(errno));
clipboardReadCancel(data, true);
return;
}
if (result == 0)
{
data->buf[data->numRead] = 0;
wlCb.stashedType = data->type;
wlCb.stashedSize = data->numRead;
wlCb.stashedContents = data->buf;
clipboardReadCancel(data, false);
app_clipboardNotify(wlCb.stashedType, 0);
return;
}
data->numRead += result;
if (data->numRead >= data->size)
{
data->size *= 2;
void * nbuf = realloc(data->buf, data->size);
if (!nbuf) {
DEBUG_ERROR("Failed to realloc clipboard buffer: %s", strerror(errno));
clipboardReadCancel(data, true);
return;
}
data->buf = nbuf;
DEBUG_ERROR("Out of memory while handling clipboard");
abort();
}
wl_data_offer_set_user_data(offer, extra);
wl_data_offer_add_listener(offer, &dataOfferListener, extra);
}
static void dataDeviceHandleSelection(void * opaque,
struct wl_data_device * dataDevice, struct wl_data_offer * offer)
{
if (wlCb.pendingType == LG_CLIPBOARD_DATA_NONE || wlCb.isSelfCopy || !offer)
return;
if (wlCb.currentRead)
clipboardReadCancel(wlCb.currentRead, true);
int fds[2];
if (pipe(fds) < 0)
if (!offer)
{
DEBUG_ERROR("Failed to get a clipboard pipe: %s", strerror(errno));
abort();
}
wl_data_offer_receive(offer, wlCb.pendingMimetype, fds[1]);
close(fds[1]);
free(wlCb.pendingMimetype);
wlCb.pendingMimetype = NULL;
wl_display_roundtrip(wlWm.display);
if (wlCb.stashedContents)
{
free(wlCb.stashedContents);
wlCb.stashedContents = NULL;
}
struct ClipboardRead * data = malloc(sizeof(struct ClipboardRead));
if (!data)
{
DEBUG_ERROR("Failed to allocate memory to read clipboard");
close(fds[0]);
waylandCBInvalidate();
return;
}
data->fd = fds[0];
data->size = 4096;
data->numRead = 0;
data->buf = malloc(data->size);
data->offer = offer;
data->type = wlCb.pendingType;
if (!data->buf)
struct DataOffer * extra = wl_data_offer_get_user_data(offer);
if (!hasAnyMimetype(extra->mimetypes) || extra->isSelfCopy)
{
DEBUG_ERROR("Failed to allocate memory to receive clipboard data");
close(data->fd);
free(data);
waylandCBInvalidate();
wl_data_offer_destroy(offer);
return;
}
if (!waylandEpollRegister(data->fd, clipboardReadCallback, data, EPOLLIN))
{
DEBUG_ERROR("Failed to register clipboard read into epoll: %s", strerror(errno));
close(data->fd);
free(data->buf);
free(data);
}
wlCb.offer = offer;
wlCb.currentRead = data;
for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
free(wlCb.mimetypes[i]);
memcpy(wlCb.mimetypes, extra->mimetypes, sizeof(wlCb.mimetypes));
wl_data_offer_set_user_data(offer, NULL);
free(extra);
int idx = 0;
enum LG_ClipboardData types[LG_CLIPBOARD_DATA_NONE];
for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
if (wlCb.mimetypes[i])
types[idx++] = i;
app_clipboardNotifyTypes(types, idx);
}
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)
{
DEBUG_ASSERT(wlCb.dndOffer == NULL);
wlCb.dndOffer = offer;
struct DataOffer * extra = wl_data_offer_get_user_data(offer);
for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
free(extra->mimetypes[i]);
free(extra);
wl_data_offer_set_user_data(offer, NULL);
wl_data_offer_set_actions(offer, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE,
WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE);
}
static void dataDeviceHandleMotion(void * data, struct wl_data_device * device,
uint32_t time, wl_fixed_t sxW, wl_fixed_t syW)
{
// Do nothing.
}
static void dataDeviceHandleLeave(void * data, struct wl_data_device * device)
{
wl_data_offer_destroy(wlCb.dndOffer);
wlCb.dndOffer = NULL;
}
static void dataDeviceHandleDrop(void * data, struct wl_data_device * device)
{
wl_data_offer_destroy(wlCb.dndOffer);
wlCb.dndOffer = NULL;
}
static const struct wl_data_device_listener dataDeviceListener = {
.data_offer = dataDeviceHandleDataOffer,
.selection = dataDeviceHandleSelection,
.enter = dataDeviceHandleEnter,
.motion = dataDeviceHandleMotion,
.leave = dataDeviceHandleLeave,
.drop = dataDeviceHandleDrop,
};
bool waylandCBInit(void)
@@ -329,7 +344,7 @@ bool waylandCBInit(void)
if (!wlWm.dataDeviceManager)
{
DEBUG_ERROR("Missing wl_data_device_manager interface");
DEBUG_ERROR("Missing wl_data_device_manager interface (version 3+)");
return false;
}
@@ -341,7 +356,6 @@ bool waylandCBInit(void)
return false;
}
wlCb.stashedType = LG_CLIPBOARD_DATA_NONE;
wl_data_device_add_listener(wlCb.dataDevice, &dataDeviceListener, NULL);
snprintf(wlCb.lgMimetype, sizeof(wlCb.lgMimetype),
@@ -350,11 +364,120 @@ bool waylandCBInit(void)
return true;
}
static void clipboardReadCancel(struct ClipboardRead * data)
{
waylandPollUnregister(data->fd);
close(data->fd);
free(data->buf);
free(data);
wlCb.currentRead = NULL;
}
static void clipboardReadCallback(uint32_t events, void * opaque)
{
struct ClipboardRead * data = opaque;
if (events & EPOLLERR)
{
clipboardReadCancel(data);
return;
}
ssize_t result = read(data->fd, data->buf + data->numRead, data->size - data->numRead);
if (result < 0)
{
DEBUG_ERROR("Failed to read from clipboard: %s", strerror(errno));
clipboardReadCancel(data);
return;
}
if (result == 0)
{
app_clipboardNotifySize(data->type, data->numRead);
app_clipboardData(data->type, data->buf, data->numRead);
clipboardReadCancel(data);
return;
}
data->numRead += result;
if (data->numRead >= data->size)
{
data->size *= 2;
void * nbuf = realloc(data->buf, data->size);
if (!nbuf) {
DEBUG_ERROR("Failed to realloc clipboard buffer: %s", strerror(errno));
clipboardReadCancel(data);
return;
}
data->buf = nbuf;
}
}
void waylandCBInvalidate(void)
{
if (wlCb.currentRead)
clipboardReadCancel(wlCb.currentRead);
app_clipboardRelease();
if (wlCb.offer)
wl_data_offer_destroy(wlCb.offer);
wlCb.offer = NULL;
}
void waylandCBRequest(LG_ClipboardData type)
{
// We only notified once, so it must be this.
assert(type == wlCb.stashedType);
app_clipboardData(wlCb.stashedType, wlCb.stashedContents, wlCb.stashedSize);
if (!wlCb.offer || !wlCb.mimetypes[type])
{
app_clipboardRelease();
return;
}
if (wlCb.currentRead)
clipboardReadCancel(wlCb.currentRead);
int fds[2];
if (pipe(fds) < 0)
{
DEBUG_ERROR("Failed to get a clipboard pipe: %s", strerror(errno));
abort();
}
wl_data_offer_receive(wlCb.offer, wlCb.mimetypes[type], fds[1]);
close(fds[1]);
struct ClipboardRead * data = malloc(sizeof(*data));
if (!data)
{
DEBUG_ERROR("Failed to allocate memory to read clipboard");
close(fds[0]);
return;
}
data->fd = fds[0];
data->size = 4096;
data->numRead = 0;
data->buf = malloc(data->size);
data->offer = wlCb.offer;
data->type = type;
if (!data->buf)
{
DEBUG_ERROR("Failed to allocate memory to receive clipboard data");
close(data->fd);
free(data);
return;
}
if (!waylandPollRegister(data->fd, clipboardReadCallback, data, EPOLLIN))
{
DEBUG_ERROR("Failed to register clipboard read into epoll: %s", strerror(errno));
close(data->fd);
free(data->buf);
free(data);
}
wlCb.currentRead = data;
}
struct ClipboardWrite
@@ -383,7 +506,7 @@ static void clipboardWriteCallback(uint32_t events, void * opaque)
return;
error:
waylandEpollUnregister(data->fd);
waylandPollUnregister(data->fd);
close(data->fd);
countedBufferRelease(&data->buffer);
free(data);
@@ -395,12 +518,7 @@ static void dataSourceHandleSend(void * data, struct wl_data_source * source,
struct WCBTransfer * transfer = (struct WCBTransfer *) data;
if (containsMimetype(transfer->mimetypes, mimetype))
{
// Consider making this do non-blocking sends to not stall the Wayland
// event loop if it becomes a problem. This is "fine" in the sense that
// wl-copy also stalls like this, but it's not necessary.
fcntl(fd, F_SETFL, 0);
struct ClipboardWrite * data = malloc(sizeof(struct ClipboardWrite));
struct ClipboardWrite * data = malloc(sizeof(*data));
if (!data)
{
DEBUG_ERROR("Out of memory trying to allocate ClipboardWrite");
@@ -411,7 +529,7 @@ static void dataSourceHandleSend(void * data, struct wl_data_source * source,
data->pos = 0;
data->buffer = transfer->data;
countedBufferAddRef(transfer->data);
waylandEpollRegister(fd, clipboardWriteCallback, data, EPOLLOUT);
waylandPollRegister(fd, clipboardWriteCallback, data, EPOLLOUT);
return;
}
@@ -436,7 +554,7 @@ static const struct wl_data_source_listener dataSourceListener = {
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,22 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define _GNU_SOURCE
#include "wayland.h"
@@ -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,19 +144,79 @@ bool waylandCursorInit(void)
return false;
}
struct wl_buffer * cursorBuffer = createCursorBuffer();
if (cursorBuffer)
wlWm.cursorSquareBuffer = createSquareCursorBuffer();
if (wlWm.cursorSquareBuffer)
{
wlWm.cursor = wl_compositor_create_surface(wlWm.compositor);
wl_surface_attach(wlWm.cursor, 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 waylandShowPointer(bool show)
void waylandCursorFree(void)
{
wlWm.showPointer = show;
wl_pointer_set_cursor(wlWm.pointer, wlWm.pointerEnterSerial, show ? wlWm.cursor : NULL, 0, 0);
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 waylandCursorScaleChange(void)
{
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,22 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"
@@ -28,9 +28,11 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "app.h"
#include "common/debug.h"
#include "util.h"
#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL)
#include "egl_dynprocs.h"
#include "eglutil.h"
bool waylandEGLInit(int w, int h)
{
@@ -40,6 +42,7 @@ bool waylandEGLInit(int w, int h)
DEBUG_ERROR("Failed to create EGL window");
return false;
}
return true;
}
@@ -49,14 +52,14 @@ EGLDisplay waylandGetEGLDisplay(void)
const char *early_exts = eglQueryString(NULL, EGL_EXTENSIONS);
if (strstr(early_exts, "EGL_KHR_platform_wayland") != NULL &&
if (util_hasGLExt(early_exts, "EGL_KHR_platform_wayland") &&
g_egl_dynProcs.eglGetPlatformDisplay)
{
DEBUG_INFO("Using eglGetPlatformDisplay");
return g_egl_dynProcs.eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, native, NULL);
}
if (strstr(early_exts, "EGL_EXT_platform_wayland") != NULL &&
if (util_hasGLExt(early_exts, "EGL_EXT_platform_wayland") &&
g_egl_dynProcs.eglGetPlatformDisplayEXT)
{
DEBUG_INFO("Using eglGetPlatformDisplayEXT");
@@ -67,23 +70,65 @@ EGLDisplay waylandGetEGLDisplay(void)
return eglGetDisplay(native);
}
void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface)
void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count)
{
eglSwapBuffers(display, surface);
if (wlWm.resizeSerial)
if (!wlWm.swapWithDamage.init)
{
wl_egl_window_resize(wlWm.eglWindow, wlWm.width, wlWm.height, 0, 0);
if (wl_proxy_get_version((struct wl_proxy *) wlWm.surface) < 4)
{
DEBUG_INFO("Swapping buffers with damage: not supported, need wl_compositor v4");
swapWithDamageDisable(&wlWm.swapWithDamage);
}
else
swapWithDamageInit(&wlWm.swapWithDamage, display);
}
waylandPresentationFrame();
swapWithDamage(&wlWm.swapWithDamage, display, surface, damage, count);
if (wlWm.needsResize)
{
wl_egl_window_resize(wlWm.eglWindow, wl_fixed_to_int(wlWm.width * wlWm.scale),
wl_fixed_to_int(wlWm.height * wlWm.scale), 0, 0);
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, (struct Border) {0, 0, 0, 0});
xdg_surface_ack_configure(wlWm.xdgSurface, wlWm.resizeSerial);
wlWm.resizeSerial = 0;
app_handleResizeEvent(wlWm.width, wlWm.height, wl_fixed_to_double(wlWm.scale),
(struct Border) {0, 0, 0, 0});
app_invalidateWindow(true);
waylandStopWaitFrame();
wlWm.needsResize = false;
}
waylandShellAckConfigureIfNeeded();
}
#endif
@@ -166,6 +211,6 @@ void waylandGLSetSwapInterval(int interval)
void waylandGLSwapBuffers(void)
{
waylandEGLSwapBuffers(wlWm.glDisplay, wlWm.glSurface);
waylandEGLSwapBuffers(wlWm.glDisplay, wlWm.glSurface, NULL, 0);
}
#endif

View File

@@ -1,22 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"

View File

@@ -1,30 +1,33 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-client.h>
#include <xkbcommon/xkbcommon.h>
#include "app.h"
#include "common/debug.h"
@@ -46,9 +49,13 @@ static void pointerEnterHandler(void * data, struct wl_pointer * pointer,
uint32_t serial, struct wl_surface * surface, wl_fixed_t sxW,
wl_fixed_t syW)
{
if (surface != wlWm.surface)
return;
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);
@@ -71,17 +78,25 @@ static void pointerEnterHandler(void * data, struct wl_pointer * pointer,
static void pointerLeaveHandler(void * data, struct wl_pointer * pointer,
uint32_t serial, struct wl_surface * surface)
{
if (surface != wlWm.surface)
return;
wlWm.pointerInSurface = false;
app_handleEnterEvent(false);
}
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)
@@ -147,12 +162,60 @@ 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 void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
uint32_t serial, struct wl_surface * surface, struct wl_array * keys)
{
if (surface != wlWm.surface)
return;
wlWm.focusedOnSurface = true;
app_handleFocusEvent(true);
wlWm.keyboardEnterSerial = serial;
@@ -164,23 +227,59 @@ static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
static void keyboardLeaveHandler(void * data, struct wl_keyboard * keyboard,
uint32_t serial, struct wl_surface * surface)
{
if (surface != wlWm.surface)
return;
wlWm.focusedOnSurface = false;
waylandCBInvalidate();
app_handleFocusEvent(false);
}
static void keyboardKeyHandler(void * data, struct wl_keyboard * keyboard,
uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
{
if (!wlWm.focusedOnSurface)
return;
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
app_handleKeyPress(key);
else
app_handleKeyRelease(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 = {
@@ -191,20 +290,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);
}
}
}
@@ -269,17 +401,14 @@ 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;
}
@@ -287,9 +416,26 @@ bool waylandInputInit(void)
void waylandInputFree(void)
{
waylandUngrabPointer();
wl_pointer_destroy(wlWm.pointer);
wl_keyboard_destroy(wlWm.keyboard);
LG_LOCK_FREE(wlWm.confineLock);
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)
@@ -306,22 +452,31 @@ void waylandGrabPointer(void)
&relativePointerListener, NULL);
}
if (!wlWm.confinedPointer)
INTERLOCKED_SECTION(wlWm.confineLock,
{
wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
}
if (!wlWm.confinedPointer)
{
wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
}
});
}
void waylandUngrabPointer(void)
inline static void internalUngrabPointer(bool lock)
{
if (lock)
LG_LOCK(wlWm.confineLock);
if (wlWm.confinedPointer)
{
zwp_confined_pointer_v1_destroy(wlWm.confinedPointer);
wlWm.confinedPointer = NULL;
}
if (lock)
LG_UNLOCK(wlWm.confineLock);
if (!wlWm.warpSupport)
{
if (!wlWm.relativePointer)
@@ -338,6 +493,61 @@ void waylandUngrabPointer(void)
}
}
void waylandUngrabPointer(void)
{
internalUngrabPointer(true);
}
void waylandCapturePointer(void)
{
if (!wlWm.warpSupport)
{
waylandGrabPointer();
return;
}
INTERLOCKED_SECTION(wlWm.confineLock,
{
if (wlWm.confinedPointer)
{
zwp_confined_pointer_v1_destroy(wlWm.confinedPointer);
wlWm.confinedPointer = NULL;
}
wlWm.lockedPointer = zwp_pointer_constraints_v1_lock_pointer(
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
});
}
void waylandUncapturePointer(void)
{
INTERLOCKED_SECTION(wlWm.confineLock,
{
if (wlWm.lockedPointer)
{
zwp_locked_pointer_v1_destroy(wlWm.lockedPointer);
wlWm.lockedPointer = NULL;
}
/* we need to ungrab the pointer on the following conditions when exiting capture mode:
* - if warp is not supported, exit via window edge detection will never work
* as the cursor can not be warped out of the window when we release it.
* - if the format is invalid as we do not know where the guest cursor is,
* which also breaks edge detection.
* - if the user has opted to use captureInputOnly mode.
*/
if (!wlWm.warpSupport || !app_isFormatValid() || app_isCaptureOnlyMode())
internalUngrabPointer(false);
else if (wlWm.pointer)
{
wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
}
});
}
void waylandGrabKeyboard(void)
{
if (wlWm.keyboardInhibitManager && !wlWm.keyboardInhibitor)
@@ -358,32 +568,44 @@ void waylandUngrabKeyboard(void)
void waylandWarpPointer(int x, int y, bool exiting)
{
if (x < 0) x = 0;
else if (x >= wlWm.width) x = wlWm.width - 1;
if (y < 0) y = 0;
else if (y >= wlWm.height) y = wlWm.height - 1;
if (!wlWm.pointerInSurface || wlWm.lockedPointer)
return;
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
wl_region_add(region, x, y, 1, 1);
if (wlWm.confinedPointer)
INTERLOCKED_SECTION(wlWm.confineLock,
{
zwp_confined_pointer_v1_set_region(wlWm.confinedPointer, region);
wl_surface_commit(wlWm.surface);
zwp_confined_pointer_v1_set_region(wlWm.confinedPointer, NULL);
}
else
{
struct zwp_confined_pointer_v1 * confine;
confine = zwp_pointer_constraints_v1_confine_pointer(
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, region,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
wl_surface_commit(wlWm.surface);
zwp_confined_pointer_v1_destroy(confine);
}
if (wlWm.lockedPointer)
{
LG_UNLOCK(wlWm.confineLock);
return;
}
wl_surface_commit(wlWm.surface);
wl_region_destroy(region);
if (x < 0) x = 0;
else if (x >= wlWm.width) x = wlWm.width - 1;
if (y < 0) y = 0;
else if (y >= wlWm.height) y = wlWm.height - 1;
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
wl_region_add(region, x, y, 1, 1);
if (wlWm.confinedPointer)
{
zwp_confined_pointer_v1_set_region(wlWm.confinedPointer, region);
wl_surface_commit(wlWm.surface);
zwp_confined_pointer_v1_set_region(wlWm.confinedPointer, NULL);
}
else
{
struct zwp_confined_pointer_v1 * confine;
confine = zwp_pointer_constraints_v1_confine_pointer(
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, region,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
wl_surface_commit(wlWm.surface);
zwp_confined_pointer_v1_destroy(confine);
}
wl_surface_commit(wlWm.surface);
wl_region_destroy(region);
});
}
void waylandRealignPointer(void)
@@ -391,3 +613,11 @@ void waylandRealignPointer(void)
if (!wlWm.warpSupport)
app_resyncMouseBasic();
}
void waylandGuestPointerUpdated(double x, double y, double localX, double localY)
{
if (!wlWm.warpSupport || !wlWm.pointerInSurface || wlWm.lockedPointer)
return;
waylandWarpPointer((int) localX, (int) localY, false);
}

View File

@@ -0,0 +1,220 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"
#include <stdbool.h>
#include <string.h>
#include <wayland-client.h>
#include "common/debug.h"
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)
{
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 * opaque, struct wl_output * wl_output, uint32_t flags,
int32_t width, int32_t height, int32_t refresh)
{
if (!(flags & WL_OUTPUT_MODE_CURRENT))
return;
struct WaylandOutput * node = opaque;
node->modeWidth = width;
node->modeHeight = height;
}
static void outputDoneHandler(void * opaque, struct wl_output * output)
{
struct WaylandOutput * node = opaque;
outputUpdateScale(node);
}
static void outputScaleHandler(void * opaque, struct wl_output * output, int32_t scale)
{
struct WaylandOutput * node = opaque;
node->scaleInt = scale;
}
static const struct wl_output_listener outputListener = {
.geometry = outputGeometryHandler,
.mode = outputModeHandler,
.done = outputDoneHandler,
.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);
return true;
}
void waylandOutputFree(void)
{
struct WaylandOutput * node;
struct WaylandOutput * temp;
wl_list_for_each_safe(node, temp, &wlWm.outputs, link)
{
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);
}
}
void waylandOutputBind(uint32_t name, uint32_t version)
{
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;
}
node->name = name;
node->scale = 0;
node->version = version;
node->output = wl_registry_bind(wlWm.registry, name,
&wl_output_interface, version >= 3 ? 3 : 2);
if (!node->output)
{
DEBUG_ERROR("Failed to bind to wl_output %u\n", name);
free(node);
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);
}
void waylandOutputTryUnbind(uint32_t name)
{
struct WaylandOutput * node;
wl_list_for_each(node, &wlWm.outputs, link)
{
if (node->name == 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;
}
}
}
wl_fixed_t waylandOutputGetScale(struct wl_output * output)
{
struct WaylandOutput * node;
wl_list_for_each(node, &wlWm.outputs, link)
if (node->output == output)
return node->scale;
return 0;
}

View File

@@ -1,22 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"
@@ -30,8 +30,13 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#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 (!waylandEpollRegister(wlWm.displayFd, waylandDisplayCallback, NULL, EPOLLIN))
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,
{
@@ -105,7 +125,7 @@ void waylandWait(unsigned int time)
});
}
static void waylandEpollRemoveNode(struct WaylandPoll * node)
static void waylandPollRemoveNode(struct WaylandPoll * node)
{
INTERLOCKED_SECTION(wlWm.pollLock,
{
@@ -113,9 +133,9 @@ static void waylandEpollRemoveNode(struct WaylandPoll * node)
});
}
bool waylandEpollRegister(int fd, WaylandPollCallback callback, void * opaque, uint32_t events)
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;
@@ -134,7 +154,7 @@ bool waylandEpollRegister(int fd, WaylandPollCallback callback, void * opaque, u
.data = (epoll_data_t) { .ptr = node },
}) < 0)
{
waylandEpollRemoveNode(node);
waylandPollRemoveNode(node);
free(node);
return false;
}
@@ -142,7 +162,7 @@ bool waylandEpollRegister(int fd, WaylandPollCallback callback, void * opaque, u
return true;
}
bool waylandEpollUnregister(int fd)
bool waylandPollUnregister(int fd)
{
struct WaylandPoll * node = NULL;
INTERLOCKED_SECTION(wlWm.pollLock,
@@ -167,7 +187,7 @@ bool waylandEpollUnregister(int fd)
return false;
}
waylandEpollRemoveNode(node);
waylandPollRemoveNode(node);
INTERLOCKED_SECTION(wlWm.pollFreeLock,
{

View File

@@ -0,0 +1,119 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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);
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,22 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"
@@ -30,17 +30,29 @@ Place, Suite 330, Boston, MA 02111-1307 USA
static void registryGlobalHandler(void * data, struct wl_registry * registry,
uint32_t name, const char * interface, uint32_t version)
{
if (!strcmp(interface, wl_seat_interface.name) && !wlWm.seat)
if (!strcmp(interface, wl_output_interface.name))
waylandOutputBind(name, version);
else if (!strcmp(interface, wl_seat_interface.name) && !wlWm.seat)
wlWm.seat = wl_registry_bind(wlWm.registry, name, &wl_seat_interface, 1);
else if (!strcmp(interface, wl_shm_interface.name))
wlWm.shm = wl_registry_bind(wlWm.registry, name, &wl_shm_interface, 1);
else if (!strcmp(interface, wl_compositor_interface.name))
wlWm.compositor = wl_registry_bind(wlWm.registry, name, &wl_compositor_interface, 4);
else if (!strcmp(interface, wl_compositor_interface.name) && version >= 3)
wlWm.compositor = wl_registry_bind(wlWm.registry, name,
// we only need v3 to run, but v4 can use eglSwapBuffersWithDamageKHR
&wl_compositor_interface, version > 4 ? 4 : version);
#ifndef ENABLE_LIBDECOR
else if (!strcmp(interface, xdg_wm_base_interface.name))
wlWm.xdgWmBase = wl_registry_bind(wlWm.registry, name, &xdg_wm_base_interface, 1);
else if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name))
wlWm.xdgDecorationManager = wl_registry_bind(wlWm.registry, name,
&zxdg_decoration_manager_v1_interface, 1);
#endif
else if (!strcmp(interface, wp_presentation_interface.name))
wlWm.presentation = wl_registry_bind(wlWm.registry, name,
&wp_presentation_interface, 1);
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);
@@ -50,18 +62,22 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
else if (!strcmp(interface, zwp_keyboard_shortcuts_inhibit_manager_v1_interface.name))
wlWm.keyboardInhibitManager = wl_registry_bind(wlWm.registry, name,
&zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
else if (!strcmp(interface, wl_data_device_manager_interface.name))
else if (!strcmp(interface, wl_data_device_manager_interface.name) && version >= 3)
wlWm.dataDeviceManager = wl_registry_bind(wlWm.registry, name,
&wl_data_device_manager_interface, 3);
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);
}
static void registryGlobalRemoveHandler(void * data,
struct wl_registry * registry, uint32_t name)
{
// Do nothing.
waylandOutputTryUnbind(name);
}
static const struct wl_registry_listener registryListener = {

View File

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

View File

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

View File

@@ -1,22 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"

View File

@@ -1,58 +1,33 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define _GNU_SOURCE
#include "wayland.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <unistd.h>
#include <linux/input.h>
#include <poll.h>
#include <sys/epoll.h>
#include <SDL2/SDL.h>
#include <string.h>
#include <wayland-client.h>
#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL)
# include <wayland-egl.h>
# include "egl_dynprocs.h"
# include <EGL/eglext.h>
#endif
#include "app.h"
#include "common/debug.h"
#include "common/locking.h"
#include "common/countedbuffer.h"
#include "common/option.h"
#include "wayland-xdg-shell-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"
static struct Option waylandOptions[] =
{
{
@@ -62,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}
};
@@ -89,32 +71,42 @@ static bool waylandProbe(void)
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;
wlWm.height = params.h;
if (!waylandPollInit())
return false;
if (!waylandOutputInit())
return false;
if (!waylandRegistryInit())
return false;
if (!waylandIdleInit())
return false;
if (!waylandInputInit())
return false;
if (!waylandWindowInit(params.title, params.fullscreen, params.maximize, params.borderless))
return false;
if (!waylandEGLInit(params.w, params.h))
if (!waylandPresentationInit())
return false;
if (!waylandCursorInit())
return false;
if (!waylandInputInit())
return false;
if (!waylandWindowInit(params.title, params.fullscreen, params.maximize, params.borderless, params.resizable))
return false;
if (!waylandEGLInit(params.w, params.h))
return false;
#ifdef ENABLE_OPENGL
if (params.opengl && !waylandOpenGLInit())
return false;
@@ -138,8 +130,11 @@ static void waylandFree(void)
{
waylandIdleFree();
waylandWindowFree();
waylandPresentationFree();
waylandInputFree();
waylandOutputFree();
waylandRegistryFree();
waylandCursorFree();
wl_display_disconnect(wlWm.display);
}
@@ -156,43 +151,49 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
struct LG_DisplayServerOps LGDS_Wayland =
{
.setup = waylandSetup,
.probe = waylandProbe,
.earlyInit = waylandEarlyInit,
.init = waylandInit,
.startup = waylandStartup,
.shutdown = waylandShutdown,
.free = waylandFree,
.getProp = waylandGetProp,
.setup = waylandSetup,
.probe = waylandProbe,
.earlyInit = waylandEarlyInit,
.init = waylandInit,
.startup = waylandStartup,
.shutdown = waylandShutdown,
.free = waylandFree,
.getProp = waylandGetProp,
#ifdef ENABLE_EGL
.getEGLDisplay = waylandGetEGLDisplay,
.getEGLNativeWindow = waylandGetEGLNativeWindow,
.eglSwapBuffers = waylandEGLSwapBuffers,
.getEGLDisplay = waylandGetEGLDisplay,
.getEGLNativeWindow = waylandGetEGLNativeWindow,
.eglSwapBuffers = waylandEGLSwapBuffers,
#endif
#ifdef ENABLE_OPENGL
.glCreateContext = waylandGLCreateContext,
.glDeleteContext = waylandGLDeleteContext,
.glMakeCurrent = waylandGLMakeCurrent,
.glSetSwapInterval = waylandGLSetSwapInterval,
.glSwapBuffers = waylandGLSwapBuffers,
.glCreateContext = waylandGLCreateContext,
.glDeleteContext = waylandGLDeleteContext,
.glMakeCurrent = waylandGLMakeCurrent,
.glSetSwapInterval = waylandGLSetSwapInterval,
.glSwapBuffers = waylandGLSwapBuffers,
#endif
.showPointer = waylandShowPointer,
.grabPointer = waylandGrabPointer,
.ungrabPointer = waylandUngrabPointer,
.grabKeyboard = waylandGrabKeyboard,
.ungrabKeyboard = waylandUngrabKeyboard,
.warpPointer = waylandWarpPointer,
.realignPointer = waylandRealignPointer,
.isValidPointerPos = waylandIsValidPointerPos,
.inhibitIdle = waylandInhibitIdle,
.uninhibitIdle = waylandUninhibitIdle,
.wait = waylandWait,
.setWindowSize = waylandSetWindowSize,
.setFullscreen = waylandSetFullscreen,
.getFullscreen = waylandGetFullscreen,
.waitFrame = waylandWaitFrame,
.skipFrame = waylandSkipFrame,
.stopWaitFrame = waylandStopWaitFrame,
.guestPointerUpdated = waylandGuestPointerUpdated,
.setPointer = waylandSetPointer,
.grabPointer = waylandGrabPointer,
.ungrabPointer = waylandUngrabPointer,
.capturePointer = waylandCapturePointer,
.uncapturePointer = waylandUncapturePointer,
.grabKeyboard = waylandGrabKeyboard,
.ungrabKeyboard = waylandUngrabKeyboard,
.warpPointer = waylandWarpPointer,
.realignPointer = waylandRealignPointer,
.isValidPointerPos = waylandIsValidPointerPos,
.inhibitIdle = waylandInhibitIdle,
.uninhibitIdle = waylandUninhibitIdle,
.wait = waylandWait,
.setWindowSize = waylandSetWindowSize,
.setFullscreen = waylandSetFullscreen,
.getFullscreen = waylandGetFullscreen,
.minimize = waylandMinimize,
.cbInit = waylandCBInit,
.cbNotice = waylandCBNotice,

View File

@@ -1,44 +1,52 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdbool.h>
#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"
typedef void (*WaylandPollCallback)(uint32_t events, void * opaque);
@@ -51,10 +59,45 @@ struct WaylandPoll
struct wl_list link;
};
struct WaylandOutput
{
uint32_t name;
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;
};
struct SurfaceOutput
{
struct wl_output * output;
struct wl_list link;
};
enum EGLSwapWithDamageState {
SWAP_WITH_DAMAGE_UNKNOWN,
SWAP_WITH_DAMAGE_UNSUPPORTED,
SWAP_WITH_DAMAGE_KHR,
SWAP_WITH_DAMAGE_EXT,
};
struct xkb_context;
struct xkb_keymap;
struct xkb_state;
struct WaylandDSState
{
bool pointerGrabbed;
bool keyboardGrabbed;
bool pointerInSurface;
bool focusedOnSurface;
struct wl_display * display;
struct wl_surface * surface;
@@ -64,14 +107,18 @@ struct WaylandDSState
struct wl_compositor * compositor;
int32_t width, height;
wl_fixed_t scale;
bool fractionalScale;
bool needsResize;
bool fullscreen;
uint32_t resizeSerial;
bool configured;
bool warpSupport;
double cursorX, cursorY;
#ifdef ENABLE_EGL
#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL)
struct wl_egl_window * eglWindow;
struct SwapWithDamageData swapWithDamage;
#endif
#ifdef ENABLE_OPENGL
@@ -80,13 +127,33 @@ 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;
#else
struct xdg_wm_base * xdgWmBase;
struct xdg_surface * xdgSurface;
struct xdg_toplevel * xdgToplevel;
struct zxdg_decoration_manager_v1 * xdgDecorationManager;
struct zxdg_toplevel_decoration_v1 * xdgToplevelDecoration;
#endif
struct wl_surface * cursor;
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;
@@ -96,18 +163,32 @@ 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;
struct zwp_pointer_constraints_v1 * pointerConstraints;
struct zwp_relative_pointer_v1 * relativePointer;
struct zwp_confined_pointer_v1 * confinedPointer;
struct zwp_locked_pointer_v1 * lockedPointer;
bool showPointer;
uint32_t pointerEnterSerial;
LG_Lock confineLock;
struct zwp_idle_inhibit_manager_v1 * idleInhibitManager;
struct zwp_idle_inhibitor_v1 * idleInhibitor;
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
LG_Lock pollLock;
@@ -137,13 +218,9 @@ struct WCBState
struct wl_data_device * dataDevice;
char lgMimetype[64];
enum LG_ClipboardData pendingType;
char * pendingMimetype;
bool isSelfCopy;
enum LG_ClipboardData stashedType;
uint8_t * stashedContents;
ssize_t stashedSize;
char * mimetypes[LG_CLIPBOARD_DATA_NONE];
struct wl_data_offer * offer;
struct wl_data_offer * dndOffer;
bool haveRequest;
LG_ClipboardData type;
@@ -159,16 +236,19 @@ bool waylandCBInit(void);
void waylandCBRequest(LG_ClipboardData type);
void waylandCBNotice(LG_ClipboardData type);
void waylandCBRelease(void);
void waylandCBInvalidate(void);
// cursor module
bool waylandCursorInit(void);
void waylandShowPointer(bool show);
void waylandCursorFree(void);
void waylandSetPointer(LG_DSPointer pointer);
void waylandCursorScaleChange(void);
// gl module
#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL)
bool waylandEGLInit(int w, int h);
EGLDisplay waylandGetEGLDisplay(void);
void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface);
void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count);
#endif
#ifdef ENABLE_EGL
@@ -197,23 +277,48 @@ void waylandGrabKeyboard(void);
void waylandGrabPointer(void);
void waylandUngrabKeyboard(void);
void waylandUngrabPointer(void);
void waylandCapturePointer(void);
void waylandUncapturePointer(void);
void waylandRealignPointer(void);
void waylandWarpPointer(int x, int y, bool exiting);
void waylandGuestPointerUpdated(double x, double y, double localX, double localY);
// output module
bool waylandOutputInit(void);
void waylandOutputFree(void);
void waylandOutputBind(uint32_t name, uint32_t version);
void waylandOutputTryUnbind(uint32_t name);
wl_fixed_t waylandOutputGetScale(struct wl_output * output);
// poll module
bool waylandPollInit(void);
void waylandWait(unsigned int time);
bool waylandEpollRegister(int fd, WaylandPollCallback callback, void * opaque, uint32_t events);
bool waylandEpollUnregister(int fd);
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);
// window module
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless);
void waylandWindowFree(void);
void waylandSetWindowSize(int x, int y);
// shell module
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable);
void waylandShellAckConfigureIfNeeded(void);
void waylandSetFullscreen(bool fs);
bool waylandGetFullscreen(void);
void waylandMinimize(void);
void waylandShellResize(int w, int h);
// window module
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable);
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,22 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wayland.h"
@@ -27,80 +27,76 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "app.h"
#include "common/debug.h"
// XDG WM base listeners.
static void xdgWmBasePing(void * data, struct xdg_wm_base * xdgWmBase, uint32_t serial)
{
xdg_wm_base_pong(xdgWmBase, serial);
}
static const struct xdg_wm_base_listener xdgWmBaseListener = {
.ping = xdgWmBasePing,
};
#include "common/event.h"
// Surface-handling listeners.
static void xdgSurfaceConfigure(void * data, struct xdg_surface * xdgSurface,
uint32_t serial)
void waylandWindowUpdateScale(void)
{
if (wlWm.configured)
wlWm.resizeSerial = serial;
else
wl_fixed_t maxScale = 0;
struct SurfaceOutput * node;
wl_list_for_each(node, &wlWm.surfaceOutputs, link)
{
xdg_surface_ack_configure(xdgSurface, serial);
wlWm.configured = true;
wl_fixed_t scale = waylandOutputGetScale(node->output);
if (scale > maxScale)
maxScale = scale;
}
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 const struct xdg_surface_listener xdgSurfaceListener = {
.configure = xdgSurfaceConfigure,
static void wlSurfaceEnterHandler(void * data, struct wl_surface * surface, struct wl_output * output)
{
struct SurfaceOutput * node = malloc(sizeof(*node));
node->output = output;
wl_list_insert(&wlWm.surfaceOutputs, &node->link);
waylandWindowUpdateScale();
}
static void wlSurfaceLeaveHandler(void * data, struct wl_surface * surface, struct wl_output * output)
{
struct SurfaceOutput * node;
wl_list_for_each(node, &wlWm.surfaceOutputs, link)
if (node->output == output)
{
wl_list_remove(&node->link);
break;
}
waylandWindowUpdateScale();
}
static const struct wl_surface_listener wlSurfaceListener = {
.enter = wlSurfaceEnterHandler,
.leave = wlSurfaceLeaveHandler,
};
// XDG Surface listeners.
static void xdgToplevelConfigure(void * data, struct xdg_toplevel * xdgToplevel,
int32_t width, int32_t height, struct wl_array * states)
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
{
wlWm.width = width;
wlWm.height = height;
wlWm.fullscreen = false;
wlWm.scale = wl_fixed_from_int(1);
enum xdg_toplevel_state * state;
wl_array_for_each(state, states)
wlWm.frameEvent = lgCreateEvent(true, 0);
if (!wlWm.frameEvent)
{
if (*state == XDG_TOPLEVEL_STATE_FULLSCREEN)
wlWm.fullscreen = true;
DEBUG_ERROR("Failed to initialize event for waitFrame");
return false;
}
}
lgSignalEvent(wlWm.frameEvent);
static void xdgToplevelClose(void * data, struct xdg_toplevel * xdgToplevel)
{
app_handleCloseEvent();
}
static const struct xdg_toplevel_listener xdgToplevelListener = {
.configure = xdgToplevelConfigure,
.close = xdgToplevelClose,
};
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless)
{
if (!wlWm.compositor)
{
DEBUG_ERROR("Compositor missing wl_compositor, will not proceed");
DEBUG_ERROR("Compositor missing wl_compositor (version 3+), will not proceed");
return false;
}
if (!wlWm.xdgWmBase)
{
DEBUG_ERROR("Compositor missing xdg_wm_base, will not proceed");
return false;
}
xdg_wm_base_add_listener(wlWm.xdgWmBase, &xdgWmBaseListener, NULL);
//wl_display_roundtrip(wlWm.display);
wlWm.surface = wl_compositor_create_surface(wlWm.compositor);
if (!wlWm.surface)
{
@@ -108,59 +104,24 @@ bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool
return false;
}
wlWm.xdgSurface = xdg_wm_base_get_xdg_surface(wlWm.xdgWmBase, wlWm.surface);
xdg_surface_add_listener(wlWm.xdgSurface, &xdgSurfaceListener, NULL);
wl_surface_add_listener(wlWm.surface, &wlSurfaceListener, NULL);
wlWm.xdgToplevel = xdg_surface_get_toplevel(wlWm.xdgSurface);
xdg_toplevel_add_listener(wlWm.xdgToplevel, &xdgToplevelListener, NULL);
xdg_toplevel_set_title(wlWm.xdgToplevel, title);
xdg_toplevel_set_app_id(wlWm.xdgToplevel, "looking-glass-client");
if (fullscreen)
xdg_toplevel_set_fullscreen(wlWm.xdgToplevel, NULL);
if (maximize)
xdg_toplevel_set_maximized(wlWm.xdgToplevel);
if (!waylandShellInit(title, fullscreen, maximize, borderless, resizable))
return false;
wl_surface_commit(wlWm.surface);
if (wlWm.xdgDecorationManager)
{
wlWm.xdgToplevelDecoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
wlWm.xdgDecorationManager, wlWm.xdgToplevel);
if (wlWm.xdgToplevelDecoration)
{
zxdg_toplevel_decoration_v1_set_mode(wlWm.xdgToplevelDecoration,
borderless ?
ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
}
}
return true;
}
void waylandWindowFree(void)
{
wl_surface_destroy(wlWm.surface);
lgFreeEvent(wlWm.frameEvent);
}
void waylandSetWindowSize(int x, int y)
{
// FIXME: implement.
}
void waylandSetFullscreen(bool fs)
{
if (fs)
xdg_toplevel_set_fullscreen(wlWm.xdgToplevel, NULL);
else
xdg_toplevel_unset_fullscreen(wlWm.xdgToplevel);
}
bool waylandGetFullscreen(void)
{
return wlWm.fullscreen;
waylandShellResize(x, y);
}
bool waylandIsValidPointerPos(int x, int y)
@@ -168,4 +129,34 @@ 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,27 +2,30 @@ cmake_minimum_required(VERSION 3.0)
project(displayserver_X11 LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(DISPLAYSERVER_X11_PKGCONFIG REQUIRED
pkg_check_modules(DISPLAYSERVER_X11 REQUIRED IMPORTED_TARGET
x11
xi
xfixes
xscrnsaver
xinerama
xcursor
xpresent
)
add_library(displayserver_X11 STATIC
x11.c
atoms.c
clipboard.c
)
add_definitions(-D GLX_GLXEXT_PROTOTYPES)
target_link_libraries(displayserver_X11
${DISPLAYSERVER_X11_PKGCONFIG_LIBRARIES}
PkgConfig::DISPLAYSERVER_X11
lg_common
)
target_include_directories(displayserver_X11
PRIVATE
src
${DISPLAYSERVER_X11_PKGCONFIG_INCLUDE_DIRS}
)

View File

@@ -0,0 +1,32 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "atoms.h"
#include "x11.h"
struct X11DSAtoms x11atoms = { 0 };
void X11AtomsInit(void)
{
#define DEF_ATOM(x, onlyIfExists) \
x11atoms.x = XInternAtom(x11.display, #x, onlyIfExists);
DEF_ATOMS()
#undef DEF_ATOM
}

View File

@@ -0,0 +1,60 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_X11DS_ATOMS_
#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_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_WINDOW_TYPE, True) \
DEF_ATOM(_NET_WM_WINDOW_TYPE_NORMAL, True) \
DEF_ATOM(_NET_WM_WINDOW_TYPE_UTILITY, True) \
DEF_ATOM(WM_DELETE_WINDOW, True) \
DEF_ATOM(_MOTIF_WM_HINTS, True) \
\
DEF_ATOM(CLIPBOARD, False) \
DEF_ATOM(TARGETS, False) \
DEF_ATOM(SEL_DATA, False) \
DEF_ATOM(INCR, False)
#include <X11/Xlib.h>
#define DEF_ATOM(x, onlyIfExists) Atom x;
struct X11DSAtoms
{
DEF_ATOMS()
};
#undef DEF_ATOM
extern struct X11DSAtoms x11atoms;
void X11AtomsInit(void);
#endif

View File

@@ -0,0 +1,424 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "clipboard.h"
#include "x11.h"
#include "atoms.h"
#include <string.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include "app.h"
#include "common/debug.h"
struct X11ClipboardState
{
Atom aCurSelection;
Atom aTypes[LG_CLIPBOARD_DATA_NONE];
LG_ClipboardData type;
bool haveRequest;
bool incrStart;
unsigned int lowerBound;
};
static const char * atomTypes[] =
{
"UTF8_STRING",
"image/png",
"image/bmp",
"image/tiff",
"image/jpeg"
};
static struct X11ClipboardState x11cb;
// forwards
static void x11CBSelectionRequest(const XSelectionRequestEvent e);
static void x11CBSelectionClear(const XSelectionClearEvent e);
static void x11CBSelectionIncr(const XPropertyEvent e);
static void x11CBSelectionNotify(const XSelectionEvent e);
static void x11CBXFixesSelectionNotify(const XFixesSelectionNotifyEvent e);
bool x11CBEventThread(const XEvent xe)
{
switch(xe.type)
{
case SelectionRequest:
x11CBSelectionRequest(xe.xselectionrequest);
return true;
case SelectionClear:
x11CBSelectionClear(xe.xselectionclear);
return true;
case SelectionNotify:
x11CBSelectionNotify(xe.xselection);
return true;
case PropertyNotify:
if (xe.xproperty.state != PropertyNewValue)
break;
if (xe.xproperty.atom == x11atoms.SEL_DATA)
{
if (x11cb.lowerBound == 0)
return true;
x11CBSelectionIncr(xe.xproperty);
return true;
}
break;
default:
if (xe.type == x11.eventBase + XFixesSelectionNotify)
{
XFixesSelectionNotifyEvent * sne = (XFixesSelectionNotifyEvent *)&xe;
x11CBXFixesSelectionNotify(*sne);
return true;
}
break;
}
return false;
}
bool x11CBInit()
{
x11cb.aCurSelection = BadValue;
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
{
x11cb.aTypes[i] = XInternAtom(x11.display, atomTypes[i], False);
if (x11cb.aTypes[i] == BadAlloc || x11cb.aTypes[i] == BadValue)
{
DEBUG_ERROR("failed to get atom for type: %s", atomTypes[i]);
return false;
}
}
// use xfixes to get clipboard change notifications
if (!XFixesQueryExtension(x11.display, &x11.eventBase, &x11.errorBase))
{
DEBUG_ERROR("failed to initialize xfixes");
return false;
}
XFixesSelectSelectionInput(x11.display, x11.window,
XA_PRIMARY, XFixesSetSelectionOwnerNotifyMask);
XFixesSelectSelectionInput(x11.display, x11.window,
x11atoms.CLIPBOARD, XFixesSetSelectionOwnerNotifyMask);
return true;
}
static void x11CBReplyFn(void * opaque, LG_ClipboardData type,
uint8_t * data, uint32_t size)
{
XEvent *s = (XEvent *)opaque;
XChangeProperty(
x11.display ,
s->xselection.requestor,
s->xselection.property ,
s->xselection.target ,
8,
PropModeReplace,
data,
size);
XSendEvent(x11.display, s->xselection.requestor, 0, 0, s);
XFlush(x11.display);
free(s);
}
static void x11CBSelectionRequest(const XSelectionRequestEvent e)
{
XEvent * s = malloc(sizeof(*s));
s->xselection.type = SelectionNotify;
s->xselection.requestor = e.requestor;
s->xselection.selection = e.selection;
s->xselection.target = e.target;
s->xselection.property = e.property;
s->xselection.time = e.time;
if (!x11cb.haveRequest)
goto nodata;
// target list requested
if (e.target == x11atoms.TARGETS)
{
Atom targets[2];
targets[0] = x11atoms.TARGETS;
targets[1] = x11cb.aTypes[x11cb.type];
XChangeProperty(
e.display,
e.requestor,
e.property,
XA_ATOM,
32,
PropModeReplace,
(unsigned char*)targets,
sizeof(targets) / sizeof(Atom));
goto send;
}
// look to see if we can satisfy the data type
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
if (x11cb.aTypes[i] == e.target && x11cb.type == i)
{
// request the data
app_clipboardRequest(x11CBReplyFn, s);
return;
}
nodata:
// report no data
s->xselection.property = None;
send:
XSendEvent(x11.display, e.requestor, 0, 0, s);
XFlush(x11.display);
free(s);
}
static void x11CBSelectionClear(const XSelectionClearEvent e)
{
if (e.selection != XA_PRIMARY && e.selection != x11atoms.CLIPBOARD)
return;
x11cb.aCurSelection = BadValue;
app_clipboardRelease();
return;
}
static void x11CBSelectionIncr(const XPropertyEvent e)
{
Atom type;
int format;
unsigned long itemCount, after;
unsigned char *data;
if (XGetWindowProperty(
e.display,
e.window,
e.atom,
0, ~0L, // start and length
True, // delete the property
x11atoms.INCR,
&type,
&format,
&itemCount,
&after,
&data) != Success)
{
DEBUG_INFO("GetProp Failed");
app_clipboardNotifySize(LG_CLIPBOARD_DATA_NONE, 0);
goto out;
}
LG_ClipboardData dataType;
for(dataType = 0; dataType < LG_CLIPBOARD_DATA_NONE; ++dataType)
if (x11cb.aTypes[dataType] == type)
break;
if (dataType == LG_CLIPBOARD_DATA_NONE)
{
DEBUG_WARN("clipboard data (%s) not in a supported format",
XGetAtomName(x11.display, type));
x11cb.lowerBound = 0;
app_clipboardNotifySize(LG_CLIPBOARD_DATA_NONE, 0);
goto out;
}
if (x11cb.incrStart)
{
app_clipboardNotifySize(dataType, x11cb.lowerBound);
x11cb.incrStart = false;
}
XFree(data);
data = NULL;
if (XGetWindowProperty(
e.display,
e.window,
e.atom,
0, ~0L, // start and length
True, // delete the property
type,
&type,
&format,
&itemCount,
&after,
&data) != Success)
{
DEBUG_ERROR("XGetWindowProperty Failed");
app_clipboardNotifySize(LG_CLIPBOARD_DATA_NONE, 0);
goto out;
}
app_clipboardData(dataType, data, itemCount);
x11cb.lowerBound -= itemCount;
out:
if (data)
XFree(data);
}
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) ||
e.owner == x11.window || e.owner == 0)
{
return;
}
// remember which selection we are working with
x11cb.aCurSelection = e.selection;
XConvertSelection(
x11.display,
e.selection,
x11atoms.TARGETS,
x11atoms.TARGETS,
x11.window,
CurrentTime);
return;
}
static void x11CBSelectionNotify(const XSelectionEvent e)
{
if (e.property == None)
return;
Atom type;
int format;
unsigned long itemCount, after;
unsigned char *data;
if (XGetWindowProperty(
e.display,
e.requestor,
e.property,
0, ~0L, // start and length
True , // delete the property
AnyPropertyType,
&type,
&format,
&itemCount,
&after,
&data) != Success)
{
app_clipboardNotifySize(LG_CLIPBOARD_DATA_NONE, 0);
goto out;
}
if (type == x11atoms.INCR)
{
x11cb.incrStart = true;
x11cb.lowerBound = *(unsigned int *)data;
goto out;
}
// the target list
if (e.property == x11atoms.TARGETS)
{
// the format is 32-bit and we must have data
// this is technically incorrect however as it's
// an array of padded 64-bit values
if (!data || format != 32)
goto out;
int typeCount = 0;
LG_ClipboardData types[itemCount];
// see if we support any of the targets listed
const uint64_t * targets = (const uint64_t *)data;
for(unsigned long i = 0; i < itemCount; ++i)
{
for(int n = 0; n < LG_CLIPBOARD_DATA_NONE; ++n)
if (x11cb.aTypes[n] == targets[i])
types[typeCount++] = n;
}
app_clipboardNotifyTypes(types, typeCount);
goto out;
}
if (e.property == x11atoms.SEL_DATA)
{
LG_ClipboardData dataType;
for(dataType = 0; dataType < LG_CLIPBOARD_DATA_NONE; ++dataType)
if (x11cb.aTypes[dataType] == type)
break;
if (dataType == LG_CLIPBOARD_DATA_NONE)
{
DEBUG_WARN("clipboard data (%s) not in a supported format",
XGetAtomName(x11.display, type));
goto out;
}
app_clipboardData(dataType, data, itemCount);
goto out;
}
out:
if (data)
XFree(data);
}
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);
}
void x11CBRelease(void)
{
x11cb.haveRequest = false;
XSetSelectionOwner(x11.display, XA_PRIMARY , None, CurrentTime);
XSetSelectionOwner(x11.display, x11atoms.CLIPBOARD, None, CurrentTime);
XFlush(x11.display);
}
void x11CBRequest(LG_ClipboardData type)
{
if (x11cb.aCurSelection == BadValue)
return;
XConvertSelection(
x11.display,
x11cb.aCurSelection,
x11cb.aTypes[type],
x11atoms.SEL_DATA,
x11.window,
CurrentTime);
}

View File

@@ -0,0 +1,36 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_X11DS_CLIPBOARD_
#define _H_X11DS_CLIPBOARD_
#include <stdbool.h>
#include <X11/extensions/Xfixes.h>
#include "interface/displayserver.h"
bool x11CBEventThread(const XEvent xe);
bool x11CBInit();
void x11CBNotice(LG_ClipboardData type);
void x11CBRelease(void);
void x11CBRequest(LG_ClipboardData type);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,103 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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;
//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;
int yValuator;
bool pointerGrabbed;
bool keyboardGrabbed;
bool entered;
bool focused;
bool fullscreen;
struct Rect rect;
struct Border border;
Cursor cursors[LG_POINTER_COUNT];
XIM xim;
XIC xic;
bool modifiers[MOD_COUNT];
// XFixes vars
int eventBase;
int errorBase;
};
extern struct X11DSState x11;
#endif

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(SDL)
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_SDL LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(FONT_SDL_PKGCONFIG REQUIRED
SDL2_ttf
fontconfig
)
add_library(font_SDL STATIC
src/sdl.c
)
target_link_libraries(font_SDL
${FONT_SDL_PKGCONFIG_LIBRARIES}
lg_common
)
target_include_directories(font_SDL
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
src
${FONT_SDL_PKGCONFIG_INCLUDE_DIRS}
)

View File

@@ -1,251 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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 <SDL2/SDL_ttf.h>
#include <fontconfig/fontconfig.h>
static int g_initCount = 0;
static FcConfig * g_fontConfig = NULL;
struct Inst
{
TTF_Font * font;
};
static bool lgf_sdl_create(LG_FontObj * opaque, const char * font_name, unsigned int size)
{
bool ret = false;
if (g_initCount == 0)
{
if (TTF_Init() < 0)
{
DEBUG_ERROR("TTF_Init 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)
{
this->font = TTF_OpenFont((char *)file, size);
if (!this->font)
{
DEBUG_ERROR("TTL_OpenFont Failed");
goto fail_match;
}
}
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)
TTF_Quit();
fail:
return false;
}
static void lgf_sdl_destroy(LG_FontObj opaque)
{
struct Inst * this = (struct Inst *)opaque;
if (this->font)
TTF_CloseFont(this->font);
free(this);
if (--g_initCount == 0)
{
FcConfigDestroy(g_fontConfig);
g_fontConfig = NULL;
TTF_Quit();
}
}
static int lgf_sdl_multiline_width(TTF_Font * font, const char * text)
{
int w, maxW = 0;
const char * ptr = text;
char * buf = NULL;
size_t size = 0;
while (*ptr)
{
const char * newLine = strchr(ptr, '\n');
size_t line = newLine ? newLine - ptr : strlen(ptr);
if (line > size)
{
size = line * 2 + 1;
void * new = realloc(buf, size);
if (!new)
{
DEBUG_ERROR("Failed to allocate memory");
free(buf);
return -1;
}
buf = new;
}
memcpy(buf, ptr, line);
buf[line] = '\0';
if (TTF_SizeUTF8(font, buf, &w, NULL) < 0)
{
free(buf);
DEBUG_ERROR("Failed to measure text: %s", TTF_GetError());
return -1;
}
if (w > maxW)
maxW = w;
ptr += line + 1;
}
free(buf);
return maxW;
}
static LG_FontBitmap * lgf_sdl_render(LG_FontObj opaque, unsigned int fg_color, const char * text)
{
struct Inst * this = (struct Inst *)opaque;
SDL_Surface * surface;
SDL_Color color;
color.r = (fg_color & 0xff000000) >> 24;
color.g = (fg_color & 0x00ff0000) >> 16;
color.b = (fg_color & 0x0000ff00) >> 8;
color.a = (fg_color & 0x000000ff) >> 0;
if (strchr(text, '\n'))
{
int width = lgf_sdl_multiline_width(this->font, text);
if (width < 1)
return NULL;
surface = TTF_RenderUTF8_Blended_Wrapped(this->font, text, color, width);
}
else
surface = TTF_RenderUTF8_Blended(this->font, text, color);
if (!surface)
{
DEBUG_ERROR("Failed to render text: %s", TTF_GetError());
return NULL;
}
LG_FontBitmap * out = malloc(sizeof(LG_FontBitmap));
if (!out)
{
SDL_FreeSurface(surface);
DEBUG_ERROR("Failed to allocate memory for font bitmap");
return NULL;
}
out->reserved = surface;
out->width = surface->w;
out->height = surface->h;
out->bpp = surface->format->BytesPerPixel;
out->pixels = surface->pixels;
return out;
}
static void lgf_sdl_release(LG_FontObj opaque, LG_FontBitmap * font)
{
LG_FontBitmap * bitmap = (LG_FontBitmap *)font;
SDL_FreeSurface(bitmap->reserved);
free(bitmap);
}
struct LG_Font LGF_SDL =
{
.name = "SDL",
.create = lgf_sdl_create,
.destroy = lgf_sdl_destroy,
.render = lgf_sdl_render,
.release = lgf_sdl_release
};

View File

@@ -1,21 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_LG_APP_
#define _H_LG_APP_
@@ -23,8 +24,10 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#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
{
@@ -37,9 +40,14 @@ LG_MsgAlert;
bool app_isRunning(void);
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, const struct Border border);
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);
@@ -49,11 +57,16 @@ void app_resyncMouseBasic(void);
void app_handleButtonPress(int button);
void app_handleButtonRelease(int button);
void app_handleWheelMotion(double motion);
void app_handleKeyPress(int scancode);
void app_handleKeyRelease(int scancode);
void app_handleKeyboardTyped(const char * typed);
void app_handleKeyboardModifiers(bool ctrl, bool shift, bool alt, bool super);
void app_handleKeyboardLEDs(bool numLock, bool capsLock, bool scrollLock);
void app_handleEnterEvent(bool entered);
void app_handleFocusEvent(bool focused);
void app_handleCloseEvent(void);
void app_handleRenderEvent(const uint64_t timeUs);
void app_setFullscreen(bool fs);
bool app_getFullscreen(void);
@@ -62,7 +75,7 @@ bool app_getProp(LG_DSProperty prop, void * ret);
#ifdef ENABLE_EGL
EGLDisplay app_getEGLDisplay(void);
EGLNativeWindowType app_getEGLNativeWindow(void);
void app_eglSwapBuffers(EGLDisplay display, EGLSurface surface);
void app_eglSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count);
#endif
#ifdef ENABLE_OPENGL
@@ -73,8 +86,37 @@ 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);
struct OverlayGraph;
typedef struct OverlayGraph * GraphHandle;
GraphHandle app_registerGraph(const char * name, RingBuffer buffer, float min, float max);
void app_unregisterGraph(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_clipboardNotify(const LG_ClipboardData type, size_t size);
void app_clipboardNotifyTypes(const LG_ClipboardData types[], int count);
void app_clipboardNotifySize(const LG_ClipboardData type, size_t size);
void app_clipboardData(const LG_ClipboardData type, uint8_t * data, size_t size);
void app_clipboardRequest(const LG_ClipboardReplyFn replyFn, void * opaque);
@@ -110,14 +152,4 @@ void app_releaseKeybind(KeybindHandle * handle);
*/
void app_releaseAllKeybinds(void);
/**
* Changes whether the help message is displayed or not.
*/
void app_showHelp(bool show);
/**
* Changes whether the FPS is displayed or not.
*/
void app_showFPS(bool showFPS);
#endif

View File

@@ -1,37 +1,45 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_LG_EGL_DYNPROCS_
#define _H_LG_EGL_DYNPROCS_
#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 (*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;
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;
@@ -41,3 +49,5 @@ void egl_dynProcsInit(void);
#else
#define egl_dynProcsInit(...)
#endif
#endif // _H_LG_EGL_DYNPROCS_

41
client/include/eglutil.h Normal file
View File

@@ -0,0 +1,41 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_LG_GLUTIL_
#define _H_LG_GLUTIL_
#include <stdbool.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "common/types.h"
struct SwapWithDamageData
{
bool init;
PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC func;
};
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);
#endif

View File

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

@@ -1,27 +1,30 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_I_DISPLAYSERVER_
#define _H_I_DISPLAYSERVER_
#include <stdbool.h>
#include <EGL/egl.h>
#include "common/types.h"
#include "common/debug.h"
typedef enum LG_ClipboardData
{
@@ -60,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;
@@ -69,10 +90,13 @@ typedef struct LG_DSInitParams
bool resizable;
bool borderless;
bool maximize;
bool minimizeOnFocusLoss;
// 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;
@@ -82,6 +106,8 @@ typedef void (* LG_ClipboardReplyFn)(void * opaque, const LG_ClipboardData type,
typedef struct LG_DSGLContext
* LG_DSGLContext;
typedef struct LGEvent LGEvent;
struct LG_DisplayServerOps
{
/* called before options are parsed, useful for registering options */
@@ -116,7 +142,7 @@ struct LG_DisplayServerOps
/* EGL support */
EGLDisplay (*getEGLDisplay)(void);
EGLNativeWindowType (*getEGLNativeWindow)(void);
void (*eglSwapBuffers)(EGLDisplay display, EGLSurface surface);
void (*eglSwapBuffers)(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count);
#endif
#ifdef ENABLE_OPENGL
@@ -128,12 +154,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 (*showPointer)(bool show);
void (*grabPointer)();
void (*ungrabPointer)();
void (*guestPointerUpdated)(double x, double y, double localX, double localY);
void (*setPointer)(LG_DSPointer pointer);
void (*grabKeyboard)();
void (*ungrabKeyboard)();
/* (un)grabPointer is used to toggle cursor tracking/confine in normal mode */
void (*grabPointer)();
void (*ungrabPointer)();
/* (un)capturePointer is used do toggle special cursor tracking in capture mode */
void (*capturePointer)();
void (*uncapturePointer)();
/* exiting = true if the warp is to leave the window */
void (*warpPointer)(int x, int y, bool exiting);
@@ -153,10 +197,11 @@ struct LG_DisplayServerOps
/* wait for the specified time without blocking UI processing/event loops */
void (*wait)(unsigned int time);
/* get/set the window dimensions */
/* get/set the window dimensions & state */
void (*setWindowSize)(int x, int y);
bool (*getFullscreen)(void);
void (*setFullscreen)(bool fs);
void (*minimize)(void);
/* clipboard support, optional, if not supported set to NULL */
bool (*cbInit)(void);
@@ -166,45 +211,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)->showPointer ); \
assert((x)->grabPointer ); \
assert((x)->ungrabPointer ); \
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 );
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,21 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once

View File

@@ -0,0 +1,71 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_I_OVERLAY_
#define _H_I_OVERLAY_
#include <stdbool.h>
#include "common/types.h"
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);
/* 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);
/* 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,21 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <stdint.h>
@@ -26,26 +27,21 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "common/framebuffer.h"
#define IS_LG_RENDERER_VALID(x) \
((x)->get_name && \
((x)->getName && \
(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)->onRestart && \
(x)->onResize && \
(x)->onMouseShape && \
(x)->onMouseEvent && \
(x)->renderStartup && \
(x)->needsRender && \
(x)->render)
typedef struct LG_RendererParams
{
// TTF_Font * font;
// TTF_Font * alertFont;
bool quickSplash;
bool quickSplash;
}
LG_RendererParams;
@@ -98,49 +94,82 @@ 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 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, const int x,
const int y);
/* 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);
/* returns if the render method must be called even if nothing has changed.
* Context: renderThread */
bool (*needsRender)(LG_Renderer * renderer);
/* called to render the scene
* Context: renderThread */
bool (*render)(LG_Renderer * renderer, LG_RendererRotate rotate,
const bool newFrame, const bool invalidateWindow,
void (*preSwap)(void * udata), void * udata);
}
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,21 +1,22 @@
/*
KVMGFX Client - A KVM Client for VGA Passthrough
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdbool.h>
struct ll;

View File

@@ -0,0 +1,35 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_LG_OVERLAY_UTILS_
#define _H_LG_OVERLAY_UTILS_
#include <stdbool.h>
#include "common/types.h"
typedef struct ImVec2 ImVec2;
void overlayGetImGuiRect(struct Rect * rect);
ImVec2 * overlayGetScreenSize(void);
void overlayTextURL(const char * url, const char * text);
void overlayTextMaybeURL(const char * text, bool wrapped);
#endif

View File

@@ -1,21 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_LG_UTIL_
#define _H_LG_UTIL_
@@ -24,6 +25,12 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdbool.h>
#include "common/types.h"
#define min(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; })
#define max(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; })
#define UPCAST(type, x) \
(type *)((uintptr_t)(x) - offsetof(type, base))
// reads the specified file into a new buffer
// the callee must free the buffer
bool util_fileGetContents(const char * filename, char ** buffer, size_t * length);
@@ -32,5 +39,17 @@ void util_cursorToInt(double ex, double ey, int *x, int *y);
bool util_guestCurToLocal(struct DoublePoint *local);
void util_localCurToGuest(struct DoublePoint *guest);
void util_rotatePoint(struct DoublePoint *point);
bool util_hasGLExt(const char * exts, const char * ext);
static inline double util_clamp(double x, double min, double max)
{
if (x < min) return min;
if (x > max) return 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,66 +1,122 @@
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
pkg_check_modules(RENDERER_EGL REQUIRED IMPORTED_TARGET
egl
gl
)
pkg_check_modules(RENDERER_EGL_OPT_PKGCONFIG
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
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/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/damage.vert
shader/damage.frag
shader/splash_bg.vert
shader/splash_bg.frag
shader/splash_logo.vert
shader/splash_logo.frag
shader/basic.vert
shader/ffx_cas.frag
shader/ffx_fsr1_easu.frag
shader/ffx_fsr1_rcas.frag
shader/downscale.frag
shader/downscale_lanczos2.frag
shader/downscale_linear.frag
)
make_defines(
"${CMAKE_CURRENT_SOURCE_DIR}/shader/desktop_rgb.frag"
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
)
add_library(renderer_EGL STATIC
egl.c
egldebug.c
shader.c
texture_util.c
texture.c
texture_buffer.c
texture_framebuffer.c
texture_dmabuf.c
model.c
desktop.c
desktop_rects.c
cursor.c
fps.c
help.c
draw.c
splash.c
alert.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}
PkgConfig::RENDERER_EGL
lg_common
fonts
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}
)

View File

@@ -1,219 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
cahe 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_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,33 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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 "interface/font.h"
typedef struct EGL_Alert EGL_Alert;
bool egl_alert_init(EGL_Alert ** alert, const LG_Font * font, LG_FontObj fontObj);
void egl_alert_free(EGL_Alert ** alert);
void egl_alert_set_color(EGL_Alert * alert, const uint32_t color);
void egl_alert_set_text (EGL_Alert * alert, const char * str);
void egl_alert_render (EGL_Alert * alert, const float scaleX, const float scaleY);

View File

@@ -1,21 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
cahe 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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "cursor.h"
#include "common/debug.h"
@@ -25,7 +26,9 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "texture.h"
#include "shader.h"
#include "model.h"
#include "util.h"
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>
@@ -43,6 +46,16 @@ struct CursorTex
GLuint uCBMode;
};
struct CursorPos
{
float x, y;
};
struct CursorSize
{
float w, h;
};
struct EGL_Cursor
{
LG_Lock lock;
@@ -56,103 +69,111 @@ struct EGL_Cursor
// cursor state
bool visible;
float x, y, w, h;
LG_RendererRotate rotate;
int cbMode;
_Atomic(struct CursorPos) pos;
_Atomic(struct CursorSize) size;
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, false))
{
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->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)
{
if (mono)
{
glUniform4f(t->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h / 2);
glUniform4f(t->uMousePos, x, y, w, h / 2);
glUniform1i(t->uRotate , cursor->rotate);
glUniform1i(t->uCBMode , cursor->cbMode);
}
else
{
glUniform4f(t->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h);
glUniform4f(t->uMousePos, x, y, w, h);
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 CursorSize size = { .w = 0, .h = 0 };
atomic_init(&(*cursor)->pos, pos);
atomic_init(&(*cursor)->size, size);
return true;
}
void egl_cursor_free(EGL_Cursor ** cursor)
void egl_cursorFree(EGL_Cursor ** cursor)
{
if (!*cursor)
return;
@@ -161,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);
@@ -185,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");
@@ -202,23 +223,24 @@ 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_cursorSetState(EGL_Cursor * cursor, const bool visible, const float x, const float y)
{
cursor->visible = visible;
cursor->x = x;
cursor->y = y;
struct CursorPos pos = { .x = x, .y = y};
atomic_store(&cursor->pos, pos);
}
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)
{
@@ -234,9 +256,9 @@ void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate)
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);
egl_modelSetTexture(cursor->model, cursor->norm.texture);
break;
}
@@ -258,10 +280,10 @@ void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate)
xor[y * cursor->width + 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, cursor->width * 4);
egl_textureSetup (cursor->mono.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4);
egl_textureUpdate(cursor->norm.texture, (uint8_t *)and);
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor);
break;
}
}
@@ -270,42 +292,88 @@ void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate)
cursor->rotate = rotate;
struct CursorPos pos = atomic_load(&cursor->pos);
struct CursorSize size = atomic_load(&cursor->size);
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);
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);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
egl_model_set_texture(cursor->model, cursor->mono.texture);
egl_model_render(cursor->model);
egl_modelSetTexture(cursor->model, cursor->mono.texture);
egl_modelRender(cursor->model);
break;
}
case LG_CURSOR_COLOR:
{
egl_shader_use(cursor->norm.shader);
egl_cursor_tex_uniforms(cursor, &cursor->norm, false);
egl_shaderUse(cursor->norm.shader);
setCursorTexUniforms(cursor, &cursor->norm, false, pos.x, pos.y, size.w, size.h);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
egl_model_render(cursor->model);
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->mono.shader);
setCursorTexUniforms(cursor, &cursor->mono, false, pos.x, pos.y, size.w, size.h);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
egl_model_render(cursor->model);
egl_modelRender(cursor->model);
break;
}
}
glDisable(GL_BLEND);
return state;
}

View File

@@ -1,34 +1,41 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <stdbool.h>
#include "egl.h"
#include "interface/renderer.h"
typedef struct EGL_Cursor EGL_Cursor;
bool egl_cursor_init(EGL_Cursor ** cursor);
void egl_cursor_free(EGL_Cursor ** cursor);
struct CursorState {
bool visible;
struct Rect rect;
};
bool egl_cursor_set_shape(
bool egl_cursorInit(EGL_Cursor ** cursor);
void egl_cursorFree(EGL_Cursor ** cursor);
bool egl_cursorSetShape(
EGL_Cursor * cursor,
const LG_RendererCursor type,
const int width,
@@ -36,9 +43,10 @@ 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,
void egl_cursorSetState(EGL_Cursor * cursor, const bool visible,
const float x, const float y);
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-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 "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-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
*/
#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,31 +1,34 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
cahe 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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "desktop.h"
#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>
@@ -33,32 +36,38 @@ Place, Suite 330, Boston, MA 02111-1307 USA
// these headers are auto generated by cmake
#include "desktop.vert.h"
#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 uNearest;
GLint uNV, uNVGain;
GLint uScaleAlgo;
GLint uNVGain;
GLint uCBMode;
};
struct EGL_Desktop
{
EGL * egl;
EGLDisplay * display;
EGL_Texture * texture;
struct DesktopShader * shader; // the active shader
EGL_Model * model;
GLuint sampler;
struct DesktopShader shader;
EGL_DesktopRects * mesh;
CountedBuffer * matrix;
// internals
int width, height;
LG_RendererRotate rotate;
// shader instances
struct DesktopShader shader_generic;
// scale algorithm
int scaleAlgo;
// night vision
int nvMax;
@@ -66,58 +75,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 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->uNearest = egl_shader_get_uniform_location(shader->shader, "nearest" );
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, true))
{
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))
{
@@ -125,25 +141,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_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->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)
@@ -152,44 +184,95 @@ 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);
}
void egl_desktop_free(EGL_Desktop ** desktop)
bool egl_desktopScaleValidate(struct Option * opt, const char ** error)
{
if (opt->value.x_int >= 0 && opt->value.x_int < EGL_SCALE_MAX)
return true;
*error = "Invalid scale algorithm number";
return false;
}
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_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 (igSelectableBool(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:
@@ -200,85 +283,177 @@ bool egl_desktop_setup(EGL_Desktop * desktop, const LG_RendererFormat format, bo
desktop->width = format.width;
desktop->height = format.height;
if (!egl_texture_setup(
if (!egl_textureSetup(
desktop->texture,
pixFmt,
format.width,
format.height,
format.pitch,
true, // streaming texture
useDMA
format.pitch
))
{
DEBUG_ERROR("Failed to setup the desktop texture");
return false;
}
glGenSamplers(1, &desktop->sampler);
glSamplerParameteri(desktop->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(desktop->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(desktop->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glSamplerParameteri(desktop->sampler, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
return true;
}
bool egl_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, true))
{
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)
{
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(desktop->texture)) != EGL_TEX_STATUS_OK)
{
if (status != EGL_TEX_STATUS_NOTREADY)
DEBUG_ERROR("Failed to process the desktop texture");
}
return true;
}
int scaleAlgo = EGL_SCALE_NEAREST;
bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y,
const float scaleX, const float scaleY, const bool nearest,
LG_RendererRotate rotate)
{
if (!desktop->shader)
return false;
egl_desktopRectsMatrix((float *)desktop->matrix->data,
desktop->width, desktop->height, x, y, scaleX, scaleY, rotate);
egl_desktopRectsUpdate(desktop->mesh, rects, desktop->width, desktop->height);
bool useNearest = nearest;
if (!nearest)
if (atomic_exchange(&desktop->processFrame, false) ||
egl_postProcessConfigModified(desktop->pp))
egl_postProcessRun(desktop->pp, desktop->texture, desktop->mesh,
desktop->width, desktop->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, desktop->sampler);
if (finalSizeX > desktop->width || finalSizeY > desktop->height)
scaleType = EGL_DESKTOP_DOWNSCALE;
switch (desktop->scaleAlgo)
{
case EGL_SCALE_AUTO:
switch (scaleType)
{
case EGL_DESKTOP_UPSCALE:
scaleAlgo = EGL_SCALE_NEAREST;
break;
case EGL_DESKTOP_NOSCALE:
case EGL_DESKTOP_DOWNSCALE:
scaleAlgo = EGL_SCALE_LINEAR;
break;
}
break;
default:
scaleAlgo = desktop->scaleAlgo;
}
const struct DesktopShader * shader = &desktop->shader;
EGL_Uniform uniforms[] =
{
switch(rotate)
{
case LG_ROTATE_90:
case LG_ROTATE_270:
if (scaleX < 1.0f || scaleY < 1.0f)
useNearest = true;
break;
default:
break;
.type = EGL_UNIFORM_TYPE_1I,
.location = shader->uScaleAlgo,
.i = { scaleAlgo },
},
{
.type = EGL_UNIFORM_TYPE_2F,
.location = shader->uDesktopSize,
.f = { desktop->width, desktop->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 }
}
}
};
const struct DesktopShader * shader = desktop->shader;
egl_shader_use(shader->shader);
glUniform4f(shader->uDesktopPos , x, y, scaleX, scaleY);
glUniform1i(shader->uRotate , rotate);
glUniform1i(shader->uNearest , useNearest ? 1 : 0);
glUniform2f(shader->uDesktopSize, desktop->width, desktop->height);
if (desktop->nvGain)
{
glUniform1i(shader->uNV, 1);
glUniform1f(shader->uNVGain, (float)desktop->nvGain);
}
else
glUniform1i(shader->uNV, 0);
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;
}

View File

@@ -1,35 +1,52 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <stdbool.h>
#include "interface/renderer.h"
#include "egl.h"
#include "desktop_rects.h"
typedef struct EGL_Desktop EGL_Desktop;
bool egl_desktop_init(EGL_Desktop ** desktop, EGLDisplay * display);
void egl_desktop_free(EGL_Desktop ** desktop);
enum EGL_DesktopScaleType
{
EGL_DESKTOP_NOSCALE,
EGL_DESKTOP_UPSCALE,
EGL_DESKTOP_DOWNSCALE,
};
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,
const float scaleX, const float scaleY, const bool nearest,
LG_RendererRotate rotate);
struct Option;
bool egl_desktopScaleValidate(struct Option * opt, const char ** error);
bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop, EGLDisplay * display,
bool useDMA, int maxRects);
void egl_desktopFree(EGL_Desktop ** desktop);
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, const struct DamageRects * rects);

View File

@@ -0,0 +1,275 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "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
{
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);
*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;
}
GLfloat vertices[(!data || data->count < 0 ? 1 : data->count) * 8];
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);
}
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-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
*/
#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,29 +1,31 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "draw.h"
#include <stdlib.h>
#include <math.h>
void egl_draw_torus(EGL_Model * model, unsigned int pts, float x, float y, float inner, float outer)
void egl_drawTorus(EGL_Model * model, unsigned int pts, float x, float y,
float inner, float outer)
{
GLfloat * v = (GLfloat *)malloc(sizeof(GLfloat) * (pts + 1) * 6);
GLfloat * v = malloc(sizeof(*v) * (pts + 1) * 6);
GLfloat * dst = v;
for(unsigned int i = 0; i <= pts; ++i)
@@ -39,13 +41,14 @@ void egl_draw_torus(EGL_Model * model, unsigned int pts, float x, float y, float
*dst = 0.0f; ++dst;
}
egl_model_add_verticies(model, v, NULL, (pts + 1) * 2);
egl_modelAddVerts(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)
void egl_drawTorusArc(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 * v = malloc(sizeof(*v) * (pts + 1) * 6);
GLfloat * dst = v;
for(unsigned int i = 0; i <= pts; ++i)
@@ -61,6 +64,6 @@ void egl_draw_torus_arc(EGL_Model * model, unsigned int pts, float x, float y, f
*dst = 0.0f; ++dst;
}
egl_model_add_verticies(model, v, NULL, (pts + 1) * 2);
egl_modelAddVerts(model, v, NULL, (pts + 1) * 2);
free(v);
}
}

View File

@@ -1,25 +1,29 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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);
void egl_drawTorus(EGL_Model * model, unsigned int pts, float x, float y,
float inner, float outer);
void egl_drawTorusArc(EGL_Model * model, unsigned int pts, float x, float y,
float inner, float outer, float s, float e);

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,24 +1,25 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "egldebug.h"
#include <GL/gl.h>
#include <GLES3/gl3.h>
#include <EGL/egl.h>
const char * egl_getErrorStr(void)
@@ -43,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,29 +1,36 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "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,72 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <stddef.h>
typedef enum EGL_TexType
{
EGL_TEXTYPE_BUFFER,
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-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 "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-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
*/
#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-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 "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-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
*/
#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-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 "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 (igSelectableBool(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-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 "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-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 "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-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
*/
#pragma once
extern EGL_FilterOps egl_filterDownscaleOps;
extern EGL_FilterOps egl_filterFFXCASOps;
extern EGL_FilterOps egl_filterFFXFSR1Ops;

View File

@@ -1,205 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
cahe 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_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

@@ -1,33 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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 "interface/font.h"
typedef struct EGL_FPS EGL_FPS;
bool egl_fps_init(EGL_FPS ** fps, const LG_Font * font, LG_FontObj fontObj);
void egl_fps_free(EGL_FPS ** fps);
void egl_fps_set_display(EGL_FPS * fps, bool display);
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);

View File

@@ -0,0 +1,108 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "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, false))
{
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-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
*/
#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,209 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2021 Guanzhong Chen <quantum2048@gmail.com>
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_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,32 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2021 Guanzhong Chen <quantum2048@gmail.com>
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 "interface/font.h"
typedef struct EGL_Help EGL_Help;
bool egl_help_init(EGL_Help ** help, const LG_Font * font, LG_FontObj fontObj);
void egl_help_free(EGL_Help ** help);
void egl_help_set_text(EGL_Help * help, const char * help_text);
void egl_help_render(EGL_Help * help, const float scaleX, const float scaleY);

View File

@@ -1,21 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "model.h"
#include "shader.h"
@@ -27,7 +28,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
struct EGL_Model
{
@@ -36,8 +36,8 @@ struct EGL_Model
size_t vertexCount;
bool finish;
bool hasBuffer;
GLuint buffer;
GLuint vao;
EGL_Shader * shader;
EGL_Texture * texture;
@@ -52,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;
@@ -82,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[] =
{
@@ -99,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,
@@ -107,16 +118,16 @@ 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));
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);
fl->u = malloc(sizeof(GLfloat) * count * 2);
memcpy(fl->v, verticies, sizeof(GLfloat) * count * 3);
if (uvs)
@@ -129,16 +140,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);
@@ -161,22 +177,24 @@ void egl_model_render(EGL_Model * model)
offset += sizeof(GLfloat) * fl->count * 2;
}
/* 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;
@@ -189,18 +207,17 @@ void egl_model_render(EGL_Model * model)
/* 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);
@@ -211,6 +228,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,21 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
@@ -23,16 +24,17 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#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,645 @@
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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 = ""
},
{ 0 }
};
option_register(options);
for (int i = 0; i < ARRAY_LENGTH(EGL_Filters); ++i)
EGL_Filters[i]->earlyInit();
}
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;
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);
}
closedir(dir);
this->activePreset = -1;
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 (igSelectableBool(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("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("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)
{
igPushStyleColorVec4(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();
igPushIDPtr(filter);
bool draw = igCollapsingHeaderBoolPtr(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(false);
}
}
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-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
*/
#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,21 +1,22 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "shader.h"
#include "common/debug.h"
@@ -29,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;
@@ -78,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)
{
@@ -185,15 +194,280 @@ 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);
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);
@@ -211,7 +485,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,37 +1,113 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
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
*/
/**
* Looking Glass
* Copyright © 2017-2021 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#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,13 +1,14 @@
#version 300 es
precision mediump float;
in highp vec2 uv;
out highp vec4 color;
in vec2 uv;
out vec4 color;
uniform sampler2D sampler1;
void main()
{
highp vec4 tmp = texture(sampler1, uv);
vec4 tmp = texture(sampler1, uv);
if (tmp.rgb == vec3(0.0, 0.0, 0.0))
discard;
color = tmp;

View File

@@ -1,11 +1,13 @@
#version 300 es
precision mediump float;
in highp vec2 uv;
out highp vec4 color;
#include "color_blind.h"
in vec2 uv;
out vec4 color;
uniform sampler2D sampler1;
uniform lowp int rotate;
uniform int cbMode;
void main()
@@ -13,39 +15,5 @@ void main()
color = texture(sampler1, uv);
if (cbMode > 0)
{
highp float L = (17.8824000 * color.r) + (43.516100 * color.g) + (4.11935 * color.b);
highp float M = (03.4556500 * color.r) + (27.155400 * color.g) + (3.86714 * color.b);
highp float S = (00.0299566 * color.r) + (00.184309 * color.g) + (1.46709 * color.b);
highp 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;
}
highp 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);
}
color = cbTransform(color, cbMode);
}

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