grdp

package module
v0.0.0-...-4c13730 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 12, 2026 License: GPL-3.0 Imports: 25 Imported by: 0

README

Golang Remote Desktop Protocol

grdp is a pure Golang implementation of the Microsoft RDP (Remote Desktop Protocol) protocol client

Forked from tomatome/grdp

How to execute example

Prepare environment variables (In your environment. You may also need to set GRDP_DOMAIN)

export GRDP_USER=user
export GRDP_PASSWORD=password
export GRDP_PORT=3389
export GRDP_HOST=host
export GRDP_WINDOW_SIZE=1280x800
Environment Variables
Variable Description Default
GRDP_HOST Hostname or IP address of the RDP server (required)
GRDP_PORT Port number (required)
GRDP_USER Username (empty)
GRDP_PASSWORD Password (empty)
GRDP_DOMAIN Domain (empty)
GRDP_WINDOW_SIZE Window size in WxH format 1280x800
GRDP_KEYBOARD_TYPE Keyboard type (see values below) IBM_101_102_KEYS
GRDP_KEYBOARD_LAYOUT Keyboard layout (see values below) US
GRDP_AUDIO_DELAY_MS Shift audio output later by N ms (use when audio leads video) 0
GRDP_VIDEO_DELAY_MS Shift video display later by N ms (use when video leads audio) 0
GRDP_KEYBOARD_TYPE values
Value Description
IBM_PC_XT_83_KEY IBM PC/XT 83-key keyboard
OLIVETTI Olivetti keyboard
IBM_PC_AT_84_KEY IBM PC/AT 84-key keyboard
IBM_101_102_KEYS IBM 101/102-key keyboard (most common)
NOKIA_1050 Nokia 1050 keyboard
NOKIA_9140 Nokia 9140 keyboard
JAPANESE Japanese keyboard
GRDP_KEYBOARD_LAYOUT values
Value Language / Region
ARABIC Arabic
BULGARIAN Bulgarian
CHINESE_US_KEYBOARD Chinese (US keyboard)
CZECH Czech
DANISH Danish
GERMAN German
GREEK Greek
US English (United States)
SPANISH Spanish
FINNISH Finnish
FRENCH French
HEBREW Hebrew
HUNGARIAN Hungarian
ICELANDIC Icelandic
ITALIAN Italian
JAPANESE Japanese
KOREAN Korean
DUTCH Dutch
NORWEGIAN Norwegian

Clone and execute example

git clone https://github.com/nakagami/grdp
cd grdp
go run ./example/
AVC/H.264 Hardware Accelerated Codec (Optional)

By default, grdp is pure Go and AVC (H.264) codecs are disabled. To enable hardware-accelerated H.264 decoding, build with the h264 tag and FFmpeg (≥ 3.4) development libraries:

# macOS
brew install ffmpeg

# Debian/Ubuntu
sudo apt install libavcodec-dev libavutil-dev libswscale-dev pkg-config

# Build and run with AVC support
go run -tags h264 ./example/

When built with -tags h264, the client advertises RDPGFX v10/v8.1 capabilities and supports AVC420/AVC444 codecs. Hardware acceleration (VideoToolbox on macOS, VAAPI on Linux) is automatically used when available, with a software fallback.

Note: Always pass ./example/ (package path) rather than a single file such as example/gxui.go. Specifying a single file omits the other source files in the package (aac_darwin.go, h264_ffmpeg.go, etc.) and results in build errors.

AAC Audio Hardware Decoding (macOS)

On macOS, the example automatically decodes AAC audio streams using AudioToolbox (hardware-accelerated on Apple Silicon and Intel iGPU) when the server offers AAC (WAVE_FORMAT_AAC, tag 0x00FF). No extra build flags are required; AudioToolbox is part of the macOS SDK.

On other platforms the example falls back to PCM audio.

This example uses gxui. Since gxui is no longer being updated, I hope to have some kind of cross-platform GUI example.

