Complete browser-based remote desktop client built from protocol analysis: - Protobuf message definitions (75+ message types, 10 enums) - NaCl encryption (Ed25519 + Curve25519 ECDH + XSalsa20-Poly1305) - WebSocket signaling (hbbs) + relay (hbbr) connection lifecycle - VP8/VP9/AV1 video decoding via ogvjs WASM + yuv-canvas WebGL - Opus audio decoding via libopus WASM + Web Audio API - Full mouse/keyboard input forwarding to protobuf events - Connection dialog, status bar, toolbar, log panel UI Also tracked web_deps (codec libraries) in rustdesk-as-ref/
1210 lines
28 KiB
JavaScript
1210 lines
28 KiB
JavaScript
/**
|
|
* RustDesk Standalone Web Client - Protobuf Message Definitions
|
|
*
|
|
* This module defines all protobuf messages used in the RustDesk protocol
|
|
* using protobuf.js programmatic definitions (parsed from .proto text format).
|
|
*/
|
|
|
|
const RDProto = (() => {
|
|
// Embedded proto definition - complete RustDesk protocol
|
|
const PROTO_TEXT = `
|
|
syntax = "proto3";
|
|
package hbb;
|
|
|
|
// ===== Enums =====
|
|
|
|
enum NatType {
|
|
UNKNOWN_NAT = 0;
|
|
ASYMMETRIC = 1;
|
|
SYMMETRIC = 2;
|
|
}
|
|
|
|
enum ConnType {
|
|
DEFAULT_CONN = 0;
|
|
FILE_TRANSFER = 1;
|
|
PORT_FORWARD = 2;
|
|
RDP = 3;
|
|
VIEW_CAMERA = 4;
|
|
TERMINAL = 5;
|
|
}
|
|
|
|
enum ImageQuality {
|
|
NotSet = 0;
|
|
Low = 2;
|
|
Balanced = 3;
|
|
Best = 4;
|
|
}
|
|
|
|
enum BoolOption {
|
|
NotSet = 0;
|
|
Yes = 1;
|
|
No = 2;
|
|
}
|
|
|
|
enum PreferCodec {
|
|
Auto = 0;
|
|
VP9 = 1;
|
|
H264 = 2;
|
|
H265 = 3;
|
|
VP8 = 4;
|
|
AV1 = 5;
|
|
}
|
|
|
|
enum Chroma {
|
|
I420 = 0;
|
|
I444 = 1;
|
|
}
|
|
|
|
enum KeyboardMode {
|
|
Legacy = 0;
|
|
Map = 1;
|
|
Translate = 2;
|
|
Auto = 3;
|
|
}
|
|
|
|
enum ControlKey {
|
|
Unknown = 0;
|
|
Alt = 1;
|
|
Backspace = 2;
|
|
CapsLock = 3;
|
|
Control = 4;
|
|
Delete = 5;
|
|
DownArrow = 6;
|
|
End = 7;
|
|
Escape = 8;
|
|
F1 = 9;
|
|
F10 = 10;
|
|
F11 = 11;
|
|
F12 = 12;
|
|
F2 = 13;
|
|
F3 = 14;
|
|
F4 = 15;
|
|
F5 = 16;
|
|
F6 = 17;
|
|
F7 = 18;
|
|
F8 = 19;
|
|
F9 = 20;
|
|
Home = 21;
|
|
LeftArrow = 22;
|
|
Meta = 23;
|
|
PageDown = 24;
|
|
PageUp = 25;
|
|
Return = 26;
|
|
RightArrow = 27;
|
|
Shift = 28;
|
|
Space = 29;
|
|
Tab = 30;
|
|
UpArrow = 31;
|
|
Numpad0 = 32;
|
|
Numpad1 = 33;
|
|
Numpad2 = 34;
|
|
Numpad3 = 35;
|
|
Numpad4 = 36;
|
|
Numpad5 = 37;
|
|
Numpad6 = 38;
|
|
Numpad7 = 39;
|
|
Numpad8 = 40;
|
|
Numpad9 = 41;
|
|
NumpadAdd = 42;
|
|
NumpadDecimal = 43;
|
|
NumpadDelete = 44;
|
|
NumpadDivide = 45;
|
|
NumpadEnter = 46;
|
|
NumpadMultiply = 47;
|
|
NumpadSubtract = 48;
|
|
Insert = 49;
|
|
LeftControl = 50;
|
|
LeftShift = 51;
|
|
LeftAlt = 52;
|
|
LeftMeta = 53;
|
|
RightControl = 54;
|
|
RightShift = 55;
|
|
RightAlt = 56;
|
|
RightMeta = 57;
|
|
VolumeDown = 58;
|
|
VolumeMute = 59;
|
|
VolumeUp = 60;
|
|
Win = 61;
|
|
WakeUp = 62;
|
|
NumLock = 63;
|
|
ScrollLock = 64;
|
|
Print = 65;
|
|
PrintScreen = 66;
|
|
CtrlAltDel = 100;
|
|
LockScreen = 101;
|
|
}
|
|
|
|
// ===== Rendezvous Protocol =====
|
|
|
|
message RendezvousMessage {
|
|
oneof union {
|
|
RegisterPeer register_peer = 6;
|
|
RegisterPeerResponse register_peer_response = 7;
|
|
PunchHoleRequest punch_hole_request = 8;
|
|
PunchHole punch_hole = 9;
|
|
PunchHoleSent punch_hole_sent = 10;
|
|
PunchHoleResponse punch_hole_response = 11;
|
|
FetchLocalAddr fetch_local_addr = 12;
|
|
LocalAddr local_addr = 13;
|
|
ConfigUpdate configure_update = 14;
|
|
RegisterPk register_pk = 15;
|
|
RegisterPkResponse register_pk_response = 16;
|
|
SoftwareUpdate software_update = 17;
|
|
RequestRelay request_relay = 18;
|
|
RelayResponse relay_response = 19;
|
|
TestNatRequest test_nat_request = 20;
|
|
TestNatResponse test_nat_response = 21;
|
|
PeerDiscovery peer_discovery = 22;
|
|
OnlineRequest online_request = 23;
|
|
OnlineResponse online_response = 24;
|
|
KeyExchange key_exchange = 25;
|
|
HealthCheck hc = 26;
|
|
HttpProxyRequest http_proxy_request = 27;
|
|
HttpProxyResponse http_proxy_response = 28;
|
|
}
|
|
}
|
|
|
|
message RegisterPeer {
|
|
string id = 1;
|
|
uint32 serial = 2;
|
|
}
|
|
|
|
message RegisterPeerResponse {
|
|
bool request_pk = 1;
|
|
}
|
|
|
|
message RegisterPk {
|
|
string id = 1;
|
|
string uuid = 2;
|
|
bytes pk = 3;
|
|
string old_id = 4;
|
|
bool no_register_device = 5;
|
|
}
|
|
|
|
message RegisterPkResponse {
|
|
enum Result {
|
|
OK = 0;
|
|
UUID_MISMATCH = 2;
|
|
ID_EXISTS = 3;
|
|
TOO_FREQUENT = 4;
|
|
INVALID_ID_FORMAT = 5;
|
|
NOT_SUPPORT = 6;
|
|
SERVER_ERROR = 7;
|
|
}
|
|
Result result = 1;
|
|
}
|
|
|
|
message PunchHoleRequest {
|
|
string id = 1;
|
|
NatType nat_type = 2;
|
|
string licence_key = 3;
|
|
ConnType conn_type = 4;
|
|
string token = 5;
|
|
string version = 6;
|
|
uint32 udp_port = 7;
|
|
bool force_relay = 8;
|
|
string socket_addr_v6 = 9;
|
|
}
|
|
|
|
message PunchHole {
|
|
string socket_addr = 1;
|
|
string relay_server = 2;
|
|
NatType nat_type = 3;
|
|
uint32 udp_port = 4;
|
|
bool force_relay = 5;
|
|
string socket_addr_v6 = 6;
|
|
}
|
|
|
|
message PunchHoleSent {
|
|
string socket_addr = 1;
|
|
string id = 2;
|
|
string relay_server = 3;
|
|
NatType nat_type = 4;
|
|
string version = 5;
|
|
uint32 udp_port = 6;
|
|
string socket_addr_v6 = 7;
|
|
}
|
|
|
|
message PunchHoleResponse {
|
|
string socket_addr = 1;
|
|
bytes pk = 2;
|
|
enum Failure {
|
|
ID_NOT_EXIST = 0;
|
|
OFFLINE = 2;
|
|
LICENSE_MISMATCH = 3;
|
|
LICENSE_OVERUSE = 4;
|
|
}
|
|
Failure failure = 3;
|
|
string relay_server = 4;
|
|
oneof union {
|
|
NatType nat_type = 5;
|
|
bool is_local = 6;
|
|
}
|
|
string other_failure = 7;
|
|
string feedback = 8;
|
|
bool is_udp = 9;
|
|
uint32 udp_port = 10;
|
|
string socket_addr_v6 = 11;
|
|
}
|
|
|
|
message RequestRelay {
|
|
string id = 1;
|
|
string uuid = 2;
|
|
string socket_addr = 3;
|
|
string relay_server = 4;
|
|
bool secure = 5;
|
|
string licence_key = 6;
|
|
ConnType conn_type = 7;
|
|
string token = 8;
|
|
}
|
|
|
|
message RelayResponse {
|
|
string socket_addr = 1;
|
|
string uuid = 2;
|
|
string relay_server = 3;
|
|
oneof union {
|
|
string id = 4;
|
|
bytes pk = 5;
|
|
}
|
|
string refuse_reason = 6;
|
|
string version = 7;
|
|
string feedback = 8;
|
|
string socket_addr_v6 = 9;
|
|
uint32 udp_port = 10;
|
|
}
|
|
|
|
message OnlineRequest {
|
|
string id = 1;
|
|
repeated string peers = 2;
|
|
}
|
|
|
|
message OnlineResponse {
|
|
bytes states = 1;
|
|
}
|
|
|
|
message KeyExchange {
|
|
repeated bytes keys = 1;
|
|
}
|
|
|
|
message FetchLocalAddr {
|
|
string socket_addr = 1;
|
|
string relay_server = 2;
|
|
string socket_addr_v6 = 3;
|
|
}
|
|
|
|
message LocalAddr {
|
|
string socket_addr = 1;
|
|
string local_addr = 2;
|
|
string relay_server = 3;
|
|
string id = 4;
|
|
string version = 5;
|
|
string socket_addr_v6 = 6;
|
|
}
|
|
|
|
message ConfigUpdate {
|
|
uint32 serial = 1;
|
|
repeated string rendezvous_servers = 2;
|
|
}
|
|
|
|
message SoftwareUpdate {
|
|
string url = 1;
|
|
}
|
|
|
|
message TestNatRequest {
|
|
uint32 serial = 1;
|
|
}
|
|
|
|
message TestNatResponse {
|
|
uint32 port = 1;
|
|
ConfigUpdate cu = 2;
|
|
}
|
|
|
|
message PeerDiscovery {
|
|
string cmd = 1;
|
|
string mac = 2;
|
|
string id = 3;
|
|
string username = 4;
|
|
string hostname = 5;
|
|
string platform = 6;
|
|
string misc = 7;
|
|
}
|
|
|
|
message HealthCheck {
|
|
string token = 1;
|
|
}
|
|
|
|
message HttpProxyRequest {
|
|
string method = 1;
|
|
string path = 2;
|
|
repeated HeaderEntry headers = 3;
|
|
bytes body = 4;
|
|
}
|
|
|
|
message HttpProxyResponse {
|
|
int32 status = 1;
|
|
repeated HeaderEntry headers = 2;
|
|
bytes body = 3;
|
|
string error = 4;
|
|
}
|
|
|
|
message HeaderEntry {
|
|
string name = 1;
|
|
string value = 2;
|
|
}
|
|
|
|
// ===== Session Protocol =====
|
|
|
|
message Message {
|
|
oneof union {
|
|
SignedId signed_id = 3;
|
|
PublicKey public_key = 4;
|
|
TestDelay test_delay = 5;
|
|
VideoFrame video_frame = 6;
|
|
LoginRequest login_request = 7;
|
|
LoginResponse login_response = 8;
|
|
Hash hash = 9;
|
|
MouseEvent mouse_event = 10;
|
|
AudioFrame audio_frame = 11;
|
|
CursorData cursor_data = 12;
|
|
CursorPosition cursor_position = 13;
|
|
uint64 cursor_id = 14;
|
|
KeyEvent key_event = 15;
|
|
Clipboard clipboard = 16;
|
|
FileAction file_action = 17;
|
|
FileResponse file_response = 18;
|
|
Misc misc = 19;
|
|
Cliprdr cliprdr = 20;
|
|
MessageBox message_box = 21;
|
|
SwitchSidesResponse switch_sides_response = 22;
|
|
VoiceCallRequest voice_call_request = 23;
|
|
VoiceCallResponse voice_call_response = 24;
|
|
PeerInfo peer_info = 25;
|
|
PointerDeviceEvent pointer_device_event = 26;
|
|
Auth2FA auth_2fa = 27;
|
|
MultiClipboards multi_clipboards = 28;
|
|
ScreenshotRequest screenshot_request = 29;
|
|
ScreenshotResponse screenshot_response = 30;
|
|
TerminalAction terminal_action = 31;
|
|
TerminalResponse terminal_response = 32;
|
|
}
|
|
}
|
|
|
|
message SignedId {
|
|
bytes id = 1;
|
|
}
|
|
|
|
message IdPk {
|
|
string id = 1;
|
|
bytes pk = 2;
|
|
}
|
|
|
|
message PublicKey {
|
|
bytes asymmetric_value = 1;
|
|
bytes symmetric_value = 2;
|
|
}
|
|
|
|
message Hash {
|
|
string salt = 1;
|
|
string challenge = 2;
|
|
}
|
|
|
|
message LoginRequest {
|
|
string username = 1;
|
|
bytes password = 2;
|
|
string my_id = 4;
|
|
string my_name = 5;
|
|
OptionMessage option = 6;
|
|
oneof union {
|
|
FileTransfer file_transfer = 7;
|
|
PortForward port_forward = 8;
|
|
ViewCamera view_camera = 15;
|
|
Terminal terminal = 16;
|
|
}
|
|
bool video_ack_required = 9;
|
|
uint64 session_id = 10;
|
|
string version = 11;
|
|
OSLogin os_login = 12;
|
|
string my_platform = 13;
|
|
bytes hwid = 14;
|
|
string avatar = 17;
|
|
}
|
|
|
|
message LoginResponse {
|
|
oneof union {
|
|
string error = 1;
|
|
PeerInfo peer_info = 2;
|
|
}
|
|
bool enable_trusted_devices = 3;
|
|
}
|
|
|
|
message PeerInfo {
|
|
string username = 1;
|
|
string hostname = 2;
|
|
string platform = 3;
|
|
repeated DisplayInfo displays = 4;
|
|
int32 current_display = 5;
|
|
bool sas_enabled = 6;
|
|
string version = 7;
|
|
Features features = 8;
|
|
SupportedEncoding encoding = 9;
|
|
SupportedResolutions resolutions = 10;
|
|
string platform_additions = 11;
|
|
WindowsSessions windows_sessions = 12;
|
|
}
|
|
|
|
message DisplayInfo {
|
|
sint32 x = 1;
|
|
sint32 y = 2;
|
|
int32 width = 3;
|
|
int32 height = 4;
|
|
string name = 5;
|
|
bool online = 6;
|
|
bool cursor_embedded = 7;
|
|
Resolution original_resolution = 8;
|
|
double scale = 9;
|
|
}
|
|
|
|
message Resolution {
|
|
int32 width = 1;
|
|
int32 height = 2;
|
|
}
|
|
|
|
message Features {
|
|
bool privacy_mode = 1;
|
|
bool terminal = 2;
|
|
}
|
|
|
|
message SupportedEncoding {
|
|
bool h264 = 1;
|
|
bool h265 = 2;
|
|
bool vp8 = 3;
|
|
bool av1 = 4;
|
|
CodecAbility i444 = 5;
|
|
}
|
|
|
|
message CodecAbility {
|
|
bool vp8 = 1;
|
|
bool vp9 = 2;
|
|
bool av1 = 3;
|
|
bool h264 = 4;
|
|
bool h265 = 5;
|
|
}
|
|
|
|
message SupportedResolutions {
|
|
repeated Resolution resolutions = 1;
|
|
}
|
|
|
|
message WindowsSessions {
|
|
repeated WindowsSession sessions = 1;
|
|
uint32 current_sid = 2;
|
|
}
|
|
|
|
message WindowsSession {
|
|
uint32 sid = 1;
|
|
string name = 2;
|
|
}
|
|
|
|
message OptionMessage {
|
|
ImageQuality image_quality = 1;
|
|
BoolOption lock_after_session_end = 2;
|
|
BoolOption show_remote_cursor = 3;
|
|
BoolOption privacy_mode = 4;
|
|
int32 custom_image_quality = 5;
|
|
BoolOption disable_audio = 6;
|
|
BoolOption enable_file_transfer = 7;
|
|
BoolOption clipboard = 8;
|
|
SupportedDecoding supported_decoding = 9;
|
|
int32 custom_fps = 10;
|
|
BoolOption follow_remote_cursor = 11;
|
|
BoolOption follow_remote_window = 12;
|
|
BoolOption disable_camera = 13;
|
|
BoolOption block_input = 14;
|
|
BoolOption keyboard = 15;
|
|
BoolOption terminal_persistent = 16;
|
|
BoolOption show_my_cursor = 17;
|
|
}
|
|
|
|
message SupportedDecoding {
|
|
bool ability_vp9 = 1;
|
|
bool ability_h264 = 2;
|
|
bool ability_h265 = 3;
|
|
bool ability_vp8 = 4;
|
|
bool ability_av1 = 5;
|
|
PreferCodec prefer = 6;
|
|
CodecAbility i444 = 7;
|
|
Chroma prefer_chroma = 8;
|
|
}
|
|
|
|
message FileTransfer {
|
|
string dir = 1;
|
|
bool show_hidden = 2;
|
|
}
|
|
|
|
message PortForward {
|
|
string host = 1;
|
|
int32 port = 2;
|
|
}
|
|
|
|
message ViewCamera {}
|
|
|
|
message Terminal {
|
|
string service_id = 1;
|
|
}
|
|
|
|
message OSLogin {
|
|
string username = 1;
|
|
string password = 2;
|
|
}
|
|
|
|
// ===== Video =====
|
|
|
|
message VideoFrame {
|
|
oneof union {
|
|
EncodedVideoFrames vp9s = 6;
|
|
RGB rgb = 7;
|
|
YUV yuv = 8;
|
|
EncodedVideoFrames h264s = 10;
|
|
EncodedVideoFrames h265s = 11;
|
|
EncodedVideoFrames vp8s = 12;
|
|
EncodedVideoFrames av1s = 13;
|
|
}
|
|
int32 display = 14;
|
|
}
|
|
|
|
message EncodedVideoFrames {
|
|
repeated EncodedVideoFrame frames = 1;
|
|
}
|
|
|
|
message EncodedVideoFrame {
|
|
bytes data = 1;
|
|
bool key = 2;
|
|
int64 pts = 3;
|
|
}
|
|
|
|
message RGB {
|
|
bool compress = 1;
|
|
}
|
|
|
|
message YUV {
|
|
bool compress = 1;
|
|
int32 stride = 2;
|
|
}
|
|
|
|
// ===== Audio =====
|
|
|
|
message AudioFrame {
|
|
bytes data = 1;
|
|
}
|
|
|
|
// ===== Input =====
|
|
|
|
message MouseEvent {
|
|
int32 mask = 1;
|
|
sint32 x = 2;
|
|
sint32 y = 3;
|
|
repeated ControlKey modifiers = 4;
|
|
}
|
|
|
|
message KeyEvent {
|
|
bool down = 1;
|
|
bool press = 2;
|
|
oneof union {
|
|
ControlKey control_key = 3;
|
|
uint32 chr = 4;
|
|
uint32 unicode = 5;
|
|
string seq = 6;
|
|
uint32 win2win_hotkey = 7;
|
|
}
|
|
repeated ControlKey modifiers = 8;
|
|
KeyboardMode mode = 9;
|
|
}
|
|
|
|
// ===== Cursor =====
|
|
|
|
message CursorData {
|
|
uint64 id = 1;
|
|
sint32 hotx = 2;
|
|
sint32 hoty = 3;
|
|
int32 width = 4;
|
|
int32 height = 5;
|
|
bytes colors = 6;
|
|
}
|
|
|
|
message CursorPosition {
|
|
sint32 x = 1;
|
|
sint32 y = 2;
|
|
}
|
|
|
|
// ===== Misc =====
|
|
|
|
message Misc {
|
|
oneof union {
|
|
ChatMessage chat_message = 1;
|
|
SwitchDisplay switch_display = 2;
|
|
PermissionInfo permission_info = 3;
|
|
Option option = 4;
|
|
AudioFormat audio_format = 5;
|
|
string close_reason = 6;
|
|
bool refresh_video = 7;
|
|
bool video_received = 8;
|
|
BackNotification back_notification = 9;
|
|
bool restart_remote_device = 10;
|
|
bool uac = 11;
|
|
bool foreground_window_elevated = 12;
|
|
bool stop_service = 13;
|
|
ElevationRequest elevation_request = 14;
|
|
bool elevation_response = 15;
|
|
bool portable_service_running = 16;
|
|
SwitchSidesRequest switch_sides_request = 17;
|
|
SwitchBack switch_back = 18;
|
|
ChangeResolution change_resolution = 19;
|
|
PluginRequest plugin_request = 20;
|
|
PluginFailure plugin_failure = 21;
|
|
bool full_speed_fps = 22;
|
|
bool auto_adjust_fps = 23;
|
|
bool client_record_status = 24;
|
|
CaptureDisplays capture_displays = 25;
|
|
int32 refresh_video_display = 26;
|
|
ToggleVirtualDisplay toggle_virtual_display = 27;
|
|
TogglePrivacyMode toggle_privacy_mode = 28;
|
|
SupportedEncoding supported_encoding = 29;
|
|
string selected_sid = 30;
|
|
DisplayResolution change_display_resolution = 31;
|
|
MessageQuery message_query = 32;
|
|
bool follow_current_display = 33;
|
|
}
|
|
}
|
|
|
|
message ChatMessage { string text = 1; }
|
|
|
|
message SwitchDisplay {
|
|
int32 display = 1;
|
|
sint32 x = 2;
|
|
sint32 y = 3;
|
|
int32 width = 4;
|
|
int32 height = 5;
|
|
bool cursor_embedded = 6;
|
|
SupportedResolutions resolutions = 7;
|
|
Resolution original_resolution = 8;
|
|
}
|
|
|
|
message PermissionInfo {
|
|
enum Permission {
|
|
Keyboard = 0;
|
|
Clipboard = 2;
|
|
Audio = 3;
|
|
File = 4;
|
|
Restart = 5;
|
|
Recording = 6;
|
|
BlockInput = 7;
|
|
}
|
|
Permission permission = 1;
|
|
bool enabled = 2;
|
|
}
|
|
|
|
message Option { string name = 1; string value = 2; }
|
|
|
|
message AudioFormat {
|
|
uint32 sample_rate = 1;
|
|
uint32 channels = 2;
|
|
}
|
|
|
|
message BackNotification {
|
|
oneof union {
|
|
PrivacyModeState privacy_mode_state = 1;
|
|
BlockInputState block_input_state = 2;
|
|
}
|
|
string details = 3;
|
|
string impl_key = 4;
|
|
enum PrivacyModeState { OFF = 0; ON = 1; BLANK_SCREEN = 2; }
|
|
enum BlockInputState { RUNNING = 0; ON = 1; OFF = 2; }
|
|
}
|
|
|
|
message ElevationRequest {
|
|
oneof union { bool direct = 1; ElevationRequestWithLogon logon = 2; }
|
|
}
|
|
|
|
message ElevationRequestWithLogon { string username = 1; string password = 2; }
|
|
|
|
message SwitchSidesRequest { bytes uuid = 1; }
|
|
message SwitchBack {}
|
|
|
|
message ChangeResolution { Resolution resolution = 1; }
|
|
|
|
message PluginRequest { string id = 1; bytes content = 2; }
|
|
message PluginFailure { string id = 1; string name = 2; string msg = 3; }
|
|
|
|
message CaptureDisplays {
|
|
repeated int32 set = 1;
|
|
repeated int32 add = 2;
|
|
repeated int32 sub = 3;
|
|
}
|
|
|
|
message ToggleVirtualDisplay { int32 display = 1; bool on = 2; }
|
|
|
|
message TogglePrivacyMode { string impl_key = 1; bool on = 2; }
|
|
|
|
message DisplayResolution { int32 display = 1; Resolution resolution = 2; }
|
|
message MessageQuery { int32 switch_display = 1; }
|
|
|
|
// ===== Clipboard =====
|
|
|
|
message Clipboard {
|
|
enum ClipboardFormat {
|
|
Text = 0;
|
|
Rtf = 1;
|
|
Html = 2;
|
|
ImageRgba = 21;
|
|
ImagePng = 22;
|
|
ImageSvg = 23;
|
|
Special = 31;
|
|
}
|
|
bool compress = 1;
|
|
repeated ClipboardContent contents = 2;
|
|
}
|
|
|
|
message ClipboardContent {
|
|
Clipboard.ClipboardFormat format = 1;
|
|
bytes data = 2;
|
|
}
|
|
|
|
// ===== File Transfer =====
|
|
|
|
message FileAction {
|
|
oneof union {
|
|
ReadDir read_dir = 1;
|
|
FileTransferSendRequest send = 2;
|
|
FileTransferReceiveRequest receive = 3;
|
|
FileDirCreate create = 4;
|
|
FileRemoveDir remove_dir = 5;
|
|
FileRemoveFile remove_file = 6;
|
|
ReadAllFiles all_files = 7;
|
|
FileTransferCancel cancel = 8;
|
|
FileTransferSendConfirmRequest send_confirm = 9;
|
|
FileRename rename = 10;
|
|
ReadEmptyDirs read_empty_dirs = 11;
|
|
}
|
|
}
|
|
|
|
message ReadDir { string path = 1; bool include_hidden = 2; }
|
|
message ReadAllFiles { string id = 1; string path = 2; bool include_hidden = 3; }
|
|
message ReadEmptyDirs { string id = 1; string path = 2; bool include_hidden = 3; }
|
|
message FileDirCreate { string id = 1; string path = 2; }
|
|
message FileRemoveDir { string id = 1; string path = 2; bool recursive = 3; }
|
|
message FileRemoveFile { string id = 1; string path = 2; sint32 file_num = 3; }
|
|
message FileRename { string id = 1; string path = 2; string new_name = 3; }
|
|
|
|
message FileTransferSendRequest {
|
|
string id = 1;
|
|
string path = 2;
|
|
bool include_hidden = 3;
|
|
sint32 file_num = 4;
|
|
enum FileType { Generic = 0; Printer = 1; }
|
|
FileType file_type = 5;
|
|
}
|
|
|
|
message FileTransferReceiveRequest {
|
|
string id = 1;
|
|
string path = 2;
|
|
repeated FileEntry files = 3;
|
|
sint32 file_num = 4;
|
|
uint64 total_size = 5;
|
|
}
|
|
|
|
message FileTransferSendConfirmRequest {
|
|
string id = 1;
|
|
sint32 file_num = 2;
|
|
oneof union {
|
|
bool skip = 3;
|
|
uint32 offset_blk = 4;
|
|
}
|
|
}
|
|
|
|
message FileTransferCancel { string id = 1; }
|
|
|
|
message FileResponse {
|
|
oneof union {
|
|
FileDirectory dir = 1;
|
|
FileTransferBlock block = 2;
|
|
FileTransferError error = 3;
|
|
FileTransferDone done = 4;
|
|
FileTransferDigest digest = 5;
|
|
ReadEmptyDirsResponse empty_dirs = 6;
|
|
}
|
|
}
|
|
|
|
message FileDirectory {
|
|
string id = 1;
|
|
string path = 2;
|
|
repeated FileEntry entries = 3;
|
|
}
|
|
|
|
message FileEntry {
|
|
enum FileType {
|
|
Dir = 0;
|
|
DirLink = 2;
|
|
DirDrive = 3;
|
|
File = 4;
|
|
FileLink = 5;
|
|
}
|
|
FileType entry_type = 1;
|
|
string name = 2;
|
|
bool is_hidden = 3;
|
|
uint64 size = 4;
|
|
uint64 modified_time = 5;
|
|
}
|
|
|
|
message FileTransferBlock {
|
|
string id = 1;
|
|
sint32 file_num = 2;
|
|
bytes data = 3;
|
|
bool compressed = 4;
|
|
uint32 blk_id = 5;
|
|
}
|
|
|
|
message FileTransferError { string id = 1; string error = 2; sint32 file_num = 3; }
|
|
message FileTransferDone { string id = 1; sint32 file_num = 2; }
|
|
|
|
message FileTransferDigest {
|
|
string id = 1;
|
|
sint32 file_num = 2;
|
|
uint64 last_modified = 3;
|
|
uint64 file_size = 4;
|
|
bool is_upload = 5;
|
|
bool is_identical = 6;
|
|
uint64 transferred_size = 7;
|
|
bool is_resume = 8;
|
|
}
|
|
|
|
message ReadEmptyDirsResponse {
|
|
string path = 1;
|
|
repeated FileDirectory empty_dirs = 2;
|
|
}
|
|
|
|
// ===== Test Delay =====
|
|
|
|
message TestDelay {
|
|
int64 time = 1;
|
|
bool from_client = 2;
|
|
int32 last_delay = 3;
|
|
int32 target_bitrate = 4;
|
|
}
|
|
|
|
// ===== Auth =====
|
|
|
|
message Auth2FA { string code = 1; bytes hwid = 2; }
|
|
|
|
// ===== Terminal =====
|
|
|
|
message TerminalAction {
|
|
oneof union {
|
|
OpenTerminal open = 1;
|
|
TerminalData data = 2;
|
|
ResizeTerminal resize = 3;
|
|
CloseTerminal close = 4;
|
|
}
|
|
}
|
|
|
|
message TerminalResponse {
|
|
oneof union {
|
|
TerminalOpened opened = 1;
|
|
TerminalData data = 2;
|
|
TerminalClosed closed = 3;
|
|
TerminalError error = 4;
|
|
}
|
|
}
|
|
|
|
message OpenTerminal { int32 terminal_id = 1; uint32 rows = 2; uint32 cols = 3; }
|
|
message TerminalData { int32 terminal_id = 1; bytes data = 2; bool compressed = 3; }
|
|
message ResizeTerminal { int32 terminal_id = 1; uint32 rows = 2; uint32 cols = 3; }
|
|
message CloseTerminal { int32 terminal_id = 1; }
|
|
message TerminalOpened { int32 terminal_id = 1; bool success = 2; string message = 3; }
|
|
message TerminalClosed { int32 terminal_id = 1; int32 exit_code = 2; }
|
|
message TerminalError { int32 terminal_id = 1; string message = 2; }
|
|
|
|
// ===== Others =====
|
|
|
|
message MessageBox {
|
|
string msgtype = 1;
|
|
string title = 2;
|
|
string text = 3;
|
|
string link = 4;
|
|
}
|
|
|
|
message PointerDeviceEvent {
|
|
TouchEvent touch = 1;
|
|
repeated ControlKey modifiers = 2;
|
|
}
|
|
|
|
message TouchEvent {
|
|
oneof union {
|
|
TouchScaleUpdate scale_update = 1;
|
|
TouchPanStart pan_start = 2;
|
|
TouchPanUpdate pan_update = 3;
|
|
TouchPanEnd pan_end = 4;
|
|
TouchEnd end = 5;
|
|
}
|
|
}
|
|
|
|
message TouchScaleUpdate { int32 scale = 1; }
|
|
message TouchPanStart { sint32 x = 1; sint32 y = 2; }
|
|
message TouchPanUpdate { sint32 x = 1; sint32 y = 2; }
|
|
message TouchPanEnd { sint32 x = 1; sint32 y = 2; }
|
|
message TouchEnd { sint32 x = 1; sint32 y = 2; }
|
|
|
|
message ScreenshotRequest { int32 display = 1; uint64 sid = 2; }
|
|
message ScreenshotResponse { uint64 sid = 1; string msg = 2; bytes data = 3; }
|
|
|
|
message SwitchSidesResponse { bytes uuid = 1; LoginRequest lr = 2; }
|
|
|
|
message VoiceCallRequest { int64 req_timestamp = 1; bool is_connect = 2; }
|
|
message VoiceCallResponse { bool accepted = 1; int64 req_timestamp = 2; int64 ack_timestamp = 3; }
|
|
|
|
message MultiClipboards { repeated Clipboard clipboards = 1; }
|
|
|
|
message Cliprdr {
|
|
oneof union {
|
|
CliprdrMonitorReady ready = 1;
|
|
CliprdrServerFormatList format_list = 2;
|
|
CliprdrServerFormatListResponse format_list_response = 3;
|
|
CliprdrServerFormatDataRequest format_data_request = 4;
|
|
CliprdrServerFormatDataResponse format_data_response = 5;
|
|
CliprdrFileContentsRequest file_contents_request = 6;
|
|
CliprdrFileContentsResponse file_contents_response = 7;
|
|
CliprdrTryEmpty try_empty = 8;
|
|
CliprdrFiles files = 9;
|
|
}
|
|
}
|
|
|
|
message CliprdrMonitorReady {}
|
|
message CliprdrServerFormatList { repeated CliprdrFormat formats = 1; }
|
|
message CliprdrFormat { int32 id = 1; string format = 2; }
|
|
message CliprdrServerFormatListResponse { int32 msg_flags = 1; }
|
|
message CliprdrServerFormatDataRequest { int32 requested_format_id = 1; }
|
|
message CliprdrServerFormatDataResponse { int32 msg_flags = 1; bytes format_data = 2; }
|
|
message CliprdrTryEmpty {}
|
|
message CliprdrFiles { repeated CliprdrFile files = 1; }
|
|
message CliprdrFile { string name = 1; uint64 size = 2; }
|
|
message CliprdrFileContentsRequest {
|
|
uint32 stream_id = 1;
|
|
uint32 list_index = 2;
|
|
uint32 dw_flags = 3;
|
|
uint64 n_position_low = 4;
|
|
uint64 n_position_high = 5;
|
|
uint32 cb_requested = 6;
|
|
bool have_clip_data_id = 7;
|
|
uint32 clip_data_id = 8;
|
|
}
|
|
message CliprdrFileContentsResponse {
|
|
int32 msg_flags = 1;
|
|
uint32 stream_id = 2;
|
|
bytes requested_data = 3;
|
|
}
|
|
`;
|
|
|
|
let root = null;
|
|
let RendezvousMessage = null;
|
|
let Message = null;
|
|
|
|
/**
|
|
* Initialize protobuf definitions. Must be called after protobuf.js is loaded.
|
|
*/
|
|
function init() {
|
|
try {
|
|
root = protobuf.Root.fromJSON(protobuf.parse(PROTO_TEXT).toJSON());
|
|
RendezvousMessage = root.lookupType('hbb.RendezvousMessage');
|
|
Message = root.lookupType('hbb.Message');
|
|
console.log('[RDProto] Protocol definitions loaded successfully');
|
|
return true;
|
|
} catch (e) {
|
|
console.error('[RDProto] Failed to load protocol definitions:', e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lookup a message type by name
|
|
*/
|
|
function lookup(name) {
|
|
return root.lookupType(`hbb.${name}`);
|
|
}
|
|
|
|
/**
|
|
* Encode a RendezvousMessage to bytes
|
|
*/
|
|
function encodeRendezvousMsg(data) {
|
|
const errMsg = RendezvousMessage.verify(data);
|
|
if (errMsg) throw new Error(`RendezvousMessage verify error: ${errMsg}`);
|
|
const msg = RendezvousMessage.create(data);
|
|
return RendezvousMessage.encode(msg).finish();
|
|
}
|
|
|
|
/**
|
|
* Decode a RendezvousMessage from bytes
|
|
*/
|
|
function decodeRendezvousMsg(bytes) {
|
|
return RendezvousMessage.decode(new Uint8Array(bytes));
|
|
}
|
|
|
|
/**
|
|
* Encode a Message to bytes
|
|
*/
|
|
function encodeMsg(data) {
|
|
const errMsg = Message.verify(data);
|
|
if (errMsg) throw new Error(`Message verify error: ${errMsg}`);
|
|
const msg = Message.create(data);
|
|
return Message.encode(msg).finish();
|
|
}
|
|
|
|
/**
|
|
* Decode a Message from bytes
|
|
*/
|
|
function decodeMsg(bytes) {
|
|
return Message.decode(new Uint8Array(bytes));
|
|
}
|
|
|
|
/**
|
|
* Get the active union field name from a message
|
|
*/
|
|
function getActiveField(msg) {
|
|
if (msg.union === null || msg.union === undefined) return null;
|
|
// protobuf.js oneof: check which field is set
|
|
const oneofFields = msg.$type.oneofs;
|
|
if (oneofFields && oneofFields.union) {
|
|
return oneofFields.union.oneof.find(f => msg[f] != null);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Helper: Create a TestDelay message for latency measurement
|
|
*/
|
|
function createTestDelay(fromClient = true, lastDelay = 0) {
|
|
return {
|
|
test_delay: {
|
|
time: BigInt(Date.now()),
|
|
from_client: fromClient,
|
|
last_delay: lastDelay
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Helper: Create a MouseEvent
|
|
*/
|
|
function createMouseEvent(mask, x, y, modifiers = []) {
|
|
return {
|
|
mouse_event: {
|
|
mask: mask,
|
|
x: x,
|
|
y: y,
|
|
modifiers: modifiers
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Helper: Create a KeyEvent (legacy mode)
|
|
*/
|
|
function createKeyEvent(controlKey, down, press = false, modifiers = []) {
|
|
return {
|
|
key_event: {
|
|
control_key: controlKey,
|
|
down: down,
|
|
press: press,
|
|
modifiers: modifiers,
|
|
mode: 0 // Legacy
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Helper: Create a KeyEvent with character
|
|
*/
|
|
function createCharKeyEvent(chr, down, press = false, modifiers = []) {
|
|
return {
|
|
key_event: {
|
|
chr: chr,
|
|
down: down,
|
|
press: press,
|
|
modifiers: modifiers,
|
|
mode: 0 // Legacy
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Helper: Create a LoginRequest
|
|
*/
|
|
function createLoginRequest(myId, myName, password, peerPlatform, version) {
|
|
return {
|
|
login_request: {
|
|
my_id: myId,
|
|
my_name: myName,
|
|
password: password,
|
|
my_platform: peerPlatform || 'Web',
|
|
version: version || '1.3.6',
|
|
option: {
|
|
image_quality: 0, // NotSet
|
|
show_remote_cursor: 1, // Yes
|
|
privacy_mode: 0,
|
|
disable_audio: 0,
|
|
enable_file_transfer: 1,
|
|
clipboard: 1,
|
|
supported_decoding: {
|
|
ability_vp9: true,
|
|
ability_h264: false,
|
|
ability_h265: false,
|
|
ability_vp8: true,
|
|
ability_av1: true,
|
|
prefer: 0, // Auto
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Helper: Create a LoginRequest for password auth
|
|
*/
|
|
function createPasswordLogin(myId, myName, hashedPassword, peerPlatform, version) {
|
|
return createLoginRequest(myId, myName, hashedPassword, peerPlatform, version);
|
|
}
|
|
|
|
/**
|
|
* Helper: Create a Clipboard message
|
|
*/
|
|
function createClipboard(text) {
|
|
const encoder = new TextEncoder();
|
|
return {
|
|
clipboard: {
|
|
contents: [{
|
|
format: 0, // Text
|
|
data: encoder.encode(text)
|
|
}]
|
|
}
|
|
};
|
|
}
|
|
|
|
return {
|
|
init,
|
|
lookup,
|
|
encodeRendezvousMsg,
|
|
decodeRendezvousMsg,
|
|
encodeMsg,
|
|
decodeMsg,
|
|
getActiveField,
|
|
createTestDelay,
|
|
createMouseEvent,
|
|
createKeyEvent,
|
|
createCharKeyEvent,
|
|
createLoginRequest,
|
|
createPasswordLogin,
|
|
createClipboard,
|
|
// Expose type references
|
|
get Message() { return Message; },
|
|
get RendezvousMessage() { return RendezvousMessage; },
|
|
get root() { return root; }
|
|
};
|
|
})();
|