Compare commits

...

570 Commits

Author SHA1 Message Date
Geoffrey McRae
76710ef201 [all] updated issue template and readme in preperation for B2 2020-10-08 20:04:52 +11:00
Geoffrey McRae
e20c8a5cc7 [host] dxgi: don't try to get the hotspot of a null cursor 2020-10-06 23:24:01 +11:00
Geoffrey McRae
4f4d2dbf42 [host] dxgi: fix memory leak if an error occurs 2020-10-06 22:32:10 +11:00
Geoffrey McRae
8692e9af80 [client] don't hide the cursor when SPICE is disabled
Fixes #304
2020-08-21 15:40:22 +10:00
Geoffrey McRae
7d2b39058c [client] ensure the cursor is updated when the window looses/gains focus 2020-08-20 16:05:55 +10:00
Geoffrey McRae
6927dbecd2 [client] added new input:mouseRedraw option
This new option, when enabled (the default) enables cursor movements to
trigger frame updates in the client, improving responsiveness at the
cost of increased FPS while the mouse is moving around.
2020-08-20 15:50:33 +10:00
Geoffrey McRae
f9b6dcc986 [client] only resync the timer if we got an early frame
This prevents a slow update (ie, 30ups) from pulling the refresh rate
below the minimum (ie, 60fps).
2020-08-20 15:18:45 +10:00
Geoffrey McRae
5c912e3c27 [client] spice: improve mouse syncronization with the host 2020-08-20 14:52:24 +10:00
Geoffrey McRae
7e362050f7 [all] update KVMFR to provide cursor hotspot information
This commit bumps the KVMFR protocol version as it adds additional
hotspot x & y fields to the KVMFRCursor struct. This corrects the issue
of invalid alignment of the local mouse when the shape has an offset
such as the 'I' beam.
2020-08-20 13:51:01 +10:00
Ash
10fbdeb294 update client/README.md: spice:captureOnStart from #278 2020-08-19 23:08:34 +10:00
camr0
72d70e8322 Update host/README.md: c-host -> host 2020-08-17 11:44:52 +10:00
Geoffrey McRae
c66a339bbc [client] egl: ensure overflow occurs for state value checks 2020-08-15 22:39:10 +10:00
Geoffrey McRae
1c7961daeb [host] dxgi: rework locking and retry logic for lower latency 2020-08-15 20:49:49 +10:00
Geoffrey McRae
cdc3384883 [host] dxgi: improve frame signaling mechanics 2020-08-15 18:16:11 +10:00
Geoffrey McRae
969effedde [host] update information about PsExec now LG can run as a service 2020-08-13 11:41:16 +10:00
Geoffrey McRae
dc4d1d49fa [host] updated the readme with regards to log file location 2020-08-12 22:15:22 +10:00
Geoffrey McRae
4e1f947a09 [host] Windows: fix uninstaller product name 2020-08-12 22:03:10 +10:00
Geoffrey McRae
15d1a74291 [host] Windows: multiple fixes to the installer 2020-08-12 21:50:48 +10:00
TheCakeIsNaOH
7dba6b9b08 [Host] Convert installer to setup service instead of scheduled task 2020-08-12 21:32:15 +10:00
TheCakeIsNaOH
a5ad531004 [Host] Change default install dir "Looking-Glass" to "Looking Glass" 2020-08-12 21:32:15 +10:00
TheCakeIsNaOH
c119b3dcca [Host] Correct installer and shortcut names 2020-08-12 21:32:15 +10:00
TheCakeIsNaOH
e2f2437ef4 [Host] Installer command line options and install location selection add 2020-08-12 21:32:15 +10:00
TheCakeIsNaOH
b2980fea63 [Host] Add instructions on how to build NSIS installer. 2020-08-12 21:32:15 +10:00
TheCakeIsNaOH
2b518690b8 [Host] NSIS script change names from C-Host to Host 2020-08-12 21:32:15 +10:00
TheCakeIsNaOH
92aca75792 [c-host] Add NSIS installer script 2020-08-12 21:32:15 +10:00
Geoffrey McRae
64fdb8b7bb [host] Windows: service (un)install now starts/stops the service
In addition to starting and stopping the service, it now also stops the
LG process if the service started it.
2020-08-12 20:56:02 +10:00
Geoffrey McRae
431ae3fc55 [common] linux: fix issue with infinite timeout events 2020-08-11 19:31:11 +10:00
Geoffrey McRae
380b5df9f9 [host] increase sleep timeout to 100ms 2020-08-11 19:11:17 +10:00
Geoffrey McRae
c7330167cf [host] shutdown capture if there are no subscribers
Fixes #33
2020-08-11 18:30:47 +10:00
Geoffrey McRae
ca02e1aba9 [host] Windows: change "Open Log File" to "Log File Location" 2020-08-11 17:45:00 +10:00
Geoffrey McRae
ca4b1f5592 [host] Windows: don't open the log file, instead show it's location
Now that it's recommended to run LG as the `SYSTEM` user, launching an
application to read the log file is dangerous as it will be launched
with the same access rights (`SYSTEM`). Instead so as Microsoft
recommends and only present a message box with the information.
2020-08-11 17:42:00 +10:00
Geoffrey McRae
0cf1e27709 [host] Windows: run with HIGH priority if started by the service 2020-08-11 17:37:40 +10:00
Geoffrey McRae
045932ce77 [host] send the correct cursor shape on client connection 2020-08-11 17:16:54 +10:00
Geoffrey McRae
bf5481446b [host] Windows: poll more freqently for a stopped LG process 2020-08-11 15:22:29 +10:00
Geoffrey McRae
e3f97e384b [client] rework the start/restart logic to use an enum 2020-08-11 15:14:58 +10:00
Geoffrey McRae
76e119f8ad [client] egl: don't fade the splash when restarting 2020-08-11 14:54:48 +10:00
Geoffrey McRae
bfb12c74fb [client] be quicker at detecting restart and quieter about it 2020-08-11 14:52:22 +10:00
Geoffrey McRae
fa50b7824c [client] fix crash on shutdown while waiting for a restart 2020-08-11 14:45:43 +10:00
Geoffrey McRae
da8b2d0cec [client] egl: properly wait for a new frame on restart 2020-08-11 14:45:08 +10:00
Geoffrey McRae
74649ddb96 [client] gracefully restart if the host application restarts 2020-08-11 14:30:44 +10:00
Geoffrey McRae
4619ddef5d [host] Windows: added missing linker library 2020-08-11 13:15:18 +10:00
Geoffrey McRae
ea74ee6e25 [host] windows: fix crosscompile take 2 2020-08-11 13:11:42 +10:00
Geoffrey McRae
ecd73aa670 [host] windows: fix linux crosscompile 2020-08-11 13:07:23 +10:00
Geoffrey McRae
10d9678b3d [host] Windows: improved service restart detection 2020-08-11 12:47:50 +10:00
Geoffrey McRae
e08d3afdbc [host] Windows: added missing service files 2020-08-11 12:27:04 +10:00
Geoffrey McRae
9a6b598438 [host] Windows: Implemented service to launch LG as the SYSTEM user
Experimental, use at your own peril!

This commit adds the ability for the LG host to install and launch with
Windows as a system service.

To install simply run `looking-glass-host.exe InstallService` or
conversely to uninstall `looking-glass-host.exe UninstallService`.
2020-08-11 12:22:22 +10:00
Geoffrey McRae
d9a80b16f0 [common] properly define _GNU_SOURCE and set the thread names 2020-08-10 16:22:02 +10:00
Geoffrey McRae
90d0cd873d [common] added a sleep to the framebuffer spinlock and a sane timeout 2020-08-10 16:18:08 +10:00
Geoffrey McRae
82e0b7b6ab [doc] readme updated with PsExec information 2020-08-09 20:11:19 +10:00
Geoffrey McRae
2e1b0f2550 [all] update the LGMP submodule 2020-08-09 18:13:43 +10:00
Geoffrey McRae
3302d353cf [client] always use spice mouse host mode
Since we only ever use offset movements as SPICE doesn't properly
support absolute x/y positional information without a virtual tablet
device (which breaks relative mode needed for capture), just always run
in this mode. This fixes an issue when the spice guest tools are
installed and the mouse fails to work when not captured.
2020-08-09 16:17:08 +10:00
Geoffrey McRae
1899d9f1da [client] reset the frame time when we get a frame signal
This stops a duplicate frame rendering bug due to failure to discipline
based on the signal timing.
2020-08-09 15:55:12 +10:00
Geoffrey McRae
fb9b772db0 [client] we are getting the clock anyway, just reset the time 2020-08-09 15:54:45 +10:00
Geoffrey McRae
302b988524 [client] use atomics to track frame counts and avoid extra signals 2020-08-09 15:14:17 +10:00
Geoffrey McRae
19c2fe9b5e Revert "[common] linux: improve event mechanics"
The logic here is wrong, this should be done externally as multiple
waiters will cause issues
2020-08-09 14:44:00 +10:00
Geoffrey McRae
88d25ee98c [common] linux: improve event mechanics 2020-08-09 13:26:55 +10:00
Geoffrey McRae
0f2ecdf5f1 [obs] cosmetic 2020-08-09 12:31:56 +10:00
Geoffrey McRae
3511fb8d59 [obs] microsttuer fix, be sure to always grab the latest frame 2020-08-09 12:29:52 +10:00
Geoffrey McRae
1d6d640b6e [host] dxgi: default to using the acquire lock 2020-08-07 20:31:46 +10:00
Geoffrey McRae
977d7b277d [host] dxgi: boost GPU thread priority if possible 2020-08-07 19:44:00 +10:00
Geoffrey McRae
be7820303f [common] fixed debug formatting across platforms 2020-08-03 15:05:35 +10:00
Geoffrey McRae
43503222c7 [common] framebuffer: fixed incorrect streaming usage 2020-08-03 14:41:57 +10:00
Geoffrey McRae
85b8c12abf [common] adjust framebuffer read/write strategy for better cache usage 2020-08-03 12:33:08 +10:00
Geoffrey McRae
7af053497e [common] unroll the framebuffer write loop and increase the chunk size 2020-08-03 12:24:17 +10:00
Geoffrey McRae
9e3a42cb62 [host] don't stop the timer when restarting capture 2020-08-03 12:04:50 +10:00
Geoffrey McRae
aa32c5ffad [common] framebuffer: added missing header include 2020-08-03 11:58:38 +10:00
Geoffrey McRae
62d1bd1ea2 [common] framebuffer: use stream load instead of plain load 2020-08-03 11:55:38 +10:00
Geoffrey McRae
2329e993ee [common] fixed framebuffer write SIMD code performance 2020-08-03 11:44:24 +10:00
Geoffrey McRae
da655b86c3 [common] improve frambuffer copy to avoid cache pollution (SIMD) 2020-08-03 11:16:30 +10:00
Max Sistemich
c5ff8bd4ce [common] linux: implement timers 2020-07-25 00:38:15 +10:00
Geoffrey McRae
06aee158de [client] egl: make better use of atomics and fix modulus bug 2020-07-24 17:39:16 +10:00
Samuel Bowman
bd42445ea7 [client] add option to capture input on start 2020-07-17 08:39:32 +10:00
Geoffrey McRae
ede96fa486 [client] egl: don't map the texture until it's needed
The texture buffer may still be in use if we try to re-map it
immediately, instead only map when we need it mapped, and unmap
immediately after advancing the offset allowing the render thread to
continue while the unmap operation occurs
2020-05-30 16:50:27 +10:00
Geoffrey McRae
67dec216d2 [host] search the applications local directory for the config 2020-05-30 12:31:26 +10:00
Geoffrey McRae
fcbdf7ba4f [client] egl: fix non-streaming texture updates 2020-05-29 16:54:25 +10:00
Geoffrey McRae
e8c949c1e7 [client] egl: dont re-setup the fps texture on each update 2020-05-29 16:47:21 +10:00
Geoffrey McRae
28c93ef5ac [client] egl: don't unmap/map all buffers for each frame 2020-05-29 15:48:59 +10:00
Geoffrey McRae
d7921c5d5f [client] report the host version on mismatch if possible 2020-05-29 14:24:06 +10:00
Geoffrey McRae
6d296f2b44 [client] stop people running the client as root 2020-05-29 14:18:02 +10:00
Geoffrey McRae
553e2830bb [client/host] share the host version with the client for diagnostics 2020-05-29 14:14:31 +10:00
Geoffrey McRae
667ab981ba [host] send the latest cusror information when a new client connects 2020-05-25 14:37:02 +10:00
Geoffrey McRae
bc7871f630 [c-host] renamed finall to just plain host 2020-05-25 13:42:43 +10:00
Geoffrey McRae
d579705b10 [misc] minor readme update 2020-05-22 22:53:21 +10:00
Geoffrey McRae
94d383a8c1 [obs] remove useless advance operation 2020-05-22 22:51:41 +10:00
Geoffrey McRae
08062e3fc3 [client] check for underflow when checking frame time 2020-05-22 22:02:44 +10:00
Geoffrey McRae
4441427943 [client] implemented better clock drift correction 2020-05-22 20:45:59 +10:00
Geoffrey McRae
f5da432d38 [client] put back the fps correction from drift/skew 2020-05-22 18:39:19 +10:00
Geoffrey McRae
60f665a65c [client] more fps limiter fixes 2020-05-22 18:28:16 +10:00
Geoffrey McRae
9b6174793a [client] revert cusror update render trigger
While it makes the mouse a bit nicer it causes frame skips during cursor
movement.
2020-05-22 18:16:48 +10:00
Geoffrey McRae
dedab38b99 [client] rename fpsLimit to fpsMin 2020-05-22 18:15:17 +10:00
Geoffrey McRae
4580b18b04 [client] fix the fps limiter 2020-05-22 18:06:29 +10:00
Geoffrey McRae
88dad36449 [client] allow mouse movements to trigger render updates
Now EGL is lockless we can allow cursor updates to trigger frame updates
directly.
2020-05-22 18:00:18 +10:00
Geoffrey McRae
075c82b32c [client] egl: fix context binding enabling a lock free implementation 2020-05-22 17:47:19 +10:00
Geoffrey McRae
ae2ffd0a28 [client] drop the default FPS target to 60 now that the fps is dynamic 2020-05-21 14:59:51 +10:00
Geoffrey McRae
26eea64689 [client] remove microstutter warning when using the fps display
This warning was added when it was thought to be the cause of the
microstutters, however this has been disproven with the latest batch of
changes.
2020-05-21 14:16:01 +10:00
Geoffrey McRae
c9ff1e1949 [client] egl: alter warning about low fps as it doesn't apply anymore 2020-05-21 14:09:51 +10:00
Geoffrey McRae
e31f38eadc [client] allow frame updates to be triggered by a timed event
This is a major change to how the LG client performs it's updates. In
the past LG would operate a fixed FPS regardless of incoming update
speed and/or frequency. This change allows LG to dynamically increase
it's FPS in order to better sync with the guest as it's rate changes.
2020-05-21 13:41:59 +10:00
Geoffrey McRae
756b57400b [client] egl: move context init to lock function 2020-05-21 11:55:35 +10:00
Geoffrey McRae
01bfd2e090 [client] egl: make better use of the second thread for streaming 2020-05-21 11:44:56 +10:00
Geoffrey McRae
dc3e89e65c [obs] add delay to fix startup
this delay is needed to allow the host clock to change so we can
validate the session.
2020-05-21 09:37:20 +10:00
Geoffrey McRae
240d0ff263 [client] add short delay to improve initial startup 2020-05-21 09:32:08 +10:00
Geoffrey McRae
3b47a4113f [client/obs] update to use new LGMP init api 2020-05-21 09:28:41 +10:00
Geoffrey McRae
a6d6a49f82 [client] egl: use atomic members instead of locking the entire state 2020-05-21 08:20:30 +10:00
Geoffrey McRae
f8ff3faf78 [obs] improvements to help prevent client timeouts 2020-05-21 07:31:12 +10:00
Geoffrey McRae
d899c26617 [client] egl: add low FPS warning when failing to keep up 2020-05-19 22:42:55 +10:00
Geoffrey McRae
73ba325072 [client] egl: reworked the streaming texture pipeline 2020-05-19 22:03:36 +10:00
Geoffrey McRae
aff19e13c7 [profiler] client: updated to use new lgmp API and path 2020-05-19 11:37:44 +10:00
Geoffrey McRae
007122df43 [all] remove github specific unused config file 2020-05-19 11:19:20 +10:00
Geoffrey McRae
06f8911ee1 [all] project cleanup 2020-05-19 11:06:39 +10:00
Geoffrey McRae
f96f0fecda [client] egl: use proper atomics for pbo counting 2020-05-18 09:06:11 +10:00
Geoffrey McRae
21987cb423 [obs] update to use new LGMP interface 2020-05-17 12:04:41 +10:00
Geoffrey McRae
18cc8d7cab [client] fix host wait logic and print more useful help 2020-05-17 11:54:07 +10:00
Geoffrey McRae
fc0dbd8782 [c-host] add kvmfr version to host output 2020-05-17 11:26:45 +10:00
Geoffrey McRae
b7ca3d7e37 [client] cleanup debug output 2020-05-17 11:25:27 +10:00
Geoffrey McRae
c4bf992c0c [client/host] added enforcement of KVMFR versioning 2020-05-17 11:13:08 +10:00
Geoffrey McRae
dcce288a98 [obs] fix another potential deadlock 2020-04-25 02:26:34 +10:00
Geoffrey McRae
cfd8126e5d [obs] remove debug printf 2020-04-25 02:26:16 +10:00
Geoffrey McRae
7a96642498 [client & host] update the LGMP project to fix timeout issues 2020-04-25 02:25:44 +10:00
Geoffrey McRae
8d5a42c233 [obs] fix potential deadlock 2020-04-24 23:03:40 +10:00
Geoffrey McRae
00a41be413 [obs] use thread to handle frame advance when obs is behind 2020-04-24 21:31:12 +10:00
Geoffrey McRae
fdb9a9cca8 use a timer for the LGMP host instead of a thread 2020-04-24 21:31:12 +10:00
feltcat
e7f088ef52 [client] egl: typo fix in info message
"Multsampling" to "Multisampling"
2020-04-24 19:01:42 +10:00
Geoffrey McRae
243efcd51a [client] fix missing release_key_binds for mouse sensitivity bindings 2020-04-23 18:00:17 +10:00
feltcat
e3cbdd18a0 [client] add quit keybind 2020-04-23 17:57:58 +10:00
Geoffrey McRae
b9cdaf8e19 update PureSpice to fix clipboard bug 2020-04-21 13:17:49 +10:00
Geoffrey McRae
4758caa772 updated PureSpice submodule 2020-04-21 11:36:24 +10:00
Geoffrey McRae
4058522f68 update PureSpice submodule 2020-04-20 09:54:12 +10:00
Geoffrey McRae
80437c564d update PureSpice submodule 2020-04-15 17:55:43 +10:00
Geoffrey McRae
503fc7c312 [spice] updated the submodule to fix a minor shutdown glitch 2020-04-14 16:46:55 +10:00
Geoffrey McRae
f6691a90c0 [client/obs] improve frambuffer_read functions to support copy pitch
Fixes #244
2020-04-14 13:27:07 +10:00
Mikko Rasa
ead09ed110 [client] opengl: render frame if config didn't change 2020-04-14 12:19:59 +10:00
Geoffrey McRae
ac1ecd2e7b [client] update PureSpice submodule to resolve build issue with -O3 2020-04-13 20:07:08 +10:00
Geoffrey McRae
3538e7f6f4 [c-host] dxgi: add more robust error handling on cursor shape failure
Closes #264 - Credit to https://github.com/DataBeaver
2020-04-12 14:43:50 +10:00
Geoffrey McRae
75bc038144 [client] removed accidental debug commit 2020-04-12 13:48:59 +10:00
Geoffrey McRae
7018a3e737 [c-host] dxgi: close the desktop on deinit 2020-04-12 13:46:56 +10:00
Geoffrey McRae
d3836d4548 [c-host] Enable secure desktop capture using SetThreadDesktop
Closes #263 - Credit to https://github.com/DataBeaver for this gem!
2020-04-12 13:35:40 +10:00
Geoffrey McRae
dbd7db7787 [common] fix framebuffer_prepare to use atomic_store 2020-04-12 13:16:55 +10:00
Geoffrey McRae
1222fd40b7 [common] fix FrameBuffer to use atomics correctly
Might Fix #248
2020-04-12 13:14:53 +10:00
Geoffrey McRae
b5f4c639fd [client] provide better mouse tracking when exiting/entering the window 2020-04-07 14:54:38 +10:00
Geoffrey McRae
cddeeff3fc [c-host] LGMP: increase the timeouts 2020-04-07 14:54:38 +10:00
fishery
94a35a6558 [client] fix buffer overflow in opengl_options
buffer overflow loading opengl_options
2020-04-02 00:17:19 +11:00
Geoffrey McRae
b953b2b807 [module] added missing kvmfr.h, fixes #253 2020-03-22 09:20:09 +11:00
Geoffrey McRae
367a73d033 [spice] updated the submodule, fixes #249 2020-03-22 09:12:54 +11:00
Geoffrey McRae
1ac13658e1 [module] fix compilation for linux 5.6 2020-03-11 00:28:44 +11:00
chrsm
2440272307 [common] fix build for newer versions of binutils
binutils has changed several macros. Added ifdef to allow building with
stable and bleeding edge versions.

refs #232
2020-02-25 22:31:55 +11:00
Geoffrey McRae
582ed6b5d1 [c-host] dxgi: dont send null movements when only the visibility changed 2020-02-12 18:40:28 +11:00
Geoffrey McRae
e2adbaa5c1 [c-host] dxgi: fix failure to provide cursor visibility information 2020-02-12 18:36:11 +11:00
Geoffrey McRae
4acf800ace [client] updated the PureSpice submodule 2020-02-03 17:31:56 +11:00
Geoffrey McRae
7cc305c2f5 [client] updated spice submodule to fix shutdown bug 2020-02-01 14:31:46 +11:00
Geoffrey McRae
95f5962186 [client] update to properly disconnect from spice 2020-02-01 14:24:23 +11:00
Geoffrey McRae
f4c2996a3a [repos] updated submodules 2020-02-01 14:22:07 +11:00
Geoffrey McRae
10c4037694 [doc] added new github sponsorship option to README.md 2020-02-01 11:44:51 +11:00
Geoffrey McRae
52be6deccf [github] updated sponsorship for github sponsors 2020-02-01 11:43:42 +11:00
feltcat
0d736efc88 Fixed typo in issue template 2020-02-01 11:37:11 +11:00
Geoffrey McRae
9cc21c2a62 [all] updated the main README.md file 2020-01-31 21:43:58 +11:00
Geoffrey McRae
0b7f422d5d [client] moved spice into a seperate repository 2020-01-31 21:39:57 +11:00
Geoffrey McRae
0ca760fad6 [c-host] revert locking series, this needs more thought
Revert "[c-host] make pointer post function thread safe"

This reverts commit 3feed7ba07.

Revert "[c-hots] fix incorrect unlock timing"

This reverts commit 57f1f2d1fe.

Revert "[c-host] increase the queue length and remove debug output"

This reverts commit b0f9f15a60.

Revert "[c-host] dxgi: use low level mouse input by default"

This reverts commit dc4d820666.

Revert "[c-host] nvfbc: no need for a cursor position event with LGMP"

This reverts commit e30b54ddb2.
2020-01-29 23:23:31 +11:00
Geoffrey McRae
3feed7ba07 [c-host] make pointer post function thread safe 2020-01-29 22:58:59 +11:00
Geoffrey McRae
57f1f2d1fe [c-hots] fix incorrect unlock timing 2020-01-29 22:12:59 +11:00
Geoffrey McRae
b0f9f15a60 [c-host] increase the queue length and remove debug output 2020-01-29 22:05:47 +11:00
Geoffrey McRae
dc4d820666 [c-host] dxgi: use low level mouse input by default
This is known to prevent cursor updates on the secure desktop
(UAC) but DXGI DD does not provide us with the real mouse
coordinates when applications have 'captured' the cursor.
2020-01-29 21:58:39 +11:00
Geoffrey McRae
e30b54ddb2 [c-host] nvfbc: no need for a cursor position event with LGMP 2020-01-29 21:58:00 +11:00
Geoffrey McRae
939bb07603 [all] cleanup use of atomic locking and switch to C11 stdatomic 2020-01-29 19:06:09 +11:00
Geoffrey McRae
cc2c49644d [spice] reworked to avoid locking requirements on the input channel
POSIX `send` is thread safe, to take advantage of this the code has been
changed to construct a contiguous buffer and perform the send in a
single operation preventing any risk of a race condition.

Only the main channel still requires an interlock as the VD agent
requires multiple sends to transmit a full buffer.
2020-01-29 18:53:33 +11:00
Geoffrey McRae
29f221d547 [spice] improve connection code to use a single buffer 2020-01-29 16:52:23 +11:00
Geoffrey McRae
2e32ceb6e0 [LGMP] update the submodule 2020-01-29 14:03:12 +11:00
Geoffrey McRae
2cbc9b6426 [kvmfr] stop the module building the test application by default 2020-01-29 14:01:52 +11:00
Geoffrey McRae
3f3a8f898d [common] 1e9 is a floating point notation 2020-01-29 14:01:14 +11:00
Geoffrey McRae
6e62ea5364 [common] fix building on mingw for linux 2020-01-28 05:10:38 +11:00
Geoffrey McRae
5d39b6160a [lgmp] update module again with actual fix! 2020-01-28 04:28:55 +11:00
Geoffrey McRae
a9e8187f28 [LGMP] updated the module again 2020-01-28 03:58:59 +11:00
Geoffrey McRae
228f5bfdff [c-host] don't hog CPU resources if queues are full 2020-01-28 03:58:28 +11:00
Geoffrey McRae
29e5f193f0 [common] added timestamps to log output 2020-01-28 03:57:19 +11:00
Geoffrey McRae
8f8ebab712 [c-host] respect the full queue 2020-01-28 01:04:46 +11:00
Geoffrey McRae
418149c9a6 [LGMP] updated the submodule with the fixed locking mechanics 2020-01-27 22:12:20 +11:00
Geoffrey McRae
e30e5da75a [c-host] nvfbc: correct frame change check logic 2020-01-27 16:16:43 +11:00
Geoffrey McRae
fc6681306e [c-host] nvfbc: do not send frames that have not changed 2020-01-27 16:01:31 +11:00
Geoffrey McRae
60acc3ef44 [obs] update the LGMP module to fix low frame rate capture issue 2020-01-27 15:05:25 +11:00
Geoffrey McRae
9958e557b7 [c-host] increase delay as lgmp clients can now remove empty messages 2020-01-27 14:48:20 +11:00
Geoffrey McRae
8dbc1daaf4 [common] linux: signal should signal all listeners 2020-01-27 14:33:57 +11:00
Geoffrey McRae
5a23d048bd [LGMP] submodule update again 2020-01-27 13:14:16 +11:00
Geoffrey McRae
b658ea6459 [LGMP] another submodule update 2020-01-27 13:06:46 +11:00
Geoffrey McRae
dc91a0d807 [LGMP] updated the submodule again 2020-01-27 12:49:36 +11:00
Geoffrey McRae
c1fd6552d2 [client] fix hang when trying to terminate an unconnected client 2020-01-27 12:25:47 +11:00
Geoffrey McRae
6b2e78acdf [all] updated LGMP module, a rebuild of host and client IS required 2020-01-27 11:29:54 +11:00
Geoffrey McRae
7b11ab04c6 [client] always update the renderer cursor state 2020-01-27 02:11:21 +11:00
Geoffrey McRae
bced5f95ff [all] make cursor visible a flag and send it seperate to position 2020-01-27 02:07:32 +11:00
Geoffrey McRae
9d7f773b9c [c-host] decrease LGMP polling interval to a sane value and comment 2020-01-27 01:55:14 +11:00
Geoffrey McRae
fea0a98b9e [c-host] dxgi: invisible cursors do not have position information 2020-01-27 01:47:40 +11:00
Geoffrey McRae
8745858bcf [lgmp] updated the lgmp submodule 2020-01-27 01:25:49 +11:00
Geoffrey McRae
2885c73a9a [c-host] increased the polling delay as there is a better fix for LGMP 2020-01-27 01:22:40 +11:00
Geoffrey McRae
893b23f3cd [c-host] increase lgmp host process resolution 2020-01-26 18:50:07 +11:00
Geoffrey McRae
d860d6b891 [c-host] win: fixed improper signal detection in event code 2020-01-26 17:49:04 +11:00
Geoffrey McRae
dcc9625803 [client] updated to use new cursor state flags 2020-01-26 17:30:16 +11:00
Geoffrey McRae
b7e4426002 [c-host] inform the client if we have positional cursor information 2020-01-26 17:25:14 +11:00
Geoffrey McRae
b4cf8f76c8 [c-host] mousehook: ignore repeated hook events 2020-01-26 16:23:35 +11:00
Geoffrey McRae
687eddcc63 [kvmfr] fixed incorrect buffer size calculation 2020-01-24 17:07:09 +11:00
Geoffrey McRae
9d6d137b50 [c-host] fix bounds checking on frame index 2020-01-24 16:31:03 +11:00
Geoffrey McRae
a75b95694b [c-host] actually use the 2nd LGMP frame 2020-01-24 16:06:38 +11:00
Geoffrey McRae
c7aa8871e4 [common] fixed improper comment parsing, fixes #233 2020-01-21 16:35:21 +11:00
Geoffrey McRae
f9d919bdbb [client] increase the lgmp queue timeouts 2020-01-20 14:18:45 +11:00
Geoffrey McRae
4d0f019ad5 [spice] prepare spice for external usage 2020-01-19 06:51:21 +11:00
Geoffrey McRae
e6154e685f [client] cosmetics 2020-01-19 06:49:56 +11:00
Geoffrey McRae
2c59b5f557 [client] added checking for invalid arguments to custom string options 2020-01-19 06:48:20 +11:00
Geoffrey McRae
4746c89227 [all] moved time and locking methods to the common library 2020-01-17 14:35:08 +11:00
Geoffrey McRae
278d851c7c [egl] added fallback for platforms not supporting eglGetPlatformDisplay 2020-01-17 11:50:00 +11:00
Geoffrey McRae
406e22a681 [client] override new behaviour in SDL 2.0.15 and disable xinput2
xinput2 is used to get touch interface events with the side effect of
consuming MotionNotify events which we use because of SDL2's inability
to correctly track the window size. Since we are not that intertested in
touch for our usecase, we just turn the events off again.
2020-01-13 22:21:12 +11:00
Geoffrey McRae
17e05c6fd5 [all] expose the FrameBuffer struct for correct sizeof calculations 2020-01-13 19:30:49 +11:00
Geoffrey McRae
9846762991 [all] align the frame data to the page boundary 2020-01-13 19:17:09 +11:00
Geoffrey McRae
17df1ebc6b [c-host] adjust maximum size to account for alignment 2020-01-13 16:06:53 +11:00
Geoffrey McRae
ad8a8b52be [c-host] ensure frames are page aligned 2020-01-13 15:52:54 +11:00
Geoffrey McRae
0d29527758 [common] added agnostic function sysinfo_getPageSize 2020-01-13 15:52:31 +11:00
Geoffrey McRae
7a96c9fe24 [kvmfr] don't recreate the pages for each map 2020-01-13 15:42:45 +11:00
Geoffrey McRae
c71e5c63ca [lgmp] updated the module to bring in support for aligned allocations 2020-01-13 15:19:25 +11:00
Geoffrey McRae
f82a164d75 [client] enable SDL_SYSWMEVENT on X11 to work around SDL2 bugs 2020-01-13 14:03:26 +11:00
Geoffrey McRae
5d4e9b1ead [kvmfr] bump the version in dkms.conf 2020-01-13 13:45:05 +11:00
Geoffrey McRae
788f885759 [kvmfr] added the ability to obtain a dmabuf of the ivshmem memory
This is to enable the ability to use dri3 to create dmabuf backed
pixmaps directly.
2020-01-13 13:39:24 +11:00
Geoffrey McRae
6aeafc6651 [common] add comment support to the ini parser 2020-01-12 22:44:41 +11:00
Geoffrey McRae
1aadf91901 [common] revert /dev/uio0 naming change behaviour 2020-01-12 22:37:10 +11:00
Geoffrey McRae
7de030bb69 [c-host] nvfbc: free event on deinit 2020-01-12 18:09:11 +11:00
Geoffrey McRae
b5d91ccc21 [c-host] nvfbc: fixed invalid nvfbc init 2020-01-11 22:28:52 +11:00
Geoffrey McRae
0eafa7de5d [c-host] update NvFBC to use new capture interface 2020-01-11 21:51:59 +11:00
Geoffrey McRae
e554635e48 [spice] turn on TCP_QUICKACK
https://assets.extrahop.com/whitepapers/TCP-Optimization-Guide-by-ExtraHop.pdf
2020-01-11 16:03:28 +11:00
Geoffrey McRae
5e915dd1ff [client] don't send mouse click events when out of view 2020-01-11 13:11:12 +11:00
Geoffrey McRae
13f55011c0 [client] don't draw the cursor if it leaves the frame 2020-01-11 12:56:46 +11:00
Geoffrey McRae
05dc713dac [client] more cursor tweaks for better integration with the WM 2020-01-11 06:03:16 +11:00
Geoffrey McRae
80f3c7934a [client] more cursor tweaks and some cleanup 2020-01-11 05:22:12 +11:00
Geoffrey McRae
1341bf8fbd [client] fix mouse acceleration when in capture mode
SDL2 really doesn't do this well, instead I have implemented our own
capture method that allows us to maintain better client/server cursor
sync.
2020-01-11 04:53:46 +11:00
Geoffrey McRae
5b163063c3 [client] improved sync with guest cursor position 2020-01-11 03:41:44 +11:00
Geoffrey McRae
c2a15ad89d [c-host] updated to use new LGMP API to increase the timeout 2020-01-10 20:04:46 +11:00
Geoffrey McRae
c92312a6c6 [obs] implemented intial OBS Looking Glass Client plugin
Yes, it works! but no cursor support yet
2020-01-10 18:14:08 +11:00
Geoffrey McRae
3253e7fd10 [all] updated LGMP submodule 2020-01-10 18:12:42 +11:00
Geoffrey McRae
e5178793b3 [client] don't fail on invalid magic at startup 2020-01-10 18:07:18 +11:00
Geoffrey McRae
bec4f83778 [profiler] updated to use LGMP 2020-01-10 18:04:22 +11:00
Geoffrey McRae
22f04a926f [common] numerious bad usage bug fixes 2020-01-10 18:04:22 +11:00
Geoffrey McRae
76fa390e3d [c-host] increase the pointer queue length 2020-01-10 11:40:56 +11:00
Geoffrey McRae
1ef406bbaf [lgmp] updated submodule 2020-01-10 11:19:34 +11:00
Geoffrey McRae
0aa8711796 [lgmp] updated submodule 2020-01-10 11:04:16 +11:00
Geoffrey McRae
bea7c94cae [client/c-host] updated to use new LGMP naming conventions 2020-01-10 11:01:35 +11:00
Geoffrey McRae
e7239c53fd [c-host] cleanup dxgi cursor code a bit 2020-01-09 21:20:01 +11:00
Geoffrey McRae
6f551c770c [client] handle pointer visibility properly 2020-01-09 21:18:35 +11:00
Geoffrey McRae
2d755a45e0 [client] added support for LGMP 2020-01-09 20:32:42 +11:00
Geoffrey McRae
7a98a886b6 [c-host] use the correct buffer for the cursor shape 2020-01-09 20:27:55 +11:00
Geoffrey McRae
b0fb7177bb [c-host] improved intial connection sync 2020-01-09 19:49:47 +11:00
Geoffrey McRae
73e8bc41cd [c-host] don't overflow the pointerMemory array 2020-01-09 16:15:04 +11:00
Geoffrey McRae
0b8f1a18b2 [LGMP] start of c-host conversion to use LGMP 2020-01-09 15:42:32 +11:00
Geoffrey McRae
8caa220ad5 [common] link setupapi for ivshmem windows implementation 2020-01-06 20:59:34 +11:00
Geoffrey McRae
b8203bec53 [common] properly detect all versions of Windows 8 2020-01-06 20:55:21 +11:00
Geoffrey McRae
5db4c32035 [c-host] dont use DX12 feature levels on Windows8
Fixes #218
2020-01-06 20:53:15 +11:00
Geoffrey McRae
9282ed19b2 [client] check for clock drift and correct for it
Fixes #224
2020-01-06 20:38:01 +11:00
Geoffrey McRae
45ee79014d [common] added back support for shared memory files 2020-01-06 00:20:30 +11:00
Geoffrey McRae
0dc0e6490c [c-host] dxgi: check for failure of getDesc1 2020-01-03 17:29:07 +11:00
Geoffrey McRae
127113a59b [client] fixed strange resize effect due to loss of precision 2020-01-03 17:23:48 +11:00
Geoffrey McRae
49bf115c84 [client] fix issue with windowmanager forcing the window size (i3wm) 2020-01-03 16:51:24 +11:00
Geoffrey McRae
2196516e2b [client] added new win:forceAspect option
Fixes #225
2020-01-03 15:53:44 +11:00
Geoffrey McRae
899dbff7e9 [client] use the event data instead of calling SDL_GetWindowSize 2020-01-03 15:26:07 +11:00
Geoffrey McRae
4345d94d68 [client] update client to use the common ivshmem* methods 2020-01-03 15:17:14 +11:00
Geoffrey McRae
074af5d16c [c-host] init platform app struct 2020-01-03 14:56:13 +11:00
Geoffrey McRae
89d6ea0b5d [common] move ivshmem code into the common library 2020-01-03 14:53:56 +11:00
Geoffrey McRae
c5baf212c8 [client] switch from SDL_Cond to LGEvent 2020-01-03 00:09:07 +11:00
Geoffrey McRae
ba31c78412 [client] switch from SDL_Thread to lgThread 2020-01-02 23:59:06 +11:00
Geoffrey McRae
1c1d2a0568 [common] moved linux agnostic code into the common library 2020-01-02 23:34:35 +11:00
Geoffrey McRae
0c6ff6822d [common/c-host] move agnostic code into common library 2020-01-02 22:21:42 +11:00
Jonathan (JJRcop) Rubenstein
491ffc3576 Fix client not building on void linux
Thanks to

