GUACAMOLE-261: Add native SPICE protocol support (libguac-client-spice)#688
Open
ciroiriarte wants to merge 36 commits into
Open
GUACAMOLE-261: Add native SPICE protocol support (libguac-client-spice)#688ciroiriarte wants to merge 36 commits into
ciroiriarte wants to merge 36 commits into
Conversation
Contributor
|
@ciroiriarte Just at a glance you have 39 commits in this PR, and most of them are not tagged with the |
Author
|
I cherry picked some commits associated to GUACAMOLE-288 as pre-req/enabler, should I label those as GUACAMOLE-261 too or should I use GUACAMOLE-288 ? |
Contributor
|
@ciroiriarte All commits that go in as part of this particular PR should be tagged with
|
Introduces a new in-tree protocol module under src/protocols/spice/ which
allows guacd to proxy SPICE servers (as used by QEMU/KVM/libvirt), built as
libguac-client-spice.so with the standard guac_client_init entry point.
The module is built against spice-gtk (spice-client-glib-2.0) and mirrors the
structure of the existing VNC module. Because spice-gtk is event-driven, the
client thread runs a private GMainContext/GMainLoop and dispatches channels via
the SpiceSession "channel-new" signal.
Implemented:
* Build integration: --with-spice, PKG_CHECK_MODULES for
spice-client-glib-2.0 (and optional libphodav for folder sharing),
ENABLE_SPICE/ENABLE_SPICE_WEBDAV conditionals, wired into the root
configure.ac and Makefile.am with a configure-summary entry.
* Display channel -> guac_display (primary surface + damage rects).
* Keyboard and absolute/relative mouse via SpiceInputsChannel, with an
X11-keysym to PC-scancode keymap.
* Cursor channel -> guac cursor layer.
* Clipboard (SPICE guest agent) <-> guac_common_clipboard.
* Audio playback -> guac_audio_stream.
* Plaintext and TLS transport (CA file, cert-subject, ignore-cert).
* Folder sharing via the SPICE WebDAV channel (shared-dir), gated on
libphodav.
* SFTP via common-ssh, screen recording, Wake-on-LAN, and runtime argv
password/username updates.
USB redirection and smartcard passthrough are intentionally left unconnected
(no physical devices on a headless proxy); multi-monitor renders the primary
monitor only.
The merge of the patch branch left src/terminal/terminal.c uncompilable
(broke main as well, independent of the SPICE work):
* A duplicate, truncated modifier-tracking block (using the
GUAC_TERMINAL_KEY_* aliases) preceded the canonical GUAC_KEYSYM_*
block; its final "else if (... SHIFT ...)" had no body, triggering
-Werror=dangling-else.
* A stray '}' inside the Ctrl+letter handling closed the function early,
cascading into "expected identifier before 'return'" at end of function.
* Four helper functions (__guac_terminal_is_function_keysym,
__guac_terminal_is_editing_keysym, __guac_terminal_send_modified_editing,
__guac_terminal_send_modified_function) were defined but never wired in,
failing -Werror=unused-function.
Removes the duplicate block and the stray brace, and wires the editing/
function modified-key helpers in alongside the existing arrow-key handling
(mirroring __guac_terminal_send_modified_arrow), so modified editing and
function keys emit their xterm-style CSI sequences.
Two changes to the SPICE module:
1. Fix the SPICE connection never fully establishing.
The client thread ran its GLib event loop on a private GMainContext
(created with g_main_context_new() and pushed as thread-default).
spice-gtk, however, schedules each channel's connection coroutine on
the *default* main context, so that loop never drove the link: only the
main channel was ever created, the display/inputs/cursor/playback
channels never connected, and the connection rendered a blank frame and
eventually timed out ("User is not responding").
Drive the event loop from the thread's default GMainContext instead
(matching how spicy/virt-viewer use spice-gtk). guacd forks a dedicated
process per connection, so the default context is private to the
connection and safe to use. With this, all channels open, the remote
primary surface is received, and display + input work end to end.
2. Add keyboard-layout-aware keysym translation.
Replaces the single static US keysym->scancode table with the keymap
subsystem used by the RDP module: per-layout .keymap files compiled to
_generated_keymaps.c by keymaps/generate.pl, a guac_spice_keyboard that
tracks modifier/lock state and picks the lowest-cost scancode sequence
for each keysym, and dead-key decomposition (decompose.c) so precomposed
accented characters can be typed on layouts that produce them via a dead
key. The layout is selected with the new "server-layout" parameter
(default en-us-qwerty); 20 layouts are included.
generate.pl additionally encodes the "+ext" flag into the 0x100 bit of
the scancode so extended keys (arrows, navigation cluster, right-hand
modifiers, keypad Enter/Divide) are transmitted correctly -- the RDP
generator this was adapted from drops that flag.
Verified end to end against a QEMU/SPICE desktop through the Guacamole web
client: real desktop renders, and "ñ á é í ó ú" type correctly via
es-latam-qwerty (ñ direct, accented vowels via dead-acute decomposition).
Ported from the draft by Nick Couchman in apache#394
(GUACAMOLE-261).
Co-authored-by: Virtually Nick <vnick@apache.org>
…arams
Adds the connection parameters from the upstream draft (GUACAMOLE-261) that
map cleanly onto spice-gtk / the guac_display rendering path:
* proxy - connect to the SPICE server through a proxy
(SpiceSession "proxy" property).
* pubkey - base64 (DER) public key to pin the server's TLS
certificate against (SpiceSession "pubkey" +
SPICE_SESSION_VERIFY_PUBKEY).
* swap-red-blue - swap the red/blue channels when copying the remote
framebuffer, for servers reporting BGR rather than RGB.
The remaining draft parameters are intentionally not ported: "clipboard-
encoding" (SPICE vd_agent clipboard is always UTF-8), "encodings"/"cursor"
(tied to the deprecated guac_common_surface rendering the draft used, not the
guac_display path used here), and "autoretry" (guacd connections are
single-shot by design, matching the VNC/RDP modules).
Verified: default connections and swap-red-blue=true both render correctly
against a QEMU/SPICE desktop.
…annel
Forwards audio from the connected Guacamole user to the SPICE server,
mirroring the playback path in reverse:
* The SPICE record channel is connected (gated on the new
"enable-audio-input" parameter), with record-start/record-stop signal
handlers that ack the owner's audio input stream so the client starts or
stops sending audio when the guest opens or closes its capture device.
* The user's audio handler parses the offered audio/L16 mimetype and wires
a blob handler that forwards received PCM data to the record channel via
spice_record_channel_send_data().
* "enable-audio" is negotiated when either playback or input is requested,
since spice-gtk's single property governs both audio channels.
Verified against a QEMU/SPICE desktop with an intel-hda duplex codec:
capturing audio in the guest (arecord) fires the record-start handler, and
the record/playback channels connect as expected.
Ported from the draft by Nick Couchman in apache#394
(GUACAMOLE-261).
Co-authored-by: Virtually Nick <vnick@apache.org>
Exposes a host-side directory to the connected Guacamole user as a file
browser (upload/download/directory listing) and to the SPICE guest via the
WebDAV shared-folder channel, ported from the upstream draft (GUACAMOLE-261)
and modeled on the existing SFTP/RDP-drive filesystem.
* channels/file.c/.h - guac_spice_folder: a local-directory filesystem
exposed as a Guacamole filesystem object, plus an
auto-served "Download/" subfolder watched via
inotify.
* channels/file-ls.c - directory listing (stream-index JSON).
* channels/file-download.c - download/get handler + the Download/ monitor.
* channels/file-upload.c - upload/put handler.
New parameters: file-transfer, file-directory, file-transfer-create-folder,
file-transfer-ro, disable-download, disable-upload. When file-transfer is set,
spice.c sets the spice-gtk "shared-dir"/"share-dir-ro" properties (taking
precedence over the basic enable-drive path), allocates the folder, and
exposes it to the connection owner; client.c frees it on disconnect; user.c
wires the drag-and-drop upload handler.
Two bugs in the ported draft were fixed during integration:
* file-upload.c generated upload paths with a leading Windows-style '\'
(RDP heritage), which guac_spice_folder_normalize_path() rejects; use '/'.
* file.c created the Download/ folder by mkdir()'ing the wrong path (the
shared root rather than .../Download) into an undersized strdup'd buffer
(a heap overflow via guac_strlcat); use a correctly-sized buffer and the
Download path.
Verified end to end against a QEMU/SPICE desktop: directory listing, file
download, and file upload all work through the exposed filesystem object, the
Download/ folder and its inotify monitor initialize cleanly, and SFTP remains
available alongside.
Ported from the draft by Nick Couchman in apache#394
(GUACAMOLE-261).
Co-authored-by: Virtually Nick <vnick@apache.org>
* preferred-compression: new connection parameter mapping "off", "auto-glz",
"auto-lz", "quic", "glz", "lz", or "lz4" to the SpiceImageCompression enum
and setting the session "preferred-compression" property. Unknown values
log a warning and are ignored (the connection still proceeds).
* Audio mute: the playback channel's mute state (notify::mute) is tracked and
received PCM is dropped while the SPICE server reports playback as muted.
The channel's volume is intentionally not re-applied -- the guest's PCM is
already scaled to the reported volume, so re-applying would double-attenuate.
Verified against a QEMU/SPICE desktop: preferred-compression=quic renders, an
unknown value warns and still renders, and the mute-state handler fires. Closes
the low-hanging-fruit items from the SPICE enhancement backlog (#9, #13).
…nel expects The audio input (microphone) path forwarded the connected user's PCM to the SPICE record channel unchanged, even when the user was capturing at a different sample rate or channel count than the rate/channels the record channel negotiated (reported by record-start). That mismatch is sent to the guest mislabeled, producing wrong-pitch/artifacted audio. Record the format requested by record-start, remember the format advertised by the user, and convert inbound audio to the expected rate/channels (linear resampling + simple mono<->stereo mixing) before spice_record_channel_send_data when they differ. Matching formats are forwarded directly as before. Ported from the draft by Nick Couchman in apache#394 (GUACAMOLE-261). Co-authored-by: Virtually Nick <vnick@apache.org>
Add a "disable-audio-opus" connection parameter which, when set, exports SPICE_DISABLE_OPUS=1 before the SPICE session is created, causing spice-gtk to negotiate the legacy CELT audio codec instead of Opus. guacd runs a dedicated process per connection, so the environment variable is scoped to the individual connection. This is the only client-side audio-codec control spice-gtk exposes; it is intended for compatibility with SPICE servers whose Opus support is missing or unreliable, and does not improve audio quality. Choosing raw/lossless playback remains a server-side setting (QEMU -spice playback-compression=off).
Empirically, disabling Opus does not force CELT on modern SPICE servers: QEMU/spice-server dropped CELT 0.5.1 years ago, so the server falls back to raw (lossless) audio when the client stops advertising Opus. Only older servers fall back to CELT. Update the comments to describe this accurately (the option can therefore yield lossless raw audio on a LAN, not merely a legacy compatibility fallback).
…work on shift-lock layouts guac_spice_keyboard_get_cost() estimated the cost of a key definition using the modifier fields (set_modifiers/clear_modifiers vs keyboard->modifiers) for BOTH the lock term and the modifier term, never reading the separate set_locks/ clear_locks fields the keymaps actually carry. On layouts that model Caps Lock as Shift-Lock (e.g. de-de-qwertz), a shifted symbol has two definitions -- one via Shift and one via Caps Lock -- and the mis-costed function selected the Caps-Lock definition, releasing Shift and toggling Caps Lock. On a modern guest (where Caps Lock does not shift the number row) this produced digits instead of symbols, matching the German-keyboard bug reported against the original SPICE draft (GUACAMOLE-261, apache#394). Use def->set_locks/clear_locks against the lock state (keyboard->modifiers, in SPICE_INPUTS_* form) for the lock term, mirroring the RDP keyboard subsystem. Closes #19.
…read spice-gtk is not thread-safe: each channel's outbound messages are dispatched by a coroutine on the GMainContext driven by the SPICE client thread, and invoking channel functions from other threads races with that loop via spice_channel_wakeup(). Keyboard, mouse, and clipboard-grab calls were made directly from Guacamole user/handler threads, which can freeze the session under load -- the crash reported against the original SPICE draft (GUACAMOLE-261, Nick Downer: "narrowed the clipboard grab freeze to spice_channel_wakeup"). Add guac_spice_defer_call(), which schedules a spice-gtk call on the default GMainContext (owned by the loop thread) via g_main_context_invoke_full(), and route the off-thread inputs-channel (key/mouse/lock) and main-channel (clipboard grab) calls through it. The clipboard "selection" and "request" paths already ran inside spice-gtk signal handlers (on the loop thread) and are unchanged. Closes #20.
The disable-display-resize parameter was parsed but never used: there was no user size_handler, so resizing the client window did not resize the guest (only the reverse direction — resizing the Guacamole display to match the SPICE server's surface — was handled). Add guac_spice_user_size_handler, registered for each user unless disable-display-resize is set. It requests that the guest resize its primary display via spice_main_channel_update_display(), marshalled onto the SPICE event-loop thread with guac_spice_defer_call() (spice-gtk channel functions must not be called from user threads). Width is aligned down to a multiple of 8, as SPICE guests generally require. Closes #5.
…t connects the output
…izes reach the guest agent spice-gtk debounces the implicit monitors-config send (update=TRUE), so only the first resize per session reached spice-vdagent. Set the geometry without the implicit send and call spice_main_channel_send_monitor_config() explicitly.
spice_main_channel_send_monitor_config() did not send the config (the first resize stopped working), so revert to the implicit send via update=TRUE, which reliably applies the first resize of a session.
…inistically Dynamic resize was unreliable: the size handler sent a monitors config at a fixed point after connect, but spice-gtk only delivers one when the agent is connected with VD_AGENT_CAP_MONITORS_CONFIG and the display's primary surface exists; sent earlier it is silently dropped. update_display(...,update=TRUE) also only arms a one-second coalescing timer, so successive resizes collapsed or were lost. Queue the requested size and send it only once ready: track agent readiness via the main channel "notify::agent-connected" (checking the monitors-config capability) and display readiness via display-primary-create. guac_spice_resize_try() sends the queued size with an explicit spice_main_channel_send_monitor_config() (update=FALSE) so every resize is delivered, and is re-invoked whenever a readiness condition changes to flush a request queued before the guest was ready. Refs #5.
…t-update notify::agent-connected fires before the guest agent announces its capabilities, so VD_AGENT_CAP_MONITORS_CONFIG was not yet available and the gated resize never sent. Also watch main-agent-update (emitted when capabilities arrive) and re-evaluate readiness there, flushing any queued resize.
send_monitor_config() did not reliably deliver the monitors config to the guest agent (session vdagent received nothing). With the readiness gate now ensuring the agent is connected and monitors-config-capable, use update=TRUE (spice-gtk's short coalescing-timer send), which does reach the agent; real resizes are spaced beyond the timer interval so each is delivered.
The readiness gate (agent connected + VD_AGENT_CAP_MONITORS_CONFIG, detected via main-agent-update) plus explicit spice_main_channel_send_monitor_config() is validated: send_monitor_config returns TRUE and the config is delivered to the guest agent every resize. Remove the temporary diagnostic INFO logging.
…amed regions spice-server chooses the video-stream codec from an ordered list whose default puts MJPEG first, and only reorders it when the client sends a preferred-codec message. guacd never sent one, so streamed video was always MJPEG even when the GStreamer H264/VP9/VP8 encoders/decoders are present. On display-channel open, call spice_display_channel_change_preferred_video_codec_types() with [H264, VP9, VP8, MJPEG] so the server prefers efficient codecs (MJPEG fallback). No-op if the server lacks SPICE_DISPLAY_CAP_PREF_VIDEO_CODEC_TYPE.
…codec param Unconditionally requesting H.264 exposed a crash: libspice-server 0.15.2's GStreamer encoder segfaults when it actually encodes H.264/VP8/VP9, taking down the whole VM (MJPEG never crashes). Requesting a non-MJPEG codec by default is therefore unsafe against buggy servers. Gate the preferred-codec request behind a new "preferred-video-codec" parameter (h264|vp9|vp8|mjpeg), NULL/blank by default so guacd leaves the server's default (MJPEG-first) untouched and never triggers the crash out of the box. When set, send the requested codec first with MJPEG appended as fallback. Opt-in lets deployments with a fixed/newer spice-server use efficient codecs.
The drive-read-only parameter was parsed and logged but never applied — guacd set spice-gtk's shared-dir but not share-dir-ro, so drive-read-only=true was a no-op and the shared folder was always writable. Set share-dir-ro from settings->drive_read_only.
Adds a CUnit test suite (test_spice) for libguac-client-spice, following the existing per-module tests/ convention (RDP/kubernetes). Covers the keymap lookup API: guac_spice_keymap_find() resolution, registry well-formedness, and mapping-array integrity. Wires the tests/ subdir into the build (SUBDIRS, configure.ac substitutions + AC_CONFIG_FILES).
Ports the reusable, protocol-agnostic multi-monitor support added upstream in apache#560 (GUACAMOLE-288) by Corentin Soriano (@corentin-soriano): * guac_user_size_handler gains per-monitor x_position/top_offset * new "multimon-layout" default-layer parameter (a JSON monitor map) * __guac_handle_size parses the new size-instruction fields * guac_rect_shrink guards against divide-by-zero Every protocol's size handler is updated to the new signature; the new per-monitor arguments are ignored by all protocols except SPICE (see the following commit), preserving existing single-monitor behavior. Original work by @corentin-soriano, adapted for this fork: apache#560
Builds on the ported libguac multi-monitor primitive to add client-driven multi-monitor support to the SPICE protocol, following the design of the RDP implementation in apache#560 (by @corentin-soriano) adapted to SPICE's model, where a QXL guest presents all heads as regions of a single combined framebuffer. * settings: new "secondary-monitors" parameter (max_secondary_monitors, default 0 = disabled) * the size handler tracks a per-monitor array (fixed, up to GUAC_SPICE_MAX_MONITORS) keyed by x_position, tiling monitors left-to-right and computing each monitor's left_offset; a non-positive size closes a secondary monitor * resize pushes the whole layout to the guest agent in a single monitors config (enabling/positioning active monitors and disabling removed ones) * on each combined-surface (re)create, the current layout is sent to the client as the "multimon-layout" parameter on the default layer so a multi-monitor client can split it into per-monitor windows * joining users are told the permitted secondary-monitor count via a "secondary-monitors" argv stream The monitor state uses a fixed array (no dynamic allocation), avoiding the pointer-ownership concerns raised in review of the upstream RDP change, and the default layer is referenced via GUAC_DEFAULT_LAYER rather than casting a guac_display_layer.
Derive the multimon-layout sent to the client from the guest's actual SpiceDisplayMonitorConfig (read via the display channel's monitors property) instead of the client-requested geometry, clamped to the current combined surface and keyed by the guest head id. Republish on notify::monitors, and reset the recorded surface dimensions on display-primary-destroy so a delayed publish cannot validate against a destroyed surface. Fixes secondary windows rendering blank/mirrored when the guest's real layout diverged from what the client requested.
Coalesce rapid client resize requests into a single guest monitors config, sent 500ms after the first request via a GLib timeout on the SPICE event-loop context. A burst of resize events (primary + secondary windows) previously pushed the monitors config to the guest several times per second, so the guest agent never settled and thrashed between clone/extend/disabled states. The timer is cancelled on teardown so it cannot fire after the client is freed.
Extend the previously text-only SPICE clipboard to images (image/png, image/bmp, image/jpeg, image/tiff) in both directions. Maps Guacamole clipboard mimetypes to/from VD_AGENT clipboard types, requests the most-preferred type the guest offers (text, then PNG, then other image formats), and offers the client's clipboard to the guest using the type matching its mimetype. Data is handled by length throughout, so binary image payloads are safe. The clipboard lock is held across the guest's request so a concurrent paste cannot swap contents mid-transfer.
Add the recording-include-clipboard connection parameter (matching RDP and VNC) and pass it to guac_recording_create, so clipboard state changes are captured in session recordings. Previously hardcoded off.
#7) The guest-to-client clipboard (data copied OUT of the guest) is broadcast per-user and so was not captured by the recording's client-socket tee, leaving the data-exfiltration direction unrecorded. Record it explicitly via guac_recording_report_clipboard, and annotate every clipboard transfer in the recording with a direction/mimetype/stream log line (guest-to-client vs client-to-guest) so recordings can be audited for clipboard-based data movement. The annotation is written only to the recording socket and is gated identically to the clipboard data it describes.
…oses #14) Add a client->guest upload path over the SPICE agent (spice_main_channel_file_copy_async), selected by a new file-transfer-mode parameter: none | agent | drive | both. There is only one Guacamole upload handler, so the mode deterministically routes the drag/upload gesture: - agent: push files into the guest via spice-vdagent (no mount, upload-only) - drive: existing bidirectional WebDAV shared folder - both: browse/download via the folder, drag-drop upload via the agent A blank mode preserves the legacy file-transfer behaviour (drive if enabled). Uploaded files are staged in a private temp dir (O_EXCL|O_NOFOLLOW) and pushed on the SPICE event-loop thread. A data_destroy hook on deferred calls guarantees cleanup if the loop ends before dispatch, an aborted flag prevents pushing a truncated file after a staging error, and the live main channel is resolved on the loop thread to avoid dangling references. Completion is logged with direction and mechanism for auditing.
Hardens the SPICE handler against the memory-safety and DoS findings from the security assessment (#21): - F1 (High, CWE-190->787): bound the client-advertised audio rate/channels and use the checked-multiply allocation in the resampler, so a crafted audio format (e.g. rate=1) can no longer overflow the output-size arithmetic into a heap OOB write (32-bit) or a multi-GB allocation (64-bit). - F2 (Medium, CWE-400/770): cap the SPICE-agent upload staging size so an over-large or endless upload cannot exhaust the guacd host's temp storage. - F3 (Medium, CWE-416): do not start the download-monitor inotify thread. Its transfer action was unimplemented (dead code) and it dereferenced the folder after guac_spice_folder_free() released it (use-after-free). A completed feature must re-introduce it with a proper shutdown path. - F4 (Medium, CWE-400/789): clamp SPICE-server-supplied cursor/primary-surface dimensions to GUAC_DISPLAY_MAX_* before resizing, preventing a malicious or MITM'd server from driving an oversized allocation.
…on recordings (#27) New `guacclip` CLI (peer to guaclog/guacenc) that recovers clipboard artifacts — text and images — from a `.guac` session recording, filling the gap those tools leave (guacenc→video, guaclog→keystrokes, neither touches clipboard). Phase 1 / MVP of #27; no recording-format change, purely additive tooling. Enabled by the clipboard recording added in #7. `guacclip [-f] [-o OUTDIR] [--direction guest-to-client|client-to-guest] [--include image|text|all] [--max-item-bytes N] FILE...` writes `OUTDIR/manifest.json` + `OUTDIR/items/` with per-item direction, timing, mimetype, byte count, and SHA-256. Design mirrors guaclog (guac_parser/guac_socket loop). Clipboard streams are reassembled strictly by the stream index opened by a `clipboard` instruction, so graphical `img`/file/pipe blob streams are never misread as clipboard data. Direction comes from the recording-only `log stream=N direction=...` annotation, buffered per stream index and consumed at stream open; older recordings without it get direction "unknown". A self-contained FIPS 180-4 SHA-256 (sha256.c) keeps the tool dependency-free — it links only libguac. Hardened against hostile recordings: mimetype is slugged to [A-Za-z0-9_] for filenames (no path traversal) and \u-escaped in JSON (valid manifest even with non-ASCII bytes); per-item size is capped (64 MiB default, `0` opts out) and the manifest item count is bounded to prevent OOM; item files are written via O_EXCL|O_NOFOLLOW temp + atomic rename; stream indices are parsed with strtol validation. Builds clean under -Werror -Wall; gated by --disable-guacclip. Part of #27 (Phase 1). Phase 2 (in-browser audit cockpit) is separate.
Complete the guacclip Phase 1 code: add duplicate detection and a unit-test suite. --dedup none|skip (default none): duplicates are detected by SHA-256 of the reassembled item bytes. Every manifest item gains a duplicate_of field (the sequence number of the first identical item, or null). In skip mode a duplicate's file is not written (filename null) and a "duplicate" warning is added; none mode writes every item. Repeated copies are legitimate audit events, so none is the default. tests/: a CUnit suite (test_guacclip, wired into make check) covering SHA-256 FIPS 180-4 vectors and, via the guacclip_interpret() integration path, multi-blob reassembly, interleaved streams, direction correlation (and the unknown-direction fallback), the non-clipboard-stream guard, truncation, oversized capping, bad base64, non-numeric stream index, and both dedup modes. 14/14 pass; -Werror -Wall clean. Requires libcunit (already the test dep for the other modules).
90115e3 to
79d366a
Compare
Author
|
Understood. Will wait for @corentin-soriano feedback in the Jira ticket regarding those original commits. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements GUACAMOLE-261: a native SPICE client protocol plugin for guacd (
libguac-client-spice), built onspice-client-glib.Features
server-layoutkeymaps (including correct handling of shift-lock layouts).disable-audio-opus,preferred-compression.file-transfer-mode(none/agent/drive/both).preferred-video-codec.proxy, TLS public-key pinning,swap-red-blue, and more.guacclip: utility to extract clipboard artifacts (text/images) from session recordings.Scope note
This branch also carries the GUACAMOLE-288 multi-monitor primitive (ported from #560 by @corentin-soriano, original authorship preserved), which the SPICE multi-monitor implementation builds on.
Testing
End-to-end QA battery — 14/14 PASS against live SPICE guests: clean
-Werrorbuild, unit tests, connect+render, dynamic resize, bidirectional text/image clipboard, session recording with clipboard direction, multi-monitor extend (validated up to 4 heads at 1080p), agent file-copy, and audio. Results are summarized on GUACAMOLE-261.Companion PRs: client (
apache/guacamole-client) and documentation (apache/guacamole-manual).