remove all guacd references
This commit is contained in:
5
.env
5
.env
@@ -11,9 +11,8 @@ JWT_SECRET=c4baf6aca61629fcdf2285be2162e662ffec79a5db4e24d8bad2556f6f10c8c5
|
|||||||
ADMIN_USER=admin
|
ADMIN_USER=admin
|
||||||
ADMIN_PASSWORD=admin123
|
ADMIN_PASSWORD=admin123
|
||||||
|
|
||||||
# Apache Guacamole daemon
|
# RDP daemon WebSocket URL
|
||||||
GUACD_HOST=guacd
|
RDPD_URL=ws://rdpd:7777
|
||||||
GUACD_PORT=4822
|
|
||||||
|
|
||||||
# Backend port
|
# Backend port
|
||||||
PORT=3000
|
PORT=3000
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ JWT_SECRET=change-me-to-a-secure-jwt-secret
|
|||||||
ADMIN_USER=admin
|
ADMIN_USER=admin
|
||||||
ADMIN_PASSWORD=admin123
|
ADMIN_PASSWORD=admin123
|
||||||
|
|
||||||
# Apache Guacamole daemon
|
# RDP daemon WebSocket URL
|
||||||
GUACD_HOST=guacd
|
RDPD_URL=ws://rdpd:7777
|
||||||
GUACD_PORT=4822
|
|
||||||
|
|
||||||
# Backend port
|
# Backend port
|
||||||
PORT=3000
|
PORT=3000
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
# syntax=docker/dockerfile:1
|
|
||||||
#
|
|
||||||
# Custom guacd image built against FreeRDP 3.8.0+ on Ubuntu 24.04.
|
|
||||||
#
|
|
||||||
# Why FreeRDP from source: Ubuntu 24.04 ships freerdp3-dev 3.5.1.
|
|
||||||
# guacamole-server git main (1.7-dev) targets FreeRDP 3.8.0+:
|
|
||||||
# - gdi.c's GUAC_ASSERT(current_context == NULL) is gated #if MINOR < 8
|
|
||||||
# so with 3.5.x it fires when GFX calls begin_paint before desktop_resize
|
|
||||||
# - gdi_resize() in 3.8.0+ calls EndPaint internally, which guacamole relies on
|
|
||||||
# - GFX pipeline surface synchronisation is fixed in 3.8.0+
|
|
||||||
# Building 3.x HEAD from source (always >= 3.8.0 by now) gives us the
|
|
||||||
# correct FreeRDP that guacamole-server git main was developed against.
|
|
||||||
#
|
|
||||||
# Build notes:
|
|
||||||
# - CPPFLAGS=-DHAVE_FREERDP_VERIFYCERTIFICATEEX=1 fixes a macro name mismatch
|
|
||||||
# in guacamole-server: configure.ac generates HAVE_STRUCT_FREERDP_VERIFYCERTIFICATEEX
|
|
||||||
# (AC_CHECK_MEMBERS adds STRUCT_ prefix) but rdp.c checks
|
|
||||||
# HAVE_FREERDP_VERIFYCERTIFICATEEX (no STRUCT_). Without this the VerifyCertificateEx
|
|
||||||
# callback is never registered, causing a silent drop ~430ms after keymap load.
|
|
||||||
# - display-flush.c / display-plan.c patch: loop over layers doesn't advance
|
|
||||||
# `current` before `continue` when pending_frame.buffer is NULL → infinite loop
|
|
||||||
# or GUAC_ASSERT abort. Fixed by replacing the GUAC_ASSERT with a pointer advance.
|
|
||||||
# - gdi.c patch: GUAC_ASSERT(current_context == NULL) in desktop_resize fires on
|
|
||||||
# FreeRDP 3.5.x because the GFX pipeline calls begin_paint then gdi_resize before
|
|
||||||
# end_paint. Replaced with a guard that calls end_paint if context is still open.
|
|
||||||
# With FreeRDP 3.8.0+ this code is inside #if MINOR < 8 so the patch is harmless.
|
|
||||||
# - user.c patch: guac_user_supports_webp() dereferences image_mimetypes without
|
|
||||||
# NULL check → SIGSEGV when client didn't send image MIME types.
|
|
||||||
|
|
||||||
FROM ubuntu:24.04
|
|
||||||
|
|
||||||
COPY docker/patch-display-flush.py /patch-display-flush.py
|
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
|
|
||||||
# Build dependencies for guacamole-server AND FreeRDP source build
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
autoconf \
|
|
||||||
automake \
|
|
||||||
build-essential \
|
|
||||||
ca-certificates \
|
|
||||||
cmake \
|
|
||||||
curl \
|
|
||||||
git \
|
|
||||||
libcairo2-dev \
|
|
||||||
libjpeg-turbo8-dev \
|
|
||||||
libossp-uuid-dev \
|
|
||||||
libpango1.0-dev \
|
|
||||||
libpng-dev \
|
|
||||||
libpulse-dev \
|
|
||||||
libssl-dev \
|
|
||||||
libssh2-1-dev \
|
|
||||||
libtelnet-dev \
|
|
||||||
libtool \
|
|
||||||
libvncserver-dev \
|
|
||||||
libwebp-dev \
|
|
||||||
libwebsockets-dev \
|
|
||||||
libkrb5-dev \
|
|
||||||
libavcodec-dev \
|
|
||||||
libavutil-dev \
|
|
||||||
libswscale-dev \
|
|
||||||
libusb-1.0-0-dev \
|
|
||||||
pkgconf \
|
|
||||||
zlib1g-dev \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Build FreeRDP 3.x from source (3.8.0+ required for guacamole-server git main).
|
|
||||||
# We install to /usr with lib under lib/ (not lib/x86_64-linux-gnu/) so that
|
|
||||||
# pkg-config --variable=libdir freerdp3 returns /usr/lib cleanly.
|
|
||||||
# Optional features (audio, printing, X11 display, H264, Kerberos) are disabled
|
|
||||||
# since guacd runs headless and we have disable-audio: true.
|
|
||||||
RUN git clone --depth=1 --branch 3.22.0 https://github.com/FreeRDP/FreeRDP.git /tmp/freerdp \
|
|
||||||
&& cmake -S /tmp/freerdp -B /tmp/freerdp-build \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
|
||||||
-DCMAKE_INSTALL_LIBDIR=lib \
|
|
||||||
-DWITH_X11=OFF \
|
|
||||||
-DWITH_WAYLAND=OFF \
|
|
||||||
-DWITH_PULSEAUDIO=OFF \
|
|
||||||
-DWITH_ALSA=OFF \
|
|
||||||
-DWITH_OSS=OFF \
|
|
||||||
-DWITH_CUPS=OFF \
|
|
||||||
-DWITH_FFMPEG=OFF \
|
|
||||||
-DWITH_OPENH264=OFF \
|
|
||||||
-DWITH_JPEG=ON \
|
|
||||||
-DWITH_CAIRO=ON \
|
|
||||||
-DWITH_CHANNELS=ON \
|
|
||||||
-DWITH_CLIENT=ON \
|
|
||||||
-DWITH_CLIENT_COMMON=ON \
|
|
||||||
-DWITH_SERVER=OFF \
|
|
||||||
-DWITH_SAMPLE=OFF \
|
|
||||||
-DBUILD_TESTING=OFF \
|
|
||||||
-DWITH_GSSAPI=OFF \
|
|
||||||
-DWITH_FUSE=OFF \
|
|
||||||
&& cmake --build /tmp/freerdp-build -j"$(nproc)" \
|
|
||||||
&& cmake --install /tmp/freerdp-build \
|
|
||||||
&& ldconfig \
|
|
||||||
&& rm -rf /tmp/freerdp /tmp/freerdp-build
|
|
||||||
|
|
||||||
# Build guacamole-server from git main against our newly installed FreeRDP 3.x.
|
|
||||||
RUN FREERDP_PLUGIN_DIR=$(pkg-config --variable=libdir freerdp3 2>/dev/null)/freerdp3 \
|
|
||||||
&& echo "Building guacamole-server (git main) with FreeRDP plugin dir: ${FREERDP_PLUGIN_DIR}" \
|
|
||||||
&& echo "FreeRDP version: $(pkg-config --modversion freerdp3)" \
|
|
||||||
&& git clone --depth=1 https://github.com/apache/guacamole-server.git \
|
|
||||||
&& cd guacamole-server \
|
|
||||||
&& python3 /patch-display-flush.py \
|
|
||||||
&& autoreconf -fi \
|
|
||||||
&& CPPFLAGS="-Wno-error=deprecated-declarations -DHAVE_FREERDP_VERIFYCERTIFICATEEX=1" \
|
|
||||||
CFLAGS="-O2 -Wno-error=unused-variable" \
|
|
||||||
./configure \
|
|
||||||
--prefix=/usr \
|
|
||||||
--sysconfdir=/etc \
|
|
||||||
--with-freerdp-plugin-dir="${FREERDP_PLUGIN_DIR}" \
|
|
||||||
&& make -j"$(nproc)" \
|
|
||||||
&& make install \
|
|
||||||
&& ldconfig \
|
|
||||||
&& cd / && rm -rf guacamole-server
|
|
||||||
|
|
||||||
# guacd log level is passed via -L flag; exposed as env var for docker-compose
|
|
||||||
ENV GUACD_LOG_LEVEL=info
|
|
||||||
|
|
||||||
EXPOSE 4822
|
|
||||||
|
|
||||||
CMD sh -c "exec /usr/sbin/guacd -b 0.0.0.0 -f -L \"${GUACD_LOG_LEVEL}\""
|
|
||||||
@@ -1,240 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Patch guacamole-server source files to fix bugs in git main that abort()
|
|
||||||
or segfault the child process in debug builds (no -DNDEBUG).
|
|
||||||
|
|
||||||
--- Fix 1: src/libguac/display-flush.c ---
|
|
||||||
|
|
||||||
The frame-flush loop iterates over display layers to diff pending vs last frame.
|
|
||||||
When a layer has pending_frame.buffer == NULL (e.g. cursor/aux layers that have
|
|
||||||
never been drawn), the code does:
|
|
||||||
|
|
||||||
GUAC_ASSERT(current->pending_frame.buffer_is_external);
|
|
||||||
continue; <- BUG: never advances `current`
|
|
||||||
|
|
||||||
Two problems:
|
|
||||||
1. GUAC_ASSERT aborts the process if buffer_is_external is false, which happens
|
|
||||||
for any fresh layer that has not yet received display data. In a debug build
|
|
||||||
(no -DNDEBUG) this is a real assert -> abort() -> child process dies.
|
|
||||||
2. If the assert passes, `continue` loops forever on the same layer.
|
|
||||||
|
|
||||||
Fix: replace the GUAC_ASSERT with a proper pointer advance before the continue.
|
|
||||||
|
|
||||||
--- Fix 2: src/libguac/display-plan.c ---
|
|
||||||
|
|
||||||
Identical bug: the same GUAC_ASSERT(current->pending_frame.buffer_is_external)
|
|
||||||
pattern exists in display-plan.c (same layer-iteration loop, same missing pointer
|
|
||||||
advance before continue). Applying the same fix.
|
|
||||||
|
|
||||||
--- Fix 3: src/libguac/user.c ---
|
|
||||||
|
|
||||||
guac_user_supports_webp() iterates user->info.image_mimetypes without checking
|
|
||||||
whether the pointer itself is NULL. If the client did not send any image MIME
|
|
||||||
types (or guac_copy_mimetypes() returned NULL), the very first dereference
|
|
||||||
`*mimetype` causes a SIGSEGV in the display worker thread.
|
|
||||||
|
|
||||||
Fix: insert a NULL guard immediately after the pointer is loaded from the struct.
|
|
||||||
|
|
||||||
--- Fix 6: src/protocols/rdp/gdi.c (guac_rdp_gdi_end_paint) ---
|
|
||||||
|
|
||||||
With disable-gfx/legacy RDP, many Windows bitmap updates do NOT include
|
|
||||||
FrameMarker PDUs. guac_display_render_thread_notify_frame() (which wakes the
|
|
||||||
render thread to encode dirty tiles) is ONLY called from guac_rdp_gdi_mark_frame
|
|
||||||
— the FrameMarker handler. Legacy bitmaps that arrive without FrameMarkers mark
|
|
||||||
regions dirty but never wake the render thread → tiles are never encoded →
|
|
||||||
black squares on screen that only appear when a subsequent hover redraw (which
|
|
||||||
DOES carry a FrameMarker) finally wakes the thread for that region.
|
|
||||||
|
|
||||||
Root cause: the gdi_modified → notify_modified path in the main event loop only
|
|
||||||
fires AFTER the RDP event queue drains. During a continuous burst of bitmap
|
|
||||||
updates (initial desktop load), the queue never drains, so notify_modified is
|
|
||||||
never called, and the render thread sleeps indefinitely. Additionally, the render
|
|
||||||
thread applies lag compensation: if the browser hasn't ACK'd the last sync
|
|
||||||
instruction, processing_lag can reach 500 ms, and the render thread pauses up to
|
|
||||||
500 ms between flushes. Both factors combine to cause large areas of the screen
|
|
||||||
to remain black throughout the initial render.
|
|
||||||
|
|
||||||
Fix: call notify_frame unconditionally on every EndPaint. The render thread
|
|
||||||
naturally rate-limits output via MIN_FRAME_DURATION (10 ms = 100 fps max). To
|
|
||||||
prevent the processing_lag from reaching the 500 ms cap (which would cause the
|
|
||||||
render thread to throttle itself), the Node.js proxy (rdp.ts) immediately echoes
|
|
||||||
every sync instruction received from guacd back to guacd as a client ACK. This
|
|
||||||
keeps processing_lag near 0 ms so the render thread flushes as fast as
|
|
||||||
MIN_FRAME_DURATION allows. The browser's own sync ACKs are dropped by the proxy
|
|
||||||
to avoid double-updating the processing_lag measurement.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def patch_file_replace(path, search_string, replacement_fn, description):
|
|
||||||
"""Replace the line containing search_string with replacement_fn(indent)."""
|
|
||||||
with open(path) as f:
|
|
||||||
lines = f.readlines()
|
|
||||||
|
|
||||||
patched = False
|
|
||||||
i = 0
|
|
||||||
while i < len(lines):
|
|
||||||
if search_string in lines[i]:
|
|
||||||
indent = lines[i][: len(lines[i]) - len(lines[i].lstrip())]
|
|
||||||
lines[i] = indent + replacement_fn(indent) + "\n"
|
|
||||||
patched = True
|
|
||||||
break
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
if not patched:
|
|
||||||
print("ERROR: patch target not found in " + path + " (" + description + ")", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
with open(path, "w") as f:
|
|
||||||
f.writelines(lines)
|
|
||||||
|
|
||||||
print("Patched " + path + " (" + description + ")")
|
|
||||||
|
|
||||||
|
|
||||||
def patch_file_insert_after(path, search_string, insert_lines, description):
|
|
||||||
"""Insert insert_lines (with matching indent) after the line containing search_string."""
|
|
||||||
with open(path) as f:
|
|
||||||
lines = f.readlines()
|
|
||||||
|
|
||||||
patched = False
|
|
||||||
i = 0
|
|
||||||
while i < len(lines):
|
|
||||||
if search_string in lines[i]:
|
|
||||||
indent = lines[i][: len(lines[i]) - len(lines[i].lstrip())]
|
|
||||||
for j, new_line in enumerate(insert_lines):
|
|
||||||
lines.insert(i + 1 + j, indent + new_line + "\n")
|
|
||||||
patched = True
|
|
||||||
break
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
if not patched:
|
|
||||||
print("ERROR: patch target not found in " + path + " (" + description + ")", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
with open(path, "w") as f:
|
|
||||||
f.writelines(lines)
|
|
||||||
|
|
||||||
print("Patched " + path + " (" + description + ")")
|
|
||||||
|
|
||||||
|
|
||||||
# Fix 1: display-flush.c
|
|
||||||
patch_file_replace(
|
|
||||||
"src/libguac/display-flush.c",
|
|
||||||
"GUAC_ASSERT(current->pending_frame.buffer_is_external)",
|
|
||||||
lambda indent: "current = current->pending_frame.next;",
|
|
||||||
"replace GUAC_ASSERT with pointer advance",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Fix 2: display-plan.c (identical bug pattern)
|
|
||||||
patch_file_replace(
|
|
||||||
"src/libguac/display-plan.c",
|
|
||||||
"GUAC_ASSERT(current->pending_frame.buffer_is_external)",
|
|
||||||
lambda indent: "current = current->pending_frame.next;",
|
|
||||||
"replace GUAC_ASSERT with pointer advance",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Fix 3: gdi.c - handle begin_paint context still open during desktop_resize.
|
|
||||||
# With the GFX pipeline on FreeRDP < 3.8.0, begin_paint may be active when
|
|
||||||
# desktop_resize fires, leaving current_context non-NULL. FreeRDP 3.8.0 fixed
|
|
||||||
# this by calling EndPaint inside gdi_resize(); backport that behaviour here.
|
|
||||||
gdi_path = "src/protocols/rdp/gdi.c"
|
|
||||||
with open(gdi_path) as f:
|
|
||||||
gdi_content = f.read()
|
|
||||||
|
|
||||||
gdi_old = (
|
|
||||||
" /* For FreeRDP versions prior to 3.8.0, EndPaint will not be called in\n"
|
|
||||||
" * `gdi_resize()`, so the current context should be NULL. If it is not\n"
|
|
||||||
" * NULL, it means that the current context is still open, and therefore the\n"
|
|
||||||
" * GDI buffer has not been flushed yet. */\n"
|
|
||||||
" GUAC_ASSERT(rdp_client->current_context == NULL);"
|
|
||||||
)
|
|
||||||
|
|
||||||
gdi_new = (
|
|
||||||
" /* For FreeRDP < 3.8.0, gdi_resize() does not call EndPaint internally.\n"
|
|
||||||
" * With the GFX pipeline begin_paint may still be active when desktop_resize\n"
|
|
||||||
" * fires, leaving current_context non-NULL. End the paint now so the context\n"
|
|
||||||
" * is properly closed before we open a new one for the resize. */\n"
|
|
||||||
" if (rdp_client->current_context != NULL)\n"
|
|
||||||
" guac_rdp_gdi_end_paint(context);"
|
|
||||||
)
|
|
||||||
|
|
||||||
if gdi_old not in gdi_content:
|
|
||||||
print("ERROR: patch target not found in " + gdi_path + " (desktop_resize assert)", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
gdi_content = gdi_content.replace(gdi_old, gdi_new, 1)
|
|
||||||
with open(gdi_path, "w") as f:
|
|
||||||
f.write(gdi_content)
|
|
||||||
|
|
||||||
print("Patched " + gdi_path + " (desktop_resize assert -> end_paint guard)")
|
|
||||||
|
|
||||||
|
|
||||||
# Fix 5: user.c - NULL guard for image_mimetypes in guac_user_supports_webp
|
|
||||||
patch_file_insert_after(
|
|
||||||
"src/libguac/user.c",
|
|
||||||
"const char** mimetype = user->info.image_mimetypes;",
|
|
||||||
[
|
|
||||||
"",
|
|
||||||
"/* Guard: image_mimetypes may be NULL if client sent no image types */",
|
|
||||||
"if (mimetype == NULL)",
|
|
||||||
" return 0;",
|
|
||||||
"",
|
|
||||||
],
|
|
||||||
"NULL guard for image_mimetypes in guac_user_supports_webp",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Fix 6: gdi.c - notify_frame on every EndPaint (no rate limit)
|
|
||||||
#
|
|
||||||
# Problem: legacy RDP bitmap-cache updates (taskbar icons, static content) carry
|
|
||||||
# no FrameMarker PDU. The gdi_modified → notify_modified path only fires after
|
|
||||||
# the RDP event queue drains — never during a burst — so tiles accumulate as
|
|
||||||
# black squares. Additionally the render thread's lag-compensation can pause up
|
|
||||||
# to 500 ms between flushes if the browser hasn't ACK'd the previous sync.
|
|
||||||
#
|
|
||||||
# Fix: call notify_frame unconditionally in EndPaint. The render thread's own
|
|
||||||
# MIN_FRAME_DURATION (10 ms) provides natural batching at up to 100 fps. The
|
|
||||||
# sync-flood / lag-compensation problem is solved at the proxy layer (rdp.ts):
|
|
||||||
# the Node.js proxy immediately echoes every sync timestamp back to guacd so
|
|
||||||
# processing_lag stays near 0 ms, and the browser's own ACKs are dropped to
|
|
||||||
# prevent double-updating the measurement.
|
|
||||||
gdi_end_paint_old = (
|
|
||||||
" /* There will be no further drawing operations */\n"
|
|
||||||
" rdp_client->current_context = NULL;\n"
|
|
||||||
" guac_display_layer_close_raw(default_layer, current_context);\n"
|
|
||||||
"\n"
|
|
||||||
" return TRUE;\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
gdi_end_paint_new = (
|
|
||||||
" /* There will be no further drawing operations */\n"
|
|
||||||
" rdp_client->current_context = NULL;\n"
|
|
||||||
" guac_display_layer_close_raw(default_layer, current_context);\n"
|
|
||||||
"\n"
|
|
||||||
" /* Notify the render thread that new tiles are ready to encode.\n"
|
|
||||||
" * notify_frame rather than notify_modified ensures the frame counter\n"
|
|
||||||
" * advances and a sync is emitted after each batch. This causes legacy\n"
|
|
||||||
" * bitmap-cache updates (which carry no FrameMarker PDU) to be flushed\n"
|
|
||||||
" * immediately on every EndPaint instead of accumulating as black squares\n"
|
|
||||||
" * until the next FrameMarker. The render thread's MIN_FRAME_DURATION\n"
|
|
||||||
" * (10 ms) limits output to 100 fps; the Node.js proxy handles sync ACKs\n"
|
|
||||||
" * so guacd's processing_lag stays near 0 ms. */\n"
|
|
||||||
" if (rdp_client->render_thread != NULL)\n"
|
|
||||||
" guac_display_render_thread_notify_frame(rdp_client->render_thread);\n"
|
|
||||||
"\n"
|
|
||||||
" return TRUE;\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
gdi_path = "src/protocols/rdp/gdi.c"
|
|
||||||
with open(gdi_path) as f:
|
|
||||||
gdi_end_content = f.read()
|
|
||||||
|
|
||||||
if gdi_end_paint_old not in gdi_end_content:
|
|
||||||
print("ERROR: patch target not found in " + gdi_path + " (notify_frame on EndPaint)", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
gdi_end_content = gdi_end_content.replace(gdi_end_paint_old, gdi_end_paint_new, 1)
|
|
||||||
with open(gdi_path, "w") as f:
|
|
||||||
f.write(gdi_end_content)
|
|
||||||
|
|
||||||
print("Patched " + gdi_path + " (notify_frame on every EndPaint)")
|
|
||||||
@@ -14,8 +14,7 @@
|
|||||||
"@xterm/xterm": "^5.5.0",
|
"@xterm/xterm": "^5.5.0",
|
||||||
"antd": "^5.20.5",
|
"antd": "^5.20.5",
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"guacamole-common-js": "^1.5.0",
|
"react": "^18.3.1",
|
||||||
"react": "^18.3.1",
|
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-router-dom": "^6.26.2",
|
"react-router-dom": "^6.26.2",
|
||||||
"zustand": "^4.5.5"
|
"zustand": "^4.5.5"
|
||||||
|
|||||||
84
frontend/src/types/guacamole.d.ts
vendored
84
frontend/src/types/guacamole.d.ts
vendored
@@ -1,84 +0,0 @@
|
|||||||
// Custom type declarations for guacamole-common-js.
|
|
||||||
// We declare this instead of using @types/guacamole-common-js to avoid version mismatches.
|
|
||||||
|
|
||||||
declare namespace Guacamole {
|
|
||||||
interface Tunnel {
|
|
||||||
onerror: ((status: Status) => void) | null;
|
|
||||||
onstatechange: ((state: number) => void) | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
class WebSocketTunnel implements Tunnel {
|
|
||||||
constructor(
|
|
||||||
tunnelURL: string,
|
|
||||||
crossDomain?: boolean,
|
|
||||||
extraTunnelHeaders?: Record<string, string>
|
|
||||||
);
|
|
||||||
onerror: ((status: Status) => void) | null;
|
|
||||||
onstatechange: ((state: number) => void) | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Client {
|
|
||||||
constructor(tunnel: Tunnel);
|
|
||||||
connect(data?: string): void;
|
|
||||||
disconnect(): void;
|
|
||||||
getDisplay(): Display;
|
|
||||||
sendKeyEvent(pressed: number, keysym: number): void;
|
|
||||||
sendMouseState(mouseState: Mouse.State, applyDisplayScale?: boolean): void;
|
|
||||||
onerror: ((status: Status) => void) | null;
|
|
||||||
onstatechange: ((state: number) => void) | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Display {
|
|
||||||
getElement(): HTMLDivElement;
|
|
||||||
getWidth(): number;
|
|
||||||
getHeight(): number;
|
|
||||||
scale(scale: number): void;
|
|
||||||
onresize: (() => void) | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Mouse {
|
|
||||||
constructor(element: Element);
|
|
||||||
onmousedown: ((mouseState: Mouse.State) => void) | null;
|
|
||||||
onmouseup: ((mouseState: Mouse.State) => void) | null;
|
|
||||||
onmousemove: ((mouseState: Mouse.State) => void) | null;
|
|
||||||
onmouseout: ((mouseState: Mouse.State) => void) | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
||||||
namespace Mouse {
|
|
||||||
class State {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
left: boolean;
|
|
||||||
middle: boolean;
|
|
||||||
right: boolean;
|
|
||||||
up: boolean;
|
|
||||||
down: boolean;
|
|
||||||
constructor(
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
left: boolean,
|
|
||||||
middle: boolean,
|
|
||||||
right: boolean,
|
|
||||||
up: boolean,
|
|
||||||
down: boolean
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Keyboard {
|
|
||||||
constructor(element: Element | Document);
|
|
||||||
onkeydown: ((keysym: number) => void) | null;
|
|
||||||
onkeyup: ((keysym: number) => void) | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Status {
|
|
||||||
code: number;
|
|
||||||
message: string;
|
|
||||||
constructor(code: number, message?: string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module 'guacamole-common-js' {
|
|
||||||
export = Guacamole;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user