SharkWipf#8539,
Aiber#4888,
and Hadet#6969 on the VFIO discord
2019-12-28 00:07:39 +11:00
Geoffrey McRae
da5ebee3f7 [c-host] fix #220, invalid handle provided to WaitForObjects 2019-12-19 13:38:05 +11:00
Rikard Falkeborn
6530ca62da [client] fix return value in spice_read_nl error path
Returning -1 from a function with bool as return argument is the same as
returning true. If the channel is not connected, return false instead to
indicate the error.
2019-12-18 08:55:27 +11:00
Geoffrey McRae
0bd19cfd38 [c-host] dxgi: fix segfault with maxTextures=1 on client reconnect 2019-12-17 20:56:14 +11:00
Geoffrey McRae
8ada29e25f [c-host] nvfbc: fix build attempt 2 :) 2019-12-17 16:42:48 +11:00
Geoffrey McRae
3b5c1bd09c [c-host] nvfbc: fix failure to build due to new event interface 2019-12-17 16:41:02 +11:00
Geoffrey McRae
c82a5e0523 [c-host] dxgi: futher event improvements 2019-12-17 16:36:43 +11:00
Geoffrey McRae
9c5f9906fa [c-host] add spinlock support to events and alter dxgi to use them 2019-12-17 14:59:58 +11:00
Geoffrey McRae
db2f5b85a9 [c-host] dxgi: added new useAcquireLock option for quirked GPUs 2019-12-17 13:45:08 +11:00
Geoffrey McRae
547598c61c [common] locked section macro should use it's argument 2019-12-16 15:47:23 +11:00
Geoffrey McRae
711fbc549a [c-host] dxgi: interlock so we can map outside of the capture thread 2019-12-16 15:18:26 +11:00
Geoffrey McRae
f85c017184 [c-host] DXGI profiled and tuned again :) 2019-12-15 16:21:21 +11:00
Geoffrey McRae
85d46ed2b0 [profile] added a tool to help profile the host capture perf 2019-12-14 16:20:17 +11:00
Geoffrey McRae
2d9f578719 [c-host] windows: don't attach to the debuggers console 2019-12-13 23:33:11 +11:00
Geoffrey McRae
e75f3a7278 [c-host] windows: fix --help output in command prompt 2019-12-13 23:22:11 +11:00
Geoffrey McRae
26fa5c8860 [c-host] readme: change windows instructions to use MSYS2 2019-12-13 21:55:34 +11:00
Geoffrey McRae
ed5140568a [c-host] readme: added dev setup instructions for Windows 2019-12-13 21:13:17 +11:00
Andrew Sheldon
70110b4a5a [client] Use eglGetPlatformDisplay() to fix surface creation
[Why]
Recent versions of Mesa may have trouble with surface creation, resulting in
errors like:
egl.c:428  | egl_render_startup             | Failed to create EGL surface (eglError: 0x300b)

[How]
Replace eglGetDisplay() with eglGetPlatformDisplay(). Requires EGL 1.5, but should
be supported with any desktop driver released in the past few years.
2019-12-13 00:35:35 +11:00
Geoffrey McRae
a6f23f00b4 [client] opengl: handle configuration failure properly 2019-12-12 23:32:31 +11:00
Geoffrey McRae
30e3a43311 [client] opengl: fixed failure to render full frame 2019-12-12 23:04:58 +11:00
Geoffrey McRae
dce6aaefea [client] fix rare race condition when renderer is not ready 2019-12-10 03:30:04 +11:00
thejavascriptman
4843a278ff respect minimizeOnFocusLoss 2019-11-15 18:13:11 +11:00
Geoffrey McRae
fe7d611fb9 [misc] added sponsorship config for github 2019-10-30 18:40:12 +11:00
Geoffrey McRae
0e7e918e2c [client] cleanup and re-order startup/shutdown code 2019-10-26 12:03:10 +11:00
Geoffrey McRae
7d6e061ade [client] properly shutdown on failure to connect to the spice server 2019-10-26 11:27:05 +11:00
Geoffrey McRae
66891aa536 [client] don't require wayland-egl, fixes #204 2019-10-26 11:23:04 +11:00
Geoffrey McRae
1d7a2ccf82 [c-host] windows: update ivshmem driver header and usage 2019-10-24 19:46:09 +11:00
Geoffrey McRae
e1bfb1234b [common] obey the destination buffer size 2019-10-14 18:08:06 +11:00
Geoffrey McRae
9377fdfc37 [all] bump KVMFR version due to incompatible changes 2019-10-14 17:19:19 +11:00
Geoffrey McRae
5f1d17ba1f [host] cosmetics 2019-10-09 19:52:31 +11:00
Geoffrey McRae
4c0ca1c8e7 [client] fix xor support for masked color cursors
fixes #200
2019-10-09 19:48:42 +11:00
Geoffrey McRae
8ef1aee35c [common] fix bug in framebuffer_read 2019-10-09 14:11:45 +11:00
Geoffrey McRae
4168cc8d78 [all] fix the version 2019-10-09 14:04:36 +11:00
Geoffrey McRae
bca54ab1f6 [client/host] added new asyncronous memory copy
This changes the method of the memory copy from the host application to
the guest. Instead of performing a full copy from the capture device
into shared memory, and then flagging the new frame, we instead set a
write pointer, flag the client that there is a new frame and then copy
in chunks of 1024 bytes until the entire frame is copied. The client
upon seeing the new frame flag begins to poll at high frequency the
write pointer and upon each update copies as much as it can into the
texture.

This should improve latency but also slightly increase CPU usage on the
client due to the high frequency polling.
2019-10-09 13:53:02 +11:00
Geoffrey McRae
6d2c464436 [client] egl: improved streaming texture syncronization 2019-08-30 12:09:05 +10:00
Geoffrey McRae
e93bd7a3bf [client] fix shutdown race condition with the frame thread 2019-08-30 11:54:26 +10:00
Geoffrey McRae
da94075e7b [client] egl: more verbose error on texture egl failures 2019-08-30 11:40:38 +10:00
Geoffrey McRae
69522495de [client] fix invalid shutdown of renderer outside of it's thread 2019-08-30 11:36:28 +10:00
Geoffrey McRae
fce88fc72c [EGL] add debug printf helper 2019-08-30 11:33:43 +10:00
Geoffrey McRae
163a2e5d0a [client] fix failure to build due to broken symlink, fixes #173 2019-07-23 11:06:51 +10:00
Geoffrey McRae
b979752989 [client] added missing include 2019-07-15 18:30:39 +10:00
orcephrye
8ad2d5f949 [client] autodetect monitor refresh rate for fps limit 2019-07-10 05:04:29 +10:00
Rokas Kupstys
745ba66119 Implement option to disable minimizing window on focus loss. Default behavior is not changed - not configuring these options unfocused window is minimized.
* Added config entry win:minimizeOnFocusLoss (default true).
2019-07-09 21:57:47 +10:00
Geoffrey McRae
4cf6dec592 [all] allow disable of backtrace support during build 2019-06-19 09:13:03 +10:00
Geoffrey McRae
d7fa0aeff9 [client] fix typo in SDL_VIDEODRIVER from prior patch, whoops :) 2019-06-19 09:03:15 +10:00
Geoffrey McRae
2def6346e6 [client] don't override SDL_VIDEODRIVER if it is already set 2019-06-19 09:01:28 +10:00
Geoffrey McRae
607539a2af [client] improve streaming texture performance 2019-06-13 08:54:51 +10:00
Geoffrey McRae
6d24dd52d6 [c-host] not all versions of mingw support wcstombs_s
While the _s functions are for security as they avoid exceeding the
supplied buffer, in our case they are not really required as we are
allocating a buffer large enough to store the entire result.

Fixes #171
2019-06-12 15:31:18 +10:00
Omar Pakker
e3343cbd01 Rewrite dkms.conf
1) With the change to the Makefile, this update allows dkms to build and install the module for different kernels.
2) As per dkms documentation, no use of ${dkms_tree}.
3) Removed the use of REMAKE_INITRD as this module is not needed that early in the boot process.
4) Updated version to match what's defined in the module
2019-06-06 13:40:06 +10:00
Omar Pakker
71ffa0a137 Update makefile to allow kernel override 2019-06-06 13:40:06 +10:00
Geoffrey McRae
2b4f8091f9 [client] README.md cosmetics 2019-05-31 16:45:55 +10:00
Geoffrey McRae
113da121e9 [client] updated documentation for new keybinds 2019-05-31 16:44:08 +10:00
Geoffrey McRae
dd7413f973 [client] added keybinds to send Ctrl+Alt+Fn
Fixes #165
2019-05-31 16:39:55 +10:00
Geoffrey McRae
0851fd13e6 [all] made a nicer icon, hopefully just a placeholder for now 2019-05-30 22:21:53 +10:00
Geoffrey McRae
97024041f3 [client] allow the screensaver to run 2019-05-30 20:54:39 +10:00
Geoffrey McRae
22238c3200 [client] fix invalid access on early termination 2019-05-30 20:24:51 +10:00
Geoffrey McRae
780bb248f7 [c-host] dxgi: fix invalid cursor type define 2019-05-28 15:17:11 +10:00
Geoffrey McRae
026bdb00f2 [c-host] take just the ivshmem headers and omit the kvm-guest submodule 2019-05-28 14:51:47 +10:00
Geoffrey McRae
373d4ac932 [host] removed old host application from the project, see c-host 2019-05-28 14:47:09 +10:00
Geoffrey McRae
7d26027752 [c-host] resend the last on client reconnect if a timeout occurs 2019-05-28 14:24:48 +10:00
Geoffrey McRae
3d426ccef8 [all] fix missing cursor when client reconnects 2019-05-28 14:06:15 +10:00
Geoffrey McRae
b31e8e1cee [client] egl: remove accidental commit of debug code 2019-05-27 18:46:05 +10:00
Geoffrey McRae
f0923c4ed7 [client] egl: expose a few new tuneables 2019-05-27 18:42:46 +10:00
Geoffrey McRae
aabf19e63b [client] main: properly shutdown if renderer fails to init 2019-05-27 18:40:36 +10:00
Geoffrey McRae
f4fc1eb5f6 [client] typo in config help text 2019-05-27 18:40:36 +10:00
Geoffrey McRae
5e201a32ca [c-host] dxgi: allow out of order frame mapping 2019-05-27 15:26:58 +10:00
Geoffrey McRae
438e9e0969 [common] option: fixed missing null terminator 2019-05-27 01:56:55 +10:00
Geoffrey McRae
9554e82c47 [common] fix failure to initialize structure 2019-05-27 01:50:38 +10:00
Geoffrey McRae
4cf2c7a350 [client] check for failure to map pbo memory 2019-05-27 01:39:01 +10:00
Geoffrey McRae
664d7dccdb [client] fix the binary path when debugging 2019-05-27 01:38:50 +10:00
Geoffrey McRae
21b02efb4d [c-host] dxgi: don't stall the GPU pipeline to map textures to ram
ID3D11DeviceContext_Map by default will force a CPU sync if the prior call to
CopyResource has not completed, this change defers the mapping and sets the
D3D11_MAP_FLAG_DO_NOT_WAIT when attempting to map the texture allowing the
capture to continue without incurring an expensive CPU/GPU sync.

A new tuneable has also been added

  * dxgi:maxTextures
2019-05-26 23:36:17 +10:00
Jonathan (JJRcop) Rubenstein
d07aa4b29e [client] Add win:maximize to maximize on startup 2019-05-26 20:47:59 +10:00
JJRcop
9f33043d17 [client] Fix typo in example of README.md 2019-05-25 10:25:57 +10:00
Geoffrey McRae
2e6301fca1 [common] fixed issue with building for windows (typo) 2019-05-24 21:39:51 +10:00
Geoffrey McRae
83c5df2c47 [client] main: add + sign to mouse sensitivit alerts 2019-05-24 05:35:16 +10:00
Geoffrey McRae
759b4ef811 [client] fix typo 2019-05-24 05:31:16 +10:00
Geoffrey McRae
437ebf6265 [client] main: new feature to increase/decrease mouse sensitivity 2019-05-24 05:29:38 +10:00
Geoffrey McRae
bffd02b8c7 [client] main: better UX with the escape key combinations 2019-05-23 20:31:01 +10:00
Geoffrey McRae
196b27ee9c [doc] client: document defaults instead of my preconfigured values 2019-05-23 20:26:37 +10:00
Geoffrey McRae
ff08540fd3 [doc] fixed another formatting snafu 2019-05-23 20:20:18 +10:00
Geoffrey McRae
07be380f34 [doc] fix formatting a bit 2019-05-23 20:19:37 +10:00
Geoffrey McRae
76d58deefa [doc] Updated project documentation in leu of Beta 1 release 2019-05-23 20:13:41 +10:00
Geoffrey McRae
dba9764c5e [egl] alert: fix fuzzy font bug and make alerts a little more plesant 2019-05-23 19:58:12 +10:00
Geoffrey McRae
ee5d6c7c3e [module] update instructions and add dkms.conf
Fixes #148
2019-05-23 18:46:27 +10:00
Geoffrey McRae
1492196bbf [client] shutdown application on window close even if ignoreQuit is set
Fixes #138
2019-05-23 18:27:21 +10:00
Geoffrey McRae
9378f69653 [all] corrected NV keybinding information in README.md 2019-05-23 17:00:00 +10:00
Geoffrey McRae
d2d427b533 [client] egl: query maximum multisample support for MSAA context
Based on @rLink234's work in 4ac781b4516678b6c59d9ecf4a61df64a01ec8c1

Fixes #128
2019-05-23 16:56:50 +10:00
Geoffrey McRae
78a6af8dae [common] added new sysinfo unit and multisample query support
Based on @rLink234's work in 4ac781b4516678b6c59d9ecf4a61df64a01ec8c1
2019-05-23 16:54:50 +10:00
Geoffrey McRae
3585e02993 [client] egl: add options for maximum NV level and initial level
Adds options:

 * egl:nvGain
 * egl:nvGainMax

Fixes #153
2019-05-23 16:33:40 +10:00
Geoffrey McRae
5af88ae61e Set theme jekyll-theme-cayman 2019-05-23 16:21:32 +10:00
Geoffrey McRae
f946117dac [all] futher tweaks to the readme.md 2019-05-23 16:15:42 +10:00
Geoffrey McRae
666a6a218f [all] updated the main readme in preperation of beta 1 2019-05-23 16:08:22 +10:00
Geoffrey McRae
1b031582a4 [c-host] nvfbc: expose option to disable cursor decoupling 2019-05-23 15:12:28 +10:00
Geoffrey McRae
afe072adf1 [c-host] nvfbc: print out the SDK version and enable NvFBC 2019-05-23 14:49:38 +10:00
Geoffrey McRae
09d4fea9e2 [c-host] correct NvFBC information in the README.md 2019-05-23 14:31:05 +10:00
Geoffrey McRae
58c3fba6b9 [c-host] just another minor readme update 2019-05-23 13:42:51 +10:00
Geoffrey McRae
773dd7773b [c-host] try to be more compatible with mingw headers 2019-05-22 19:46:18 +10:00
Geoffrey McRae
732ce05866 [c-host] minor readme updates 2019-05-22 18:33:04 +10:00
Geoffrey McRae
108c7d3aaa [c-host] fixed project for cross compliation for Win on Linux
Thanks @fatalis for your guidance on this
2019-05-22 14:59:19 +10:00
Geoffrey McRae
86f4256b5a [client] egl: fix streaming texture re-init crash 2019-05-22 12:19:03 +10:00
Geoffrey McRae
84b2917706 [client] app: new options to reduce CPU usage
This patch increases the default cursor and frame polling interval from
1us to 1000us which for most use cases should be more then fast enough.

It also adds two new configuration options to adjust these should it be
required:

  * app:cursorPollInterval
  * app:framePollInterval
2019-05-22 12:00:06 +10:00
Geoffrey McRae
fc66a4a19c [client] egl: use persistant mapped texture buffers
While it is recommended to use memory barriers when updating a buffer
like we are, since we double buffer it is unlikely we will corrupt a
prior frame, and even if we do since it's just texture data at worst
we might see a tear.
2019-05-22 11:37:27 +10:00
Geoffrey McRae
087387087e [client] fix race condition on initial uniform access 2019-05-22 11:36:47 +10:00
Geoffrey McRae
3f404905d2 [c-host] added tray icon and context menu 2019-05-21 17:52:58 +10:00
Geoffrey McRae
67595d6deb [client] added missing semi-colon (not sure how that happened) 2019-05-21 15:51:45 +10:00
Geoffrey McRae
77f942711a [client] fixed typo in option description 2019-05-21 15:51:14 +10:00
Geoffrey McRae
e3c98ddc35 [client] port all configuration parsing to use the new option helper 2019-05-21 15:03:59 +10:00
Geoffrey McRae
db0d966102 [common] option: add debug errors for invalid options 2019-05-21 14:58:11 +10:00
Geoffrey McRae
a29639fceb [common] option: fix another invalid usage of an unset variable 2019-05-21 13:24:28 +10:00
Geoffrey McRae
0605b7df8c [common] option: allow short options to toggle boolean values 2019-05-21 12:58:53 +10:00
Geoffrey McRae
51ca08719e [common] option: trim whitespace from option names and values 2019-05-21 12:34:41 +10:00
Geoffrey McRae
ce9b94e93d [common] option: fix crash on failure to parse invalid config file 2019-05-21 12:28:13 +10:00
Geoffrey McRae
7cc0f7cb99 [common] option: fix incorrect column header padding 2019-05-21 11:38:40 +10:00
Geoffrey McRae
06c229dfd4 [common] option: fix invalid access of null/invalid options 2019-05-21 11:34:50 +10:00
Geoffrey McRae
2d5f6d65ce [common] option: added shortopt support and pretty help print 2019-05-21 11:31:31 +10:00
Geoffrey McRae
b9841351b4 [common] added stringutils with alloc_sprintf helper 2019-05-21 11:31:19 +10:00
Geoffrey McRae
d9b6d115d1 [common] fix stringlist const free bug 2019-05-21 11:30:05 +10:00
Geoffrey McRae
cc6dd58778 [c-host] windows: fix dxgi option struct syntax 2019-05-17 09:27:04 +10:00
Geoffrey McRae
0ba931cbed [c-host] windows: add log file output option 2019-05-17 09:26:42 +10:00
Geoffrey McRae
a7daeb2a12 [c-host] option: fix memory corruption due to usage of old pointers 2019-05-17 09:25:57 +10:00
Geoffrey McRae
2fe9dc7ca1 [common] track if option set failed and print help if so 2019-05-12 16:51:37 +10:00
Geoffrey McRae
b662128708 [c-host] linux: implemented getValues support for shmDevice option 2019-05-12 16:14:25 +10:00
Geoffrey McRae
e22f33a44b [common] add getValues callback for options 2019-05-12 16:13:50 +10:00
Geoffrey McRae
5d69d2aba9 [common] added new StringList helper module 2019-05-12 16:12:12 +10:00
Geoffrey McRae
0090580a64 [c-host] be compatible with new option ABI 2019-05-11 20:59:31 +10:00
Geoffrey McRae
538a6dc08e [common] rework option API to allow for custom types 2019-05-11 20:58:49 +10:00
Geoffrey McRae
5b199d8f25 [common] make local struct local 2019-05-11 19:07:10 +10:00
Geoffrey McRae
51ddb62126 [c-host] load config from looking-glass-host.ini if available 2019-05-11 18:23:06 +10:00
Geoffrey McRae
785bc33192 [common] added config file loading capability 2019-05-11 18:22:01 +10:00
Geoffrey McRae
522bacb1f0 [c-host] linux: remove extra shm device name validation
This is now validated by the option validator callback
2019-05-11 11:59:26 +10:00
Geoffrey McRae
cf030f6f0c [common] remove "Option" from the option help output 2019-05-11 11:51:29 +10:00
Geoffrey McRae
823164a924 [common] further option help cleanup 2019-05-11 11:50:26 +10:00
Geoffrey McRae
2ddae623b8 [c-host] update to use new option validator and tidy up output 2019-05-11 11:35:42 +10:00
Geoffrey McRae
86c7286aad [common] seperate validator and clean up output 2019-05-11 11:35:17 +10:00
Geoffrey McRae
9886316e07 [c-host] linux: fix shmDevice file size check 2019-05-11 11:23:27 +10:00
Geoffrey McRae
8a3356859c [c-host] implement shmDevice option validator and help 2019-05-11 11:21:18 +10:00
Geoffrey McRae
32d5f1db85 [common] validate all options including defaults 2019-05-11 11:21:00 +10:00
Paul Götzinger
b5975e0f05 [host] [c-host] added support to specify IVSHMEM device
[host] basic IVSHMEM device selecting implemented

Minor fixes for IVSHMEM device scanning

[c-host] added support to specify IVSHMEM device
2019-05-10 22:28:27 +10:00
Geoffrey McRae
53ade56b4e [common] fix option parser memory leak 2019-05-09 23:05:33 +10:00
Geoffrey McRae
5677117c0d [c-host] nvfbc: remove debug line from prior commit 2019-05-09 22:53:02 +10:00
Geoffrey McRae
558ae5dc45 [c-host] dxgi: initialize option to NULL for consistancy 2019-05-09 22:51:12 +10:00
Geoffrey McRae
83f63f4c42 [c-host] dxgi: add the ability to specify the adapter and/or output.
Fixes #132
2019-05-09 22:48:39 +10:00
Geoffrey McRae
247e92937c [common] match the correct option 2019-05-09 22:47:48 +10:00
Geoffrey McRae
63314941f6 [c-host] fix build under windows 2019-05-09 22:13:31 +10:00
Geoffrey McRae
e7345b9711 [c-host] initial agnostic option api and parser 2019-05-09 22:06:58 +10:00
Geoffrey McRae
22f9fa3938 [c-host] windows: fix errors from prior commit 2019-05-09 19:32:19 +10:00
Geoffrey McRae
4617829d41 [c-host] provide a platform agnostic method of passing args to app_main 2019-05-09 19:30:09 +10:00
Geoffrey McRae
fc907b802f [c-host] linux: updated to use the correct headers 2019-05-09 19:07:23 +10:00
Geoffrey McRae
ba50fbdc3e [client] egl: implement pixel perfect upscaling 2019-04-19 11:23:51 +10:00
Geoffrey McRae
6f77ba8aea [client] perform proper shutdown on SIGTERM 2019-04-14 09:15:03 +10:00
Geoffrey McRae
972ff93e6c [common] fix build under arch, thanks @techfreak for pointing this out 2019-04-12 12:20:24 +10:00
Geoffrey McRae
338bc2e0dc [c-host] nvfbc: disable ARGB10 until NVIDIA fix the API (if ever) 2019-04-11 19:30:42 +10:00
Geoffrey McRae
8cedad8241 [c-host] fix NvFBC build after moving headers 2019-04-11 17:15:17 +10:00
Geoffrey McRae
32bd6d96e3 [common] implemented crash handler for linux (including backtrace) 2019-04-11 16:41:52 +10:00
Geoffrey McRae
611216286e [c-host] added initial crash handler stub 2019-04-11 11:34:46 +10:00
Geoffrey McRae
d8915dbfc9 [build] make "common" a static library (part 2/2) 2019-04-11 11:12:59 +10:00
Geoffrey McRae
28b12c85f4 [build] make "common" a static library (part 1/2) 2019-04-11 11:03:30 +10:00
Geoffrey McRae
bee221c18d [c-host] add ability to provide NvFBC privData 2019-04-10 22:23:56 +10:00
Geoffrey McRae
878eb057d1 [c-host] better formatting of README.md 2019-04-10 22:08:14 +10:00
Geoffrey McRae
da7c66419a [c-host] a few build tweaks and updated README.md 2019-04-10 22:04:36 +10:00
Geoffrey McRae
d5ad53dae7 [c-host] stop the capture when stopping threads 2019-04-10 21:36:43 +10:00
Geoffrey McRae
a03075416c [c-host] cmake: fix bad method of setting CFLAGS 2019-04-10 21:20:15 +10:00
Geoffrey McRae
e4d8cf2d76 [c-host] remove extra NvFBC noise 2019-04-10 21:14:11 +10:00
Geoffrey McRae
8b47d740a8 bump version 2019-04-10 21:10:03 +10:00
Geoffrey McRae
0cac3e1c40 [c-host] tons of windows specific fixes 2019-04-10 21:07:56 +10:00
Geoffrey McRae
3f13485ced [c-host] nvfbc: continued implementation of NvFBC 2019-04-10 16:25:13 +10:00
Geoffrey McRae
24c99c4ff9 [c-host] added initial nvfbc support 2019-04-10 13:07:42 +10:00
Geoffrey McRae
4002f2716d [c-host] fix multiple re-inits due to threads flagging for reinit 2019-04-10 13:06:33 +10:00
Geoffrey McRae
f0758768b9 [c-host] show the build version 2019-04-09 16:30:07 +10:00
Geoffrey McRae
a82b1a2e2f [c-host] restructure project to use cmake 2019-04-09 16:28:11 +10:00
Łukasz Kostka
ccd0fd8902 Add install binary target 2019-04-05 03:18:11 +11:00
Geoffrey McRae
1fbba5cf2d [client] egl: make nightvision enhance luminosity before gain 2019-03-31 00:08:52 +11:00
Geoffrey McRae
d6805cfa0f [client] main: move config free into new config_free method 2019-03-30 16:00:47 +11:00
Geoffrey McRae
4dee965fdf [client] main: move config and option parsing into a seperate unit 2019-03-30 15:52:00 +11:00
Geoffrey McRae
35094a57cb [client] more cleanup and added alerts for new events 2019-03-30 12:26:06 +11:00
Geoffrey McRae
5d254c7751 [client] main: don't ignore keybinds when spice is disabled 2019-03-29 02:33:09 +11:00
Geoffrey McRae
10217fc8d9 [all] fix typo in readme 2019-03-29 02:27:17 +11:00
Geoffrey McRae
226dd28be8 [all] fix readme table formatting 2019-03-29 02:26:28 +11:00
Geoffrey McRae
c6d2b6ea8a [all] updated README.md 2019-03-29 02:25:30 +11:00
Geoffrey McRae
7fd4ba3aad [client] main: added <escape>+I for spice input enable toggle 2019-03-29 02:17:06 +11:00
Geoffrey McRae
ecfcf11c05 [client] main: fix errornous double keybind registration 2019-03-29 02:08:16 +11:00
Geoffrey McRae
30ea57c644 [client] main: add full screen toggle key bind <escape>+F
Fixes #139
2019-03-29 02:06:37 +11:00
Geoffrey McRae
c4001c727a [client] egl: added new super+N binding to increase image gain
This feture is to allow the use of the key combination <super>+N to
increase the brightness of the screen when using monitors with poor
backlighting. Can help in some games.