Take ideas from

Documentation

Overview

patch_solidping.go carries the SolidPing patches to this vendored grdp fork, kept in ONE file deliberately: every SolidPing need that upstream grdp does not already expose lives here, and nothing else in the fork is modified beyond the import-path rewrite and the deletion of the cgo-only codec plugins. See README-solidping.md.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SwapRB

func SwapRB(p []byte)

SwapRB swaps the red and blue byte of every 32-bit pixel in p in place and forces the alpha byte to 0xFF, converting between RGBA and BGRA byte order (the operation is its own inverse). It reuses the SIMD-accelerated batch converter (SSE2 on amd64, NEON on arm64), so callers that need BGRA pixels for an SDL_PIXELFORMAT_BGRA32 texture can convert an *image.RGBA buffer with a single vectorised pass instead of a scalar per-byte swap loop. len(p) must be a multiple of 4; any trailing bytes are ignored.

Types

type Bitmap

type Bitmap struct {
	DestLeft     int
	DestTop      int
	DestRight    int
	DestBottom   int
	Width        int
	Height       int
	BitsPerPixel int
	Data         []byte
}

Bitmap is a single rendered region delivered to the OnBitmap callback.

Lifecycle: Bitmap.Data is borrowed from an internal buffer pool and is only valid for the duration of the synchronous OnBitmap callback. After the callback returns the slice may be returned to the pool and overwritten by subsequent updates. Callers that need to retain the pixels (e.g. to hand them to an asynchronous paint goroutine) MUST copy the bytes before the callback returns.

func (*Bitmap) FillBGRA

func (bm *Bitmap) FillBGRA(dst []byte) []byte

FillBGRA converts the bitmap pixel data into packed BGRA32 (4 bytes/pixel, B at offset 0) and writes it into dst, growing dst if necessary. The caller may pass a previously returned slice to reuse the allocation across calls (common for tiled rendering where many same-sized bitmaps are converted in a loop). The returned slice has length == bm.Width * bm.Height * 4.

Unlike RGBA() + SwapRB, FillBGRA avoids an intermediate *image.RGBA allocation and the second-pass R/B swap for BGR24/BGRA32 inputs. For BGR24 the output is a single-pass direct pack; for BGRA32 it is a memcopy.

func (*Bitmap) FillRGBA

func (bm *Bitmap) FillRGBA(dst *image.RGBA) *image.RGBA

FillRGBA converts the bitmap's pixel data to RGBA format, writing into dst. If dst is nil or has the wrong dimensions a new *image.RGBA is allocated. Callers that process tiles of stable dimensions can reuse the same *image.RGBA across frames to avoid repeated heap allocations:

var tile *image.RGBA
tile = bm.FillRGBA(tile)

func (*Bitmap) RGBA

func (bm *Bitmap) RGBA() *image.RGBA

RGBA converts the bitmap pixel data to an *image.RGBA. A new *image.RGBA is allocated on each call. If the caller processes tiles of the same dimensions across frames, prefer FillRGBA to avoid allocations.

type RdpClient

type RdpClient struct {
	// contains filtered or unexported fields
}

func NewRdpClient

func NewRdpClient(host string, width, height int, dialer func(string) (net.Conn, error)) *RdpClient

func (*RdpClient) Close

func (g *RdpClient) Close()

func (*RdpClient) DisableAVC444

func (g *RdpClient) DisableAVC444() *RdpClient

DisableAVC444 prevents the client from advertising AVC444/AVC444v2 support. When called before Login, the RDPGFX CAPS_ADVERTISE is limited to v8.1 (AVC420 only), so the server will never send LC=2 chroma-upgrade frames. This avoids the colour distortion seen with VirtualBox VRDE, which sends LC=2 data but does not include stream2 in LC=0 IDR packets. The setting is preserved across automatic reconnects.

