mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-05 15:41:23 +03:00
* hbb_common: bump to the port-forward-mux proto Also latches PortForward.multiplex into login_scope_digest, which destructures PortForward's fields exhaustively by design (a new field must be latched or deliberately ignored to compile). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port_forward_mux: window accounting and channel frame builders Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port_forward_mux: fix RecvWindow counter overflow on long transfers Replace cumulative accounting (granted/received) with remaining credit tracking to prevent u32 overflow after 4 GiB of data on a single channel. Wire behavior is identical, but the fix allows large file transfers without mid-stream channel closure. Add regression test for 8 GiB transfer to verify fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port_forward_mux: credit-windowed relay halves and channel coordinator Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * server: PortForwardMux channel table and per-channel tasks Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * server: multiplexed port-forward connections stay in the protobuf loop Wire PortForwardMux into Connection: take the multiplexed path at login when the controller sets PortForward.multiplex, route PortForwardChannel frames to it from on_message, sweep the channel table's targets after open/close, and clean it up on connection close. Introduce is_port_forward() (socket-based or multiplexed) and use it at the four sites that classify the connection, so a multiplexed connection stays in the message loop, gets TestDelay keepalives, and reports features.port_forward_mux in PeerInfo. The three sites that break into the raw pipe loop or gate the keepalive still check port_forward_socket specifically, since a multiplexed connection must not take that path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * cm: update a port-forward row's targets as tunnel channels come and go Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port_forward_mux: controller tunnel with a single-writer stream loop Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port_forward_mux: publish Muxed before spawning the tunnel loop Publishing after spawn let a loop that dies immediately reset the state first, so the later publish pinned it at Muxed with a dead handle forever. Also adds a test pinning open-before-data ordering across many concurrently opened channels, and drops an unused Clone derive. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: share one multiplexed tunnel across a window's listeners Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: fix round 1 review findings Drop the mux default-false assignment now that definite-assignment proves every path that reads it has set it; the enable-port-forward-mux config commit picks up the missing attribution trailers; the default-on test pins the enable- prefix itself rather than option2bool's weaker fallback. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port_forward_mux: end-to-end tests over a loopback tunnel Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port_forward_mux: fix bulk test's premature half-close, pin the half-close limitation many_channels_echo_concurrently_and_a_bulk_one_does_not_starve_them dropped its bulk write half as soon as writing finished, which shuts down the write side of the socket and, by design (see the design doc's TCP half-close non-goal; today's run_forward does the same), ends the whole channel. Keep the write half alive until the reader is done so the test measures starvation, not half-close. Add a_local_half_close_ends_the_whole_channel to pin that limitation in code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port_forward_mux: cap send credit and other final review fixes Fix 1 (critical): clamp SendCredit to MAX_SEND_CREDIT (= CHANNEL_WINDOW) in both new() and add(), so a peer with tunnel permission can no longer advertise an unbounded window and force the controlled side's unbounded FrameSink::Direct sink to buffer unlimited target data per channel. Fix 2: rename the "starve" test to many_channels_echo_concurrently and drop its (untrue) starvation claim, since it opens every channel before the bulk transfer starts. Add a_channel_opened_during_a_bulk_transfer_ is_served_promptly, which opens the small channel while the bulk one is demonstrably mid-flight. Fix 3: only look up the tunnel permission for `open` frames in the PortForwardChannel arm of on_message, instead of once per data frame. Fix 4: two rustfmt deviations in connection.rs (matches! wrapping and a tuple literal), fixed by hand without a blanket cargo fmt run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port_forward_mux: report a refused channel's reason as an error dialog The controlled side already answers a refused port-forward channel with opened { success: false, message }; on the multiplexed path TunnelHandle:: on_frame only logged that message at debug and closed the channel, so the user saw a closed connection with no explanation, worst on the RDP path where only the RDP client's own error remained. on_frame now returns the message the window should show, deduplicated per distinct reason (capped at MAX_REPORTED_OPEN_ERRORS) so one page load's dozen refused connections surface one dialog per reason instead of a dozen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * Use on_error for refused-channel dialog in tunnel_loop Redirect the refused-channel error through the standard on_error path instead of calling msgbox directly, for consistency with other errors in the port-forward flow. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: apply the whole-branch review Correctness: - listen(): the Legacy arm is merged with the Claimed arm. On its own it ignored outcome.local_eof, so a client that hung up during login still got a target connect, an audit record and a CM row on the controlled side, and ignored outcome.mux, so a peer upgraded while a legacy window stayed open answered as a tunnel while the controller went raw. - Refusal dialogs are deduplicated per quiet spell (10 s) rather than per tunnel lifetime; the lifetime set went silent for the rest of a long-lived window after the first burst. - Android's CM listener handles UpdatePortForward; it fell into `_ => {}`. - relay_socket_to_tunnel reads into one scratch buffer per channel and sends an exact-size copy. A frame owning its 64 KiB read allocation pinned it until sent, once per byte on interactive traffic. Consistency and cleanups: - The controlled side's refusal text is the raw pipe's wording, RDP substitution included. - connection.rs: the PortForwardChannel arm is a one-line hook, the CM label is pushed from the 1 s tick alone, and the unreachable inner.tx fall-through is gone. - The Ready enum is removed; wait_ready() returns Option<Claim>. - SendCredit::add wakes with notify_one alone. - on_ui_command() replaces the two ui_receiver handlers in listen(). - TunnelHandle is no longer re-exported (unused-import warning). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: a legacy window stays legacy until it is reopened Review: the merged `Claimed | Legacy` arm gave a legacy window a hot transition to a tunnel — every accept re-negotiated, and a peer upgraded while the window stayed open was promoted underneath live connections. The product does not need a mode switch inside a window's lifetime, and the transition was extra state-machine surface for nothing: reopening the window picks up an upgraded peer. The two arms are separate again. `Claimed` negotiates once and the peer's answer fixes the window's mode. `Legacy` logs in for every accept as before, asks for no tunnel — `LoginConfigHandler::port_forward_mux` carries the request per login, so the raw pipe never has to talk to a peer that thinks it agreed to multiplex — and ignores what the peer reports. Both arms keep skipping a local socket that hung up during login. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * hbb_common: bump to main with rustdesk/hbb_common#594 merged Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * server: admit only INITIAL_WINDOW on a channel before opened The demultiplexer accepted CHANNEL_WINDOW into a pending channel's unbounded queue, four times the bound the channel task enforces once it polls. The window now starts at INITIAL_WINDOW and is widened right before `opened` advertises the rest. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port_forward_mux: a tunnel ends when its window drops the Tunnel The loop held its own handle and state sender, so once the window closed nothing was left to stop it: it kept answering TestDelay and the peer connection, CM row included, lived on until the peer went away. `Tunnel` now owns a watch sender nobody sends on; the loop's receiver errors when the last `Tunnel` drops, and the loop ends. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: one tunnel per mapping, bound to the authenticated target The login latches `PortForward.host`/`port` into the session scope and approval is shown that target, but a window-wide tunnel let any later `open` name another target with only `enable-tunnel` rechecked. A tunnel now belongs to one listener and serves the one target its login authenticated: the controlled side refuses an `open` for any other target, and a window with several targets uses one connection each, approved on its own. With one owner per tunnel the claim needs no waiters: `Establishing`, `Claim::Wait` and `wait_ready` go, and `try_claim` becomes a plain read. The CM label that followed a tunnel's targets goes with them; a row shows its mapping's target, as before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: the legacy comment names the mapping, not the window Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port_forward_mux: a window violation drops the channel on the spot Both demultiplexers only queued a `Violation` and left the entry until the channel task woke and exited, so a peer that kept sending past the window queued one more entry per frame in the meantime, bounded by nothing. The entry now goes the moment `accept` fails; later frames for that id are unknown-channel noise. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: the login's target travels with the accept, not the handler `listen()` wrote `lc.port_forward` (and, on this branch, `port_forward_mux`) into the window's shared `LoginConfigHandler` before connecting, and `create_login_msg` read them back only when the peer's `Hash` arrived. Two mappings logging in at the same time could therefore swap targets: on master that bridged a local socket to the wrong target, and with a tunnel bound to its login's target it also left the mapping refusing every later accept until it was recreated. The target is now a `PortForward` carried by the interface clone that handles one accept, passed explicitly down to `create_login_msg`; the handler no longer has a field to race on. No lock spans the login. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port_forward_mux: pin permission revocation and whole-tunnel failure in tests Both already hold; the review asked for them to be stated. `enable-tunnel` turned off mid-session refuses the next `open` while the live channel keeps relaying, and a dead tunnel ends every channel on it together, after which the next accept establishes again on the same `Tunnel`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: the legacy comment names re-adding the mapping only Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: the raw pipe runs the code it always ran The multiplexed login had replaced `connect_and_login`, so a mapping with the setting off, a peer without the feature, or a listener latched `Legacy` still went through the tunnel's state machine, the capped pre-read and the changed local-EOF rule. Feature off now means the old code: `listen()` keeps its accept arm and `connect_and_login` as they were, and the tunnel is a branch taken only when the setting is on, in `establish_tunnel` with its own `connect_and_login_mux`. The one line the raw path does differently is the target riding with the accept's interface clone instead of the shared handler. `get_port_forward_mux_enabled` had one caller and moves in here, so `common.rs` is untouched. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: a UI login answers the challenge its own connection was given `handle_login_from_ui` hashed the typed password against `lc.hash`, the window's shared handler field, and the window's password prompt is broadcast to every listener. With two mappings both waiting on that prompt, the `Hash` that arrived last had overwritten the other's, so one of the two answered the wrong challenge and failed to log in. Master shares the same state and broadcasts the same way. The `Hash` is now a parameter of the login; `Session` keeps it beside the connection it belongs to, and the per-accept clone that `with_port_forward` makes gets a slot of its own. `lc.hash` stays for `handle_peer_info`, which only needs the salt, and that is per peer. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: a mapping without its hash waits for it before answering the prompt The window's password prompt is broadcast to every mapping, and can reach one whose own connection has not received its `Hash` yet. That mapping used to answer anyway, with a digest over an empty challenge: the peer refused it and counted a failed attempt, and the empty-salt result was written into the shared `lc.password`, where the mapping that prompted had just stored the right one and the next `handle_peer_info` would persist whatever was there. The connection's challenge is now `Option<Hash>`, `None` until `handle_hash` runs, and `handle_login_from_ui` sends nothing without it. The mapping that prompted stores the salted password in the shared handler, and the waiting one logs in with that against its own challenge when its `Hash` arrives, without prompting again. Test: A answers its prompt, the same broadcast reaches B before its hash, B sends nothing, B's hash arrives and its login carries B's challenge and B's target with no dialog. It runs the real `handle_hash` for B. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: the tunnel's login is the raw pipe's, asked for by a window flag Master's fix for the shared login slots (#16069) keeps the target and the challenge in the window's `LoginConfigHandler` and serializes the mappings' logins with a turn lock, all inside `port_forward.rs`. This branch had carried a broader shape of the same fix, a `with_port_forward` on `Interface` and the target and `Hash` as parameters through the login functions, which every caller had to follow. That is gone: `Interface`, `Session`, `create_login_msg`, `send_login`, `handle_hash` and `handle_login_from_ui` are as on master. What the tunnel needs on top is one bit in the login, `multiplex`. It is a window flag beside `port_forward` in the handler, set once in `io_loop` before the window's mappings start, so an accept's claim and its login read the same value; the setting takes effect for windows opened after it changes. `connect_and_login_mux` is now master's `connect_and_login` with the tunnel's three differences and the same `hash_arrived` and `login_from_ui` calls. The raw pipe is master's, line for line. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * hbb_common: bump to main with rustdesk/hbb_common#595 merged 840c8ec..f94e3fe is that one merge: the five local settings custom clients could not preset. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: the off switch gets a checkbox in Settings → General `enable-port-forward-mux` was readable only by editing the config file. It is a local setting of the controlling side, so it sits with the other outgoing ones, after "Open connection in new tab", with a tooltip saying what it does. The two new keys are translated in every language. The three that the mobile file manager added, "Export", "Export Logs" and "Import Folder", were empty everywhere but five languages; they are filled in too, and Korean's "xdp-portal-unavailable" with them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * Urdu: fill the backlog of empty and missing translations ur.rs had fallen behind: 104 keys carried an empty value and 35 keys the other languages have were absent altogether. Both are filled in, the missing ones in the order template.rs lists them. Eight entries stay empty on purpose. They are keys that only ur.rs still carries, absent from template.rs and from every other language, so their English source cannot be recovered and nothing reads them: remember_account_tip, os_account_desk_tip, another_user_login_*_tip, xorg_not_found_*_tip and no_desktop_*_tip. Twelve more dead keys keep the values they have; removing either group is a separate decision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * Urdu: drop the keys template.rs no longer lists The twenty keys removed here are absent from template.rs and from every other language file; ur.rs was the only one still carrying them, eight of them with no value at all. They are leftovers of features that are gone: the plugin menu, the OS-account login prompts, the Xorg and no-desktop errors. ur.rs now holds exactly the template's key set, all of it translated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: closing the tunnel reaches channels parked on their socket A channel whose far end neither reads nor writes has both relays parked on the socket, not on the inbound queue, so `close_all` dropping the queue's sender woke neither: the socket and both tasks lived on until the far end hung up. Both sides now hold a per-tunnel teardown signal that `run_channel` selects on beside its own cancel, and `close_all` sends it after clearing the map. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: a mapping latched to the raw pipe logs in without asking for the tunnel The login copied the window's `port_forward_mux` into `multiplex`, so a mapping that had latched to the raw pipe on an old peer kept asking for the tunnel. Once that peer was upgraded it answered with a tunnel while the controller switched to raw framing, and every later connection on the mapping was dead until it was re-added. The login now carries its own `port_forward_multiplex`, filled with the target under the turn lock: the probe asks, the raw pipe does not. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: a channel opened as its tunnel closes still gets the teardown `open` can straddle `close_all`: the claim passed, the frame receiver was still alive, and the channel subscribed after the signal had gone out. `watch::subscribe` marks earlier sends as seen, and the entry sits in a map that was already cleared, so nothing would ever end it. The signal is now a level: `close_all` raises it with `send_replace`, which stores even with no channel live, and `run_channel` waits for the value rather than for a change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: the connect guard counts a live tunnel as connected `connect_port_forward_if_needed` returned early only for a raw-pipe socket; called again with a tunnel up it would have built a second `PortForwardMux` and dropped every channel of the first. Not reachable today, since the logon response is sent once, but the other checks in this change already read `is_port_forward()`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * Urdu: the two terminal clipboard keys master added Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: a tunnel's TCP stream refuses packets over twice MAX_FRAME The codec takes a header declaring up to 1 GiB and hands the packet up only once it has all arrived, so the channel window bounded what the peer may send, not what this side buffers. Both sides now cap the codec at 2 * MAX_FRAME as soon as multiplexing is agreed: a data frame with its envelope and MAC fits with room to spare, and a header over the cap ends the tunnel before a byte of payload is read. TCP only; the WebSocket and WebRTC codecs carry caps of their own. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * port forward: a channel id still live when the counter comes round is skipped The controller handed out `next_id` unchecked. 2^32 opens later it lands on a channel still up: the entry here was replaced, while the peer, which ignores an `open` for a live id, kept routing that id to the old socket, so the new local connection's bytes went into the old target connection. The id is now taken under the map's lock and advanced past any id in use. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
717 lines
27 KiB
Dart
717 lines
27 KiB
Dart
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_hbb/common.dart';
|
|
import 'package:flutter_hbb/models/state_model.dart';
|
|
import 'package:get/get.dart';
|
|
|
|
const int kMaxVirtualDisplayCount = 4;
|
|
const int kAllVirtualDisplay = -1;
|
|
|
|
const double kDesktopRemoteTabBarHeight = 28.0;
|
|
const int kInvalidWindowId = -1;
|
|
const int kMainWindowId = 0;
|
|
|
|
const kAllDisplayValue = -1;
|
|
|
|
const kKeyLegacyMode = 'legacy';
|
|
const kKeyMapMode = 'map';
|
|
const kKeyTranslateMode = 'translate';
|
|
|
|
const String kPlatformAdditionsIsWayland = "is_wayland";
|
|
const String kPlatformAdditionsIsInstalled = "is_installed";
|
|
const String kPlatformAdditionsIddImpl = "idd_impl";
|
|
const String kPlatformAdditionsRustDeskVirtualDisplays =
|
|
"rustdesk_virtual_displays";
|
|
const String kPlatformAdditionsAmyuniVirtualDisplays =
|
|
"amyuni_virtual_displays";
|
|
const String kPlatformAdditionsHasFileClipboard = "has_file_clipboard";
|
|
const String kPlatformAdditionsSupportedPrivacyModeImpl =
|
|
"supported_privacy_mode_impl";
|
|
|
|
const String kPrivacyModeImplMag = 'privacy_mode_impl_mag';
|
|
const String kPrivacyModeImplExcludeFromCapture =
|
|
'privacy_mode_impl_exclude_from_capture';
|
|
|
|
const String kPeerPlatformWindows = "Windows";
|
|
const String kPeerPlatformLinux = "Linux";
|
|
const String kPeerPlatformMacOS = "Mac OS";
|
|
const String kPeerPlatformAndroid = "Android";
|
|
const String kPeerPlatformWebDesktop = "WebDesktop";
|
|
|
|
const double kScrollbarThickness = 12.0;
|
|
|
|
/// [kAppTypeMain] used by 'Desktop Main Page' , 'Mobile (Client and Server)', "Install Page"
|
|
const String kAppTypeMain = "main";
|
|
|
|
/// [kAppTypeConnectionManager] only for 'Desktop CM Page'
|
|
const String kAppTypeConnectionManager = "cm";
|
|
|
|
const String kAppTypeDesktopRemote = "remote";
|
|
const String kAppTypeDesktopFileTransfer = "file transfer";
|
|
const String kAppTypeDesktopViewCamera = "view camera";
|
|
const String kAppTypeDesktopPortForward = "port forward";
|
|
const String kAppTypeDesktopTerminal = "terminal";
|
|
|
|
const String kWindowMainWindowOnTop = "main_window_on_top";
|
|
const String kWindowRefreshCurrentUser = "refresh_current_user";
|
|
const String kWindowGetScreenList = "get_screen_list";
|
|
// This method is not used, maybe it can be removed.
|
|
const String kWindowDisableGrabKeyboard = "disable_grab_keyboard";
|
|
const String kWindowActionRebuild = "rebuild";
|
|
const String kWindowEventHide = "hide";
|
|
const String kWindowEventShow = "show";
|
|
const String kWindowConnect = "connect";
|
|
const String kWindowBumpMouse = "bump_mouse";
|
|
|
|
const String kWindowEventNewRemoteDesktop = "new_remote_desktop";
|
|
const String kWindowEventNewFileTransfer = "new_file_transfer";
|
|
const String kWindowEventNewViewCamera = "new_view_camera";
|
|
const String kWindowEventNewPortForward = "new_port_forward";
|
|
const String kWindowEventNewTerminal = "new_terminal";
|
|
const String kWindowEventRestoreTerminalSessions = "restore_terminal_sessions";
|
|
const String kWindowEventActiveSession = "active_session";
|
|
const String kWindowEventActiveDisplaySession = "active_display_session";
|
|
const String kWindowEventGetRemoteList = "get_remote_list";
|
|
const String kWindowEventGetSessionIdList = "get_session_id_list";
|
|
const String kWindowEventRemoteWindowCoords = "remote_window_coords";
|
|
const String kWindowEventSetFullscreen = "set_fullscreen";
|
|
|
|
const String kWindowEventMoveTabToNewWindow = "move_tab_to_new_window";
|
|
const String kWindowEventGetCachedSessionData = "get_cached_session_data";
|
|
const String kWindowEventOpenMonitorSession = "open_monitor_session";
|
|
|
|
const String kOptionViewStyle = "view_style";
|
|
const String kOptionScrollStyle = "scroll_style";
|
|
const String kOptionEdgeScrollEdgeThickness = "edge-scroll-edge-thickness";
|
|
const String kOptionImageQuality = "image_quality";
|
|
const String kOptionOpenNewConnInTabs = "enable-open-new-connections-in-tabs";
|
|
const String kOptionTextureRender = "use-texture-render";
|
|
const String kOptionD3DRender = "allow-d3d-render";
|
|
const String kOptionOpenInTabs = "allow-open-in-tabs";
|
|
const String kOptionOpenInWindows = "allow-open-in-windows";
|
|
const String kOptionForceAlwaysRelay = "force-always-relay";
|
|
const String kOptionViewOnly = "view_only";
|
|
const String kOptionEnableLanDiscovery = "enable-lan-discovery";
|
|
const String kOptionWhitelist = "whitelist";
|
|
const String kOptionIdWhitelist = "id-whitelist";
|
|
const String kOptionEnableAbr = "enable-abr";
|
|
const String kOptionEnableRecordSession = "enable-record-session";
|
|
const String kOptionDirectServer = "direct-server";
|
|
const String kOptionDirectAccessPort = "direct-access-port";
|
|
const String kOptionAllowAutoDisconnect = "allow-auto-disconnect";
|
|
const String kOptionAutoDisconnectTimeout = "auto-disconnect-timeout";
|
|
const String kOptionEnableHwcodec = "enable-hwcodec";
|
|
const String kOptionAllowAutoRecordIncoming = "allow-auto-record-incoming";
|
|
const String kOptionAllowAutoRecordOutgoing = "allow-auto-record-outgoing";
|
|
const String kOptionHideRecordingButton = "hide-recording-button";
|
|
const String kOptionVideoSaveDirectory = "video-save-directory";
|
|
const String kOptionAccessMode = "access-mode";
|
|
const String kOptionEnableKeyboard = "enable-keyboard";
|
|
// "Settings -> Security -> Permissions"
|
|
const String kOptionEnableRemotePrinter = "enable-remote-printer";
|
|
const String kOptionEnableClipboard = "enable-clipboard";
|
|
const String kOptionEnableFileTransfer = "enable-file-transfer";
|
|
const String kOptionEnableAudio = "enable-audio";
|
|
const String kOptionEnableCamera = "enable-camera";
|
|
const String kOptionEnableTerminal = "enable-terminal";
|
|
const String kOptionTerminalPersistent = "terminal-persistent";
|
|
const String kOptionAllowTerminalClipboardWrite =
|
|
"allow-terminal-clipboard-write";
|
|
const String kTerminalClipboardWriteUnconfigured = "";
|
|
const String kTerminalClipboardWriteAllowed = "Y";
|
|
const String kTerminalClipboardWriteDenied = "N";
|
|
const String kOptionEnableTunnel = "enable-tunnel";
|
|
const String kOptionEnableRemoteRestart = "enable-remote-restart";
|
|
const String kOptionEnableBlockInput = "enable-block-input";
|
|
const String kOptionEnablePrivacyMode = "enable-privacy-mode";
|
|
const String kOptionEnablePermChangeInAcceptWindow =
|
|
"enable-perm-change-in-accept-window";
|
|
const String kOptionAllowRemoteConfigModification =
|
|
"allow-remote-config-modification";
|
|
const String kOptionVerificationMethod = "verification-method";
|
|
const String kOptionApproveMode = "approve-mode";
|
|
const String kOptionAllowNumericOneTimePassword =
|
|
"allow-numeric-one-time-password";
|
|
const String kOptionCollapseToolbar = "collapse_toolbar";
|
|
const String kOptionHideToolbar = "hide-toolbar";
|
|
const String kOptionShowRemoteCursor = "show_remote_cursor";
|
|
const String kOptionFollowRemoteCursor = "follow_remote_cursor";
|
|
const String kOptionFollowRemoteWindow = "follow_remote_window";
|
|
const String kOptionZoomCursor = "zoom-cursor";
|
|
const String kOptionShowQualityMonitor = "show_quality_monitor";
|
|
const String kOptionDisableAudio = "disable_audio";
|
|
const String kOptionEnableFileCopyPaste = "enable-file-copy-paste";
|
|
// "Settings -> Display -> Other default options"
|
|
const String kOptionDisableClipboard = "disable_clipboard";
|
|
const String kOptionLockAfterSessionEnd = "lock_after_session_end";
|
|
const String kOptionPrivacyMode = "privacy_mode";
|
|
const String kOptionTouchMode = "touch-mode";
|
|
const String kOptionI444 = "i444";
|
|
const String kOptionSwapLeftRightMouse = "swap-left-right-mouse";
|
|
const String kOptionCodecPreference = "codec-preference";
|
|
const String kOptionRemoteMenubarDragLeft = "remote-menubar-drag-left";
|
|
const String kOptionRemoteMenubarDragRight = "remote-menubar-drag-right";
|
|
const String kOptionRemoteMenubarEdge = "remote-menubar-edge";
|
|
const String kOptionRemoteMenubarFraction = "remote-menubar-frac";
|
|
const String kOptionAllowMultiEdgeToolbarDock =
|
|
"allow-multi-edge-toolbar-dock";
|
|
const String kOptionHideAbTagsPanel = "hideAbTagsPanel";
|
|
const String kOptionRemoteMenubarState = "remoteMenubarState";
|
|
const String kOptionPeerSorting = "peer-sorting";
|
|
const String kOptionPeerTabIndex = "peer-tab-index";
|
|
const String kOptionPeerTabOrder = "peer-tab-order";
|
|
const String kOptionPeerTabVisible = "peer-tab-visible";
|
|
const String kOptionPeerCardUiType = "peer-card-ui-type";
|
|
const String kOptionCurrentAbName = "current-ab-name";
|
|
const String kOptionEnableConfirmClosingTabs = "enable-confirm-closing-tabs";
|
|
const String kOptionEnablePortForwardMux = "enable-port-forward-mux";
|
|
const String kOptionAllowAlwaysSoftwareRender = "allow-always-software-render";
|
|
const String kOptionEnableCheckUpdate = "enable-check-update";
|
|
const String kOptionAllowAutoUpdate = "allow-auto-update";
|
|
const String kOptionAllowRemoveWallpaper = "allow-remove-wallpaper";
|
|
const String kOptionStopService = "stop-service";
|
|
const String kOptionDirectxCapture = "enable-directx-capture";
|
|
const String kOptionAllowRemoteCmModification = "allow-remote-cm-modification";
|
|
const String kOptionEnableUdpPunch = "enable-udp-punch";
|
|
const String kOptionEnableIpv6Punch = "enable-ipv6-punch";
|
|
const String kOptionAllowSyncClipboardBetweenSessions =
|
|
"allow-sync-clipboard-between-sessions";
|
|
const String kOptionEnableTrustedDevices = "enable-trusted-devices";
|
|
const String kOptionShowVirtualMouse = "show-virtual-mouse";
|
|
const String kOptionVirtualMouseScale = "virtual-mouse-scale";
|
|
const String kOptionShowVirtualJoystick = "show-virtual-joystick";
|
|
const String kOptionAllowAskForNoteAtEndOfConnection = "allow-ask-for-note";
|
|
const String kOptionAllowMonitorSwitchMainToolbar = "allow-monitor-switch-main-toolbar";
|
|
const String kOptionAllowMonitorSwitchMinToolbar = "allow-monitor-switch-min-toolbar";
|
|
const String kOptionEnableShowTerminalExtraKeys = "enable-show-terminal-extra-keys";
|
|
const String kOptionShowTerminalCtrlKeys = "show-terminal-extra-ctrl-keys";
|
|
|
|
// network options
|
|
const String kOptionAllowWebSocket = "allow-websocket";
|
|
const String kOptionAllowInsecureTLSFallback = "allow-insecure-tls-fallback";
|
|
const String kOptionDisableUdp = "disable-udp";
|
|
const String kOptionEnableFlutterHttpOnRust = "enable-flutter-http-on-rust";
|
|
|
|
// builtin options
|
|
const String kOptionHideServerSetting = "hide-server-settings";
|
|
const String kOptionHideProxySetting = "hide-proxy-settings";
|
|
const String kOptionHideWebSocketSetting = "hide-websocket-settings";
|
|
const String kOptionHideStopService = "hide-stop-service";
|
|
const String kOptionHideRemotePrinterSetting = "hide-remote-printer-settings";
|
|
const String kOptionHideGeneralSetting = "hide-general-settings";
|
|
const String kOptionHideSecuritySetting = "hide-security-settings";
|
|
const String kOptionHideNetworkSetting = "hide-network-settings";
|
|
const String kOptionRemovePresetPasswordWarning =
|
|
"remove-preset-password-warning";
|
|
const String kOptionDisableChangePermanentPassword =
|
|
"disable-change-permanent-password";
|
|
const String kOptionDisableChangeId = "disable-change-id";
|
|
const String kOptionDisableUnlockPin = "disable-unlock-pin";
|
|
const kHideUsernameOnCard = "hide-username-on-card";
|
|
const String kOptionHideHelpCards = "hide-help-cards";
|
|
const String kOptionAllowDeepLinkPassword = "allow-deep-link-password";
|
|
const String kOptionAllowDeepLinkServerSettings =
|
|
"allow-deep-link-server-settings";
|
|
|
|
const String kOptionToggleViewOnly = "view-only";
|
|
const String kOptionToggleShowMyCursor = "show-my-cursor";
|
|
|
|
const String kOptionDisableFloatingWindow = "disable-floating-window";
|
|
|
|
const String kOptionKeepScreenOn = "keep-screen-on";
|
|
|
|
const String kOptionKeepAwakeDuringIncomingSessions = "keep-awake-during-incoming-sessions";
|
|
const String kOptionKeepAwakeDuringOutgoingSessions = "keep-awake-during-outgoing-sessions";
|
|
|
|
const String kOptionShowMobileAction = "showMobileActions";
|
|
|
|
const String kUrlActionClose = "close";
|
|
|
|
const String kTabLabelHomePage = "Home";
|
|
const String kTabLabelSettingPage = "Settings";
|
|
|
|
const String kWindowPrefix = "wm_";
|
|
const int kWindowMainId = 0;
|
|
|
|
const String kPointerEventKindTouch = "touch";
|
|
const String kPointerEventKindMouse = "mouse";
|
|
|
|
const String kMouseEventTypeDefault = "";
|
|
const String kMouseEventTypePanStart = "pan_start";
|
|
const String kMouseEventTypePanUpdate = "pan_update";
|
|
const String kMouseEventTypePanEnd = "pan_end";
|
|
const String kMouseEventTypeDown = "down";
|
|
const String kMouseEventTypeUp = "up";
|
|
|
|
const String kKeyFlutterKey = "flutter_key";
|
|
|
|
const String kKeyShowDisplaysAsIndividualWindows =
|
|
'displays_as_individual_windows';
|
|
const String kKeyUseAllMyDisplaysForTheRemoteSession =
|
|
'use_all_my_displays_for_the_remote_session';
|
|
const String kKeyShowMonitorsToolbar = 'show_monitors_toolbar';
|
|
const String kKeyReverseMouseWheel = "reverse_mouse_wheel";
|
|
|
|
const String kMsgboxTextWaitingForImage = 'Connected, waiting for image...';
|
|
|
|
// the executable name of the portable version
|
|
const String kEnvPortableExecutable = "RUSTDESK_APPNAME";
|
|
|
|
const Color kColorWarn = Color.fromARGB(255, 245, 133, 59);
|
|
const Color kColorCanvas = Colors.black;
|
|
|
|
const int kMobileDefaultDisplayWidth = 720;
|
|
const int kMobileDefaultDisplayHeight = 1280;
|
|
|
|
const int kDesktopDefaultDisplayWidth = 1080;
|
|
const int kDesktopDefaultDisplayHeight = 720;
|
|
|
|
const int kMobileMaxDisplaySize = 1280;
|
|
const int kDesktopMaxDisplaySize = 3840;
|
|
|
|
const double kDesktopFileTransferRowHeight = 30.0;
|
|
const double kDesktopFileTransferHeaderHeight = 25.0;
|
|
|
|
const double kMinFps = 5;
|
|
const double kDefaultFps = 30;
|
|
const double kMaxFps = 120;
|
|
|
|
const double kMinQuality = 10;
|
|
const double kDefaultQuality = 50;
|
|
const double kMaxQuality = 100;
|
|
const double kMaxMoreQuality = 2000;
|
|
|
|
// trackpad speed
|
|
const String kKeyTrackpadSpeed = 'trackpad-speed';
|
|
const int kMinTrackpadSpeed = 10;
|
|
const int kDefaultTrackpadSpeed = 100;
|
|
const int kMaxTrackpadSpeed = 1000;
|
|
|
|
// relative mouse mode
|
|
/// Throttle duration (in milliseconds) for updating pointer lock center during
|
|
/// window move/resize events. Lower values provide more responsive updates but
|
|
/// may cause performance issues during rapid window operations.
|
|
const int kDefaultPointerLockCenterThrottleMs = 100;
|
|
|
|
/// Minimum server version required for relative mouse mode (MOUSE_TYPE_MOVE_RELATIVE).
|
|
/// Servers older than this version will ignore relative mouse events.
|
|
///
|
|
/// IMPORTANT: This value must be kept in sync with the Rust constant
|
|
/// `MIN_VERSION_RELATIVE_MOUSE_MODE` in `src/common.rs`.
|
|
const String kMinVersionForRelativeMouseMode = '1.4.5';
|
|
|
|
/// Maximum delta value for relative mouse movement.
|
|
/// Large values could cause issues with i32 overflow on server side,
|
|
/// and no reasonable mouse movement should exceed this bound.
|
|
///
|
|
/// IMPORTANT: This value must be kept in sync with the Rust constant
|
|
/// `MAX_RELATIVE_MOUSE_DELTA` in `src/server/input_service.rs`.
|
|
const int kMaxRelativeMouseDelta = 10000;
|
|
|
|
/// Debounce duration (in milliseconds) for relative mouse mode toggle.
|
|
/// This prevents double-toggle from race condition between Rust rdev grab loop
|
|
/// and Flutter keyboard handling. Value should be small enough to allow
|
|
/// intentional quick toggles but large enough to prevent accidental double-triggers.
|
|
const int kRelativeMouseModeToggleDebounceMs = 150;
|
|
|
|
// incomming (should be incoming) is kept, because change it will break the previous setting.
|
|
const String kKeyPrinterIncomingJobAction = 'printer-incomming-job-action';
|
|
const String kValuePrinterIncomingJobDismiss = 'dismiss';
|
|
const String kValuePrinterIncomingJobDefault = '';
|
|
const String kValuePrinterIncomingJobSelected = 'selected';
|
|
const String kKeyPrinterSelected = 'printer-selected-name';
|
|
const String kKeyPrinterSave = 'allow-printer-dialog-save';
|
|
const String kKeyPrinterAllowAutoPrint = 'allow-printer-auto-print';
|
|
|
|
double kNewWindowOffset = isWindows
|
|
? 56.0
|
|
: isLinux
|
|
? 50.0
|
|
: isMacOS
|
|
? 30.0
|
|
: 50.0;
|
|
|
|
const kDragToResizeAreaPaddingSize = 5.0;
|
|
EdgeInsets get kDragToResizeAreaPadding => !kUseCompatibleUiMode && isLinux
|
|
? stateGlobal.fullscreen.isTrue || stateGlobal.isMaximized.value
|
|
? EdgeInsets.zero
|
|
: EdgeInsets.all(kDragToResizeAreaPaddingSize)
|
|
: EdgeInsets.zero;
|
|
// https://en.wikipedia.org/wiki/Non-breaking_space
|
|
const int $nbsp = 0x00A0;
|
|
|
|
extension StringExtension on String {
|
|
String get nonBreaking => replaceAll(' ', String.fromCharCode($nbsp));
|
|
}
|
|
|
|
const Size kConnectionManagerWindowSizeClosedChat = Size(300, 490);
|
|
const Size kConnectionManagerWindowSizeOpenChat = Size(700, 490);
|
|
// Tabbar transition duration, now we remove the duration
|
|
const Duration kTabTransitionDuration = Duration.zero;
|
|
const double kEmptyMarginTop = 50;
|
|
const double kDesktopIconButtonSplashRadius = 20;
|
|
|
|
/// [kMinCursorSize] indicates min cursor (w, h)
|
|
const int kMinCursorSize = 12;
|
|
|
|
const kFullScreenEdgeSize = 0.0;
|
|
const kMaximizeEdgeSize = 0.0;
|
|
// Do not use kWindowResizeEdgeSize directly. Use `windowResizeEdgeSize` in `common.dart` instead.
|
|
const kWindowResizeEdgeSize = 5.0;
|
|
final kWindowBorderWidth = isWindows ? 0.0 : 1.0;
|
|
const kDesktopMenuPadding = EdgeInsets.only(left: 12.0, right: 3.0);
|
|
const kFrameBorderRadius = 12.0;
|
|
const kFrameClipRRectBorderRadius = 12.0;
|
|
const kFrameBoxShadowBlurRadius = 32.0;
|
|
const kFrameBoxShadowOffsetFocused = 4.0;
|
|
const kFrameBoxShadowOffsetUnfocused = 2.0;
|
|
|
|
const kInvalidValueStr = 'InvalidValueStr';
|
|
|
|
// Config key shared by flutter and other ui.
|
|
const kCommConfKeyTheme = 'theme';
|
|
const kCommConfKeyLang = 'lang';
|
|
|
|
const kMobilePageConstraints = BoxConstraints(maxWidth: 600);
|
|
|
|
/// [kMouseControlDistance] indicates the distance that self-side move to get control of mouse.
|
|
const kMouseControlDistance = 12;
|
|
|
|
/// [kMouseControlTimeoutMSec] indicates the timeout (in milliseconds) that self-side can get control of mouse.
|
|
const kMouseControlTimeoutMSec = 1000;
|
|
|
|
/// [kRemoteViewStyleOriginal] Show remote image without scaling.
|
|
const kRemoteViewStyleOriginal = 'original';
|
|
|
|
/// [kRemoteViewStyleAdaptive] Show remote image scaling by ratio factor.
|
|
const kRemoteViewStyleAdaptive = 'adaptive';
|
|
|
|
/// [kRemoteViewStyleCustom] Show remote image at a user-defined scale percent.
|
|
const kRemoteViewStyleCustom = 'custom';
|
|
|
|
/// [kRemoteScrollStyleAuto] Scroll image auto by position.
|
|
const kRemoteScrollStyleAuto = 'scrollauto';
|
|
|
|
/// [kRemoteScrollStyleBar] Scroll image with scroll bar.
|
|
const kRemoteScrollStyleBar = 'scrollbar';
|
|
|
|
/// [kRemoteScrollStyleEdge] Scroll image auto at edges.
|
|
const kRemoteScrollStyleEdge = 'scrolledge';
|
|
|
|
/// [kScrollModeDefault] Mouse or touchpad, the default scroll mode.
|
|
const kScrollModeDefault = 'default';
|
|
|
|
/// [kScrollModeReverse] Mouse or touchpad, the reverse scroll mode.
|
|
const kScrollModeReverse = 'reverse';
|
|
|
|
/// [kRemoteImageQualityBest] Best image quality.
|
|
const kRemoteImageQualityBest = 'best';
|
|
|
|
/// [kRemoteImageQualityBalanced] Balanced image quality, mid performance.
|
|
const kRemoteImageQualityBalanced = 'balanced';
|
|
|
|
/// [kRemoteImageQualityLow] Low image quality, better performance.
|
|
const kRemoteImageQualityLow = 'low';
|
|
|
|
/// [kRemoteImageQualityCustom] Custom image quality.
|
|
const kRemoteImageQualityCustom = 'custom';
|
|
|
|
const kIgnoreDpi = true;
|
|
|
|
const Set<PointerDeviceKind> kTouchBasedDeviceKinds = {
|
|
PointerDeviceKind.touch,
|
|
PointerDeviceKind.stylus,
|
|
PointerDeviceKind.invertedStylus,
|
|
};
|
|
|
|
// Scale custom related constants
|
|
const String kCustomScalePercentKey =
|
|
'custom_scale_percent'; // Flutter option key for storing custom scale percent (integer 5-1000)
|
|
const int kScaleCustomMinPercent = 5;
|
|
const int kScaleCustomPivotPercent = 100; // 100% should be at 1/3 of track
|
|
const int kScaleCustomMaxPercent = 1000;
|
|
const double kScaleCustomPivotPos = 1.0 / 3.0; // first 1/3 → up to 100%
|
|
const double kScaleCustomDetentEpsilon =
|
|
0.006; // snap range around pivot (~0.6%)
|
|
const Duration kDebounceCustomScaleDuration = Duration(milliseconds: 300);
|
|
|
|
// ================================ mobile ================================
|
|
|
|
// Magic numbers, maybe need to avoid it or use a better way to get them.
|
|
const kMobileDelaySoftKeyboard = Duration(milliseconds: 30);
|
|
const kMobileDelaySoftKeyboardFocus = Duration(milliseconds: 30);
|
|
|
|
/// Android constants
|
|
const kActionApplicationDetailsSettings =
|
|
"android.settings.APPLICATION_DETAILS_SETTINGS";
|
|
const kActionAccessibilitySettings = "android.settings.ACCESSIBILITY_SETTINGS";
|
|
|
|
const kRecordAudio = "android.permission.RECORD_AUDIO";
|
|
const kRequestIgnoreBatteryOptimizations =
|
|
"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
|
|
const kSystemAlertWindow = "android.permission.SYSTEM_ALERT_WINDOW";
|
|
const kAndroid13Notification = "android.permission.POST_NOTIFICATIONS";
|
|
|
|
/// Android channel invoke type key
|
|
class AndroidChannel {
|
|
static final kStartAction = "start_action";
|
|
static final kGetStartOnBootOpt = "get_start_on_boot_opt";
|
|
static final kSetStartOnBootOpt = "set_start_on_boot_opt";
|
|
static final kSyncAppDirConfigPath = "sync_app_dir";
|
|
static final kPickImportFiles = "pick_import_files";
|
|
static final kImportFile = "import_file";
|
|
static final kExportFile = "export_file";
|
|
static final kPickImportDirectory = "pick_import_directory";
|
|
static final kImportDirectory = "import_directory";
|
|
static final kExportFiles = "export_files";
|
|
}
|
|
|
|
/// flutter/packages/flutter/lib/src/services/keyboard_key.dart -> _keyLabels
|
|
/// see [LogicalKeyboardKey.keyLabel]
|
|
const Map<int, String> logicalKeyMap = <int, String>{
|
|
0x00000000020: 'VK_SPACE',
|
|
0x00000000022: 'VK_QUOTE',
|
|
0x0000000002c: 'VK_COMMA',
|
|
0x0000000002d: 'VK_MINUS',
|
|
0x0000000002f: 'VK_SLASH',
|
|
0x00000000030: 'VK_0',
|
|
0x00000000031: 'VK_1',
|
|
0x00000000032: 'VK_2',
|
|
0x00000000033: 'VK_3',
|
|
0x00000000034: 'VK_4',
|
|
0x00000000035: 'VK_5',
|
|
0x00000000036: 'VK_6',
|
|
0x00000000037: 'VK_7',
|
|
0x00000000038: 'VK_8',
|
|
0x00000000039: 'VK_9',
|
|
0x0000000003b: 'VK_SEMICOLON',
|
|
0x0000000003d: 'VK_PLUS', // it is =
|
|
0x0000000005b: 'VK_LBRACKET',
|
|
0x0000000005c: 'VK_BACKSLASH',
|
|
0x0000000005d: 'VK_RBRACKET',
|
|
0x00000000061: 'VK_A',
|
|
0x00000000062: 'VK_B',
|
|
0x00000000063: 'VK_C',
|
|
0x00000000064: 'VK_D',
|
|
0x00000000065: 'VK_E',
|
|
0x00000000066: 'VK_F',
|
|
0x00000000067: 'VK_G',
|
|
0x00000000068: 'VK_H',
|
|
0x00000000069: 'VK_I',
|
|
0x0000000006a: 'VK_J',
|
|
0x0000000006b: 'VK_K',
|
|
0x0000000006c: 'VK_L',
|
|
0x0000000006d: 'VK_M',
|
|
0x0000000006e: 'VK_N',
|
|
0x0000000006f: 'VK_O',
|
|
0x00000000070: 'VK_P',
|
|
0x00000000071: 'VK_Q',
|
|
0x00000000072: 'VK_R',
|
|
0x00000000073: 'VK_S',
|
|
0x00000000074: 'VK_T',
|
|
0x00000000075: 'VK_U',
|
|
0x00000000076: 'VK_V',
|
|
0x00000000077: 'VK_W',
|
|
0x00000000078: 'VK_X',
|
|
0x00000000079: 'VK_Y',
|
|
0x0000000007a: 'VK_Z',
|
|
0x00100000008: 'VK_BACK',
|
|
0x00100000009: 'VK_TAB',
|
|
0x0010000000d: 'VK_ENTER',
|
|
0x0010000001b: 'VK_ESCAPE',
|
|
0x0010000007f: 'VK_DELETE',
|
|
0x00100000104: 'VK_CAPITAL',
|
|
0x00100000301: 'VK_DOWN',
|
|
0x00100000302: 'VK_LEFT',
|
|
0x00100000303: 'VK_RIGHT',
|
|
0x00100000304: 'VK_UP',
|
|
0x00100000305: 'VK_END',
|
|
0x00100000306: 'VK_HOME',
|
|
0x00100000307: 'VK_NEXT',
|
|
0x00100000308: 'VK_PRIOR',
|
|
0x00100000401: 'VK_CLEAR',
|
|
0x00100000407: 'VK_INSERT',
|
|
0x00100000504: 'VK_CANCEL',
|
|
0x00100000506: 'VK_EXECUTE',
|
|
0x00100000508: 'VK_HELP',
|
|
0x00100000509: 'VK_PAUSE',
|
|
0x0010000050c: 'VK_SELECT',
|
|
0x00100000608: 'VK_PRINT',
|
|
0x00100000705: 'VK_CONVERT',
|
|
0x00100000706: 'VK_FINAL',
|
|
0x00100000711: 'VK_HANGUL',
|
|
0x00100000712: 'VK_HANJA',
|
|
0x00100000713: 'VK_JUNJA',
|
|
0x00100000718: 'VK_KANA',
|
|
0x00100000719: 'VK_KANJI',
|
|
0x00100000801: 'VK_F1',
|
|
0x00100000802: 'VK_F2',
|
|
0x00100000803: 'VK_F3',
|
|
0x00100000804: 'VK_F4',
|
|
0x00100000805: 'VK_F5',
|
|
0x00100000806: 'VK_F6',
|
|
0x00100000807: 'VK_F7',
|
|
0x00100000808: 'VK_F8',
|
|
0x00100000809: 'VK_F9',
|
|
0x0010000080a: 'VK_F10',
|
|
0x0010000080b: 'VK_F11',
|
|
0x0010000080c: 'VK_F12',
|
|
0x00100000d2b: 'Apps',
|
|
0x00200000002: 'VK_SLEEP',
|
|
0x00200000100: 'VK_CONTROL',
|
|
0x00200000101: 'RControl',
|
|
0x00200000102: 'VK_SHIFT',
|
|
0x00200000103: 'RShift',
|
|
0x00200000104: 'VK_MENU',
|
|
0x00200000105: 'RAlt',
|
|
0x002000001f0: 'VK_CONTROL',
|
|
0x002000001f2: 'VK_SHIFT',
|
|
0x002000001f4: 'VK_MENU',
|
|
0x002000001f6: 'Meta',
|
|
0x0020000022a: 'VK_MULTIPLY',
|
|
0x0020000022b: 'VK_ADD',
|
|
0x0020000022d: 'VK_SUBTRACT',
|
|
0x0020000022e: 'VK_DECIMAL',
|
|
0x0020000022f: 'VK_DIVIDE',
|
|
0x00200000230: 'VK_NUMPAD0',
|
|
0x00200000231: 'VK_NUMPAD1',
|
|
0x00200000232: 'VK_NUMPAD2',
|
|
0x00200000233: 'VK_NUMPAD3',
|
|
0x00200000234: 'VK_NUMPAD4',
|
|
0x00200000235: 'VK_NUMPAD5',
|
|
0x00200000236: 'VK_NUMPAD6',
|
|
0x00200000237: 'VK_NUMPAD7',
|
|
0x00200000238: 'VK_NUMPAD8',
|
|
0x00200000239: 'VK_NUMPAD9',
|
|
};
|
|
|
|
/// flutter/packages/flutter/lib/src/services/keyboard_key.dart -> _debugName
|
|
/// see [PhysicalKeyboardKey.debugName] -> _debugName
|
|
const Map<int, String> physicalKeyMap = <int, String>{
|
|
0x00010082: 'VK_SLEEP',
|
|
0x00070004: 'VK_A',
|
|
0x00070005: 'VK_B',
|
|
0x00070006: 'VK_C',
|
|
0x00070007: 'VK_D',
|
|
0x00070008: 'VK_E',
|
|
0x00070009: 'VK_F',
|
|
0x0007000a: 'VK_G',
|
|
0x0007000b: 'VK_H',
|
|
0x0007000c: 'VK_I',
|
|
0x0007000d: 'VK_J',
|
|
0x0007000e: 'VK_K',
|
|
0x0007000f: 'VK_L',
|
|
0x00070010: 'VK_M',
|
|
0x00070011: 'VK_N',
|
|
0x00070012: 'VK_O',
|
|
0x00070013: 'VK_P',
|
|
0x00070014: 'VK_Q',
|
|
0x00070015: 'VK_R',
|
|
0x00070016: 'VK_S',
|
|
0x00070017: 'VK_T',
|
|
0x00070018: 'VK_U',
|
|
0x00070019: 'VK_V',
|
|
0x0007001a: 'VK_W',
|
|
0x0007001b: 'VK_X',
|
|
0x0007001c: 'VK_Y',
|
|
0x0007001d: 'VK_Z',
|
|
0x0007001e: 'VK_1',
|
|
0x0007001f: 'VK_2',
|
|
0x00070020: 'VK_3',
|
|
0x00070021: 'VK_4',
|
|
0x00070022: 'VK_5',
|
|
0x00070023: 'VK_6',
|
|
0x00070024: 'VK_7',
|
|
0x00070025: 'VK_8',
|
|
0x00070026: 'VK_9',
|
|
0x00070027: 'VK_0',
|
|
0x00070028: 'VK_ENTER',
|
|
0x00070029: 'VK_ESCAPE',
|
|
0x0007002a: 'VK_BACK',
|
|
0x0007002b: 'VK_TAB',
|
|
0x0007002c: 'VK_SPACE',
|
|
0x0007002d: 'VK_MINUS',
|
|
0x0007002e: 'VK_PLUS', // it is =
|
|
0x0007002f: 'VK_LBRACKET',
|
|
0x00070030: 'VK_RBRACKET',
|
|
0x00070033: 'VK_SEMICOLON',
|
|
0x00070034: 'VK_QUOTE',
|
|
0x00070036: 'VK_COMMA',
|
|
0x00070038: 'VK_SLASH',
|
|
0x00070039: 'VK_CAPITAL',
|
|
0x0007003a: 'VK_F1',
|
|
0x0007003b: 'VK_F2',
|
|
0x0007003c: 'VK_F3',
|
|
0x0007003d: 'VK_F4',
|
|
0x0007003e: 'VK_F5',
|
|
0x0007003f: 'VK_F6',
|
|
0x00070040: 'VK_F7',
|
|
0x00070041: 'VK_F8',
|
|
0x00070042: 'VK_F9',
|
|
0x00070043: 'VK_F10',
|
|
0x00070044: 'VK_F11',
|
|
0x00070045: 'VK_F12',
|
|
0x00070049: 'VK_INSERT',
|
|
0x0007004a: 'VK_HOME',
|
|
0x0007004b: 'VK_PRIOR', // Page Up
|
|
0x0007004c: 'VK_DELETE',
|
|
0x0007004d: 'VK_END',
|
|
0x0007004e: 'VK_NEXT', // Page Down
|
|
0x0007004f: 'VK_RIGHT',
|
|
0x00070050: 'VK_LEFT',
|
|
0x00070051: 'VK_DOWN',
|
|
0x00070052: 'VK_UP',
|
|
0x00070053: 'Num Lock', // TODO rust not impl
|
|
0x00070054: 'VK_DIVIDE', // numpad
|
|
0x00070055: 'VK_MULTIPLY',
|
|
0x00070056: 'VK_SUBTRACT',
|
|
0x00070057: 'VK_ADD',
|
|
0x00070058: 'VK_ENTER', // num enter
|
|
0x00070059: 'VK_NUMPAD1',
|
|
0x0007005a: 'VK_NUMPAD2',
|
|
0x0007005b: 'VK_NUMPAD3',
|
|
0x0007005c: 'VK_NUMPAD4',
|
|
0x0007005d: 'VK_NUMPAD5',
|
|
0x0007005e: 'VK_NUMPAD6',
|
|
0x0007005f: 'VK_NUMPAD7',
|
|
0x00070060: 'VK_NUMPAD8',
|
|
0x00070061: 'VK_NUMPAD9',
|
|
0x00070062: 'VK_NUMPAD0',
|
|
0x00070063: 'VK_DECIMAL',
|
|
0x00070075: 'VK_HELP',
|
|
0x00070077: 'VK_SELECT',
|
|
0x00070088: 'VK_KANA',
|
|
0x0007008a: 'VK_CONVERT',
|
|
0x000700e0: 'VK_CONTROL',
|
|
0x000700e1: 'VK_SHIFT',
|
|
0x000700e2: 'VK_MENU',
|
|
0x000700e3: 'Meta',
|
|
0x000700e4: 'RControl',
|
|
0x000700e5: 'RShift',
|
|
0x000700e6: 'RAlt',
|
|
0x000700e7: 'RWin',
|
|
0x000c00b1: 'VK_PAUSE',
|
|
0x000c00cd: 'VK_PAUSE',
|
|
0x000c019e: 'LOCK_SCREEN',
|
|
0x000c0208: 'VK_PRINT',
|
|
};
|
|
|
|
/// The windows targets in the publish time order.
|
|
enum WindowsTarget {
|
|
naw, // not a windows target
|
|
xp,
|
|
vista,
|
|
w7,
|
|
w8,
|
|
w8_1,
|
|
w10,
|
|
w11
|
|
}
|
|
|
|
/// A convenient method to transform a build number to the corresponding windows version.
|
|
extension WindowsTargetExt on int {
|
|
WindowsTarget get windowsVersion => getWindowsTarget(this);
|
|
}
|
|
|
|
const kCheckSoftwareUpdateFinish = 'check_software_update_finish';
|