N = Night vision
2019-03-29 00:15:14 +11:00
Geoffrey McRae
fd4cfc2ff3 [client] main: add interface for modules to register key binds 2019-03-29 00:15:04 +11:00
Geoffrey McRae
03cb61f746 [client] main: prevent the cursor thread starting too early
fixes #136
2019-03-28 21:23:24 +11:00
Geoffrey McRae
8eed25b469 [client] cmake: list enabled features in configure output 2019-03-28 20:27:38 +11:00
Geoffrey McRae
ee09594190 [client] cmake: cosmetics 2019-03-28 20:15:13 +11:00
Geoffrey McRae
66c3c0115f [client] added options to disable/enable interfaces 2019-03-28 20:12:18 +11:00
Geoffrey McRae
3e021f3a6b [client] use cmake to generate renderers/fonts/clipboards headers/code
This is in preperation of cmake options to enable/disable various
functionallity.
2019-03-28 19:56:14 +11:00
Geoffrey McRae
b524c077a4 [client] egl: remove the rest of the shaders into seperate files 2019-03-28 15:53:15 +11:00
Geoffrey McRae
10f7efecb2 [client] cmake: fix inconsistent versioning 2019-03-28 15:03:35 +11:00
Geoffrey McRae
f09ee0bdb3 [client] egl: fix minor error in CMakeLists 2019-03-28 15:01:52 +11:00
Geoffrey McRae
d5a52241b0 [client] egl: move shaders into seperate files and build into objects 2019-03-28 14:59:54 +11:00
Geoffrey McRae
52c4e15c76 [client] project restructure part 2/2 2019-03-28 12:42:41 +11:00
Geoffrey McRae
fdba14691c [client] egl: requires gl 2019-03-28 12:31:28 +11:00
Geoffrey McRae
3d136a28a0 [all] added pre-commit script and VERSION
This script belongs in .git/hooks/ to bump the version each commit.
2019-03-28 12:24:38 +11:00
Geoffrey McRae
db398d41a0 [client] project restructure part 1/2 2019-03-28 11:02:36 +11:00
Geoffrey McRae
7cbaf8b5be [egl] don't assume SDL is compiled with Wayland support 2019-03-26 17:30:16 +11:00
NamoDev
d1c0d2b5f8 [CLIENT] cosmetic code changes 2019-03-18 08:32:38 +11:00
NamoDev
909606627f [CLIENT] Fixed invalid value initialization 2019-03-18 08:32:38 +11:00
NamoDev
80f5d3a660 [CLIENT] Added option for custom window title 2019-03-18 08:32:38 +11:00
Geoffrey McRae
182c4752d5 [c-host] dxgi: added pointer support 2019-03-04 19:26:19 +11:00
Geoffrey McRae
273ef55857 [c-host] app: added pointer interface and support 2019-03-04 19:26:02 +11:00
Geoffrey McRae
88c2e55acf [c-host] change getFrame/Pointer to return a real status 2019-03-04 17:55:45 +11:00
Geoffrey McRae
496fd79714 [c-host] initial stubs for pointer support 2019-03-04 17:45:19 +11:00
Geoffrey McRae
40a1b860bf [c-host] linux: updated, but not working yet :) 2019-03-04 17:08:49 +11:00
Geoffrey McRae
8120913acb [c-host] dxgi: reworked for better pipelining 2019-03-04 16:56:45 +11:00
Geoffrey McRae
935eb0651d [c-host] dxgi: remove needsUnmap bool 2019-03-04 15:11:40 +11:00
Geoffrey McRae
925a93686b [c-host] dxgi: cleanup init code failure path 2019-03-04 15:09:41 +11:00
Geoffrey McRae
6f545483c9 [c-host] rework events 2019-03-04 15:03:11 +11:00
Geoffrey McRae
a8b018d5da [c-host] app: fix updateEvent race problem 2019-03-04 13:38:17 +11:00
Geoffrey McRae
6e35033f2e [c-host] app: reinit on failure to capture a frame
A failed to capture a frame should not be considered fatal as it may be due to a driver issue or bug, instead try to reinitialize first
2019-03-04 13:06:30 +11:00
Geoffrey McRae
f79a1b2533 [c-host] dxgi: fixed memory leak 2019-03-04 12:04:17 +11:00
Geoffrey McRae
79ce98116a [c-host] dxgi: allow a 2nd frame to be captured during a prior copy 2019-03-04 10:42:54 +11:00
Geoffrey McRae
942c417cbb [c-host] dxgi: only flag frame updates if there was actually an update 2019-03-04 10:17:19 +11:00
Geoffrey McRae
8df850023c [c-host] fix deadlock when there is no update 2019-03-04 10:16:51 +11:00
Geoffrey McRae
eedde4abcb [c-host] fixed build under linux 2019-03-04 09:45:45 +11:00
Geoffrey McRae
fcc06dfad4 [c-host] app: inital capture is now working 2019-03-04 09:37:50 +11:00
Geoffrey McRae
ff850c4251 [c-host] remove testing code 2019-03-03 23:47:04 +11:00
Geoffrey McRae
20f8c92bb2 [c-host] dxgi: implement getFrame 2019-03-03 23:46:03 +11:00
Geoffrey McRae
22dcb39adb [c-host] app: fix deadlock on reinit 2019-03-03 23:45:37 +11:00
Geoffrey McRae
f572a72c2a [c-host] windows: added event support 2019-03-03 23:30:02 +11:00
Geoffrey McRae
be736c48e9 [c-host] dxgi: release resources 2019-03-03 21:55:29 +11:00
Geoffrey McRae
67c7c79dae [c-host] linux: add getFrame support to xcb capture 2019-03-02 20:33:45 +11:00
Geoffrey McRae
61108ba760 [c-host] app: add initial frame capture support 2019-03-02 20:33:21 +11:00
Geoffrey McRae
7285f9e9ad [c-host] add app_quit for clean shutdown support 2019-03-02 20:31:33 +11:00
Geoffrey McRae
b29de8f370 [c-host] add platform event interface and linux support 2019-03-02 20:22:35 +11:00
Geoffrey McRae
7a828b3aee [c-host] linux: initial xcb capture 2019-03-02 11:59:03 +11:00
Geoffrey McRae
afc264e846 [c-host] linux: added initial stubs for XCB capture 2019-03-01 21:41:32 +11:00
Geoffrey McRae
37c1d7ea58 [c-host] dont use a interface that fails to create 2019-03-01 21:41:06 +11:00
Geoffrey McRae
4a72dab02a [c-host] linux: correct device name comparison 2019-03-01 21:17:16 +11:00
Geoffrey McRae
22e5b323c8 [c-host] linux: simplify read from device 2019-03-01 21:12:42 +11:00
Geoffrey McRae
b275ac5765 [c-host] linux: check the device name 2019-03-01 21:12:11 +11:00
Geoffrey McRae
1475845675 [c-host] correct buffer size for stncat 2019-03-01 21:03:10 +11:00
Geoffrey McRae
6d6034870e [c-host] implemented linux ivshmem support 2019-03-01 21:01:25 +11:00
Geoffrey McRae
0a3b1e930a [c-host] update linux startup 2019-03-01 15:59:53 +11:00
Geoffrey McRae
836e8a5654 [c-host] improve capture reinitialization 2019-03-01 15:57:48 +11:00
Geoffrey McRae
39ac07bfde [c-host] removed debug code 2019-03-01 15:47:50 +11:00
Geoffrey McRae
fc178b40bc [c-host] general windows fixes 2019-03-01 15:46:09 +11:00
Geoffrey McRae
9170b24fee [c-host] added linux thread support 2019-03-01 12:54:31 +11:00
Geoffrey McRae
3674b4ed96 [c-host] added cursor and frame thread stubs 2019-03-01 12:42:12 +11:00
Geoffrey McRae
c9d9205bb8 [c-host] add missing pointer initialization 2019-03-01 12:41:37 +11:00
Geoffrey McRae
2c54fd2357 [c-host] added platform agnostic thread interface 2019-03-01 12:24:23 +11:00
Geoffrey McRae
d881df916e [c-host] more windows basics and ivshmem pointer init 2019-02-28 20:50:22 +11:00
Geoffrey McRae
6894ed7d5c [c-host] don't include windows.h in the main app 2019-02-28 19:47:25 +11:00
Geoffrey McRae
25a2b2d5d3 [c-host] fix makefile for linux builds 2019-02-28 19:46:33 +11:00
Geoffrey McRae
4fd62a58bd [c-host] move dxgi capture into windows directory 2019-02-28 19:44:15 +11:00
Geoffrey McRae
532dc07c7b [c-host] move windows dll folder into windows dir 2019-02-28 19:35:42 +11:00
Geoffrey McRae
fb2a2076a2 [c-host] added linux platform stubs 2019-02-28 19:31:04 +11:00
Geoffrey McRae
a8622be1c6 [c-host] added windows ivshmem unmap support 2019-02-28 19:27:17 +11:00
Geoffrey McRae
810fb73362 [common] gnuc on windows still uses / as the directory separator 2019-02-28 19:21:00 +11:00
Geoffrey McRae
6950379d94 [c-host] initial ivshmem code and platform specific init 2019-02-28 19:20:35 +11:00
Geoffrey McRae
f9020659e6 [c-host] only include windows defines when building on windows 2019-02-28 16:45:58 +11:00
Geoffrey McRae
c99f4e31c5 [c-host] added new pure C host project, see README.md 2019-02-28 16:35:30 +11:00
Geoffrey McRae
526c09b7ff [host] added missing file from build 2019-02-28 16:26:09 +11:00
Geoffrey McRae
5a37a53cb0 [host] move windows specific debug code to the host 2019-02-28 16:23:31 +11:00
Geoffrey McRae
a57d68acd5 [client] main: fixed help text typo 2019-02-26 03:12:57 +11:00
Geoffrey McRae
a33734e2d3 [client] main: help output cosmetics 2019-02-26 03:09:59 +11:00
Geoffrey McRae
e5921b3949 [client] main: better error text for changed spice options 2019-02-26 03:08:26 +11:00
Geoffrey McRae
5de25f2b43 [client] main: add options to control spice features 2019-02-26 03:06:53 +11:00
Geoffrey McRae
41f4166aed [client] x11: remove noise about unsupported formats 2019-02-25 05:44:31 +11:00
Geoffrey McRae
4f8fa6e7aa [client] fixes #135, double free crash 2019-02-25 05:43:18 +11:00
Geoffrey McRae
dbd09a431a Revert "[client] x11: prevent race condition causing double free"
This reverts commit 8d48dd973a.
2019-02-25 04:59:51 +11:00
Geoffrey McRae
8d48dd973a [client] x11: prevent race condition causing double free 2019-02-25 04:42:58 +11:00
Geoffrey McRae
c7666b314b [client] x11: fix bidirectional clipboard functionallity 2019-02-24 15:35:31 +11:00
Geoffrey McRae
03628505ed [client] spice: correct sending of large va agent buffers 2019-02-24 15:35:17 +11:00
Geoffrey McRae
b368873f4d [client] x11: ignore clipboard select events from ourself 2019-02-24 12:16:32 +11:00
Geoffrey McRae
dd38f3ce13 [client] x11: don't notify to clear if no property 2019-02-24 12:10:57 +11:00
Geoffrey McRae
d8b01c0257 [client] initial host to client clipboard sync working 2019-02-24 11:43:32 +11:00
Geoffrey McRae
0a2fbe1f7f [client] spice: implement full clipboard guest copy support 2019-02-23 04:24:30 +11:00
Geoffrey McRae
de0b54ae70 [client] cosmetics 2019-02-22 22:40:57 +11:00
Geoffrey McRae
54e8cce33c [client] core: added initial clipboard interface and x11 stubs 2019-02-22 22:38:52 +11:00
Geoffrey McRae
08bf01b649 [all] update copyright dates 2019-02-22 22:16:14 +11:00
Geoffrey McRae
1a66c11091 [client] spice: better align the spice api for x11 cliboard integration 2019-02-22 19:51:14 +11:00
Geoffrey McRae
689a1de69b [client] spice: added clipboard callbacks to decouple spice from SDL 2019-02-22 18:59:45 +11:00
Geoffrey McRae
0dfa7425c1 [client] spice: fixed copying large amounts of text to the clipboard 2019-02-22 12:33:55 +11:00
Geoffrey McRae
4098db039e [client] spice: fix read to allow for larger amounts of data 2019-02-22 09:15:29 +11:00
Geoffrey McRae
a7834611d1 [client] spice: fix memory leak 2019-02-22 09:08:53 +11:00
Geoffrey McRae
9dd4e4756b [client] spice: implemented guest -> client clipboard sync 2019-02-22 09:02:34 +11:00
Geoffrey McRae
108369414e [client] spice: implement channel caps and initial clipboard support 2019-02-22 07:40:43 +11:00
Geoffrey McRae
00e07c0384 [client] spice: remove extra debug output 2019-02-22 07:39:55 +11:00
Geoffrey McRae
1ebee561bc [client] spice: fix incorrect message size for agent messages 2019-02-22 07:38:05 +11:00
Geoffrey McRae
ec0db86663 [client] spice: fix unitialized value 2019-02-22 04:13:27 +11:00
Geoffrey McRae
3df4bb3a54 [client] spice: protocol updates for performance and agent support
Note: agent support is not complete at this point due to lack of documentation.
2019-02-22 03:04:06 +11:00
Geoffrey McRae
5bd748680f client: cosmetics 2019-02-22 03:04:06 +11:00
Marius Barbu
e09ff31c09 [client] update viewport size after window is created
Make sure glViewport gets called as soon as we know the window
dimensions, otherwise nothing gets rendered until KVMFR communication is
established (mostly the splash).

Without the fix, './looking-glass-client -s' behaves differently than
'./looking-glass-client -s -F' in that the latter triggers a
SDL_WINDOWEVENT_SIZE_CHANGED and updates the viewport immediately after
window creation while the former doesn't and all rendering is delayed
until the frameThread successfully decodes the first frame.

As all the possible ways of updating the viewport (window creation,
window size change, frame size change) are covered with
updatePositionInfo(), the 'started' state becomes redundant and is
removed.

Note: this might be the wrong way to fix it (possible driver bug?),
glViewport's specification mentions that the default viewport size
matches the window size when the GL context is first attached.

Tested on:

Debian Buster with GNOME 3.30.2 on X.Org 1.20.3
Looking Glass (a12-21-g07e4c1c20f)
Locking Method: Atomic
Using: EGL
Vendor  : Intel Open Source Technology Center
Renderer: Mesa DRI Intel(R) Haswell Desktop
Version : OpenGL ES 3.1 Mesa 18.3.2

Signed-off-by: Marius Barbu <msb@avengis.com>
2019-02-21 07:56:25 +11:00
Geoffrey McRae
07e4c1c20f [client] spice: correct issue with new channel list support 2019-02-21 05:31:51 +11:00
Geoffrey McRae
daf854c692 [client] spice: initial agent support 2019-02-21 05:06:13 +11:00
Geoffrey McRae
65c1e0391c [client] spice: minor fixes, don't assume inputs channel exists 2019-02-21 03:43:11 +11:00
Geoffrey McRae
769edba1a5 [client] spice: remove useless mouse queue, this is not needed
When this was first developed the official spice client was used as an example refrence, however upon inspecting the source of spice-server it has been determined that there is no server side queueing going on, and acks are simply sent after every 4 to reduce bandwidth.
2019-02-19 14:50:30 +11:00
Frediano Ziglio
2567447b24 [client] spice: use correct enumeration for SpiceLinkReply
SPICEC_ERROR_CODE_xxx constants are supposed to be used by old
client (obsoleted years ago).
SpiceLinkReply error field uses SPICE_LINK_ERR_xxx enumeration
constants.

Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
2019-02-10 09:06:07 +11:00
Geoffrey McRae
263b412fdf [host] dxgi: fixed reversed logic for fallback 2019-02-07 15:13:07 +11:00
Geoffrey McRae
037ea5b1fc [host] fix compiler warnings 2019-02-07 14:43:18 +11:00
Geoffrey McRae
18634fa805 [host] fallback to IDXGIOutput1 if IDXGIOutput5 is not available 2019-02-07 14:43:18 +11:00
Geoffrey McRae
473e4716fc [client] spice: debug print unsigned int re #127 2019-01-25 14:17:06 +11:00
Dominik Csapak
59cac9c0cc change spice port type to unsigned short
so that ports >32767 get displayed correctly,
also signed overflow is undefined behaviour

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2019-01-25 14:15:22 +11:00
Geoffrey McRae
92d87d983b [client] spice: fix incorrect cursor button state being sent 2019-01-17 02:45:46 +11:00
Geoffrey McRae
bfc4a1bc16 [client] update client to handle new cursor move code 2019-01-12 00:00:21 +11:00
Geoffrey McRae
1ef61f6cd3 [host] use a global hook to obtain cursor move pos 2019-01-11 23:58:50 +11:00
Geoffrey McRae
5518ccb795 [host] NvFBC: user specified privateData for debug 2019-01-09 16:10:46 +11:00
Luke Brown
027b27dda1 updated links to latest accurate urls.
added trello board url.
2019-01-08 10:33:16 +11:00
Geoffrey McRae
6e1180ce06 [host] nvfbc: initial updates to re-enable support 2019-01-03 17:08:05 +11:00
Geoffrey McRae
e4ae9134ae [client] egl: fix graphical glitch with splash 2019-01-02 10:36:17 +11:00
Geoffrey McRae
640bc03c6b [client] [Patch 2/2] fixes #106 2019-01-02 10:30:55 +11:00
Geoffrey McRae
2a86339b1d [host] [Patch 1/2] fix copy of padded resolutions 2019-01-02 10:29:46 +11:00
Geoffrey McRae
667aed635d [client] egl: added untested support for wayland 2019-01-02 00:04:40 +11:00
Alexander Olofsson
1d3a23e051 Store the initial window size in the state
Fixes #107
2018-12-17 16:07:21 +11:00
210 changed files with 15579 additions and 10193 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: gnif
patreon: gnif
open_collective: # Replace with a single Open Collective username
ko_fi: lookingglass
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -1,11 +1,69 @@
### Required information
### Issues are for Bug Reports and Feature Requests Only!
Host CPU:
Host GPU:
Guest GPU:
Host Kernel version:
Host QEMU version:
If you are looking for help or support please use one of the following methods
Please describe what were you doing when the problem occured. If the Windows host application crashed please check for file named `looking-glass-host.dmp` and attach it to this bug report.
Create a New Topic on the Level1Tech's forum under the Looking Glass category:
* https://forum.level1techs.com/c/software/lookingGlass/142
**Reports that do no include this information will be ignored and closed**
Ask for help in #looking-glass in the VFIO discord server
* https://discord.gg/4ahCn4c
*Issues that are not bug reports or feature requests will be closed & ignored*
### Errors that are not bugs
Some errors generated by the LG client are not bugs, but rather issues with your
system's configuration and/or timing. Please do not report these, but rather use
one of the above resources to ask for advice/help.
* `LGMP_ERR_QUEUE_UNSUBSCRIBED` - Failure to heed advice on things such as
using `isolcpus` and CPU pinning may result in this message, especially if you
are over-taxing your CPU.
* `Could not create an SDL window: *` - Failure to create a SDL window is not an
issue with Looking Glass but rather a more substantial issue with your system,
such as missing hardware support for the RGBA32 pixmap format, or missing
required OpenGL EGL features.
* `The host application is not compatible with this client` - The Looking Glass
Host application in Windows is the incorrect version and is not compatible,
you need to make sure you run matching versions of both the host and client
applications.
### Bug Report Required Information
The entire (not truncated) output from the client application (if applicable).
To obtain this run `looking-glass-client` in a terminal.
```
PASTE CLIENT OUTPUT HERE
```
The entire (not truncated) log file from the host application (if applicable).
To obtain this locate the log file on your system, it will be in one of the
following two locations depending on how you are launching the Looking Glass Host
application:
* C:\Windows\Temp\looking-glass.txt
* C:\Users\YOUR_USER\AppData\Local\Temp\looking-glass.txt
This log may be quite long, please delete the file first and then proceed to
launch the host and reproduce the issue so that the log only contains the
pertinent information.
```
PASTE HOST LOG FILE CONTENTS HERE
```
If the client is unexpetedly exiting without a backtrace, please provide one via
gdb with the command `thread apply all bt`. If you are unsure how to do this
please watch the video below on how to perform a Debug build and generate this
backtrace.
https://www.youtube.com/watch?v=EqxxJK9Yo64
```
PASTE FULL BACKTRACE HERE
```

4
.gitignore vendored
View File

@@ -4,3 +4,7 @@ module/*.mod.c
module/.*
module/Module.symvers
module/modules.order
*.a
*.o
*.exe
*/build

9
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "vendor/kvm-guest-drivers-windows"]
path = vendor/kvm-guest-drivers-windows
url = https://github.com/virtio-win/kvm-guest-drivers-windows.git
[submodule "LGMP"]
path = repos/LGMP
url = https://github.com/gnif/LGMP.git
[submodule "repos/PureSpice"]
path = repos/PureSpice
url = https://github.com/gnif/PureSpice

View File