func (*RdpClient) EventReady

func (g *RdpClient) EventReady() bool

EventReady reports whether the session accepts input events. A script can poll it instead of acting blind after a session drop.

func (*RdpClient) Height

func (g *RdpClient) Height() int

func (*RdpClient) KeyDown

func (g *RdpClient) KeyDown(sc int)

func (*RdpClient) KeyUp

func (g *RdpClient) KeyUp(sc int)

func (*RdpClient) Login

func (g *RdpClient) Login(domain string, user string, password string) error

func (*RdpClient) MouseDown

func (g *RdpClient) MouseDown(button int, x, y int)

func (*RdpClient) MouseMove

func (g *RdpClient) MouseMove(x, y int)

MouseMove queues a mouse-move event. Successive moves within mouseCoalesceInterval are collapsed: only the latest (x,y) is sent. The first move in a burst is sent immediately so the server sees no extra latency for a single isolated motion.

func (*RdpClient) MouseUp

func (g *RdpClient) MouseUp(button int, x, y int)

func (*RdpClient) MouseWheel

func (g *RdpClient) MouseWheel(delta float64)

MouseWheel sends a vertical scroll event to the remote desktop. delta is the rotation amount in physical notches (1.0 = one click of a scroll wheel = Windows WHEEL_DELTA). Fractional values are accepted for smooth / high-resolution input devices such as trackpads. Positive values scroll up (away from the user); negative values scroll down.

func (*RdpClient) NotifyClipboardChanged

func (g *RdpClient) NotifyClipboardChanged()

NotifyClipboardChanged tells the server that the local clipboard has changed. The UI should call this when it detects a system clipboard change (e.g. via polling or a platform clipboard-change signal).

func (*RdpClient) OnAudio

func (g *RdpClient) OnAudio(f func(rdpsnd.AudioFormat, []byte)) *RdpClient

OnAudio registers a callback for server audio data. The callback receives the AudioFormat describing the PCM data and the raw audio bytes. Must be called before Login.

func (*RdpClient) OnAudioReset

func (g *RdpClient) OnAudioReset(f func()) *RdpClient

OnAudioReset registers a callback that is called when the server closes the audio channel (e.g. media seek or stream restart). The application should flush its audio playback buffer so that stale audio does not keep playing. Must be called before Login.

func (*RdpClient) OnBitmap

func (g *RdpClient) OnBitmap(paint func([]Bitmap)) *RdpClient

OnBitmap registers a callback for bitmap update events. For compressed bitmaps, Bitmap.Data is borrowed from an internal pool and is valid only for the duration of the paint call. If you need to retain the raw pixel data beyond paint, copy it or call bm.RGBA() inside paint.

func (*RdpClient) OnClipboard

func (g *RdpClient) OnClipboard(onRemote func(text string), getLocal func() string) *RdpClient

OnClipboard registers callbacks for bidirectional clipboard sharing.

  • onRemote is called with the text when the RDP server's clipboard content is received (server → client).
  • getLocal is called to retrieve the current local clipboard text when the server requests it (client → server).

Must be called before Login.

func (*RdpClient) OnClose

func (g *RdpClient) OnClose(f func()) *RdpClient

func (*RdpClient) OnDecoderBroken

func (g *RdpClient) OnDecoderBroken(f func()) *RdpClient

OnDecoderBroken registers a callback that is invoked when the H.264 decoder enters an unrecoverable state (all hard-reset attempts exhausted). When this callback is set, grdp does NOT automatically call Reconnect; the application is responsible for deciding when to reconnect (e.g. via its own stall watchdog). If no callback is registered, grdp falls back to the previous behaviour of reconnecting immediately.

func (*RdpClient) OnError

func (g *RdpClient) OnError(f func(e error)) *RdpClient

func (*RdpClient) OnH264I420

func (g *RdpClient) OnH264I420(fn func(destX, destY, w, h int, y []byte, yStride int, u []byte, uStride int, v []byte, vStride int)) *RdpClient

