Files
0740063863 Keyboard shortcuts (#14933)
* fix web break introduced in 38f130071 fix(linux): enable mouse side buttons in remote sessions (#14848)

* feat(shortcuts): user-configurable keyboard shortcuts for session actions

  Adds a keyboard shortcut feature (Rust matcher + Dart UI + cross-language
  parity tests) that lets users bind combinations like Ctrl+Alt+Shift+P to
  session actions. Bindings are stored in LocalConfig under
  `keyboard-shortcuts`; the matcher gates dispatch on `enabled` and
  `pass_through` flags so flipping the master switch off is a hard stop.

  Wire-up summary:
  - src/keyboard/shortcuts.rs: matcher, default bindings, parity test against
    flutter/test/fixtures/default_keyboard_shortcuts.json
  - src/keyboard.rs: shortcut intercept in process_event{,_with_session},
    feature-gated to `flutter`; runs before key swapping so users bind to
    physical keys
  - src/flutter_ffi.rs: main_reload_keyboard_shortcuts +
    main_get_default_keyboard_shortcuts; reload_from_config seeded in main_init
  - flutter/lib/common/widgets/keyboard_shortcuts/: shared config page body,
    recording dialog, shortcut display formatter, action group registry
  - flutter/lib/desktop/pages/desktop_keyboard_shortcuts_page.dart and
    flutter/lib/mobile/pages/mobile_keyboard_shortcuts_page.dart: platform
    shells around the shared body
  - flutter/lib/models/shortcut_model.dart: per-session ShortcutModel +
    registerSessionShortcutActions for actions with no toolbar TToggleMenu /
    TRadioMenu (fullscreen, switch display/tab, close tab, voice call, etc.)
  - flutter/lib/common/widgets/toolbar.dart: optional `actionId` field on
    TToggleMenu / TRadioMenu, plus per-helper auto-register pass that wires
    tagged entries' existing onChanged into the ShortcutModel
  - flutter/test/keyboard_shortcuts_test.dart + fixtures: cross-language
    parity (default bindings, supported key vocabulary)

  Design principles applied during review:

  1. Additions are fine; modifications to original logic must be deliberate.
     Tagging an existing TToggleMenu entry with `actionId:` is an addition.
     Rewriting its onChanged to satisfy a new contract is a modification —
     and was reverted for every case where the original click behavior was
     working. Four closures were touched and then reverted (mobile View
     Mode, Privacy mode multi-impl, Relative mouse mode, Reverse mouse
     wheel); their shortcuts are wired via standalone closures in
     shortcut_model.dart instead.

  2. Toolbar auto-register is reserved for entries whose onChanged is
     inherently self-flipping — typically `sessionToggleOption(name)` where
     the named option is flipped in place and the input bool is unused. The
     register pass passes `!menu.value` from registration time, which is
     harmless under self-flipping but wrong for closures that consume the
     input bool directly. Tagging a non-self-flipping entry forces a closure
     rewrite; choose non-toolbar registration in that case.

  3. When shortcuts are disabled, toolbar behavior must be bit-for-bit
     unchanged. The matcher's `enabled`-gate already guarantees no
     dispatch; the auto-register pass is left unconditional (its only effect
     is HashMap operations on a separate ShortcutModel) so mid-session
     enable works without a reconnect. The trade-off is intentional and
     documented at the top of toolbarControls.

  4. Comments stay terse. Rationale lives in one place — the doc comment of
     the helper or registration site, not duplicated at every call site.

  5. Where an existing helper needs a new optional behavior (e.g.
     `_OptionCheckBox` gaining a tooltip slot), the new branch must reduce
     to byte-identical output for existing callers (`trailing == null`
     case → original `Expanded(Text)` layout). Verified.

  6. Action IDs and labels stay consistent. Renamed `reset_cursor` →
     `reset_canvas` so the action ID matches its user-facing label
     ("Reset canvas") and capability flag.

  Out-of-scope but included:
  - AGENTS.md: documents flutter_rust_bridge no-codegen workflow and the
    Web target's hand-written TS client, since both are load-bearing for
    any new FFI work.
  - remote_toolbar.dart: i18n fix for the per-monitor tooltip ("All
    monitors" / "Monitor #N"), unrelated to shortcuts but kept here.

* langs

* fix(keyboard): shortcuts, harden config and callback lifecycle

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat(keyboard): shortcuts, color of "Reset to defaults"

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat(keyboard): shortcuts, release keys before shortcut callback

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat(keyboard): shortcuts, debug web

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(keyboard): iPad, icon

Signed-off-by: fufesou <linlong1266@gmail.com>

* langs: add shortcut keys to az, gl, ur

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(keyboard): shortcuts, keep local modifiers and release keys on the right session

When a chord fired, the shortcut path reused release_remote_keys(), which
replays synthetic releases through process_event(). That cleared the local
MODIFIERS_STATE while the user still held the modifiers, so the next key of
a held chord no longer matched and leaked to the remote, and it sent the
releases to the globally current session instead of the one that typed the
chord.

The shortcut path now releases the remote keys itself: it sends through the
caller's session, and restores the modifiers it released on the remote as
still held locally. The press that fired is remembered until its release,
so its auto repeats and its release are consumed instead of reaching the
remote.

Also state the contract for unavailable actions: a bound chord always
belongs to RustDesk and is a no-op when the session has no handler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(keyboard): shortcuts, record the physical key

The recording dialog and the Web Flutter matcher named keys by
LogicalKeyboardKey, while the native matcher sees the physical key through
its USB HID usage and the Web JS matcher through KeyboardEvent.code. On
AZERTY, QWERTZ, Dvorak and similar layouts a recorded binding then did not
fire, or fired from another key.

Name keys by their physical position everywhere: physicalKeyName maps the
USB HID usage to the stored name. A new fixture pins every name to its USB
HID usage, checked on the Dart side and against rdev::usb_hid_key_from_code
on the Rust side, and a test covers AZERTY and QWERTZ events.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(keyboard): shortcuts, keep an empty binding list on re-enable

setEnabled seeded the defaults whenever the binding list was empty, so a
user who cleared every binding got all defaults back after disable and
enable. Seed only when the config has never held a bindings list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(keyboard): shortcuts, track every fired key until its release

FIRED_KEY held a single key, so overlapping shortcut keys broke it: with the
chord held, P then C overwrote P, P's release reached the remote, and a P
repeat could fire the screenshot again. A repeat after the modifiers were
released was also treated as a new press and leaked to the remote.

Keep a set of fired keys instead. Once a press fires, every event of that
key belongs to RustDesk until its release: repeats are consumed without
dispatching, whatever the modifiers do, and the release removes the key.
Entries whose release never arrives are dropped when a session view is
entered or a session is closed, not by guessing from the modifiers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(keyboard): shortcuts, keep fired keys per session

FIRED_KEYS was process-global while dispatch is per session. Closing or
entering any session cleared the fired keys of all of them, so a key still
held in another session leaked its release to that remote, and a key fired
in one session made the same key in another session look like a repeat.

Key the fired set by session: try_dispatch resolves the session first and
only looks at that session's keys, and session_close clears only the
closing session. Entering a session view no longer clears anything: it is
a pointer enter, and a fired key may still be physically held across it.
A key whose release is missed otherwise heals itself: its next press is
consumed as a repeat and that release removes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(shortcuts): preserve input state and refresh action availability

* fix(keyboard): shortcuts, keep a fired key owned across session close

Close tab is itself a shortcut, so the session that consumed the key press
is gone before the key is released. Both matchers dropped the key with the
session: the Flutter dispatcher removed its own entries in clear(), and the
Rust set was keyed by session and cleared in session_close. The release,
and the repeats while the key stayed held, then reached the next session's
remote as ordinary input.

Ownership of a fired key is physical, so it is now one set shared by every
session on each side, and closing a session no longer touches it: clear()
only drops the pending action and modifier replay, and the Rust session
state only tracks released modifiers. A key whose release is missed heals
itself on its next press. Entering view-only with the Flutter input source
still clears the Rust set, since that source stops routing key events to it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(keyboard): shortcuts, drop the Toggle Input Source action

The action switches the key capture backend between key down and key up.
Fired-key ownership lives in whichever matcher saw the press, the Flutter
dispatcher or the Rust set, and there is no handoff between them: after the
switch the repeats and the release of the held key reach the other backend,
which forwards them to the remote and, while the chord is held, matches the
action again and toggles the source back. Remove the shortcut instead of
building a handoff for one action. The toolbar radio menu is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* perf(keyboard): shortcuts, parse the config once per stored value

The Flutter matcher read the local config three times and parsed its JSON
three times on every key press in legacy mode, on Web and in view-only.
Keep one parsed snapshot keyed by the raw stored string, so a key press
costs one config read and no parsing while the config is unchanged. The
existing readers are thin views of the same snapshot.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix(keyboard): shortcuts, run focus-changing actions on key release

Close tab and tab switching move keyboard focus to another session before
the key that fired them is released. On Linux the Flutter input source
routes map-mode sessions through the Rust matcher and legacy-mode sessions
through the Dart matcher, and neither knows the other's fired keys, so
after such an action the repeats and the release of the held key reached
the next session's remote, and the key stayed recorded as fired in the
matcher that saw the press.

Routing Linux legacy mode through the Rust FFI path instead is not a drop-in
change: the raw key handler builds its event with no unicode, so Rust's
legacy mode would fall back to US key names and lose the layout-aware
characters the Dart path sends today, and it would take modifiers from the
X11 key state instead of Flutter's.

Run these three actions when the fired key is released. The matcher that
consumed the press then also sees the repeats and the release, the remote
modifiers are still released on the press, and a session that closes drops
the action it owed. Both matchers keep the same list of actions.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* test(keyboard): shortcuts, pin the key-up action list on both sides

kShortcutActionsRunOnKeyUp and runs_on_release are two hand-kept lists of
the actions that run on key release. Add a shared fixture and check each
side against it, the way the key vocabulary and the default bindings are
already checked, so adding a focus-changing action on one side breaks a
test until the other side follows. The Dart test also checks that every
listed id is a configurable action.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra4my61t8q1FN5n59wB16D

* fix: prevent lost input after remote session shortcuts

Avoid synthetic Alt presses that activate the Windows system menu
and swallow the next character.

Trigger restart, switch-sides, and chat actions on key release so
focus changes cannot leave subsequent shortcut presses ignored.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(mobile): correct Chat keyboard and input helper behavior

- Keep soft keyboard and helper hidden when Chat opens via shortcut
- Restore soft input on touch and remote focus after Chat closes
- Handle modifier releases while Chat owns focus
- Clear hidden virtual modifiers while preserving held keys and Pin behavior

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(shortcuts): prevent recording non-simultaneous key combinations

Build new captures from currently held keys instead of reusing released
letters. Preserve the last valid chord after release so it can be saved.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(shortcuts): isolate window actions and prevent duplicate Alt presses

- Dispatch shortcut actions only to the originating session window.
- Clear local modifier state on focus loss without resending modifiers
  already released to Windows peers, preventing lost following input.
- Strengthen the regression assertion against extra Alt keydown events

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(shortcuts): empty default shortcuts for Cmd+Option+Shift+Del on macOS and iOS

Because Del may be ambiguous for new macOS users.
Cmd+Option+Shift+Backspace is "Empty Trash without confirmation" shortcut on macOS.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: use session peer platform for Ctrl+Alt+Del events

Fix Android-to-Linux Ctrl+Alt+Del delivery by using the session's peer
platform instead of the desktop global session, which defaults to Windows

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: hide relative mouse mode shortcuts on Wayland

Separate relative mouse mode availability from screenshot support.
Filter unsupported actions and defaults while preserving saved bindings

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: align shortcut availability with platform support

- Hide unsupported mobile and web shortcut actions
- Enable mobile scaling and Android voice call shortcuts
- Use openMonitorInTheSameTab() for display switching
- Preserve cursor position when switching displays on desktop and web

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: prevent held-key shortcut recording bypass

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: preserve keyboard input after shortcut dialogs

Run native clipboard shortcuts on key release and wait for remote modifier releases before dispatching.

Release remote modifiers when mobile local UI takes focus, and document the tested iPad Bluetooth Mute shortcut limitation.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: preserve remote key state during local shortcuts

Keep unrelated held keys pressed for session actions that retain input. Preserve key release when focus or input handling changes, and avoid synthetic Alt taps during shortcut dispatch.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: suspend saved relative mouse shortcuts on Wayland

Filter the native active binding cache without changing stored configuration, so the binding becomes available again under X11.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: exclude unsupported Wayland shortcuts from Flutter matching

Keep saved bindings intact while filtering the active config used by the Flutter matcher. Cover stored bindings and enable/pass-through flags in the existing shortcut tests.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: retain forwarded key ownership across shortcut repeats

Ignore repeat presses for keys already forwarded to the peer, and release ordinary held keys before Show My Cursor enables view-only. Cover both paths with converter-level regressions.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: release Flutter-held keys before view-only shortcuts

Track ordinary keys forwarded by the Dart matcher paths and await their releases before View Only or Show My Cursor disables input. Keep Mute key ownership and existing empty-cleanup timing unchanged.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: filter active Flutter shortcuts by platform capabilities

Reuse the Settings action filter for saved bindings while preserving storage, default-seeding behavior and configurable Apple Ctrl+Alt+Del bindings.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: filter native shortcut caches for mobile controllers

Suspend saved desktop-only actions on Android and iOS and hide unavailable iOS actions without changing persisted bindings. Keep native cache filtering aligned with Flutter platform capabilities.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: share filtered shortcut bindings with the Web matcher

Pass the active platform config to JavaScript at startup and after settings changes, preserving unsupported saved bindings in local storage.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: release Flutter-held keys before focus-changing shortcuts

Drain ordinary keys owned by the original session before run-on-key-up
actions and native clipboard callbacks. Preserve Mute holds, pending-action
cancellation and Web clipboard key-down activation.

Document the required Web matcher counterpart in the bridge:
rustdesk/rustdesk-web-js@94abe431d6

Validation: 61 native Flutter tests, 32 Chrome/HTML tests, targeted analysis
and Windows debug build.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: remove the useless comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: prevent Windows menu activation from Alt-only shortcuts

Neutralize the forwarded Alt with a balanced temporary Ctrl press when a consumed local shortcut releases its only modifier in Windows Map mode. Keep physical modifier tracking and normal Alt pass-through unchanged.

Add a regression covering both Alt keys, trigger repeats and the eventual physical release. Verified with 36 native shortcut tests and real Windows menu/input checks.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: prevent Alt menu activation in all keyboard modes

Extend Windows Alt-only shortcut neutralization to Translate and Legacy. Keep the temporary Ctrl packets in Map mode so Legacy modifier synchronization cannot release Alt before Ctrl.

Extend the existing regression across keyboard modes and decode scan, virtual and control-key events. Verify 36 native tests and 34 Windows menu replay cases.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: initialize outgoing chat before shortcut dispatch

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: guard Windows Alt release in Flutter legacy shortcuts

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: coordinate Web shortcut dispatch and Alt modifier cleanup

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: distinguish AltGraph from ordinary Alt shortcuts

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: dispatch macOS Web shortcuts without waiting for keyup

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: support legacy peers in privacy mode shortcut

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: exclude AltGraph from Flutter shortcut matching

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: exclude GTK AltGr input from raw shortcut matching

Signed-off-by: fufesou <linlong1266@gmail.com>

* test: trim redundant keyboard shortcut coverage

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(keyboard): prevent Cmd shortcuts from opening remote Start

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(shortcuts): guard Windows menu keys after Ctrl/Cmd swapping

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(shortcuts): align recording and display routing

Reject non-Apple AltGraph captures to match runtime filtering. Honor individual-display windows for next, previous, and all-display shortcuts.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(shortcuts): reject GTK AltGr chords in recorder

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): restore keyboard focus for modifier-first input

Focus the existing FlutterView when a modifier is pressed without native view focus. This restores shortcut-first input after returning to RustDesk while preserving focused inputs and normal key dispatch.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(shortcuts): release held keys before keyboard mode changes

Drain Flutter-forwarded keys before keyboard-mode shortcut actions so releases use the original encoding. This prevents held keys from remaining down after a Web Input source 2 mode change.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(i18n): use sentence case for shortcut labels

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(mobile): preserve touch modifiers in the input helper

Keep touch-selected modifiers visible after hiding the keyboard. Clear physical modifier releases locally in view-only mode so they do not reopen the helper.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(web): preserve modifiers across Legacy-to-Map shortcuts

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(shortcuts): check support before registering all displays view

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:31:44 +08:00

2085 lines
61 KiB
Dart

import 'dart:async';
import 'dart:js' as js;
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:uuid/uuid.dart';
import 'dart:html' as html;
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/models/shortcut_model.dart';
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
mixin _$EventToUI {
Object get field0 => throw _privateConstructorUsedError;
}
sealed class EventToUI {
const factory EventToUI.event(
String field0,
) = EventToUI_Event;
const factory EventToUI.rgba(
int field0,
) = EventToUI_Rgba;
const factory EventToUI.texture(
int field0,
bool field1,
) = EventToUI_Texture;
const factory EventToUI.cursor({
required String id,
required int hotx,
required int hoty,
required int width,
required int height,
required Uint8List colors,
}) = EventToUI_Cursor;
}
class EventToUI_Event implements EventToUI {
const EventToUI_Event(final String field0) : this.field = field0;
final String field;
String get field0 => field;
}
class EventToUI_Rgba implements EventToUI {
const EventToUI_Rgba(final int field0) : field = field0;
final int field;
int get field0 => field;
}
class EventToUI_Texture implements EventToUI {
const EventToUI_Texture(final int field0, final bool field1)
: f0 = field0,
f1 = field1;
final int f0;
final bool f1;
int get field0 => f0;
bool get field1 => f1;
}
class EventToUI_Cursor implements EventToUI {
const EventToUI_Cursor({
required this.id,
required this.hotx,
required this.hoty,
required this.width,
required this.height,
required this.colors,
});
final String id;
final int hotx;
final int hoty;
final int width;
final int height;
final Uint8List colors;
}
class CursorShape {
final int hotx;
final int hoty;
final int width;
final int height;
final Uint8List colors;
const CursorShape({
required this.hotx,
required this.hoty,
required this.width,
required this.height,
required this.colors,
});
}
class RustdeskImpl {
// The core answers through the callback, before callMethod returns.
Future<CursorShape?> sessionGetCursorShape(
{required UuidValue sessionId, required String id, dynamic hint}) {
final completer = Completer<CursorShape?>();
js.context.callMethod('getCursorShape', [
id,
(int hotx, int hoty, int width, int height, Uint8List? colors) {
completer.complete(colors == null
? null
: CursorShape(
hotx: hotx,
hoty: hoty,
width: width,
height: height,
colors: colors));
}
]);
return completer.future;
}
Future<void> stopGlobalEventStream({required String appType, dynamic hint}) {
throw UnimplementedError("stopGlobalEventStream");
}
Future<void> hostStopSystemKeyPropagate(
{required bool stopped, dynamic hint}) {
throw UnimplementedError("hostStopSystemKeyPropagate");
}
int peerGetSessionsCount(
{required String id, required int connType, dynamic hint}) {
return 0;
}
String sessionAddExistedSync(
{required String id,
required UuidValue sessionId,
required Int32List displays,
required bool isViewCamera,
dynamic hint}) {
return '';
}
String sessionAddSync(
{required UuidValue sessionId,
required String id,
required bool isFileTransfer,
required bool isViewCamera,
required bool isPortForward,
required bool isRdp,
required bool isTerminal,
required String switchUuid,
required bool forceRelay,
required String password,
required bool isSharedPassword,
String? connToken,
dynamic hint}) {
return js.context.callMethod('setByName', [
'session_add_sync',
jsonEncode({
'id': id,
'password': password,
'is_shared_password': isSharedPassword,
'isFileTransfer': isFileTransfer,
'isViewCamera': isViewCamera,
'isTerminal': isTerminal
})
]);
}
Stream<EventToUI> sessionStart(
{required UuidValue sessionId, required String id, dynamic hint}) {
js.context.callMethod('setByName', [
'session_start',
jsonEncode({'id': id})
]);
return Stream.empty();
}
Stream<EventToUI> sessionStartWithDisplays(
{required UuidValue sessionId,
required String id,
required Int32List displays,
dynamic hint}) {
throw UnimplementedError("sessionStartWithDisplays");
}
Future<bool?> sessionGetRemember(
{required UuidValue sessionId, dynamic hint}) {
return Future(
() => js.context.callMethod('getByName', ['remember']) == 'true');
}
Future<bool?> sessionGetToggleOption(
{required UuidValue sessionId, required String arg, dynamic hint}) {
return Future(
() => sessionGetToggleOptionSync(sessionId: sessionId, arg: arg));
}
bool sessionGetToggleOptionSync(
{required UuidValue sessionId, required String arg, dynamic hint}) {
return 'true' == js.context.callMethod('getByName', ['option:toggle', arg]);
}
Future<String?> sessionGetOption(
{required UuidValue sessionId, required String arg, dynamic hint}) {
return Future(
() => js.context.callMethod('getByName', ['option:session', arg]));
}
Future<void> sessionLogin(
{required UuidValue sessionId,
required String osUsername,
required String osPassword,
required String password,
required bool remember,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'login',
jsonEncode({
'os_username': osUsername,
'os_password': osPassword,
'password': password,
'remember': remember
})
]));
}
Future<void> sessionSend2Fa(
{required UuidValue sessionId,
required String code,
required bool trustThisDevice,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'send_2fa',
jsonEncode({'code': code, 'trust_this_device': trustThisDevice})
]));
}
Future<void> sessionClose({required UuidValue sessionId, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', ['session_close']));
}
Future<void> sessionRefresh(
{required UuidValue sessionId, required int display, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', ['refresh']));
}
Future<void> sessionRecordScreen(
{required UuidValue sessionId, required bool start, dynamic hint}) {
throw UnimplementedError("sessionRecordScreen");
}
bool sessionGetIsRecording({required UuidValue sessionId, dynamic hint}) {
return false;
}
Future<void> sessionReconnect(
{required UuidValue sessionId, required bool forceRelay, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', ['reconnect']));
}
Future<void> sessionToggleOption(
{required UuidValue sessionId, required String value, dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['option:toggle', value]));
}
Future<void> sessionTogglePrivacyMode(
{required UuidValue sessionId,
required String implKey,
required bool on,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'toggle_privacy_mode',
jsonEncode({'impl_key': implKey, 'on': on})
]));
}
Future<String?> sessionGetFlutterOption(
{required UuidValue sessionId, required String k, dynamic hint}) {
return Future(
() => js.context.callMethod('getByName', ['option:flutter:peer', k]));
}
Future<void> sessionSetFlutterOption(
{required UuidValue sessionId,
required String k,
required String v,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'option:flutter:peer',
jsonEncode({'name': k, 'value': v})
]));
}
int getNextTextureKey({dynamic hint}) {
return 0;
}
String getLocalFlutterOption({required String k, dynamic hint}) {
return js.context.callMethod('getByName', ['option:flutter:local', k]);
}
Future<void> setLocalFlutterOption(
{required String k, required String v, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'option:flutter:local',
jsonEncode({'name': k, 'value': v})
]));
}
String getLocalKbLayoutType({dynamic hint}) {
return js.context.callMethod('getByName', ['option:local', 'kb_layout']);
}
Future<void> setLocalKbLayoutType(
{required String kbLayoutType, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'option:local',
jsonEncode({'name': 'kb_layout', 'value': kbLayoutType})
]));
}
Future<String?> sessionGetViewStyle(
{required UuidValue sessionId, dynamic hint}) {
return Future(() =>
js.context.callMethod('getByName', ['option:session', 'view_style']));
}
Future<void> sessionSetViewStyle(
{required UuidValue sessionId, required String value, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'option:session',
jsonEncode({'name': 'view_style', 'value': value})
]));
}
Future<int?> sessionGetTrackpadSpeed(
{required UuidValue sessionId, dynamic hint}) {
throw UnimplementedError("sessionGetTrackpadSpeed");
}
Future<void> sessionSetTrackpadSpeed(
{required UuidValue sessionId, required int value, dynamic hint}) {
throw UnimplementedError("sessionSetTrackpadSpeed");
}
Future<String?> sessionGetScrollStyle(
{required UuidValue sessionId, dynamic hint}) {
return Future(() =>
js.context.callMethod('getByName', ['option:session', 'scroll_style']));
}
Future<void> sessionSetScrollStyle(
{required UuidValue sessionId, required String value, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'option:session',
jsonEncode({'name': 'scroll_style', 'value': value})
]));
}
Future<String?> sessionGetImageQuality(
{required UuidValue sessionId, dynamic hint}) {
return Future(() => js.context.callMethod('getByName', ['image_quality']));
}
Future<void> sessionSetImageQuality(
{required UuidValue sessionId, required String value, dynamic hint}) {
print('set image quality: $value');
return Future(
() => js.context.callMethod('setByName', ['image_quality', value]));
}
Future<String?> sessionGetKeyboardMode(
{required UuidValue sessionId, dynamic hint}) {
final mode =
js.context.callMethod('getByName', ['option:session', 'keyboard_mode']);
return Future(() => mode == '' ? null : mode);
}
Future<void> sessionSetKeyboardMode(
{required UuidValue sessionId, required String value, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'option:session',
jsonEncode({'name': 'keyboard_mode', 'value': value})
]));
}
String? sessionGetReverseMouseWheelSync(
{required UuidValue sessionId, dynamic hint}) {
return js.context
.callMethod('getByName', ['option:session', 'reverse_mouse_wheel']);
}
Future<void> sessionSetReverseMouseWheel(
{required UuidValue sessionId, required String value, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'option:session',
jsonEncode({'name': 'reverse_mouse_wheel', 'value': value})
]));
}
String? sessionGetDisplaysAsIndividualWindows(
{required UuidValue sessionId, dynamic hint}) {
return js.context.callMethod(
'getByName', ['option:session', 'displays_as_individual_windows']);
}
Future<void> sessionSetDisplaysAsIndividualWindows(
{required UuidValue sessionId, required String value, dynamic hint}) {
return Future.value();
}
String? sessionGetUseAllMyDisplaysForTheRemoteSession(
{required UuidValue sessionId, dynamic hint}) {
return '';
}
Future<void> sessionSetUseAllMyDisplaysForTheRemoteSession(
{required UuidValue sessionId, required String value, dynamic hint}) {
return Future.value();
}
Future<Int32List?> sessionGetCustomImageQuality(
{required UuidValue sessionId, dynamic hint}) {
try {
return Future(() => Int32List.fromList([
int.parse(js.context.callMethod(
'getByName', ['option:session', 'custom_image_quality']))
]));
} catch (e) {
return Future.value(null);
}
}
bool sessionIsKeyboardModeSupported(
{required UuidValue sessionId, required String mode, dynamic hint}) {
if (mainGetInputSource(hint: hint) == 'Input source 1') {
return [kKeyMapMode, kKeyTranslateMode].contains(mode);
} else {
return [kKeyLegacyMode, kKeyMapMode].contains(mode);
}
}
bool sessionIsMultiUiSession({required UuidValue sessionId, dynamic hint}) {
return false;
}
Future<void> sessionSetCustomImageQuality(
{required UuidValue sessionId, required int value, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'custom_image_quality',
value,
]));
}
Future<void> sessionSetCustomFps(
{required UuidValue sessionId, required int fps, dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['custom-fps', fps]));
}
Future<void> sessionLockScreen({required UuidValue sessionId, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', ['lock_screen']));
}
Future<void> sessionCtrlAltDel({required UuidValue sessionId, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', ['ctrl_alt_del']));
}
Future<void> sessionSwitchDisplay(
{required bool isDesktop,
required UuidValue sessionId,
required Int32List value,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'switch_display',
jsonEncode({
'isDesktop': isDesktop,
'sessionId': sessionId.toString(),
'value': value
})
]));
}
Future<void> sessionHandleFlutterKeyEvent(
{required UuidValue sessionId,
required String character,
required int usbHid,
required int lockModes,
required bool downOrUp,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'flutter_key_event',
jsonEncode({
'name': character,
'usb_hid': usbHid,
'lock_modes': lockModes,
if (downOrUp) 'down': 'true',
})
]));
}
Future<void> sessionHandleFlutterRawKeyEvent(
{required UuidValue sessionId,
required String name,
required int platformCode,
required int positionCode,
required int lockModes,
required bool downOrUp,
dynamic hint}) {
throw UnimplementedError("sessionHandleFlutterRawKeyEvent");
}
void sessionEnterOrLeave(
{required UuidValue sessionId, required bool enter, dynamic hint}) {
js.context.callMethod('setByName', ['enter_or_leave', enter]);
}
Future<void> sessionInputKey(
{required UuidValue sessionId,
required String name,
required bool down,
required bool press,
required bool alt,
required bool ctrl,
required bool shift,
required bool command,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'input_key',
jsonEncode({
'name': name,
if (down) 'down': 'true',
if (press) 'press': 'true',
if (alt) 'alt': 'true',
if (ctrl) 'ctrl': 'true',
if (shift) 'shift': 'true',
if (command) 'command': 'true'
})
]));
}
Future<void> sessionInputString(
{required UuidValue sessionId, required String value, dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['input_string', value]));
}
Future<void> sessionSendChat(
{required UuidValue sessionId, required String text, dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['send_chat', text]));
}
Future<void> sessionPeerOption(
{required UuidValue sessionId,
required String name,
required String value,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'option:session',
jsonEncode({'name': name, 'value': value})
]));
}
Future<String> sessionGetPeerOption(
{required UuidValue sessionId, required String name, dynamic hint}) {
return Future(
() => js.context.callMethod('getByName', ['option:session', name]));
}
Future<void> sessionInputOsPassword(
{required UuidValue sessionId, required String value, dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['input_os_password', value]));
}
Future<void> sessionReadRemoteDir(
{required UuidValue sessionId,
required String path,
required bool includeHidden,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'read_remote_dir',
jsonEncode({'path': path, 'include_hidden': includeHidden})
]));
}
Future<void> sessionSendFiles(
{required UuidValue sessionId,
required int actId,
required String path,
required String to,
required int fileNum,
required bool includeHidden,
required bool isRemote,
required bool isDir,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'send_files',
jsonEncode({
'id': actId,
'path': path,
'to': to,
'file_num': fileNum,
'include_hidden': includeHidden,
'is_remote': isRemote,
'is_dir': isDir,
})
]));
}
Future<void> sessionSetConfirmOverrideFile(
{required UuidValue sessionId,
required int actId,
required int fileNum,
required bool needOverride,
required bool remember,
required bool isUpload,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'confirm_override_file',
jsonEncode({
'id': actId,
'file_num': fileNum,
'need_override': needOverride,
'remember': remember,
'is_upload': isUpload
})
]));
}
Future<void> sessionRemoveFile(
{required UuidValue sessionId,
required int actId,
required String path,
required int fileNum,
required bool isRemote,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'remove_file',
jsonEncode({
'id': actId,
'path': path,
'file_num': fileNum,
'is_remote': isRemote
})
]));
}
Future<void> sessionReadDirToRemoveRecursive(
{required UuidValue sessionId,
required int actId,
required String path,
required bool isRemote,
required bool showHidden,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'read_dir_to_remove_recursive',
jsonEncode({
'id': actId,
'path': path,
'is_remote': isRemote,
'show_hidden': showHidden
})
]));
}
Future<void> sessionRemoveAllEmptyDirs(
{required UuidValue sessionId,
required int actId,
required String path,
required bool isRemote,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'remove_all_empty_dirs',
jsonEncode({'id': actId, 'path': path, 'is_remote': isRemote})
]));
}
Future<void> sessionCancelJob(
{required UuidValue sessionId, required int actId, dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['cancel_job', actId]));
}
Future<void> sessionCreateDir(
{required UuidValue sessionId,
required int actId,
required String path,
required bool isRemote,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'create_dir',
jsonEncode({'id': actId, 'path': path, 'is_remote': isRemote})
]));
}
Future<String> sessionReadLocalDirSync(
{required UuidValue sessionId,
required String path,
required bool showHidden,
dynamic hint}) {
throw UnimplementedError("sessionReadLocalDirSync");
}
Future<String> sessionGetPlatform(
{required UuidValue sessionId, required bool isRemote, dynamic hint}) {
if (isRemote) {
return Future(() => js.context.callMethod('getByName', ['platform']));
} else {
return Future(() => 'Web');
}
}
Future<void> sessionLoadLastTransferJobs(
{required UuidValue sessionId, dynamic hint}) {
throw UnimplementedError("sessionLoadLastTransferJobs");
}
Future<void> sessionAddJob(
{required UuidValue sessionId,
required int actId,
required String path,
required String to,
required int fileNum,
required bool includeHidden,
required bool isRemote,
dynamic hint}) {
throw UnimplementedError("sessionAddJob");
}
Future<void> sessionResumeJob(
{required UuidValue sessionId,
required int actId,
required bool isRemote,
dynamic hint}) {
throw UnimplementedError("sessionResumeJob");
}
Future<void> sessionElevateDirect(
{required UuidValue sessionId, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', ['elevate_direct']));
}
Future<void> sessionElevateWithLogon(
{required UuidValue sessionId,
required String username,
required String password,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'elevate_with_logon',
jsonEncode({'username': username, 'password': password})
]));
}
Future<void> sessionSwitchSides(
{required UuidValue sessionId, dynamic hint}) {
throw UnimplementedError("sessionSwitchSides");
}
Future<void> sessionChangeResolution(
{required UuidValue sessionId,
required int display,
required int width,
required int height,
dynamic hint}) {
// note: restore on disconnected
return Future(() => js.context.callMethod('setByName', [
'change_resolution',
jsonEncode({'display': display, 'width': width, 'height': height})
]));
}
Future<void> sessionSetSize(
{required UuidValue sessionId,
required int display,
required int width,
required int height,
dynamic hint}) {
return Future.value();
}
Future<void> sessionSendSelectedSessionId(
{required UuidValue sessionId, required String sid, dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['selected_sid', sid]));
}
Future<List<String>> mainGetSoundInputs({dynamic hint}) {
throw UnimplementedError("mainGetSoundInputs");
}
Future<String?> mainGetDefaultSoundInput({dynamic hint}) {
throw UnimplementedError("mainGetDefaultSoundInput");
}
String mainGetLoginDeviceInfo({dynamic hint}) {
String userAgent = html.window.navigator.userAgent;
String appName = html.window.navigator.appName;
String appVersion = html.window.navigator.appVersion;
String? platform = html.window.navigator.platform;
return jsonEncode({
'os': '$userAgent, $appName $appVersion ($platform)',
'type': 'Web client',
'name': js.context.callMethod('getByName', ['my_name']),
});
}
Future<void> mainChangeId({required String newId, dynamic hint}) {
throw UnimplementedError("mainChangeId");
}
Future<String> mainGetAsyncStatus({dynamic hint}) {
throw UnimplementedError("mainGetAsyncStatus");
}
Future<String> mainGetOption({required String key, dynamic hint}) {
return Future.value(mainGetOptionSync(key: key));
}
String mainGetOptionSync({required String key, dynamic hint}) {
return js.context.callMethod('getByName', ['option', key]);
}
Future<String> mainGetError({dynamic hint}) {
throw UnimplementedError("mainGetError");
}
Future<void> mainSetOption(
{required String key, required String value, dynamic hint}) {
js.context.callMethod('setByName', [
'option',
jsonEncode({'name': key, 'value': value})
]);
return Future.value();
}
// get server settings
Future<String> mainGetOptions({dynamic hint}) {
return Future(() => mainGetOptionsSync());
}
// get server settings
String mainGetOptionsSync({dynamic hint}) {
return js.context.callMethod('getByName', ['options']);
}
Future<void> mainSetOptions({required String json, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', ['options', json]));
}
Future<String> mainTestIfValidServer(
{required String server, required bool testWithProxy, dynamic hint}) {
// TODO: implement
return Future.value('');
}
Future<void> mainSetSocks(
{required String proxy,
required String username,
required String password,
dynamic hint}) {
throw UnimplementedError("mainSetSocks");
}
Future<List<String>> mainGetSocks({dynamic hint}) {
throw UnimplementedError("mainGetSocks");
}
Future<String> mainGetAppName({dynamic hint}) {
return Future.value(mainGetAppNameSync(hint: hint));
}
String mainGetAppNameSync({dynamic hint}) {
return js.context.callMethod('getByName', ['app-name']);
}
String mainUriPrefixSync({dynamic hint}) {
throw UnimplementedError("mainUriPrefixSync");
}
Future<String> mainGetLicense({dynamic hint}) {
// TODO: implement
return Future(() => '');
}
Future<String> mainGetVersion({dynamic hint}) {
return Future(() => js.context.callMethod('getByName', ['version']));
}
Future<List<String>> mainGetFav({dynamic hint}) {
List<String> favs = [];
try {
favs = (jsonDecode(js.context.callMethod('getByName', ['fav']))
as List<dynamic>)
.map((e) => e.toString())
.toList();
} catch (e) {
debugPrint('Failed to load favs: $e');
}
return Future.value(favs);
}
Future<void> mainStoreFav({required List<String> favs, dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['fav', jsonEncode(favs)]));
}
String mainGetPeerSync({required String id, dynamic hint}) {
// TODO:
throw UnimplementedError("mainGetPeerSync");
}
Future<String> mainGetLanPeers({dynamic hint}) {
throw UnimplementedError("mainGetLanPeers");
}
Future<String> mainGetConnectStatus({dynamic hint}) {
return Future(
() => js.context.callMethod('getByName', ["get_conn_status"]));
}
Future<void> mainCheckConnectStatus({dynamic hint}) {
throw UnimplementedError("mainCheckConnectStatus");
}
Future<bool> mainIsUsingPublicServer({dynamic hint}) {
return Future(() =>
js.context.callMethod('getByName', ["is_using_public_server"]) ==
'true');
}
Future<void> mainDiscover({dynamic hint}) {
throw UnimplementedError("mainDiscover");
}
Future<String> mainGetApiServer({dynamic hint}) {
return Future(() => js.context.callMethod('getByName', ['api_server']));
}
Future<void> mainPostRequest(
{required String url,
required String body,
required String header,
dynamic hint}) {
throw UnimplementedError("mainPostRequest");
}
Future<bool> mainGetProxyStatus({dynamic hint}) {
return Future(() => false);
}
Future<void> mainHttpRequest({
required String url,
required String method,
String? body,
required String header,
dynamic hint,
}) {
throw UnimplementedError("mainHttpRequest");
}
Future<String?> mainGetHttpStatus({required String url, dynamic hint}) {
throw UnimplementedError("mainGetHttpStatus");
}
String mainGetLocalOption({required String key, dynamic hint}) {
final v = js.context.callMethod('getByName', ['option:local', key]);
if (key == 'lang' && (v == 'pt' || v == 'br')) {
return 'pt-br';
}
return v;
}
// Do not return the real environment variables.
// Use the global variable as the environment variable in web.
String mainGetEnv({required String key, dynamic hint}) {
return js.context.callMethod('getByName', ['envvar', key]);
}
// Use the global variable as the environment variable in web.
void mainSetEnv({required String key, String? value, dynamic hint}) {
js.context.callMethod('setByName', [
'envvar',
jsonEncode({'name': key, 'value': value})
]);
}
Future<void> mainSetLocalOption(
{required String key, required String value, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'option:local',
jsonEncode({'name': key, 'value': value})
]));
}
// Tell the JS-side matcher (flutter/web/js/src/shortcut_matcher.ts) to
// use the same platform-filtered bindings as the Flutter matcher, while
// keeping unsupported saved bindings in LocalStorage.
void mainReloadKeyboardShortcuts({dynamic hint}) {
final config = ShortcutModel.config(active: true);
js.context.callMethod('reloadShortcuts', [
jsonEncode({
'enabled': config.enabled,
'pass_through': config.passThrough,
'bindings': config.bindings,
})
]);
}
// Web has no Rust at runtime, so the defaults seed comes from the
// [kDefaultShortcutBindings] canonical in shortcut_constants.dart. Parity
// with Rust's `default_bindings()` is enforced by tests on both sides
// against `flutter/test/fixtures/default_keyboard_shortcuts.json`.
String mainGetDefaultKeyboardShortcuts({dynamic hint}) {
return jsonEncode(kDefaultShortcutBindings);
}
String mainGetInputSource({dynamic hint}) {
final inputSource =
js.context.callMethod('getByName', ['option:local', 'input-source']);
// // js grab mode
// export const CONFIG_INPUT_SOURCE_1 = "Input source 1";
// // flutter grab mode
// export const CONFIG_INPUT_SOURCE_2 = "Input source 2";
return inputSource != '' ? inputSource : 'Input source 1';
}
Future<void> mainSetInputSource(
{required UuidValue sessionId, required String value, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'option:local',
jsonEncode({'name': 'input-source', 'value': value})
]));
}
Future<String> mainGetMyId({dynamic hint}) {
return Future(() => js.context.callMethod('getByName', ['my_id']));
}
Future<String> mainGetUuid({dynamic hint}) {
return Future(() => js.context.callMethod('getByName', ['uuid']));
}
Future<String> mainGetPeerOption(
{required String id, required String key, dynamic hint}) {
return Future(() => mainGetPeerOptionSync(id: id, key: key, hint: hint));
}
String mainGetPeerOptionSync(
{required String id, required String key, dynamic hint}) {
return js.context.callMethod('getByName', [
'option:peer',
jsonEncode({'id': id, 'name': key})
]);
}
String mainGetPeerFlutterOptionSync(
{required String id, required String k, dynamic hint}) {
return js.context.callMethod('getByName', ['option:flutter:peer', k]);
}
void mainSetPeerFlutterOptionSync(
{required String id,
required String k,
required String v,
dynamic hint}) {
js.context.callMethod('setByName', [
'option:flutter:peer',
jsonEncode({'name': k, 'value': v})
]);
}
Future<void> mainSetPeerOption(
{required String id,
required String key,
required String value,
dynamic hint}) {
mainSetPeerOptionSync(id: id, key: key, value: value, hint: hint);
return Future.value();
}
bool mainSetPeerOptionSync(
{required String id,
required String key,
required String value,
dynamic hint}) {
js.context.callMethod('setByName', [
'option:peer',
jsonEncode({'id': id, 'name': key, 'value': value})
]);
return true;
}
Future<void> mainSetPeerAlias(
{required String id, required String alias, dynamic hint}) {
mainSetPeerOptionSync(id: id, key: 'alias', value: alias, hint: hint);
return Future.value();
}
Future<String> mainGetNewStoredPeers({dynamic hint}) {
throw UnimplementedError("mainGetNewStoredPeers");
}
Future<void> mainForgetPassword({required String id, dynamic hint}) {
return mainSetPeerOption(id: id, key: 'password', value: '');
}
Future<bool> mainPeerHasPassword({required String id, dynamic hint}) {
return Future(() =>
js.context.callMethod('getByName', ['peer_has_password', id]) ==
'true');
}
Future<bool> mainPeerExists({required String id, dynamic hint}) {
return Future(
() => js.context.callMethod('getByName', ['peer_exists', id]));
}
Future<void> mainLoadRecentPeers({dynamic hint}) {
return Future(
() => js.context.callMethod('getByName', ['load_recent_peers']));
}
String mainLoadRecentPeersSync({dynamic hint}) {
return js.context.callMethod('getByName', ['load_recent_peers_sync']);
}
String mainLoadLanPeersSync({dynamic hint}) {
return '{}';
}
Future<String> mainLoadRecentPeersForAb(
{required String filter, dynamic hint}) {
throw UnimplementedError("mainLoadRecentPeersForAb");
}
Future<void> mainLoadFavPeers({dynamic hint}) {
return Future(() => js.context.callMethod('getByName', ['load_fav_peers']));
}
Future<void> mainLoadLanPeers({dynamic hint}) {
throw UnimplementedError("mainLoadLanPeers");
}
Future<void> mainRemoveDiscovered({required String id, dynamic hint}) {
throw UnimplementedError("mainRemoveDiscovered");
}
Future<void> mainChangeTheme({required String dark, dynamic hint}) {
throw UnimplementedError("mainChangeTheme");
}
Future<void> mainChangeLanguage({required String lang, dynamic hint}) {
throw UnimplementedError("mainChangeLanguage");
}
String mainVideoSaveDirectory({required bool root, dynamic hint}) {
throw UnimplementedError("mainVideoSaveDirectory");
}
Future<void> mainSetUserDefaultOption(
{required String key, required String value, dynamic hint}) {
js.context.callMethod('setByName', [
'option:user:default',
jsonEncode({'name': key, 'value': value})
]);
return Future.value();
}
String mainGetUserDefaultOption({required String key, dynamic hint}) {
return js.context.callMethod('getByName', ['option:user:default', key]);
}
Future<String> mainHandleRelayId({required String id, dynamic hint}) {
var newId = id;
if (id.endsWith("\\r") || id.endsWith("/r")) {
newId = id.substring(0, id.length - 2);
}
return Future.value(newId);
}
String mainGetMainDisplay({dynamic hint}) {
return js.context.callMethod('getByName', ['main_display']);
}
String mainGetDisplays({dynamic hint}) {
throw UnimplementedError("mainGetDisplays");
}
Future<void> sessionAddPortForward(
{required UuidValue sessionId,
required int localPort,
required String remoteHost,
required int remotePort,
dynamic hint}) {
throw UnimplementedError("sessionAddPortForward");
}
Future<void> sessionRemovePortForward(
{required UuidValue sessionId, required int localPort, dynamic hint}) {
throw UnimplementedError("sessionRemovePortForward");
}
Future<void> sessionNewRdp({required UuidValue sessionId, dynamic hint}) {
throw UnimplementedError("sessionNewRdp");
}
Future<void> sessionRequestVoiceCall(
{required UuidValue sessionId, dynamic hint}) {
throw UnimplementedError("sessionRequestVoiceCall");
}
Future<void> sessionCloseVoiceCall(
{required UuidValue sessionId, dynamic hint}) {
throw UnimplementedError("sessionCloseVoiceCall");
}
Future<void> cmHandleIncomingVoiceCall(
{required int id, required bool accept, dynamic hint}) {
throw UnimplementedError("cmHandleIncomingVoiceCall");
}
Future<void> cmCloseVoiceCall({required int id, dynamic hint}) {
throw UnimplementedError("cmCloseVoiceCall");
}
Future<String> mainGetLastRemoteId({dynamic hint}) {
return Future(() => mainGetLocalOption(key: 'last_remote_id'));
}
Future<void> mainGetSoftwareUpdateUrl({dynamic hint}) {
throw UnimplementedError("mainGetSoftwareUpdateUrl");
}
Future<String> mainGetHomeDir({dynamic hint}) {
return Future.value('');
}
Future<String> mainGetLangs({dynamic hint}) {
return Future(() => js.context.callMethod('getByName', ['langs']));
}
Future<String> mainGetTemporaryPassword({dynamic hint}) {
return Future.value('');
}
Future<String> mainGetFingerprint({dynamic hint}) {
return Future.value('');
}
Future<String> cmGetClientsState({dynamic hint}) {
throw UnimplementedError("cmGetClientsState");
}
Future<String?> cmCheckClientsLength({required int length, dynamic hint}) {
throw UnimplementedError("cmCheckClientsLength");
}
Future<int> cmGetClientsLength({dynamic hint}) {
throw UnimplementedError("cmCheckClientsLength");
}
Future<void> mainInit({required String appDir, dynamic hint}) {
// JS -> Dart shortcut bridge. The matcher in flutter/web/js/src/
// shortcut_matcher.ts calls `window.onShortcutTriggered(actionId)` when a
// binding fires; route it to the active session's ShortcutModel.
// Web uses a JS-side connection, so the event does not arrive through the
// native session event stream.
js.context['onShortcutTriggered'] = (dynamic action) {
if (action is String) {
ShortcutModel.onWebTriggered(action);
}
};
mainReloadKeyboardShortcuts();
return Future.value();
}
Future<void> mainDeviceId({required String id, dynamic hint}) {
// TODO: ?
throw UnimplementedError("mainDeviceId");
}
Future<void> mainDeviceName({required String name, dynamic hint}) {
// TODO: ?
throw UnimplementedError("mainDeviceName");
}
Future<void> mainRemovePeer({required String id, dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['remove_peer', id]));
}
bool mainHasHwcodec({dynamic hint}) {
throw UnimplementedError("mainHasHwcodec");
}
bool mainHasVram({dynamic hint}) {
throw UnimplementedError("mainHasVram");
}
String mainSupportedHwdecodings({dynamic hint}) {
return '{}';
}
Future<bool> mainIsRoot({dynamic hint}) {
throw UnimplementedError("mainIsRoot");
}
int getDoubleClickTime({dynamic hint}) {
return 500;
}
Future<void> mainStartDbusServer({dynamic hint}) {
throw UnimplementedError("mainStartDbusServer");
}
Future<void> mainSaveAb({required String json, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', ['save_ab', json]));
}
Future<void> mainClearAb({dynamic hint}) {
return Future(() => js.context.callMethod('setByName', ['clear_ab']));
}
Future<String> mainLoadAb({dynamic hint}) {
Completer<String> completer = Completer();
Future<String> timeoutFuture = completer.future.timeout(
Duration(seconds: 2),
onTimeout: () {
completer.completeError(TimeoutException('Load ab timed out'));
return 'Timeout';
},
);
js.context["onLoadAbFinished"] = (String s) {
completer.complete(s);
};
js.context.callMethod('setByName', ['load_ab']);
return timeoutFuture;
}
Future<void> mainSaveGroup({required String json, dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['save_group', json]));
}
Future<void> mainClearGroup({dynamic hint}) {
return Future(() => js.context.callMethod('setByName', ['clear_group']));
}
Future<String> mainLoadGroup({dynamic hint}) {
Completer<String> completer = Completer();
Future<String> timeoutFuture = completer.future.timeout(
Duration(seconds: 2),
onTimeout: () {
completer.completeError(TimeoutException('Load group timed out'));
return 'Timeout';
},
);
js.context["onLoadGroupFinished"] = (String s) {
completer.complete(s);
};
js.context.callMethod('setByName', ['load_group']);
return timeoutFuture;
}
Future<void> sessionSendPointer(
{required UuidValue sessionId, required String msg, dynamic hint}) {
throw UnimplementedError("sessionSendPointer");
}
Future<void> sessionSendMouse(
{required UuidValue sessionId, required String msg, dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['send_mouse', msg]));
}
Future<void> sessionRestartRemoteDevice(
{required UuidValue sessionId, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', ['restart']));
}
String sessionGetAuditServerSync(
{required UuidValue sessionId, required String typ, dynamic hint}) {
return js.context.callMethod('getByName', ['audit_server', typ]);
}
Future<void> sessionSendNote(
{required UuidValue sessionId, required String note, dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['send_note', note]));
}
Future<String> sessionAlternativeCodecs(
{required UuidValue sessionId, dynamic hint}) {
return Future(
() => js.context.callMethod('getByName', ['alternative_codecs']));
}
Future<void> sessionChangePreferCodec(
{required UuidValue sessionId, dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['change_prefer_codec']));
}
Future<void> sessionOnWaitingForImageDialogShow(
{required UuidValue sessionId, dynamic hint}) {
return Future.value();
}
Future<void> sessionToggleVirtualDisplay(
{required UuidValue sessionId,
required int index,
required bool on,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'toggle_virtual_display',
jsonEncode({'index': index, 'on': on})
]));
}
Future<void> mainSetHomeDir({required String home, dynamic hint}) {
throw UnimplementedError("mainSetHomeDir");
}
String mainGetDataDirIos({dynamic hint}) {
throw UnimplementedError("mainGetDataDirIos");
}
Future<void> mainStopService({dynamic hint}) {
throw UnimplementedError("mainStopService");
}
Future<void> mainStartService({dynamic hint}) {
throw UnimplementedError("mainStartService");
}
Future<void> mainUpdateTemporaryPassword({dynamic hint}) {
throw UnimplementedError("mainUpdateTemporaryPassword");
}
Future<bool> mainSetPermanentPasswordWithResult(
{required String password, dynamic hint}) {
throw UnimplementedError("mainSetPermanentPasswordWithResult");
}
Future<bool> mainCheckSuperUserPermission({dynamic hint}) {
throw UnimplementedError("mainCheckSuperUserPermission");
}
Future<void> mainCheckMouseTime({dynamic hint}) {
throw UnimplementedError("mainCheckMouseTime");
}
Future<double> mainGetMouseTime({dynamic hint}) {
throw UnimplementedError("mainGetMouseTime");
}
Future<void> mainWol({required String id, dynamic hint}) {
throw UnimplementedError("mainWol");
}
Future<void> mainCreateShortcut({required String id, dynamic hint}) {
throw UnimplementedError("mainCreateShortcut");
}
Future<void> cmSendChat(
{required int connId, required String msg, dynamic hint}) {
throw UnimplementedError("cmSendChat");
}
Future<void> cmLoginRes(
{required int connId, required bool res, dynamic hint}) {
throw UnimplementedError("cmLoginRes");
}
Future<void> cmCloseConnectionWindow({required int connId, dynamic hint}) {
throw UnimplementedError("cmCloseConnectionWindow");
}
Future<void> cmCloseConnection({required int connId, dynamic hint}) {
throw UnimplementedError("cmCloseConnection");
}
Future<void> cmRemoveDisconnectedConnection(
{required int connId, dynamic hint}) {
throw UnimplementedError("cmRemoveDisconnectedConnection");
}
Future<void> cmCheckClickTime({required int connId, dynamic hint}) {
throw UnimplementedError("cmCheckClickTime");
}
Future<double> cmGetClickTime({dynamic hint}) {
throw UnimplementedError("cmGetClickTime");
}
Future<void> cmSwitchPermission(
{required int connId,
required String name,
required bool enabled,
dynamic hint}) {
throw UnimplementedError("cmSwitchPermission");
}
bool cmCanElevate({dynamic hint}) {
throw UnimplementedError("cmCanElevate");
}
Future<void> cmElevatePortable({required int connId, dynamic hint}) {
throw UnimplementedError("cmElevatePortable");
}
Future<void> cmSwitchBack({required int connId, dynamic hint}) {
throw UnimplementedError("cmSwitchBack");
}
Future<String> cmGetConfig({required String name, dynamic hint}) {
throw UnimplementedError("cmGetConfig");
}
Future<String> mainGetBuildDate({dynamic hint}) {
return Future(() => js.context.callMethod('getByName', ['build_date']));
}
String translate(
{required String name, required String locale, dynamic hint}) {
return js.context.callMethod('getByName', [
'translate',
jsonEncode({'locale': locale, 'text': name})
]);
}
int sessionGetRgbaSize(
{required UuidValue sessionId, required int display, dynamic hint}) {
return 0;
}
void sessionNextRgba(
{required UuidValue sessionId, required int display, dynamic hint}) {}
void sessionRegisterPixelbufferTexture(
{required UuidValue sessionId,
required int display,
required int ptr,
dynamic hint}) {}
void sessionRegisterGpuTexture(
{required UuidValue sessionId,
required int display,
required int ptr,
dynamic hint}) {}
Future<void> queryOnlines({required List<String> ids, dynamic hint}) {
return Future(() =>
js.context.callMethod('setByName', ['query_onlines', jsonEncode(ids)]));
}
// Dup to the function in hbb_common, lib.rs
// Maybe we need to move this function to js part.
int versionToNumber({required String v, dynamic hint}) {
return int.tryParse(
js.context.callMethod('getByName', ['get_version_number', v])) ??
0;
}
Future<bool> optionSynced({dynamic hint}) {
return Future.value(true);
}
bool mainIsInstalled({dynamic hint}) {
throw UnimplementedError("mainIsInstalled");
}
void mainInitInputSource({dynamic hint}) {
throw UnimplementedError("mainIsInstalled");
}
bool mainIsInstalledLowerVersion({dynamic hint}) {
throw UnimplementedError("mainIsInstalledLowerVersion");
}
bool mainIsInstalledDaemon({required bool prompt, dynamic hint}) {
throw UnimplementedError("mainIsInstalledDaemon");
}
bool mainIsProcessTrusted({required bool prompt, dynamic hint}) {
throw UnimplementedError("mainIsProcessTrusted");
}
bool mainIsCanScreenRecording({required bool prompt, dynamic hint}) {
throw UnimplementedError("mainIsCanScreenRecording");
}
bool mainIsCanInputMonitoring({required bool prompt, dynamic hint}) {
throw UnimplementedError("mainIsCanInputMonitoring");
}
bool mainIsShareRdp({dynamic hint}) {
throw UnimplementedError("mainIsShareRdp");
}
Future<void> mainSetShareRdp({required bool enable, dynamic hint}) {
throw UnimplementedError("mainSetShareRdp");
}
bool mainGotoInstall({dynamic hint}) {
throw UnimplementedError("mainGotoInstall");
}
String mainGetNewVersion({dynamic hint}) {
throw UnimplementedError("mainGetNewVersion");
}
bool mainUpdateMe({dynamic hint}) {
throw UnimplementedError("mainUpdateMe");
}
Future<void> setCurSessionId({required UuidValue sessionId, dynamic hint}) {
throw UnimplementedError("setCurSessionId");
}
bool installShowRunWithoutInstall({dynamic hint}) {
throw UnimplementedError("installShowRunWithoutInstall");
}
Future<void> installRunWithoutInstall({dynamic hint}) {
throw UnimplementedError("installRunWithoutInstall");
}
Future<void> installInstallMe(
{required String options, required String path, dynamic hint}) {
throw UnimplementedError("installInstallMe");
}
String installInstallPath({dynamic hint}) {
throw UnimplementedError("installInstallPath");
}
Future<void> mainAccountAuth(
{required String op, required bool rememberMe, dynamic hint}) {
// Safari only allows auth popups while handling the original user gesture.
// Use Future.sync so the JS call runs synchronously (pre-opening the OIDC
// window) while any interop error still surfaces as a Future error.
return Future.sync(() => js.context.callMethod('setByName', [
'account_auth',
jsonEncode({'op': op, 'remember': rememberMe})
]));
}
Future<void> mainAccountAuthCancel({dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['account_auth_cancel']));
}
Future<String> mainAccountAuthResult({dynamic hint}) {
return Future(
() => js.context.callMethod('getByName', ['account_auth_result']));
}
Future<void> mainOnMainWindowClose({dynamic hint}) {
throw UnimplementedError("mainOnMainWindowClose");
}
bool mainCurrentIsWayland({dynamic hint}) {
return false;
}
bool mainIsLoginWayland({dynamic hint}) {
return false;
}
bool mainHideDock({dynamic hint}) {
throw UnimplementedError("mainHideDock");
}
bool mainHasFileClipboard({dynamic hint}) {
return false;
}
bool mainHasGpuTextureRender({dynamic hint}) {
return false;
}
Future<void> cmInit({dynamic hint}) {
throw UnimplementedError("cmInit");
}
Future<void> mainStartIpcUrlServer({dynamic hint}) {
throw UnimplementedError("mainStartIpcUrlServer");
}
Future<void> mainTestWallpaper({required int second, dynamic hint}) {
// TODO: implement mainTestWallpaper
return Future.value();
}
Future<bool> mainSupportRemoveWallpaper({dynamic hint}) {
// TODO: implement mainSupportRemoveWallpaper
return Future.value(false);
}
bool isIncomingOnly({dynamic hint}) {
return false;
}
bool isOutgoingOnly({dynamic hint}) {
return false;
}
bool isCustomClient({dynamic hint}) {
// is_custom_client() checks if app name is not "RustDesk"
return mainGetAppNameSync(hint: hint) != "RustDesk";
}
bool isDisableSettings({dynamic hint}) {
// Checks HARD_SETTINGS["disable-settings"] == "Y"
return mainGetHardOption(key: "disable-settings", hint: hint) == "Y";
}
bool isDisableAb({dynamic hint}) {
// Checks HARD_SETTINGS["disable-ab"] == "Y"
return mainGetHardOption(key: "disable-ab", hint: hint) == "Y";
}
bool isDisableGroupPanel({dynamic hint}) {
// Checks LocalConfig::get_option("disable-group-panel") == "Y"
return mainGetLocalOption(key: "disable-group-panel", hint: hint) == "Y";
}
bool isDisableAccount({dynamic hint}) {
// Checks HARD_SETTINGS["disable-account"] == "Y"
return mainGetHardOption(key: "disable-account", hint: hint) == "Y";
}
bool isDisableInstallation({dynamic hint}) {
return false;
}
Future<bool> isPresetPassword({dynamic hint}) {
return Future.value(false);
}
Future<void> sendUrlScheme({required String url, dynamic hint}) {
throw UnimplementedError("sendUrlScheme");
}
bool isSupportMultiUiSession({required String version, dynamic hint}) {
return versionToNumber(v: version) > versionToNumber(v: '1.2.4');
}
bool isSelinuxEnforcing({dynamic hint}) {
return false;
}
String mainDefaultPrivacyModeImpl({dynamic hint}) {
throw UnimplementedError("mainDefaultPrivacyModeImpl");
}
String mainSupportedPrivacyModeImpls({dynamic hint}) {
return '[]';
}
String mainSupportedInputSource({dynamic hint}) {
return jsonEncode([
['Input source 1', 'input_source_1_tip'],
['Input source 2', 'input_source_2_tip']
]);
}
Future<String> mainGenerate2Fa({dynamic hint}) {
throw UnimplementedError("mainGenerate2Fa");
}
Future<bool> mainVerify2Fa({required String code, dynamic hint}) {
throw UnimplementedError("mainVerify2Fa");
}
bool mainHasValid2FaSync({dynamic hint}) {
throw UnimplementedError("mainHasValid2FaSync");
}
String mainGetHardOption({required String key, dynamic hint}) {
return mainGetLocalOption(key: key, hint: hint);
}
Future<void> mainCheckHwcodec({dynamic hint}) {
throw UnimplementedError("mainCheckHwcodec");
}
Future<void> sessionRequestNewDisplayInitMsgs(
{required UuidValue sessionId, required int display, dynamic hint}) {
throw UnimplementedError("sessionRequestNewDisplayInitMsgs");
}
Future<String> mainHandleWaylandScreencastRestoreToken(
{required String key, required String value, dynamic hint}) {
throw UnimplementedError("mainHandleWaylandScreencastRestoreToken");
}
bool mainIsOptionFixed({required String key, dynamic hint}) {
return false;
}
bool mainGetUseTextureRender({dynamic hint}) {
throw UnimplementedError("mainGetUseTextureRender");
}
bool mainHasValidBotSync({dynamic hint}) {
throw UnimplementedError("mainHasValidBotSync");
}
Future<String> mainVerifyBot({required String token, dynamic hint}) {
throw UnimplementedError("mainVerifyBot");
}
String mainGetUnlockPin({dynamic hint}) {
throw UnimplementedError("mainGetUnlockPin");
}
String mainSetUnlockPin({required String pin, dynamic hint}) {
throw UnimplementedError("mainSetUnlockPin");
}
bool sessionGetEnableTrustedDevices(
{required UuidValue sessionId, dynamic hint}) {
return js.context.callMethod('getByName', ['enable_trusted_devices']) ==
'Y';
}
Future<String> mainGetTrustedDevices({dynamic hint}) {
throw UnimplementedError("mainGetTrustedDevices");
}
Future<void> mainRemoveTrustedDevices({required String json, dynamic hint}) {
throw UnimplementedError("mainRemoveTrustedDevices");
}
Future<void> mainClearTrustedDevices({dynamic hint}) {
throw UnimplementedError("mainClearTrustedDevices");
}
Future<String> getVoiceCallInputDevice({required bool isCm, dynamic hint}) {
throw UnimplementedError("getVoiceCallInputDevice");
}
Future<void> setVoiceCallInputDevice(
{required bool isCm, required String device, dynamic hint}) {
throw UnimplementedError("setVoiceCallInputDevice");
}
bool isPresetPasswordMobileOnly({dynamic hint}) {
throw UnimplementedError("isPresetPasswordMobileOnly");
}
String mainGetBuildinOption({required String key, dynamic hint}) {
return mainGetLocalOption(key: key, hint: hint);
}
String installInstallOptions({dynamic hint}) {
throw UnimplementedError("installInstallOptions");
}
int mainMaxEncryptLen({dynamic hint}) {
throw UnimplementedError("mainMaxEncryptLen");
}
bool mainAudioSupportLoopback({dynamic hint}) {
return false;
}
Future<String> sessionReadLocalEmptyDirsRecursiveSync(
{required UuidValue sessionId,
required String path,
required bool includeHidden,
dynamic hint}) {
throw UnimplementedError("sessionReadLocalEmptyDirsRecursiveSync");
}
Future<void> sessionReadRemoteEmptyDirsRecursiveSync(
{required UuidValue sessionId,
required String path,
required bool includeHidden,
dynamic hint}) {
throw UnimplementedError("sessionReadRemoteEmptyDirsRecursiveSync");
}
Future<void> sessionRenameFile(
{required UuidValue sessionId,
required int actId,
required String path,
required String newName,
required bool isRemote,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'rename_file',
jsonEncode({
'id': actId,
'path': path,
'new_name': newName,
'is_remote': isRemote
})
]));
}
Future<void> sessionSelectFiles(
{required UuidValue sessionId, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', ['select_files']));
}
String? sessionGetConnToken({required UuidValue sessionId, dynamic hint}) {
throw UnimplementedError("sessionGetConnToken");
}
String mainGetPrinterNames({dynamic hint}) {
return '';
}
Future<void> sessionPrinterResponse(
{required UuidValue sessionId,
required int id,
required String path,
required String printerName,
dynamic hint}) {
throw UnimplementedError("sessionPrinterResponse");
}
Future<String> mainGetCommon({required String key, dynamic hint}) {
throw UnimplementedError("mainGetCommon");
}
String mainGetCommonSync({required String key, dynamic hint}) {
throw UnimplementedError("mainGetCommonSync");
}
Future<void> mainSetCommon(
{required String key, required String value, dynamic hint}) {
throw UnimplementedError("mainSetCommon");
}
Future<String> sessionHandleScreenshot(
{required UuidValue sessionId, required String action, dynamic hint}) {
throw UnimplementedError("sessionHandleScreenshot");
}
Future<void> sessionSetCommon(
{required UuidValue sessionId, required String key, required String value, dynamic hint}) {
js.context.callMethod('setByName', [
'common',
jsonEncode({'name': key, 'value': value})
]);
return Future.value();
}
String? sessionGetCommonSync(
{required UuidValue sessionId,
required String key,
required String param,
dynamic hint}) {
throw UnimplementedError("sessionGetCommonSync");
}
Future<void> sessionTakeScreenshot(
{required UuidValue sessionId, required int display, dynamic hint}) {
throw UnimplementedError("sessionTakeScreenshot");
}
Future<void> sessionOpenTerminal(
{required UuidValue sessionId,
required int terminalId,
required int rows,
required int cols,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'open_terminal',
jsonEncode({
'terminal_id': terminalId,
'rows': rows,
'cols': cols,
})
]));
}
Future<void> sessionSendTerminalInput(
{required UuidValue sessionId,
required int terminalId,
required String data,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'send_terminal_input',
jsonEncode({
'terminal_id': terminalId,
'data': data,
})
]));
}
Future<void> sessionResizeTerminal(
{required UuidValue sessionId,
required int terminalId,
required int rows,
required int cols,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'resize_terminal',
jsonEncode({
'terminal_id': terminalId,
'rows': rows,
'cols': cols,
})
]));
}
Future<void> sessionCloseTerminal(
{required UuidValue sessionId, required int terminalId, dynamic hint}) {
return Future(() => js.context.callMethod('setByName', [
'close_terminal',
jsonEncode({
'terminal_id': terminalId,
})
]));
}
Future<int?> sessionGetEdgeScrollEdgeThickness(
{required UuidValue sessionId, dynamic hint}) {
final thickness = js.context.callMethod(
'getByName', ['option:session', 'edge-scroll-edge-thickness']);
return Future(() => int.tryParse(thickness) ?? 100);
}
Future<void> sessionSetEdgeScrollEdgeThickness(
{required UuidValue sessionId, required int value, dynamic hint}) {
return Future(() => js.context.callMethod('setByName',
['option:session', 'edge-scroll-edge-thickness', value.toString()]));
}
String sessionGetConnSessionId({required UuidValue sessionId, dynamic hint}) {
return js.context.callMethod('getByName', ['conn_session_id']);
}
bool willSessionCloseCloseSession(
{required UuidValue sessionId, dynamic hint}) {
return true;
}
String sessionGetLastAuditNote({required UuidValue sessionId, dynamic hint}) {
return js.context.callMethod('getByName', ['last_audit_note']);
}
Future<void> sessionSetAuditGuid(
{required UuidValue sessionId, required String guid, dynamic hint}) {
return Future(
() => js.context.callMethod('setByName', ['audit_guid', guid]));
}
String sessionGetAuditGuid({required UuidValue sessionId, dynamic hint}) {
return js.context.callMethod('getByName', ['audit_guid']);
}
bool mainSetCursorPosition({required int x, required int y, dynamic hint}) {
return false;
}
bool mainClipCursor(
{required int left,
required int top,
required int right,
required int bottom,
required bool enable,
dynamic hint}) {
return false;
}
String mainResolveAvatarUrl({required String avatar, dynamic hint}) {
return js.context.callMethod(
'getByName', ['resolve_avatar_url', avatar])?.toString() ??
avatar;
}
Future<String> mainDeployDevice(
{required String token, required String id, dynamic hint}) {
throw UnimplementedError("mainDeployDevice");
}
void dispose() {}
}