@@ -1,22 +1,66 @@
# LookingGlass
An extremely low latency KVMFR (KVM FrameRelay) implementation for guests with VGA PCI Passthrough.
# Looking Glass
An extremely low latency KVMFR (KVM FrameRelay) implementation for guests with
VGA PCI Passthrough.
* Project Website: https://looking-glass.hostfission.com
* Support Forum: https://forum.level1techs.com/t/looking-glass-guides-help-and-support/122387
* Getting Started: https://looking-glass.hostfission.com/wiki/Installation
## Obtaining and using Looking Glass
## Donations
Please see https://looking-glass.hostfission.com/quickstart
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
** 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:
https://looking-glass.hostfission.com/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.hostfission.com/wiki, and as such the
information in these files may be dated.
* [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://ci.appveyor.com/project/gnif/lookingglass/build/artifacts
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.hostfission.com/downloads
# Help and support
## Web
https://forum.level1techs.com/t/looking-glass-guides-help-and-support/122387
https://forum.level1techs.com/t/looking-glass-triage/130952
## Discord
https://discord.gg/4ahCn4c
## IRC
Join us in the #LookingGlass channel on the FreeNode network
## Trello
* https://trello.com/b/tI1Xbwsg/looking-glass

1
VERSION Normal file
View File

@@ -0,0 +1 @@
B2-rc4-11-g8692e9af80+1

View File

@@ -1,28 +1,57 @@
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 3.0)
project(looking-glass-client C)
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
SET(CMAKE_C_FLAGS "-std=gnu99 -g -O3 -march=native -Wall -Werror -Wfatal-errors -ffast-math -fdata-sections -ffunction-sections")
SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections")
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()
option(ENABLE_OPENGL "Enable the OpenGL renderer" ON)
add_feature_info(ENABLE_OPENGL ENABLE_OPENGL "Legacy OpenGL renderer.")
option(ENABLE_EGL "Enable the EGL renderer" ON)
add_feature_info(ENABLE_EGL ENABLE_EGL "EGL renderer.")
option(ENABLE_CB_X11 "Enable X11 clipboard integration" ON)
add_feature_info(ENABLE_CB_X11 ENABLE_CB_X11 "X11 Clipboard Integration.")
option(ENABLE_BACKTRACE "Enable backtrace support on crash" ON)
add_feature_info(ENABLE_BACKTRACE ENABLE_BACKTRACE "Backtrace support.")
add_compile_options(
"-Wall"
"-Werror"
"-Wfatal-errors"
"-ffast-math"
"-fdata-sections"
"-ffunction-sections"
"$<$<CONFIG:DEBUG>:-O0;-g3;-ggdb>"
)
set(EXE_FLAGS "-Wl,--gc-sections")
set(CMAKE_C_STANDARD 11)
find_package(PkgConfig)
pkg_check_modules(PKGCONFIG REQUIRED
sdl2
SDL2_ttf
gl
glu
egl
spice-protocol
fontconfig
x11
libconfig
nettle
hogweed
)
pkg_check_modules(PKGCONFIG_OPT
xi
)
execute_process(
COMMAND git describe --always --long --dirty --abbrev=10 --tags
COMMAND cat ../VERSION
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE BUILD_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
@@ -31,46 +60,55 @@ execute_process(
find_package(GMP)
add_definitions(-D BUILD_VERSION='"${BUILD_VERSION}"')
add_definitions(-D USE_NETTLE)
add_definitions(-D ATOMIC_LOCKING)
add_definitions(-D GL_GLEXT_PROTOTYPES)
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
include_directories(
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/../common
${PKGCONFIG_INCLUDE_DIRS}
${PROJECT_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/include
${PKGCONFIG_INCLUDE_DIRS} ${PKGCONFIG_OPT_INCLUDE_DIRS}
${GMP_INCLUDE_DIR}
)
link_libraries(
${PKGCONFIG_LIBRARIES}
${PKGCONFIG_LIBRARIES} ${PKGCONFIG_OPT_LIBRARIES}
${GMP_LIBRARIES}
rt m
${CMAKE_DL_LIBS}
rt
m
)
set(SOURCES
main.c
lg-renderer.c
lg-fonts.c
ll.c
utils.c
spice/rsa.c
spice/spice.c
decoders/null.c
decoders/yuv420.c
renderers/opengl.c
renderers/egl.c
renderers/egl/shader.c
renderers/egl/texture.c
renderers/egl/model.c
renderers/egl/desktop.c
renderers/egl/cursor.c
renderers/egl/fps.c
renderers/egl/draw.c
renderers/egl/splash.c
renderers/egl/alert.c
fonts/sdl.c
src/main.c
src/app.c
src/config.c
src/lg-renderer.c
src/ll.c
src/utils.c
)
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(renderers)
add_subdirectory(clipboards)
add_subdirectory(fonts)
add_subdirectory(decoders)
add_executable(looking-glass-client ${SOURCES})
target_compile_options(looking-glass-client PUBLIC ${PKGCONFIG_CFLAGS_OTHER})
target_compile_options(looking-glass-client PUBLIC ${PKGCONFIG_CFLAGS_OTHER} ${PKGCONFIG_OPT_CFLAGS_OTHER})
target_link_libraries(looking-glass-client
${EXE_FLAGS}
lg_common
lgmp
purespice
renderers
clipboards
fonts
)
install(PROGRAMS ${CMAKE_BINARY_DIR}/looking-glass-client DESTINATION bin/ COMPONENT binary)
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)

164
client/README.md Normal file
View File

@@ -0,0 +1,164 @@
# 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
#### 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
### Building
mkdir build
cd build
cmake ../
make
Should this all go well you should be left with the file `looking-glass-client`
---
## 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>I</kbd> | Spice keyboard & mouse enable toggle |
| <kbd>ScrLk</kbd>+<kbd>N</kbd> | Toggle night vision mode (EGL renderer only!) |
| <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 |
### 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:shmFile | -f | /dev/shm/looking-glass | The path to the shared memory file |
| app:shmSize | -L | 0 | Specify the size in MB of the shared memory file (0 = detect) |
| 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 |
|-------------------------------------------------------------------------------------------------------------------------|
|-------------------------------------------------------------------------------------------------------------|
| 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 | Aallow the window to be manually resized |
| win:keepAspect | -r | yes | Maintain the correct aspect ratio |
| 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:fpsLimit | -K | 200 | Frame rate limit (0 = disable - not recommended) |
| 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 |
|-------------------------------------------------------------------------------------------------------------|
|---------------------------------------------------------------------------------------------------------------------------------------|
| Long | Short | Value | Description |
|---------------------------------------------------------------------------------------------------------------------------------------|
| input:grabKeyboard | -G | yes | Grab the keyboard in capture mode |
| input:escapeKey | -m | 71 = ScrollLock | Specify the escape key, see https://wiki.libsdl.org/SDLScancodeLookup for valid values |
| input:hideCursor | -M | yes | Hide the local mouse cursor |
| input:mouseSens | | 0 | Initial mouse sensitivity when in capture mode (-9 to 9) |
|---------------------------------------------------------------------------------------------------------------------------------------|
|------------------------------------------------------------------------------------------------------------------|
| 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 |
|------------------------------------------------------------------------------------------------------------------|
|--------------------------------------------------------------------------|
| Long | Short | Value | Description |
|--------------------------------------------------------------------------|
| egl:vsync | | no | Enable vsync |
| egl:nvGainMax | | 1 | The maximum night vision gain |
| egl:nvGain | | 0 | The initial night vision gain at startup |
|--------------------------------------------------------------------------|
|------------------------------------------------------------------------------------|
| Long | Short | Value | Description |
|------------------------------------------------------------------------------------|
| opengl:mipmap | | yes | Enable mipmapping |
| opengl:vsync | | yes | Enable vsync |
| opengl:preventBuffer | | yes | Prevent the driver from buffering frames |
| opengl:amdPinnedMem | | yes | Use GL_AMD_pinned_memory if it is available |
|------------------------------------------------------------------------------------|
```

View File

@@ -0,0 +1,43 @@
cmake_minimum_required(VERSION 3.0)
project(clipboards LANGUAGES C)
set(CLIPBOARD_H "${CMAKE_BINARY_DIR}/include/dynamic/clipboards.h")
set(CLIPBOARD_C "${CMAKE_BINARY_DIR}/src/clipboards.c")
file(WRITE ${CLIPBOARD_H} "#include \"interface/clipboard.h\"\n\n")
file(APPEND ${CLIPBOARD_H} "extern LG_Clipboard * LG_Clipboards[];\n\n")
file(WRITE ${CLIPBOARD_C} "#include \"interface/clipboard.h\"\n\n")
file(APPEND ${CLIPBOARD_C} "#include <stddef.h>\n\n")
set(CLIPBOARDS "_")
set(CLIPBOARDS_LINK "_")
function(add_clipboard name)
set(CLIPBOARDS "${CLIPBOARDS};${name}" PARENT_SCOPE)
set(CLIPBOARDS_LINK "${CLIPBOARDS_LINK};clipboard_${name}" PARENT_SCOPE)
add_subdirectory(${name})
endfunction()
# Add/remove clipboards here!
if (ENABLE_CB_X11)
add_clipboard(X11)
endif()
list(REMOVE_AT CLIPBOARDS 0)
list(REMOVE_AT CLIPBOARDS_LINK 0)
list(LENGTH CLIPBOARDS CLIPBOARD_COUNT)
file(APPEND ${CLIPBOARD_H} "#define LG_CLIPBOARD_COUNT ${CLIPBOARD_COUNT}\n")
foreach(clipboard ${CLIPBOARDS})
file(APPEND ${CLIPBOARD_C} "extern LG_Clipboard LGC_${clipboard};\n")
endforeach()
file(APPEND ${CLIPBOARD_C} "\nconst LG_Clipboard * LG_Clipboards[] =\n{\n")
foreach(clipboard ${CLIPBOARDS})
file(APPEND ${CLIPBOARD_C} " &LGC_${clipboard},\n")
endforeach()
file(APPEND ${CLIPBOARD_C} " NULL\n};\n\n")
add_library(clipboards STATIC ${CLIPBOARD_C})
target_link_libraries(clipboards ${CLIPBOARDS_LINK})

View File

@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.0)
project(clipboard_X11 LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(CLIPBOARD_PKGCONFIG REQUIRED
x11
xfixes
)
add_library(clipboard_X11 STATIC
src/x11.c
)
target_link_libraries(clipboard_X11
${CLIPBOARD_PKGCONFIG_LIBRARIES}
lg_common
)
target_include_directories(clipboard_X11
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
src
${CLIPBOARD_PKGCONFIG_INCLUDE_DIRS}
)

View File

@@ -0,0 +1,359 @@
/*
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 "interface/clipboard.h"
#include "common/debug.h"
#include <X11/extensions/Xfixes.h>
struct state
{
Display * display;
Window window;
Atom aSelection;
Atom aCurSelection;
Atom aTargets;
Atom aSelData;
Atom aIncr;
Atom aTypes[LG_CLIPBOARD_DATA_NONE];
LG_ClipboardReleaseFn releaseFn;
LG_ClipboardRequestFn requestFn;
LG_ClipboardNotifyFn notifyFn;
LG_ClipboardDataFn dataFn;
LG_ClipboardData type;
// XFixes vars
int eventBase;
int errorBase;
};
static struct state * this = NULL;
static const char * atomTypes[] =
{
"UTF8_STRING",
"image/png",
"image/bmp",
"image/tiff",
"image/jpeg"
};
static const char * x11_cb_getName()
{
return "X11";
}
static bool x11_cb_init(
SDL_SysWMinfo * wminfo,
LG_ClipboardReleaseFn releaseFn,
LG_ClipboardNotifyFn notifyFn,
LG_ClipboardDataFn dataFn)
{
// final sanity check
if (wminfo->subsystem != SDL_SYSWM_X11)
{
DEBUG_ERROR("wrong subsystem");
return false;
}
this = (struct state *)malloc(sizeof(struct state));
memset(this, 0, sizeof(struct state));
this->display = wminfo->info.x11.display;
this->window = wminfo->info.x11.window;
this->aSelection = XInternAtom(this->display, "CLIPBOARD", False);
this->aTargets = XInternAtom(this->display, "TARGETS" , False);
this->aSelData = XInternAtom(this->display, "SEL_DATA" , False);
this->aIncr = XInternAtom(this->display, "INCR" , False);
this->aCurSelection = BadValue;
this->releaseFn = releaseFn;
this->notifyFn = notifyFn;
this->dataFn = dataFn;
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
{
this->aTypes[i] = XInternAtom(this->display, atomTypes[i], False);
if (this->aTypes[i] == BadAlloc || this->aTypes[i] == BadValue)
{
DEBUG_ERROR("failed to get atom for type: %s", atomTypes[i]);
free(this);
this = NULL;
return false;
}
}
// we need the raw X events
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
// use xfixes to get clipboard change notifications
if (!XFixesQueryExtension(this->display, &this->eventBase, &this->errorBase))
{
DEBUG_ERROR("failed to initialize xfixes");
free(this);
this = NULL;
return false;
}
XFixesSelectSelectionInput(this->display, this->window, XA_PRIMARY , XFixesSetSelectionOwnerNotifyMask);
XFixesSelectSelectionInput(this->display, this->window, this->aSelection, XFixesSetSelectionOwnerNotifyMask);
return true;
}
static void x11_cb_free()
{
free(this);
this = NULL;
}
static void x11_cb_reply_fn(void * opaque, LG_ClipboardData type, uint8_t * data, uint32_t size)
{
XEvent *s = (XEvent *)opaque;
XChangeProperty(
this->display ,
s->xselection.requestor,
s->xselection.property ,
s->xselection.target ,
8,
PropModeReplace,
data,
size);
XSendEvent(this->display, s->xselection.requestor, 0, 0, s);
XFlush(this->display);
free(s);
}
static void x11_cb_wmevent(SDL_SysWMmsg * msg)
{
XEvent e = msg->msg.x11.event;
if (e.type == SelectionRequest)
{
XEvent * s = (XEvent *)malloc(sizeof(XEvent));
s->xselection.type = SelectionNotify;
s->xselection.requestor = e.xselectionrequest.requestor;
s->xselection.selection = e.xselectionrequest.selection;
s->xselection.target = e.xselectionrequest.target;
s->xselection.property = e.xselectionrequest.property;
s->xselection.time = e.xselectionrequest.time;
if (!this->requestFn)
{
s->xselection.property = None;
XSendEvent(this->display, e.xselectionrequest.requestor, 0, 0, s);
XFlush(this->display);
free(s);
return;
}
// target list requested
if (e.xselectionrequest.target == this->aTargets)
{
Atom targets[2];
targets[0] = this->aTargets;
targets[1] = this->aTypes[this->type];
XChangeProperty(
e.xselectionrequest.display,
e.xselectionrequest.requestor,
e.xselectionrequest.property,
XA_ATOM,
32,
PropModeReplace,
(unsigned char*)targets,
sizeof(targets) / sizeof(Atom));
XSendEvent(this->display, e.xselectionrequest.requestor, 0, 0, s);
XFlush(this->display);
free(s);
return;
}
// look to see if we can satisfy the data type
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
if (this->aTypes[i] == e.xselectionrequest.target && this->type == i)
{
// request the data
this->requestFn(x11_cb_reply_fn, s);
return;
}
// report no data
s->xselection.property = None;
XSendEvent(this->display, e.xselectionrequest.requestor, 0, 0, s);
XFlush(this->display);
}
if (e.type == SelectionClear && (
e.xselectionclear.selection == XA_PRIMARY ||
e.xselectionclear.selection == this->aSelection)
)
{
this->aCurSelection = BadValue;
this->releaseFn();
return;
}
// if someone selected data
if (e.type == this->eventBase + XFixesSelectionNotify)
{
XFixesSelectionNotifyEvent * sne = (XFixesSelectionNotifyEvent *)&e;
// check if the selection is valid and it isn't ourself
if (
(sne->selection != XA_PRIMARY && sne->selection != this->aSelection) ||
sne->owner == this->window ||
sne->owner == 0
)
{
return;
}
// remember which selection we are working with
this->aCurSelection = sne->selection;
XConvertSelection(
this->display,
sne->selection,
this->aTargets,
this->aTargets,
this->window,
CurrentTime);
return;
}
if (e.type == SelectionNotify)
{
if (e.xselection.property == None)
return;
Atom type;
int format;
unsigned long itemCount, after;
unsigned char *data;
XGetWindowProperty(
this->display,
this->window,
e.xselection.property,
0, ~0L, // start and length
True , // delete the property
AnyPropertyType,
&type,
&format,
&itemCount,
&after,
&data);
// the target list
if (e.xselection.property == this->aTargets)
{
// 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)
{
if (data)
XFree(data);
return;
}
// 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 (this->aTypes[n] == targets[i])
{
// we have a match, so send the notification
this->notifyFn(n);
XFree(data);
return;
}
}
// no matches
this->notifyFn(LG_CLIPBOARD_DATA_NONE);
XFree(data);
return;
}
if (format == this->aIncr)
{
DEBUG_WARN("fixme: large paste buffers are not yet supported");
XFree(data);
return;
}
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
if (this->aTypes[i] == type)
{
this->dataFn(i, data, itemCount);
XFree(data);
return;
}
DEBUG_WARN("clipboard data (%s) not in a supported format", XGetAtomName(this->display, type));
XFree(data);
return;
}
}
static void x11_cb_notice(LG_ClipboardRequestFn requestFn, LG_ClipboardData type)
{
this->requestFn = requestFn;
this->type = type;
XSetSelectionOwner(this->display, XA_PRIMARY , this->window, CurrentTime);
XSetSelectionOwner(this->display, this->aSelection, this->window, CurrentTime);
XFlush(this->display);
}
static void x11_cb_release()
{
this->requestFn = NULL;
XSetSelectionOwner(this->display, XA_PRIMARY , None, CurrentTime);
XSetSelectionOwner(this->display, this->aSelection, None, CurrentTime);
XFlush(this->display);
}
static void x11_cb_request(LG_ClipboardData type)
{
if (this->aCurSelection == BadValue)
return;
XConvertSelection(
this->display,
this->aCurSelection,
this->aTypes[type],
this->aSelData,
this->window,
CurrentTime);
}
const LG_Clipboard LGC_X11 =
{
.getName = x11_cb_getName,
.init = x11_cb_init,
.free = x11_cb_free,
.wmevent = x11_cb_wmevent,
.notice = x11_cb_notice,
.release = x11_cb_release,
.request = x11_cb_request
};

View File

@@ -0,0 +1,34 @@
function(make_object out_var)
set(result)
set(result_h)
foreach(in_f ${ARGN})
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})
add_custom_command(OUTPUT ${out_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}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Creating object from ${in_f}"
VERBATIM
)
file(WRITE ${out_h} "extern const char b_${sym_in}[];\n")
file(APPEND ${out_h} "extern const char b_${sym_in}_end[];\n")
file(APPEND ${out_h} "#define b_${sym_in}_size (b_${sym_in}_end - b_${sym_in})\n")
get_filename_component(h_dir ${out_h} DIRECTORY)
list(APPEND result_h ${h_dir})
list(APPEND result ${out_f})
endforeach()
list(REMOVE_DUPLICATES result_h)
set(${out_var}_OBJS "${result}" PARENT_SCOPE)
set(${out_var}_INCS "${result_h}" PARENT_SCOPE)
endfunction()

View File

@@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.0)
project(decoders LANGUAGES C)
#find_package(PkgConfig)
#pkg_check_modules(DECODERS_PKGCONFIG REQUIRED
#)
add_library(decoders STATIC
src/null.c
src/yuv420.c
)
target_link_libraries(decoders
lg_common
${DECODERS_PKGCONFIG_LIBRARIES}
)
target_include_directories(decoders
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
src
${DECODERS_PKGCONFIG_INCLUDE_DIRS}
)

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -17,10 +17,10 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "lg-decoder.h"
#include "interface/decoder.h"
#include "debug.h"
#include "memcpySSE.h"
#include "common/debug.h"
#include "common/memcpySSE.h"
#include <stdlib.h>
#include <string.h>

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -17,10 +17,10 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "lg-decoder.h"
#include "interface/decoder.h"
#include "debug.h"
#include "memcpySSE.h"
#include "common/debug.h"
#include "common/memcpySSE.h"
#include <stdlib.h>
#include <string.h>

View File

@@ -0,0 +1,41 @@
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

@@ -0,0 +1,26 @@
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,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -20,8 +20,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdlib.h>
#include <stdbool.h>
#include "lg-font.h"
#include "debug.h"
#include "interface/font.h"
#include "common/debug.h"
#include <SDL2/SDL_ttf.h>
#include <fontconfig/fontconfig.h>

View File

@@ -0,0 +1,58 @@
/*
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 <SDL2/SDL.h>
typedef enum LG_MsgAlert
{
LG_ALERT_INFO ,
LG_ALERT_SUCCESS,
LG_ALERT_WARNING,
LG_ALERT_ERROR
}
LG_MsgAlert;
typedef struct KeybindHandle * KeybindHandle;
typedef void (*SuperEventFn)(SDL_Scancode key, void * opaque);
/**
* Show an alert on screen
* @param type The alert type
* param fmt The alert message format
@ param ... formatted message values
*/
void app_alert(LG_MsgAlert type, const char * fmt, ...);
/**
* Register a handler for the <super>+<key> combination
* @param key The scancode to register
* @param callback The function to be called when the combination is pressed
* @param opaque A pointer to be passed to the callback, may be NULL
* @retval A handle for the binding or NULL on failure.
* The caller is required to release the handle via `app_release_keybind` when it is no longer required
*/
KeybindHandle app_register_keybind(SDL_Scancode key, SuperEventFn callback, void * opaque);
/**
* Release an existing key binding
* @param handle A pointer to the keybind handle to release, may be NULL
*/
void app_release_keybind(KeybindHandle * handle);

View File

@@ -0,0 +1,62 @@
/*
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 <SDL2/SDL.h>
#include <SDL2/SDL_syswm.h>
typedef enum LG_ClipboardData
{
LG_CLIPBOARD_DATA_TEXT = 0,
LG_CLIPBOARD_DATA_PNG,
LG_CLIPBOARD_DATA_BMP,
LG_CLIPBOARD_DATA_TIFF,
LG_CLIPBOARD_DATA_JPEG,
LG_CLIPBOARD_DATA_NONE // enum max, not a data type
}
LG_ClipboardData;
typedef void (* LG_ClipboardReplyFn )(void * opaque, const LG_ClipboardData type, uint8_t * data, uint32_t size);
typedef void (* LG_ClipboardRequestFn)(LG_ClipboardReplyFn replyFn, void * opaque);
typedef void (* LG_ClipboardReleaseFn)();
typedef void (* LG_ClipboardNotifyFn)(LG_ClipboardData type);
typedef void (* LG_ClipboardDataFn )(const LG_ClipboardData type, uint8_t * data, size_t size);
typedef const char * (* LG_ClipboardGetName)();
typedef bool (* LG_ClipboardInit)(SDL_SysWMinfo * wminfo, LG_ClipboardReleaseFn releaseFn, LG_ClipboardNotifyFn notifyFn, LG_ClipboardDataFn dataFn);
typedef void (* LG_ClipboardFree)();
typedef void (* LG_ClipboardWMEvent)(SDL_SysWMmsg * msg);
typedef void (* LG_ClipboardNotice)(LG_ClipboardRequestFn requestFn, LG_ClipboardData type);
typedef void (* LG_ClipboardRelease)();
typedef void (* LG_ClipboardRequest)(LG_ClipboardData type);
typedef struct LG_Clipboard
{
LG_ClipboardGetName getName;
LG_ClipboardInit init;
LG_ClipboardFree free;
LG_ClipboardWMEvent wmevent;
LG_ClipboardNotice notice;
LG_ClipboardRelease release;
LG_ClipboardRequest request;
}
LG_Clipboard;

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -19,7 +19,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#pragma once
#include "lg-renderer.h"
#include "renderer.h"
#include <stdint.h>
#include <stdbool.h>

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -24,13 +24,16 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include "KVMFR.h"
#include "app.h"
#include "common/KVMFR.h"
#include "common/framebuffer.h"
#define IS_LG_RENDERER_VALID(x) \
((x)->get_name && \
(x)->create && \
(x)->initialize && \
(x)->deinitialize && \
(x)->on_restart && \
(x)->on_resize && \
(x)->on_mouse_shape && \
(x)->on_mouse_event && \
@@ -39,28 +42,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
(x)->render && \
(x)->update_fps)
#define LGR_OPTION_COUNT(x) (sizeof(x) / sizeof(LG_RendererOpt))
typedef bool(* LG_RendererOptValidator)(const char * value);
typedef void(* LG_RendererOptHandler )(void * opaque, const char * value);
typedef struct LG_RendererOpt
{
const char * name;
const char * desc;
LG_RendererOptValidator validator;
LG_RendererOptHandler handler;
}
LG_RendererOpt;
typedef struct LG_RendererOptValue
{
const LG_RendererOpt * opt;
char * value;
} LG_RendererOptValue;
typedef LG_RendererOpt * LG_RendererOptions;
typedef struct LG_RendererParams
{
// TTF_Font * font;
@@ -98,35 +79,33 @@ typedef enum LG_RendererCursor
}
LG_RendererCursor;
typedef enum LG_RendererOnAlert
{
LG_ALERT_INFO ,
LG_ALERT_SUCCESS,
LG_ALERT_WARNING,
LG_ALERT_ERROR
}
LG_RendererAlert;
// returns the friendly name of the renderer
typedef const char * (* LG_RendererGetName)();
// called pre-creation to allow the renderer to register any options it might have
typedef void (* LG_RendererSetup)();
typedef const char * (* LG_RendererGetName )();
typedef bool (* LG_RendererCreate )(void ** opaque, const LG_RendererParams params);
typedef bool (* LG_RendererInitialize )(void * opaque, Uint32 * sdlFlags);
typedef void (* LG_RendererDeInitialize)(void * opaque);
typedef void (* LG_RendererOnRestart )(void * opaque);
typedef void (* LG_RendererOnResize )(void * opaque, const int width, const int height, const LG_RendererRect destRect);
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_RendererOnFrameEvent)(void * opaque, const LG_RendererFormat format, const uint8_t * data);
typedef void (* LG_RendererOnAlert )(void * opaque, const LG_RendererAlert alert, const char * message, bool ** closeFlag);
typedef bool (* LG_RendererOnFrameEvent)(void * opaque, const LG_RendererFormat format, const FrameBuffer * frame);
typedef void (* LG_RendererOnAlert )(void * opaque, const LG_MsgAlert alert, const char * message, bool ** closeFlag);
typedef bool (* LG_RendererRender )(void * opaque, SDL_Window *window);
typedef void (* LG_RendererUpdateFPS )(void * opaque, const float avgUPS, const float avgFPS);
typedef struct LG_Renderer
{
LG_RendererCreate create;
LG_RendererGetName get_name;
LG_RendererOptions options;
unsigned int option_count;
LG_RendererSetup setup;
LG_RendererCreate create;
LG_RendererInitialize initialize;
LG_RendererDeInitialize deinitialize;
LG_RendererOnRestart on_restart;
LG_RendererOnResize on_resize;
LG_RendererOnMouseShape on_mouse_shape;
LG_RendererOnMouseEvent on_mouse_event;
@@ -137,7 +116,3 @@ typedef struct LG_Renderer
LG_RendererUpdateFPS update_fps;
}
LG_Renderer;
// generic option helpers
bool LG_RendererValidatorBool(const char * value);
bool LG_RendererValueToBool (const char * value);

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -18,7 +18,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include "lg-decoder.h"
#include "interface/decoder.h"
extern const LG_Decoder LGD_NULL;
extern const LG_Decoder LGD_YUV420;

View File

@@ -1,6 +1,6 @@
/*
KVMGFX Client - A KVM Client for VGA Passthrough
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -17,15 +17,11 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "lg-fonts.h"
#pragma once
#include <stdlib.h>
#include <stdbool.h>
extern const LG_Font LGF_SDL;
const LG_Font * LG_Fonts[] =
{
&LGF_SDL,
NULL // end of array sentinal
};
#define LG_FONT_COUNT ((sizeof(LG_Fonts) / sizeof(LG_Font *)) - 1)
// reads the specified file into a new buffer
// the callee must free the buffer
bool file_get_contents(const char * filename, char ** buffer, size_t * length);

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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

View File

@@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 3.0)
project(renderers LANGUAGES C)
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(WRITE ${RENDERER_C} "#include \"interface/renderer.h\"\n\n")
file(APPEND ${RENDERER_C} "#include <stddef.h>\n\n")
set(RENDERERS "_")
set(RENDERERS_LINK "_")
function(add_renderer name)
set(RENDERERS "${RENDERERS};${name}" PARENT_SCOPE)
set(RENDERERS_LINK "${RENDERERS_LINK};renderer_${name}" PARENT_SCOPE)
add_subdirectory(${name})
endfunction()
# Add/remove renderers here!
if(ENABLE_EGL)
add_renderer(EGL)
endif()
if (ENABLE_OPENGL)
add_renderer(OpenGL)
endif()
list(REMOVE_AT RENDERERS 0)
list(REMOVE_AT RENDERERS_LINK 0)
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")
endforeach()
file(APPEND ${RENDERER_C} "\nconst LG_Renderer * LG_Renderers[] =\n{\n")
foreach(renderer ${RENDERERS})
file(APPEND ${RENDERER_C} " &LGR_${renderer},\n")
endforeach()
file(APPEND ${RENDERER_C} " NULL\n};")
add_library(renderers STATIC ${RENDERER_C})
target_link_libraries(renderers ${RENDERERS_LINK})

View File

@@ -0,0 +1,63 @@
cmake_minimum_required(VERSION 3.0)
project(renderer_EGL LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(RENDERER_EGL_PKGCONFIG REQUIRED
egl
gl
)
pkg_check_modules(RENDERER_EGL_OPT_PKGCONFIG
wayland-egl
)
include(MakeObject)
make_object(
EGL_SHADER
shader/desktop.vert
shader/desktop_rgb.frag
shader/desktop_yuv.frag
shader/cursor.vert
shader/cursor_rgb.frag
shader/cursor_mono.frag
shader/fps.vert
shader/fps.frag
shader/fps_bg.frag
shader/alert.vert
shader/alert.frag
shader/alert_bg.frag
shader/splash_bg.vert
shader/splash_bg.frag
shader/splash_logo.vert
shader/splash_logo.frag
)
add_library(renderer_EGL STATIC
egl.c
debug.c
shader.c
texture.c
model.c
desktop.c
cursor.c
fps.c
draw.c
splash.c
alert.c
${EGL_SHADER_OBJS}
)
target_link_libraries(renderer_EGL
${RENDERER_EGL_PKGCONFIG_LIBRARIES}
${RENDERER_EGL_OPT_PKGCONFIG_LIBRARIES}
lg_common
fonts
)
target_include_directories(renderer_EGL
PRIVATE
src
${EGL_SHADER_INCS}
${RENDERER_EGL_PKGCONFIG_INCLUDE_DIRS}
${RENDERER_EGL_OPT_PKGCONFIG_INCLUDE_DIRS}
)

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -18,8 +18,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "alert.h"
#include "debug.h"
#include "utils.h"
#include "common/debug.h"
#include "common/locking.h"
#include "texture.h"
#include "shader.h"
@@ -28,6 +28,11 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#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;
@@ -43,7 +48,8 @@ struct EGL_Alert
LG_FontBitmap * bmp;
bool ready;
float width, height;
float width , height ;
float bgWidth, bgHeight;
float r, g, b, a;
// uniforms
@@ -51,57 +57,6 @@ struct EGL_Alert
GLint uScreenBG, uSizeBG, uColorBG;
};
static const char vertex_shader[] = "\
#version 300 es\n\
\
layout(location = 0) in vec3 vertexPosition_modelspace;\
layout(location = 1) in vec2 vertexUV;\
\
uniform vec2 screen;\
uniform vec2 size;\
uniform vec4 color;\
\
out highp vec2 uv;\
out highp vec4 c;\
\
void main()\
{\
gl_Position.xyz = vertexPosition_modelspace; \
gl_Position.w = 1.0; \
gl_Position.xy *= screen.xy * size.xy; \
\
uv = vertexUV;\
c = color;\
}\
";
static const char frag_shader[] = "\
#version 300 es\n\
\
in highp vec2 uv;\
out highp vec4 color;\
\
uniform sampler2D sampler1;\
\
void main()\
{\
color = texture(sampler1, uv);\
}\
";
static const char frag_shaderBG[] = "\
#version 300 es\n\
\
in highp vec4 c;\
out highp vec4 color;\
\
void main()\
{\
color = c;\
}\
";
bool egl_alert_init(EGL_Alert ** alert, const LG_Font * font, LG_FontObj fontObj)
{
*alert = (EGL_Alert *)malloc(sizeof(EGL_Alert));
@@ -137,16 +92,16 @@ bool egl_alert_init(EGL_Alert ** alert, const LG_Font * font, LG_FontObj fontObj
if (!egl_shader_compile((*alert)->shader,
vertex_shader, sizeof(vertex_shader),
frag_shader , sizeof(frag_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,
vertex_shader, sizeof(vertex_shader),
frag_shaderBG, sizeof(frag_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;
@@ -219,14 +174,19 @@ void egl_alert_render(EGL_Alert * alert, const float scaleX, const float scaleY)
EGL_PF_BGRA,
alert->bmp->width ,
alert->bmp->height,
alert->bmp->width * alert->bmp->height * alert->bmp->bpp,
alert->bmp->width * alert->bmp->bpp,
false
);
egl_texture_update(alert->texture, alert->bmp->pixels);
alert->width = alert->bmp->width;
alert->height = alert->bmp->height;
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);
@@ -243,16 +203,16 @@ void egl_alert_render(EGL_Alert * alert, const float scaleX, const float scaleY)
// render the background first
egl_shader_use(alert->shaderBG);
glUniform2f(alert->uScreenBG, scaleX , scaleY );
glUniform2f(alert->uSizeBG , alert->width, alert->height);
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 );
glUniform2f(alert->uSize , alert->width, alert->height);
glUniform2i(alert->uSize , alert->width, alert->height);
egl_model_render(alert->model);
glDisable(GL_BLEND);
}
}

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -21,7 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdbool.h>
#include "lg-fonts.h"
#include "interface/font.h"
typedef struct EGL_Alert EGL_Alert;

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -18,8 +18,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "cursor.h"
#include "debug.h"
#include "utils.h"
#include "common/debug.h"
#include "common/locking.h"
#include "texture.h"
#include "shader.h"
@@ -28,13 +28,18 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdlib.h>
#include <string.h>
// these headers are auto generated by cmake
#include "cursor.vert.h"
#include "cursor_rgb.frag.h"
#include "cursor_mono.frag.h"
struct EGL_Cursor
{
LG_Lock lock;
LG_RendererCursor type;
int width;
int height;
int pitch;
int stride;
uint8_t * data;
size_t dataSize;
bool update;
@@ -59,65 +64,6 @@ struct EGL_Cursor
struct EGL_Model * model;
};
static const char vertex_shader[] = "\
#version 300 es\n\
\
layout(location = 0) in vec3 vertexPosition_modelspace;\
layout(location = 1) in vec2 vertexUV;\
\
uniform vec4 mouse;\
\
out highp vec2 uv;\
\
void main()\
{\
gl_Position.xyz = vertexPosition_modelspace;\
gl_Position.w = 1.0;\
\
gl_Position.x += 1.0f;\
gl_Position.y -= 1.0f;\
\
gl_Position.x *= mouse.z;\
gl_Position.y *= mouse.w;\
\
gl_Position.x += mouse.x;\
gl_Position.y -= mouse.y;\
\
uv = vertexUV;\
}\
";
static const char frag_mouse_mono[] = "\
#version 300 es\n\
\
in highp vec2 uv;\
out highp vec4 color;\
\
uniform sampler2D sampler1;\
\
void main()\
{\
highp vec4 tmp = texture(sampler1, uv);\
if (tmp.rgb == vec3(0.0, 0.0, 0.0))\
discard;\
color = tmp;\
}\
";
static const char frag_rgba[] = "\
#version 300 es\n\
\
in highp vec2 uv;\
out highp vec4 color;\
\
uniform sampler2D sampler1;\
\
void main()\
{\
color = texture(sampler1, uv);\
}\
";
bool egl_cursor_init(EGL_Cursor ** cursor)
{
*cursor = (EGL_Cursor *)malloc(sizeof(EGL_Cursor));
@@ -156,8 +102,8 @@ bool egl_cursor_init(EGL_Cursor ** cursor)
if (!egl_shader_compile(
(*cursor)->shader,
vertex_shader, sizeof(vertex_shader),
frag_rgba , sizeof(frag_rgba )))
b_shader_cursor_vert , b_shader_cursor_vert_size,
b_shader_cursor_rgb_frag, b_shader_cursor_rgb_frag_size))
{
DEBUG_ERROR("Failed to compile the cursor shader");
return false;
@@ -165,8 +111,8 @@ bool egl_cursor_init(EGL_Cursor ** cursor)
if (!egl_shader_compile(
(*cursor)->shaderMono,
vertex_shader , sizeof(vertex_shader ),
frag_mouse_mono, sizeof(frag_mouse_mono)))
b_shader_cursor_vert , b_shader_cursor_vert_size,
b_shader_cursor_mono_frag, b_shader_cursor_mono_frag_size))
{
DEBUG_ERROR("Failed to compile the cursor mono shader");
return false;
@@ -204,16 +150,16 @@ void egl_cursor_free(EGL_Cursor ** cursor)
*cursor = NULL;
}
bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type, const int width, const int height, const int pitch, const uint8_t * data)
bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type, const int width, const int height, const int stride, const uint8_t * data)
{
LG_LOCK(cursor->lock);
cursor->type = type;
cursor->width = width;
cursor->height = (type == LG_CURSOR_MONOCHROME ? height / 2 : height);
cursor->pitch = pitch;
cursor->stride = stride;
const size_t size = height * pitch;
const size_t size = height * stride;
if (size > cursor->dataSize)
{
if (cursor->data)
@@ -261,28 +207,14 @@ void egl_cursor_render(EGL_Cursor * cursor)
uint8_t * data = cursor->data;
// tmp buffer for masked colour
uint32_t tmp[cursor->width * cursor->height];
switch(cursor->type)
{
case LG_CURSOR_MASKED_COLOR:
{
for(int i = 0; i < cursor->width * cursor->height; ++i)
{
const uint32_t c = ((uint32_t *)data)[i];
tmp[i] = (c & ~0xFF000000) | (c & 0xFF000000 ? 0x0 : 0xFF000000);
}
data = (uint8_t *)tmp;
// fall through to LG_CURSOR_COLOR
//
// technically we should also create an XOR texture from the data but this
// usage seems very rare in modern software.
}
// fall through
case LG_CURSOR_COLOR:
{
egl_texture_setup(cursor->texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * cursor->height * 4, false);
egl_texture_setup(cursor->texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->stride, false);
egl_texture_update(cursor->texture, data);
egl_model_set_texture(cursor->model, cursor->texture);
break;
@@ -296,8 +228,8 @@ void egl_cursor_render(EGL_Cursor * cursor)
for(int y = 0; y < cursor->height; ++y)
for(int x = 0; x < cursor->width; ++x)
{
const uint8_t * srcAnd = data + (cursor->pitch * y) + (x / 8);
const uint8_t * srcXor = srcAnd + cursor->pitch * cursor->height;
const uint8_t * srcAnd = data + (cursor->stride * y) + (x / 8);
const uint8_t * srcXor = srcAnd + cursor->stride * cursor->height;
const uint8_t mask = 0x80 >> (x % 8);
const uint32_t andMask = (*srcAnd & mask) ? 0xFFFFFFFF : 0xFF000000;
const uint32_t xorMask = (*srcXor & mask) ? 0x00FFFFFF : 0x00000000;
@@ -306,8 +238,8 @@ void egl_cursor_render(EGL_Cursor * cursor)
xor[y * cursor->width + x] = xorMask;
}
egl_texture_setup (cursor->texture , EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * cursor->height * 4, false);
egl_texture_setup (cursor->textureMono, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * cursor->height * 4, false);
egl_texture_setup (cursor->texture , EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false);
egl_texture_setup (cursor->textureMono, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false);
egl_texture_update(cursor->texture , (uint8_t *)and);
egl_texture_update(cursor->textureMono, (uint8_t *)xor);
break;
@@ -316,33 +248,42 @@ void egl_cursor_render(EGL_Cursor * cursor)
LG_UNLOCK(cursor->lock);
}
if (cursor->type == LG_CURSOR_MONOCHROME)
glEnable(GL_BLEND);
switch(cursor->type)
{
glEnable(GL_BLEND);
case LG_CURSOR_MONOCHROME:
{
egl_shader_use(cursor->shader);
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h / 2);
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
egl_model_set_texture(cursor->model, cursor->texture);
egl_model_render(cursor->model);
egl_shader_use(cursor->shader);
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h / 2);
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
egl_model_set_texture(cursor->model, cursor->texture);
egl_model_render(cursor->model);
egl_shader_use(cursor->shaderMono);
glUniform4f(cursor->uMousePosMono, cursor->x, cursor->y, cursor->w, cursor->h / 2);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
egl_model_set_texture(cursor->model, cursor->textureMono);
egl_model_render(cursor->model);
break;
}
egl_shader_use(cursor->shaderMono);
glUniform4f(cursor->uMousePosMono, cursor->x, cursor->y, cursor->w, cursor->h / 2);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
egl_model_set_texture(cursor->model, cursor->textureMono);
egl_model_render(cursor->model);
case LG_CURSOR_COLOR:
{
egl_shader_use(cursor->shader);
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
egl_model_render(cursor->model);
break;
}
glDisable(GL_BLEND);
case LG_CURSOR_MASKED_COLOR:
{
egl_shader_use(cursor->shaderMono);
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
egl_model_render(cursor->model);
break;
}
}
else
{
glEnable(GL_BLEND);
egl_shader_use(cursor->shader);
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h);
glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);
egl_model_render(cursor->model);
glDisable(GL_BLEND);
}
}
glDisable(GL_BLEND);
}

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -20,14 +20,15 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#pragma once
#include <stdbool.h>
#include "lg-renderer.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);
bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type, const int width, const int height, const int pitch, const uint8_t * data);
bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type, const int width, const int height, const int stride, const uint8_t * data);
void egl_cursor_set_size (EGL_Cursor * cursor, const float x, const float y);
void egl_cursor_set_state(EGL_Cursor * cursor, const bool visible, const float x, const float y);
void egl_cursor_render (EGL_Cursor * cursor);

View File

@@ -0,0 +1,58 @@
/*
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 <GL/gl.h>
#include <stdarg.h>
#include <stdio.h>
void egl_debug_printf(char * format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
GLenum error = glGetError();
switch(error)
{
case GL_NO_ERROR:
fprintf(stderr, " (GL_NO_ERROR)\n");
break;
case GL_INVALID_ENUM:
fprintf(stderr, " (GL_INVALID_ENUM)\n");
break;
case GL_INVALID_VALUE:
fprintf(stderr, " (GL_INVALID_VALUE)\n");
break;
case GL_INVALID_OPERATION:
fprintf(stderr, " (GL_INVALID_OPERATION)\n");
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
fprintf(stderr, " (GL_INVALID_FRAMEBUFFER_OPERATION)\n");
break;
case GL_OUT_OF_MEMORY:
fprintf(stderr, " (GL_OUT_OF_MEMORY)\n");
break;
}
}

View File

@@ -0,0 +1,27 @@
/*
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 <common/debug.h>
#define EGL_DEBUG_PRINT(type, fmt, ...) do {egl_debug_printf(type " %20s:%-4u | %-30s | " fmt, STRIPPATH(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__);} while (0)
#define EGL_ERROR(fmt, ...) EGL_DEBUG_PRINT("[E]", fmt, ##__VA_ARGS__)
void egl_debug_printf(char * format, ...);

View File

@@ -0,0 +1,261 @@
/*
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 "desktop.h"
#include "common/debug.h"
#include "common/option.h"
#include "common/locking.h"
#include "texture.h"
#include "shader.h"
#include "model.h"
#include <stdlib.h>
#include <string.h>
#include "interface/app.h"
// these headers are auto generated by cmake
#include "desktop.vert.h"
#include "desktop_rgb.frag.h"
#include "desktop_yuv.frag.h"
struct DesktopShader
{
EGL_Shader * shader;
GLint uDesktopPos;
GLint uDesktopSize;
GLint uNearest;
GLint uNV, uNVGain;
};
struct EGL_Desktop
{
void * egl;
EGL_Texture * texture;
struct DesktopShader * shader; // the active shader
EGL_Model * model;
// internals
int width, height;
// shader instances
struct DesktopShader shader_generic;
struct DesktopShader shader_yuv;
// night vision
KeybindHandle kbNV;
int nvMax;
int nvGain;
};
// forwards
void egl_desktop_toggle_nv(SDL_Scancode key, void * opaque);
static bool egl_init_desktop_shader(
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))
return false;
if (!egl_shader_compile(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->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" );
return true;
}
bool egl_desktop_init(void * egl, EGL_Desktop ** desktop)
{
*desktop = (EGL_Desktop *)malloc(sizeof(EGL_Desktop));
if (!*desktop)
{
DEBUG_ERROR("Failed to malloc EGL_Desktop");
return false;
}
memset(*desktop, 0, sizeof(EGL_Desktop));
if (!egl_texture_init(&(*desktop)->texture))
{
DEBUG_ERROR("Failed to initialize the desktop texture");
return false;
}
if (!egl_init_desktop_shader(
&(*desktop)->shader_generic,
b_shader_desktop_vert , b_shader_desktop_vert_size,
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size))
{
DEBUG_ERROR("Failed to initialize the generic desktop shader");
return false;
}
if (!egl_init_desktop_shader(
&(*desktop)->shader_yuv,
b_shader_desktop_vert , b_shader_desktop_vert_size,
b_shader_desktop_yuv_frag, b_shader_desktop_yuv_frag_size))
{
DEBUG_ERROR("Failed to initialize the yuv desktop shader");
return false;
}
if (!egl_model_init(&(*desktop)->model))
{
DEBUG_ERROR("Failed to initialize the desktop model");
return false;
}
egl_model_set_default((*desktop)->model);
egl_model_set_texture((*desktop)->model, (*desktop)->texture);
(*desktop)->egl = egl;
(*desktop)->kbNV = app_register_keybind(SDL_SCANCODE_N, egl_desktop_toggle_nv, *desktop);
(*desktop)->nvMax = option_get_int("egl", "nvGainMax");
(*desktop)->nvGain = option_get_int("egl", "nvGain" );
return true;
}
void egl_desktop_toggle_nv(SDL_Scancode key, void * opaque)
{
EGL_Desktop * desktop = (EGL_Desktop *)opaque;
if (desktop->nvGain++ == desktop->nvMax)
desktop->nvGain = 0;
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);
}
void egl_desktop_free(EGL_Desktop ** desktop)
{
if (!*desktop)
return;
egl_texture_free(&(*desktop)->texture );
egl_shader_free (&(*desktop)->shader_generic.shader);
egl_shader_free (&(*desktop)->shader_yuv.shader );
egl_model_free (&(*desktop)->model );
app_release_keybind(&(*desktop)->kbNV);
free(*desktop);
*desktop = NULL;
}
bool egl_desktop_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const FrameBuffer * frame)
{
if (sourceChanged)
{
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_YUV420:
pixFmt = EGL_PF_YUV420;
desktop->shader = &desktop->shader_yuv;
break;
default:
DEBUG_ERROR("Unsupported frame format");
return false;
}
desktop->width = format.width;
desktop->height = format.height;
if (!egl_texture_setup(
desktop->texture,
pixFmt,
format.width,
format.height,
format.pitch,
true // streaming texture
))
{
DEBUG_ERROR("Failed to setup the desktop texture");
return false;
}
}
if (!egl_texture_update_from_frame(desktop->texture, frame))
return false;
enum EGL_TexStatus status;
if ((status = egl_texture_process(desktop->texture)) != EGL_TEX_STATUS_OK)
{
if (status != EGL_TEX_STATUS_NOTREADY)
DEBUG_ERROR("Failed to process the desktop texture");
}
return true;
}
bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y, const float scaleX, const float scaleY, const bool nearest)
{
if (!desktop->shader)
return false;
const struct DesktopShader * shader = desktop->shader;
egl_shader_use(shader->shader);
glUniform4f(shader->uDesktopPos , x, y, scaleX, scaleY);
glUniform1i(shader->uNearest , nearest ? 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);
egl_model_render(desktop->model);
return true;
}

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -21,13 +21,12 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdbool.h>
#include "lg-renderer.h"
#include "interface/renderer.h"
typedef struct EGL_Desktop EGL_Desktop;
bool egl_desktop_init(EGL_Desktop ** desktop);
bool egl_desktop_init(void * egl, EGL_Desktop ** desktop);
void egl_desktop_free(EGL_Desktop ** desktop);
bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const uint8_t * data);
bool egl_desktop_perform_update(EGL_Desktop * desktop, const bool sourceChanged);
void egl_desktop_render(EGL_Desktop * desktop, const float x, const float y, const float scaleX, const float scaleY);
bool egl_desktop_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const FrameBuffer * frame);
bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y, const float scaleX, const float scaleY, const bool nearest);

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -17,22 +17,30 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "lg-renderer.h"
#include "interface/renderer.h"
#include "debug.h"
#include "common/debug.h"
#include "common/option.h"
#include "common/sysinfo.h"
#include "common/time.h"
#include "common/locking.h"
#include "utils.h"
#include "lg-fonts.h"
#include "dynamic/fonts.h"
#include <SDL2/SDL_syswm.h>
#include <SDL2/SDL_egl.h>
#include "egl/model.h"
#include "egl/shader.h"
#include "egl/desktop.h"
#include "egl/cursor.h"
#include "egl/fps.h"
#include "egl/splash.h"
#include "egl/alert.h"
#if defined(SDL_VIDEO_DRIVER_WAYLAND)
#include <wayland-egl.h>
#endif
#include "model.h"
#include "shader.h"
#include "desktop.h"
#include "cursor.h"
#include "fps.h"
#include "splash.h"
#include "alert.h"
#define SPLASH_FADE_TIME 1000000
#define ALERT_TIMEOUT 2000000
@@ -42,22 +50,16 @@ struct Options
bool vsync;
};
static struct Options defaultOptions =
{
.vsync = false
};
struct Inst
{
LG_RendererParams params;
struct Options opt;
Display * xDisplay;
Window xWindow;
EGLDisplay display;
EGLConfig configs;
EGLSurface surface;
EGLContext context;
EGLNativeWindowType nativeWind;
EGLDisplay display;
EGLConfig configs;
EGLSurface surface;
EGLContext context, frameContext;
EGL_Desktop * desktop; // the desktop
EGL_Cursor * cursor; // the mouse cursor
@@ -66,7 +68,7 @@ struct Inst
EGL_Alert * alert; // the alert display
LG_RendererFormat format;
bool sourceChanged;
bool start;
uint64_t waitFadeTime;
bool waitDone;
@@ -82,7 +84,10 @@ struct Inst
float scaleX , scaleY;
float splashRatio;
float screenScaleX, screenScaleY;
bool useNearest;
bool cursorVisible;
int cursorX , cursorY;
float mouseWidth , mouseHeight;
float mouseScaleX, mouseScaleY;
@@ -91,6 +96,46 @@ struct Inst
};
static struct Option egl_options[] =
{
{
.module = "egl",
.name = "vsync",
.description = "Enable vsync",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
{
.module = "egl",
.name = "doubleBuffer",
.description = "Enable double buffering",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
{
.module = "egl",
.name = "multisample",
.description = "Enable Multisampling",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "egl",
.name = "nvGainMax",
.description = "The maximum night vision gain",
.type = OPTION_TYPE_INT,
.value.x_int = 1
},
{
.module = "egl",
.name = "nvGain",
.description = "The initial night vision gain at startup",
.type = OPTION_TYPE_INT,
.value.x_int = 0
},
{0}
};
void update_mouse_shape(struct Inst * this);
const char * egl_get_name()
@@ -98,6 +143,11 @@ const char * egl_get_name()
return "EGL";
}
void egl_setup()
{
option_register(egl_options);
}
bool egl_create(void ** opaque, const LG_RendererParams params)
{
// create our local storage
@@ -111,8 +161,9 @@ bool egl_create(void ** opaque, const LG_RendererParams params)
// safe off parameteres and init our default option values
struct Inst * this = (struct Inst *)*opaque;
memcpy(&this->params, &params , sizeof(LG_RendererParams));
memcpy(&this->opt , &defaultOptions, sizeof(struct Options ));
memcpy(&this->params, &params, sizeof(LG_RendererParams));
this->opt.vsync = option_get_bool("egl", "vsync");
this->translateX = 0;
this->translateY = 0;
@@ -122,7 +173,7 @@ bool egl_create(void ** opaque, const LG_RendererParams params)
this->screenScaleY = 1.0f;
this->font = LG_Fonts[0];
if (!this->font->create(&this->fontObj, NULL, 14))
if (!this->font->create(&this->fontObj, NULL, 16))
{
DEBUG_ERROR("Failed to create a font instance");
return false;
@@ -133,12 +184,27 @@ bool egl_create(void ** opaque, const LG_RendererParams params)
bool egl_initialize(void * opaque, Uint32 * sdlFlags)
{
const bool doubleBuffer = option_get_bool("egl", "doubleBuffer");
DEBUG_INFO("Double buffering is %s", doubleBuffer ? "on" : "off");
*sdlFlags = SDL_WINDOW_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_DOUBLEBUFFER , doubleBuffer ? 1 : 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
if (option_get_bool("egl", "multisample"))
{
int maxSamples = sysinfo_gfx_max_multisample();
if (maxSamples > 1)
{
if (maxSamples > 4)
maxSamples = 4;
DEBUG_INFO("Multisampling enabled, max samples: %d", maxSamples);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, maxSamples);
}
}
return true;
}
@@ -155,9 +221,20 @@ void egl_deinitialize(void * opaque)
egl_splash_free (&this->splash);
egl_alert_free (&this->alert );
LG_LOCK_FREE(this->lock);
free(this);
}
void egl_on_restart(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
eglDestroyContext(this->display, this->frameContext);
this->frameContext = NULL;
this->start = false;
}
void egl_on_resize(void * opaque, const int width, const int height, const LG_RendererRect destRect)
{
struct Inst * this = (struct Inst *)opaque;
@@ -186,6 +263,13 @@ void egl_on_resize(void * opaque, const int width, const int height, const LG_Re
this->splashRatio = (float)width / (float)height;
this->screenScaleX = 1.0f / width;
this->screenScaleY = 1.0f / height;
egl_cursor_set_state(
this->cursor,
this->cursorVisible,
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleX,
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleY
);
}
bool egl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data)
@@ -207,47 +291,70 @@ bool egl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, const int
return true;
}
bool egl_on_mouse_event(void * opaque, const bool visible , const int x, const int y)
bool egl_on_mouse_event(void * opaque, const bool visible, const int x, const int y)
{
struct Inst * this = (struct Inst *)opaque;
this->cursorVisible = visible;
this->cursorX = x;
this->cursorY = y;
egl_cursor_set_state(
this->cursor,
visible,
(((float)x * this->mouseScaleX) - 1.0f) * this->scaleX,
(((float)y * this->mouseScaleY) - 1.0f) * this->scaleY
this->cursorVisible,
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleX,
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleY
);
return true;
}
bool egl_on_frame_event(void * opaque, const LG_RendererFormat format, const uint8_t * data)
bool egl_on_frame_event(void * opaque, const LG_RendererFormat format, const FrameBuffer * frame)
{
struct Inst * this = (struct Inst *)opaque;
this->sourceChanged = (
this->sourceChanged ||
const bool sourceChanged = (
this->format.type != format.type ||
this->format.width != format.width ||
this->format.height != format.height ||
this->format.pitch != format.pitch
);
if (this->sourceChanged)
if (sourceChanged)
memcpy(&this->format, &format, sizeof(LG_RendererFormat));
if (!egl_desktop_prepare_update(this->desktop, this->sourceChanged, format, data))
this->useNearest = this->width < format.width || this->height < format.height;
/* this event runs in a second thread so we need to init it here */
if (!this->frameContext)
{
DEBUG_INFO("Failed to prepare to update the desktop");
static EGLint attrs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
if (!(this->frameContext = eglCreateContext(this->display, this->configs, this->context, attrs)))
{
DEBUG_ERROR("Failed to create the frame context");
return false;
}
if (!eglMakeCurrent(this->display, EGL_NO_SURFACE, EGL_NO_SURFACE, this->frameContext))
{
DEBUG_ERROR("Failed to make the frame context current");
return false;
}
}
if (!egl_desktop_update(this->desktop, sourceChanged, format, frame))
{
DEBUG_INFO("Failed to to update the desktop");
return false;
}
if (!this->waitFadeTime)
this->waitFadeTime = microtime() + SPLASH_FADE_TIME;
this->start = true;
return true;
}
void egl_on_alert(void * opaque, const LG_RendererAlert alert, const char * message, bool ** closeFlag)
void egl_on_alert(void * opaque, const LG_MsgAlert alert, const char * message, bool ** closeFlag)
{
struct Inst * this = (struct Inst *)opaque;
@@ -294,10 +401,52 @@ bool egl_render_startup(void * opaque, SDL_Window * window)
return false;
}
this->xDisplay = wminfo.info.x11.display;
this->xWindow = wminfo.info.x11.window;
const char *client_exts = eglQueryString(NULL, EGL_EXTENSIONS);
DEBUG_INFO("Supported extensions: %s", client_exts);
bool useNative = false;
if (strstr(client_exts, "EGL_KHR_platform_base") != NULL)
useNative = true;
DEBUG_INFO("use native: %s", useNative ? "true" : "false");
switch(wminfo.subsystem)
{
case SDL_SYSWM_X11:
{
if (!useNative)
this->display = eglGetPlatformDisplay(EGL_PLATFORM_X11_KHR, wminfo.info.x11.display, NULL);
else
{
EGLNativeDisplayType native = (EGLNativeDisplayType)wminfo.info.x11.display;
this->display = eglGetDisplay(native);
}
this->nativeWind = (EGLNativeWindowType)wminfo.info.x11.window;
break;
}
#if defined(SDL_VIDEO_DRIVER_WAYLAND)
case SDL_SYSWM_WAYLAND:
{
int width, height;
SDL_GetWindowSize(window, &width, &height);
if (!useNative)
this->display = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wminfo.info.wl.display, NULL);
else
{
EGLNativeDisplayType native = (EGLNativeDisplayType)wminfo.info.wl.display;
this->display = eglGetDisplay(native);
}
this->nativeWind = (EGLNativeWindowType)wl_egl_window_create(wminfo.info.wl.surface, width, height);
break;
}
#endif
default:
DEBUG_ERROR("Unsupported subsystem");
return false;
}
this->display = eglGetDisplay((EGLNativeDisplayType)this->xDisplay);
if (this->display == EGL_NO_DISPLAY)
{
DEBUG_ERROR("eglGetDisplay failed");
@@ -312,10 +461,10 @@ bool egl_render_startup(void * opaque, SDL_Window * window)
EGLint attr[] =
{
EGL_BUFFER_SIZE , 16,
EGL_BUFFER_SIZE , 32,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SAMPLE_BUFFERS , 1,
EGL_SAMPLES , 8,
EGL_SAMPLES , 4,
EGL_NONE
};
@@ -326,7 +475,7 @@ bool egl_render_startup(void * opaque, SDL_Window * window)
return false;
}
this->surface = eglCreateWindowSurface(this->display, this->configs, this->xWindow, NULL);
this->surface = eglCreateWindowSurface(this->display, this->configs, this->nativeWind, NULL);
if (this->surface == EGL_NO_SURFACE)
{
DEBUG_ERROR("Failed to create EGL surface (eglError: 0x%x)", eglGetError());
@@ -354,7 +503,7 @@ bool egl_render_startup(void * opaque, SDL_Window * window)
eglSwapInterval(this->display, this->opt.vsync ? 1 : 0);
if (!egl_desktop_init(&this->desktop))
if (!egl_desktop_init(this, &this->desktop))
{
DEBUG_ERROR("Failed to initialize the desktop");
return false;
@@ -394,8 +543,15 @@ bool egl_render(void * opaque, SDL_Window * window)
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
egl_desktop_render(this->desktop, this->translateX, this->translateY, this->scaleX, this->scaleY);
egl_cursor_render(this->cursor);
if (this->start && egl_desktop_render(this->desktop,
this->translateX, this->translateY,
this->scaleX , this->scaleY ,
this->useNearest))
{
if (!this->waitFadeTime)
this->waitFadeTime = microtime() + SPLASH_FADE_TIME;
egl_cursor_render(this->cursor);
}
if (!this->waitDone)
{
@@ -413,7 +569,14 @@ bool egl_render(void * opaque, SDL_Window * window)
a = 1.0f / SPLASH_FADE_TIME * delta;
}
}
egl_splash_render(this->splash, a, this->splashRatio);
if (!this->waitDone)
egl_splash_render(this->splash, a, this->splashRatio);
}
else
{
if (!this->start)
egl_splash_render(this->splash, 1.0f, this->splashRatio);
}
if (this->showAlert)
@@ -432,15 +595,6 @@ bool egl_render(void * opaque, SDL_Window * window)
egl_fps_render(this->fps, this->screenScaleX, this->screenScaleY);
eglSwapBuffers(this->display, this->surface);
// defer texture uploads until after the flip to avoid stalling
if (!egl_desktop_perform_update(this->desktop, this->sourceChanged))
{
DEBUG_ERROR("Failed to perform the desktop update");
return false;
}
this->sourceChanged = false;
return true;
}
@@ -453,33 +607,14 @@ void egl_update_fps(void * opaque, const float avgUPS, const float avgFPS)
egl_fps_update(this->fps, avgUPS, avgFPS);
}
static void handle_opt_vsync(void * opaque, const char *value)
{
struct Inst * this = (struct Inst *)opaque;
if (!this)
return;
this->opt.vsync = LG_RendererValueToBool(value);
}
static LG_RendererOpt egl_options[] =
{
{
.name = "vsync",
.desc ="Enable or disable vsync [default: enabled]",
.validator = LG_RendererValidatorBool,
.handler = handle_opt_vsync
}
};
struct LG_Renderer LGR_EGL =
{
.create = egl_create,
.get_name = egl_get_name,
.options = egl_options,
.option_count = LGR_OPTION_COUNT(egl_options),
.setup = egl_setup,
.create = egl_create,
.initialize = egl_initialize,
.deinitialize = egl_deinitialize,
.on_restart = egl_on_restart,
.on_resize = egl_on_resize,
.on_mouse_shape = egl_on_mouse_shape,
.on_mouse_event = egl_on_mouse_event,
@@ -488,4 +623,4 @@ struct LG_Renderer LGR_EGL =
.render_startup = egl_render_startup,
.render = egl_render,
.update_fps = egl_update_fps
};
};

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -18,7 +18,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "fps.h"
#include "debug.h"
#include "common/debug.h"
#include "utils.h"
#include "texture.h"
@@ -28,6 +28,11 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#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;
@@ -39,6 +44,7 @@ struct EGL_FPS
EGL_Model * model;
bool ready;
int iwidth, iheight;
float width, height;
// uniforms
@@ -46,57 +52,6 @@ struct EGL_FPS
GLint uScreenBG, uSizeBG;
};
static const char vertex_shader[] = "\
#version 300 es\n\
\
layout(location = 0) in vec3 vertexPosition_modelspace;\
layout(location = 1) in vec2 vertexUV;\
\
uniform vec2 screen;\
uniform vec2 size;\
\
out highp vec2 uv;\
\
void main()\
{\
gl_Position.xyz = vertexPosition_modelspace; \
gl_Position.w = 1.0; \
gl_Position.xy *= screen.xy * size.xy; \
gl_Position.x -= 1.0 - (screen.x * size.x);\
gl_Position.y += 1.0 - (screen.y * size.y);\
gl_Position.x += screen.x * 10.0; \
gl_Position.y -= screen.y * 10.0; \
\
uv = vertexUV;\
}\
";
static const char frag_shader[] = "\
#version 300 es\n\
\
in highp vec2 uv;\
out highp vec4 color;\
\
uniform sampler2D sampler1;\
\
void main()\
{\
color = texture(sampler1, uv);\
}\
";
static const char frag_shaderBG[] = "\
#version 300 es\n\
\
out highp vec4 color;\
\
void main()\
{\
color = vec4(0.0, 0.0, 1.0, 0.5);\
}\
";
bool egl_fps_init(EGL_FPS ** fps, const LG_Font * font, LG_FontObj fontObj)
{
*fps = (EGL_FPS *)malloc(sizeof(EGL_FPS));
@@ -131,16 +86,16 @@ bool egl_fps_init(EGL_FPS ** fps, const LG_Font * font, LG_FontObj fontObj)
if (!egl_shader_compile((*fps)->shader,
vertex_shader, sizeof(vertex_shader),
frag_shader , sizeof(frag_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,
vertex_shader, sizeof(vertex_shader),
frag_shaderBG, sizeof(frag_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;
@@ -190,14 +145,22 @@ void egl_fps_update(EGL_FPS * fps, const float avgFPS, const float renderFPS)
return;
}
egl_texture_setup(
fps->texture,
EGL_PF_BGRA,
bmp->width ,
bmp->height,
bmp->width * bmp->height * bmp->bpp,
false
);
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
);
}
egl_texture_update
(
@@ -205,10 +168,7 @@ void egl_fps_update(EGL_FPS * fps, const float avgFPS, const float renderFPS)
bmp->pixels
);
fps->width = bmp->width;
fps->height = bmp->height;
fps->ready = true;
fps->font->release(fps->fontObj, bmp);
}
@@ -233,4 +193,4 @@ void egl_fps_render(EGL_FPS * fps, const float scaleX, const float scaleY)
egl_model_render(fps->model);
glDisable(GL_BLEND);
}
}

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -21,7 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdbool.h>
#include "lg-fonts.h"
#include "interface/font.h"
typedef struct EGL_FPS EGL_FPS;

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -21,7 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "shader.h"
#include "texture.h"
#include "debug.h"
#include "common/debug.h"
#include "utils.h"
#include "ll.h"

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -18,7 +18,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "shader.h"
#include "debug.h"
#include "common/debug.h"
#include "utils.h"
#include <stdlib.h>

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -20,6 +20,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <GL/gl.h>

View File

@@ -0,0 +1,12 @@
#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

@@ -0,0 +1,24 @@
#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

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

View File

@@ -0,0 +1,25 @@
#version 300 es
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;
uniform vec4 mouse;
out highp vec2 uv;
void main()
{
gl_Position.xyz = vertexPosition_modelspace;
gl_Position.w = 1.0;
gl_Position.x += 1.0f;
gl_Position.y -= 1.0f;
gl_Position.x *= mouse.z;
gl_Position.y *= mouse.w;
gl_Position.x += mouse.x;
gl_Position.y -= mouse.y;
uv = vertexUV;
}

View File

@@ -0,0 +1,14 @@
#version 300 es
in highp vec2 uv;
out highp vec4 color;
uniform sampler2D sampler1;
void main()
{
highp vec4 tmp = texture(sampler1, uv);
if (tmp.rgb == vec3(0.0, 0.0, 0.0))
discard;
color = tmp;
}

View File

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

View File

@@ -0,0 +1,20 @@
#version 300 es
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;
uniform vec4 position;
out highp vec2 uv;
void main()
{
gl_Position.xyz = vertexPosition_modelspace;
gl_Position.w = 1.0;
gl_Position.x -= position.x;
gl_Position.y -= position.y;
gl_Position.x *= position.z;
gl_Position.y *= position.w;
uv = vertexUV;
}

View File

@@ -0,0 +1,27 @@
#version 300 es
in highp vec2 uv;
out highp vec4 color;
uniform sampler2D sampler1;
uniform int nearest;
uniform highp vec2 size;
uniform int nv;
uniform highp float nvGain;
void main()
{
if(nearest == 1)
color = texture(sampler1, uv);
else
color = texelFetch(sampler1, ivec2(uv * size), 0);
if (nv == 1)
{
highp float lumi = 1.0 - (0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b);
color *= 1.0 + lumi;
color *= nvGain;
}
}

View File

@@ -0,0 +1,53 @@
#version 300 es
in highp vec2 uv;
out highp vec4 color;
uniform int nearest;
uniform highp vec2 size;
uniform int nv;
uniform highp float nvGain;
uniform sampler2D sampler1;
uniform sampler2D sampler2;
uniform sampler2D sampler3;
void main()
{
highp vec4 yuv;
if(nearest == 1)
{
yuv = vec4(
texture(sampler1, uv).r,
texture(sampler2, uv).r,
texture(sampler3, uv).r,
1.0
);
}
else
{
highp ivec2 px = ivec2(uv * size);
yuv = vec4(
texelFetch(sampler1, px, 0).r,
texelFetch(sampler2, px, 0).r,
texelFetch(sampler3, px, 0).r,
1.0
);
}
highp mat4 yuv_to_rgb = mat4(
1.0, 0.0 , 1.402, -0.701,
1.0, -0.344, -0.714, 0.529,
1.0, 1.772, 0.0 , -0.886,
1.0, 1.0 , 1.0 , 1.0
);
color = yuv * yuv_to_rgb;
if (nv == 1)
{
highp float lumi = 1.0 - (0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b);
color *= 1.0 + lumi;
color *= nvGain;
}
}

View File

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

View File

@@ -0,0 +1,22 @@
#version 300 es
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;
uniform vec2 screen;
uniform vec2 size;
out highp vec2 uv;
void main()
{
gl_Position.xyz = vertexPosition_modelspace;
gl_Position.w = 1.0;
gl_Position.xy *= screen.xy * size.xy;
gl_Position.x -= 1.0 - (screen.x * size.x);
gl_Position.y += 1.0 - (screen.y * size.y);
gl_Position.x += screen.x * 10.0;
gl_Position.y -= screen.y * 10.0;
uv = vertexUV;
}

View File

@@ -0,0 +1,8 @@
#version 300 es
out highp vec4 color;
void main()
{
color = vec4(0.0, 0.0, 1.0, 0.5);
}

View File

@@ -0,0 +1,13 @@
#version 300 es
in highp vec3 pos;
in highp float a;
out highp vec4 color;
uniform sampler2D sampler1;
void main()
{
highp float d = 1.0 - sqrt(pos.x * pos.x + pos.y * pos.y) / 2.0;
color = vec4(0.234375 * d, 0.015625f * d, 0.425781f * d, a);
}

View File

@@ -0,0 +1,17 @@
#version 300 es
layout(location = 0) in vec3 vertexPosition_modelspace;
uniform float alpha;
out highp vec3 pos;
out highp float a;
void main()
{
gl_Position.xyz = vertexPosition_modelspace;
gl_Position.w = 1.0;
pos = vertexPosition_modelspace;
a = alpha;
}

View File

@@ -0,0 +1,11 @@
#version 300 es
out highp vec4 color;
in highp float a;
uniform sampler2D sampler1;
void main()
{
color = vec4(1.0, 1.0, 1.0, a);
}

View File

@@ -0,0 +1,16 @@
#version 300 es
layout(location = 0) in vec3 vertexPosition_modelspace;
uniform vec2 scale;
out highp float a;
void main()
{
gl_Position.xyz = vertexPosition_modelspace;
gl_Position.y *= scale.y;
gl_Position.w = 1.0;
a = scale.x;
}

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -18,7 +18,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "splash.h"
#include "debug.h"
#include "common/debug.h"
#include "utils.h"
#include "draw.h"
@@ -31,6 +31,12 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <string.h>
#include <math.h>
// these headers are auto generated by cmake
#include "splash_bg.vert.h"
#include "splash_bg.frag.h"
#include "splash_logo.vert.h"
#include "splash_logo.frag.h"
struct EGL_Splash
{
EGL_Shader * bgShader;
@@ -44,75 +50,6 @@ struct EGL_Splash
GLint uScale;
};
static const char vertex_bgShader[] = "\
#version 300 es\n\
\
layout(location = 0) in vec3 vertexPosition_modelspace;\
\
uniform float alpha;\
\
out highp vec3 pos; \
out highp float a; \
\
void main()\
{\
gl_Position.xyz = vertexPosition_modelspace; \
gl_Position.w = 1.0; \
\
pos = vertexPosition_modelspace; \
a = alpha; \
}\
";
static const char frag_bgShader[] = "\
#version 300 es\n\
\
in highp vec3 pos;\
in highp float a;\
out highp vec4 color;\
\
uniform sampler2D sampler1;\
\
void main()\
{\
highp float d = 1.0 - sqrt(pos.x * pos.x + pos.y * pos.y) / 2.0; \
color = vec4(0.234375 * d, 0.015625f * d, 0.425781f * d, a); \
}\
";
static const char vertex_logoShader[] = "\
#version 300 es\n\
\
layout(location = 0) in vec3 vertexPosition_modelspace;\
\
uniform vec2 scale;\
\
out highp float a; \
\
void main()\
{\
gl_Position.xyz = vertexPosition_modelspace; \
gl_Position.y *= scale.y; \
gl_Position.w = 1.0; \
\
a = scale.x; \
}\
";
static const char frag_logoShader[] = "\
#version 300 es\n\
\
out highp vec4 color;\
in highp float a;\
\
uniform sampler2D sampler1;\
\
void main()\
{\
color = vec4(1.0, 1.0, 1.0, a);\
}\
";
bool egl_splash_init(EGL_Splash ** splash)
{
*splash = (EGL_Splash *)malloc(sizeof(EGL_Splash));
@@ -131,8 +68,8 @@ bool egl_splash_init(EGL_Splash ** splash)
}
if (!egl_shader_compile((*splash)->bgShader,
vertex_bgShader, sizeof(vertex_bgShader),
frag_bgShader , sizeof(frag_bgShader )))
b_shader_splash_bg_vert, b_shader_splash_bg_vert_size,
b_shader_splash_bg_frag, b_shader_splash_bg_frag_size))
{
DEBUG_ERROR("Failed to compile the splash bgShader");
return false;
@@ -155,8 +92,8 @@ bool egl_splash_init(EGL_Splash ** splash)
}
if (!egl_shader_compile((*splash)->logoShader,
vertex_logoShader, sizeof(vertex_logoShader),
frag_logoShader , sizeof(frag_logoShader )))
b_shader_splash_logo_vert, b_shader_splash_logo_vert_size,
b_shader_splash_logo_frag, b_shader_splash_logo_frag_size))
{
DEBUG_ERROR("Failed to compile the splash logoShader");
return false;

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -21,8 +21,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdbool.h>
#include "lg-fonts.h"
typedef struct EGL_Splash EGL_Splash;
bool egl_splash_init(EGL_Splash ** splash);

View File

@@ -0,0 +1,479 @@
/*
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 "texture.h"
#include "common/debug.h"
#include "common/framebuffer.h"
#include "debug.h"
#include "utils.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdatomic.h>
#include <SDL2/SDL_egl.h>
/* this must be a multiple of 2 */
#define TEXTURE_COUNT 2
struct Tex
{
GLuint t[3];
bool hasPBO;
GLuint pbo;
void * map;
GLsync sync;
};
struct TexState
{
_Atomic(uint8_t) w, u, s, d;
};
struct EGL_Texture
{
enum EGL_PixelFormat pixFmt;
size_t width, height, stride;
size_t bpp;
bool streaming;
bool ready;
int planeCount;
GLuint samplers[3];
size_t planes [3][3];
GLintptr offsets [3];
GLenum intFormat;
GLenum format;
GLenum dataType;
size_t pboBufferSize;
struct TexState state;
int textureCount;
struct Tex tex[TEXTURE_COUNT];
};
bool egl_texture_init(EGL_Texture ** texture)
{
*texture = (EGL_Texture *)malloc(sizeof(EGL_Texture));
if (!*texture)
{
DEBUG_ERROR("Failed to malloc EGL_Texture");
return false;
}
memset(*texture, 0, sizeof(EGL_Texture));
return true;
}
void egl_texture_free(EGL_Texture ** texture)
{
if (!*texture)
return;
if ((*texture)->planeCount > 0)
glDeleteSamplers((*texture)->planeCount, (*texture)->samplers);
for(int i = 0; i < (*texture)->textureCount; ++i)
{
struct Tex * t = &(*texture)->tex[i];
if (t->hasPBO)
{
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, t->pbo);
if ((*texture)->tex[i].map)
{
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
(*texture)->tex[i].map = NULL;
}
glDeleteBuffers(1, &t->pbo);
if (t->sync)
glDeleteSync(t->sync);
}
if ((*texture)->planeCount > 0)
glDeleteTextures((*texture)->planeCount, t->t);
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
free(*texture);
*texture = NULL;
}
static bool egl_texture_map(EGL_Texture * texture, uint8_t i)
{
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->tex[i].pbo);
texture->tex[i].map = glMapBufferRange(
GL_PIXEL_UNPACK_BUFFER,
0,
texture->pboBufferSize,
GL_MAP_WRITE_BIT |
GL_MAP_UNSYNCHRONIZED_BIT |
GL_MAP_INVALIDATE_BUFFER_BIT
);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
if (!texture->tex[i].map)
{
EGL_ERROR("glMapBufferRange failed for %d of %lu bytes", i, texture->pboBufferSize);
return false;
}
return true;
}
static void egl_texture_unmap(EGL_Texture * texture, uint8_t i)
{
if (!texture->tex[i].map)
return;
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->tex[i].pbo);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
texture->tex[i].map = NULL;
}
bool egl_texture_setup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt, size_t width, size_t height, size_t stride, bool streaming)
{
int planeCount;
if (texture->streaming)
{
for(int i = 0; i < texture->textureCount; ++i)
{
egl_texture_unmap(texture, i);
if (texture->tex[i].hasPBO)
{
glDeleteBuffers(1, &texture->tex[i].pbo);
texture->tex[i].hasPBO = false;
}
}
}
texture->pixFmt = pixFmt;
texture->width = width;
texture->height = height;
texture->stride = stride;
texture->streaming = streaming;
texture->textureCount = streaming ? TEXTURE_COUNT : 1;
texture->ready = false;
atomic_store_explicit(&texture->state.w, 0, memory_order_relaxed);
atomic_store_explicit(&texture->state.u, 0, memory_order_relaxed);
atomic_store_explicit(&texture->state.s, 0, memory_order_relaxed);
atomic_store_explicit(&texture->state.d, 0, memory_order_relaxed);
switch(pixFmt)
{
case EGL_PF_BGRA:
planeCount = 1;
texture->bpp = 4;
texture->format = GL_BGRA;
texture->planes[0][0] = width;
texture->planes[0][1] = height;
texture->planes[0][2] = stride / 4;
texture->offsets[0] = 0;
texture->intFormat = GL_BGRA;
texture->dataType = GL_UNSIGNED_BYTE;
texture->pboBufferSize = height * stride;
break;
case EGL_PF_RGBA:
planeCount = 1;
texture->bpp = 4;
texture->format = GL_RGBA;
texture->planes[0][0] = width;
texture->planes[0][1] = height;
texture->planes[0][2] = stride / 4;
texture->offsets[0] = 0;
texture->intFormat = GL_BGRA;
texture->dataType = GL_UNSIGNED_BYTE;
texture->pboBufferSize = height * stride;
break;
case EGL_PF_RGBA10:
planeCount = 1;
texture->bpp = 4;
texture->format = GL_RGBA;
texture->planes[0][0] = width;
texture->planes[0][1] = height;
texture->planes[0][2] = stride / 4;
texture->offsets[0] = 0;
texture->intFormat = GL_RGB10_A2;
texture->dataType = GL_UNSIGNED_INT_2_10_10_10_REV;
texture->pboBufferSize = height * stride;
break;
case EGL_PF_YUV420:
planeCount = 3;
texture->bpp = 4;
texture->format = GL_RED;
texture->planes[0][0] = width;
texture->planes[0][1] = height;
texture->planes[0][2] = stride;
texture->planes[1][0] = width / 2;
texture->planes[1][1] = height / 2;
texture->planes[1][2] = stride / 2;
texture->planes[2][0] = width / 2;
texture->planes[2][1] = height / 2;
texture->planes[2][2] = stride / 2;
texture->offsets[0] = 0;
texture->offsets[1] = stride * height;
texture->offsets[2] = texture->offsets[1] + (texture->offsets[1] / 4);
texture->dataType = GL_UNSIGNED_BYTE;
texture->pboBufferSize = texture->offsets[2] + (texture->offsets[1] / 4);
break;
default:
DEBUG_ERROR("Unsupported pixel format");
return false;
}
if (planeCount > texture->planeCount)
{
if (texture->planeCount > 0)
glDeleteSamplers(texture->planeCount, texture->samplers);
for(int i = 0; i < texture->textureCount; ++i)
{
if (texture->planeCount > 0)
glDeleteTextures(texture->planeCount, texture->tex[i].t);
glGenTextures(planeCount, texture->tex[i].t);
}
glGenSamplers(planeCount, texture->samplers);
for(int p = 0; p < planeCount; ++p)
{
glSamplerParameteri(texture->samplers[p], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(texture->samplers[p], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(texture->samplers[p], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glSamplerParameteri(texture->samplers[p], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
}
texture->planeCount = planeCount;
}
for(int i = 0; i < texture->textureCount; ++i)
{
for(int p = 0; p < planeCount; ++p)
{
glBindTexture(GL_TEXTURE_2D, texture->tex[i].t[p]);
glTexImage2D(GL_TEXTURE_2D, 0, texture->intFormat, texture->planes[p][0],
texture->planes[p][1], 0, texture->format, texture->dataType, NULL);
}
}
glBindTexture(GL_TEXTURE_2D, 0);
if (!streaming)
return true;
for(int i = 0; i < texture->textureCount; ++i)
{
glGenBuffers(1, &texture->tex[i].pbo);
texture->tex[i].hasPBO = true;
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->tex[i].pbo);
glBufferStorage(
GL_PIXEL_UNPACK_BUFFER,
texture->pboBufferSize,
NULL,
GL_MAP_WRITE_BIT
);
}
return true;
}
static void egl_warn_slow()
{
static bool warnDone = false;
if (!warnDone)
{
warnDone = true;
DEBUG_BREAK();
DEBUG_WARN("The guest is providing updates faster then your computer can display them");
DEBUG_WARN("This is a hardware limitation, expect microstutters & frame skips");
DEBUG_BREAK();
}
}
bool egl_texture_update(EGL_Texture * texture, const uint8_t * buffer)
{
if (texture->streaming)
{
const uint8_t sw =
atomic_load_explicit(&texture->state.w, memory_order_acquire);
if (atomic_load_explicit(&texture->state.u, memory_order_acquire) == (uint8_t)(sw + 1))
{
egl_warn_slow();
return true;
}
const uint8_t t = sw % TEXTURE_COUNT;
if (!egl_texture_map(texture, t))
return EGL_TEX_STATUS_ERROR;
memcpy(texture->tex[t].map, buffer, texture->pboBufferSize);
atomic_fetch_add_explicit(&texture->state.w, 1, memory_order_release);
egl_texture_unmap(texture, t);
}
else
{
for(int p = 0; p < texture->planeCount; ++p)
{
glBindTexture(GL_TEXTURE_2D, texture->tex[0].t[p]);
glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->planes[p][0]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->planes[p][0], texture->planes[p][1],
texture->format, texture->dataType, buffer + texture->offsets[p]);
}
glBindTexture(GL_TEXTURE_2D, 0);
}
return true;
}
bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer * frame)
{
if (!texture->streaming)
return false;
const uint8_t sw =
atomic_load_explicit(&texture->state.w, memory_order_acquire);
if (atomic_load_explicit(&texture->state.u, memory_order_acquire) == (uint8_t)(sw + 1))
{
egl_warn_slow();
return true;
}
const uint8_t t = sw % TEXTURE_COUNT;
if (!egl_texture_map(texture, t))
return EGL_TEX_STATUS_ERROR;
framebuffer_read(
frame,
texture->tex[t].map,
texture->stride,
texture->height,
texture->width,
texture->bpp,
texture->stride
);
atomic_fetch_add_explicit(&texture->state.w, 1, memory_order_release);
egl_texture_unmap(texture, t);
return true;
}
enum EGL_TexStatus egl_texture_process(EGL_Texture * texture)
{
if (!texture->streaming)
return EGL_TEX_STATUS_OK;
const uint8_t su =
atomic_load_explicit(&texture->state.u, memory_order_acquire);
const uint8_t nextu = su + 1;
if (
su == atomic_load_explicit(&texture->state.w, memory_order_acquire) ||
nextu == atomic_load_explicit(&texture->state.s, memory_order_acquire) ||
nextu == atomic_load_explicit(&texture->state.d, memory_order_acquire))
return texture->ready ? EGL_TEX_STATUS_OK : EGL_TEX_STATUS_NOTREADY;
/* update the texture */
const uint8_t t = su % TEXTURE_COUNT;
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->tex[t].pbo);
for(int p = 0; p < texture->planeCount; ++p)
{
glBindTexture(GL_TEXTURE_2D, texture->tex[t].t[p]);
glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->planes[p][2]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->planes[p][0], texture->planes[p][1],
texture->format, texture->dataType, (const void *)texture->offsets[p]);
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
/* create a fence to prevent usage before the update is complete */
texture->tex[t].sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
/* we must flush to ensure the sync is in the command buffer */
glFlush();
texture->ready = true;
atomic_fetch_add_explicit(&texture->state.u, 1, memory_order_release);
return EGL_TEX_STATUS_OK;
}
enum EGL_TexStatus egl_texture_bind(EGL_Texture * texture)
{
uint8_t ss = atomic_load_explicit(&texture->state.s, memory_order_acquire);
uint8_t sd = atomic_load_explicit(&texture->state.d, memory_order_acquire);
if (texture->streaming)
{
if (!texture->ready)
return EGL_TEX_STATUS_NOTREADY;
const uint8_t t = ss % TEXTURE_COUNT;
if (texture->tex[t].sync != 0)
{
switch(glClientWaitSync(texture->tex[t].sync, 0, 20000000)) // 20ms
{
case GL_ALREADY_SIGNALED:
case GL_CONDITION_SATISFIED:
glDeleteSync(texture->tex[t].sync);
texture->tex[t].sync = 0;
ss = atomic_fetch_add_explicit(&texture->state.s, 1,
memory_order_release) + 1;
break;
case GL_TIMEOUT_EXPIRED:
break;
case GL_WAIT_FAILED:
case GL_INVALID_VALUE:
glDeleteSync(texture->tex[t].sync);
texture->tex[t].sync = 0;
EGL_ERROR("glClientWaitSync failed");
return EGL_TEX_STATUS_ERROR;
}
}
if (ss != sd && ss != (uint8_t)(sd + 1))
sd = atomic_fetch_add_explicit(&texture->state.d, 1,
memory_order_release) + 1;
}
const uint8_t t = sd % TEXTURE_COUNT;
for(int i = 0; i < texture->planeCount; ++i)
{
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, texture->tex[t].t[i]);
glBindSampler(i, texture->samplers[i]);
}
return EGL_TEX_STATUS_OK;
}
int egl_texture_count(EGL_Texture * texture)
{
return texture->planeCount;
}

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -21,6 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdbool.h>
#include "shader.h"
#include "common/framebuffer.h"
#include <GL/gl.h>
@@ -34,10 +35,19 @@ enum EGL_PixelFormat
EGL_PF_YUV420
};
enum EGL_TexStatus
{
EGL_TEX_STATUS_NOTREADY,
EGL_TEX_STATUS_OK,
EGL_TEX_STATUS_ERROR
};
bool egl_texture_init(EGL_Texture ** tex);
void egl_texture_free(EGL_Texture ** tex);
bool egl_texture_setup (EGL_Texture * texture, enum EGL_PixelFormat pixfmt, size_t width, size_t height, size_t bufferSize, bool streaming);
bool egl_texture_update(EGL_Texture * texture, const uint8_t * buffer);
void egl_texture_bind (EGL_Texture * texture);
int egl_texture_count (EGL_Texture * texture);
bool egl_texture_setup (EGL_Texture * texture, enum EGL_PixelFormat pixfmt, size_t width, size_t height, size_t stride, bool streaming);
bool egl_texture_update (EGL_Texture * texture, const uint8_t * buffer);
bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer * frame);
enum EGL_TexStatus egl_texture_process(EGL_Texture * texture);
enum EGL_TexStatus egl_texture_bind (EGL_Texture * texture);
int egl_texture_count (EGL_Texture * texture);

View File

@@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.0)
project(renderer_Opengl LANGUAGES C)
find_package(PkgConfig)
pkg_check_modules(RENDERER_OPENGL_PKGCONFIG REQUIRED
gl
glu
)
add_library(renderer_OpenGL STATIC
opengl.c
)
target_link_libraries(renderer_OpenGL
${RENDERER_OPENGL_PKGCONFIG_LIBRARIES}
lg_common
decoders
fonts
)
target_include_directories(renderer_OpenGL
PRIVATE
src
${RENDERER_OPENGL_PKGCONFIG_INCLUDE_DIRS}
)

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -17,7 +17,7 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "lg-renderer.h"
#include "interface/renderer.h"
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
@@ -30,10 +30,11 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <GL/glu.h>
#include <GL/glx.h>
#include "debug.h"
#include "utils.h"
#include "lg-decoders.h"
#include "lg-fonts.h"
#include "common/debug.h"
#include "common/option.h"
#include "common/framebuffer.h"
#include "common/locking.h"
#include "dynamic/fonts.h"
#include "ll.h"
#define BUFFER_COUNT 2
@@ -47,7 +48,40 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#define FADE_TIME 1000000
struct Options
static struct Option opengl_options[] =
{
{
.module = "opengl",
.name = "mipmap",
.description = "Enable mipmapping",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "opengl",
.name = "vsync",
.description = "Enable vsync",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "opengl",
.name = "preventBuffer",
.description = "Prevent the driver from buffering frames",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "opengl",
.name = "amdPinnedMem",
.description = "Use GL_AMD_pinned_memory if it is available",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{0}
};
struct OpenGL_Options
{
bool mipmap;
bool vsync;
@@ -55,14 +89,6 @@ struct Options
bool amdPinnedMem;
};
static struct Options defaultOptions =
{
.mipmap = true,
.vsync = true,
.preventBuffer = true,
.amdPinnedMem = true,
};
struct Alert
{
bool ready;
@@ -76,8 +102,8 @@ struct Alert
struct Inst
{
LG_RendererParams params;
struct Options opt;
LG_RendererParams params;
struct OpenGL_Options opt;
bool amdPinnedMemSupport;
bool renderStarted;
@@ -91,14 +117,14 @@ struct Inst
const LG_Font * font;
LG_FontObj fontObj, alertFontObj;
LG_Lock formatLock;
LG_RendererFormat format;
GLuint intFormat;
GLuint vboFormat;
GLuint dataFormat;
size_t texSize;
const LG_Decoder* decoder;
void * decoderData;
LG_Lock formatLock;
LG_RendererFormat format;
GLuint intFormat;
GLuint vboFormat;
GLuint dataFormat;
size_t texSize;
size_t texPos;
const FrameBuffer * frame;
uint64_t drawStart;
bool hasBuffers;
@@ -115,7 +141,6 @@ struct Inst
bool hasTextures, hasFrames;
GLuint frames[BUFFER_COUNT];
GLsync fences[BUFFER_COUNT];
void * decoderFrames[BUFFER_COUNT];
GLuint textures[TEXTURE_COUNT];
struct ll * alerts;
int alertList;
@@ -145,8 +170,15 @@ struct Inst
static bool _check_gl_error(unsigned int line, const char * name);
#define check_gl_error(name) _check_gl_error(__LINE__, name)
enum ConfigStatus
{
CONFIG_STATUS_OK,
CONFIG_STATUS_ERROR,
CONFIG_STATUS_NOOP
};
static void deconfigure(struct Inst * this);
static bool configure(struct Inst * this, SDL_Window *window);
static enum ConfigStatus configure(struct Inst * this, SDL_Window *window);
static void update_mouse_shape(struct Inst * this, bool * newShape);
static bool draw_frame(struct Inst * this);
static void draw_mouse(struct Inst * this);
@@ -157,6 +189,11 @@ const char * opengl_get_name()
return "OpenGL";
}
static void opengl_setup()
{
option_register(opengl_options);
}
bool opengl_create(void ** opaque, const LG_RendererParams params)
{
// create our local storage
@@ -169,8 +206,13 @@ bool opengl_create(void ** opaque, const LG_RendererParams params)
memset(*opaque, 0, sizeof(struct Inst));
struct Inst * this = (struct Inst *)*opaque;
memcpy(&this->params, &params , sizeof(LG_RendererParams));
memcpy(&this->opt , &defaultOptions, sizeof(struct Options ));
memcpy(&this->params, &params, sizeof(LG_RendererParams));
this->opt.mipmap = option_get_bool("opengl", "mipmap" );
this->opt.vsync = option_get_bool("opengl", "vsync" );
this->opt.preventBuffer = option_get_bool("opengl", "preventBuffer");
this->opt.amdPinnedMem = option_get_bool("opengl", "amdPinnedMem" );
LG_LOCK_INIT(this->formatLock);
LG_LOCK_INIT(this->syncLock );
@@ -253,6 +295,12 @@ void opengl_deinitialize(void * opaque)
free(this);
}
void opengl_on_restart(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
this->waiting = true;
}
void opengl_on_resize(void * opaque, const int width, const int height, const LG_RendererRect destRect)
{
struct Inst * this = (struct Inst *)opaque;
@@ -327,7 +375,7 @@ bool opengl_on_mouse_event(void * opaque, const bool visible, const int x, const
return false;
}
bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const uint8_t * data)
bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const FrameBuffer * frame)
{
struct Inst * this = (struct Inst *)opaque;
if (!this)
@@ -359,12 +407,7 @@ bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const
LG_UNLOCK(this->formatLock);
LG_LOCK(this->syncLock);
if (!this->decoder->decode(this->decoderData, data, format.pitch))
{
DEBUG_ERROR("decode returned failure");
LG_UNLOCK(this->syncLock);
return false;
}
this->frame = frame;
this->frameUpdate = true;
LG_UNLOCK(this->syncLock);
@@ -377,7 +420,7 @@ bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const
return true;
}
void opengl_on_alert(void * opaque, const LG_RendererAlert alert, const char * message, bool ** closeFlag)
void opengl_on_alert(void * opaque, const LG_MsgAlert alert, const char * message, bool ** closeFlag)
{
struct Inst * this = (struct Inst *)opaque;
struct Alert * a = malloc(sizeof(struct Alert));
@@ -520,10 +563,18 @@ bool opengl_render(void * opaque, SDL_Window * window)
if (!this)
return false;
if (configure(this, window))
if (!draw_frame(this))
switch(configure(this, window))
{
case CONFIG_STATUS_ERROR:
DEBUG_ERROR("configure failed");
return false;
case CONFIG_STATUS_NOOP :
case CONFIG_STATUS_OK :
if (!draw_frame(this))
return false;
}
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
@@ -770,79 +821,15 @@ static void render_wait(struct Inst * this)
glDisable(GL_BLEND);
}
static void handle_opt_mipmap(void * opaque, const char *value)
{
struct Inst * this = (struct Inst *)opaque;
if (!this)
return;
this->opt.mipmap = LG_RendererValueToBool(value);
}
static void handle_opt_vsync(void * opaque, const char *value)
{
struct Inst * this = (struct Inst *)opaque;
if (!this)
return;
this->opt.vsync = LG_RendererValueToBool(value);
}
static void handle_opt_prevent_buffer(void * opaque, const char *value)
{
struct Inst * this = (struct Inst *)opaque;
if (!this)
return;
this->opt.preventBuffer = LG_RendererValueToBool(value);
}
static void handle_opt_amd_pinned_mem(void * opaque, const char *value)
{
struct Inst * this = (struct Inst *)opaque;
if (!this)
return;
this->opt.amdPinnedMem = LG_RendererValueToBool(value);
}
static LG_RendererOpt opengl_options[] =
{
{
.name = "mipmap",
.desc = "Enable or disable mipmapping [default: enabled]",
.validator = LG_RendererValidatorBool,
.handler = handle_opt_mipmap
},
{
.name = "vsync",
.desc ="Enable or disable vsync [default: enabled]",
.validator = LG_RendererValidatorBool,
.handler = handle_opt_vsync
},
{
.name = "preventBuffer",
.desc = "Prevent the driver from buffering frames [default: disabled]",
.validator = LG_RendererValidatorBool,
.handler = handle_opt_prevent_buffer
},
{
.name = "amdPinnedMem",
.desc = "Use GL_AMD_pinned_memory if it is available [default: enabled]",
.validator = LG_RendererValidatorBool,
.handler = handle_opt_amd_pinned_mem
}
};
const LG_Renderer LGR_OpenGL =
{
.get_name = opengl_get_name,
.options = opengl_options,
.option_count = LGR_OPTION_COUNT(opengl_options),
.setup = opengl_setup,
.create = opengl_create,
.initialize = opengl_initialize,
.deinitialize = opengl_deinitialize,
.on_restart = opengl_on_restart,
.on_resize = opengl_on_resize,
.on_mouse_shape = opengl_on_mouse_shape,
.on_mouse_event = opengl_on_mouse_event,
@@ -864,13 +851,13 @@ static bool _check_gl_error(unsigned int line, const char * name)
return true;
}
static bool configure(struct Inst * this, SDL_Window *window)
static enum ConfigStatus configure(struct Inst * this, SDL_Window *window)
{
LG_LOCK(this->formatLock);
if (!this->reconfigure)
{
LG_UNLOCK(this->formatLock);
return this->configured;
return CONFIG_STATUS_NOOP;
}
if (this->configured)
@@ -879,142 +866,101 @@ static bool configure(struct Inst * this, SDL_Window *window)
switch(this->format.type)
{
case FRAME_TYPE_BGRA:
case FRAME_TYPE_RGBA:
case FRAME_TYPE_RGBA10:
this->decoder = &LGD_NULL;
break;
case FRAME_TYPE_YUV420:
this->decoder = &LGD_YUV420;
break;
default:
DEBUG_ERROR("Unknown/unsupported compression type");
return false;
}
DEBUG_INFO("Using decoder: %s", this->decoder->name);
if (!this->decoder->create(&this->decoderData))
{
DEBUG_ERROR("Failed to create the decoder");
return false;
}
if (!this->decoder->initialize(
this->decoderData,
this->format,
window))
{
DEBUG_ERROR("Failed to initialize decoder");
return false;
}
switch(this->decoder->get_out_format(this->decoderData))
{
case LG_OUTPUT_BGRA:
this->intFormat = GL_RGBA8;
this->vboFormat = GL_BGRA;
this->dataFormat = GL_UNSIGNED_BYTE;
break;
case LG_OUTPUT_RGBA:
case FRAME_TYPE_RGBA:
this->intFormat = GL_RGBA8;
this->vboFormat = GL_RGBA;
this->dataFormat = GL_UNSIGNED_BYTE;
break;
case LG_OUTPUT_RGBA10:
case FRAME_TYPE_RGBA10:
this->intFormat = GL_RGB10_A2;
this->vboFormat = GL_RGBA;
this->dataFormat = GL_UNSIGNED_INT_2_10_10_10_REV;
break;
case LG_OUTPUT_YUV420:
// fixme
this->intFormat = GL_RGBA8;
this->vboFormat = GL_BGRA;
this->dataFormat = GL_UNSIGNED_BYTE;
break;
default:
DEBUG_ERROR("Format not supported");
LG_UNLOCK(this->formatLock);
return false;
DEBUG_ERROR("Unknown/unsupported compression type");
return CONFIG_STATUS_ERROR;
}
// calculate the texture size in bytes
this->texSize =
this->format.height *
this->decoder->get_frame_pitch(this->decoderData);
this->texSize = this->format.height * this->format.pitch;
this->texPos = 0;
// generate the pixel unpack buffers if the decoder isn't going to do it for us
if (!this->decoder->has_gl)
glGenBuffers(BUFFER_COUNT, this->vboID);
if (check_gl_error("glGenBuffers"))
{
glGenBuffers(BUFFER_COUNT, this->vboID);
if (check_gl_error("glGenBuffers"))
{
LG_UNLOCK(this->formatLock);
return false;
}
this->hasBuffers = true;
LG_UNLOCK(this->formatLock);
return false;
}
this->hasBuffers = true;
if (this->amdPinnedMemSupport)
{
const int pagesize = getpagesize();
this->texPixels[0] = memalign(pagesize, this->texSize * BUFFER_COUNT);
memset(this->texPixels[0], 0, this->texSize * BUFFER_COUNT);
for(int i = 1; i < BUFFER_COUNT; ++i)
this->texPixels[i] = this->texPixels[0] + this->texSize;
if (this->amdPinnedMemSupport)
{
const int pagesize = getpagesize();
for(int i = 0; i < BUFFER_COUNT; ++i)
for(int i = 0; i < BUFFER_COUNT; ++i)
{
this->texPixels[i] = aligned_alloc(pagesize, this->texSize);
if (!this->texPixels[i])
{
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, this->vboID[i]);
if (check_gl_error("glBindBuffer"))
{
LG_UNLOCK(this->formatLock);
return false;
}
glBufferData(
GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD,
this->texSize,
this->texPixels[i],
GL_STREAM_DRAW);
if (check_gl_error("glBufferData"))
{
LG_UNLOCK(this->formatLock);
return false;
}
DEBUG_ERROR("Failed to allocate memory for texture");
return CONFIG_STATUS_ERROR;
}
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0);
}
else
{
for(int i = 0; i < BUFFER_COUNT; ++i)
memset(this->texPixels[i], 0, this->texSize);
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, this->vboID[i]);
if (check_gl_error("glBindBuffer"))
{
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[i]);
if (check_gl_error("glBindBuffer"))
{
LG_UNLOCK(this->formatLock);
return false;
}
glBufferData(
GL_PIXEL_UNPACK_BUFFER,
this->texSize,
NULL,
GL_STREAM_DRAW
);
if (check_gl_error("glBufferData"))
{
LG_UNLOCK(this->formatLock);
return false;
}
LG_UNLOCK(this->formatLock);
return CONFIG_STATUS_ERROR;
}
glBufferData(
GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD,
this->texSize,
this->texPixels[i],
GL_STREAM_DRAW
);
if (check_gl_error("glBufferData"))
{
LG_UNLOCK(this->formatLock);
return CONFIG_STATUS_ERROR;
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0);
}
else
{
for(int i = 0; i < BUFFER_COUNT; ++i)
{
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[i]);
if (check_gl_error("glBindBuffer"))
{
LG_UNLOCK(this->formatLock);
return CONFIG_STATUS_ERROR;
}
glBufferData(
GL_PIXEL_UNPACK_BUFFER,
this->texSize,
NULL,
GL_STREAM_DRAW
);
if (check_gl_error("glBufferData"))
{
LG_UNLOCK(this->formatLock);
return CONFIG_STATUS_ERROR;
}
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
// create the frame textures
@@ -1022,7 +968,7 @@ static bool configure(struct Inst * this, SDL_Window *window)
if (check_gl_error("glGenTextures"))
{
LG_UNLOCK(this->formatLock);
return false;
return CONFIG_STATUS_ERROR;
}
this->hasFrames = true;
@@ -1033,7 +979,7 @@ static bool configure(struct Inst * this, SDL_Window *window)
if (check_gl_error("glBindTexture"))
{
LG_UNLOCK(this->formatLock);
return false;
return CONFIG_STATUS_ERROR;
}
glTexImage2D(
@@ -1050,29 +996,14 @@ static bool configure(struct Inst * this, SDL_Window *window)
if (check_gl_error("glTexImage2D"))
{
LG_UNLOCK(this->formatLock);
return false;
return CONFIG_STATUS_ERROR;
}
if (this->decoder->has_gl)
{
if (!this->decoder->init_gl_texture(
this->decoderData,
GL_TEXTURE_2D,
this->frames[i],
&this->decoderFrames[i]))
{
LG_UNLOCK(this->formatLock);
return false;
}
}
else
{
// configure the texture
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
// configure the texture
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// create the display lists
glNewList(this->texList + i, GL_COMPILE);
@@ -1096,7 +1027,7 @@ static bool configure(struct Inst * this, SDL_Window *window)
this->reconfigure = false;
LG_UNLOCK(this->formatLock);
return true;
return CONFIG_STATUS_OK;
}
static void deconfigure(struct Inst * this)
@@ -1112,19 +1043,6 @@ static void deconfigure(struct Inst * this)
if (this->hasFrames)
{
if (this->decoder->has_gl)
{
for(int i = 0; i < BUFFER_COUNT; ++i)
{
if (this->decoderFrames[i])
this->decoder->free_gl_texture(
this->decoderData,
this->decoderFrames[i]
);
this->decoderFrames[i] = NULL;
}
}
glDeleteTextures(BUFFER_COUNT, this->frames);
this->hasFrames = false;
}
@@ -1137,9 +1055,6 @@ static void deconfigure(struct Inst * this)
if (this->amdPinnedMemSupport)
{
if (this->texPixels[0])
free(this->texPixels[0]);
for(int i = 0; i < BUFFER_COUNT; ++i)
{
if (this->fences[i])
@@ -1147,14 +1062,13 @@ static void deconfigure(struct Inst * this)
glDeleteSync(this->fences[i]);
this->fences[i] = NULL;
}
this->texPixels[i] = NULL;
}
}
if (this->decoderData)
{
this->decoder->destroy(this->decoderData);
this->decoderData = NULL;
if (this->texPixels[i])
{
free(this->texPixels[i]);
this->texPixels[i] = NULL;
}
}
}
this->configured = false;
@@ -1307,6 +1221,23 @@ static void update_mouse_shape(struct Inst * this, bool * newShape)
LG_UNLOCK(this->mouseLock);
}
static bool opengl_buffer_fn(void * opaque, const void * data, size_t size)
{
struct Inst * this = (struct Inst *)opaque;
// update the buffer, this performs a DMA transfer if possible
glBufferSubData(
GL_PIXEL_UNPACK_BUFFER,
this->texPos,
size,
data
);
check_gl_error("glBufferSubData");
this->texPos += size;
return true;
}
static bool draw_frame(struct Inst * this)
{
LG_LOCK(this->syncLock);
@@ -1323,96 +1254,75 @@ static bool draw_frame(struct Inst * this)
LG_UNLOCK(this->syncLock);
LG_LOCK(this->formatLock);
if (this->decoder->has_gl)
if (glIsSync(this->fences[this->texIndex]))
{
if (!this->decoder->update_gl_texture(
this->decoderData,
this->decoderFrames[this->texIndex]
))
switch(glClientWaitSync(this->fences[this->texIndex], 0, GL_TIMEOUT_IGNORED))
{
LG_UNLOCK(this->formatLock);
DEBUG_ERROR("Failed to update the texture from the decoder");
return false;
case GL_ALREADY_SIGNALED:
break;
case GL_CONDITION_SATISFIED:
DEBUG_WARN("Had to wait for the sync");
break;
case GL_TIMEOUT_EXPIRED:
DEBUG_WARN("Timeout expired, DMA transfers are too slow!");
break;
case GL_WAIT_FAILED:
DEBUG_ERROR("Wait failed %s", gluErrorString(glGetError()));
break;
}
glDeleteSync(this->fences[this->texIndex]);
this->fences[this->texIndex] = NULL;
}
else
glBindTexture(GL_TEXTURE_2D, this->frames[this->texIndex]);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[this->texIndex]);
const int bpp = this->format.bpp / 8;
glPixelStorei(GL_UNPACK_ALIGNMENT , bpp);
glPixelStorei(GL_UNPACK_ROW_LENGTH, this->format.width);
this->texPos = 0;
framebuffer_read_fn(
this->frame,
this->format.height,
this->format.width,
bpp,
this->format.pitch,
opengl_buffer_fn,
this
);
// update the texture
glTexSubImage2D(
GL_TEXTURE_2D,
0,
0,
0,
this->format.width ,
this->format.height,
this->vboFormat,
this->dataFormat,
(void*)0
);
if (check_gl_error("glTexSubImage2D"))
{
if (glIsSync(this->fences[this->texIndex]))
{
switch(glClientWaitSync(this->fences[this->texIndex], 0, GL_TIMEOUT_IGNORED))
{
case GL_ALREADY_SIGNALED:
break;
case GL_CONDITION_SATISFIED:
DEBUG_WARN("Had to wait for the sync");
break;
case GL_TIMEOUT_EXPIRED:
DEBUG_WARN("Timeout expired, DMA transfers are too slow!");
break;
case GL_WAIT_FAILED:
DEBUG_ERROR("Wait failed %s", gluErrorString(glGetError()));
break;
}
glDeleteSync(this->fences[this->texIndex]);
this->fences[this->texIndex] = NULL;
}
const uint8_t * data = this->decoder->get_buffer(this->decoderData);
if (!data)
{
LG_UNLOCK(this->formatLock);
DEBUG_ERROR("Failed to get the buffer from the decoder");
return false;
}
glBindTexture(GL_TEXTURE_2D, this->frames[this->texIndex]);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[this->texIndex]);
glPixelStorei(GL_UNPACK_ALIGNMENT , 4);
glPixelStorei(GL_UNPACK_ROW_LENGTH ,
this->decoder->get_frame_stride(this->decoderData)
DEBUG_ERROR("texIndex: %u, width: %u, height: %u, vboFormat: %x, texSize: %lu",
this->texIndex, this->format.width, this->format.height, this->vboFormat, this->texSize
);
// update the buffer, this performs a DMA transfer if possible
glBufferSubData(
GL_PIXEL_UNPACK_BUFFER,
0,
this->texSize,
data
);
check_gl_error("glBufferSubData");
// update the texture
glTexSubImage2D(
GL_TEXTURE_2D,
0,
0,
0,
this->format.width ,
this->format.height,
this->vboFormat,
this->dataFormat,
(void*)0
);
if (check_gl_error("glTexSubImage2D"))
{
DEBUG_ERROR("texIndex: %u, width: %u, height: %u, vboFormat: %x, texSize: %lu",
this->texIndex, this->format.width, this->format.height, this->vboFormat, this->texSize
);
}
// set a fence so we don't overwrite a buffer in use
this->fences[this->texIndex] =
glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
// unbind the buffer
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
// set a fence so we don't overwrite a buffer in use
this->fences[this->texIndex] =
glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
// unbind the buffer
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
const bool mipmap = this->opt.mipmap && (
(this->format.width > this->destRect.w) ||
(this->format.height > this->destRect.h));
@@ -1445,4 +1355,4 @@ static void draw_mouse(struct Inst * this)
glTranslatef(this->mousePos.x, this->mousePos.y, 0.0f);
glCallList(this->mouseList);
glPopMatrix();
}
}

View File

@@ -1,278 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 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 "desktop.h"
#include "debug.h"
#include "utils.h"
#include "texture.h"
#include "shader.h"
#include "model.h"
#include <stdlib.h>
#include <string.h>
struct EGL_Desktop
{
EGL_Texture * texture;
EGL_Shader * shader; // the active shader
EGL_Model * model;
// shader instances
EGL_Shader * shader_generic;
EGL_Shader * shader_yuv;
// uniforms
GLint uDesktopPos;
// internals
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
size_t frameSize;
const uint8_t * data;
bool update;
};
static const char vertex_shader[] = "\
#version 300 es\n\
\
layout(location = 0) in vec3 vertexPosition_modelspace;\
layout(location = 1) in vec2 vertexUV;\
\
uniform vec4 position;\
\
out highp vec2 uv;\
\
void main()\
{\
gl_Position.xyz = vertexPosition_modelspace; \
gl_Position.w = 1.0; \
gl_Position.x -= position.x; \
gl_Position.y -= position.y; \
gl_Position.x *= position.z; \
gl_Position.y *= position.w; \
\
uv = vertexUV;\
}\
";
static const char frag_generic[] = "\
#version 300 es\n\
\
in highp vec2 uv;\
out highp vec4 color;\
\
uniform sampler2D sampler1;\
\
void main()\
{\
color = texture(sampler1, uv);\
}\
";
static const char frag_yuv[] = "\
#version 300 es\n\
\
in highp vec2 uv;\
out highp vec4 color;\
\
uniform sampler2D sampler1;\
uniform sampler2D sampler2;\
uniform sampler2D sampler3;\
\
void main()\
{\
highp vec4 yuv = vec4(\
texture(sampler1, uv).r,\
texture(sampler2, uv).r,\
texture(sampler3, uv).r,\
1.0\
);\
\
highp mat4 yuv_to_rgb = mat4(\
1.0, 0.0 , 1.402, -0.701,\
1.0, -0.344, -0.714, 0.529,\
1.0, 1.772, 0.0 , -0.886,\
1.0, 1.0 , 1.0 , 1.0\
);\
\
color = yuv * yuv_to_rgb;\
}\
";
bool egl_desktop_init(EGL_Desktop ** desktop)
{
*desktop = (EGL_Desktop *)malloc(sizeof(EGL_Desktop));
if (!*desktop)
{
DEBUG_ERROR("Failed to malloc EGL_Desktop");
return false;
}
memset(*desktop, 0, sizeof(EGL_Desktop));
if (!egl_texture_init(&(*desktop)->texture))
{
DEBUG_ERROR("Failed to initialize the desktop texture");
return false;
}
if (!egl_shader_init(&(*desktop)->shader_generic))
{
DEBUG_ERROR("Failed to initialize the generic desktop shader");
return false;
}
if (!egl_shader_init(&(*desktop)->shader_yuv))
{
DEBUG_ERROR("Failed to initialize the yuv desktop shader");
return false;
}
if (!egl_shader_compile((*desktop)->shader_generic,
vertex_shader, sizeof(vertex_shader),
frag_generic , sizeof(frag_generic)))
{
DEBUG_ERROR("Failed to compile the generic desktop shader");
return false;
}
if (!egl_shader_compile((*desktop)->shader_yuv,
vertex_shader, sizeof(vertex_shader),
frag_yuv , sizeof(frag_yuv )))
{
DEBUG_ERROR("Failed to compile the yuv desktop shader");
return false;
}
if (!egl_model_init(&(*desktop)->model))
{
DEBUG_ERROR("Failed to initialize the desktop model");
return false;
}
egl_model_set_default((*desktop)->model);
egl_model_set_texture((*desktop)->model, (*desktop)->texture);
return true;
}
void egl_desktop_free(EGL_Desktop ** desktop)
{
if (!*desktop)
return;
egl_texture_free(&(*desktop)->texture );
egl_shader_free (&(*desktop)->shader_generic);
egl_shader_free (&(*desktop)->shader_yuv );
egl_model_free (&(*desktop)->model );
free(*desktop);
*desktop = NULL;
}
bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const uint8_t * data)
{
if (sourceChanged)
{
switch(format.type)
{
case FRAME_TYPE_BGRA:
desktop->pixFmt = EGL_PF_BGRA;
desktop->shader = desktop->shader_generic;
desktop->frameSize = format.height * format.pitch;
break;
case FRAME_TYPE_RGBA:
desktop->pixFmt = EGL_PF_RGBA;
desktop->shader = desktop->shader_generic;
desktop->frameSize = format.height * format.pitch;
break;
case FRAME_TYPE_RGBA10:
desktop->pixFmt = EGL_PF_RGBA10;
desktop->shader = desktop->shader_generic;
desktop->frameSize = format.height * format.pitch;
break;
case FRAME_TYPE_YUV420:
desktop->pixFmt = EGL_PF_YUV420;
desktop->shader = desktop->shader_yuv;
desktop->frameSize = format.width * format.height * 3 / 2;
break;
default:
DEBUG_ERROR("Unsupported frame format");
return false;
}
desktop->width = format.width;
desktop->height = format.height;
}
desktop->data = data;
desktop->update = true;
return true;
}
bool egl_desktop_perform_update(EGL_Desktop * desktop, const bool sourceChanged)
{
if (sourceChanged)
{
if (desktop->shader)
desktop->uDesktopPos = egl_shader_get_uniform_location(desktop->shader, "position");
if (!egl_texture_setup(
desktop->texture,
desktop->pixFmt,
desktop->width,
desktop->height,
desktop->frameSize,
true // streaming texture
))
{
DEBUG_ERROR("Failed to setup the desktop texture");
return false;
}
}
if (!desktop->update)
return true;
if (!egl_texture_update(desktop->texture, desktop->data))
{
DEBUG_ERROR("Failed to update the desktop texture");
return false;
}
desktop->update = false;
return true;
}
void egl_desktop_render(EGL_Desktop * desktop, const float x, const float y, const float scaleX, const float scaleY)
{
if (!desktop->shader)
return;
egl_shader_use(desktop->shader);
glUniform4f(desktop->uDesktopPos, x, y, scaleX, scaleY);
egl_model_render(desktop->model);
}

View File

@@ -1,256 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 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 "texture.h"
#include "debug.h"
#include "utils.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <SDL2/SDL_egl.h>
struct EGL_Texture
{
enum EGL_PixelFormat pixFmt;
size_t width, height;
bool streaming;
int textureCount;
GLuint textures[3];
GLuint samplers[3];
size_t planes[3][2];
GLintptr offsets[3];
GLenum intFormat;
GLenum format;
GLenum dataType;
bool hasPBO;
GLuint pbo[2];
int pboIndex;
bool needsUpdate;
size_t pboBufferSize;
};
bool egl_texture_init(EGL_Texture ** texture)
{
*texture = (EGL_Texture *)malloc(sizeof(EGL_Texture));
if (!*texture)
{
DEBUG_ERROR("Failed to malloc EGL_Texture");
return false;
}
memset(*texture, 0, sizeof(EGL_Texture));
return true;
}
void egl_texture_free(EGL_Texture ** texture)
{
if (!*texture)
return;
if ((*texture)->textureCount > 0)
{
glDeleteTextures((*texture)->textureCount, (*texture)->textures);
glDeleteSamplers((*texture)->textureCount, (*texture)->samplers);
}
if ((*texture)->hasPBO)
glDeleteBuffers(2, (*texture)->pbo);
free(*texture);
*texture = NULL;
}
bool egl_texture_setup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt, size_t width, size_t height, size_t bufferSize, bool streaming)
{
int textureCount;
texture->pixFmt = pixFmt;
texture->width = width;
texture->height = height;
texture->pboBufferSize = bufferSize;
texture->streaming = streaming;
switch(pixFmt)
{
case EGL_PF_BGRA:
textureCount = 1;
texture->format = GL_BGRA;
texture->planes[0][0] = width;
texture->planes[0][1] = height;
texture->offsets[0] = 0;
texture->intFormat = GL_BGRA;
texture->dataType = GL_UNSIGNED_BYTE;
break;
case EGL_PF_RGBA:
textureCount = 1;
texture->format = GL_RGBA;
texture->planes[0][0] = width;
texture->planes[0][1] = height;
texture->offsets[0] = 0;
texture->intFormat = GL_BGRA;
texture->dataType = GL_UNSIGNED_BYTE;
break;
case EGL_PF_RGBA10:
textureCount = 1;
texture->format = GL_RGBA;
texture->planes[0][0] = width;
texture->planes[0][1] = height;
texture->offsets[0] = 0;
texture->intFormat = GL_RGB10_A2;
texture->dataType = GL_UNSIGNED_INT_2_10_10_10_REV;
break;
case EGL_PF_YUV420:
textureCount = 3;
texture->format = GL_RED;
texture->planes[0][0] = width;
texture->planes[0][1] = height;
texture->planes[1][0] = width / 2;
texture->planes[1][1] = height / 2;
texture->planes[2][0] = width / 2;
texture->planes[2][1] = height / 2;
texture->offsets[0] = 0;
texture->offsets[1] = width * height;
texture->offsets[2] = texture->offsets[1] + (texture->offsets[1] / 4);
texture->dataType = GL_UNSIGNED_BYTE;
break;
default:
DEBUG_ERROR("Unsupported pixel format");
return false;
}
if (textureCount > texture->textureCount)
{
if (texture->textureCount > 0)
{
glDeleteTextures(texture->textureCount, texture->textures);
glDeleteSamplers(texture->textureCount, texture->samplers);
}
texture->textureCount = textureCount;
glGenTextures(texture->textureCount, texture->textures);
glGenSamplers(texture->textureCount, texture->samplers);
}
for(int i = 0; i < textureCount; ++i)
{
glSamplerParameteri(texture->samplers[i], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(texture->samplers[i], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(texture->samplers[i], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glSamplerParameteri(texture->samplers[i], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, texture->textures[i]);
glTexImage2D(GL_TEXTURE_2D, 0, texture->intFormat, texture->planes[i][0], texture->planes[i][1],
0, texture->format, texture->dataType, NULL);
}
glBindTexture(GL_TEXTURE_2D, 0);
if (streaming)
{
if (!texture->hasPBO)
{
glGenBuffers(2, texture->pbo);
texture->hasPBO = true;
}
for(int i = 0; i < 2; ++i)
{
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->pbo[i]);
glBufferData(
GL_PIXEL_UNPACK_BUFFER,
bufferSize,
NULL,
GL_DYNAMIC_DRAW
);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
}
return true;
}
bool egl_texture_update(EGL_Texture * texture, const uint8_t * buffer)
{
if (texture->streaming)
{
if (texture->needsUpdate)
{
DEBUG_ERROR("Previous frame was not consumed");
return false;
}
if (++texture->pboIndex == 2)
texture->pboIndex = 0;
/* initiate the data upload */
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->pbo[texture->pboIndex]);
glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, texture->pboBufferSize, buffer);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
texture->needsUpdate = true;
}
else
{
for(int i = 0; i < texture->textureCount; ++i)
{
glBindTexture(GL_TEXTURE_2D, texture->textures[i]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->planes[i][0], texture->planes[i][1],
texture->format, texture->dataType, buffer + texture->offsets[i]);
}
glBindTexture(GL_TEXTURE_2D, 0);
}
return true;
}
void egl_texture_bind(EGL_Texture * texture)
{
if (texture->streaming && texture->needsUpdate)
{
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->pbo[texture->pboIndex]);
for(int i = 0; i < texture->textureCount; ++i)
{
glBindTexture(GL_TEXTURE_2D, texture->textures[i]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->planes[i][0], texture->planes[i][1],
texture->format, texture->dataType, (const void *)texture->offsets[i]);
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
texture->needsUpdate = false;
}
for(int i = 0; i < texture->textureCount; ++i)
{
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, texture->textures[i]);
glBindSampler(i, texture->samplers[i]);
}
}
int egl_texture_count(EGL_Texture * texture)
{
return texture->textureCount;
}

View File

@@ -1,121 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 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 <stdint.h>
#pragma pack(push,1)
typedef struct SpicePoint16
{
int16_t x, y;
}
SpicePoint16;
typedef struct SpiceMsgMainInit
{
uint32_t session_id;
uint32_t display_channels_hint;
uint32_t supported_mouse_modes;
uint32_t current_mouse_mode;
uint32_t agent_connected;
uint32_t agent_tokens;
uint32_t multi_media_time;
uint32_t ram_hint;
}
SpiceMsgMainInit;
typedef struct SpiceMsgcMainMouseModeRequest
{
uint16_t mouse_mode;
}
SpiceMsgcMainMouseModeRequest;
typedef struct SpiceMsgPing
{
uint32_t id;
uint64_t timestamp;
}
SpiceMsgPing,
SpiceMsgcPong;
typedef struct SpiceMsgSetAck
{
uint32_t generation;
uint32_t window;
}
SpiceMsgSetAck;
typedef struct SpiceMsgcAckSync
{
uint32_t generation;
}
SpiceMsgcAckSync;
typedef struct SpiceMsgNotify
{
uint64_t time_stamp;
uint32_t severity;
uint32_t visibility;
uint32_t what;
uint32_t message_len;
//char message[message_len+1]
}
SpiceMsgNotify;
typedef struct SpiceMsgInputsInit
{
uint16_t modifiers;
}
SpiceMsgInputsInit,
SpiceMsgInputsKeyModifiers,
SpiceMsgcInputsKeyModifiers;
typedef struct SpiceMsgcKeyDown
{
uint32_t code;
}
SpiceMsgcKeyDown,
SpiceMsgcKeyUp;
typedef struct SpiceMsgcMousePosition
{
uint32_t x;
uint32_t y;
uint16_t button_state;
uint8_t display_id;
}
SpiceMsgcMousePosition;
typedef struct SpiceMsgcMouseMotion
{
int32_t x;
int32_t y;
uint16_t button_state;
}
SpiceMsgcMouseMotion;
typedef struct SpiceMsgcMousePress
{
uint8_t button;
uint16_t button_state;
}
SpiceMsgcMousePress,
SpiceMsgcMouseRelease;
#pragma pack(pop)

View File

@@ -1,227 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 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 "rsa.h"
#include "debug.h"
#include <spice/protocol.h>
#include <malloc.h>
#include <string.h>
#if defined(USE_OPENSSL) && defined(USE_NETTLE)
#error "USE_OPENSSL and USE_NETTLE are both defined"
#elif !defined(USE_OPENSSL) && !defined(USE_NETTLE)
#error "One of USE_OPENSSL or USE_NETTLE must be defined"
#endif
#if defined(USE_OPENSSL)
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#endif
#if defined(USE_NETTLE)
#include <stdlib.h>
#include <nettle/asn1.h>
#include <nettle/sha1.h>
#include <nettle/rsa.h>
#include <nettle/bignum.h>
#include <gmp.h>
#define SHA1_HASH_LEN 20
#endif
#if defined(USE_NETTLE)
/* the below OAEP implementation is derived from the FreeTDS project */
static void memxor(uint8_t * a, const uint8_t * b, const unsigned int len)
{
for(unsigned int i = 0; i < len; ++i)
a[i] = a[i] ^ b[i];
}
static void sha1(uint8_t * hash, const uint8_t *data, unsigned int len)
{
struct sha1_ctx ctx;
sha1_init(&ctx);
sha1_update(&ctx, len, data);
sha1_digest(&ctx, SHA1_HASH_LEN, hash);
}
static void oaep_mask(uint8_t * dest, size_t dest_len, const uint8_t * mask, size_t mask_len)
{
uint8_t hash[SHA1_HASH_LEN];
uint8_t seed[mask_len + 4 ];
memcpy(seed, mask, mask_len);
for(unsigned int n = 0;; ++n)
{
(seed+mask_len)[0] = n >> 24;
(seed+mask_len)[1] = n >> 16;
(seed+mask_len)[2] = n >> 8;
(seed+mask_len)[3] = n >> 0;
sha1(hash, seed, sizeof(seed));
if (dest_len <= SHA1_HASH_LEN)
{
memxor(dest, hash, dest_len);
break;
}
memxor(dest, hash, SHA1_HASH_LEN);
dest += SHA1_HASH_LEN;
dest_len -= SHA1_HASH_LEN;
}
}
static bool oaep_pad(mpz_t m, unsigned int key_size, const uint8_t * message, unsigned int len)
{
if (len + SHA1_HASH_LEN * 2 + 2 > key_size)
{
DEBUG_ERROR("Message too long");
return false;
}
struct
{
uint8_t all[1];
uint8_t ros[SHA1_HASH_LEN];
uint8_t db [key_size - SHA1_HASH_LEN - 1];
} em;
memset(&em, 0, sizeof(em));
sha1(em.db, (uint8_t *)"", 0);
em.all[key_size - len - 1] = 0x1;
memcpy(em.all + (key_size - len), message, len);
/* we are not too worried about randomness since we are just making a local
* connection, should anyone use this code outside of LookingGlass please be
* sure to use something better such as `gnutls_rnd` */
for(int i = 0; i < SHA1_HASH_LEN; ++i)
em.ros[i] = rand() % 255;
const int db_len = key_size - SHA1_HASH_LEN - 1;
oaep_mask(em.db , db_len , em.ros, SHA1_HASH_LEN);
oaep_mask(em.ros, SHA1_HASH_LEN, em.db , db_len );
nettle_mpz_set_str_256_u(m, key_size, em.all);
return true;
}
#endif
bool spice_rsa_encrypt_password(uint8_t * pub_key, char * password, struct spice_password * result)
{
result->size = 0;
result->data = NULL;
#if defined(USE_OPENSSL)
BIO *bioKey = BIO_new(BIO_s_mem());
if (!bioKey)
{
DEBUG_ERROR("failed to allocate bioKey");
return false;
}
BIO_write(bioKey, pub_key, SPICE_TICKET_PUBKEY_BYTES);
EVP_PKEY *rsaKey = d2i_PUBKEY_bio(bioKey, NULL);
RSA *rsa = EVP_PKEY_get1_RSA(rsaKey);
result->size = RSA_size(rsa);
result->data = (char *)malloc(result->size);
if (RSA_public_encrypt(
strlen(password) + 1,
(uint8_t*)password,
(uint8_t*)result->data,
rsa,
RSA_PKCS1_OAEP_PADDING
) <= 0)
{
free(result->data);
result->size = 0;
result->data = NULL;
DEBUG_ERROR("rsa public encrypt failed");
EVP_PKEY_free(rsaKey);
BIO_free(bioKey);
return false;
}
EVP_PKEY_free(rsaKey);
BIO_free(bioKey);
return true;
#endif
#if defined(USE_NETTLE)
struct asn1_der_iterator der;
struct asn1_der_iterator j;
struct rsa_public_key pub;
if (asn1_der_iterator_first(&der, SPICE_TICKET_PUBKEY_BYTES, pub_key) == ASN1_ITERATOR_CONSTRUCTED
&& der.type == ASN1_SEQUENCE
&& asn1_der_decode_constructed_last(&der) == ASN1_ITERATOR_CONSTRUCTED
&& der.type == ASN1_SEQUENCE
&& asn1_der_decode_constructed(&der, &j) == ASN1_ITERATOR_PRIMITIVE
&& j.type == ASN1_IDENTIFIER
&& asn1_der_iterator_next(&der) == ASN1_ITERATOR_PRIMITIVE
&& der.type == ASN1_BITSTRING
&& asn1_der_decode_bitstring_last(&der))
{
if (j.length != 9)
{
DEBUG_ERROR("Invalid key, not RSA");
return false;
}
if (asn1_der_iterator_next(&j) == ASN1_ITERATOR_PRIMITIVE
&& j.type == ASN1_NULL
&& j.length == 0
&& asn1_der_iterator_next(&j) == ASN1_ITERATOR_END)
{
rsa_public_key_init(&pub);
if (!rsa_public_key_from_der_iterator(&pub, 0, &der))
{
DEBUG_ERROR("Unable to load public key from DER iterator");
rsa_public_key_clear(&pub);
return false;
}
}
}
mpz_t p;
mpz_init(p);
oaep_pad(p, pub.size, (uint8_t *)password, strlen(password)+1);
mpz_powm(p, p, pub.e, pub.n);
result->size = pub.size;
result->data = malloc(pub.size);
nettle_mpz_get_str_256(pub.size, (uint8_t *)result->data, p);
rsa_public_key_clear(&pub);
mpz_clear(p);
return true;
#endif
}
void spice_rsa_free_password(struct spice_password * pass)
{
free(pass->data);
pass->size = 0;
pass->data = NULL;
}

View File

@@ -1,952 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 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 "spice.h"
#include "utils.h"
#include "debug.h"
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <spice/protocol.h>
#include <spice/error_codes.h>
#include "messages.h"
#include "rsa.h"
#ifdef DEBUG_SPICE_MOUSE
#define DEBUG_MOUSE(fmt, args...) DEBUG_PRINT("[M]", fmt, ##args)
#else
#define DEBUG_MOUSE(fmt, args...) do {} while(0)
#endif
#ifdef DEBUG_SPICE_KEYBOARD
#define DEBUG_KEYBOARD(fmt, args...) DEBUG_PRINT("[K]", fmt, ##args)
#else
#define DEBUG_KEYBOARD(fmt, args...) do {} while(0)
#endif
// ============================================================================
// internal structures
struct SpiceChannel
{
bool connected;
bool initDone;
uint8_t channelType;
int socket;
uint32_t ackFrequency;
uint32_t ackCount;
uint32_t serial;
LG_Lock lock;
};
struct SpiceKeyboard
{
uint32_t modifiers;
};
#define SPICE_MOUSE_QUEUE_SIZE 64
struct SpiceMouse
{
uint32_t buttonState;
int sentCount;
SpiceMsgcMouseMotion queue[SPICE_MOUSE_QUEUE_SIZE];
int rpos, wpos;
int queueLen;
LG_Lock lock;
};
union SpiceAddr
{
struct sockaddr addr;
struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr_un un;
};
struct Spice
{
char password[32];
short family;
union SpiceAddr addr;
uint32_t sessionID;
uint32_t channelID;
struct SpiceChannel scMain;
struct SpiceChannel scInputs;
struct SpiceKeyboard kb;
struct SpiceMouse mouse;
};
// globals
struct Spice spice =
{
.sessionID = 0,
.scMain .connected = false,
.scMain .channelType = SPICE_CHANNEL_MAIN,
.scInputs.connected = false,
.scInputs.channelType = SPICE_CHANNEL_INPUTS,
};
// internal forward decls
bool spice_connect_channel (struct SpiceChannel * channel);
void spice_disconnect_channel(struct SpiceChannel * channel);
bool spice_process_ack(struct SpiceChannel * channel);
bool spice_on_common_read (struct SpiceChannel * channel, SpiceDataHeader * header, bool * handled);
bool spice_on_main_channel_read ();
bool spice_on_inputs_channel_read();
bool spice_read (const struct SpiceChannel * channel, void * buffer, const ssize_t size);
ssize_t spice_write (const struct SpiceChannel * channel, const void * buffer, const ssize_t size);
bool spice_write_msg(struct SpiceChannel * channel, uint32_t type, const void * buffer, const ssize_t size);
bool spice_discard (const struct SpiceChannel * channel, ssize_t size);
// ============================================================================
bool spice_connect(const char * host, const short port, const char * password)
{
strncpy(spice.password, password, sizeof(spice.password) - 1);
memset(&spice.addr, 0, sizeof(spice.addr));
if (port == 0)
{
spice.family = AF_UNIX;
spice.addr.un.sun_family = spice.family;
strncpy(spice.addr.un.sun_path, host, sizeof(spice.addr.un.sun_path) - 1);
DEBUG_INFO("Remote: %s", host);
}
else
{
spice.family = AF_INET;
inet_pton(spice.family, host, &spice.addr.in.sin_addr);
spice.addr.in.sin_family = spice.family;
spice.addr.in.sin_port = htons(port);
DEBUG_INFO("Remote: %s:%d", host, port);
}
LG_LOCK_INIT(spice.mouse.lock);
spice.channelID = 0;
if (!spice_connect_channel(&spice.scMain))
{
DEBUG_ERROR("connect main channel failed");
return false;
}
return true;
}
// ============================================================================
void spice_disconnect()
{
spice_disconnect_channel(&spice.scMain );
spice_disconnect_channel(&spice.scInputs);
LG_LOCK_FREE(spice.mouse.lock);
spice.sessionID = 0;
}
// ============================================================================
bool spice_ready()
{
return spice.scMain.connected &&
spice.scInputs.connected;
}
// ============================================================================
bool spice_process()
{
fd_set readSet;
FD_ZERO(&readSet);
FD_SET(spice.scMain.socket , &readSet);
FD_SET(spice.scInputs.socket, &readSet);
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
int rc = select(FD_SETSIZE, &readSet, NULL, NULL, &timeout);
if (rc < 0)
{
DEBUG_ERROR("select failure");
return false;
}
for(int i = 0; i < FD_SETSIZE; ++i)
if (FD_ISSET(i, &readSet))
{
if (i == spice.scMain.socket)
{
if (spice_on_main_channel_read())
{
if (spice.scMain.connected && !spice_process_ack(&spice.scMain))
{
DEBUG_ERROR("failed to process ack on main channel");
return false;
}
continue;
}
else
{
DEBUG_ERROR("failed to perform read on main channel");
return false;
}
}
if (spice.scInputs.connected && i == spice.scInputs.socket)
{
if (!spice_process_ack(&spice.scInputs))
{
DEBUG_ERROR("failed to process ack on inputs channel");
return false;
}
if (spice_on_inputs_channel_read())
continue;
else
{
DEBUG_ERROR("failed to perform read on inputs channel");
return false;
}
}
}
return true;
}
// ============================================================================
bool spice_process_ack(struct SpiceChannel * channel)
{
if (channel->ackFrequency == 0)
return true;
if (channel->ackCount++ != channel->ackFrequency)
return true;
channel->ackCount = 0;
return spice_write_msg(channel, SPICE_MSGC_ACK, "\0", 1);
}
// ============================================================================
bool spice_on_common_read(struct SpiceChannel * channel, SpiceDataHeader * header, bool * handled)
{
if (!spice_read(channel, header, sizeof(SpiceDataHeader)))
{
DEBUG_ERROR("read failure");
*handled = false;
return false;
}
#if 0
printf("socket: %d, serial: %6u, type: %2u, size %6u, sub_list %4u\n",
channel->socket,
header->serial, header->type, header->size, header->sub_list);
#endif
if (!channel->initDone)
{
*handled = false;
return true;
}
switch(header->type)
{
case SPICE_MSG_MIGRATE:
case SPICE_MSG_MIGRATE_DATA:
{
DEBUG_PROTO("SPICE_MSG_MIGRATE_DATA");
*handled = true;
DEBUG_WARN("migration is not supported");
return false;
}
case SPICE_MSG_SET_ACK:
{
DEBUG_INFO("SPICE_MSG_SET_ACK");
*handled = true;
SpiceMsgSetAck in;
if (!spice_read(channel, &in, sizeof(in)))
return false;
channel->ackFrequency = in.window;
SpiceMsgcAckSync out;
out.generation = in.generation;
if (!spice_write_msg(channel, SPICE_MSGC_ACK_SYNC, &out, sizeof(out)))
return false;
return true;
}
case SPICE_MSG_PING:
{
DEBUG_PROTO("SPICE_MSG_PING");
*handled = true;
SpiceMsgPing in;
if (!spice_read(channel, &in, sizeof(in)))
return false;
if (!spice_discard(channel, header->size - sizeof(in)))
{
DEBUG_ERROR("failed discarding enough bytes from the ping packet");
return false;
}
SpiceMsgcPong out;
out.id = in.id;
out.timestamp = in.timestamp;
if (!spice_write_msg(channel, SPICE_MSGC_PONG, &out, sizeof(out)))
return false;
return true;
}
case SPICE_MSG_WAIT_FOR_CHANNELS:
case SPICE_MSG_DISCONNECTING :
{
*handled = true;
DEBUG_FIXME("ignored wait-for-channels or disconnect message");
return false;
}
case SPICE_MSG_NOTIFY:
{
DEBUG_PROTO("SPICE_MSG_NOTIFY");
SpiceMsgNotify in;
if (!spice_read(channel, &in, sizeof(in)))
return false;
char msg[in.message_len+1];
if (!spice_read(channel, msg, in.message_len+1))
return false;
DEBUG_INFO("notify message: %s", msg);
*handled = true;
return true;
}
}
*handled = false;
return true;
}
// ============================================================================
bool spice_on_main_channel_read()
{
struct SpiceChannel *channel = &spice.scMain;
SpiceDataHeader header;
bool handled;
if (!spice_on_common_read(channel, &header, &handled))
{
DEBUG_ERROR("read failure");
return false;
}
if (handled)
return true;
if (!channel->initDone)
{
if (header.type != SPICE_MSG_MAIN_INIT)
{
spice_disconnect();
DEBUG_ERROR("expected main init message but got type %u", header.type);
return false;
}
DEBUG_PROTO("SPICE_MSG_MAIN_INIT");
channel->initDone = true;
SpiceMsgMainInit msg;
if (!spice_read(channel, &msg, sizeof(msg)))
{
spice_disconnect();
return false;
}
spice.sessionID = msg.session_id;
if (!spice_connect_channel(&spice.scInputs))
{
DEBUG_ERROR("failed to connect inputs channel");
return false;
}
if (msg.current_mouse_mode != SPICE_MOUSE_MODE_CLIENT && !spice_mouse_mode(false))
{
DEBUG_ERROR("failed to set mouse mode");
return false;
}
return true;
}
DEBUG_WARN("main channel unhandled message type %u", header.type);
spice_discard(channel, header.size);
return true;
}
// ============================================================================
bool spice_on_inputs_channel_read()
{
struct SpiceChannel *channel = &spice.scInputs;
SpiceDataHeader header;
bool handled;
if (!spice_on_common_read(channel, &header, &handled))
{
DEBUG_ERROR("read failure");
return false;
}
if (handled)
return true;
switch(header.type)
{
case SPICE_MSG_INPUTS_INIT:
{
DEBUG_PROTO("SPICE_MSG_INPUTS_INIT");
if (channel->initDone)
{
DEBUG_ERROR("input init message already done");
return false;
}
channel->initDone = true;
SpiceMsgInputsInit in;
if (!spice_read(channel, &in, sizeof(in)))
return false;
return true;
}
case SPICE_MSG_INPUTS_KEY_MODIFIERS:
{
DEBUG_PROTO("SPICE_MSG_INPUTS_KEY_MODIFIERS");
SpiceMsgInputsInit in;
if (!spice_read(channel, &in, sizeof(in)))
return false;
spice.kb.modifiers = in.modifiers;
return true;
}
case SPICE_MSG_INPUTS_MOUSE_MOTION_ACK:
{
DEBUG_PROTO("SPICE_MSG_INPUTS_MOUSE_MOTION_ACK");
int sent = 0;
LG_LOCK(spice.mouse.lock);
while(spice.mouse.queueLen && sent < 4)
{
SpiceMsgcMouseMotion *msg = &spice.mouse.queue[spice.mouse.rpos];
if (!spice_write_msg(channel, SPICE_MSGC_INPUTS_MOUSE_MOTION, msg, sizeof(SpiceMsgcMouseMotion)))
{
DEBUG_ERROR("failed to send post ack");
spice.mouse.sentCount = sent;
LG_UNLOCK(spice.mouse.lock);
return false;
}
if (++spice.mouse.rpos == SPICE_MOUSE_QUEUE_SIZE)
spice.mouse.rpos = 0;
++sent;
--spice.mouse.queueLen;
}
spice.mouse.sentCount = sent;
LG_UNLOCK(spice.mouse.lock);
return true;
}
}
DEBUG_WARN("inputs channel unhandled message type %u", header.type);
spice_discard(channel, header.size);
return true;
}
// ============================================================================
bool spice_connect_channel(struct SpiceChannel * channel)
{
channel->initDone = false;
channel->ackFrequency = 0;
channel->ackCount = 0;
channel->serial = 0;
LG_LOCK_INIT(channel->lock);
size_t addrSize;
switch(spice.family)
{
case AF_UNIX:
addrSize = sizeof(spice.addr.un);
break;
case AF_INET:
addrSize = sizeof(spice.addr.in);
break;
case AF_INET6:
addrSize = sizeof(spice.addr.in6);
break;
default:
DEBUG_ERROR("Unsupported socket family");
return false;
}
channel->socket = socket(spice.family, SOCK_STREAM, 0);
if (channel->socket == -1)
return false;
if (spice.family != AF_UNIX)
{
int flag = 1;
setsockopt(channel->socket, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int));
}
if (connect(channel->socket, &spice.addr.addr, addrSize) == -1)
{
DEBUG_ERROR("socket connect failure");
close(channel->socket);
return false;
}
channel->connected = true;
SpiceLinkHeader header =
{
.magic = SPICE_MAGIC ,
.major_version = SPICE_VERSION_MAJOR,
.minor_version = SPICE_VERSION_MINOR,
.size = sizeof(SpiceLinkMess)
};
SpiceLinkMess message =
{
.connection_id = spice.sessionID,
.channel_type = channel->channelType,
.channel_id = spice.channelID,
.num_common_caps = 0,
.num_channel_caps = 0,
.caps_offset = sizeof(SpiceLinkMess)
};
spice_write(channel, &header , sizeof(header ));
spice_write(channel, &message, sizeof(message));
if (!spice_read(channel, &header, sizeof(header)))
{
DEBUG_ERROR("failed to read SpiceLinkHeader");
spice_disconnect_channel(channel);
return false;
}
if (header.magic != SPICE_MAGIC || header.major_version != SPICE_VERSION_MAJOR)
{
DEBUG_ERROR("invalid or unsupported protocol version");
spice_disconnect_channel(channel);
return false;
}
if (header.size < sizeof(SpiceLinkReply))
{
DEBUG_ERROR("reported data size too small");
spice_disconnect_channel(channel);
return false;
}
SpiceLinkReply reply;
if (!spice_read(channel, &reply, sizeof(reply)))
{
DEBUG_ERROR("failed to read SpiceLinkReply");
spice_disconnect_channel(channel);
return false;
}
if (reply.error != SPICEC_ERROR_CODE_SUCCESS)
{
DEBUG_ERROR("server replied with error %u", reply.error);
spice_disconnect_channel(channel);
return false;
}
uint32_t capsCommon [reply.num_common_caps ];
uint32_t capsChannel[reply.num_channel_caps];
spice_read(channel, &capsCommon , sizeof(capsCommon ));
spice_read(channel, &capsChannel, sizeof(capsChannel));
struct spice_password pass;
if (!spice_rsa_encrypt_password(reply.pub_key, spice.password, &pass))
{
spice_disconnect_channel(channel);
return false;
}
if (!spice_write(channel, pass.data, pass.size))
{
spice_rsa_free_password(&pass);
DEBUG_ERROR("failed to write encrypted data");
spice_disconnect_channel(channel);
return false;
}
spice_rsa_free_password(&pass);
uint32_t linkResult;
if (!spice_read(channel, &linkResult, sizeof(linkResult)))
{
DEBUG_ERROR("failed to read SpiceLinkResult");
spice_disconnect_channel(channel);
return false;
}
if (linkResult != SPICE_LINK_ERR_OK)
{
DEBUG_ERROR("connect code error %u", linkResult);
spice_disconnect_channel(channel);
return false;
}
return true;
}
// ============================================================================
void spice_disconnect_channel(struct SpiceChannel * channel)
{
if (channel->connected)
{
shutdown(channel->socket, SHUT_WR);
char buffer[1024];
ssize_t len = 0;
do
len = read(channel->socket, buffer, sizeof(buffer));
while(len > 0);
close(channel->socket);
}
channel->connected = false;
LG_LOCK_FREE(channel->lock);
}
// ============================================================================
ssize_t spice_write(const struct SpiceChannel * channel, const void * buffer, const ssize_t size)
{
if (!channel->connected)
{
DEBUG_ERROR("not connected");
return -1;
}
if (!buffer)
{
DEBUG_ERROR("invalid buffer argument supplied");
return -1;
}
ssize_t len = send(channel->socket, buffer, size, 0);
if (len != size)
DEBUG_WARN("incomplete write");
return len;
}
// ============================================================================
bool spice_write_msg(struct SpiceChannel * channel, uint32_t type, const void * buffer, const ssize_t size)
{
LG_LOCK(channel->lock);
SpiceDataHeader header;
header.serial = channel->serial++;
header.type = type;
header.size = size;
header.sub_list = 0;
if (spice_write(channel, &header, sizeof(header)) != sizeof(header))
{
DEBUG_ERROR("failed to write message header");
LG_UNLOCK(channel->lock);
return false;
}
if (spice_write(channel, buffer, size) != size)
{
DEBUG_ERROR("failed to write message body");
LG_UNLOCK(channel->lock);
return false;
}
LG_UNLOCK(channel->lock);
return true;
}
// ============================================================================
bool spice_read(const struct SpiceChannel * channel, void * buffer, const ssize_t size)
{
if (!channel->connected)
{
DEBUG_ERROR("not connected");
return false;
}
if (!buffer)
{
DEBUG_ERROR("invalid buffer argument supplied");
return false;
}
ssize_t len = read(channel->socket, buffer, size);
if (len != size)
{
DEBUG_ERROR("incomplete write");
return false;
}
return true;
}
// ============================================================================
bool spice_discard(const struct SpiceChannel * channel, ssize_t size)
{
while(size)
{
char c[8192];
size_t len = read(channel->socket, c, size > sizeof(c) ? sizeof(c) : size);
if (len <= 0)
return false;
size -= len;
}
return true;
}
// ============================================================================
bool spice_key_down(uint32_t code)
{
DEBUG_KEYBOARD("%u", code);
if (!spice.scInputs.connected)
{
DEBUG_ERROR("not connected");
return false;
}
if (code > 0x100)
code = 0xe0 | ((code - 0x100) << 8);
SpiceMsgcKeyDown msg;
msg.code = code;
return spice_write_msg(&spice.scInputs, SPICE_MSGC_INPUTS_KEY_DOWN, &msg, sizeof(msg));
}
// ============================================================================
bool spice_key_up(uint32_t code)
{
DEBUG_KEYBOARD("%u", code);
if (!spice.scInputs.connected)
{
DEBUG_ERROR("not connected");
return false;
}
if (code < 0x100)
code |= 0x80;
else
code = 0x80e0 | ((code - 0x100) << 8);
SpiceMsgcKeyDown msg;
msg.code = code;
return spice_write_msg(&spice.scInputs, SPICE_MSGC_INPUTS_KEY_UP, &msg, sizeof(msg));
}
// ============================================================================
bool spice_mouse_mode(bool server)
{
DEBUG_MOUSE("%s", server ? "server" : "client");
if (!spice.scInputs.connected)
{
DEBUG_ERROR("not connected");
return false;
}
SpiceMsgcMainMouseModeRequest msg;
msg.mouse_mode = server ? SPICE_MOUSE_MODE_SERVER : SPICE_MOUSE_MODE_CLIENT;
return spice_write_msg(&spice.scMain, SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST, &msg, sizeof(msg));
}
// ============================================================================
bool spice_mouse_position(uint32_t x, uint32_t y)
{
DEBUG_MOUSE("x=%u, y=%u", x, y);
if (!spice.scInputs.connected)
{
DEBUG_ERROR("not connected");
return false;
}
SpiceMsgcMousePosition msg;
msg.x = x;
msg.y = y;
msg.button_state = spice.mouse.buttonState;
msg.display_id = 0;
return spice_write_msg(&spice.scInputs, SPICE_MSGC_INPUTS_MOUSE_POSITION, &msg, sizeof(msg));
}
// ============================================================================
bool spice_mouse_motion(int32_t x, int32_t y)
{
DEBUG_MOUSE("x=%d, y=%d", x, y);
if (!spice.scInputs.connected)
{
DEBUG_ERROR("not connected");
return false;
}
LG_LOCK(spice.mouse.lock);
if (spice.mouse.sentCount == 4)
{
if (spice.mouse.queueLen == SPICE_MOUSE_QUEUE_SIZE)
{
DEBUG_ERROR("mouse motion ringbuffer full!");
LG_UNLOCK(spice.mouse.lock);
return false;
}
SpiceMsgcMouseMotion *msg =
&spice.mouse.queue[spice.mouse.wpos++];
msg->x = x;
msg->y = y;
msg->button_state = spice.mouse.buttonState;
if (spice.mouse.wpos == SPICE_MOUSE_QUEUE_SIZE)
spice.mouse.wpos = 0;
++spice.mouse.queueLen;
LG_UNLOCK(spice.mouse.lock);
return true;
}
SpiceMsgcMouseMotion msg;
msg.x = x;
msg.y = y;
msg.button_state = spice.mouse.buttonState;
++spice.mouse.sentCount;
LG_UNLOCK(spice.mouse.lock);
return spice_write_msg(&spice.scInputs, SPICE_MSGC_INPUTS_MOUSE_MOTION, &msg, sizeof(msg));
}
// ============================================================================
bool spice_mouse_press(uint32_t button)
{
DEBUG_MOUSE("%u", button);
if (!spice.scInputs.connected)
{
DEBUG_ERROR("not connected");
return false;
}
switch(button)
{
case SPICE_MOUSE_BUTTON_LEFT : spice.mouse.buttonState |= SPICE_MOUSE_BUTTON_MASK_LEFT ; break;
case SPICE_MOUSE_BUTTON_MIDDLE: spice.mouse.buttonState |= SPICE_MOUSE_BUTTON_MASK_MIDDLE; break;
case SPICE_MOUSE_BUTTON_RIGHT : spice.mouse.buttonState |= SPICE_MOUSE_BUTTON_MASK_RIGHT ; break;
}
SpiceMsgcMousePress msg;
msg.button = button;
msg.button_state = spice.mouse.buttonState;
return spice_write_msg(&spice.scInputs, SPICE_MSGC_INPUTS_MOUSE_PRESS, &msg, sizeof(msg));
}
// ============================================================================
bool spice_mouse_release(uint32_t button)
{
DEBUG_MOUSE("%u", button);
if (!spice.scInputs.connected)
{
DEBUG_ERROR("not connected");
return false;
}
switch(button)
{
case SPICE_MOUSE_BUTTON_LEFT : spice.mouse.buttonState &= ~SPICE_MOUSE_BUTTON_MASK_LEFT ; break;
case SPICE_MOUSE_BUTTON_MIDDLE: spice.mouse.buttonState &= ~SPICE_MOUSE_BUTTON_MASK_MIDDLE; break;
case SPICE_MOUSE_BUTTON_RIGHT : spice.mouse.buttonState &= ~SPICE_MOUSE_BUTTON_MASK_RIGHT ; break;
}
SpiceMsgcMouseRelease msg;
msg.button = button;
msg.button_state = spice.mouse.buttonState;
return spice_write_msg(&spice.scInputs, SPICE_MSGC_INPUTS_MOUSE_RELEASE, &msg, sizeof(msg));
}

View File

@@ -1,35 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 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 <sys/types.h>
#include <stdbool.h>
#include <stdint.h>
bool spice_connect(const char * host, const short port, const char * password);
void spice_disconnect();
bool spice_process();
bool spice_ready();
bool spice_key_down (uint32_t code);
bool spice_key_up (uint32_t code);
bool spice_mouse_mode (bool server);
bool spice_mouse_position(uint32_t x, uint32_t y);
bool spice_mouse_motion ( int32_t x, int32_t y);
bool spice_mouse_press (uint32_t button);
bool spice_mouse_release (uint32_t button);

75
client/src/app.c Normal file
View File

@@ -0,0 +1,75 @@
/*
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 "main.h"
#include "common/debug.h"
#include <stdarg.h>
void app_alert(LG_MsgAlert type, const char * fmt, ...)
{
if (!state.lgr || !params.showAlerts)
return;
va_list args;
va_start(args, fmt);
const int length = vsnprintf(NULL, 0, fmt, args);
va_end(args);
char *buffer = malloc(length + 1);
va_start(args, fmt);
vsnprintf(buffer, length + 1, fmt, args);
va_end(args);
state.lgr->on_alert(
state.lgrData,
type,
buffer,
NULL
);
free(buffer);
}
KeybindHandle app_register_keybind(SDL_Scancode key, SuperEventFn callback, void * opaque)
{
// don't allow duplicate binds
if (state.bindings[key])
{
DEBUG_INFO("Key already bound");
return NULL;
}
KeybindHandle handle = (KeybindHandle)malloc(sizeof(struct KeybindHandle));
handle->key = key;
handle->callback = callback;
handle->opaque = opaque;
state.bindings[key] = handle;
return handle;
}
void app_release_keybind(KeybindHandle * handle)
{
if (!*handle)
return;
state.bindings[(*handle)->key] = NULL;
free(*handle);
*handle = NULL;
}

586
client/src/config.c Normal file
View File

@@ -0,0 +1,586 @@
/*
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 "main.h"
#include "config.h"
#include "common/option.h"
#include "common/debug.h"
#include "common/stringutils.h"
#include <sys/stat.h>
#include <pwd.h>
#include <unistd.h>
// forwards
static bool optRendererParse (struct Option * opt, const char * str);
static StringList optRendererValues (struct Option * opt);
static char * optRendererToString(struct Option * opt);
static bool optPosParse (struct Option * opt, const char * str);
static StringList optPosValues (struct Option * opt);
static char * optPosToString (struct Option * opt);
static bool optSizeParse (struct Option * opt, const char * str);
static StringList optSizeValues (struct Option * opt);
static char * optSizeToString (struct Option * opt);
static char * optScancodeToString(struct Option * opt);
static void doLicense();
static struct Option options[] =
{
// app options
{
.module = "app",
.name = "configFile",
.description = "A file to read additional configuration from",
.shortopt = 'C',
.type = OPTION_TYPE_STRING,
.value.x_string = NULL,
},
{
.module = "app",
.name = "renderer",
.description = "Specify the renderer to use",
.shortopt = 'g',
.type = OPTION_TYPE_CUSTOM,
.parser = optRendererParse,
.getValues = optRendererValues,
.toString = optRendererToString
},
{
.module = "app",
.name = "license",
.description = "Show the license for this application and then terminate",
.shortopt = 'l',
.type = OPTION_TYPE_BOOL,
.value.x_bool = false,
},
{
.module = "app",
.name = "cursorPollInterval",
.description = "How often to check for a cursor update in microseconds",
.type = OPTION_TYPE_INT,
.value.x_int = 1000
},
{
.module = "app",
.name = "framePollInterval",
.description = "How often to check for a frame update in microseconds",
.type = OPTION_TYPE_INT,
.value.x_int = 1000
},
// window options
{
.module = "win",
.name = "title",
.description = "The window title",
.type = OPTION_TYPE_STRING,
.value.x_string = "Looking Glass (client)"
},
{
.module = "win",
.name = "position",
.description = "Initial window position at startup",
.type = OPTION_TYPE_CUSTOM,
.parser = optPosParse,
.getValues = optPosValues,
.toString = optPosToString
},
{
.module = "win",
.name = "size",
.description = "Initial window size at startup",
.type = OPTION_TYPE_CUSTOM,
.parser = optSizeParse,
.getValues = optSizeValues,
.toString = optSizeToString
},
{
.module = "win",
.name = "autoResize",
.description = "Auto resize the window to the guest",
.shortopt = 'a',
.type = OPTION_TYPE_BOOL,
.value.x_bool = false,
},
{
.module = "win",
.name = "allowResize",
.description = "Allow the window to be manually resized",
.shortopt = 'n',
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
{
.module = "win",
.name = "keepAspect",
.description = "Maintain the correct aspect ratio",
.shortopt = 'r',
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
{
.module = "win",
.name = "forceAspect",
.description = "Force the window to maintain the aspect ratio",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
{
.module = "win",
.name = "borderless",
.description = "Borderless mode",
.shortopt = 'd',
.type = OPTION_TYPE_BOOL,
.value.x_bool = false,
},
{
.module = "win",
.name = "fullScreen",
.description = "Launch in fullscreen borderless mode",
.shortopt = 'F',
.type = OPTION_TYPE_BOOL,
.value.x_bool = false,
},
{
.module = "win",
.name = "maximize",
.description = "Launch window maximized",
.shortopt = 'T',
.type = OPTION_TYPE_BOOL,
.value.x_bool = false,
},
{
.module = "win",
.name = "minimizeOnFocusLoss",
.description = "Minimize window on focus loss",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
{
.module = "win",
.name = "fpsMin",
.description = "Frame rate minimum (0 = disable - not recommended, -1 = auto detect)",
.shortopt = 'K',
.type = OPTION_TYPE_INT,
.value.x_int = -1,
},
{
.module = "win",
.name = "showFPS",
.description = "Enable the FPS & UPS display",
.shortopt = 'k',
.type = OPTION_TYPE_BOOL,
.value.x_bool = false,
},
{
.module = "win",
.name = "ignoreQuit",
.description = "Ignore requests to quit (ie: Alt+F4)",
.shortopt = 'Q',
.type = OPTION_TYPE_BOOL,
.value.x_bool = false,
},
{
.module = "win",
.name = "noScreensaver",
.description = "Prevent the screensaver from starting",
.shortopt = 'S',
.type = OPTION_TYPE_BOOL,
.value.x_bool = false,
},
{
.module = "win",
.name = "alerts",
.description = "Show on screen alert messages",
.shortopt = 'q',
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
// input options
{
.module = "input",
.name = "grabKeyboard",
.description = "Grab the keyboard in capture mode",
.shortopt = 'G',
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
{
.module = "input",
.name = "escapeKey",
.description = "Specify the escape key, see https://wiki.libsdl.org/SDLScancodeLookup for valid values",
.shortopt = 'm',
.type = OPTION_TYPE_INT,
.value.x_int = SDL_SCANCODE_SCROLLLOCK,
.toString = optScancodeToString
},
{
.module = "input",
.name = "hideCursor",
.description = "Hide the local mouse cursor",
.shortopt = 'M',
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
{
.module = "input",
.name = "mouseSens",
.description = "Initial mouse sensitivity when in capture mode (-9 to 9)",
.type = OPTION_TYPE_INT,
.value.x_int = 0,
},
{
.module = "input",
.name = "mouseRedraw",
.description = "Mouse movements trigger redraws (ignores FPS minimum)",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
// spice options
{
.module = "spice",
.name = "enable",
.description = "Enable the built in SPICE client for input and/or clipboard support",
.shortopt = 's',
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "spice",
.name = "host",
.description = "The SPICE server host or UNIX socket",
.shortopt = 'c',
.type = OPTION_TYPE_STRING,
.value.x_string = "127.0.0.1"
},
{
.module = "spice",
.name = "port",
.description = "The SPICE server port (0 = unix socket)",
.shortopt = 'p',
.type = OPTION_TYPE_INT,
.value.x_int = 5900
},
{
.module = "spice",
.name = "input",
.description = "Use SPICE to send keyboard and mouse input events to the guest",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "spice",
.name = "clipboard",
.description = "Use SPICE to syncronize the clipboard contents with the guest",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "spice",
.name = "clipboardToVM",
.description = "Allow the clipboard to be syncronized TO the VM",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "spice",
.name = "clipboardToLocal",
.description = "Allow the clipboard to be syncronized FROM the VM",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "spice",
.name = "scaleCursor",
.description = "Scale cursor input position to screen size when up/down scaled",
.shortopt = 'j',
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "spice",
.name = "captureOnStart",
.description = "Capture mouse and keyboard on start",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
{0}
};
void config_init()
{
params.center = true;
params.w = 1024;
params.h = 768;
option_register(options);
}
bool config_load(int argc, char * argv[])
{
// load any global options first
struct stat st;
if (stat("/etc/looking-glass-client.ini", &st) >= 0)
{
DEBUG_INFO("Loading config from: /etc/looking-glass-client.ini");
if (!option_load("/etc/looking-glass-client.ini"))
return false;
}
// load user's local options
struct passwd * pw = getpwuid(getuid());
char * localFile;
alloc_sprintf(&localFile, "%s/.looking-glass-client.ini", pw->pw_dir);
if (stat(localFile, &st) >= 0)
{
DEBUG_INFO("Loading config from: %s", localFile);
if (!option_load(localFile))
{
free(localFile);
return false;
}
}
free(localFile);
// parse the command line arguments
if (!option_parse(argc, argv))
return false;
// if a file was specified to also load, do it
const char * configFile = option_get_string("app", "configFile");
if (configFile)
{
DEBUG_INFO("Loading config from: %s", configFile);
if (!option_load(configFile))
return false;
}
// validate the values are sane
if (!option_validate())
return false;
if (option_get_bool("app", "license"))
{
doLicense();
return false;
}
// setup the application params for the basic types
params.cursorPollInterval = option_get_int ("app", "cursorPollInterval");
params.framePollInterval = option_get_int ("app", "framePollInterval" );
params.windowTitle = option_get_string("win", "title" );
params.autoResize = option_get_bool ("win", "autoResize" );
params.allowResize = option_get_bool ("win", "allowResize" );
params.keepAspect = option_get_bool ("win", "keepAspect" );
params.forceAspect = option_get_bool ("win", "forceAspect" );
params.borderless = option_get_bool ("win", "borderless" );
params.fullscreen = option_get_bool ("win", "fullScreen" );
params.maximize = option_get_bool ("win", "maximize" );
params.fpsMin = option_get_int ("win", "fpsMin" );
params.showFPS = option_get_bool ("win", "showFPS" );
params.ignoreQuit = option_get_bool ("win", "ignoreQuit" );
params.noScreensaver = option_get_bool ("win", "noScreensaver");
params.showAlerts = option_get_bool ("win", "alerts" );
params.grabKeyboard = option_get_bool ("input", "grabKeyboard");
params.escapeKey = option_get_int ("input", "escapeKey" );
params.hideMouse = option_get_bool ("input", "hideCursor" );
params.mouseSens = option_get_int ("input", "mouseSens" );
params.mouseRedraw = option_get_bool ("input", "mouseRedraw" );
params.minimizeOnFocusLoss = option_get_bool("win", "minimizeOnFocusLoss");
if (option_get_bool("spice", "enable"))
{
params.spiceHost = option_get_string("spice", "host");
params.spicePort = option_get_int ("spice", "port");
params.useSpiceInput = option_get_bool("spice", "input" );
params.useSpiceClipboard = option_get_bool("spice", "clipboard");
if (params.useSpiceClipboard)
{
params.clipboardToVM = option_get_bool("spice", "clipboardToVM" );
params.clipboardToLocal = option_get_bool("spice", "clipboardToLocal");
if (!params.clipboardToVM && !params.clipboardToLocal)
params.useSpiceClipboard = false;
}
params.scaleMouseInput = option_get_bool("spice", "scaleCursor");
params.captureOnStart = option_get_bool("spice", "captureOnStart");
}
return true;
}
void config_free()
{
option_free();
}
static void doLicense()
{
fprintf(stderr,
"\n"
"Looking Glass - KVM FrameRelay (KVMFR) Client\n"
"Copyright(C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>\n"
"https://looking-glass.hostfission.com\n"
"\n"
"This program is free software; you can redistribute it and / or modify it under\n"
"the terms of the GNU General Public License as published by the Free Software\n"
"Foundation; either version 2 of the License, or (at your option) any later\n"
"version.\n"
"\n"
"This program is distributed in the hope that it will be useful, but WITHOUT ANY\n"
"WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A\n"
"PARTICULAR PURPOSE.See the GNU General Public License for more details.\n"
"\n"
"You should have received a copy of the GNU General Public License along with\n"
"this program; if not, write to the Free Software Foundation, Inc., 59 Temple\n"
"Place, Suite 330, Boston, MA 02111 - 1307 USA\n"
"\n"
);
}
static bool optRendererParse(struct Option * opt, const char * str)
{
if (!str)
return false;
if (strcasecmp(str, "auto") == 0)
{
params.forceRenderer = false;
return true;
}
for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i)
if (strcasecmp(str, LG_Renderers[i]->get_name()) == 0)
{
params.forceRenderer = true;
params.forceRendererIndex = i;
return true;
}
return false;
}
static StringList optRendererValues(struct Option * opt)
{
StringList sl = stringlist_new(false);
// this typecast is safe as the stringlist doesn't own the values
for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i)
stringlist_push(sl, (char *)LG_Renderers[i]->get_name());
return sl;
}
static char * optRendererToString(struct Option * opt)
{
if (!params.forceRenderer)
return strdup("auto");
if (params.forceRendererIndex >= LG_RENDERER_COUNT)
return NULL;
return strdup(LG_Renderers[params.forceRendererIndex]->get_name());
}
static bool optPosParse(struct Option * opt, const char * str)
{
if (!str)
return false;
if (strcmp(str, "center") == 0)
{
params.center = true;
return true;
}
if (sscanf(str, "%dx%d", &params.x, &params.y) == 2)
{
params.center = false;
return true;
}
return false;
}
static StringList optPosValues(struct Option * opt)
{
StringList sl = stringlist_new(false);
stringlist_push(sl, "center");
stringlist_push(sl, "<left>x<top>, ie: 100x100");
return sl;
}
static char * optPosToString(struct Option * opt)
{
if (params.center)
return strdup("center");
int len = snprintf(NULL, 0, "%dx%d", params.x, params.y);
char * str = malloc(len + 1);
sprintf(str, "%dx%d", params.x, params.y);
return str;
}
static bool optSizeParse(struct Option * opt, const char * str)
{
if (!str)
return false;
if (sscanf(str, "%dx%d", &params.w, &params.h) == 2)
{
if (params.w < 1 || params.h < 1)
return false;
return true;
}
return false;
}
static StringList optSizeValues(struct Option * opt)
{
StringList sl = stringlist_new(false);
stringlist_push(sl, "<left>x<top>, ie: 100x100");
return sl;
}
static char * optSizeToString(struct Option * opt)
{
int len = snprintf(NULL, 0, "%dx%d", params.w, params.h);
char * str = malloc(len + 1);
sprintf(str, "%dx%d", params.w, params.h);
return str;
}
static char * optScancodeToString(struct Option * opt)
{
char * str;
alloc_sprintf(&str, "%d = %s", opt->value.x_int, SDL_GetScancodeName(opt->value.x_int));
return str;
}

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -17,7 +17,8 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include "lg-font.h"
#include <stdbool.h>
extern const LG_Font * LG_Fonts[];
void config_init();
bool config_load(int argc, char * argv[]);
void config_free();

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -17,8 +17,7 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "lg-renderer.h"
#include <stdbool.h>
#include <string.h>
bool LG_RendererValidatorBool(const char * value)

View File

@@ -1,6 +1,6 @@
/*
KVMGFX Client - A KVM Client for VGA Passthrough
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -19,7 +19,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "ll.h"
#include "utils.h"
#include "common/locking.h"
#include <stdlib.h>
#include <assert.h>
@@ -157,4 +157,4 @@ bool ll_walk(struct ll * list, void ** data)
LG_UNLOCK(list->lock);
return true;
}
}

1700
client/src/main.c Normal file

File diff suppressed because it is too large Load Diff

182
client/src/main.h Normal file
View File

@@ -0,0 +1,182 @@
/*
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 <stdbool.h>
#include <stdatomic.h>
#include <SDL2/SDL.h>
#include "interface/app.h"
#include "dynamic/renderers.h"
#include "dynamic/clipboards.h"
#include "common/ivshmem.h"
#include "spice/spice.h"
#include <lgmp/client.h>
enum RunState
{
APP_STATE_RUNNING,
APP_STATE_RESTART,
APP_STATE_SHUTDOWN
};
struct CursorInfo
{
int x , y;
int hx, hy;
};
enum WarpState
{
WARP_STATE_ARMED,
WARP_STATE_ON,
WARP_STATE_ACTIVE,
WARP_STATE_OFF
};
struct AppState
{
enum RunState state;
bool ignoreInput;
bool escapeActive;
SDL_Scancode escapeAction;
KeybindHandle bindings[SDL_NUM_SCANCODES];
bool keyDown[SDL_NUM_SCANCODES];
bool haveSrcSize;
int windowW, windowH;
SDL_Point srcSize;
LG_RendererRect dstRect;
struct CursorInfo cursor;
bool cursorVisible;
bool serverMode;
bool haveCursorPos;
bool drawCursor;
bool cursorInView;
bool updateCursor;
bool initialCursorSync;
float scaleX, scaleY;
float accX, accY;
int curLastX;
int curLastY;
bool haveCurLocal;
int curLocalX;
int curLocalY;
bool haveAligned;
enum WarpState warpState;
int warpFromX, warpFromY;
int warpToX , warpToY;
const LG_Renderer * lgr;
void * lgrData;
bool lgrResize;
const LG_Clipboard * lgc;
SpiceDataType cbType;
struct ll * cbRequestList;
SDL_SysWMinfo wminfo;
SDL_Window * window;
struct IVSHMEM shm;
PLGMPClient lgmp;
PLGMPClientQueue frameQueue;
PLGMPClientQueue pointerQueue;
atomic_uint_least64_t frameTime;
uint64_t lastFrameTime;
uint64_t renderTime;
uint64_t frameCount;
uint64_t renderCount;
uint64_t resizeTimeout;
bool resizeDone;
KeybindHandle kbFS;
KeybindHandle kbInput;
KeybindHandle kbQuit;
KeybindHandle kbMouseSensInc;
KeybindHandle kbMouseSensDec;
KeybindHandle kbCtrlAltFn[12];
int mouseSens;
float sensX, sensY;
};
struct AppParams
{
bool autoResize;
bool allowResize;
bool keepAspect;
bool forceAspect;
bool borderless;
bool fullscreen;
bool maximize;
bool minimizeOnFocusLoss;
bool center;
int x, y;
unsigned int w, h;
unsigned int fpsMin;
bool showFPS;
bool useSpiceInput;
bool useSpiceClipboard;
const char * spiceHost;
unsigned int spicePort;
bool clipboardToVM;
bool clipboardToLocal;
bool scaleMouseInput;
bool hideMouse;
bool ignoreQuit;
bool noScreensaver;
bool grabKeyboard;
SDL_Scancode escapeKey;
bool showAlerts;
bool captureOnStart;
unsigned int cursorPollInterval;
unsigned int framePollInterval;
bool forceRenderer;
unsigned int forceRendererIndex;
const char * windowTitle;
int mouseSens;
bool mouseRedraw;
};
struct CBRequest
{
SpiceDataType type;
LG_ClipboardReplyFn replyFn;
void * opaque;
};
struct KeybindHandle
{
SDL_Scancode key;
SuperEventFn callback;
void * opaque;
};
// forwards
extern struct AppState state;
extern struct AppParams params;

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -18,7 +18,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "utils.h"
#include "debug.h"
#include "common/debug.h"
#include <stdlib.h>
#include <stdio.h>

View File

@@ -1,99 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 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 <time.h>
#include <stdint.h>
#include <stdbool.h>
static inline uint64_t microtime()
{
struct timespec time;
clock_gettime(CLOCK_MONOTONIC_RAW, &time);
return ((uint64_t)time.tv_sec * 1000000) + (time.tv_nsec / 1000);
}
static inline uint64_t nanotime()
{
struct timespec time;
clock_gettime(CLOCK_MONOTONIC_RAW, &time);
return ((uint64_t)time.tv_sec * 1e9) + time.tv_nsec;
}
static inline void nsleep(uint64_t ns)
{
const struct timespec ts =
{
.tv_sec = ns / 1e9,
.tv_nsec = ns - ((ns / 1e9) * 1e9)
};
nanosleep(&ts, NULL);
}
#ifdef ATOMIC_LOCKING
#define LG_LOCK_MODE "Atomic"
typedef volatile int LG_Lock;
#define LG_LOCK_INIT(x) (x) = 0
#define LG_LOCK(x) while(__sync_lock_test_and_set(&(x), 1)) {nsleep(100);}
#define LG_UNLOCK(x) __sync_lock_release(&x)
#define LG_LOCK_FREE(x)
#else
#include <SDL2/SDL.h>
#define LG_LOCK_MODE "Mutex"
typedef SDL_mutex * LG_Lock;
#define LG_LOCK_INIT(x) (x = SDL_CreateMutex())
#define LG_LOCK(x) SDL_LockMutex(x)
#define LG_UNLOCK(x) SDL_UnlockMutex(x)
#define LG_LOCK_FREE(x) SDL_DestroyMutex(x)
#endif
static inline uint32_t get_bit(const uint8_t * const base, size_t * const offset)
{
uint32_t out = ((*(base + (*offset >> 0x3))) >> (0x7 - (*offset & 0x7))) & 0x1;
++*offset;
return out;
}
static inline uint32_t get_bits(const uint8_t * const base, size_t * const offset, const uint8_t bits)
{
uint32_t value = 0;
for (int i = 0; i < bits; ++i)
value |= (get_bit(base, offset) ? 1 : 0) << (bits - i - 1);
return value;
}
static inline uint32_t decode_u_golomb(const uint8_t * const base, size_t * const offset)
{
uint32_t i = 0;
while(get_bit(base, offset) == 0)
++i;
return ((1 << i) - 1 + get_bits(base, offset, i));
}
static inline int32_t decode_s_golomb(const uint8_t * const base, size_t * const offset)
{
const uint32_t g = decode_u_golomb(base, offset);
return (g & 0x1) ? (g + 1) / 2 : -(g / 2);
}
// reads the specified file into a new buffer
// the callee must free the buffer
bool file_get_contents(const char * filename, char ** buffer, size_t * length);

31
common/CMakeLists.txt Normal file
View File

@@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.0)
project(lg_common LANGUAGES C)
include_directories(
${PROJECT_SOURCE_DIR}/include
)
add_definitions(-D_GNU_SOURCE)
if(ENABLE_BACKTRACE)
add_definitions(-DENABLE_BACKTRACE)
endif()
add_subdirectory(src/platform)
set(COMMON_SOURCES
src/stringutils.c
src/stringlist.c
src/option.c
src/framebuffer.c
)
add_library(lg_common STATIC ${COMMON_SOURCES})
target_link_libraries(lg_common lg_common_platform)
target_include_directories(lg_common
INTERFACE
include
PRIVATE
src
)

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR)
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -20,8 +20,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdint.h>
#define KVMFR_HEADER_MAGIC "[[KVMFR]]"
#define KVMFR_HEADER_VERSION 8
#define LGMP_Q_POINTER 1
#define LGMP_Q_FRAME 2
typedef enum FrameType
{
@@ -34,6 +34,14 @@ typedef enum FrameType
}
FrameType;
enum
{
CURSOR_FLAG_POSITION = 0x1,
CURSOR_FLAG_VISIBLE = 0x2,
CURSOR_FLAG_SHAPE = 0x4
};
typedef uint32_t KVMFRCursorFlags;
typedef enum CursorType
{
CURSOR_TYPE_COLOR ,
@@ -42,49 +50,35 @@ typedef enum CursorType
}
CursorType;
#define KVMFR_CURSOR_FLAG_UPDATE 1 // cursor update available
#define KVMFR_CURSOR_FLAG_VISIBLE 2 // cursor is visible
#define KVMFR_CURSOR_FLAG_SHAPE 4 // shape updated
#define KVMFR_CURSOR_FLAG_POS 8 // position updated
#define KVMFR_MAGIC "KVMFR---"
#define KVMFR_VERSION 3
typedef struct KVMFR
{
char magic[8];
uint32_t version;
char hostver[32];
}
KVMFR;
typedef struct KVMFRCursor
{
uint8_t flags; // KVMFR_CURSOR_FLAGS
int16_t x, y; // cursor x & y position
uint32_t version; // shape version
CursorType type; // shape buffer data type
int8_t hx, hy; // shape hotspot x & y
uint32_t width; // width of the shape
uint32_t height; // height of the shape
uint32_t pitch; // row length in bytes of the shape
uint64_t dataPos; // offset to the shape data
}
KVMFRCursor;
#define KVMFR_FRAME_FLAG_UPDATE 1 // frame update available
typedef struct KVMFRFrame
{
uint8_t flags; // KVMFR_FRAME_FLAGS
FrameType type; // the frame data type
uint32_t width; // the width
uint32_t height; // the height
uint32_t stride; // the row stride (zero if compressed data)
uint32_t pitch; // the row pitch (stride in bytes or the compressed frame size)
uint64_t dataPos; // offset to the frame
FrameType type; // the frame data type
uint32_t width; // the width
uint32_t height; // the height
uint32_t stride; // the row stride (zero if compressed data)
uint32_t pitch; // the row pitch (stride in bytes or the compressed frame size)
uint32_t offset; // offset from the start of this header to the FrameBuffer header
}
KVMFRFrame;
#define KVMFR_HEADER_FLAG_RESTART 1 // restart signal from client
#define KVMFR_HEADER_FLAG_READY 2 // ready signal from client
#define KVMFR_HEADER_FLAG_PAUSED 4 // capture has been paused by the host
typedef struct KVMFRHeader
{
char magic[sizeof(KVMFR_HEADER_MAGIC)];
uint32_t version; // version of this structure
uint8_t flags; // KVMFR_HEADER_FLAGS
KVMFRFrame frame; // the frame information
KVMFRCursor cursor; // the cursor information
}
KVMFRHeader;

View File

@@ -0,0 +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
*/
#include <stdbool.h>
bool installCrashHandler(const char * exe);

View File

@@ -1,6 +1,6 @@
/*
KVMGFX Client - A KVM Client for VGA Passthrough
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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
@@ -17,9 +17,15 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#if _WIN32
#include <stdio.h>
#include <inttypes.h>
#include "time.h"
#if defined(_WIN32) && !defined(__GNUC__)
#define DIRECTORY_SEPARATOR '\\'
#else
#define DIRECTORY_SEPARATOR '/'
@@ -47,8 +53,9 @@ Place, Suite 330, Boston, MA 02111-1307 USA
sizeof(s) > 20 && (s)[sizeof(s)-21] == DIRECTORY_SEPARATOR ? (s) + sizeof(s) - 20 : \
sizeof(s) > 21 && (s)[sizeof(s)-22] == DIRECTORY_SEPARATOR ? (s) + sizeof(s) - 21 : (s))
#define DEBUG_PRINT(type, fmt, ...) do {fprintf(stderr, type " %20s:%-4u | %-30s | " fmt "\n", STRIPPATH(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__);} while (0)
#define DEBUG_PRINT(type, fmt, ...) do {fprintf(stderr, "%12" PRId64 " " type " %20s:%-4u | %-30s | " fmt "\n", microtime(), STRIPPATH(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__);} while (0)
#define DEBUG_BREAK() DEBUG_PRINT("[ ]", "%s", "================================================================================")
#define DEBUG_INFO(fmt, ...) DEBUG_PRINT("[I]", fmt, ##__VA_ARGS__)
#define DEBUG_WARN(fmt, ...) DEBUG_PRINT("[W]", fmt, ##__VA_ARGS__)
#define DEBUG_ERROR(fmt, ...) DEBUG_PRINT("[E]", fmt, ##__VA_ARGS__)
@@ -59,8 +66,3 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#else
#define DEBUG_PROTO(fmt, ...) do {} while(0)
#endif
#ifdef _WIN32
#include "Util.h"
#define DEBUG_WINERROR(x, y) Util::DebugWinError(STRIPPATH(__FILE__), __LINE__, __FUNCTION__, x, y)
#endif

View File

@@ -0,0 +1,42 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2020 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 <time.h>
#define TIMEOUT_INFINITE ((unsigned int)~0)
typedef struct LGEvent LGEvent;
LGEvent * lgCreateEvent(bool autoReset, unsigned int msSpinTime);
void lgFreeEvent (LGEvent * handle);
bool lgWaitEvent (LGEvent * handle, unsigned int timeout);
bool lgWaitEvents (LGEvent * handles[], int count, bool waitAll, unsigned int timeout);
bool lgSignalEvent(LGEvent * handle);
bool lgResetEvent (LGEvent * handle);
// os specific method to wrap/convert a native event into a LGEvent
// for windows this is an event HANDLE
LGEvent * lgWrapEvent(void * handle);
// Posix specific, not implmented/possible in windows
bool lgWaitEventAbs(LGEvent * handle, struct timespec * ts);
bool lgWaitEventNS (LGEvent * handle, unsigned int timeout);

View File

@@ -0,0 +1,60 @@
/*
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
*/
#pragma once
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
typedef struct stFrameBuffer FrameBuffer;
typedef bool (*FrameBufferReadFn)(void * opaque, const void * src, size_t size);
/**
* The size of the FrameBuffer struct
*/
extern const size_t FrameBufferStructSize;
/**
* Wait for the framebuffer to fill to the specified size
*/
void framebuffer_wait(const FrameBuffer * frame, size_t size);
/**
* Read data from the KVMFRFrame into the dst buffer
*/
bool framebuffer_read(const FrameBuffer * frame, void * dst, size_t dstpitch,
size_t height, size_t width, size_t bpp, size_t pitch);
/**
* Read data from the KVMFRFrame using a callback
*/
bool framebuffer_read_fn(const FrameBuffer * frame, size_t height, size_t width,
size_t bpp, size_t pitch, FrameBufferReadFn fn, void * opaque);
/**
* Prepare the framebuffer for writing
*/
void framebuffer_prepare(FrameBuffer * frame);
/**
* Write data from the src buffer into the KVMFRFrame
*/
bool framebuffer_write(FrameBuffer * frame, const void * src, size_t size);

View File

@@ -1,6 +1,6 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
Copyright (C) 2017-2020 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
@@ -17,14 +17,20 @@ 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 <stdint.h>
#pragma once
struct spice_password
#include <stdbool.h>
struct IVSHMEM
{
char * data;
unsigned int size;
void * mem;
// internal use
void * opaque;
};
bool spice_rsa_encrypt_password(uint8_t * pub_key, char * password, struct spice_password * result);
void spice_rsa_free_password(struct spice_password * pass);
void ivshmemOptionsInit();
bool ivshmemOpen(struct IVSHMEM * dev);
bool ivshmemOpenDev(struct IVSHMEM * dev, const char * shmDev);
void ivshmemClose(struct IVSHMEM * dev);

View File

@@ -0,0 +1,40 @@
/*
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 "time.h"
#include <stdatomic.h>
#define LG_LOCK_MODE "Atomic"
typedef atomic_flag LG_Lock;
#define LG_LOCK_INIT(x) atomic_flag_clear(&(x))
#define LG_LOCK(x) \
while(atomic_flag_test_and_set_explicit(&(x), memory_order_acquire)) { ; }
#define LG_UNLOCK(x) \
atomic_flag_clear_explicit(&(x), memory_order_release);
#define LG_LOCK_FREE(x)
#define INTERLOCKED_INC(x) atomic_fetch_add((x), 1)
#define INTERLOCKED_DEC(x) atomic_fetch_sub((x), 1)
#define INTERLOCKED_SECTION(lock, x) \
LG_LOCK(lock) \
x \
LG_UNLOCK(lock)

View File

@@ -1,6 +1,6 @@
/*
KVMGFX Client - A KVM Client for VGA Passthrough
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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

View File

@@ -0,0 +1,84 @@
/*
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
*/
#include <stdbool.h>
#include "common/stringlist.h"
enum OptionType
{
OPTION_TYPE_NONE = 0,
OPTION_TYPE_INT,
OPTION_TYPE_STRING,
OPTION_TYPE_BOOL,
OPTION_TYPE_CUSTOM
};
struct Option;
struct Option
{
char * module;
char * name;
char * description;
const char shortopt;
enum OptionType type;
union
{
int x_int;
char * x_string;
bool x_bool;
void * x_custom;
}
value;
bool (*parser )(struct Option * opt, const char * str);
bool (*validator)(struct Option * opt, const char ** error);
char * (*toString )(struct Option * opt);
StringList (*getValues)(struct Option * opt);
void (*printHelp)();
// internal use only
bool failed_set;
};
// register an NULL terminated array of options
bool option_register(struct Option options[]);
// lookup the value of an option
struct Option * option_get (const char * module, const char * name);
int option_get_int (const char * module, const char * name);
const char * option_get_string(const char * module, const char * name);
bool option_get_bool (const char * module, const char * name);
// called by the main application to parse the command line arguments
bool option_parse(int argc, char * argv[]);
// called by the main application to load configuration from a file
bool option_load(const char * filename);
// called by the main application to validate the option values
bool option_validate();
// print out the options, help, and their current values
void option_print();
// final cleanup
void option_free();

View File

@@ -0,0 +1,28 @@
/*
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
*/
#include <stdbool.h>
typedef struct StringList * StringList;
StringList stringlist_new (bool owns_strings);
void stringlist_free (StringList * sl);
int stringlist_push (StringList sl, char * str);
unsigned int stringlist_count(StringList sl);
char * stringlist_at (StringList sl, unsigned int index);

View File

@@ -0,0 +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
*/
// sprintf but with buffer allocation
int alloc_sprintf(char ** str, const char * format, ...)
__attribute__ ((format (printf, 2, 3)));

View File

@@ -0,0 +1,24 @@
/*
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
*/
// returns the maximum number of multisamples supported by the system
int sysinfo_gfx_max_multisample();
// returns the page size
long sysinfo_getPageSize();

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