OnH264I420 registers a callback that receives decoded H.264 frames in I420 planar format (Y, U, V planes with associated strides). When set, the decoded frame is NOT delivered via OnBitmap; the caller is responsible for rendering it directly (e.g. via an SDL2 IYUV texture for GPU-accelerated YUV→RGB conversion). When I420 extraction is unavailable for a frame (e.g. non-YUV420P/NV12 formats), grdp falls back to OnBitmap delivery. destX, destY are top-left canvas coordinates; w, h are frame dimensions. The plane slices are only valid for the duration of the callback; copy them if they need to be retained beyond the callback's return.

func (*RdpClient) OnH264NV12

func (g *RdpClient) OnH264NV12(fn func(destX, destY, w, h int, y []byte, yStride int, uv []byte, uvStride int)) *RdpClient

OnH264NV12 registers a callback that receives decoded H.264 frames in NV12 format (Y plane plus interleaved UV plane). This is the fastest SDL2 path on platforms whose hardware decoder already outputs NV12 (notably macOS VideoToolbox), because callers can upload the planes directly with an NV12 texture and avoid NV12->I420 deinterleaving in grdp. When NV12 extraction is unavailable for a frame, grdp falls back to OnBitmap delivery. destX, destY are top-left canvas coordinates; w, h are frame dimensions. The plane slices are only valid for the duration of the callback; copy them if they need to be retained beyond the callback's return.

func (*RdpClient) OnH264Raw

func (g *RdpClient) OnH264Raw(fn func(destX, destY, w, h int, isKey bool, data []byte)) *RdpClient

OnH264Raw registers a callback that receives raw H.264 NAL unit data when the built-in decoder is unavailable (e.g. WASM builds without CGo). destX, destY are the top-left canvas coordinates; isKey flags an IDR frame. The caller owns data and may retain it beyond the callback.

func (*RdpClient) OnPointerCached

func (g *RdpClient) OnPointerCached(f func(uint16)) *RdpClient

func (*RdpClient) OnPointerHide

func (g *RdpClient) OnPointerHide(f func()) *RdpClient

func (*RdpClient) OnPointerUpdate

func (g *RdpClient) OnPointerUpdate(f func(uint16, uint16, uint16, uint16, uint16, uint16, []byte, []byte)) *RdpClient

func (*RdpClient) OnReady

func (g *RdpClient) OnReady(f func()) *RdpClient

func (*RdpClient) OnSuccess

func (g *RdpClient) OnSuccess(f func()) *RdpClient

func (*RdpClient) Reconnect

func (g *RdpClient) Reconnect(width, height int) error

func (*RdpClient) RequestKeyframe

func (g *RdpClient) RequestKeyframe()

RequestKeyframe asks the server to send a fresh full-screen IDR keyframe via the SuppressOutput off→on toggle (SendForceRefresh). It is the same request the RDPGFX decoder issues internally when it stalls, exposed publicly so the frontend's black-screen watchdog can recover an initial all-black session — where the server sent its first IDR before the desktop finished painting (decoded as a black warm-up frame and dropped) and then went idle — without the cost of a full reconnect. Safe to call from the render loop goroutine.

func (*RdpClient) SendLogoff

func (g *RdpClient) SendLogoff()

