Compare commits

..

818 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
Geoffrey McRae
507732587e [client] egl: fixed uninitialized variable bug 2018-12-16 12:17:12 +11:00
Geoffrey McRae
d1e3508d55 [client] fix early render issue 2018-12-16 11:02:51 +11:00
Geoffrey McRae
3a8998f1f9 [client] make EGL the default renderer 2018-12-16 11:02:40 +11:00
Geoffrey McRae
de5795e368 [client] egl: implemented alerts and some minor fixes to fps 2018-12-16 10:57:01 +11:00
Geoffrey McRae
fca71e2b95 [client] egl: slight correction to splash 2018-12-16 00:56:35 +11:00
Geoffrey McRae
0e2b371e59 [client] egl: added splash screen rendering 2018-12-16 00:54:37 +11:00
Geoffrey McRae
e1fa6b4057 [client] egl: fix cursor regression 2018-12-13 02:11:37 +11:00
Geoffrey McRae
b6c8d3fae5 [client] egl: fix mono cursor double height regression 2018-12-13 01:39:52 +11:00
Geoffrey McRae
eb1c61f335 [client] warn about poor FPS display performance 2018-12-13 01:34:23 +11:00
Geoffrey McRae
5842ce23a3 [client] pre-calculate the frame time 2018-12-13 01:34:14 +11:00
Geoffrey McRae
692d48df87 [client] don't calculate FPS if we are not showing it 2018-12-13 01:28:00 +11:00
Geoffrey McRae
49bd091359 [client] use clock_nanosleep for more accurate frame timing 2018-12-13 01:22:57 +11:00
Geoffrey McRae
5fe2db7e56 [client] rename fps variables to be more correct 2018-12-12 23:59:22 +11:00
Geoffrey McRae
b927f991d6 Revert "[client] improve fps target accuracy"
This reverts commit 4d7e1054bd.
This causes FPS runaway after a time
2018-12-12 23:40:29 +11:00
Geoffrey McRae
4d7e1054bd [client] improve fps target accuracy 2018-12-12 23:33:35 +11:00
Geoffrey McRae
42fa0e1d1f [client] egl: corrected fps alpha blending 2018-12-12 22:38:08 +11:00
Geoffrey McRae
abfe3a9b4d [client] egl: moved desktop rendering into seperate unit 2018-12-12 21:41:51 +11:00
Geoffrey McRae
b9f8f1a0ad [client] egl: add and use default quad helper for models 2018-12-12 20:08:52 +11:00
Geoffrey McRae
608b67af77 [client] egl: moved fps code into seperate unit 2018-12-12 20:04:43 +11:00
Geoffrey McRae
2a65e39848 [client] egl: added missing files from last commit 2018-12-12 18:57:31 +11:00
Geoffrey McRae
c23bf6a0c4 [client] egl: migrate cursor code into seperate unit 2018-12-12 18:53:55 +11:00
Geoffrey McRae
50c460df5a [client] define GL_GLEXT_PROTOTYPES globally 2018-12-12 16:53:30 +11:00
Geoffrey McRae
61f0577ab2 [client] egl: costmetics 2018-12-12 16:41:29 +11:00
Geoffrey McRae
a9aab3c1ee [client] egl: moved egl sources into subdirectory 2018-12-12 16:39:04 +11:00
Geoffrey McRae
73da86ac0e [client] egl: add checking to egl_shader_get_uniform_location 2018-12-12 16:32:16 +11:00
Geoffrey McRae
43d08df6b3 [client] egl: rename "shader" to "this" for consistancy 2018-12-12 16:31:25 +11:00
Geoffrey McRae
4654f317ca [client] opengl: fixed incorrect colors and added 10-bit RGBA support 2018-12-12 10:55:18 +11:00
Geoffrey McRae
d2b83027b4 [client] egl: removed accidental commit of test code 2018-12-11 16:35:53 +11:00
Geoffrey McRae
7be930a69c [client] removed use of now removed frame type, fixed #105 2018-12-11 16:34:41 +11:00
Geoffrey McRae
a1b1ed0060 [host] initiate the texture copy earlier 2018-12-07 20:54:30 +11:00
Geoffrey McRae
2cb18a3f8f [host] removed incomplete h264 2018-12-07 20:54:30 +11:00
Geoffrey McRae
2a30bb718a [host] updated to take advantage of DXGI v1.5 2018-12-04 21:26:46 +11:00
Geoffrey McRae
75ffcacfe4 [client] added support for RGBA, BGRA and 10-bit RGBA 2018-12-04 21:24:01 +11:00
Geoffrey McRae
1beeac545d [client] added missing lg-fonts.c to the repo 2018-11-20 22:34:01 +11:00
Geoffrey McRae
ab98c87e7c [client] egl: added FPS rendering 2018-11-20 09:50:09 +11:00
Geoffrey McRae
5b453d604e [client] remove other render modes from font ABI 2018-11-20 05:50:22 +11:00
Geoffrey McRae
90fc2a8164 [client] move FPS calculations out of renderers 2018-11-20 05:26:51 +11:00
Geoffrey McRae
0ed9301ed9 [client] font: implemented font ABI and updated OpenGL to use it 2018-11-20 04:38:53 +11:00
Geoffrey McRae
d235d076c4 [host] simplify capture logic and fix re-init bug 2018-11-02 21:38:02 +11:00
Geoffrey McRae
9f67f42f94 [host] fix hang on capture error 2018-10-19 21:16:47 +11:00
Geoffrey McRae
31a25c94c6 [host] fix failure to re-init 2018-10-19 21:16:42 +11:00
Geoffrey McRae
6a9f687eae [host] increase cursor ring size to avoid a race 2018-10-19 20:20:01 +11:00
Geoffrey McRae
df7e9b1184 [host] remove unused critical sections 2018-10-19 20:15:42 +11:00
Geoffrey McRae
1350ba6c4b [host] accumulate cursor updates rather then queue 2018-10-19 20:14:43 +11:00
Geoffrey McRae
2692ccc7b3 [client] use mouse visibility info properly 2018-10-09 18:33:18 +11:00
Geoffrey McRae
f36fd5ac1a [host] correct cursor visibility information 2018-10-09 18:28:08 +11:00
Geoffrey McRae
0e8678b182 [host] correct mouse position with hotspot offset 2018-10-09 18:10:59 +11:00
Geoffrey McRae
ce4f1be2a6 [host] fix cursor visibility bug 2018-10-09 17:52:13 +11:00
Geoffrey McRae
db907b1b67 [host] improve mouse sync with the client 2018-10-09 17:48:59 +11:00
Geoffrey McRae
d8b4d0c1ce [client] consume all SDL events in the filter 2018-10-04 17:18:09 +10:00
Geoffrey McRae
fb37174e5f [dxgi] cleaned up retry logic 2018-10-04 17:05:32 +10:00
Geoffrey McRae
9613127162 [client] better usage of SDL event loops 2018-10-04 17:03:09 +10:00
Geoffrey McRae
4e7de236d3 [egl] implement window positioning 2018-10-04 02:31:37 +10:00
Geoffrey McRae
741dfd418d [egl] improve texture upload performance 2018-10-04 00:09:47 +10:00
Geoffrey McRae
1d6dfa048e [client] tighten timings 2018-10-04 00:09:47 +10:00
Geoffrey McRae
8f0a6cd810 [host] general performance improvements 2018-10-04 00:07:34 +10:00
Geoffrey McRae
471303a179 [host] better sync, helps enormously with 4K!!! 2018-09-30 03:50:43 +10:00
Geoffrey McRae
73a2597c8a [dxgi] fix crash caused by failure to release in some instances. 2018-09-27 12:49:52 +10:00
Geoffrey McRae
3cd152c9d5 [host] DXGI capture improvements 2018-09-26 21:20:17 +10:00
Geoffrey McRae
e70928d603 [egl] fix incorrect xor blending for monochrome cursors 2018-09-25 23:32:45 +10:00
Geoffrey McRae
e2b33348f3 [egl] added monochrome cursor rendering 2018-09-25 23:04:29 +10:00
Geoffrey McRae
3ff712fea5 [egl] fix performance issue with cursor updates and add todo message 2018-09-24 20:26:31 +10:00
Geoffrey McRae
2db26ae37e [egl] fix incorrect mouse size and position scaling 2018-09-24 20:11:42 +10:00
Geoffrey McRae
375b97ca6f [egl] fix incorrect mouse colors 2018-09-24 19:52:44 +10:00
Geoffrey McRae
d331a3dd5a [egl] added intial cursor support 2018-09-24 19:48:11 +10:00
Geoffrey McRae
c0c63fd93b [egl] simplify yuv to rgb shader 2018-09-23 20:56:18 +10:00
Geoffrey McRae
b5a47cae25 [egl] implemented YUV420 decode support in hardware 2018-09-23 20:45:20 +10:00
Geoffrey McRae
1f1c9dfa59 [egl] don't re-create the buffer each frame 2018-09-23 16:56:09 +10:00
Geoffrey McRae
0903b4a610 [egl] make new OpenGL ES renderer available
Note that this renderer is incomplete at this time as it doesn't
render the cursor.
2018-09-23 16:04:20 +10:00
Geoffrey McRae
884ad6557b [egl] cleanup texture API 2018-09-23 15:56:47 +10:00
Geoffrey McRae
00658f3d64 [egl] split out texture code into it's own object 2018-09-23 15:48:44 +10:00
Geoffrey McRae
fff3ec30b8 [egl] added basic shaders and use dma to xfer buffer to the gpu 2018-09-22 18:00:52 +10:00
Geoffrey McRae
26434f7baf [egl] initial commit of new modern OpenGL ES renderer 2018-09-22 16:26:55 +10:00
Andy Chun
f75e2fe8db Default XDG_SESSION_TYPE to unspecified
Minimal systems in cases may not have XDG_SESSION_TYPE set at all, causing the program to segfault at the `strcmp`. This commit sets XDG_SESSION_TYPE to `unspecified` (according to https://www.freedesktop.org/software/systemd/man/pam_systemd.html) if it is not defined in the environment.
2018-08-03 10:02:54 +10:00
Yvan da Silva
0674e04597 Corrects an error in a debug message
* This happened during the last edit.
2018-07-30 08:07:53 +10:00
Yvan da Silva
29f1d6cd42 [client] Adds back support for wayland
* Since LG is now using SDL2, the SDL_VIDEODRIVER must be set.
* This fixes SDL error 'Couldn't find matching GLX visual' when creating the window.
2018-07-30 08:07:53 +10:00
Geoffrey McRae
83592f7e4a [client] cleanup of renderer API for better usage
* Added new on_render_start for render initialization
* Changed on_resize to execute inside the render thread
2018-07-28 14:49:37 +10:00
Geoffrey McRae
13cd50f92c [client] disable multisample after logo is gone
We only use multisample to smooth out the edges of the LG logo, it is
pointless to leave it on after the logo is gone.
2018-07-28 10:36:41 +10:00
Geoffrey McRae
a989914fef [host] remove the invalid usage of SafeRelease
SafeRelease was really useless, derefencing the smart pointers through
the use of & releases the value before SafeRelease get's to it. Instead
either allow the destructor to handle it's release, or explicityly
release it by assigning NULL
2018-07-28 10:27:50 +10:00
Geoffrey McRae
f692284f27 [host] don't uselessly try to scale 1:1 textures 2018-07-28 10:27:50 +10:00
Geoffrey McRae
05bd587c74 [client] implemented initial slow yuv420 support 2018-07-28 08:41:39 +10:00
Geoffrey McRae
d292d46fcb [host] correct YUV output, do not copy padding bytes 2018-07-28 07:48:10 +10:00
Geoffrey McRae
b899a65726 [host] correct RGBtoYUV shader output 2018-07-28 07:47:49 +10:00
Geoffrey McRae
63b4dd633c [host] correct invalid copy size for U & V planes 2018-07-28 07:25:00 +10:00
Geoffrey McRae
eba99f6968 [host] fix compiler warnings 2018-07-28 06:29:34 +10:00
Geoffrey McRae
354bef94ee [host] fixed project Release shader build 2018-07-28 06:25:41 +10:00
Geoffrey McRae
e515cdc8dd [host] added YUV420 output support 2018-07-28 06:19:59 +10:00
Geoffrey McRae
2a03d1c4a9 [host] removed unused shader view 2018-07-28 06:19:59 +10:00
Geoffrey McRae
3e3c409fc4 [host] ignore compiled shader headers 2018-07-28 06:19:59 +10:00
Geoffrey McRae
62e3dd250b [host] remove compiled shader headers from repo 2018-07-28 06:19:59 +10:00
Geoffrey McRae
3799929f59 [host] remove the depth buffer, it's unused 2018-07-28 06:19:59 +10:00
Geoffrey McRae
2019766989 [host] added format converter class 2018-07-28 06:19:58 +10:00
Geoffrey McRae
58c3b37e49 [h264] cosmetics 2018-07-28 06:19:58 +10:00
Geoffrey McRae
c650c2e474 [dxgi/h264] fix failure to re-init h264 correctly 2018-07-28 06:19:58 +10:00
Geoffrey McRae
ef336d552c [parser/nal] no need to check for null before free (fixes #87) 2018-07-26 06:09:12 +10:00
Geoffrey McRae
e4cdc58399 [host] move H264 out of DXGI into seperate class 2018-07-26 05:50:06 +10:00
Geoffrey McRae
48d3403c40 [memcpy] fix error caused by switch to shorter OPs 2018-07-26 05:49:24 +10:00
Geoffrey McRae
af143bdd82 [dxgi] update DXGI to use timeout return value 2018-07-26 03:09:59 +10:00
Geoffrey McRae
343983d9af [host] add timeout return value for repeated frame
This is to allow a repeat frame without incuring an additional
memory copy when the frame is already in shared memory.
2018-07-26 03:08:52 +10:00
Geoffrey McRae
5cabf155ab [host] flag paused when waiting for sec desktop 2018-07-24 01:12:24 +10:00
Geoffrey McRae
60070e6076 [client] implement stream paused alert 2018-07-24 01:09:53 +10:00
Geoffrey McRae
697dbc7a96 Updated README.md 2018-07-23 15:28:36 +10:00
Geoffrey McRae
43593d8aea [host] replaced MultiMemcpy with plain memcpySSE 2018-07-23 15:21:43 +10:00
Geoffrey McRae
1f90010cbd [client] add switch to disable alert messages, fixes #83 2018-07-20 01:09:51 +10:00
Geoffrey McRae
d839026ade [opengl] added fade out to wait screen 2018-07-20 01:01:16 +10:00
Geoffrey McRae
34de213926 [opengl] render alerts and fps on wait screen 2018-07-20 00:10:29 +10:00
Geoffrey McRae
b5ec4dd305 [client] scale up the logo and put a gradient behind it 2018-07-19 23:48:35 +10:00
Geoffrey McRae
023d3f811b [client] render the looking glass logo (almost) 2018-07-19 23:33:51 +10:00
Geoffrey McRae
53c32cc5a4 [client] enable multisampling 2018-07-19 23:33:26 +10:00
Geoffrey McRae
eb6ee8ea46 [client] allow window resize event's before startup 2018-07-19 23:32:42 +10:00
Geoffrey McRae
9f8c20c3e7 [common] no need to use AVX operands here either 2018-07-10 16:38:07 +10:00
Geoffrey McRae
a72ad4e46c [host] replaced use of AVX in memcpySSE
There is no need to use AVX instructions as we are not using the
wider registers. Removing their use removes the requirement to run
a AVX capable guest CPU.
2018-07-10 16:32:50 +10:00
williamvds
b19518a1f8 [client] Allow keyboard capture (#26) 2018-06-05 12:09:19 +10:00
Geoffrey McRae
8a9d0b0bfb [host] fix crash on screen blanking 2018-06-01 00:39:18 +10:00
Geoffrey McRae
14954cc426 [client] fix too early release of OpenGL context
Fixes a problem where resolution changes would require a restart of
the client
2018-05-31 18:54:29 +10:00
Geoffrey McRae
32dca9ea3f [client] only provide linker flags at link time 2018-05-31 18:54:10 +10:00
Geoffrey McRae
d4c41d2d94 [host] improve capture performance
Contrary to the MS documentation, benchmarking shows a substantial
increase in performance when releasing the captured frame as soon
as possible. This change makes it possible to achieve 60FPS at 4K
resolutions.
2018-05-31 18:53:11 +10:00
Txanton
3f331f2e62 Fixed typo in word wish 2018-05-31 13:28:36 +10:00
Geoffrey McRae
d753af9d17 [host] remove debug tracing noise 2018-05-29 18:37:21 +10:00
Geoffrey McRae
b23c7808c0 [host] don't count re-init requests as failures 2018-05-29 18:37:21 +10:00
Geoffrey McRae
792200cac4 [client] use atomic locking by default 2018-05-29 18:18:22 +10:00
Geoffrey McRae
526b607e37 [client] fixed warning/error text 2018-05-29 18:10:58 +10:00
Geoffrey McRae
298885083b [client] remove xlib shim as it is no longer needed 2018-05-29 18:09:27 +10:00
Geoffrey McRae
26c4804892 [client] replace custom Makefile with cmake build 2018-05-29 18:08:26 +10:00
Geoffrey McRae
a507dd0c51 [client] remove h264 decoder for now 2018-05-29 17:44:25 +10:00
Geoffrey McRae
c6830bab16 [client] remove useless linking against libssl 2018-05-29 17:44:07 +10:00
Geoffrey McRae
a0457a2dd9 [client] added nettle/bignum.h to rsa.c, CentOS 7 seems to need it 2018-05-29 14:55:28 +10:00
Geoffrey McRae
ce60cafa19 [client] removed invalid MS Windows specific hint
See: https://wiki.libsdl.org/SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4

> A hint that specifies that SDL should not to generate
> SDL_WINDOWEVENT_CLOSE events for Alt+F4 on Microsoft Windows.
2018-05-29 14:09:44 +10:00
Geoffrey McRae
9e02131525 [client] don't scale the mouse when in capture mode 2018-05-29 11:55:28 +10:00
Geoffrey McRae
6918eeca26 [client] added missing library for floor function 2018-05-29 11:08:56 +10:00
Geoffrey McRae
fbbee1cdac [opengl] added support for alerts 2018-05-29 11:08:25 +10:00
Geoffrey McRae
89959b48a7 [client] added linked list utility 2018-05-29 11:08:01 +10:00
Geoffrey McRae
b26a535451 [client] added support for alert text 2018-05-29 11:06:42 +10:00
Geoffrey McRae
86207993b8 [client] alert the user when capture mode is on or off 2018-05-29 09:02:34 +10:00
Geoffrey McRae
ee9213da76 [client] add alert method to renderer api 2018-05-29 08:59:07 +10:00
Geoffrey McRae
a084b2b32f [client] added the ability to specify the capture key (-m) 2018-05-29 08:51:58 +10:00
Geoffrey McRae
afdae8efc0 [client] allow the system screensaver to operate, -S to disable 2018-05-29 08:34:52 +10:00
Geoffrey McRae
64ad862116 [client] remove noise about scaling/alignment as this is fixed 2018-05-29 08:28:55 +10:00
r4m0n
4d81aaa763 Code cleanup 2018-05-29 02:18:17 +10:00
r4m0n
8cb25792ba Fixing scaled mouse movement 2018-05-29 02:18:17 +10:00
Geoffrey McRae
f715034fc4 [doc] make the module README a Markdown file 2018-05-28 17:51:03 +10:00
Geoffrey McRae
c97ebb135f [doc] Updated module readme 2018-05-28 17:50:32 +10:00
Geoffrey McRae
97749b335a [client] start rendering a little earlier 2018-05-28 15:36:12 +10:00
Geoffrey McRae
a647a602bf [opengl] render a blue screen while waiting for sync 2018-05-28 15:30:31 +10:00
commander kotori
80581a4aa2 [host] ask for unicode with mingw-w64 makefile
Pass -DUNICODE as a CFLAG.  The visual studio project asks for
a unicode (wide-string) build, but the unix makefile did not.

This fixes the build on msys2.
2018-05-28 12:10:52 +10:00
Geoffrey McRae
882b31aeaa [client] add support for masked colour cursors (fixes #61)
Also allows early SDL usage for cursor and keyboard control before
the host application starts
2018-05-28 11:40:56 +10:00
Geoffrey McRae
871aee2aae [host] fixed missed cursor shape updates 2018-05-28 10:34:24 +10:00
Geoffrey McRae
62e67c345c [client] reduce weight of FPS limiter feedback 2018-05-25 08:35:52 +10:00
Geoffrey McRae
5de9a8dce6 [client] prevent usleep underflow in FPS limiter 2018-05-24 18:10:23 +10:00
Geoffrey McRae
3adcbfaa7d [host] cosmetics 2018-05-24 18:08:59 +10:00
Geoffrey McRae
213c220d83 [host] don't assume the capture was successful 2018-05-24 17:05:49 +10:00
Geoffrey McRae
eef18dd655 [host] add critical section for m_cursorInfo 2018-05-24 16:50:50 +10:00
Geoffrey McRae
a4600e7278 [client] added FPS limiter for when running without vsync 2018-05-24 11:56:11 +10:00
Geoffrey McRae
c42bff99e2 [client] adjustments for better sync 2018-05-24 11:26:09 +10:00
Geoffrey McRae
b29f1c62bb [host] update to KVMFR v6 and decouple mouse 2018-05-24 11:24:24 +10:00
Geoffrey McRae
df7183a572 [kvmfr] decouple cursor flags from frame flags and fix timings 2018-05-24 09:01:53 +10:00
Geoffrey McRae
7a5bbb1e59 [client] add unix socket support, fixes #67 2018-05-23 12:16:44 +10:00
Geoffrey McRae
a3cd0385d0 [doc] Fix formatting 2018-05-23 08:46:03 +10:00
Geoffrey McRae
8fdc11813d [doc] Rename DEBUGGING file for Markdown formatting 2018-05-23 08:45:07 +10:00
Geoffrey McRae
fb412e8440 [doc] Added some documentaion for how to debug the LG client 2018-05-23 08:44:11 +10:00
Geoffrey McRae
15a337fee8 [host] use the new memcpySSE implementation 2018-05-22 18:59:24 +10:00
Geoffrey McRae
6f141fe393 [client] removed unused define in rsa.c 2018-05-22 15:40:08 +10:00
Geoffrey McRae
9b0f974648 [client] switch to nettle and gmp for RSA EME-OAEP support
Fixes #68
2018-05-22 15:36:36 +10:00
Geoffrey McRae
ceac6a60e6 [spice] initial GnuTLS implementation (incomplete) 2018-05-22 09:49:35 +10:00
Geoffrey McRae
cba6630aa0 [spice] relocate openssl code into seperate function
This is in preperation of switching to an alternative SSL library
as OpenSSL conflicts with the GNU licence.
2018-05-22 09:39:03 +10:00
Geoffrey McRae
6e0eac0abc [common] fix getopt license to be compatible 2018-05-22 03:21:35 +10:00
Geoffrey McRae
b3aadccfc4 [client] use glFinish to prevent buffering and re-enable by default 2018-05-21 23:16:16 +10:00
Geoffrey McRae
116926f7c0 [module] enable write-through mapping for the device
This is the magic I have been looking for the last few weeks, with
this change VM->VM is now useful at 60+fps :D
2018-05-21 21:56:53 +10:00
Geoffrey McRae
d0a7b8df9c [module] added 'test' to Makefile to simplify development testing 2018-05-21 21:56:23 +10:00
Geoffrey McRae
b8a1743d8f [client] fixed crash when specifying the shmFile as a command 2018-05-21 21:46:48 +10:00
Geoffrey McRae
e8b1b8fbdf [common] tune windows memcpySSE asm implementation:wq 2018-05-19 21:40:13 +10:00
Geoffrey McRae
e9d77e6c52 [common] inline memcpy into memcpySSE for the final bytes 2018-05-19 18:31:49 +10:00
Geoffrey McRae
56f0a8525b [common] more SSE improvements
* 32bit inlined is slow for only large copies, warn if memcpySSE is
used when it shouldn't be.

* Removed 64bit memcpySSE as native inlined is faster

See: https://stackoverflow.com/questions/50422510/why-is-i386-memcpy-slow-on-x86-64
2018-05-19 18:27:04 +10:00
Geoffrey McRae
778af24d82 [common] inline get_pc to memcpySSE 2018-05-19 16:21:12 +10:00
Geoffrey McRae
cd6caea4b0 [x86] use a proper call/ret to obtain the current IP
See: https://blogs.msdn.microsoft.com/oldnewthing/20041216-00/?p=36973
2018-05-19 16:16:01 +10:00
Geoffrey McRae
f63c8043af [common] new sse2 memcpy improvements 2018-05-18 20:56:57 +10:00
Geoffrey McRae
3c77c1eb2b NASM version of a SSE2 memcpy 2018-05-18 18:50:07 +10:00
Geoffrey McRae
ffec6c2014 Incoming new memcpy implementation 2018-05-18 01:59:00 +10:00
Geoffrey McRae
d097531926 [client] allow the mouse and keyboard to operate early 2018-05-16 18:19:32 +10:00
Geoffrey McRae
d339ca3599 [client] remove dependencies on libva until h264 is ready 2018-05-16 18:14:08 +10:00
Geoffrey McRae
adb1ca58b9 [opengl-basic] removed basic renderer as it is no longer needed 2018-05-16 18:13:20 +10:00
Geoffrey McRae
70ffe1de43 [h264] disable the module until I or someone finds time to finish it 2018-05-16 18:12:29 +10:00
Geoffrey McRae
cf4d16b528 [opengl] numerous improvements to buffer transfer 2018-05-16 17:58:36 +10:00
Geoffrey McRae
da2bcfdf9a [module] rename defines from LG to KVMFR 2018-05-15 20:53:10 +10:00
Geoffrey McRae
7f81d21aaa [module] added kvmfr kernel module for VM->VM shared memory 2018-05-15 20:50:52 +10:00
Geoffrey McRae
d0756cf00c [main] make it possible to manually specify the memory size 2018-05-15 20:07:48 +10:00
Geoffrey McRae
4fd59ce8c9 [opengl] fix free bug with new contiguous buffer 2018-05-15 19:25:22 +10:00
Geoffrey McRae
adca879fb9 [opengl] use a single contiguous buffer for the AMD pinned buffer 2018-05-15 19:23:57 +10:00
Geoffrey McRae
3a2d612b41 [decoders] change the API to allow more flexability in the future 2018-05-15 19:19:39 +10:00
Geoffrey McRae
332d53e016 [opengl] Add support for AMD_pinned_memory if it is available 2018-05-15 13:23:44 +10:00
Geoffrey McRae
ae1344d1a0 [spice] fix out by one error in call to strncpy 2018-05-15 09:56:42 +10:00
Geoffrey McRae
ae382949c8 [opengl] glxWaitVideoSyncSGI is not well supported in Mesa 18
It seems Mesa 18 has problems with the glxWaitVideoSyncSGI API so
we disable the 'preventBuffer' option by default
2018-05-15 09:54:24 +10:00
Geoffrey McRae
fd8d4d3d38 [client] enable configuration of spice via config 2018-02-05 19:08:31 +11:00
Geoffrey McRae
a2216e4b68 [client] cosmetics 2018-02-05 19:08:15 +11:00
Alam Arias
4fb9fc3b3f [client]: ignore Alt-F4 in ignoreQuit mode (#46)
* [client]: ignore Alt-F4 in ignoreQuit mode

* [client]: turn down the SNR for SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4

* [client]: turn down the SNR for SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS
2018-02-02 10:32:46 +11:00
arcnmx
35b4d75eea [host] mingw-w64 compile fixes (#44)
* [host] rename min() to LG_MIN()

* [host] format string type fixes, %Ix doesn't exist in mingw

* [host] DXGI minor fixes

* [host] mingw lacks media foundation api headers and QISearch
2018-01-30 21:07:46 +11:00
arcnmx
d7321d5f5f [client] fix event loop sleep (#45) 2018-01-30 21:07:14 +11:00
Geoffrey McRae
35eda57cb2 [client] remoted accidential commit of experimental method from header 2018-01-29 17:30:46 +11:00
Geoffrey McRae
78a100135b [client] fix improper spice socket shutdown 2018-01-29 17:27:12 +11:00
Geoffrey McRae
dc6932a9ba [client] no need to supress bin directory creation command 2018-01-29 17:04:08 +11:00
Geoffrey McRae
d765674913 [client] fix concurrent make, fixes #43 2018-01-29 17:02:41 +11:00
Geoffrey McRae
2af522aea7 [client] prevent 100% CPU usage in event loop, fixes #41 2018-01-29 16:56:23 +11:00
arcnmx
9aba969296 [client] fix SDL mutex compile errors (#42) 2018-01-29 16:51:14 +11:00
Geoffrey McRae
2114b73c11 [client] makefile: use $(CC) instead of assuming gcc 2018-01-25 09:58:03 +11:00
Geoffrey McRae
d591e2fd36 [client] added xlib-shim to disable calls to XSync
The compiled xlib-shim.so can be used to intercept and prevent SDL
from calling XSync, which causes latency issues on some video
hardware.

To use specify the full path to the file in the LD_PRELOAD
environment variable, like so:

LD_PRELOAD=/full/path/xlib-shim.so ./looking-glass
2018-01-25 09:55:21 +11:00
Geoffrey McRae
c61d97b0ac [client] spice: add channel and mouse locking
This fixes a race condition which causes the mouse ringbuffer to
overflow. It also corrects out of order message index IDs due to
multiple threads sending messages asyncronously.
2018-01-25 07:41:11 +11:00
Geoffrey McRae
37ea662998 [client] use SDL_SetEventFilter for better mouse performance
This partially resolves lag issues on hosts running the amdgpu driver.
If mouse caputure is enable the lag issue returns, this is because SDL
calls `XSync` in `X11_WarpMouse` and `X11_WarpMouseGlobal`, if these
calls are removed all input lag issues dissapear.

This issue has been reported to SDL as the calls to `XSync` are not
required per the xlib documentation.
2018-01-24 23:46:11 +11:00
Aaron
3d9d275d61 Ignore SDL_MOUSEBUTTONDOWN events incompatible with SPICE (#38)
* Ignore SDL_MOUSEBUTTONDOWN events that aren't compatible with the SPICE PS/2 mouse
2018-01-15 10:55:17 +11:00
Geoffrey McRae
a02087e5e4 [client] h264: unroll silly loop 2018-01-06 13:47:35 +11:00
Geoffrey McRae
2ccf17b9b7 [client] h264: setup slice parameters correctly (incomplete) 2018-01-06 13:43:24 +11:00
Geoffrey McRae
8ccce5666c [client] nal: corrections to parsing logic 2018-01-06 13:40:31 +11:00
Geoffrey McRae
859e984827 [client] h264: vaapi deprecated these fields, FMO is not supported
fbed1dbb5b/va/va.h (L2840)
2018-01-06 09:47:49 +11:00
Geoffrey McRae
5e84cfb3f1 [client] h264: use parameters from nal (incomplete) 2018-01-06 00:11:38 +11:00
Geoffrey McRae
5a84d3bef7 [client] h264: don't treat parse failure as fatal 2018-01-05 23:18:52 +11:00
Geoffrey McRae
634be5b096 [client] nal: removed useless debug print 2018-01-05 23:13:22 +11:00
Geoffrey McRae
80c9e24604 [client] nal: added final SLICE parser features 2018-01-05 23:09:43 +11:00
Geoffrey McRae
5808089fce [client] nal: added SLICE parser 2018-01-05 21:36:26 +11:00
Geoffrey McRae
d6f84ddd12 [client] nal: added PPS parser 2018-01-05 16:18:28 +11:00
Geoffrey McRae
c809eeb2a8 [client] fixed nal parser memory leaks 2018-01-05 15:27:36 +11:00
Geoffrey McRae
2dfb1cf1a6 [client] Makefile: stop at the first error 2018-01-05 11:45:30 +11:00
Geoffrey McRae
50ba9b4899 [client] added initial NAL unit parser 2018-01-05 11:36:45 +11:00
Geoffrey McRae
a36d312844 [host] dxgi: fixed missed header update for timeout fix 2018-01-04 09:30:55 +11:00
Geoffrey McRae
6653340bac [host] dxgi: follow suit and use h264 high profile 2018-01-04 09:30:27 +11:00
Geoffrey McRae
b9723adc30 [client] h264: use high profile as it's more compatible on newer cards 2018-01-04 09:27:24 +11:00
Geoffrey McRae
7648ea712c [client] opengl: fix termination on configure failure 2018-01-04 09:25:42 +11:00
Geoffrey McRae
3f29897506 [client] fix application termination on error 2018-01-04 09:25:17 +11:00
Geoffrey McRae
bebbdc4089 [client] h264 switch to contrained decoder as baseline is deprecated 2018-01-01 12:56:26 +11:00
Geoffrey McRae
9000fdf6fc [host] fix frame duplication problem with new MFT implementation 2017-12-31 00:32:39 +11:00
Geoffrey McRae
fbf08b94aa [client] initial vaapi h264 decode support (unfinished) 2017-12-31 00:27:26 +11:00
Geoffrey McRae
a6d2fe73ae [common] fixed bug in memcpySSE skipping remaining bytes 2017-12-30 18:39:57 +11:00
Geoffrey McRae
e854723aa3 [client] fixed incorrect cursor dataPos validation 2017-12-30 13:48:32 +11:00
Geoffrey McRae
9b7f54fa35 [host] service restarts now restart capture interfaces 2017-12-30 13:35:45 +11:00
Geoffrey McRae
9ef9f60505 [host] dxgi: fixed MFT memory leak and re-init failure 2017-12-30 13:35:45 +11:00
Geoffrey McRae
076a45acc5 [client] added initial decoder framework 2017-12-29 22:48:21 +11:00
Geoffrey McRae
c239306d82 [client] initial support for compressed frames 2017-12-29 21:20:51 +11:00
Geoffrey McRae
b5f2092e9c [host] return the compressed frame size in the pitch field 2017-12-29 21:01:02 +11:00
Geoffrey McRae
03622f61b0 [host] Added experimental H264 compression to DXGI (disabled by default)
This is not yet working, the client is yet to be updated to support
decompressing this stream.
2017-12-29 20:53:52 +11:00
Geoffrey McRae
3d9230ac93 [host] dxgi: fixed frame update regression 2017-12-29 07:15:59 +11:00
Geoffrey McRae
2d746cbfd4 [host] dxgi: performance improvements 2017-12-29 07:01:13 +11:00
Geoffrey McRae
2f2813037b [host] improved latency of multimemcpy with hybrid locking and preempt 2017-12-29 07:00:27 +11:00
Geoffrey McRae
f6f4c8070a [host] adjusted frame and cursor offset calculations 2017-12-29 06:11:32 +11:00
Geoffrey McRae
40bfdcdf8c [client] added configuration file loading support
the client now will look for a configuration file in the following
locations by default.

* /etc/looking-glass.conf
* ~/.looking-glass.conf

All configuration files are loaded and may override values specified by
any prior configuration files loaded.

Sample Config:

    global:
    {
      fullScreen=false;
      showFPS=true;
      x=0;
      y=0;
      w=800;
      h=600;
    }

    OpenGL:
    {
      mipmap="false";
    }
2017-12-28 19:58:19 +11:00
Geoffrey McRae
59fa025292 [host] add store fence to shm writes as we are using writecombine 2017-12-28 15:42:44 +11:00
Geoffrey McRae
e09d7f0ad0 [host] Update IVSHMEM class to support incoming driver version 2017-12-28 15:42:44 +11:00
Geoffrey McRae
6a6e53f728 [client] removed the dependency on the ivshmem-server
Since we do not use IRQs anymore we can use the ivshmem-plain device
which doesn't need the ivshmem-server. The QEMU arguments now should be
as follows:

-device ivshmem-plain,memdev=ivshmem
-object memory-backend-file,id=ivshmem,share=on,mem-path=/dev/shm/looking-glass,size=32M

Obviously adjusting the memory size as required. It is suggested that
the shared memory file be created before the guest is started with the
appropriate permissions, for example:

touch /dev/shm/looking-glass
chown user:kvm /dev/shm/looking-glass
chmod 660 /dev/shm/looking-glass
2017-12-28 15:34:18 +11:00
Geoffrey McRae
16e804b068 [host] added tracing class to help profile slow code points 2017-12-23 18:15:15 +11:00
Geoffrey McRae
db52a55b36 [client] opengl: remove deprecated glScissor, fixes FPS display 2017-12-23 17:40:50 +11:00
Geoffrey McRae
0574daca13 [client] removed unused function argument 2017-12-23 17:38:25 +11:00
209 changed files with 22523 additions and 8715 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
```

10
.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
module/*.ko
module/*.o
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

@@ -60,9 +60,9 @@ Members of the community that donated the funding to obtain the remaining hardwa
Michael Hillman
Andreas Jacobsen
NikkyAi
Michael Lindman
Michael Lindman
And another 41 people that whish to remain anonymous.
And another 41 people that wish to remain anonymous.
Thank you everyone for making this project possible.
- Geoffrey McRae <geoff@hostfission.com (gnif)

View File

@@ -1,14 +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
* Windows Builds of the host application: https://looking-glass.hostfission.com/downloads
* Getting Started: https://looking-glass.hostfission.com/wiki/Installation
## Donations
I (Geoffrey McRae) am the primary developer behind this project and I have
invested thousands of hours of development time into it.
If you like this project and find it useful and would like to help out you can
support me directly using the following platforms.
* [GitHub](https://github.com/sponsors/gnif)
* [Ko-Fi](https://ko-fi.com/lookingglass)
* [Patreon](https://www.patreon.com/gnif)
* [Paypal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ESQ72XUPGKXRY)
* BTC - 14ZFcYjsKPiVreHqcaekvHGL846u3ZuT13
## Documentation
** 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://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

114
client/CMakeLists.txt Normal file
View File

@@ -0,0 +1,114 @@
cmake_minimum_required(VERSION 3.0)
project(looking-glass-client C)
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
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
x11
)
pkg_check_modules(PKGCONFIG_OPT
xi
)
execute_process(
COMMAND cat ../VERSION
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE BUILD_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
find_package(GMP)
add_definitions(-D BUILD_VERSION='"${BUILD_VERSION}"')
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}/include
${CMAKE_BINARY_DIR}/include
${PKGCONFIG_INCLUDE_DIRS} ${PKGCONFIG_OPT_INCLUDE_DIRS}
${GMP_INCLUDE_DIR}
)
link_libraries(
${PKGCONFIG_LIBRARIES} ${PKGCONFIG_OPT_LIBRARIES}
${GMP_LIBRARIES}
${CMAKE_DL_LIBS}
rt
m
)
set(SOURCES
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} ${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)

32
client/DEBUGGING.md Normal file
View File

@@ -0,0 +1,32 @@
# Debugging the Looking Glass Client
If you are asked to provide debugging information to resolve an issue please
follow the following procedure.
## If you're experiencing a crash:
Run the program under the `gdb` debugger (you may need to install gdb), for
example:
gdb ./looking-glass-client
If you need to set any arguments, do so now by running `set args ARGS`, for
example:
set args -F -k
Now start the program by typing `r`. When the application crashes you will be
dumped back into the debugger, the application may appear to be frozen. Run
the following command:
thread apply all bt
Once you have this information please pastebin the log from looking-glass as
well as the information resulting from this command.
## If you're experencing high CPU load and/or poor performance.
The steps here are identical to the above, except instead of waiting for the
program to crash, in the debugger press `CTRL+C` while the program is
exhibiting the problem, then run `thread apply all bt` and pastebin your log
and the results of the command.

View File

@@ -1,39 +0,0 @@
BINARY = looking-glass-client
CFLAGS = -g -O3 -std=gnu99 -march=native -Wall -Werror -I./ -I../common -DDEBUG -DATOMIC_LOCKING
LDFLAGS = -lrt
CFLAGS += -ffast-math
CFLAGS += -fdata-sections -ffunction-sections
LDFLAGS += -Wl,--gc-sections
LIBS = sdl2 SDL2_ttf gl glu libssl openssl spice-protocol fontconfig x11
CFLAGS += $(shell pkg-config --cflags $(LIBS))
LDFLAGS += $(shell pkg-config --libs $(LIBS))
BUILD ?= .build
BIN ?= bin
CFLAGS += -DBUILD_VERSION='"$(shell git describe --always --long --dirty --abbrev=10 --tags)"'
OBJS = main.o \
lg-renderer.o \
spice/spice.o \
ivshmem/ivshmem.o \
renderers/opengl.o
# renderers/opengl-basic.o
BUILD_OBJS = $(foreach obj,$(OBJS),$(BUILD)/$(obj))
all: $(BIN)/$(BINARY)
$(BUILD)/%.o: %.c
@mkdir -p $(dir $@)
gcc -c $(CFLAGS) -o $@ $<
$(BIN)/$(BINARY): $(BUILD_OBJS)
@mkdir -p $(dir $@)
gcc -o $@ $(BUILD_OBJS) $(LDFLAGS)
clean:
rm -rf $(BUILD) $(BIN)
.PHONY: clean

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,19 @@
# Try to find the GMP librairies
# GMP_FOUND - system has GMP lib
# GMP_INCLUDE_DIR - the GMP include directory
# GMP_LIBRARIES - Libraries needed to use GMP
if (GMP_INCLUDE_DIR AND GMP_LIBRARIES)
# Already in cache, be silent
set(GMP_FIND_QUIETLY TRUE)
endif (GMP_INCLUDE_DIR AND GMP_LIBRARIES)
find_path(GMP_INCLUDE_DIR NAMES gmp.h )
find_library(GMP_LIBRARIES NAMES gmp libgmp )
find_library(GMPXX_LIBRARIES NAMES gmpxx libgmpxx )
MESSAGE(STATUS "GMP libs: " ${GMP_LIBRARIES} " " ${GMPXX_LIBRARIES} )
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GMP DEFAULT_MSG GMP_INCLUDE_DIR GMP_LIBRARIES)
mark_as_advanced(GMP_INCLUDE_DIR GMP_LIBRARIES)

View File

@@ -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}
)

981
client/decoders/src/h264.c Normal file
View File

@@ -0,0 +1,981 @@
/*
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 "lg-decoder.h"
#include "debug.h"
#include "memcpySSE.h"
#include "parsers/nal.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <SDL2/SDL_syswm.h>
#include <va/va_glx.h>
#define SURFACE_NUM 3
struct Inst
{
LG_RendererFormat format;
SDL_Window * window;
VADisplay vaDisplay;
int vaMajorVer, vaMinorVer;
VASurfaceID vaSurfaceID[SURFACE_NUM];
VAConfigID vaConfigID;
VAContextID vaContextID;
int lastSID;
int currentSID;
VAPictureH264 curPic;
VAPictureH264 oldPic;
int frameNum;
int fieldCount;
VABufferID picBufferID[SURFACE_NUM];
VABufferID matBufferID[SURFACE_NUM];
VABufferID sliBufferID[SURFACE_NUM];
VABufferID datBufferID[SURFACE_NUM];
bool t2First;
int sliceType;
NAL nal;
};
static const unsigned char MatrixBufferH264[] = {
//ScalingList4x4[6][16]
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
//ScalingList8x8[2][64]
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
static bool lgd_h264_create (void ** opaque);
static void lgd_h264_destroy (void * opaque);
static bool lgd_h264_initialize (void * opaque, const LG_RendererFormat format, SDL_Window * window);
static void lgd_h264_deinitialize (void * opaque);
static LG_OutFormat lgd_h264_get_out_format (void * opaque);
static unsigned int lgd_h264_get_frame_pitch (void * opaque);
static unsigned int lgd_h264_get_frame_stride(void * opaque);
static bool lgd_h264_decode (void * opaque, const uint8_t * src, size_t srcSize);
static bool lgd_h264_get_buffer (void * opaque, uint8_t * dst, size_t dstSize);
static bool lgd_h264_init_gl_texture (void * opaque, GLenum target, GLuint texture, void ** ref);
static void lgd_h264_free_gl_texture (void * opaque, void * ref);
static bool lgd_h264_update_gl_texture(void * opaque, void * ref);
#define check_surface(x, y, z) _check_surface(__LINE__, x, y, z)
static bool _check_surface(const unsigned int line, struct Inst * this, unsigned int sid, VASurfaceStatus *out)
{
VASurfaceStatus surfStatus;
VAStatus status = vaQuerySurfaceStatus(
this->vaDisplay,
this->vaSurfaceID[sid],
&surfStatus
);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaQuerySurfaceStatus: %s", vaErrorStr(status));
return false;
}
#if 0
DEBUG_INFO("L%d: surface %u status: %d", line, sid, surfStatus);
#endif
if (out)
*out = surfStatus;
return true;
}
static bool lgd_h264_create(void ** opaque)
{
// create our local storage
*opaque = malloc(sizeof(struct Inst));
if (!*opaque)
{
DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct Inst));
return false;
}
memset(*opaque, 0, sizeof(struct Inst));
struct Inst * this = (struct Inst *)*opaque;
this->vaSurfaceID[0] = VA_INVALID_ID;
this->vaConfigID = VA_INVALID_ID;
this->vaContextID = VA_INVALID_ID;
for(int i = 0; i < SURFACE_NUM; ++i)
this->picBufferID[i] =
this->matBufferID[i] =
this->sliBufferID[i] =
this->datBufferID[i] = VA_INVALID_ID;
if (!nal_initialize(&this->nal))
{
DEBUG_INFO("Failed to initialize NAL parser");
free(this);
return false;
}
lgd_h264_deinitialize(this);
return true;
}
static void lgd_h264_destroy(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
nal_deinitialize(this->nal);
lgd_h264_deinitialize(this);
free(this);
}
static bool lgd_h264_initialize(void * opaque, const LG_RendererFormat format, SDL_Window * window)
{
struct Inst * this = (struct Inst *)opaque;
lgd_h264_deinitialize(this);
memcpy(&this->format, &format, sizeof(LG_RendererFormat));
this->window = window;
SDL_SysWMinfo wminfo;
SDL_VERSION(&wminfo.version);
if (!SDL_GetWindowWMInfo(window, &wminfo))
{
DEBUG_ERROR("Failed to get SDL window WM Info");
return false;
}
switch(wminfo.subsystem)
{
case SDL_SYSWM_X11:
this->vaDisplay = vaGetDisplayGLX(wminfo.info.x11.display);
break;
default:
DEBUG_ERROR("Unsupported window subsystem");
return false;
}
VAStatus status;
status = vaInitialize(this->vaDisplay, &this->vaMajorVer, &this->vaMinorVer);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaInitialize Failed");
return false;
}
DEBUG_INFO("Vendor: %s", vaQueryVendorString(this->vaDisplay));
VAEntrypoint entryPoints[5];
int entryPointCount;
status = vaQueryConfigEntrypoints(
this->vaDisplay,
VAProfileH264High,
entryPoints,
&entryPointCount
);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaQueryConfigEntrypoints Failed");
return false;
}
int ep;
for(ep = 0; ep < entryPointCount; ++ep)
if (entryPoints[ep] == VAEntrypointVLD)
break;
if (ep == entryPointCount)
{
DEBUG_ERROR("Failed to find VAEntrypointVLD index");
return false;
}
VAConfigAttrib attrib;
attrib.type = VAConfigAttribRTFormat;
vaGetConfigAttributes(
this->vaDisplay,
VAProfileH264High,
VAEntrypointVLD,
&attrib,
1);
if (!(attrib.value & VA_RT_FORMAT_YUV420))
{
DEBUG_ERROR("Failed to find desired YUV420 RT format");
return false;
}
status = vaCreateConfig(
this->vaDisplay,
VAProfileH264High,
VAEntrypointVLD,
&attrib,
1,
&this->vaConfigID);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaCreateConfig");
return false;
}
status = vaCreateSurfaces(
this->vaDisplay,
VA_RT_FORMAT_YUV420,
this->format.width,
this->format.height,
this->vaSurfaceID,
SURFACE_NUM,
NULL,
0
);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaCreateSurfaces");
return false;
}
for(int i = 0; i < SURFACE_NUM; ++i)
if (!check_surface(this, i, NULL))
return false;
status = vaCreateContext(
this->vaDisplay,
this->vaConfigID,
this->format.width,
this->format.height,
VA_PROGRESSIVE,
this->vaSurfaceID,
SURFACE_NUM,
&this->vaContextID
);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaCreateContext");
return false;
}
this->currentSID = 0;
this->sliceType = 2;
this->t2First = true;
status = vaBeginPicture(this->vaDisplay, this->vaContextID, this->vaSurfaceID[0]);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaBeginPicture");
return false;
}
return true;
}
static void lgd_h264_deinitialize(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
for(int i = 0; i < SURFACE_NUM; ++i)
{
if (this->picBufferID[i] != VA_INVALID_ID)
vaDestroyBuffer(this->vaDisplay, this->picBufferID[i]);
if (this->matBufferID[i] != VA_INVALID_ID)
vaDestroyBuffer(this->vaDisplay, this->matBufferID[i]);
if (this->sliBufferID[i] != VA_INVALID_ID)
vaDestroyBuffer(this->vaDisplay, this->sliBufferID[i]);
if (this->datBufferID[i] != VA_INVALID_ID)
vaDestroyBuffer(this->vaDisplay, this->datBufferID[i]);
this->picBufferID[i] =
this->matBufferID[i] =
this->sliBufferID[i] =
this->datBufferID[i] = VA_INVALID_ID;
}
if (this->vaSurfaceID[0] != VA_INVALID_ID)
vaDestroySurfaces(this->vaDisplay, this->vaSurfaceID, SURFACE_NUM);
this->vaSurfaceID[0] = VA_INVALID_ID;
if (this->vaContextID != VA_INVALID_ID)
vaDestroyContext(this->vaDisplay, this->vaContextID);
this->vaContextID = VA_INVALID_ID;
if (this->vaConfigID != VA_INVALID_ID)
vaDestroyConfig(this->vaDisplay, this->vaConfigID);
this->vaConfigID = VA_INVALID_ID;
if (this->vaDisplay)
vaTerminate(this->vaDisplay);
this->vaDisplay = NULL;
}
static LG_OutFormat lgd_h264_get_out_format(void * opaque)
{
return LG_OUTPUT_YUV420;
}
static unsigned int lgd_h264_get_frame_pitch(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
return this->format.width * 4;
}
static unsigned int lgd_h264_get_frame_stride(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
return this->format.width;
}
static bool get_buffer(struct Inst * this, const VABufferType type, const unsigned int size, VABufferID * buf_id)
{
if (*buf_id != VA_INVALID_ID)
return true;
VAStatus status = vaCreateBuffer(this->vaDisplay, this->vaContextID, type, size, 1, NULL, buf_id);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("Failed to create buffer: %s", vaErrorStr(status));
return false;
}
if (!check_surface(this, this->currentSID, NULL))
return false;
return true;
}
static bool setup_pic_buffer(struct Inst * this, const NAL_SLICE * slice)
{
VAStatus status;
VABufferID * picBufferID = &this->picBufferID[this->currentSID];
if (!get_buffer(this, VAPictureParameterBufferType, sizeof(VAPictureParameterBufferH264), picBufferID))
{
DEBUG_ERROR("get picBuffer failed");
return false;
}
VAPictureParameterBufferH264 *p;
status = vaMapBuffer(this->vaDisplay, *picBufferID, (void **)&p);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaMapBuffer: %s", vaErrorStr(status));
return false;
}
const NAL_SPS * sps;
if (!nal_get_sps(this->nal, &sps))
{
DEBUG_ERROR("nal_get_sps");
return false;
}
const NAL_PPS * pps;
if (!nal_get_pps(this->nal, &pps))
{
DEBUG_ERROR("nal_get_pps");
return false;
}
memset(p, 0, sizeof(VAPictureParameterBufferH264));
p->picture_width_in_mbs_minus1 = sps->pic_width_in_mbs_minus1;
p->picture_height_in_mbs_minus1 = sps->pic_height_in_map_units_minus1;
p->bit_depth_luma_minus8 = sps->bit_depth_luma_minus8;
p->bit_depth_chroma_minus8 = sps->bit_depth_chroma_minus8;
p->num_ref_frames = sps->num_ref_frames;
p->seq_fields.value = 0;
p->seq_fields.bits.chroma_format_idc = sps->chroma_format_idc;
p->seq_fields.bits.residual_colour_transform_flag = sps->gaps_in_frame_num_value_allowed_flag;
p->seq_fields.bits.frame_mbs_only_flag = sps->frame_mbs_only_flag;
p->seq_fields.bits.mb_adaptive_frame_field_flag = sps->mb_adaptive_frame_field_flag;
p->seq_fields.bits.direct_8x8_inference_flag = sps->direct_8x8_inference_flag;
p->seq_fields.bits.MinLumaBiPredSize8x8 = sps->level_idc >= 31;
p->seq_fields.bits.log2_max_frame_num_minus4 = sps->log2_max_frame_num_minus4;
p->seq_fields.bits.pic_order_cnt_type = sps->pic_order_cnt_type;
p->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = sps->log2_max_pic_order_cnt_lsb_minus4;
p->seq_fields.bits.delta_pic_order_always_zero_flag = sps->delta_pic_order_always_zero_flag;
#if 0
// these are deprecated, FMO is not supported
p->num_slice_groups_minus1 = pps->num_slice_groups_minus1;
p->slice_group_map_type = pps->slice_group_map_type;
p->slice_group_change_rate_minus1 = pps->slice_group_change_rate_minus1;
#endif
p->pic_init_qp_minus26 = pps->pic_init_qp_minus26;
p->pic_init_qs_minus26 = pps->pic_init_qs_minus26;
p->chroma_qp_index_offset = pps->chroma_qp_index_offset;
p->second_chroma_qp_index_offset = pps->second_chroma_qp_index_offset;
p->pic_fields.value = 0;
p->pic_fields.bits.entropy_coding_mode_flag = pps->entropy_coding_mode_flag;
p->pic_fields.bits.weighted_pred_flag = pps->weighted_pred_flag;
p->pic_fields.bits.weighted_bipred_idc = pps->weighted_bipred_idc;
p->pic_fields.bits.transform_8x8_mode_flag = pps->transform_8x8_mode_flag;
p->pic_fields.bits.field_pic_flag = slice->field_pic_flag;
p->pic_fields.bits.constrained_intra_pred_flag = pps->constrained_intra_pred_flag;
p->pic_fields.bits.pic_order_present_flag = pps->pic_order_present_flag;
p->pic_fields.bits.deblocking_filter_control_present_flag = pps->deblocking_filter_control_present_flag;
p->pic_fields.bits.redundant_pic_cnt_present_flag = pps->redundant_pic_cnt_present_flag;
p->pic_fields.bits.reference_pic_flag = slice->nal_ref_idc != 0;
p->frame_num = slice->frame_num;
for(int i = 0; i < 16; ++i)
{
p->ReferenceFrames[i].flags = VA_PICTURE_H264_INVALID;
p->ReferenceFrames[i].picture_id = 0xFFFFFFFF;
}
this->curPic.picture_id = this->vaSurfaceID[this->currentSID];
this->curPic.frame_idx = p->frame_num;
this->curPic.flags = 0;
this->curPic.BottomFieldOrderCnt = this->fieldCount;
this->curPic.TopFieldOrderCnt = this->fieldCount;
memcpy(&p->CurrPic, &this->curPic, sizeof(VAPictureH264));
if (this->sliceType != 2)
{
memcpy(&p->ReferenceFrames[0], &this->oldPic, sizeof(VAPictureH264));
p->ReferenceFrames[0].flags = 0;
}
status = vaUnmapBuffer(this->vaDisplay, *picBufferID);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaUnmapBuffer: %s", vaErrorStr(status));
return false;
}
return true;
}
static bool setup_mat_buffer(struct Inst * this)
{
VAStatus status;
VABufferID * matBufferID = &this->matBufferID[this->currentSID];
if (!get_buffer(this, VAIQMatrixBufferType, sizeof(VAIQMatrixBufferH264), matBufferID))
{
DEBUG_ERROR("get matBuffer failed");
return false;
}
VAIQMatrixBufferH264 * m;
status = vaMapBuffer(this->vaDisplay, *matBufferID, (void **)&m);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaMapBuffer: %s", vaErrorStr(status));
return false;
}
memcpy(m, MatrixBufferH264, sizeof(MatrixBufferH264));
status = vaUnmapBuffer(this->vaDisplay, *matBufferID);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaUnmapBuffer: %s", vaErrorStr(status));
return false;
}
return true;
}
static void fill_pred_weight_table(
NAL_PW_TABLE_L * list,
uint32_t active,
uint32_t luma_log2_weight_denom,
uint8_t luma_weight_flag,
short luma_weight[32],
short luma_offset[32],
uint32_t chroma_log2_weight_denom,
uint8_t chroma_weight_flag,
short chroma_weight[32][2],
short chroma_offset[32][2]
)
{
assert(active < 32);
for(uint32_t i = 0; i <= active; ++i)
{
NAL_PW_TABLE_L * l = &list[i];
if (luma_weight_flag)
{
luma_weight[i] = l->luma_weight;
luma_offset[i] = l->luma_offset;
}
else
{
luma_weight[i] = 1 << luma_log2_weight_denom;
luma_weight[i] = 0;
}
if (chroma_weight_flag)
{
chroma_weight[i][0] = l->chroma_weight[0];
chroma_offset[i][0] = l->chroma_offset[0];
chroma_weight[i][1] = l->chroma_weight[1];
chroma_offset[i][1] = l->chroma_offset[1];
}
else
{
chroma_weight[i][0] = 1 << chroma_log2_weight_denom;
chroma_weight[i][0] = 0;
chroma_weight[i][1] = 1 << chroma_log2_weight_denom;
chroma_weight[i][1] = 0;
}
}
}
static bool setup_sli_buffer(struct Inst * this, size_t srcSize, const NAL_SLICE * slice, const size_t seek)
{
VAStatus status;
VABufferID * sliBufferID = &this->sliBufferID[this->currentSID];
if (!get_buffer(this, VASliceParameterBufferType, sizeof(VASliceParameterBufferH264), sliBufferID))
{
DEBUG_ERROR("get sliBuffer failed");
return false;
}
VASliceParameterBufferH264 * s;
status = vaMapBuffer(this->vaDisplay, *sliBufferID, (void **)&s);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaMapBuffer: %s", vaErrorStr(status));
return false;
}
memset(s, 0, sizeof(VASliceParameterBufferH264));
s->slice_data_size = srcSize;
s->slice_data_bit_offset = seek << 3;
s->slice_data_flag = VA_SLICE_DATA_FLAG_ALL;
s->first_mb_in_slice = slice->first_mb_in_slice;
s->slice_type = slice->slice_type;
s->direct_spatial_mv_pred_flag = slice->direct_spatial_mv_pred_flag;
s->num_ref_idx_l0_active_minus1 = slice->num_ref_idx_l0_active_minus1;
s->num_ref_idx_l1_active_minus1 = slice->num_ref_idx_l1_active_minus1;
s->cabac_init_idc = slice->cabac_init_idc;
s->slice_qp_delta = slice->slice_qp_delta;
s->disable_deblocking_filter_idc = slice->disable_deblocking_filter_idc;
s->slice_alpha_c0_offset_div2 = slice->slice_alpha_c0_offset_div2;
s->slice_beta_offset_div2 = slice->slice_beta_offset_div2;
s->luma_log2_weight_denom = slice->pred_weight_table.luma_log2_weight_denom;
s->chroma_log2_weight_denom = slice->pred_weight_table.chroma_log2_weight_denom;
s->luma_weight_l0_flag = slice->pred_weight_table.luma_weight_flag [0];
s->chroma_weight_l0_flag = slice->pred_weight_table.chroma_weight_flag[0];
s->luma_weight_l1_flag = slice->pred_weight_table.luma_weight_flag [1];
s->chroma_weight_l1_flag = slice->pred_weight_table.chroma_weight_flag[1];
//RefPicList0/1
fill_pred_weight_table(
slice->pred_weight_table.l0,
s->num_ref_idx_l0_active_minus1,
s->luma_log2_weight_denom,
s->luma_weight_l0_flag,
s->luma_weight_l0,
s->luma_offset_l0,
s->chroma_log2_weight_denom,
s->chroma_weight_l0_flag,
s->chroma_weight_l0,
s->chroma_weight_l0
);
fill_pred_weight_table(
slice->pred_weight_table.l1,
s->num_ref_idx_l1_active_minus1,
s->luma_log2_weight_denom,
s->luma_weight_l1_flag,
s->luma_weight_l1,
s->luma_offset_l1,
s->chroma_log2_weight_denom,
s->chroma_weight_l1_flag,
s->chroma_weight_l1,
s->chroma_weight_l1
);
#if 0
if (this->sliceType == 2)
{
set_slice_parameter_buffer_t2(s, this->t2First);
this->t2First = false;
}
else
{
set_slice_parameter_buffer(s);
memcpy(&s->RefPicList0[0], &this->oldPic, sizeof(VAPictureH264));
s->RefPicList0[0].flags = 0;
}
#endif
status = vaUnmapBuffer(this->vaDisplay, *sliBufferID);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaUnmapBuffer: %s", vaErrorStr(status));
return false;
}
return true;
}
static bool setup_dat_buffer(struct Inst * this, const uint8_t * src, size_t srcSize)
{
VAStatus status;
VABufferID * datBufferID = &this->datBufferID[this->currentSID];
if (!get_buffer(this, VASliceDataBufferType, srcSize, datBufferID))
{
DEBUG_ERROR("get datBuffer failed");
return false;
}
uint8_t * d;
status = vaMapBuffer(this->vaDisplay, *datBufferID, (void **)&d);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaMapBuffer: %s", vaErrorStr(status));
return false;
}
memcpySSE(d, src, srcSize);
status = vaUnmapBuffer(this->vaDisplay, *datBufferID);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaUnmapBuffer: %s", vaErrorStr(status));
return false;
}
return true;
}
static bool lgd_h264_decode(void * opaque, const uint8_t * src, size_t srcSize)
{
VAStatus status;
struct Inst * this = (struct Inst *)opaque;
size_t seek;
if (!nal_parse(this->nal, src, srcSize, &seek))
{
DEBUG_WARN("nal_parse, perhaps mid stream");
return true;
}
const NAL_SLICE * slice;
if (!nal_get_slice(this->nal, &slice))
{
DEBUG_WARN("nal_get_slice failed");
return true;
}
assert(seek < srcSize);
this->sliceType = slice->slice_type;
// don't start until we have an I-FRAME
if (this->frameNum == 0 && this->sliceType != NAL_SLICE_TYPE_I)
return true;
{
if (!setup_pic_buffer(this, slice)) return false;
if (!setup_mat_buffer(this)) return false;
VABufferID bufferIDs[] =
{
this->picBufferID[this->currentSID],
this->matBufferID[this->currentSID]
};
status = vaRenderPicture(this->vaDisplay, this->vaContextID, bufferIDs, 2);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaRenderPicture: %s", vaErrorStr(status));
return false;
}
// intel broke the ABI here, see:
// https://github.com/01org/libva/commit/3eb038aa13bdd785808286c0a4995bd7a1ef07e9
// the buffers are released by vaRenderPicture in old versions
if (this->vaMajorVer == 0 && this->vaMinorVer < 40)
{
this->picBufferID[this->currentSID] =
this->matBufferID[this->currentSID] = VA_INVALID_ID;
}
}
{
if (!setup_sli_buffer(this, srcSize, slice, seek)) return false;
if (!setup_dat_buffer(this, src, srcSize )) return false;
VABufferID bufferIDs[] =
{
this->sliBufferID[this->currentSID],
this->datBufferID[this->currentSID]
};
status = vaRenderPicture(this->vaDisplay, this->vaContextID, bufferIDs, 2);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaRenderPicture: %s", vaErrorStr(status));
return false;
}
// intel broke the ABI here, see:
// https://github.com/01org/libva/commit/3eb038aa13bdd785808286c0a4995bd7a1ef07e9
// the buffers are released by vaRenderPicture in old versions
if (this->vaMajorVer == 0 && this->vaMinorVer < 40)
{
this->sliBufferID[this->currentSID] =
this->datBufferID[this->currentSID] = VA_INVALID_ID;
}
}
status = vaEndPicture(this->vaDisplay, this->vaContextID);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaEndPicture: %s", vaErrorStr(status));
return false;
}
// advance to the next surface and save the old picture info
this->lastSID = this->currentSID;
if (++this->currentSID == SURFACE_NUM)
this->currentSID = 0;
this->frameNum += 1;
this->fieldCount += 2;
memcpy(&this->oldPic, &this->curPic, sizeof(VAPictureH264));
// prepare the next surface
status = vaBeginPicture(this->vaDisplay, this->vaContextID, this->vaSurfaceID[this->currentSID]);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaBeginPicture: %s", vaErrorStr(status));
return false;
}
return true;
}
static bool lgd_h264_get_buffer(void * opaque, uint8_t * dst, size_t dstSize)
{
struct Inst * this = (struct Inst *)opaque;
VAStatus status;
// don't return anything until we have some data
if (this->frameNum == 0)
return true;
// ensure the surface is ready
status = vaSyncSurface(this->vaDisplay, this->vaSurfaceID[this->lastSID]);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaSyncSurface: %s", vaErrorStr(status));
return false;
}
#if 0
// this doesn't work on my system, seems the vdpau va driver is bugged
VASurfaceStatus surfStatus;
if (!check_surface(this, this->lastSID, &surfStatus))
return false;
if (surfStatus != VASurfaceReady)
{
DEBUG_ERROR("vaSyncSurface didn't block, the surface is not ready!");
return false;
}
#endif
// get the decoded data
VAImage decoded =
{
.image_id = VA_INVALID_ID,
.buf = VA_INVALID_ID
};
status = vaDeriveImage(this->vaDisplay, this->vaSurfaceID[this->lastSID], &decoded);
if (status == VA_STATUS_ERROR_OPERATION_FAILED)
{
VAImageFormat format =
{
.fourcc = VA_FOURCC_NV12,
.byte_order = VA_LSB_FIRST,
.bits_per_pixel = 12
};
status = vaCreateImage(
this->vaDisplay,
&format,
this->format.width,
this->format.height,
&decoded
);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaCreateImage: %s", vaErrorStr(status));
return false;
}
status = vaPutImage(
this->vaDisplay,
this->vaSurfaceID[this->lastSID],
decoded.image_id,
0 , 0 ,
this->format.width, this->format.height,
0 , 0 ,
this->format.width, this->format.height
);
if (status != VA_STATUS_SUCCESS)
{
vaDestroyImage(this->vaDisplay, decoded.image_id);
DEBUG_ERROR("vaPutImage: %s", vaErrorStr(status));
return false;
}
}
else
{
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaDeriveImage: %s", vaErrorStr(status));
return false;
}
}
uint8_t * d;
status = vaMapBuffer(this->vaDisplay, decoded.buf, (void **)&d);
if (status != VA_STATUS_SUCCESS)
{
vaDestroyImage(this->vaDisplay, decoded.image_id);
DEBUG_ERROR("vaMapBuffer: %s", vaErrorStr(status));
return false;
}
memcpySSE(dst, d, decoded.data_size);
status = vaUnmapBuffer(this->vaDisplay, decoded.buf);
if (status != VA_STATUS_SUCCESS)
{
vaDestroyImage(this->vaDisplay, decoded.image_id);
DEBUG_ERROR("vaUnmapBuffer: %s", vaErrorStr(status));
return false;
}
status = vaDestroyImage(this->vaDisplay, decoded.image_id);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaDestroyImage: %s", vaErrorStr(status));
return false;
}
return true;
}
static bool lgd_h264_init_gl_texture(void * opaque, GLenum target, GLuint texture, void ** ref)
{
struct Inst * this = (struct Inst *)opaque;
VAStatus status;
status = vaCreateSurfaceGLX(this->vaDisplay, target, texture, ref);
if (status != VA_STATUS_SUCCESS)
{
*ref = NULL;
DEBUG_ERROR("vaCreateSurfaceGLX: %s", vaErrorStr(status));
return false;
}
return true;
}
static void lgd_h264_free_gl_texture(void * opaque, void * ref)
{
struct Inst * this = (struct Inst *)opaque;
VAStatus status;
status = vaDestroySurfaceGLX(this->vaDisplay, ref);
if (status != VA_STATUS_SUCCESS)
DEBUG_ERROR("vaDestroySurfaceGLX: %s", vaErrorStr(status));
}
static bool lgd_h264_update_gl_texture(void * opaque, void * ref)
{
struct Inst * this = (struct Inst *)opaque;
VAStatus status;
// don't return anything until we have some data
if (this->frameNum == 0)
return true;
status = vaCopySurfaceGLX(
this->vaDisplay,
ref,
this->vaSurfaceID[this->lastSID],
0
);
if (status != VA_STATUS_SUCCESS)
{
DEBUG_ERROR("vaCopySurfaceGLX: %s", vaErrorStr(status));
return false;
}
return true;
}
const LG_Decoder LGD_H264 =
{
.name = "H.264",
.create = lgd_h264_create,
.destroy = lgd_h264_destroy,
.initialize = lgd_h264_initialize,
.deinitialize = lgd_h264_deinitialize,
.get_out_format = lgd_h264_get_out_format,
.get_frame_pitch = lgd_h264_get_frame_pitch,
.get_frame_stride = lgd_h264_get_frame_stride,
.decode = lgd_h264_decode,
.get_buffer = lgd_h264_get_buffer,
.has_gl = true,
.init_gl_texture = lgd_h264_init_gl_texture,
.free_gl_texture = lgd_h264_free_gl_texture,
.update_gl_texture = lgd_h264_update_gl_texture
};

130
client/decoders/src/null.c Normal file
View File

@@ -0,0 +1,130 @@
/*
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/decoder.h"
#include "common/debug.h"
#include "common/memcpySSE.h"
#include <stdlib.h>
#include <string.h>
struct Inst
{
LG_RendererFormat format;
const uint8_t * src;
};
static bool lgd_null_create (void ** opaque);
static void lgd_null_destroy (void * opaque);
static bool lgd_null_initialize (void * opaque, const LG_RendererFormat format, SDL_Window * window);
static void lgd_null_deinitialize (void * opaque);
static LG_OutFormat lgd_null_get_out_format (void * opaque);
static unsigned int lgd_null_get_frame_pitch (void * opaque);
static unsigned int lgd_null_get_frame_stride(void * opaque);
static bool lgd_null_decode (void * opaque, const uint8_t * src, size_t srcSize);
static const uint8_t * lgd_null_get_buffer (void * opaque);
static bool lgd_null_create(void ** opaque)
{
// create our local storage
*opaque = malloc(sizeof(struct Inst));
if (!*opaque)
{
DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct Inst));
return false;
}
memset(*opaque, 0, sizeof(struct Inst));
return true;
}
static void lgd_null_destroy(void * opaque)
{
free(opaque);
}
static bool lgd_null_initialize(void * opaque, const LG_RendererFormat format, SDL_Window * window)
{
struct Inst * this = (struct Inst *)opaque;
memcpy(&this->format, &format, sizeof(LG_RendererFormat));
return true;
}
static void lgd_null_deinitialize(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
memset(this, 0, sizeof(struct Inst));
}
static LG_OutFormat lgd_null_get_out_format(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
switch(this->format.type)
{
case FRAME_TYPE_BGRA : return LG_OUTPUT_BGRA;
case FRAME_TYPE_RGBA : return LG_OUTPUT_RGBA;
case FRAME_TYPE_RGBA10: return LG_OUTPUT_RGBA10;
default:
DEBUG_ERROR("Unknown frame type");
return LG_OUTPUT_INVALID;
}
}
static unsigned int lgd_null_get_frame_pitch(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
return this->format.pitch;
}
static unsigned int lgd_null_get_frame_stride(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
return this->format.stride;
}
static bool lgd_null_decode(void * opaque, const uint8_t * src, size_t srcSize)
{
struct Inst * this = (struct Inst *)opaque;
this->src = src;
return true;
}
static const uint8_t * lgd_null_get_buffer(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
if (!this->src)
return NULL;
return this->src;
}
const LG_Decoder LGD_NULL =
{
.name = "NULL",
.create = lgd_null_create,
.destroy = lgd_null_destroy,
.initialize = lgd_null_initialize,
.deinitialize = lgd_null_deinitialize,
.get_out_format = lgd_null_get_out_format,
.get_frame_pitch = lgd_null_get_frame_pitch,
.get_frame_stride = lgd_null_get_frame_stride,
.decode = lgd_null_decode,
.get_buffer = lgd_null_get_buffer
};

View File

@@ -0,0 +1,169 @@
/*
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/decoder.h"
#include "common/debug.h"
#include "common/memcpySSE.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <GL/gl.h>
struct Pixel
{
uint8_t b, g, r, a;
};
struct Inst
{
LG_RendererFormat format;
struct Pixel * pixels;
unsigned int yBytes;
};
static bool lgd_yuv420_create (void ** opaque);
static void lgd_yuv420_destroy (void * opaque);
static bool lgd_yuv420_initialize (void * opaque, const LG_RendererFormat format, SDL_Window * window);
static void lgd_yuv420_deinitialize (void * opaque);
static LG_OutFormat lgd_yuv420_get_out_format (void * opaque);
static unsigned int lgd_yuv420_get_frame_pitch (void * opaque);
static unsigned int lgd_yuv420_get_frame_stride(void * opaque);
static bool lgd_yuv420_decode (void * opaque, const uint8_t * src, size_t srcSize);
static const uint8_t * lgd_yuv420_get_buffer (void * opaque);
static bool lgd_yuv420_create(void ** opaque)
{
// create our local storage
*opaque = malloc(sizeof(struct Inst));
if (!*opaque)
{
DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct Inst));
return false;
}
memset(*opaque, 0, sizeof(struct Inst));
return true;
}
static void lgd_yuv420_destroy(void * opaque)
{
free(opaque);
}
static bool lgd_yuv420_initialize(void * opaque, const LG_RendererFormat format, SDL_Window * window)
{
struct Inst * this = (struct Inst *)opaque;
memcpy(&this->format, &format, sizeof(LG_RendererFormat));
this->yBytes = format.width * format.height;
this->pixels = malloc(sizeof(struct Pixel) * (format.width * format.height));
return true;
}
static void lgd_yuv420_deinitialize(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
free(this->pixels);
}
static LG_OutFormat lgd_yuv420_get_out_format(void * opaque)
{
return LG_OUTPUT_BGRA;
}
static unsigned int lgd_yuv420_get_frame_pitch(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
return this->format.width * 4;
}
static unsigned int lgd_yuv420_get_frame_stride(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
return this->format.width;
}
static bool lgd_yuv420_decode(void * opaque, const uint8_t * src, size_t srcSize)
{
//FIXME: implement this properly using GLSL
struct Inst * this = (struct Inst *)opaque;
const unsigned int hw = this->format.width / 2;
const unsigned int hp = this->yBytes / 4;
for(size_t y = 0; y < this->format.height; ++y)
for(size_t x = 0; x < this->format.width; ++x)
{
const unsigned int yoff = y * this->format.width + x;
const unsigned int uoff = this->yBytes + ((y / 2) * hw + x / 2);
const unsigned int voff = uoff + hp;
float b = 1.164f * ((float)src[yoff] - 16.0f) + 2.018f * ((float)src[uoff] - 128.0f);
float g = 1.164f * ((float)src[yoff] - 16.0f) - 0.813f * ((float)src[voff] - 128.0f) - 0.391f * ((float)src[uoff] - 128.0f);
float r = 1.164f * ((float)src[yoff] - 16.0f) + 1.596f * ((float)src[voff] - 128.0f);
#define CLAMP(x) (x < 0 ? 0 : (x > 255 ? 255 : x))
this->pixels[yoff].b = CLAMP(b);
this->pixels[yoff].g = CLAMP(g);
this->pixels[yoff].r = CLAMP(r);
}
return true;
}
static const uint8_t * lgd_yuv420_get_buffer(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
return (uint8_t *)this->pixels;
}
bool lgd_yuv420_init_gl_texture(void * opaque, GLenum target, GLuint texture, void ** ref)
{
return false;
}
void lgd_yuv420_free_gl_texture(void * opaque, void * ref)
{
}
bool lgd_yuv420_update_gl_texture(void * opaque, void * ref)
{
return false;
}
const LG_Decoder LGD_YUV420 =
{
.name = "YUV420",
.create = lgd_yuv420_create,
.destroy = lgd_yuv420_destroy,
.initialize = lgd_yuv420_initialize,
.deinitialize = lgd_yuv420_deinitialize,
.get_out_format = lgd_yuv420_get_out_format,
.get_frame_pitch = lgd_yuv420_get_frame_pitch,
.get_frame_stride = lgd_yuv420_get_frame_stride,
.decode = lgd_yuv420_decode,
.get_buffer = lgd_yuv420_get_buffer,
.has_gl = false, //FIXME: Implement this
.init_gl_texture = lgd_yuv420_init_gl_texture,
.free_gl_texture = lgd_yuv420_free_gl_texture,
.update_gl_texture = lgd_yuv420_update_gl_texture
};

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}
)

153
client/fonts/SDL/src/sdl.c Normal file
View File

@@ -0,0 +1,153 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdlib.h>
#include <stdbool.h>
#include "interface/font.h"
#include "common/debug.h"
#include <SDL2/SDL_ttf.h>
#include <fontconfig/fontconfig.h>
static int g_initCount = 0;
static FcConfig * g_fontConfig = NULL;
struct Inst
{
TTF_Font * font;
};
static bool lgf_sdl_create(LG_FontObj * opaque, const char * font_name, unsigned int size)
{
if (g_initCount++ == 0)
{
if (TTF_Init() < 0)
{
DEBUG_ERROR("TTF_Init Failed");
return false;
}
g_fontConfig = FcInitLoadConfigAndFonts();
if (!g_fontConfig)
{
DEBUG_ERROR("FcInitLoadConfigAndFonts Failed");
return false;
}
}
*opaque = malloc(sizeof(struct Inst));
if (!*opaque)
{
DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct Inst));
return false;
}
memset(*opaque, 0, sizeof(struct Inst));
struct Inst * this = (struct Inst *)*opaque;
if (!font_name)
font_name = "FreeMono";
FcPattern * pat = FcNameParse((const FcChar8*)font_name);
FcConfigSubstitute (g_fontConfig, pat, FcMatchPattern);
FcDefaultSubstitute(pat);
FcResult result;
FcChar8 * file = NULL;
FcPattern * font = FcFontMatch(g_fontConfig, pat, &result);
if (font && (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch))
{
this->font = TTF_OpenFont((char *)file, size);
if (!this->font)
{
DEBUG_ERROR("TTL_OpenFont Failed");
return false;
}
}
else
{
DEBUG_ERROR("Failed to locate the requested font: %s", font_name);
return false;
}
FcPatternDestroy(pat);
return true;
}
static void lgf_sdl_destroy(LG_FontObj opaque)
{
struct Inst * this = (struct Inst *)opaque;
if (this->font)
TTF_CloseFont(this->font);
free(this);
if (--g_initCount == 0)
TTF_Quit();
}
static LG_FontBitmap * lgf_sdl_render(LG_FontObj opaque, unsigned int fg_color, const char * text)
{
struct Inst * this = (struct Inst *)opaque;
SDL_Surface * surface;
SDL_Color color;
color.r = (fg_color & 0xff000000) >> 24;
color.g = (fg_color & 0x00ff0000) >> 16;
color.b = (fg_color & 0x0000ff00) >> 8;
color.a = (fg_color & 0x000000ff) >> 0;
if (!(surface = TTF_RenderText_Blended(this->font, text, color)))
{
DEBUG_ERROR("Failed to render text: %s", TTF_GetError());
return NULL;
}
LG_FontBitmap * out = malloc(sizeof(LG_FontBitmap));
if (!out)
{
SDL_FreeSurface(surface);
DEBUG_ERROR("Failed to allocate memory for font bitmap");
return NULL;
}
out->reserved = surface;
out->width = surface->w;
out->height = surface->h;
out->bpp = surface->format->BytesPerPixel;
out->pixels = surface->pixels;
return out;
}
static void lgf_sdl_release(LG_FontObj opaque, LG_FontBitmap * font)
{
LG_FontBitmap * bitmap = (LG_FontBitmap *)font;
SDL_FreeSurface(bitmap->reserved);
free(bitmap);
}
struct LG_Font LGF_SDL =
{
.name = "SDL",
.create = lgf_sdl_create,
.destroy = lgf_sdl_destroy,
.render = lgf_sdl_render,
.release = lgf_sdl_release
};

View File

@@ -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

@@ -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
*/
#pragma once
#include "renderer.h"
#include <stdint.h>
#include <stdbool.h>
#include <SDL2/SDL.h>
#include <GL/gl.h>
typedef enum LG_OutFormat
{
LG_OUTPUT_INVALID,
LG_OUTPUT_BGRA,
LG_OUTPUT_RGBA,
LG_OUTPUT_RGBA10,
LG_OUTPUT_YUV420
}
LG_OutFormat;
typedef bool (* LG_DecoderCreate )(void ** opaque);
typedef void (* LG_DecoderDestroy )(void * opaque);
typedef bool (* LG_DecoderInitialize )(void * opaque, const LG_RendererFormat format, SDL_Window * window);
typedef void (* LG_DecoderDeInitialize )(void * opaque);
typedef LG_OutFormat (* LG_DecoderGetOutFormat )(void * opaque);
typedef unsigned int (* LG_DecoderGetFramePitch )(void * opaque);
typedef unsigned int (* LG_DecoderGetFrameStride)(void * opaque);
typedef bool (* LG_DecoderDecode )(void * opaque, const uint8_t * src, size_t srcSize);
typedef const uint8_t * (* LG_DecoderGetBuffer )(void * opaque);
typedef bool (* LG_DecoderInitGLTexture )(void * opaque, GLenum target, GLuint texture, void ** ref);
typedef void (* LG_DecoderFreeGLTexture )(void * opaque, void * ref);
typedef bool (* LG_DecoderUpdateGLTexture)(void * opaque, void * ref);
typedef struct LG_Decoder
{
// mandatory support
const char * name;
LG_DecoderCreate create;
LG_DecoderDestroy destroy;
LG_DecoderInitialize initialize;
LG_DecoderDeInitialize deinitialize;
LG_DecoderGetOutFormat get_out_format;
LG_DecoderGetFramePitch get_frame_pitch;
LG_DecoderGetFrameStride get_frame_stride;
LG_DecoderDecode decode;
LG_DecoderGetBuffer get_buffer;
// optional support
const bool has_gl;
LG_DecoderInitGLTexture init_gl_texture;
LG_DecoderFreeGLTexture free_gl_texture;
LG_DecoderUpdateGLTexture update_gl_texture;
}
LG_Decoder;

View File

@@ -0,0 +1,50 @@
/*
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 <stdint.h>
#include <stdbool.h>
typedef void * LG_FontObj;
typedef struct LG_FontBitmap
{
void * reserved;
unsigned int width, height;
unsigned int bpp; // bytes per pixel
uint8_t * pixels;
}
LG_FontBitmap;
typedef bool (* LG_FontCreate )(LG_FontObj * opaque, const char * font_name, unsigned int size);
typedef void (* LG_FontDestroy )(LG_FontObj opaque);
typedef LG_FontBitmap * (* LG_FontRender )(LG_FontObj opaque, unsigned int fg_color, const char * text);
typedef void (* LG_FontRelease )(LG_FontObj opaque, LG_FontBitmap * bitmap);
typedef struct LG_Font
{
// mandatory support
const char * name;
LG_FontCreate create;
LG_FontDestroy destroy;
LG_FontRender render;
LG_FontRelease release;
}
LG_Font;

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,57 +24,46 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.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 && \
(x)->render)
#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;
const char * value;
} LG_RendererOptValue;
typedef LG_RendererOpt * LG_RendererOptions;
(x)->on_alert && \
(x)->render_startup && \
(x)->render && \
(x)->update_fps)
typedef struct LG_RendererParams
{
TTF_Font * font;
// TTF_Font * font;
// TTF_Font * alertFont;
bool showFPS;
}
LG_RendererParams;
typedef struct LG_RendererFormat
{
FrameType type; // frame type
unsigned int width; // image width
unsigned int height; // image height
unsigned int stride; // scanline width
unsigned int pitch; // scanline bytes
unsigned int bpp; // bits per pixel
unsigned int stride; // scanline width (zero if compresed)
unsigned int pitch; // scanline bytes (or compressed size)
unsigned int bpp; // bits per pixel (zero if compressed)
}
LG_RendererFormat;
typedef struct LG_RendererRect
{
bool valid;
int x;
int y;
unsigned int w;
@@ -90,32 +79,40 @@ typedef enum LG_RendererCursor
}
LG_RendererCursor;
typedef const char * (* LG_RendererGetName )();
// 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 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 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;
LG_RendererOnFrameEvent on_frame_event;
LG_RendererOnAlert on_alert;
LG_RendererRender render_startup;
LG_RendererRender render;
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,16 +18,16 @@ Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include "lg-renderer.h"
#include "interface/decoder.h"
extern const LG_Renderer LGR_OpenGL;
//extern const LG_Renderer LGR_OpenGLBasic;
extern const LG_Decoder LGD_NULL;
extern const LG_Decoder LGD_YUV420;
const LG_Renderer * LG_Renderers[] =
const LG_Decoder * LG_Decoders[] =
{
&LGR_OpenGL,
// &LGR_OpenGLBasic,
&LGD_NULL,
&LGD_YUV420,
NULL // end of array sentinal
};
#define LG_RENDERER_COUNT ((sizeof(LG_Renderers) / sizeof(LG_Renderer *)) - 1)
#define LG_DECODER_COUNT ((sizeof(LG_Decoders) / sizeof(LG_Decoder *)) - 1)

31
client/include/ll.h Normal file
View File

@@ -0,0 +1,31 @@
/*
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>
struct ll;
struct ll * ll_new();
void ll_free (struct ll * list);
void ll_push (struct ll * list, void * data);
bool ll_shift (struct ll * list, void ** data);
bool ll_peek_head(struct ll * list, void ** data);
unsigned int ll_count (struct ll * list);
void ll_reset (struct ll * list);
bool ll_walk (struct ll * list, void ** data);

27
client/include/utils.h Normal file
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 <stdlib.h>
#include <stdbool.h>
// 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);

View File

@@ -1,548 +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 "ivshmem.h"
#include "debug.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdint.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <sys/mman.h>
#define MAX_IRQS 32
struct IVSHMEMServer
{
int64_t version;
int64_t clientID;
int sharedFD;
int irqs[MAX_IRQS];
int irqCount;
};
struct IVSHMEMClient
{
uint16_t clientID;
int irqs[MAX_IRQS];
int irqCount;
struct IVSHMEMClient * last;
struct IVSHMEMClient * next;
};
struct IVSHMEM
{
bool connected;
bool shutdown;
int socket;
struct IVSHMEMServer server;
struct IVSHMEMClient * clients;
off_t mapSize;
void * map;
};
struct IVSHMEM ivshmem =
{
.connected = false,
.shutdown = false,
.socket = -1
};
// ============================================================================
// internal functions
void ivshmem_cleanup();
bool ivshmem_read(void * buffer, const ssize_t size);
bool ivshmem_read_msg(int64_t * index, int *fd);
struct IVSHMEMClient * ivshmem_get_client(uint16_t clientID);
void ivshmem_remove_client(struct IVSHMEMClient * client);
// ============================================================================
bool ivshmem_connect(const char * unix_socket)
{
ivshmem.shutdown = false;
ivshmem.socket = socket(AF_UNIX, SOCK_STREAM, 0);
if (ivshmem.socket < 0)
{
DEBUG_ERROR("socket creation failed");
return false;
}
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, unix_socket, sizeof(addr.sun_path));
if (connect(ivshmem.socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
{
DEBUG_ERROR("socket connect failed");
ivshmem_cleanup();
return false;
}
ivshmem.connected = true;
if (!ivshmem_read(&ivshmem.server.version, sizeof(ivshmem.server.version)))
{
DEBUG_ERROR("read protocol version failed");
ivshmem_cleanup();
return false;
}
if (ivshmem.server.version != 0)
{
DEBUG_ERROR("unsupported protocol version %ld", ivshmem.server.version);
ivshmem_cleanup();
return false;
}
if (!ivshmem_read(&ivshmem.server.clientID, sizeof(ivshmem.server.clientID)))
{
DEBUG_ERROR("read client id failed");
ivshmem_cleanup();
return false;
}
DEBUG_PROTO("Protocol : %ld", ivshmem.server.version );
DEBUG_PROTO("Client ID: %ld", ivshmem.server.clientID);
if (!ivshmem_read_msg(NULL, &ivshmem.server.sharedFD))
{
DEBUG_ERROR("failed to read shared memory file descriptor");
ivshmem_cleanup();
return false;
}
struct stat stat;
if (fstat(ivshmem.server.sharedFD, &stat) != 0)
{
DEBUG_ERROR("failed to stat shared FD");
ivshmem_cleanup();
return false;
}
ivshmem.mapSize = stat.st_size;
DEBUG_INFO("RAM Size : %ld", ivshmem.mapSize);
ivshmem.map = mmap(
NULL,
stat.st_size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
ivshmem.server.sharedFD,
0);
if (!ivshmem.map)
{
DEBUG_ERROR("failed to map memory");
ivshmem_cleanup();
return false;
}
return true;
}
// ============================================================================
void ivshmem_cleanup()
{
struct IVSHMEMClient * client, * next;
client = ivshmem.clients;
while(client)
{
for(int i = 0; i < client->irqCount; ++i)
close(client->irqs[i]);
next = client->next;
free(client);
client = next;
}
ivshmem.clients = NULL;
for(int i = 0; i < ivshmem.server.irqCount; ++i)
close(ivshmem.server.irqs[i]);
ivshmem.server.irqCount = 0;
if (ivshmem.map)
munmap(ivshmem.map, ivshmem.mapSize);
ivshmem.map = NULL;
ivshmem.mapSize = 0;
if (ivshmem.socket >= 0)
{
ivshmem.shutdown = true;
shutdown(ivshmem.socket, SHUT_RDWR);
close(ivshmem.socket);
ivshmem.socket = -1;
}
ivshmem.connected = false;
}
// ============================================================================
void ivshmem_disconnect()
{
if (!ivshmem.connected)
{
DEBUG_WARN("socket not connected");
return;
}
ivshmem_cleanup();
}
// ============================================================================
bool ivshmem_read(void * buffer, const ssize_t size)
{
if (!ivshmem.connected)
{
DEBUG_ERROR("not connected");
return false;
}
ssize_t len = read(ivshmem.socket, buffer, size);
if (len != size)
{
DEBUG_ERROR("incomplete read");
return false;
}
return true;
}
// ============================================================================
bool ivshmem_read_msg(int64_t * index, int * fd)
{
if (!ivshmem.connected)
{
DEBUG_ERROR("not connected");
return false;
}
struct msghdr msg;
struct iovec iov[1];
union {
struct cmsghdr cmsg;
char control[CMSG_SPACE(sizeof(int))];
} msg_control;
int64_t tmp;
if (!index)
index = &tmp;
iov[0].iov_base = index;
iov[0].iov_len = sizeof(*index);
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = &msg_control;
msg.msg_controllen = sizeof(msg_control);
int ret = recvmsg(ivshmem.socket, &msg, 0);
if (ret < sizeof(*index))
{
if (!ivshmem.shutdown)
DEBUG_ERROR("failed to read message\n");
return false;
}
if (ret == 0)
{
if (!ivshmem.shutdown)
DEBUG_ERROR("lost connetion to server\n");
return false;
}
if (!fd)
return true;
*fd = -1;
struct cmsghdr *cmsg;
for(cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
cmsg->cmsg_level != SOL_SOCKET ||
cmsg->cmsg_type != SCM_RIGHTS)
{
continue;
}
memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
}
return true;
}
// ============================================================================
uint16_t ivshmem_get_id()
{
if (!ivshmem.connected)
{
DEBUG_ERROR("not connected");
return -1;
}
return ivshmem.server.clientID;
}
// ============================================================================
void * ivshmem_get_map()
{
if (!ivshmem.connected)
{
DEBUG_ERROR("not connected");
return NULL;
}
if (!ivshmem.map)
{
DEBUG_ERROR("not mapped");
return NULL;
}
return ivshmem.map;
}
// ============================================================================
size_t ivshmem_get_map_size()
{
if (!ivshmem.connected)
{
DEBUG_ERROR("not connected");
return 0;
}
if (!ivshmem.map)
{
DEBUG_ERROR("not mapped");
return 0;
}
return ivshmem.mapSize;
}
// ============================================================================
struct IVSHMEMClient * ivshmem_get_client(uint16_t clientID)
{
struct IVSHMEMClient * client = NULL;
if (ivshmem.clients == NULL)
{
client = (struct IVSHMEMClient *)malloc(sizeof(struct IVSHMEMClient));
client->clientID = clientID;
client->last = NULL;
client->next = NULL;
client->irqCount = 0;
ivshmem.clients = client;
return client;
}
client = ivshmem.clients;
while(client)
{
if (client->clientID == clientID)
return client;
client = client->next;
}
client = (struct IVSHMEMClient *)malloc(sizeof(struct IVSHMEMClient));
client->clientID = clientID;
client->last = NULL;
client->next = ivshmem.clients;
client->irqCount = 0;
client->next->last = client;
ivshmem.clients = client;
return client;
}
// ============================================================================
void ivshmem_remove_client(struct IVSHMEMClient * client)
{
if (client->last)
client->last->next = client->next;
if (client->next)
client->next->last = client->last;
if (ivshmem.clients == client)
ivshmem.clients = client->next;
free(client);
}
// ============================================================================
bool ivshmem_process()
{
int64_t index;
int fd;
if (!ivshmem_read_msg(&index, &fd))
{
if (!ivshmem.shutdown)
DEBUG_ERROR("failed to read message");
return false;
}
if (index == -1)
{
DEBUG_ERROR("invalid index -1");
return false;
}
if (index > 0xFFFF)
{
DEBUG_ERROR("invalid index > 0xFFFF");
return false;
}
if (index == ivshmem.server.clientID)
{
if (fd == -1)
{
DEBUG_ERROR("server sent disconnect");
return false;
}
if (ivshmem.server.irqCount == MAX_IRQS)
{
DEBUG_WARN("maximum IRQs reached, closing extra");
close(fd);
return true;
}
ivshmem.server.irqs[ivshmem.server.irqCount++] = fd;
return true;
}
struct IVSHMEMClient * client = ivshmem_get_client(index);
if (!client)
{
DEBUG_ERROR("failed to get/create client record");
return false;
}
if (fd == -1)
{
DEBUG_PROTO("remove client %u", client->clientID);
ivshmem_remove_client(client);
return true;
}
if (client->irqCount == MAX_IRQS)
{
DEBUG_WARN("maximum client IRQs reached, closing extra");
close(fd);
return true;
}
client->irqs[client->irqCount++] = fd;
return true;
}
// ============================================================================
enum IVSHMEMWaitResult ivshmem_wait_irq(uint16_t vector, unsigned int timeout)
{
if (vector > ivshmem.server.irqCount - 1)
return false;
int fd = ivshmem.server.irqs[vector];
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
struct timeval tv;
tv.tv_sec = timeout / 1000000L;
tv.tv_usec = timeout % 1000000L;
while(true)
{
int ret = select(fd+1, &fds, NULL, NULL, &tv);
if (ret < 0)
{
if (errno == EINTR)
continue;
DEBUG_ERROR("select error");
break;
}
if (ret == 0)
return IVSHMEM_WAIT_RESULT_TIMEOUT;
if (FD_ISSET(fd, &fds))
{
uint64_t kick;
int unused = read(fd, &kick, sizeof(kick));
(void)unused;
return IVSHMEM_WAIT_RESULT_OK;
}
}
return IVSHMEM_WAIT_RESULT_ERROR;
}
// ============================================================================
bool ivshmem_kick_irq(uint16_t clientID, uint16_t vector)
{
struct IVSHMEMClient * client = ivshmem_get_client(clientID);
if (!client)
{
DEBUG_ERROR("invalid client");
return false;
}
if (vector > client->irqCount - 1)
{
DEBUG_ERROR("invalid vector for client");
return false;
}
int fd = client->irqs[vector];
uint64_t kick = ivshmem.server.clientID;
if (write(fd, &kick, sizeof(kick)) == sizeof(kick))
return true;
DEBUG_ERROR("failed to send kick");
return false;
}

File diff suppressed because it is too large Load Diff

1009
client/parsers/nal.c Normal file

File diff suppressed because it is too large Load Diff

305
client/parsers/nal.h Normal file
View File

@@ -0,0 +1,305 @@
/*
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 <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#define NAL_TYPE_CODED_SLICE_NON_IDR 1
#define NAL_TYPE_CODED_SLICE_DATA_PARTITION_A 2
#define NAL_TYPE_CODED_SLICE_DATA_PARTITION_B 3
#define NAL_TYPE_CODED_SLICE_DATA_PARTITION_C 4
#define NAL_TYPE_CODED_SLICE_IDR 5
#define NAL_TYPE_SPS 7
#define NAL_TYPE_PPS 8
#define NAL_TYPE_AUD 9
#define NAL_TYPE_END_OF_SEQUENCE 10
#define NAL_TYPE_END_OF_STREAM 11
#define NAL_TYPE_CODED_SLICE_AUX 19
#define IDC_PROFILE_BASELINE 66
#define IDC_PROFILE_MAIN 77
#define IDC_PROFILE_EXTENDED 88
#define IDC_PROFILE_HP 100
#define IDC_PROFILE_Hi10P 110
#define IDC_PROFILE_Hi422 122
#define IDC_PROFILE_Hi444 244
#define IDC_PROFILE_CAVLC444 44
#define IDC_CHROMA_FORMAT_YUV400 0
#define IDC_CHROMA_FORMAT_YUV420 1
#define IDC_CHROMA_FORMAT_YVU422 2
#define IDC_CHROMA_FORMAT_YUV444 3
#define IDC_VUI_ASPECT_RATIO_EXTENDED_SAR 0xFF
#define NAL_PICTURE_TYPE_I 0
#define NAL_PICTURE_TYPE_P 1
#define NAL_PICTURE_TYPE_B 2
#define NAL_SLICE_TYPE_P 0
#define NAL_SLICE_TYPE_B 1
#define NAL_SLICE_TYPE_I 2
#define NAL_SLICE_TYPE_SP 3
#define NAL_SLICE_TYPE_SI 4
typedef struct NAL_SPS
{
uint8_t profile_idc;
uint8_t constraint_set_flags[3];
uint8_t level_idc;
uint32_t seq_parameter_set_id;
uint32_t chroma_format_idc;
uint8_t seperate_colour_plane_flag;
uint32_t bit_depth_luma_minus8;
uint32_t bit_depth_chroma_minus8;
uint8_t lossless_qpprime_y_zero_flag;
uint8_t seq_scaling_matrix_present_flag;
uint8_t seq_scaling_list_present_flag[12];
uint32_t log2_max_frame_num_minus4;
uint32_t pic_order_cnt_type;
uint32_t log2_max_pic_order_cnt_lsb_minus4;
uint8_t delta_pic_order_always_zero_flag;
int32_t offset_for_non_ref_pic;
int32_t offset_for_top_to_bottom_field;
uint32_t num_ref_frames_in_pic_order_cnt_cycle;
int32_t * offset_for_ref_frame;
uint32_t num_ref_frames;
uint8_t gaps_in_frame_num_value_allowed_flag;
uint32_t pic_width_in_mbs_minus1;
uint32_t pic_height_in_map_units_minus1;
uint8_t frame_mbs_only_flag;
uint8_t mb_adaptive_frame_field_flag;
uint8_t direct_8x8_inference_flag;
uint8_t frame_cropping_flag;
uint32_t frame_crop_left_offset;
uint32_t frame_crop_right_offset;
uint32_t frame_crop_top_offset;
uint32_t frame_crop_bottom_offset;
uint8_t vui_parameters_present_flag;
}
NAL_SPS;
typedef struct NAL_CPB
{
uint32_t bit_rate_value_minus1;
uint32_t cpb_size_value_minus1;
uint8_t cbr_flag;
}
NAL_CPB;
typedef struct NAL_HRD
{
uint32_t cpb_cnt_minus1;
uint8_t bit_rate_scale;
uint8_t cpb_size_scale;
uint8_t cpb_size_count;
NAL_CPB * cpb;
uint8_t initial_cpb_removal_delay_length_minus1;
uint8_t cpb_removal_delay_length_minus1;
uint8_t dpb_output_delay_length_minus1;
uint8_t time_offset_length;
}
NAL_HRD;
typedef struct NAL_VUI
{
uint8_t aspect_ratio_info_present_flag;
uint8_t aspect_ratio_idc;
uint16_t sar_width;
uint16_t sar_height;
uint8_t overscan_info_present_flag;
uint8_t overscan_appropriate_flag;
uint8_t video_signal_type_present_flag;
uint8_t video_format;
uint8_t video_full_range_flag;
uint8_t colour_description_present_flag;
uint8_t colour_primaries;
uint8_t transfer_characteristics;
uint8_t matrix_coefficients;
uint8_t chroma_loc_info_present_flag;
uint32_t chroma_sample_loc_type_top_field;
uint32_t chroma_sample_loc_type_bottom_field;
uint8_t timing_info_present_flag;
uint32_t num_units_in_tick;
uint32_t time_scale;
uint8_t fixed_frame_rate_flag;
uint8_t nal_hrd_parameters_present_flag;
NAL_HRD nal_hrd_parameters;
uint8_t vcl_hrd_parameters_present_flag;
NAL_HRD vcl_hrd_parameters;
uint8_t low_delay_hrd_flag;
uint8_t pic_struct_present_flag;
uint8_t bitstream_restriction_flag;
uint8_t motion_vectors_over_pic_boundaries_flag;
uint32_t max_bytes_per_pic_denom;
uint32_t max_bits_per_mb_denom;
uint32_t log2_max_mv_length_horizontal;
uint32_t log2_max_mv_length_vertical;
uint32_t num_reorder_frames;
uint32_t max_dec_frame_buffering;
}
NAL_VUI;
typedef struct NAL_SLICE_GROUP_T0
{
uint32_t run_length_minus1;
}
NAL_SLICE_GROUP_T0;
typedef struct NAL_SLICE_GROUP_T2
{
uint32_t top_left;
uint32_t bottom_right;
}
NAL_SLICE_GROUP_T2;
typedef union NAL_SLICE_GROUP
{
NAL_SLICE_GROUP_T0 t0;
NAL_SLICE_GROUP_T2 t2;
}
NAL_SLICE_GROUP;
typedef struct NAL_PPS
{
uint32_t pic_parameter_set_id;
uint32_t seq_parameter_set_id;
uint8_t entropy_coding_mode_flag;
uint8_t pic_order_present_flag;
uint32_t num_slice_groups_minus1;
NAL_SLICE_GROUP * slice_groups;
uint32_t slice_group_map_type;
uint8_t slice_group_change_direction_flag;
uint32_t slice_group_change_rate_minus1;
uint32_t pic_size_in_map_units_minus1;
uint32_t * slice_group_id;
uint32_t num_ref_idx_l0_active_minus1;
uint32_t num_ref_idx_l1_active_minus1;
uint8_t weighted_pred_flag;
uint8_t weighted_bipred_idc;
int32_t pic_init_qp_minus26;
int32_t pic_init_qs_minus26;
int32_t chroma_qp_index_offset;
uint8_t deblocking_filter_control_present_flag;
uint8_t constrained_intra_pred_flag;
uint8_t redundant_pic_cnt_present_flag;
uint8_t transform_8x8_mode_flag;
uint8_t pic_scaling_matrix_present_flag;
uint8_t pic_scaling_list_present_flag[6];
int32_t scaling_list_4x4[6];
int32_t scaling_list_8x8[2];
int32_t second_chroma_qp_index_offset;
}
NAL_PPS;
typedef struct NAL_RPL_REORDER_L
{
bool valid;
uint32_t reordering_of_pic_nums_idc;
uint32_t abs_diff_pic_num_minus1;
uint32_t long_term_pic_num;
}
NAL_RPL_REORDER_L;
typedef struct NAL_RPL_REORDER
{
uint8_t ref_pic_list_reordering_flag_l0;
NAL_RPL_REORDER_L l0[3];
uint8_t ref_pic_list_reordering_flag_l1;
NAL_RPL_REORDER_L l1[3];
}
NAL_RPL_REORDER;
typedef struct NAL_PW_TABLE_L
{
int32_t luma_weight;
int32_t luma_offset;
int32_t chroma_weight[2];
int32_t chroma_offset[2];
}
NAL_PW_TABLE_L;
typedef struct NAL_PW_TABLE
{
uint32_t luma_log2_weight_denom;
uint32_t chroma_log2_weight_denom;
uint8_t luma_weight_flag[2];
uint8_t chroma_weight_flag[2];
NAL_PW_TABLE_L * l0;
NAL_PW_TABLE_L * l1;
}
NAL_PW_TABLE;
typedef struct NAL_RP_MARKING
{
uint8_t no_output_of_prior_pics_flag;
uint8_t long_term_reference_flag;
uint8_t adaptive_ref_pic_marking_mode_flag;
uint32_t memory_management_control_operation;
uint32_t difference_of_pic_nums_minus1;
uint32_t long_term_pic_num;
uint32_t long_term_frame_idx;
uint32_t max_long_term_frame_idx_plus1;
}
NAL_RP_MARKING;
typedef struct NAL_SLICE
{
uint8_t nal_ref_idc;
uint32_t first_mb_in_slice;
uint32_t slice_type;
uint32_t pic_parameter_set_id;
uint32_t frame_num;
uint8_t field_pic_flag;
uint8_t bottom_field_flag;
uint32_t idr_pic_id;
uint32_t pic_order_cnt_lsb;
int32_t delta_pic_order_cnt_bottom;
int32_t delta_pic_order_cnt[2];
uint32_t redundant_pic_cnt;
uint8_t direct_spatial_mv_pred_flag;
uint8_t num_ref_idx_active_override_flag;
uint32_t num_ref_idx_l0_active_minus1;
uint32_t num_ref_idx_l1_active_minus1;
NAL_RPL_REORDER ref_pic_list_reordering;
NAL_PW_TABLE pred_weight_table;
NAL_RP_MARKING dec_ref_pic_marking;
uint32_t cabac_init_idc;
int32_t slice_qp_delta;
uint8_t sp_for_switch_flag;
int32_t slice_qs_delta;
uint32_t disable_deblocking_filter_idc;
int32_t slice_alpha_c0_offset_div2;
int32_t slice_beta_offset_div2;
uint32_t slice_group_change_cycle;
}
NAL_SLICE;
typedef struct NAL * NAL;
bool nal_initialize (NAL * ptr);
void nal_deinitialize(NAL this );
bool nal_parse (NAL this, const uint8_t * src, size_t size, size_t * seek);
bool nal_get_primary_picture_type(NAL this, uint8_t * pic_type);
bool nal_get_sps (NAL this, const NAL_SPS ** sps );
bool nal_get_pps (NAL this, const NAL_PPS ** pps );
bool nal_get_slice(NAL this, const NAL_SLICE ** slice);

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

@@ -0,0 +1,218 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
cahe terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "alert.h"
#include "common/debug.h"
#include "common/locking.h"
#include "texture.h"
#include "shader.h"
#include "model.h"
#include <stdlib.h>
#include <string.h>
// these headers are auto generated by cmake
#include "alert.vert.h"
#include "alert.frag.h"
#include "alert_bg.frag.h"
struct EGL_Alert
{
const LG_Font * font;
LG_FontObj fontObj;
EGL_Texture * texture;
EGL_Shader * shader;
EGL_Shader * shaderBG;
EGL_Model * model;
LG_Lock lock;
bool update;
LG_FontBitmap * bmp;
bool ready;
float width , height ;
float bgWidth, bgHeight;
float r, g, b, a;
// uniforms
GLint uScreen , uSize;
GLint uScreenBG, uSizeBG, uColorBG;
};
bool egl_alert_init(EGL_Alert ** alert, const LG_Font * font, LG_FontObj fontObj)
{
*alert = (EGL_Alert *)malloc(sizeof(EGL_Alert));
if (!*alert)
{
DEBUG_ERROR("Failed to malloc EGL_Alert");
return false;
}
memset(*alert, 0, sizeof(EGL_Alert));
(*alert)->font = font;
(*alert)->fontObj = fontObj;
LG_LOCK_INIT((*alert)->lock);
if (!egl_texture_init(&(*alert)->texture))
{
DEBUG_ERROR("Failed to initialize the alert texture");
return false;
}
if (!egl_shader_init(&(*alert)->shader))
{
DEBUG_ERROR("Failed to initialize the alert shader");
return false;
}
if (!egl_shader_init(&(*alert)->shaderBG))
{
DEBUG_ERROR("Failed to initialize the alert bg shader");
return false;
}
if (!egl_shader_compile((*alert)->shader,
b_shader_alert_vert, b_shader_alert_vert_size,
b_shader_alert_frag, b_shader_alert_frag_size))
{
DEBUG_ERROR("Failed to compile the alert shader");
return false;
}
if (!egl_shader_compile((*alert)->shaderBG,
b_shader_alert_vert , b_shader_alert_vert_size,
b_shader_alert_bg_frag, b_shader_alert_bg_frag_size))
{
DEBUG_ERROR("Failed to compile the alert shader");
return false;
}
(*alert)->uSize = egl_shader_get_uniform_location((*alert)->shader , "size" );
(*alert)->uScreen = egl_shader_get_uniform_location((*alert)->shader , "screen");
(*alert)->uSizeBG = egl_shader_get_uniform_location((*alert)->shaderBG, "size" );
(*alert)->uScreenBG = egl_shader_get_uniform_location((*alert)->shaderBG, "screen");
(*alert)->uColorBG = egl_shader_get_uniform_location((*alert)->shaderBG, "color" );
if (!egl_model_init(&(*alert)->model))
{
DEBUG_ERROR("Failed to initialize the alert model");
return false;
}
egl_model_set_default((*alert)->model);
egl_model_set_texture((*alert)->model, (*alert)->texture);
return true;
}
void egl_alert_free(EGL_Alert ** alert)
{
if (!*alert)
return;
egl_texture_free(&(*alert)->texture );
egl_shader_free (&(*alert)->shader );
egl_shader_free (&(*alert)->shaderBG);
egl_model_free (&(*alert)->model );
free(*alert);
*alert = NULL;
}
void egl_alert_set_color(EGL_Alert * alert, const uint32_t color)
{
alert->r = (1.0f / 0xff) * ((color >> 24) & 0xFF);
alert->g = (1.0f / 0xff) * ((color >> 16) & 0xFF);
alert->b = (1.0f / 0xff) * ((color >> 8) & 0xFF);
alert->a = (1.0f / 0xff) * ((color >> 0) & 0xFF);
}
void egl_alert_set_text (EGL_Alert * alert, const char * str)
{
LG_LOCK(alert->lock);
alert->bmp = alert->font->render(alert->fontObj, 0xffffff00, str);
if (!alert->bmp)
{
alert->update = false;
LG_UNLOCK(alert->lock);
DEBUG_ERROR("Failed to render alert text");
return;
}
alert->update = true;
LG_UNLOCK(alert->lock);
}
void egl_alert_render(EGL_Alert * alert, const float scaleX, const float scaleY)
{
if (alert->update)
{
LG_LOCK(alert->lock);
egl_texture_setup(
alert->texture,
EGL_PF_BGRA,
alert->bmp->width ,
alert->bmp->height,
alert->bmp->width * alert->bmp->bpp,
false
);
egl_texture_update(alert->texture, alert->bmp->pixels);
alert->width = alert->bgWidth = alert->bmp->width;
alert->height = alert->bgHeight = alert->bmp->height;
if (alert->bgWidth < 200)
alert->bgWidth = 200;
alert->bgHeight += 4;
alert->ready = true;
alert->font->release(alert->fontObj, alert->bmp);
alert->update = false;
alert->bmp = NULL;
LG_UNLOCK(alert->lock);
}
if (!alert->ready)
return;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// render the background first
egl_shader_use(alert->shaderBG);
glUniform2f(alert->uScreenBG, scaleX , scaleY );
glUniform2i(alert->uSizeBG , alert->bgWidth, alert->bgHeight);
glUniform4f(alert->uColorBG , alert->r, alert->g, alert->b, alert->a);
egl_model_render(alert->model);
// render the texture over the background
egl_shader_use(alert->shader);
glUniform2f(alert->uScreen, scaleX , scaleY );
glUniform2i(alert->uSize , alert->width, alert->height);
egl_model_render(alert->model);
glDisable(GL_BLEND);
}

View File

@@ -0,0 +1,33 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <stdbool.h>
#include "interface/font.h"
typedef struct EGL_Alert EGL_Alert;
bool egl_alert_init(EGL_Alert ** alert, const LG_Font * font, LG_FontObj fontObj);
void egl_alert_free(EGL_Alert ** alert);
void egl_alert_set_color(EGL_Alert * alert, const uint32_t color);
void egl_alert_set_text (EGL_Alert * alert, const char * str);
void egl_alert_render (EGL_Alert * alert, const float scaleX, const float scaleY);

View File

@@ -0,0 +1,289 @@
/*
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 "cursor.h"
#include "common/debug.h"
#include "common/locking.h"
#include "texture.h"
#include "shader.h"
#include "model.h"
#include <stdlib.h>
#include <string.h>
// these headers are auto generated by cmake
#include "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 stride;
uint8_t * data;
size_t dataSize;
bool update;
// cursor state
bool visible;
float x, y, w, h;
// textures
struct EGL_Texture * texture;
struct EGL_Texture * textureMono;
// shaders
struct EGL_Shader * shader;
struct EGL_Shader * shaderMono;
// uniforms
GLuint uMousePos;
GLuint uMousePosMono;
// model
struct EGL_Model * model;
};
bool egl_cursor_init(EGL_Cursor ** cursor)
{
*cursor = (EGL_Cursor *)malloc(sizeof(EGL_Cursor));
if (!*cursor)
{
DEBUG_ERROR("Failed to malloc EGL_Cursor");
return false;
}
memset(*cursor, 0, sizeof(EGL_Cursor));
LG_LOCK_INIT((*cursor)->lock);
if (!egl_texture_init(&(*cursor)->texture))
{
DEBUG_ERROR("Failed to initialize the cursor texture");
return false;
}
if (!egl_texture_init(&(*cursor)->textureMono))
{
DEBUG_ERROR("Failed to initialize the cursor mono texture");
return false;
}
if (!egl_shader_init(&(*cursor)->shader))
{
DEBUG_ERROR("Failed to initialize the cursor shader");
return false;
}
if (!egl_shader_init(&(*cursor)->shaderMono))
{
DEBUG_ERROR("Failed to initialize the cursor mono shader");
return false;
}
if (!egl_shader_compile(
(*cursor)->shader,
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;
}
if (!egl_shader_compile(
(*cursor)->shaderMono,
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;
}
(*cursor)->uMousePos = egl_shader_get_uniform_location((*cursor)->shader , "mouse");
(*cursor)->uMousePosMono = egl_shader_get_uniform_location((*cursor)->shaderMono, "mouse");
if (!egl_model_init(&(*cursor)->model))
{
DEBUG_ERROR("Failed to initialize the cursor model");
return false;
}
egl_model_set_default((*cursor)->model);
return true;
}
void egl_cursor_free(EGL_Cursor ** cursor)
{
if (!*cursor)
return;
LG_LOCK_FREE((*cursor)->lock);
if ((*cursor)->data)
free((*cursor)->data);
egl_texture_free(&(*cursor)->texture );
egl_texture_free(&(*cursor)->textureMono);
egl_shader_free (&(*cursor)->shader );
egl_shader_free (&(*cursor)->shaderMono );
egl_model_free (&(*cursor)->model );
free(*cursor);
*cursor = NULL;
}
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->stride = stride;
const size_t size = height * stride;
if (size > cursor->dataSize)
{
if (cursor->data)
free(cursor->data);
cursor->data = (uint8_t *)malloc(size);
if (!cursor->data)
{
DEBUG_ERROR("Failed to malloc buffer for cursor shape");
return false;
}
cursor->dataSize = size;
}
memcpy(cursor->data, data, size);
cursor->update = true;
LG_UNLOCK(cursor->lock);
return true;
}
void egl_cursor_set_size(EGL_Cursor * cursor, const float w, const float h)
{
cursor->w = w;
cursor->h = h;
}
void egl_cursor_set_state(EGL_Cursor * cursor, const bool visible, const float x, const float y)
{
cursor->visible = visible;
cursor->x = x;
cursor->y = y;
}
void egl_cursor_render(EGL_Cursor * cursor)
{
if (!cursor->visible)
return;
if (cursor->update)
{
LG_LOCK(cursor->lock);
cursor->update = false;
uint8_t * data = cursor->data;
switch(cursor->type)
{
case LG_CURSOR_MASKED_COLOR:
// fall through
case LG_CURSOR_COLOR:
{
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;
}
case LG_CURSOR_MONOCHROME:
{
uint32_t and[cursor->width * cursor->height];
uint32_t xor[cursor->width * cursor->height];
for(int y = 0; y < cursor->height; ++y)
for(int x = 0; x < cursor->width; ++x)
{
const uint8_t * srcAnd = data + (cursor->stride * y) + (x / 8);
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;
and[y * cursor->width + x] = andMask;
xor[y * cursor->width + x] = xorMask;
}
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;
}
}
LG_UNLOCK(cursor->lock);
}
glEnable(GL_BLEND);
switch(cursor->type)
{
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->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;
}
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;
}
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;
}
}
glDisable(GL_BLEND);
}

View File

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

@@ -0,0 +1,32 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <stdbool.h>
#include "interface/renderer.h"
typedef struct EGL_Desktop EGL_Desktop;
bool egl_desktop_init(void * egl, EGL_Desktop ** desktop);
void egl_desktop_free(EGL_Desktop ** desktop);
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

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

View File

@@ -0,0 +1,25 @@
/*
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 "model.h"
void egl_draw_torus (EGL_Model * model, unsigned int pts, float x, float y, float inner, float outer);
void egl_draw_torus_arc(EGL_Model * model, unsigned int pts, float x, float y, float inner, float outer, float s, float e);

626
client/renderers/EGL/egl.c Normal file
View File

@@ -0,0 +1,626 @@
/*
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/renderer.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 "dynamic/fonts.h"
#include <SDL2/SDL_syswm.h>
#include <SDL2/SDL_egl.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
struct Options
{
bool vsync;
};
struct Inst
{
LG_RendererParams params;
struct Options opt;
EGLNativeWindowType nativeWind;
EGLDisplay display;
EGLConfig configs;
EGLSurface surface;
EGLContext context, frameContext;
EGL_Desktop * desktop; // the desktop
EGL_Cursor * cursor; // the mouse cursor
EGL_FPS * fps; // the fps display
EGL_Splash * splash; // the splash screen
EGL_Alert * alert; // the alert display
LG_RendererFormat format;
bool start;
uint64_t waitFadeTime;
bool waitDone;
bool showAlert;
uint64_t alertTimeout;
bool useCloseFlag;
bool closeFlag;
int width, height;
LG_RendererRect destRect;
float translateX , translateY;
float scaleX , scaleY;
float splashRatio;
float screenScaleX, screenScaleY;
bool useNearest;
bool cursorVisible;
int cursorX , cursorY;
float mouseWidth , mouseHeight;
float mouseScaleX, mouseScaleY;
const LG_Font * font;
LG_FontObj fontObj;
};
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()
{
return "EGL";
}
void egl_setup()
{
option_register(egl_options);
}
bool egl_create(void ** opaque, const LG_RendererParams params)
{
// create our local storage
*opaque = malloc(sizeof(struct Inst));
if (!*opaque)
{
DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct Inst));
return false;
}
memset(*opaque, 0, sizeof(struct Inst));
// safe off parameteres and init our default option values
struct Inst * this = (struct Inst *)*opaque;
memcpy(&this->params, &params, sizeof(LG_RendererParams));
this->opt.vsync = option_get_bool("egl", "vsync");
this->translateX = 0;
this->translateY = 0;
this->scaleX = 1.0f;
this->scaleY = 1.0f;
this->screenScaleX = 1.0f;
this->screenScaleY = 1.0f;
this->font = LG_Fonts[0];
if (!this->font->create(&this->fontObj, NULL, 16))
{
DEBUG_ERROR("Failed to create a font instance");
return false;
}
return true;
}
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 , 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;
}
void egl_deinitialize(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
if (this->font && this->fontObj)
this->font->destroy(this->fontObj);
egl_desktop_free(&this->desktop);
egl_cursor_free (&this->cursor);
egl_fps_free (&this->fps );
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;
this->width = width;
this->height = height;
memcpy(&this->destRect, &destRect, sizeof(LG_RendererRect));
glViewport(0, 0, width, height);
if (destRect.valid)
{
this->translateX = 1.0f - (((destRect.w / 2) + destRect.x) * 2) / (float)width;
this->translateY = 1.0f - (((destRect.h / 2) + destRect.y) * 2) / (float)height;
this->scaleX = (float)destRect.w / (float)width;
this->scaleY = (float)destRect.h / (float)height;
}
this->mouseScaleX = 2.0f / this->format.width ;
this->mouseScaleY = 2.0f / this->format.height;
egl_cursor_set_size(this->cursor,
(this->mouseWidth * (1.0f / this->format.width )) * this->scaleX,
(this->mouseHeight * (1.0f / this->format.height)) * this->scaleY
);
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)
{
struct Inst * this = (struct Inst *)opaque;
if (!egl_cursor_set_shape(this->cursor, cursor, width, height, pitch, data))
{
DEBUG_ERROR("Failed to update the cursor shape");
return false;
}
this->mouseWidth = width;
this->mouseHeight = height;
egl_cursor_set_size(this->cursor,
(this->mouseWidth * (1.0f / this->format.width )) * this->scaleX,
(this->mouseHeight * (1.0f / this->format.height)) * this->scaleY
);
return true;
}
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,
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 FrameBuffer * frame)
{
struct Inst * this = (struct Inst *)opaque;
const bool sourceChanged = (
this->format.type != format.type ||
this->format.width != format.width ||
this->format.height != format.height ||
this->format.pitch != format.pitch
);
if (sourceChanged)
memcpy(&this->format, &format, sizeof(LG_RendererFormat));
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)
{
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;
}
this->start = true;
return true;
}
void egl_on_alert(void * opaque, const LG_MsgAlert alert, const char * message, bool ** closeFlag)
{
struct Inst * this = (struct Inst *)opaque;
static const uint32_t colors[] =
{
0x0000CCCC, // LG_ALERT_INFO
0x00CC00CC, // LG_ALERT_SUCCESS
0xCC7F00CC, // LG_ALERT_WARNING
0xFF0000CC // LG_ALERT_ERROR
};
if (alert > LG_ALERT_ERROR || alert < 0)
{
DEBUG_ERROR("Invalid alert value");
return;
}
egl_alert_set_color(this->alert, colors[alert]);
egl_alert_set_text (this->alert, message );
if (closeFlag)
{
this->useCloseFlag = true;
*closeFlag = &this->closeFlag;
}
else
{
this->useCloseFlag = false;
this->alertTimeout = microtime() + ALERT_TIMEOUT;
}
this->showAlert = true;
}
bool egl_render_startup(void * opaque, SDL_Window * window)
{
struct Inst * this = (struct Inst *)opaque;
SDL_SysWMinfo wminfo;
SDL_VERSION(&wminfo.version);
if (!SDL_GetWindowWMInfo(window, &wminfo))
{
DEBUG_ERROR("SDL_GetWindowWMInfo failed");
return false;
}
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;
}
if (this->display == EGL_NO_DISPLAY)
{
DEBUG_ERROR("eglGetDisplay failed");
return false;
}
if (!eglInitialize(this->display, NULL, NULL))
{
DEBUG_ERROR("Unable to initialize EGL");
return false;
}
EGLint attr[] =
{
EGL_BUFFER_SIZE , 32,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SAMPLE_BUFFERS , 1,
EGL_SAMPLES , 4,
EGL_NONE
};
EGLint num_config;
if (!eglChooseConfig(this->display, attr, &this->configs, 1, &num_config))
{
DEBUG_ERROR("Failed to choose config (eglError: 0x%x)", eglGetError());
return false;
}
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());
return false;
}
EGLint ctxattr[] =
{
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
this->context = eglCreateContext(this->display, this->configs, EGL_NO_CONTEXT, ctxattr);
if (this->context == EGL_NO_CONTEXT)
{
DEBUG_ERROR("Failed to create EGL context (eglError: 0x%x)", eglGetError());
return false;
}
eglMakeCurrent(this->display, this->surface, this->surface, this->context);
DEBUG_INFO("Vendor : %s", glGetString(GL_VENDOR ));
DEBUG_INFO("Renderer: %s", glGetString(GL_RENDERER));
DEBUG_INFO("Version : %s", glGetString(GL_VERSION ));
eglSwapInterval(this->display, this->opt.vsync ? 1 : 0);
if (!egl_desktop_init(this, &this->desktop))
{
DEBUG_ERROR("Failed to initialize the desktop");
return false;
}
if (!egl_cursor_init(&this->cursor))
{
DEBUG_ERROR("Failed to initialize the cursor");
return false;
}
if (!egl_fps_init(&this->fps, this->font, this->fontObj))
{
DEBUG_ERROR("Failed to initialize the FPS display");
return false;
}
if (!egl_splash_init(&this->splash))
{
DEBUG_ERROR("Failed to initialize the splash screen");
return false;
}
if (!egl_alert_init(&this->alert, this->font, this->fontObj))
{
DEBUG_ERROR("Failed to initialize the alert display");
return false;
}
return true;
}
bool egl_render(void * opaque, SDL_Window * window)
{
struct Inst * this = (struct Inst *)opaque;
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
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)
{
float a = 1.0f;
if (!this->waitFadeTime)
a = 1.0f;
else
{
uint64_t t = microtime();
if (t > this->waitFadeTime)
this->waitDone = true;
else
{
uint64_t delta = this->waitFadeTime - t;
a = 1.0f / SPLASH_FADE_TIME * delta;
}
}
if (!this->waitDone)
egl_splash_render(this->splash, a, this->splashRatio);
}
else
{
if (!this->start)
egl_splash_render(this->splash, 1.0f, this->splashRatio);
}
if (this->showAlert)
{
bool close = false;
if (this->useCloseFlag)
close = this->closeFlag;
else if (this->alertTimeout < microtime())
close = true;
if (close)
this->showAlert = false;
else
egl_alert_render(this->alert, this->screenScaleX, this->screenScaleY);
}
egl_fps_render(this->fps, this->screenScaleX, this->screenScaleY);
eglSwapBuffers(this->display, this->surface);
return true;
}
void egl_update_fps(void * opaque, const float avgUPS, const float avgFPS)
{
struct Inst * this = (struct Inst *)opaque;
if (!this->params.showFPS)
return;
egl_fps_update(this->fps, avgUPS, avgFPS);
}
struct LG_Renderer LGR_EGL =
{
.get_name = egl_get_name,
.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,
.on_frame_event = egl_on_frame_event,
.on_alert = egl_on_alert,
.render_startup = egl_render_startup,
.render = egl_render,
.update_fps = egl_update_fps
};

196
client/renderers/EGL/fps.c Normal file
View File

@@ -0,0 +1,196 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
cahe terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "fps.h"
#include "common/debug.h"
#include "utils.h"
#include "texture.h"
#include "shader.h"
#include "model.h"
#include <stdlib.h>
#include <string.h>
// these headers are auto generated by cmake
#include "fps.vert.h"
#include "fps.frag.h"
#include "fps_bg.frag.h"
struct EGL_FPS
{
const LG_Font * font;
LG_FontObj fontObj;
EGL_Texture * texture;
EGL_Shader * shader;
EGL_Shader * shaderBG;
EGL_Model * model;
bool ready;
int iwidth, iheight;
float width, height;
// uniforms
GLint uScreen , uSize;
GLint uScreenBG, uSizeBG;
};
bool egl_fps_init(EGL_FPS ** fps, const LG_Font * font, LG_FontObj fontObj)
{
*fps = (EGL_FPS *)malloc(sizeof(EGL_FPS));
if (!*fps)
{
DEBUG_ERROR("Failed to malloc EGL_FPS");
return false;
}
memset(*fps, 0, sizeof(EGL_FPS));
(*fps)->font = font;
(*fps)->fontObj = fontObj;
if (!egl_texture_init(&(*fps)->texture))
{
DEBUG_ERROR("Failed to initialize the fps texture");
return false;
}
if (!egl_shader_init(&(*fps)->shader))
{
DEBUG_ERROR("Failed to initialize the fps shader");
return false;
}
if (!egl_shader_init(&(*fps)->shaderBG))
{
DEBUG_ERROR("Failed to initialize the fps bg shader");
return false;
}
if (!egl_shader_compile((*fps)->shader,
b_shader_fps_vert, b_shader_fps_vert_size,
b_shader_fps_frag, b_shader_fps_frag_size))
{
DEBUG_ERROR("Failed to compile the fps shader");
return false;
}
if (!egl_shader_compile((*fps)->shaderBG,
b_shader_fps_vert , b_shader_fps_vert_size,
b_shader_fps_bg_frag, b_shader_fps_bg_frag_size))
{
DEBUG_ERROR("Failed to compile the fps shader");
return false;
}
(*fps)->uSize = egl_shader_get_uniform_location((*fps)->shader , "size" );
(*fps)->uScreen = egl_shader_get_uniform_location((*fps)->shader , "screen");
(*fps)->uSizeBG = egl_shader_get_uniform_location((*fps)->shaderBG, "size" );
(*fps)->uScreenBG = egl_shader_get_uniform_location((*fps)->shaderBG, "screen");
if (!egl_model_init(&(*fps)->model))
{
DEBUG_ERROR("Failed to initialize the fps model");
return false;
}
egl_model_set_default((*fps)->model);
egl_model_set_texture((*fps)->model, (*fps)->texture);
return true;
}
void egl_fps_free(EGL_FPS ** fps)
{
if (!*fps)
return;
egl_texture_free(&(*fps)->texture );
egl_shader_free (&(*fps)->shader );
egl_shader_free (&(*fps)->shaderBG);
egl_model_free (&(*fps)->model );
free(*fps);
*fps = NULL;
}
void egl_fps_update(EGL_FPS * fps, const float avgFPS, const float renderFPS)
{
char str[128];
snprintf(str, sizeof(str), "UPS: %8.4f, FPS: %8.4f", avgFPS, renderFPS);
LG_FontBitmap * bmp = fps->font->render(fps->fontObj, 0xffffff00, str);
if (!bmp)
{
DEBUG_ERROR("Failed to render fps text");
return;
}
if (fps->iwidth != bmp->width || fps->iheight != bmp->height)
{
fps->iwidth = bmp->width;
fps->iheight = bmp->height;
fps->width = (float)bmp->width;
fps->height = (float)bmp->height;
egl_texture_setup(
fps->texture,
EGL_PF_BGRA,
bmp->width ,
bmp->height,
bmp->width * bmp->bpp,
false
);
}
egl_texture_update
(
fps->texture,
bmp->pixels
);
fps->ready = true;
fps->font->release(fps->fontObj, bmp);
}
void egl_fps_render(EGL_FPS * fps, const float scaleX, const float scaleY)
{
if (!fps->ready)
return;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// render the background first
egl_shader_use(fps->shaderBG);
glUniform2f(fps->uScreenBG, scaleX , scaleY );
glUniform2f(fps->uSizeBG , fps->width, fps->height);
egl_model_render(fps->model);
// render the texture over the background
egl_shader_use(fps->shader);
glUniform2f(fps->uScreen, scaleX , scaleY );
glUniform2f(fps->uSize , fps->width, fps->height);
egl_model_render(fps->model);
glDisable(GL_BLEND);
}

View File

@@ -0,0 +1,32 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <stdbool.h>
#include "interface/font.h"
typedef struct EGL_FPS EGL_FPS;
bool egl_fps_init(EGL_FPS ** fps, const LG_Font * font, LG_FontObj fontObj);
void egl_fps_free(EGL_FPS ** fps);
void egl_fps_update(EGL_FPS * fps, const float avgUPS, const float avgFPS);
void egl_fps_render(EGL_FPS * fps, const float scaleX, const float scaleY);

View File

@@ -0,0 +1,219 @@
/*
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 "model.h"
#include "shader.h"
#include "texture.h"
#include "common/debug.h"
#include "utils.h"
#include "ll.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <SDL2/SDL_egl.h>
struct EGL_Model
{
bool rebuild;
struct ll * verticies;
size_t vertexCount;
bool finish;
bool hasBuffer;
GLuint buffer;
EGL_Shader * shader;
EGL_Texture * texture;
};
struct FloatList
{
GLfloat * v;
GLfloat * u;
size_t count;
};
void update_uniform_bindings(EGL_Model * model);
bool egl_model_init(EGL_Model ** model)
{
*model = (EGL_Model *)malloc(sizeof(EGL_Model));
if (!*model)
{
DEBUG_ERROR("Failed to malloc EGL_Model");
return false;
}
memset(*model, 0, sizeof(EGL_Model));
(*model)->verticies = ll_new();
return true;
}
void egl_model_free(EGL_Model ** model)
{
if (!*model)
return;
struct FloatList * fl;
while(ll_shift((*model)->verticies, (void **)&fl))
{
free(fl->u);
free(fl->v);
free(fl);
}
ll_free((*model)->verticies);
if ((*model)->hasBuffer)
glDeleteBuffers(1, &(*model)->buffer);
free(*model);
*model = NULL;
}
void egl_model_set_default(EGL_Model * model)
{
static const GLfloat square[] =
{
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f
};
static const GLfloat uvs[] =
{
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
};
egl_model_add_verticies(model, square, uvs, 4);
}
void egl_model_add_verticies(EGL_Model * model, const GLfloat * verticies, const GLfloat * uvs, const size_t count)
{
struct FloatList * fl = (struct FloatList *)malloc(sizeof(struct FloatList));
fl->count = count;
fl->v = (GLfloat *)malloc(sizeof(GLfloat) * count * 3);
fl->u = (GLfloat *)malloc(sizeof(GLfloat) * count * 2);
memcpy(fl->v, verticies, sizeof(GLfloat) * count * 3);
if (uvs)
memcpy(fl->u, uvs, sizeof(GLfloat) * count * 2);
else
memset(fl->u, 0 , sizeof(GLfloat) * count * 2);
ll_push(model->verticies, fl);
model->rebuild = true;
model->vertexCount += count;
}
void egl_model_render(EGL_Model * model)
{
if (!model->vertexCount)
return;
if (model->rebuild)
{
if (model->hasBuffer)
glDeleteBuffers(1, &model->buffer);
/* create a buffer large enough */
glGenBuffers(1, &model->buffer);
glBindBuffer(GL_ARRAY_BUFFER, model->buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * (model->vertexCount * 5), NULL, GL_STATIC_DRAW);
GLintptr offset = 0;
/* buffer the verticies */
struct FloatList * fl;
for(ll_reset(model->verticies); ll_walk(model->verticies, (void **)&fl);)
{
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(GLfloat) * fl->count * 3, fl->v);
offset += sizeof(GLfloat) * fl->count * 3;
}
/* buffer the uvs */
for(ll_reset(model->verticies); ll_walk(model->verticies, (void **)&fl);)
{
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(GLfloat) * fl->count * 2, fl->u);
offset += sizeof(GLfloat) * fl->count * 2;
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
model->rebuild = false;
}
/* bind the model buffer and setup the pointers */
glBindBuffer(GL_ARRAY_BUFFER, model->buffer);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(GLfloat) * model->vertexCount * 3));
if (model->shader)
egl_shader_use(model->shader);
if (model->texture)
egl_texture_bind(model->texture);
/* draw the arrays */
GLint offset = 0;
struct FloatList * fl;
for(ll_reset(model->verticies); ll_walk(model->verticies, (void **)&fl);)
{
glDrawArrays(GL_TRIANGLE_STRIP, offset, fl->count);
offset += fl->count;
}
/* unbind and cleanup */
glBindTexture(GL_TEXTURE_2D, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glUseProgram(0);
}
void egl_model_set_shader(EGL_Model * model, EGL_Shader * shader)
{
model->shader = shader;
update_uniform_bindings(model);
}
void egl_model_set_texture(EGL_Model * model, EGL_Texture * texture)
{
model->texture = texture;
update_uniform_bindings(model);
}
void update_uniform_bindings(EGL_Model * model)
{
if (!model->shader || !model->texture)
return;
const int count = egl_texture_count(model->texture);
egl_shader_associate_textures(model->shader, count);
}

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,19 +17,22 @@ 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>
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "shader.h"
#include "texture.h"
bool spice_connect(const char * host, const short port, const char * password);
void spice_disconnect();
bool spice_process();
bool spice_ready();
#include <GL/gl.h>
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);
typedef struct EGL_Model EGL_Model;
bool egl_model_init(EGL_Model ** model);
void egl_model_free(EGL_Model ** model);
void egl_model_set_default (EGL_Model * model);
void egl_model_add_verticies(EGL_Model * model, const GLfloat * verticies, const GLfloat * uvs, const size_t count);
void egl_model_set_shader (EGL_Model * model, EGL_Shader * shader);
void egl_model_set_texture (EGL_Model * model, EGL_Texture * texture);
void egl_model_render(EGL_Model * model);

View File

@@ -0,0 +1,225 @@
/*
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 "shader.h"
#include "common/debug.h"
#include "utils.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <SDL2/SDL_egl.h>
struct EGL_Shader
{
bool hasShader;
GLuint shader;
};
bool egl_shader_init(EGL_Shader ** this)
{
*this = (EGL_Shader *)malloc(sizeof(EGL_Shader));
if (!*this)
{
DEBUG_ERROR("Failed to malloc EGL_Shader");
return false;
}
memset(*this, 0, sizeof(EGL_Shader));
return true;
}
void egl_shader_free(EGL_Shader ** this)
{
if (!*this)
return;
if ((*this)->hasShader)
glDeleteProgram((*this)->shader);
free(*this);
*this = NULL;
}
bool egl_shader_load(EGL_Shader * this, const char * vertex_file, const char * fragment_file)
{
char * vertex_code, * fragment_code;
size_t vertex_size, fragment_size;
if (!file_get_contents(vertex_file, &vertex_code, &vertex_size))
{
DEBUG_ERROR("Failed to read vertex shader");
return false;
}
DEBUG_INFO("Loaded vertex shader: %s", vertex_file);
if (!file_get_contents(fragment_file, &fragment_code, &fragment_size))
{
DEBUG_ERROR("Failed to read fragment shader");
free(vertex_code);
return false;
}
DEBUG_INFO("Loaded fragment shader: %s", fragment_file);
bool ret = egl_shader_compile(this, vertex_code, vertex_size, fragment_code, fragment_size);
free(vertex_code);
free(fragment_code);
return ret;
}
bool egl_shader_compile(EGL_Shader * this, const char * vertex_code, size_t vertex_size, const char * fragment_code, size_t fragment_size)
{
if (this->hasShader)
{
glDeleteProgram(this->shader);
this->hasShader = false;
}
GLint length;
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
length = vertex_size;
glShaderSource(vertexShader, 1, (const char**)&vertex_code, &length);
glCompileShader(vertexShader);
GLint result = GL_FALSE;
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE)
{
DEBUG_ERROR("Failed to compile vertex shader");
int logLength;
glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0)
{
char *log = malloc(logLength + 1);
glGetShaderInfoLog(vertexShader, logLength, NULL, log);
log[logLength] = 0;
DEBUG_ERROR("%s", log);
free(log);
}
glDeleteShader(vertexShader);
return false;
}
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
length = fragment_size;
glShaderSource(fragmentShader, 1, (const char**)&fragment_code, &length);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE)
{
DEBUG_ERROR("Failed to compile fragment shader");
int logLength;
glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0)
{
char *log = malloc(logLength + 1);
glGetShaderInfoLog(fragmentShader, logLength, NULL, log);
log[logLength] = 0;
DEBUG_ERROR("%s", log);
free(log);
}
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader );
return false;
}
this->shader = glCreateProgram();
glAttachShader(this->shader, vertexShader );
glAttachShader(this->shader, fragmentShader);
glLinkProgram(this->shader);
glGetProgramiv(this->shader, GL_LINK_STATUS, &result);
if (result == GL_FALSE)
{
DEBUG_ERROR("Failed to link shader program");
int logLength;
glGetProgramiv(this->shader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0)
{
char *log = malloc(logLength + 1);
glGetProgramInfoLog(this->shader, logLength, NULL, log);
log[logLength] = 0;
DEBUG_ERROR("%s", log);
free(log);
}
glDetachShader(this->shader, vertexShader );
glDetachShader(this->shader, fragmentShader);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader );
glDeleteProgram(this->shader );
return false;
}
glDetachShader(this->shader, vertexShader );
glDetachShader(this->shader, fragmentShader);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader );
this->hasShader = true;
return true;
}
void egl_shader_use(EGL_Shader * this)
{
if (this->hasShader)
glUseProgram(this->shader);
else
DEBUG_ERROR("Shader program has not been compiled");
}
void egl_shader_associate_textures(EGL_Shader * this, const int count)
{
char name[] = "sampler1";
glUseProgram(this->shader);
for(int i = 0; i < count; ++i, name[7]++)
{
GLint loc = glGetUniformLocation(this->shader, name);
if (loc == -1)
{
DEBUG_WARN("Shader uniform location `%s` not found", name);
continue;
}
glUniform1i(loc, i);
}
glUseProgram(0);
}
GLint egl_shader_get_uniform_location(EGL_Shader * this, const char * name)
{
if (!this->shader)
{
DEBUG_ERROR("Shader program has not been compiled");
return 0;
}
return glGetUniformLocation(this->shader, name);
}

View File

@@ -0,0 +1,37 @@
/*
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 <stddef.h>
#include <GL/gl.h>
typedef struct EGL_Shader EGL_Shader;
bool egl_shader_init(EGL_Shader ** shader);
void egl_shader_free(EGL_Shader ** shader);
bool egl_shader_load (EGL_Shader * model, const char * vertex_file, const char * fragment_file);
bool egl_shader_compile(EGL_Shader * model, const char * vertex_code, size_t vertex_size, const char * fragment_code, size_t fragment_size);
void egl_shader_use (EGL_Shader * shader);
void egl_shader_associate_textures(EGL_Shader * shader, const int count);
GLint egl_shader_get_uniform_location(EGL_Shader * shader, const char * name);

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

@@ -0,0 +1,177 @@
/*
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 "splash.h"
#include "common/debug.h"
#include "utils.h"
#include "draw.h"
#include "texture.h"
#include "shader.h"
#include "model.h"
#include <GL/gl.h>
#include <stdlib.h>
#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;
EGL_Model * bg;
EGL_Shader * logoShader;
EGL_Model * logo;
// uniforms
GLint uBGAlpha;
GLint uScale;
};
bool egl_splash_init(EGL_Splash ** splash)
{
*splash = (EGL_Splash *)malloc(sizeof(EGL_Splash));
if (!*splash)
{
DEBUG_ERROR("Failed to malloc EGL_Splash");
return false;
}
memset(*splash, 0, sizeof(EGL_Splash));
if (!egl_shader_init(&(*splash)->bgShader))
{
DEBUG_ERROR("Failed to initialize the splash bgShader");
return false;
}
if (!egl_shader_compile((*splash)->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;
}
(*splash)->uBGAlpha = egl_shader_get_uniform_location((*splash)->bgShader, "alpha");
if (!egl_model_init(&(*splash)->bg))
{
DEBUG_ERROR("Failed to intiailize the splash bg model");
return false;
}
egl_model_set_default((*splash)->bg);
if (!egl_shader_init(&(*splash)->logoShader))
{
DEBUG_ERROR("Failed to initialize the splash logoShader");
return false;
}
if (!egl_shader_compile((*splash)->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;
}
(*splash)->uScale = egl_shader_get_uniform_location((*splash)->logoShader, "scale");
if (!egl_model_init(&(*splash)->logo))
{
DEBUG_ERROR("Failed to intiailize the splash model");
return false;
}
/* build the splash model */
#define P(x) ((1.0f/800.0f)*(float)(x))
egl_draw_torus_arc((*splash)->logo, 30, P( 0 ), P(0), P(102), P(98), 0.0f, -M_PI);
egl_draw_torus ((*splash)->logo, 30, P(-100), P(8), P(8 ), P(4 ));
egl_draw_torus ((*splash)->logo, 30, P( 100), P(8), P(8 ), P(4 ));
egl_draw_torus ((*splash)->logo, 60, P(0), P(0), P(83), P(79));
egl_draw_torus ((*splash)->logo, 60, P(0), P(0), P(67), P(63));
static const GLfloat lines[][12] =
{
{
P( -2), P(-140), 0.0f,
P( -2), P(-100), 0.0f,
P( 2), P(-140), 0.0f,
P( 2), P(-100), 0.0f
},
{
P(-26), P(-144), 0.0f,
P(-26), P(-140), 0.0f,
P( 26), P(-144), 0.0f,
P( 26), P(-140), 0.0f
},
{
P(-40), P(-156), 0.0f,
P(-40), P(-152), 0.0f,
P( 40), P(-156), 0.0f,
P( 40), P(-152), 0.0f
}
};
egl_model_add_verticies((*splash)->logo, lines[0], NULL, 4);
egl_model_add_verticies((*splash)->logo, lines[1], NULL, 4);
egl_model_add_verticies((*splash)->logo, lines[2], NULL, 4);
egl_draw_torus_arc((*splash)->logo, 10, P(-26), P(-154), P(10), P(14), M_PI , -M_PI / 2.0);
egl_draw_torus_arc((*splash)->logo, 10, P( 26), P(-154), P(10), P(14), M_PI / 2.0f, -M_PI / 2.0);
#undef P
return true;
}
void egl_splash_free(EGL_Splash ** splash)
{
if (!*splash)
return;
egl_model_free(&(*splash)->logo);
free(*splash);
*splash = NULL;
}
void egl_splash_render(EGL_Splash * splash, float alpha, float scaleY)
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
egl_shader_use(splash->bgShader);
glUniform1f(splash->uBGAlpha, alpha);
egl_model_render(splash->bg);
egl_shader_use(splash->logoShader);
glUniform2f(splash->uScale, alpha, scaleY);
egl_model_render(splash->logo);
glDisable(GL_BLEND);
}

View File

@@ -0,0 +1,29 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#include <stdbool.h>
typedef struct EGL_Splash EGL_Splash;
bool egl_splash_init(EGL_Splash ** splash);
void egl_splash_free(EGL_Splash ** splash);
void egl_splash_render(EGL_Splash * splash, float alpha, float scaleY);

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

@@ -0,0 +1,53 @@
/*
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 "shader.h"
#include "common/framebuffer.h"
#include <GL/gl.h>
typedef struct EGL_Texture EGL_Texture;
enum EGL_PixelFormat
{
EGL_PF_RGBA,
EGL_PF_BGRA,
EGL_PF_RGBA10,
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 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}
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,814 +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 "lg-renderer.h"
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <SDL2/SDL_ttf.h>
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#include "debug.h"
#include "memcpySSE.h"
#include "utils.h"
#define FRAME_TEXTURE 0
#define FPS_TEXTURE 1
#define MOUSE_TEXTURE 2
#define TEXTURE_COUNT 3
static PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI = NULL;
static PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI = NULL;
struct Options
{
bool mipmap;
bool vsync;
bool preventBuffer;
bool splitMouse;
};
static struct Options defaultOptions =
{
.mipmap = true,
.vsync = true,
.preventBuffer = true,
.splitMouse = false
};
struct LGR_OpenGLBasic
{
LG_RendererParams params;
struct Options opt;
bool configured;
SDL_Window * sdlWindow;
SDL_GLContext glContext;
bool doneInfo;
SDL_Point window;
bool resizeWindow;
bool frameUpdate;
LG_RendererFormat format;
GLuint intFormat;
GLuint vboFormat;
size_t texSize;
uint64_t drawStart;
int texList;
int fpsList;
int mouseList;
LG_RendererRect destRect;
bool hasTextures;
GLuint textures[TEXTURE_COUNT];
uint gpuFrameCount;
bool fpsTexture;
uint64_t lastFrameTime;
uint64_t renderTime;
uint64_t frameCount;
uint64_t renderCount;
SDL_Rect fpsRect;
bool mouseUpdate;
bool newShape;
uint64_t lastMouseDraw;
LG_RendererCursor mouseType;
bool mouseVisible;
SDL_Rect mousePos;
};
bool lgr_opengl_basic_check_error(const char * name)
{
GLenum error = glGetError();
if (error == GL_NO_ERROR)
return false;
const GLubyte * errStr = gluErrorString(error);
DEBUG_ERROR("%s = %d (%s)", name, error, errStr);
return true;
}
const char * lgr_opengl_basic_get_name()
{
return "OpenGL-Basic";
}
bool lgr_opengl_basic_create(void ** opaque, const LG_RendererParams params)
{
// create our local storage
*opaque = malloc(sizeof(struct LGR_OpenGLBasic));
if (!*opaque)
{
DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct LGR_OpenGLBasic));
return false;
}
memset(*opaque, 0, sizeof(struct LGR_OpenGLBasic));
struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)*opaque;
memcpy(&this->params, &params , sizeof(LG_RendererParams));
memcpy(&this->opt , &defaultOptions, sizeof(struct Options ));
return true;
}
bool lgr_opengl_basic_initialize(void * opaque, Uint32 * sdlFlags)
{
struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)opaque;
if (!this)
return false;
if (!glXGetVideoSyncSGI)
{
glXGetVideoSyncSGI = (PFNGLXGETVIDEOSYNCSGIPROC )glXGetProcAddress((const GLubyte *)"glXGetVideoSyncSGI" );
glXWaitVideoSyncSGI = (PFNGLXWAITVIDEOSYNCSGIPROC)glXGetProcAddress((const GLubyte *)"glXWaitVideoSyncSGI");
if (!glXGetVideoSyncSGI || !glXWaitVideoSyncSGI)
{
glXGetVideoSyncSGI = NULL;
DEBUG_ERROR("Failed to get proc addresses");
return false;
}
}
*sdlFlags = SDL_WINDOW_OPENGL;
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
return true;
}
bool lgr_opengl_basic_configure(void * opaque, SDL_Window *window, const LG_RendererFormat format)
{
struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)opaque;
if (!this)
return false;
if (this->configured)
{
DEBUG_ERROR("Renderer already configured, call deconfigure first");
return false;
}
this->sdlWindow = window;
this->glContext = SDL_GL_CreateContext(window);
if (!this->glContext)
{
DEBUG_ERROR("Failed to create the OpenGL context");
return false;
}
if (!this->doneInfo)
{
DEBUG_INFO("Vendor : %s", glGetString(GL_VENDOR ));
DEBUG_INFO("Renderer: %s", glGetString(GL_RENDERER));
DEBUG_INFO("Version : %s", glGetString(GL_VERSION ));
this->doneInfo = true;
}
if (SDL_GL_MakeCurrent(window, this->glContext) != 0)
{
DEBUG_ERROR("Failed to make the GL context current");
return false;
}
SDL_GL_SetSwapInterval(this->opt.vsync ? 1 : 0);
// check if the GPU supports GL_ARB_buffer_storage first
// there is no advantage to this renderer if it is not present.
const GLubyte * extensions = glGetString(GL_EXTENSIONS);
if (!gluCheckExtension((const GLubyte *)"GL_ARB_buffer_storage", extensions))
{
DEBUG_INFO("The GPU doesn't support GL_ARB_buffer_storage");
return false;
}
// assume 24 and 32 bit formats are RGB and RGBA
switch(format.bpp)
{
case 24:
this->intFormat = GL_RGB8;
this->vboFormat = GL_BGR;
break;
case 32:
this->intFormat = GL_RGBA8;
this->vboFormat = GL_BGRA;
break;
default:
DEBUG_INFO("%d bpp not supported", format.bpp);
return false;
}
// calculate the texture size in bytes
this->texSize = format.height * format.pitch;
// generate lists for drawing
this->texList = glGenLists(1);
this->fpsList = glGenLists(1);
this->mouseList = glGenLists(1);
// create the textures
glGenTextures(TEXTURE_COUNT, this->textures);
if (lgr_opengl_basic_check_error("glGenTextures"))
return false;
this->hasTextures = true;
// create the frame texture
glBindTexture(GL_TEXTURE_2D, this->textures[FRAME_TEXTURE]);
if (lgr_opengl_basic_check_error("glBindTexture"))
return false;
glTexImage2D(
GL_TEXTURE_2D,
0,
this->intFormat,
format.width,
format.height,
0,
this->vboFormat,
GL_UNSIGNED_BYTE,
(void*)0
);
if (lgr_opengl_basic_check_error("glTexImage2D"))
return false;
// 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 list
glNewList(this->texList, GL_COMPILE);
glBindTexture(GL_TEXTURE_2D, this->textures[FRAME_TEXTURE]);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f, 0.0f); glVertex2i(0 , 0 );
glTexCoord2f(1.0f, 0.0f); glVertex2i(format.width, 0 );
glTexCoord2f(0.0f, 1.0f); glVertex2i(0 , format.height);
glTexCoord2f(1.0f, 1.0f); glVertex2i(format.width, format.height);
glEnd();
glEndList();
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glEnable(GL_TEXTURE_2D);
glEnable(GL_COLOR_MATERIAL);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
glEnable(GL_SCISSOR_TEST);
this->resizeWindow = true;
this->drawStart = nanotime();
glXGetVideoSyncSGI(&this->gpuFrameCount);
// copy the format into the local storage
memcpy(&this->format, &format, sizeof(LG_RendererFormat));
this->configured = true;
return true;
}
void lgr_opengl_basic_deconfigure(void * opaque)
{
struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)opaque;
if (!this || !this->configured)
return;
if (this->hasTextures)
{
glDeleteTextures(TEXTURE_COUNT, this->textures);
this->hasTextures = false;
}
if (this->glContext)
{
SDL_GL_DeleteContext(this->glContext);
this->glContext = NULL;
}
this->configured = false;
}
void lgr_opengl_basic_deinitialize(void * opaque)
{
struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)opaque;
if (!this)
return;
if (this->configured)
lgr_opengl_basic_deconfigure(opaque);
free(this);
}
bool lgr_opengl_basic_is_compatible(void * opaque, const LG_RendererFormat format)
{
const struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)opaque;
if (!this || !this->configured)
return false;
return (memcmp(&this->format, &format, sizeof(LG_RendererFormat)) == 0);
}
void lgr_opengl_basic_on_resize(void * opaque, const int width, const int height, const LG_RendererRect destRect)
{
struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)opaque;
if (!this || !this->configured)
return;
this->window.x = width;
this->window.y = height;
memcpy(&this->destRect, &destRect, sizeof(LG_RendererRect));
this->resizeWindow = true;
}
bool lgr_opengl_basic_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data)
{
struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)opaque;
if (!this || !this->configured)
return false;
this->mouseType = cursor;
switch(cursor)
{
case LG_CURSOR_COLOR:
{
glBindTexture(GL_TEXTURE_2D, this->textures[MOUSE_TEXTURE]);
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
glTexImage2D
(
GL_TEXTURE_2D,
0 ,
GL_RGBA,
width ,
height ,
0 ,
GL_BGRA, // windows cursors are in BGRA format
GL_UNSIGNED_BYTE,
data
);
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_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
this->mousePos.w = width;
this->mousePos.h = height;
glNewList(this->mouseList, GL_COMPILE);
glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, this->textures[MOUSE_TEXTURE]);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f, 0.0f); glVertex2i(0 , 0 );
glTexCoord2f(1.0f, 0.0f); glVertex2i(width, 0 );
glTexCoord2f(0.0f, 1.0f); glVertex2i(0 , height);
glTexCoord2f(1.0f, 1.0f); glVertex2i(width, height);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_BLEND);
glEndList();
break;
}
case LG_CURSOR_MONOCHROME:
{
const int hheight = height / 2;
uint32_t d[width * height];
for(int y = 0; y < hheight; ++y)
for(int x = 0; x < width; ++x)
{
const uint8_t * srcAnd = data + (pitch * y) + (x / 8);
const uint8_t * srcXor = srcAnd + pitch * hheight;
const uint8_t mask = 0x80 >> (x % 8);
const uint32_t andMask = (*srcAnd & mask) ? 0xFFFFFFFF : 0xFF000000;
const uint32_t xorMask = (*srcXor & mask) ? 0x00FFFFFF : 0x00000000;
d[y * width + x ] = andMask;
d[y * width + x + width * hheight] = xorMask;
}
glBindTexture(GL_TEXTURE_2D, this->textures[MOUSE_TEXTURE]);
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
glTexImage2D
(
GL_TEXTURE_2D,
0 ,
GL_RGBA,
width ,
height ,
0 ,
GL_RGBA,
GL_UNSIGNED_BYTE,
d
);
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_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
this->mousePos.w = width;
this->mousePos.h = hheight;
glNewList(this->mouseList, GL_COMPILE);
glEnable(GL_COLOR_LOGIC_OP);
glBindTexture(GL_TEXTURE_2D, this->textures[MOUSE_TEXTURE]);
glLogicOp(GL_AND);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f, 0.0f); glVertex2i(0 , 0 );
glTexCoord2f(1.0f, 0.0f); glVertex2i(width, 0 );
glTexCoord2f(0.0f, 0.5f); glVertex2i(0 , hheight);
glTexCoord2f(1.0f, 0.5f); glVertex2i(width, hheight);
glEnd();
glLogicOp(GL_XOR);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f, 0.5f); glVertex2i(0 , 0 );
glTexCoord2f(1.0f, 0.5f); glVertex2i(width, 0 );
glTexCoord2f(0.0f, 1.0f); glVertex2i(0 , hheight);
glTexCoord2f(1.0f, 1.0f); glVertex2i(width, hheight);
glEnd();
glDisable(GL_COLOR_LOGIC_OP);
glEndList();
break;
}
case LG_CURSOR_MASKED_COLOR:
{
//TODO
break;
}
}
this->mouseUpdate = true;
this->newShape = true;
return true;
}
bool lgr_opengl_basic_on_mouse_event(void * opaque, const bool visible, const int x, const int y)
{
struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)opaque;
if (!this || !this->configured)
return false;
if (this->mousePos.x == x && this->mousePos.y == y && this->mouseVisible == visible)
return true;
this->mouseVisible = visible;
this->mousePos.x = x;
this->mousePos.y = y;
this->mouseUpdate = true;
return false;
}
bool lgr_opengl_basic_on_frame_event(void * opaque, const uint8_t * data)
{
struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)opaque;
if (!this)
{
DEBUG_ERROR("Invalid opaque pointer");
return false;
}
if (!this->configured)
{
DEBUG_ERROR("Not configured");
return false;
}
if (this->params.showFPS && this->renderTime > 1e9)
{
char str[128];
const float avgFPS = 1000.0f / (((float)this->renderTime / this->frameCount ) / 1e6f);
const float renderFPS = 1000.0f / (((float)this->renderTime / this->renderCount) / 1e6f);
snprintf(str, sizeof(str), "UPS: %8.4f, FPS: %8.4f", avgFPS, renderFPS);
SDL_Color color = {0xff, 0xff, 0xff};
SDL_Surface *textSurface = NULL;
if (!(textSurface = TTF_RenderText_Blended(this->params.font, str, color)))
{
DEBUG_ERROR("Failed to render text");
return false;
}
glBindTexture(GL_TEXTURE_2D , this->textures[FPS_TEXTURE]);
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
glPixelStorei(GL_UNPACK_ROW_LENGTH, textSurface->w );
glTexImage2D(
GL_TEXTURE_2D,
0,
textSurface->format->BytesPerPixel,
textSurface->w,
textSurface->h,
0,
GL_BGRA,
GL_UNSIGNED_BYTE,
textSurface->pixels
);
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_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
this->fpsRect.x = 5;
this->fpsRect.y = 5;
this->fpsRect.w = textSurface->w;
this->fpsRect.h = textSurface->h;
SDL_FreeSurface(textSurface);
this->renderTime = 0;
this->frameCount = 0;
this->renderCount = 0;
this->fpsTexture = true;
glNewList(this->fpsList, GL_COMPILE);
glEnable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glColor4f(0.0f, 0.0f, 1.0f, 0.5f);
glBegin(GL_TRIANGLE_STRIP);
glVertex2i(this->fpsRect.x , this->fpsRect.y );
glVertex2i(this->fpsRect.x + this->fpsRect.w, this->fpsRect.y );
glVertex2i(this->fpsRect.x , this->fpsRect.y + this->fpsRect.h);
glVertex2i(this->fpsRect.x + this->fpsRect.w, this->fpsRect.y + this->fpsRect.h);
glEnd();
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, this->textures[FPS_TEXTURE]);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f , 0.0f); glVertex2i(this->fpsRect.x , this->fpsRect.y );
glTexCoord2f(1.0f , 0.0f); glVertex2i(this->fpsRect.x + this->fpsRect.w, this->fpsRect.y );
glTexCoord2f(0.0f , 1.0f); glVertex2i(this->fpsRect.x , this->fpsRect.y + this->fpsRect.h);
glTexCoord2f(1.0f, 1.0f); glVertex2i(this->fpsRect.x + this->fpsRect.w, this->fpsRect.y + this->fpsRect.h);
glEnd();
glDisable(GL_BLEND);
glEndList();
}
// bind the texture and update it
glBindTexture(GL_TEXTURE_2D , this->textures[FRAME_TEXTURE]);
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
glPixelStorei(GL_UNPACK_ROW_LENGTH , this->format.stride );
// update the texture
glTexSubImage2D(
GL_TEXTURE_2D,
0,
0,
0,
this->format.width ,
this->format.height,
this->vboFormat,
GL_UNSIGNED_BYTE,
data
);
lgr_opengl_basic_check_error("glTexSubImage2D");
// 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));
if (mipmap)
{
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
glBindTexture(GL_TEXTURE_2D, 0);
++this->frameCount;
this->frameUpdate = true;
return true;
}
static inline void lgr_opengl_basic_draw_mouse(struct LGR_OpenGLBasic * this)
{
if (!this->mouseVisible)
return;
glPushMatrix();
glTranslatef(this->mousePos.x, this->mousePos.y, 0.0f);
glCallList(this->mouseList);
glPopMatrix();
}
bool lgr_opengl_basic_render(void * opaque)
{
struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)opaque;
if (!this || !this->configured)
return false;
if (this->resizeWindow)
{
// setup the projection matrix
glViewport(0, 0, this->window.x, this->window.y);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, this->window.x, this->window.y, 0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(this->destRect.x, this->destRect.y, 0.0f);
glScalef(
(float)this->destRect.w / (float)this->format.width,
(float)this->destRect.h / (float)this->format.height,
1.0f
);
// update the scissor rect to prevent drawing outside of the frame
glScissor(
this->destRect.x,
this->destRect.y,
this->destRect.w,
this->destRect.h
);
this->resizeWindow = false;
}
if (this->opt.splitMouse)
{
if (!this->frameUpdate)
{
if (!this->mouseUpdate)
return true;
if (!this->newShape)
{
// don't update the mouse too fast
const uint64_t delta = nanotime() - this->lastMouseDraw;
if (delta < 5e6)
return true;
}
this->newShape = false;
glDrawBuffer(GL_FRONT);
glCallList(this->texList);
lgr_opengl_basic_draw_mouse(this);
if (this->fpsTexture)
glCallList(this->fpsList);
glDrawBuffer(GL_BACK);
glFlush();
this->mouseUpdate = false;
this->lastMouseDraw = nanotime();
return true;
}
}
glDisable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
glCallList(this->texList);
lgr_opengl_basic_draw_mouse(this);
if (this->fpsTexture)
glCallList(this->fpsList);
if (this->opt.preventBuffer)
{
unsigned int before, after;
glXGetVideoSyncSGI(&before);
SDL_GL_SwapWindow(this->sdlWindow);
// wait for the swap to happen to ensure we dont buffer frames
glXGetVideoSyncSGI(&after);
if (before == after)
glXWaitVideoSyncSGI(1, 0, &before);
}
else
SDL_GL_SwapWindow(this->sdlWindow);
const uint64_t t = nanotime();
this->renderTime += t - this->lastFrameTime;
this->lastFrameTime = t;
++this->renderCount;
this->frameUpdate = false;
this->mouseUpdate = false;
this->lastMouseDraw = t;
return true;
}
static void handle_opt_mipmap(void * opaque, const char *value)
{
struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)opaque;
if (!this)
return;
this->opt.mipmap = LG_RendererValueToBool(value);
}
static void handle_opt_vsync(void * opaque, const char *value)
{
struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)opaque;
if (!this)
return;
this->opt.vsync = LG_RendererValueToBool(value);
}
static void handle_opt_prevent_buffer(void * opaque, const char *value)
{
struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)opaque;
if (!this)
return;
this->opt.preventBuffer = LG_RendererValueToBool(value);
}
static void handle_opt_split_mouse(void * opaque, const char *value)
{
struct LGR_OpenGLBasic * this = (struct LGR_OpenGLBasic *)opaque;
if (!this)
return;
this->opt.splitMouse = LG_RendererValueToBool(value);
}
static LG_RendererOpt lgr_opengl_basic_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: enabled]",
.validator = LG_RendererValidatorBool,
.handler = handle_opt_prevent_buffer
},
{
.name = "splitMouse",
.desc = "Draw mouse updates directly to the front buffer [default: disabled]",
.validator = LG_RendererValidatorBool,
.handler = handle_opt_split_mouse
}
};
const LG_Renderer LGR_OpenGLBasic =
{
.get_name = lgr_opengl_basic_get_name,
.options = lgr_opengl_basic_options,
.option_count = LGR_OPTION_COUNT(lgr_opengl_basic_options),
.create = lgr_opengl_basic_create,
.initialize = lgr_opengl_basic_initialize,
.configure = lgr_opengl_basic_configure,
.deconfigure = lgr_opengl_basic_deconfigure,
.deinitialize = lgr_opengl_basic_deinitialize,
.is_compatible = lgr_opengl_basic_is_compatible,
.on_resize = lgr_opengl_basic_on_resize,
.on_mouse_shape = lgr_opengl_basic_on_mouse_shape,
.on_mouse_event = lgr_opengl_basic_on_mouse_event,
.on_frame_event = lgr_opengl_basic_on_frame_event,
.render = lgr_opengl_basic_render
};

View File

@@ -1,951 +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 "lg-renderer.h"
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <SDL2/SDL_ttf.h>
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#include "debug.h"
#include "memcpySSE.h"
#include "utils.h"
#define BUFFER_COUNT 2
#define FRAME_TEXTURE 0
#define FPS_TEXTURE 1
#define MOUSE_TEXTURE 2
#define TEXTURE_COUNT 3
static PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI = NULL;
static PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI = NULL;
struct Options
{
bool mipmap;
bool vsync;
bool preventBuffer;
};
static struct Options defaultOptions =
{
.mipmap = true,
.vsync = true,
.preventBuffer = true,
};
struct Inst
{
LG_RendererParams params;
struct Options opt;
bool configured;
bool reconfigure;
SDL_GLContext glContext;
bool doneInfo;
SDL_Point window;
bool resizeWindow;
bool frameUpdate;
LG_Lock formatLock;
LG_RendererFormat format;
GLuint intFormat;
GLuint vboFormat;
size_t texSize;
uint64_t drawStart;
bool hasBuffers;
GLuint vboID[1];
uint8_t * texPixels[BUFFER_COUNT];
LG_Lock syncLock;
int texIndex, wTexIndex;
int texList;
int fpsList;
int mouseList;
LG_RendererRect destRect;
bool hasTextures;
GLuint textures[TEXTURE_COUNT];
uint gpuFrameCount;
bool fpsTexture;
uint64_t lastFrameTime;
uint64_t renderTime;
uint64_t frameCount;
uint64_t renderCount;
SDL_Rect fpsRect;
LG_Lock mouseLock;
LG_RendererCursor mouseCursor;
int mouseWidth;
int mouseHeight;
int mousePitch;
uint8_t * mouseData;
size_t mouseDataSize;
bool mouseUpdate;
bool newShape;
uint64_t lastMouseDraw;
LG_RendererCursor mouseType;
bool mouseVisible;
SDL_Rect mousePos;
};
static bool _check_gl_error(unsigned int line, const char * name);
#define check_gl_error(name) _check_gl_error(__LINE__, name)
static void deconfigure(struct Inst * this);
static bool configure(struct Inst * this, SDL_Window *window);
static void update_mouse_shape(struct Inst * this, bool * newShape);
static bool draw_frame(struct Inst * this, bool * frameUpdate);
static void draw_mouse(struct Inst * this);
const char * opengl_get_name()
{
return "OpenGL";
}
bool opengl_create(void ** opaque, const LG_RendererParams params)
{
// create our local storage
*opaque = malloc(sizeof(struct Inst));
if (!*opaque)
{
DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct Inst));
return false;
}
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 ));
LG_LOCK_INIT(this->formatLock);
LG_LOCK_INIT(this->syncLock );
LG_LOCK_INIT(this->mouseLock );
return true;
}
bool opengl_initialize(void * opaque, Uint32 * sdlFlags)
{
struct Inst * this = (struct Inst *)opaque;
if (!this)
return false;
if (!glXGetVideoSyncSGI)
{
glXGetVideoSyncSGI = (PFNGLXGETVIDEOSYNCSGIPROC )glXGetProcAddress((const GLubyte *)"glXGetVideoSyncSGI" );
glXWaitVideoSyncSGI = (PFNGLXWAITVIDEOSYNCSGIPROC)glXGetProcAddress((const GLubyte *)"glXWaitVideoSyncSGI");
if (!glXGetVideoSyncSGI || !glXWaitVideoSyncSGI)
{
glXGetVideoSyncSGI = NULL;
DEBUG_ERROR("Failed to get proc addresses");
return false;
}
}
*sdlFlags = SDL_WINDOW_OPENGL;
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
return true;
}
void opengl_deinitialize(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
if (!this)
return;
deconfigure(this);
if (this->mouseData)
free(this->mouseData);
LG_LOCK_FREE(this->formatLock);
LG_LOCK_FREE(this->syncLock );
LG_LOCK_FREE(this->mouseLock );
free(this);
}
void opengl_on_resize(void * opaque, const int width, const int height, const LG_RendererRect destRect)
{
struct Inst * this = (struct Inst *)opaque;
if (!this)
return;
this->window.x = width;
this->window.y = height;
memcpy(&this->destRect, &destRect, sizeof(LG_RendererRect));
this->resizeWindow = true;
}
bool opengl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data)
{
struct Inst * this = (struct Inst *)opaque;
if (!this)
return false;
LG_LOCK(this->mouseLock);
this->mouseCursor = cursor;
this->mouseWidth = width;
this->mouseHeight = height;
this->mousePitch = pitch;
const size_t size = height * pitch;
if (size > this->mouseDataSize)
{
if (this->mouseData)
free(this->mouseData);
this->mouseData = (uint8_t *)malloc(size);
this->mouseDataSize = size;
}
memcpy(this->mouseData, data, size);
this->newShape = true;
LG_UNLOCK(this->mouseLock);
return true;
}
bool opengl_on_mouse_event(void * opaque, const bool visible, const int x, const int y)
{
struct Inst * this = (struct Inst *)opaque;
if (!this)
return false;
if (this->mousePos.x == x && this->mousePos.y == y && this->mouseVisible == visible)
return true;
this->mouseVisible = visible;
this->mousePos.x = x;
this->mousePos.y = y;
this->mouseUpdate = true;
return false;
}
bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const uint8_t * data)
{
struct Inst * this = (struct Inst *)opaque;
if (!this)
{
DEBUG_ERROR("Invalid opaque pointer");
return false;
}
LG_LOCK(this->formatLock);
if (this->reconfigure)
{
LG_UNLOCK(this->formatLock);
return true;
}
if (!this->configured || memcmp(&this->format, &format, sizeof(LG_RendererFormat)) != 0)
{
memcpy(&this->format, &format, sizeof(LG_RendererFormat));
this->reconfigure = true;
LG_UNLOCK(this->formatLock);
return true;
}
LG_UNLOCK(this->formatLock);
// lock, perform the update, then unlock
LG_LOCK(this->syncLock);
memcpySSE(this->texPixels[this->wTexIndex], data, this->texSize);
this->frameUpdate = true;
LG_UNLOCK(this->syncLock);
++this->frameCount;
return true;
}
bool opengl_render(void * opaque, SDL_Window * window)
{
struct Inst * this = (struct Inst *)opaque;
if (!this)
return false;
if (!configure(this, window))
return true;
if (this->resizeWindow)
{
// setup the projection matrix
glViewport(0, 0, this->window.x, this->window.y);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, this->window.x, this->window.y, 0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(this->destRect.x, this->destRect.y, 0.0f);
glScalef(
(float)this->destRect.w / (float)this->format.width,
(float)this->destRect.h / (float)this->format.height,
1.0f
);
// update the scissor rect to prevent drawing outside of the frame
glScissor(
this->destRect.x,
this->destRect.y,
this->destRect.w,
this->destRect.h
);
this->resizeWindow = false;
}
if (this->params.showFPS && this->renderTime > 1e9)
{
char str[128];
const float avgFPS = 1000.0f / (((float)this->renderTime / this->frameCount ) / 1e6f);
const float renderFPS = 1000.0f / (((float)this->renderTime / this->renderCount) / 1e6f);
snprintf(str, sizeof(str), "UPS: %8.4f, FPS: %8.4f", avgFPS, renderFPS);
SDL_Color color = {0xff, 0xff, 0xff};
SDL_Surface *textSurface = NULL;
if (!(textSurface = TTF_RenderText_Blended(this->params.font, str, color)))
{
DEBUG_ERROR("Failed to render text");
LG_UNLOCK(this->formatLock);
return false;
}
glBindTexture(GL_TEXTURE_2D , this->textures[FPS_TEXTURE]);
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
glPixelStorei(GL_UNPACK_ROW_LENGTH, textSurface->w );
glTexImage2D(
GL_TEXTURE_2D,
0,
textSurface->format->BytesPerPixel,
textSurface->w,
textSurface->h,
0,
GL_BGRA,
GL_UNSIGNED_BYTE,
textSurface->pixels
);
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_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
this->fpsRect.x = 5;
this->fpsRect.y = 5;
this->fpsRect.w = textSurface->w;
this->fpsRect.h = textSurface->h;
SDL_FreeSurface(textSurface);
this->renderTime = 0;
this->frameCount = 0;
this->renderCount = 0;
this->fpsTexture = true;
glNewList(this->fpsList, GL_COMPILE);
glPushMatrix();
glLoadIdentity();
glEnable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glColor4f(0.0f, 0.0f, 1.0f, 0.5f);
glBegin(GL_TRIANGLE_STRIP);
glVertex2i(this->fpsRect.x , this->fpsRect.y );
glVertex2i(this->fpsRect.x + this->fpsRect.w, this->fpsRect.y );
glVertex2i(this->fpsRect.x , this->fpsRect.y + this->fpsRect.h);
glVertex2i(this->fpsRect.x + this->fpsRect.w, this->fpsRect.y + this->fpsRect.h);
glEnd();
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, this->textures[FPS_TEXTURE]);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f , 0.0f); glVertex2i(this->fpsRect.x , this->fpsRect.y );
glTexCoord2f(1.0f , 0.0f); glVertex2i(this->fpsRect.x + this->fpsRect.w, this->fpsRect.y );
glTexCoord2f(0.0f , 1.0f); glVertex2i(this->fpsRect.x , this->fpsRect.y + this->fpsRect.h);
glTexCoord2f(1.0f, 1.0f); glVertex2i(this->fpsRect.x + this->fpsRect.w, this->fpsRect.y + this->fpsRect.h);
glEnd();
glDisable(GL_BLEND);
glPopMatrix();
glEndList();
}
bool frameUpdate;
if (!draw_frame(this, &frameUpdate))
return false;
bool newShape;
update_mouse_shape(this, &newShape);
glDisable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
glCallList(this->texList + this->texIndex);
draw_mouse(this);
if (this->fpsTexture)
glCallList(this->fpsList);
if (this->opt.preventBuffer)
{
unsigned int before, after;
glXGetVideoSyncSGI(&before);
SDL_GL_SwapWindow(window);
// wait for the swap to happen to ensure we dont buffer frames //
glXGetVideoSyncSGI(&after);
if (before == after)
glXWaitVideoSyncSGI(1, 0, &before);
}
else
SDL_GL_SwapWindow(window);
const uint64_t t = nanotime();
this->renderTime += t - this->lastFrameTime;
this->lastFrameTime = t;
++this->renderCount;
this->mouseUpdate = false;
this->lastMouseDraw = t;
return true;
}
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 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: enabled]",
.validator = LG_RendererValidatorBool,
.handler = handle_opt_prevent_buffer
}
};
const LG_Renderer LGR_OpenGL =
{
.get_name = opengl_get_name,
.options = opengl_options,
.option_count = LGR_OPTION_COUNT(opengl_options),
.create = opengl_create,
.initialize = opengl_initialize,
.deinitialize = opengl_deinitialize,
.on_resize = opengl_on_resize,
.on_mouse_shape = opengl_on_mouse_shape,
.on_mouse_event = opengl_on_mouse_event,
.on_frame_event = opengl_on_frame_event,
.render = opengl_render
};
static bool _check_gl_error(unsigned int line, const char * name)
{
GLenum error = glGetError();
if (error == GL_NO_ERROR)
return false;
const GLubyte * errStr = gluErrorString(error);
DEBUG_ERROR("%d: %s = %d (%s)", line, name, error, errStr);
return true;
}
static bool configure(struct Inst * this, SDL_Window *window)
{
LG_LOCK(this->formatLock);
if (!this->reconfigure)
{
LG_UNLOCK(this->formatLock);
return this->configured;
}
if (this->configured)
deconfigure(this);
this->glContext = SDL_GL_CreateContext(window);
if (!this->glContext)
{
DEBUG_ERROR("Failed to create the OpenGL context");
LG_UNLOCK(this->formatLock);
return false;
}
if (!this->doneInfo)
{
DEBUG_INFO("Vendor : %s", glGetString(GL_VENDOR ));
DEBUG_INFO("Renderer: %s", glGetString(GL_RENDERER));
DEBUG_INFO("Version : %s", glGetString(GL_VERSION ));
this->doneInfo = true;
}
SDL_GL_SetSwapInterval(this->opt.vsync ? 1 : 0);
// check if the GPU supports GL_ARB_buffer_storage first
// there is no advantage to this renderer if it is not present.
const GLubyte * extensions = glGetString(GL_EXTENSIONS);
if (!gluCheckExtension((const GLubyte *)"GL_ARB_buffer_storage", extensions))
{
DEBUG_INFO("The GPU doesn't support GL_ARB_buffer_storage");
LG_UNLOCK(this->formatLock);
return false;
}
// assume 24 and 32 bit formats are RGB and RGBA
switch(this->format.bpp)
{
case 24:
this->intFormat = GL_RGB8;
this->vboFormat = GL_BGR;
break;
case 32:
this->intFormat = GL_RGBA8;
this->vboFormat = GL_BGRA;
break;
default:
DEBUG_INFO("%d bpp not supported", this->format.bpp);
LG_UNLOCK(this->formatLock);
return false;
}
// calculate the texture size in bytes
this->texSize = this->format.height * this->format.pitch;
// generate lists for drawing
this->texList = glGenLists(BUFFER_COUNT);
this->fpsList = glGenLists(1);
this->mouseList = glGenLists(1);
// generate the pixel unpack buffers
glGenBuffers(1, this->vboID);
if (check_gl_error("glGenBuffers"))
{
LG_UNLOCK(this->formatLock);
return false;
}
this->hasBuffers = true;
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[0]);
if (check_gl_error("glBindBuffer"))
{
LG_UNLOCK(this->formatLock);
return false;
}
glBufferStorage(
GL_PIXEL_UNPACK_BUFFER,
this->texSize * BUFFER_COUNT,
NULL,
GL_MAP_WRITE_BIT |
GL_MAP_PERSISTENT_BIT
);
if (check_gl_error("glBufferStorage"))
{
LG_UNLOCK(this->formatLock);
return false;
}
this->texPixels[0] = glMapBufferRange(
GL_PIXEL_UNPACK_BUFFER,
0,
this->texSize * BUFFER_COUNT,
GL_MAP_WRITE_BIT |
GL_MAP_PERSISTENT_BIT |
GL_MAP_FLUSH_EXPLICIT_BIT
);
if (check_gl_error("glMapBufferRange"))
{
LG_UNLOCK(this->formatLock);
return false;
}
for(int i = 1; i < BUFFER_COUNT; ++i)
this->texPixels[i] = this->texPixels[i-1] + this->texSize;
// create the textures
glGenTextures(TEXTURE_COUNT, this->textures);
if (check_gl_error("glGenTextures"))
{
LG_UNLOCK(this->formatLock);
return false;
}
this->hasTextures = true;
// create the frame texture
glBindTexture(GL_TEXTURE_2D, this->textures[FRAME_TEXTURE]);
if (check_gl_error("glBindTexture"))
{
LG_UNLOCK(this->formatLock);
return false;
}
glTexImage2D(
GL_TEXTURE_2D,
0,
this->intFormat,
this->format.width,
this->format.height * BUFFER_COUNT,
0,
this->vboFormat,
GL_UNSIGNED_BYTE,
(void*)0
);
if (check_gl_error("glTexImage2D"))
{
LG_UNLOCK(this->formatLock);
return false;
}
// 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);
for (int i = 0; i < BUFFER_COUNT; ++i)
{
const float ts = (1.0f / BUFFER_COUNT) * i;
const float te = (1.0f / BUFFER_COUNT) + ts;
// create the display lists
glNewList(this->texList + i, GL_COMPILE);
glBindTexture(GL_TEXTURE_2D, this->textures[FRAME_TEXTURE]);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f, ts); glVertex2i(0 , 0 );
glTexCoord2f(1.0f, ts); glVertex2i(this->format.width, 0 );
glTexCoord2f(0.0f, te); glVertex2i(0 , this->format.height);
glTexCoord2f(1.0f, te); glVertex2i(this->format.width, this->format.height);
glEnd();
glEndList();
}
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glEnable(GL_TEXTURE_2D);
glEnable(GL_COLOR_MATERIAL);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
glEnable(GL_SCISSOR_TEST);
this->resizeWindow = true;
this->drawStart = nanotime();
glXGetVideoSyncSGI(&this->gpuFrameCount);
this->configured = true;
this->reconfigure = false;
LG_UNLOCK(this->formatLock);
return true;
}
static void deconfigure(struct Inst * this)
{
if (!this->configured)
return;
if (this->hasTextures)
{
glDeleteTextures(TEXTURE_COUNT, this->textures);
this->hasTextures = false;
}
if (this->hasBuffers)
{
glDeleteBuffers(1, this->vboID);
this->hasBuffers = false;
}
if (this->glContext)
{
SDL_GL_DeleteContext(this->glContext);
this->glContext = NULL;
}
this->configured = false;
}
static void update_mouse_shape(struct Inst * this, bool * newShape)
{
LG_LOCK(this->mouseLock);
*newShape = this->newShape;
if (!this->newShape)
{
LG_UNLOCK(this->mouseLock);
return;
}
const LG_RendererCursor cursor = this->mouseCursor;
const int width = this->mouseWidth;
const int height = this->mouseHeight;
const int pitch = this->mousePitch;
const uint8_t * data = this->mouseData;
this->mouseType = cursor;
switch(cursor)
{
case LG_CURSOR_COLOR:
{
glBindTexture(GL_TEXTURE_2D, this->textures[MOUSE_TEXTURE]);
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
glTexImage2D
(
GL_TEXTURE_2D,
0 ,
GL_RGBA,
width ,
height ,
0 ,
GL_BGRA, // windows cursors are in BGRA format
GL_UNSIGNED_BYTE,
data
);
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_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
this->mousePos.w = width;
this->mousePos.h = height;
glNewList(this->mouseList, GL_COMPILE);
glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, this->textures[MOUSE_TEXTURE]);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f, 0.0f); glVertex2i(0 , 0 );
glTexCoord2f(1.0f, 0.0f); glVertex2i(width, 0 );
glTexCoord2f(0.0f, 1.0f); glVertex2i(0 , height);
glTexCoord2f(1.0f, 1.0f); glVertex2i(width, height);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_BLEND);
glEndList();
break;
}
case LG_CURSOR_MONOCHROME:
{
const int hheight = height / 2;
uint32_t d[width * height];
for(int y = 0; y < hheight; ++y)
for(int x = 0; x < width; ++x)
{
const uint8_t * srcAnd = data + (pitch * y) + (x / 8);
const uint8_t * srcXor = srcAnd + pitch * hheight;
const uint8_t mask = 0x80 >> (x % 8);
const uint32_t andMask = (*srcAnd & mask) ? 0xFFFFFFFF : 0xFF000000;
const uint32_t xorMask = (*srcXor & mask) ? 0x00FFFFFF : 0x00000000;
d[y * width + x ] = andMask;
d[y * width + x + width * hheight] = xorMask;
}
glBindTexture(GL_TEXTURE_2D, this->textures[MOUSE_TEXTURE]);
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
glTexImage2D
(
GL_TEXTURE_2D,
0 ,
GL_RGBA,
width ,
height ,
0 ,
GL_RGBA,
GL_UNSIGNED_BYTE,
d
);
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_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
this->mousePos.w = width;
this->mousePos.h = hheight;
glNewList(this->mouseList, GL_COMPILE);
glEnable(GL_COLOR_LOGIC_OP);
glBindTexture(GL_TEXTURE_2D, this->textures[MOUSE_TEXTURE]);
glLogicOp(GL_AND);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f, 0.0f); glVertex2i(0 , 0 );
glTexCoord2f(1.0f, 0.0f); glVertex2i(width, 0 );
glTexCoord2f(0.0f, 0.5f); glVertex2i(0 , hheight);
glTexCoord2f(1.0f, 0.5f); glVertex2i(width, hheight);
glEnd();
glLogicOp(GL_XOR);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f, 0.5f); glVertex2i(0 , 0 );
glTexCoord2f(1.0f, 0.5f); glVertex2i(width, 0 );
glTexCoord2f(0.0f, 1.0f); glVertex2i(0 , hheight);
glTexCoord2f(1.0f, 1.0f); glVertex2i(width, hheight);
glEnd();
glDisable(GL_COLOR_LOGIC_OP);
glEndList();
break;
}
case LG_CURSOR_MASKED_COLOR:
{
//TODO
break;
}
}
this->mouseUpdate = true;
LG_UNLOCK(this->mouseLock);
}
static bool draw_frame(struct Inst * this, bool * frameUpdate)
{
LG_LOCK(this->syncLock);
*frameUpdate = this->frameUpdate;
if (!this->frameUpdate)
{
LG_UNLOCK(this->syncLock);
return true;
}
this->texIndex = this->wTexIndex;
if (++this->wTexIndex == BUFFER_COUNT)
this->wTexIndex = 0;
this->frameUpdate = false;
LG_UNLOCK(this->syncLock);
LG_LOCK(this->formatLock);
// bind the texture and update it
glBindTexture(GL_TEXTURE_2D , this->textures[FRAME_TEXTURE]);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[0] );
glPixelStorei(GL_UNPACK_ALIGNMENT , 4 );
glPixelStorei(GL_UNPACK_ROW_LENGTH , this->format.stride );
// copy the buffer to the texture
glFlushMappedBufferRange(
GL_PIXEL_UNPACK_BUFFER,
this->texSize * this->texIndex,
this->texSize
);
// update the texture
glTexSubImage2D(
GL_TEXTURE_2D,
0,
0,
this->texIndex * this->format.height,
this->format.width ,
this->format.height,
this->vboFormat,
GL_UNSIGNED_BYTE,
(void*)(this->texIndex * this->texSize)
);
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
);
// 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));
if (mipmap)
{
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
glBindTexture(GL_TEXTURE_2D, 0);
LG_UNLOCK(this->formatLock);
return true;
}
static void draw_mouse(struct Inst * this)
{
if (!this->mouseVisible)
return;
glPushMatrix();
glTranslatef(this->mousePos.x, this->mousePos.y, 0.0f);
glCallList(this->mouseList);
glPopMatrix();
}

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,900 +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 "debug.h"
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <sys/types.h>
#include <sys/socket.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"
#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;
};
struct SpiceKeyboard
{
uint32_t modifiers;
};
#define SPICE_MOUSE_QUEUE_SIZE 32
struct SpiceMouse
{
uint32_t buttonState;
int sentCount;
SpiceMsgcMouseMotion queue[SPICE_MOUSE_QUEUE_SIZE];
int rpos, wpos;
int queueLen;
};
struct Spice
{
char password[32];
struct sockaddr_in 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));
memset(&spice.addr, 0, sizeof(struct sockaddr_in));
inet_pton(AF_INET, host, &spice.addr.sin_addr);
spice.addr.sin_family = AF_INET;
spice.addr.sin_port = htons(port);
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);
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_on_inputs_channel_read())
{
if (!spice_process_ack(&spice.scInputs))
{
DEBUG_ERROR("failed to process ack on inputs channel");
return false;
}
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;
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;
return false;
}
if (++spice.mouse.rpos == SPICE_MOUSE_QUEUE_SIZE)
spice.mouse.rpos = 0;
++sent;
--spice.mouse.queueLen;
}
spice.mouse.sentCount = sent;
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;
channel->socket = socket(AF_INET, SOCK_STREAM, 0);
if (channel->socket == -1)
return false;
int flag = 1;
setsockopt(channel->socket, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int));
if (connect(channel->socket, (const struct sockaddr *)&spice.addr, sizeof(spice.addr)) == -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));
BIO *bioKey = BIO_new(BIO_s_mem());
if (!bioKey)
{
DEBUG_ERROR("failed to allocate bioKey");
spice_disconnect_channel(channel);
return false;
}
BIO_write(bioKey, reply.pub_key, SPICE_TICKET_PUBKEY_BYTES);
EVP_PKEY *rsaKey = d2i_PUBKEY_bio(bioKey, NULL);
RSA *rsa = EVP_PKEY_get1_RSA(rsaKey);
char enc[RSA_size(rsa)];
if (RSA_public_encrypt(
strlen(spice.password) + 1,
(uint8_t*)spice.password,
(uint8_t*)enc,
rsa,
RSA_PKCS1_OAEP_PADDING
) <= 0)
{
DEBUG_ERROR("rsa public encrypt failed");
spice_disconnect_channel(channel);
EVP_PKEY_free(rsaKey);
BIO_free(bioKey);
return false;
}
ssize_t rsaSize = RSA_size(rsa);
EVP_PKEY_free(rsaKey);
BIO_free(bioKey);
if (!spice_write(channel, enc, rsaSize))
{
DEBUG_ERROR("failed to write encrypted data");
spice_disconnect_channel(channel);
return false;
}
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)
close(channel->socket);
channel->connected = false;
}
// ============================================================================
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)
{
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");
return false;
}
if (spice_write(channel, buffer, size) != size)
{
DEBUG_ERROR("failed to write message body");
return false;
}
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;
}
if (spice.mouse.sentCount == 4)
{
if (spice.mouse.queueLen == SPICE_MOUSE_QUEUE_SIZE)
{
DEBUG_ERROR("mouse motion ringbuffer full!");
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;
return true;
}
SpiceMsgcMouseMotion msg;
msg.x = x;
msg.y = y;
msg.button_state = spice.mouse.buttonState;
++spice.mouse.sentCount;
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));
}

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;
}

24
client/src/config.h Normal file
View File

@@ -0,0 +1,24 @@
/*
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>
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)

160
client/src/ll.c Normal file
View File

@@ -0,0 +1,160 @@
/*
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 "ll.h"
#include "common/locking.h"
#include <stdlib.h>
#include <assert.h>
struct ll_item
{
void * data;
struct ll_item * next;
};
struct ll
{
struct ll_item * head;
struct ll_item * tail;
struct ll_item * pos;
unsigned int count;
LG_Lock lock;
};
struct ll * ll_new()
{
struct ll * list = malloc(sizeof(struct ll));
list->head = NULL;
list->tail = NULL;
list->pos = NULL;
LG_LOCK_INIT(list->lock);
return list;
}
void ll_free(struct ll * list)
{
// never free a list with items in it!
assert(!list->head);
LG_LOCK_FREE(list->lock);
free(list);
}
void ll_push(struct ll * list, void * data)
{
struct ll_item * item = malloc(sizeof(struct ll_item));
item->data = data;
item->next = NULL;
LG_LOCK(list->lock);
if (!list->head)
{
list->head = item;
list->tail = item;
LG_UNLOCK(list->lock);
return;
}
++list->count;
list->tail->next = item;
list->tail = item;
LG_UNLOCK(list->lock);
}
bool ll_shift(struct ll * list, void ** data)
{
LG_LOCK(list->lock);
if (!list->head)
{
LG_UNLOCK(list->lock);
return false;
}
--list->count;
struct ll_item * item = list->head;
list->head = item->next;
list->pos = NULL;
LG_UNLOCK(list->lock);
if (data)
*data = item->data;
free(item);
return true;
}
bool ll_peek_head(struct ll * list, void ** data)
{
LG_LOCK(list->lock);
if (!list->head)
{
LG_UNLOCK(list->lock);
return false;
}
*data = list->head->data;
LG_UNLOCK(list->lock);
return true;
}
unsigned int ll_count(struct ll * list)
{
return list->count;
}
void ll_reset (struct ll * list)
{
LG_LOCK(list->lock);
list->pos = NULL;
LG_UNLOCK(list->lock);
}
bool ll_walk(struct ll * list, void ** data)
{
LG_LOCK(list->lock);
if (!list->pos)
{
if (!list->head)
{
LG_UNLOCK(list->lock);
return false;
}
list->pos = list->head;
}
else
{
if (!list->pos->next)
{
LG_UNLOCK(list->lock);
return false;
}
list->pos = list->pos->next;
}
*data = list->pos->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;

70
client/src/utils.c Normal file
View File

@@ -0,0 +1,70 @@
/*
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 "utils.h"
#include "common/debug.h"
#include <stdlib.h>
#include <stdio.h>
bool file_get_contents(const char * filename, char ** buffer, size_t * length)
{
FILE * fh = fopen(filename, "r");
if (!fh)
{
DEBUG_ERROR("Failed to open the file: %s", filename);
return false;
}
if (fseek(fh, 0, SEEK_END) != 0)
{
DEBUG_ERROR("Failed to seek");
fclose(fh);
return false;
}
long fsize = ftell(fh);
if (fseek(fh, 0, SEEK_SET) != 0)
{
DEBUG_ERROR("Failed to seek");
fclose(fh);
return false;
}
*buffer = malloc(fsize + 1);
if (!*buffer)
{
DEBUG_ERROR("Failed to allocate buffer of %lu bytes", fsize + 1);
fclose(fh);
return false;
}
if (fread(*buffer, 1, fsize, fh) != fsize)
{
DEBUG_ERROR("Failed to read the entire file");
fclose(fh);
free(*buffer);
return false;
}
fclose(fh);
buffer[fsize] = 0;
*length = fsize;
return true;
}

View File

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

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,91 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR)
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 <stdint.h>
#define KVMFR_HEADER_MAGIC "[[KVMFR]]"
#define KVMFR_HEADER_VERSION 5
typedef enum FrameType
{
FRAME_TYPE_INVALID ,
FRAME_TYPE_ARGB , // ABGR interleaved: A,R,G,B 32bpp
FRAME_TYPE_RGB , // RGB interleaved : R,G,B 24bpp
FRAME_TYPE_MAX , // sentinel value
}
FrameType;
typedef enum CursorType
{
CURSOR_TYPE_COLOR ,
CURSOR_TYPE_MONOCHROME ,
CURSOR_TYPE_MASKED_COLOR
}
CursorType;
#define KVMFR_CURSOR_FLAG_VISIBLE 1 // cursor is visible
#define KVMFR_CURSOR_FLAG_SHAPE 2 // shape updated
#define KVMFR_CURSOR_FLAG_POS 4 // position updated
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
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;
typedef struct KVMFRFrame
{
FrameType type; // the frame data type
uint32_t width; // the width
uint32_t height; // the height
uint32_t stride; // the row stride (zero if cursor data)
uint32_t pitch; // the row pitch (stride in bytes)
uint64_t dataPos; // offset to the frame
}
KVMFRFrame;
#define KVMFR_HEADER_FLAG_FRAME 1 // frame update available
#define KVMFR_HEADER_FLAG_CURSOR 2 // cursor update available
#define KVMFR_HEADER_FLAG_RESTART 4 // restart signal from client
#define KVMFR_HEADER_FLAG_READY 8 // ready signal from client
typedef struct KVMFRDetail
{
KVMFRFrame frame; // the frame information
KVMFRCursor cursor; // the cursor information
}
KVMFRDetail;
typedef struct KVMFRHeader
{
char magic[sizeof(KVMFR_HEADER_MAGIC)];
uint32_t version; // version of this structure
uint8_t flags; // KVMFR_HEADER_FLAGS
KVMFRDetail detail; // details
}
KVMFRHeader;

View File

@@ -0,0 +1,84 @@
/*
Looking Glass - KVM FrameRelay (KVMFR)
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 <stdint.h>
#define LGMP_Q_POINTER 1
#define LGMP_Q_FRAME 2
typedef enum FrameType
{
FRAME_TYPE_INVALID ,
FRAME_TYPE_BGRA , // BGRA interleaved: B,G,R,A 32bpp
FRAME_TYPE_RGBA , // RGBA interleaved: R,G,B,A 32bpp
FRAME_TYPE_RGBA10 , // RGBA interleaved: R,G,B,A 10,10,10,2 bpp
FRAME_TYPE_YUV420 , // YUV420
FRAME_TYPE_MAX , // sentinel value
}
FrameType;
enum
{
CURSOR_FLAG_POSITION = 0x1,
CURSOR_FLAG_VISIBLE = 0x2,
CURSOR_FLAG_SHAPE = 0x4
};
typedef uint32_t KVMFRCursorFlags;
typedef enum CursorType
{
CURSOR_TYPE_COLOR ,
CURSOR_TYPE_MONOCHROME ,
CURSOR_TYPE_MASKED_COLOR
}
CursorType;
#define KVMFR_MAGIC "KVMFR---"
#define KVMFR_VERSION 3
typedef struct KVMFR
{
char magic[8];
uint32_t version;
char hostver[32];
}
KVMFR;
typedef struct KVMFRCursor
{
int16_t x, y; // cursor x & y position
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
}
KVMFRCursor;
typedef struct KVMFRFrame
{
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;

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__)

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,24 +17,20 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stddef.h>
#include <stdint.h>
#pragma once
#include <stdbool.h>
bool ivshmem_connect(const char * unix_socket);
void ivshmem_disconnect();
bool ivshmem_process();
uint16_t ivshmem_get_id();
void * ivshmem_get_map();
size_t ivshmem_get_map_size();
enum IVSHMEMWaitResult
struct IVSHMEM
{
IVSHMEM_WAIT_RESULT_OK,
IVSHMEM_WAIT_RESULT_TIMEOUT,
IVSHMEM_WAIT_RESULT_ERROR
unsigned int size;
void * mem;
// internal use
void * opaque;
};
enum IVSHMEMWaitResult ivshmem_wait_irq(uint16_t vector, unsigned int timeout);
bool ivshmem_kick_irq(uint16_t clientID, uint16_t vector);
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

@@ -0,0 +1,135 @@
/*
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 <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <tmmintrin.h>
#include <immintrin.h>
#include "debug.h"
#if defined(NATIVE_MEMCPY)
#define memcpySSE memcpy
#elif defined(_MSC_VER)
extern "C" void * memcpySSE(void *dst, const void * src, size_t length);
#elif (defined(__GNUC__) || defined(__GNUG__)) && defined(__i386__)
inline static void * memcpySSE(void *dst, const void * src, size_t length)
{
if (length == 0 || dst == src)
return;
// copies under 1MB are faster with the inlined memcpy
// tell the dev to use that instead
if (length < 1048576)
{
static bool smallBufferWarn = false;
if (!smallBufferWarn)
{
DEBUG_WARN("Do not use memcpySSE for copies under 1MB in size!");
smallBufferWarn = true;
}
memcpy(dst, src, length);
return;
}
const void * end = dst + (length & ~0x7F);
const size_t off = (7 - ((length & 0x7F) >> 4)) * 9;
__asm__ __volatile__ (
"cmp %[dst],%[end] \n\t"
"je Remain_%= \n\t"
// perform SIMD block copy
"loop_%=: \n\t"
"movaps 0x00(%[src]),%%xmm0 \n\t"
"movaps 0x10(%[src]),%%xmm1 \n\t"
"movaps 0x20(%[src]),%%xmm2 \n\t"
"movaps 0x30(%[src]),%%xmm3 \n\t"
"movaps 0x40(%[src]),%%xmm4 \n\t"
"movaps 0x50(%[src]),%%xmm5 \n\t"
"movaps 0x60(%[src]),%%xmm6 \n\t"
"movaps 0x70(%[src]),%%xmm7 \n\t"
"movntdq %%xmm0 ,0x00(%[dst]) \n\t"
"movntdq %%xmm1 ,0x10(%[dst]) \n\t"
"movntdq %%xmm2 ,0x20(%[dst]) \n\t"
"movntdq %%xmm3 ,0x30(%[dst]) \n\t"
"movntdq %%xmm4 ,0x40(%[dst]) \n\t"
"movntdq %%xmm5 ,0x50(%[dst]) \n\t"
"movntdq %%xmm6 ,0x60(%[dst]) \n\t"
"movntdq %%xmm7 ,0x70(%[dst]) \n\t"
"add $0x80,%[dst] \n\t"
"add $0x80,%[src] \n\t"
"cmp %[dst],%[end] \n\t"
"jne loop_%= \n\t"
"Remain_%=: \n\t"
// copy any remaining 16 byte blocks
"call GetPC_%=\n\t"
"Offset_%=:\n\t"
"add $(BlockTable_%= - Offset_%=), %%eax \n\t"
"add %[off],%%eax \n\t"
"jmp *%%eax \n\t"
"GetPC_%=:\n\t"
"mov (%%esp), %%eax \n\t"
"ret \n\t"
"BlockTable_%=:\n\t"
"movaps 0x60(%[src]),%%xmm6 \n\t"
"movntdq %%xmm6 ,0x60(%[dst]) \n\t"
"movaps 0x50(%[src]),%%xmm5 \n\t"
"movntdq %%xmm5 ,0x50(%[dst]) \n\t"
"movaps 0x40(%[src]),%%xmm4 \n\t"
"movntdq %%xmm4 ,0x40(%[dst]) \n\t"
"movaps 0x30(%[src]),%%xmm3 \n\t"
"movntdq %%xmm3 ,0x30(%[dst]) \n\t"
"movaps 0x20(%[src]),%%xmm2 \n\t"
"movntdq %%xmm2 ,0x20(%[dst]) \n\t"
"movaps 0x10(%[src]),%%xmm1 \n\t"
"movntdq %%xmm1 ,0x10(%[dst]) \n\t"
"movaps 0x00(%[src]),%%xmm0 \n\t"
"movntdq %%xmm0 ,0x00(%[dst]) \n\t"
"nop\n\t"
"nop\n\t"
: [dst]"+r" (dst),
[src]"+r" (src)
: [off]"r" (off),
[end]"r" (end)
: "eax",
"xmm0",
"xmm1",
"xmm2",
"xmm3",
"xmm4",
"xmm5",
"xmm6",
"xmm7",
"memory"
);
//copy any remaining bytes
memcpy(dst, src, length & 0xF);
}
#else
#define memcpySSE memcpy
#endif

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