SendLogoff asks the SERVER to end the session by sending the TS_SHUTDOWN_REQUEST data PDU (MS-RDPBCGR 2.2.9.3 — the protocol's "log me off" verb; the server acknowledges with TS_SHUTDOWN_DENIED-prefixed teardown and removes the session). This is what `end_session: logoff` sends, and what a script's logoff() triggers.

Compare Close(), which only drops the transport: that is a DISCONNECT — the session survives on the server until its idle policy ends it, and the next connect reattaches to it.

No-op when the session is not ready: a log-off request before the session exists is meaningless, and the caller's logoff error reporting treats a closed session the same way.

func (*RdpClient) SendScancode

func (g *RdpClient) SendScancode(sc uint16, release bool)

SendScancode sends one scancode key event: press when release is false, release when true. sc may carry the 0xE0 extended prefix (0xE04B for Left Arrow); it is stripped and translated into KBDFLAGS_EXTENDED, which is the form both the slow-path serializer and the fast-path encoder expect.

`key()` composes named keys and combos out of these — scancodes, not Unicode, because named keys and combos (Enter, ctrl+alt+end, win+r) have no Unicode position.

func (*RdpClient) SendUnicodeKey

func (g *RdpClient) SendUnicodeKey(r rune)

SendUnicodeKey sends one Unicode character as a TS_UNICODE_KEYBOARD_EVENT press followed by its release (MS-RDPBCGR §2.2.8.1.2.2.5 fast-path, with a slow-path fallback through SendInputEvents).

Unicode input is what `type()` is built on: it carries the character itself, so the target's keyboard layout is irrelevant — "hello" arrives as h-e-l-l-o on a French AZERTY exactly as on a US QWERTY. `key()` uses scancodes instead, because named keys and combos (Enter, ctrl+alt+end) have no Unicode position.

No-op when the session is not ready: an event sent before the connection sequence finishes is dropped by the server, and silently dropping it here keeps the check's script from having to poll readiness between actions.

func (*RdpClient) SetKeyboardLayout

func (g *RdpClient) SetKeyboardLayout(layout string)

SetKeyboardLayout sets the keyboard layout by name (e.g. "US", "FRENCH"). Must be called before Login.

func (*RdpClient) SetKeyboardType

func (g *RdpClient) SetKeyboardType(keyboardType string)

SetKeyboardType sets the keyboard type by name (e.g. "IBM_101_102_KEYS"). Must be called before Login.

func (*RdpClient) SetQueueDepthHint

func (g *RdpClient) SetQueueDepthHint(depth uint32)

SetQueueDepthHint controls the frame-rate and encoding quality reported to the server via the RDPGFX FRAME_ACKNOWLEDGE queueDepth field (MS-RDPEGFX 2.2.2.8).

A higher value signals a larger client decode backlog, causing the server to slow down or reduce H.264/RFX encoding quality. 0 (default) means "report the real decode-queue length" — no artificial throttling.

Typical values: 0 = off, 10–50 = moderate throttle, 100+ = heavy throttle. Use 0xFFFFFFFF to pause new frames entirely (the stream resumes when hint is reduced or cleared).

func (*RdpClient) SetResolution

func (g *RdpClient) SetResolution(width, height int)

SetResolution requests a desktop resolution change via the MS-RDPEDISP Display Update Virtual Channel. The server will reshape the desktop to the given dimensions and send a fresh RDPGFX ResetGraphics command.

width must be even and both width and height must be >= 200. This method is a no-op when the RDPEDISP channel has not been established (e.g. when the server does not support it).

func (*RdpClient) Width

func (g *RdpClient) Width() int

Directories

Path Synopsis
cliprdr
Generic, OS-independent clipboard support with text format only
Generic, OS-independent clipboard support with text format only
rail
rail.go
rail.go
rdpedisp
Package rdpedisp implements the RDP Display Update Virtual Channel (MS-RDPEDISP), which allows the client to request a resolution change while connected.
Package rdpedisp implements the RDP Display Update Virtual Channel (MS-RDPEDISP), which allows the client to request a resolution change while connected.
rdpsnd
Package rdpsnd implements the RDPSND (Audio Output Virtual Channel Extension) protocol (MS-RDPEA) for server-to-client audio redirection.
Package rdpsnd implements the RDPSND (Audio Output Virtual Channel Extension) protocol (MS-RDPEA) for server-to-client audio redirection.
protocol
lic
nla
pdu
sec

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL