mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-03-11 15:21:01 +03:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3f43f55c1 | ||
|
|
016a0b1141 | ||
|
|
fd7bcf54bd | ||
|
|
db3f5fe816 | ||
|
|
0d3016fcd8 |
@@ -167,7 +167,7 @@ target/release/rustdesk
|
|||||||
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: графический пользовательский интерфейс на Sciter (устаревшее)
|
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: графический пользовательский интерфейс на Sciter (устаревшее)
|
||||||
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: сервисы аудио, буфера обмена, ввода, видео и сетевых подключений
|
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: сервисы аудио, буфера обмена, ввода, видео и сетевых подключений
|
||||||
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: одноранговое соединение
|
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: одноранговое соединение
|
||||||
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: связь с [сервером Rustdesk](https://github.com/rustdesk/rustdesk-server), ожидает удаленного прямого (через TCP hole punching) или ретранслируемого соединения
|
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: связь с [сервером RustDesk](https://github.com/rustdesk/rustdesk-server), ожидает удаленного прямого (через TCP hole punching) или ретранслируемого соединения
|
||||||
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: специфичный для платформы код
|
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: специфичный для платформы код
|
||||||
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: код Flutter для ПК-версии и мобильных устройств
|
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: код Flutter для ПК-версии и мобильных устройств
|
||||||
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/v1/js)**: JavaScript для Web-клиента Flutter
|
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/v1/js)**: JavaScript для Web-клиента Flutter
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:ui' as ui;
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@@ -65,9 +64,7 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
|
|||||||
bool _showGestureHelp = false;
|
bool _showGestureHelp = false;
|
||||||
String _value = '';
|
String _value = '';
|
||||||
Orientation? _currentOrientation;
|
Orientation? _currentOrientation;
|
||||||
double _viewInsetsBottom = 0;
|
|
||||||
final _uniqueKey = UniqueKey();
|
final _uniqueKey = UniqueKey();
|
||||||
Timer? _timerDidChangeMetrics;
|
|
||||||
Timer? _iosKeyboardWorkaroundTimer;
|
Timer? _iosKeyboardWorkaroundTimer;
|
||||||
|
|
||||||
final _blockableOverlayState = BlockableOverlayState();
|
final _blockableOverlayState = BlockableOverlayState();
|
||||||
@@ -140,7 +137,6 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
|
|||||||
_physicalFocusNode.dispose();
|
_physicalFocusNode.dispose();
|
||||||
await gFFI.close();
|
await gFFI.close();
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
_timerDidChangeMetrics?.cancel();
|
|
||||||
_iosKeyboardWorkaroundTimer?.cancel();
|
_iosKeyboardWorkaroundTimer?.cancel();
|
||||||
gFFI.dialogManager.dismissAll();
|
gFFI.dialogManager.dismissAll();
|
||||||
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
|
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
|
||||||
@@ -167,26 +163,6 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
|
|||||||
gFFI.invokeMethod("try_sync_clipboard");
|
gFFI.invokeMethod("try_sync_clipboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeMetrics() {
|
|
||||||
// If the soft keyboard is visible and the canvas has been changed(panned or scaled)
|
|
||||||
// Don't try reset the view style and focus the cursor.
|
|
||||||
if (gFFI.cursorModel.lastKeyboardIsVisible &&
|
|
||||||
gFFI.canvasModel.isMobileCanvasChanged) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final newBottom = MediaQueryData.fromView(ui.window).viewInsets.bottom;
|
|
||||||
_timerDidChangeMetrics?.cancel();
|
|
||||||
_timerDidChangeMetrics = Timer(Duration(milliseconds: 100), () async {
|
|
||||||
// We need this comparation because poping up the floating action will also trigger `didChangeMetrics()`.
|
|
||||||
if (newBottom != _viewInsetsBottom) {
|
|
||||||
gFFI.canvasModel.mobileFocusCanvasCursor();
|
|
||||||
_viewInsetsBottom = newBottom;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// to-do: It should be better to use transparent color instead of the bgColor.
|
// to-do: It should be better to use transparent color instead of the bgColor.
|
||||||
// But for now, the transparent color will cause the canvas to be white.
|
// But for now, the transparent color will cause the canvas to be white.
|
||||||
// I'm sure that the white color is caused by the Overlay widget in BlockableOverlay.
|
// I'm sure that the white color is caused by the Overlay widget in BlockableOverlay.
|
||||||
|
|||||||
@@ -348,6 +348,12 @@ class InputModel {
|
|||||||
final _trackpadAdjustPeerLinux = 0.06;
|
final _trackpadAdjustPeerLinux = 0.06;
|
||||||
// This is an experience value.
|
// This is an experience value.
|
||||||
final _trackpadAdjustMacToWin = 2.50;
|
final _trackpadAdjustMacToWin = 2.50;
|
||||||
|
// Ignore directional locking for very small deltas on both axes (including
|
||||||
|
// tiny single-axis movement) to avoid over-filtering near zero.
|
||||||
|
static const double _trackpadAxisNoiseThreshold = 0.2;
|
||||||
|
// Lock to dominant axis only when one axis is clearly stronger.
|
||||||
|
// 1.6 means the dominant axis must be >= 60% larger than the other.
|
||||||
|
static const double _trackpadAxisLockRatio = 1.6;
|
||||||
int _trackpadSpeed = kDefaultTrackpadSpeed;
|
int _trackpadSpeed = kDefaultTrackpadSpeed;
|
||||||
double _trackpadSpeedInner = kDefaultTrackpadSpeed / 100.0;
|
double _trackpadSpeedInner = kDefaultTrackpadSpeed / 100.0;
|
||||||
var _trackpadScrollUnsent = Offset.zero;
|
var _trackpadScrollUnsent = Offset.zero;
|
||||||
@@ -1172,6 +1178,7 @@ class InputModel {
|
|||||||
if (isMacOS && peerPlatform == kPeerPlatformWindows) {
|
if (isMacOS && peerPlatform == kPeerPlatformWindows) {
|
||||||
delta *= _trackpadAdjustMacToWin;
|
delta *= _trackpadAdjustMacToWin;
|
||||||
}
|
}
|
||||||
|
delta = _filterTrackpadDeltaAxis(delta);
|
||||||
_trackpadLastDelta = delta;
|
_trackpadLastDelta = delta;
|
||||||
|
|
||||||
var x = delta.dx.toInt();
|
var x = delta.dx.toInt();
|
||||||
@@ -1204,6 +1211,24 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Offset _filterTrackpadDeltaAxis(Offset delta) {
|
||||||
|
final absDx = delta.dx.abs();
|
||||||
|
final absDy = delta.dy.abs();
|
||||||
|
// Keep diagonal intent when movement is tiny on both axes.
|
||||||
|
if (absDx < _trackpadAxisNoiseThreshold &&
|
||||||
|
absDy < _trackpadAxisNoiseThreshold) {
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
// Dominant-axis lock to reduce accidental cross-axis scrolling noise.
|
||||||
|
if (absDy >= absDx * _trackpadAxisLockRatio) {
|
||||||
|
return Offset(0, delta.dy);
|
||||||
|
}
|
||||||
|
if (absDx >= absDy * _trackpadAxisLockRatio) {
|
||||||
|
return Offset(delta.dx, 0);
|
||||||
|
}
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
|
||||||
void _scheduleFling(double x, double y, int delay) {
|
void _scheduleFling(double x, double y, int delay) {
|
||||||
if (isViewCamera) return;
|
if (isViewCamera) return;
|
||||||
if ((x == 0 && y == 0) || _stopFling) {
|
if ((x == 0 && y == 0) || _stopFling) {
|
||||||
|
|||||||
@@ -2152,6 +2152,9 @@ class CanvasModel with ChangeNotifier {
|
|||||||
ViewStyle _lastViewStyle = ViewStyle.defaultViewStyle();
|
ViewStyle _lastViewStyle = ViewStyle.defaultViewStyle();
|
||||||
|
|
||||||
Timer? _timerMobileFocusCanvasCursor;
|
Timer? _timerMobileFocusCanvasCursor;
|
||||||
|
Timer? _timerMobileRestoreCanvasOffset;
|
||||||
|
Offset? _offsetBeforeMobileSoftKeyboard;
|
||||||
|
double? _scaleBeforeMobileSoftKeyboard;
|
||||||
|
|
||||||
// `isMobileCanvasChanged` is used to avoid canvas reset when changing the input method
|
// `isMobileCanvasChanged` is used to avoid canvas reset when changing the input method
|
||||||
// after showing the soft keyboard.
|
// after showing the soft keyboard.
|
||||||
@@ -2639,6 +2642,9 @@ class CanvasModel with ChangeNotifier {
|
|||||||
_scale = 1.0;
|
_scale = 1.0;
|
||||||
_lastViewStyle = ViewStyle.defaultViewStyle();
|
_lastViewStyle = ViewStyle.defaultViewStyle();
|
||||||
_timerMobileFocusCanvasCursor?.cancel();
|
_timerMobileFocusCanvasCursor?.cancel();
|
||||||
|
_timerMobileRestoreCanvasOffset?.cancel();
|
||||||
|
_offsetBeforeMobileSoftKeyboard = null;
|
||||||
|
_scaleBeforeMobileSoftKeyboard = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateScrollPercent() {
|
updateScrollPercent() {
|
||||||
@@ -2667,6 +2673,31 @@ class CanvasModel with ChangeNotifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void saveMobileOffsetBeforeSoftKeyboard() {
|
||||||
|
_timerMobileRestoreCanvasOffset?.cancel();
|
||||||
|
_offsetBeforeMobileSoftKeyboard = Offset(_x, _y);
|
||||||
|
_scaleBeforeMobileSoftKeyboard = _scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void restoreMobileOffsetAfterSoftKeyboard() {
|
||||||
|
_timerMobileRestoreCanvasOffset?.cancel();
|
||||||
|
_timerMobileFocusCanvasCursor?.cancel();
|
||||||
|
final targetOffset = _offsetBeforeMobileSoftKeyboard;
|
||||||
|
final targetScale = _scaleBeforeMobileSoftKeyboard;
|
||||||
|
if (targetOffset == null || targetScale == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_timerMobileRestoreCanvasOffset = Timer(Duration(milliseconds: 100), () {
|
||||||
|
updateSize();
|
||||||
|
_x = targetOffset.dx;
|
||||||
|
_y = targetOffset.dy;
|
||||||
|
_scale = targetScale;
|
||||||
|
_offsetBeforeMobileSoftKeyboard = null;
|
||||||
|
_scaleBeforeMobileSoftKeyboard = null;
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// mobile only
|
// mobile only
|
||||||
// Move the canvas to make the cursor visible(center) on the screen.
|
// Move the canvas to make the cursor visible(center) on the screen.
|
||||||
void _moveToCenterCursor() {
|
void _moveToCenterCursor() {
|
||||||
@@ -2919,8 +2950,13 @@ class CursorModel with ChangeNotifier {
|
|||||||
_lastIsBlocked = true;
|
_lastIsBlocked = true;
|
||||||
}
|
}
|
||||||
if (isMobile && _lastKeyboardIsVisible != keyboardIsVisible) {
|
if (isMobile && _lastKeyboardIsVisible != keyboardIsVisible) {
|
||||||
parent.target?.canvasModel.mobileFocusCanvasCursor();
|
if (keyboardIsVisible) {
|
||||||
parent.target?.canvasModel.isMobileCanvasChanged = false;
|
parent.target?.canvasModel.saveMobileOffsetBeforeSoftKeyboard();
|
||||||
|
parent.target?.canvasModel.mobileFocusCanvasCursor();
|
||||||
|
parent.target?.canvasModel.isMobileCanvasChanged = false;
|
||||||
|
} else {
|
||||||
|
parent.target?.canvasModel.restoreMobileOffsetAfterSoftKeyboard();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_lastKeyboardIsVisible = keyboardIsVisible;
|
_lastKeyboardIsVisible = keyboardIsVisible;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -286,10 +286,14 @@ fn heartbeat_url() -> String {
|
|||||||
|
|
||||||
fn handle_config_options(config_options: HashMap<String, String>) {
|
fn handle_config_options(config_options: HashMap<String, String>) {
|
||||||
let mut options = Config::get_options();
|
let mut options = Config::get_options();
|
||||||
|
let default_settings = config::DEFAULT_SETTINGS.read().unwrap().clone();
|
||||||
config_options
|
config_options
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, v)| {
|
.map(|(k, v)| {
|
||||||
if v.is_empty() {
|
// Priority: user config > default advanced options.
|
||||||
|
// Only when default advanced options are also empty, remove user option (fallback to built-in default);
|
||||||
|
// otherwise insert an empty value so user config remains present.
|
||||||
|
if v.is_empty() && default_settings.get(k).map_or("", |v| v).is_empty() {
|
||||||
options.remove(k);
|
options.remove(k);
|
||||||
} else {
|
} else {
|
||||||
options.insert(k.to_string(), v.to_string());
|
options.insert(k.to_string(), v.to_string());
|
||||||
|
|||||||
@@ -738,5 +738,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Changelog", "Változáslista"),
|
("Changelog", "Változáslista"),
|
||||||
("keep-awake-during-outgoing-sessions-label", "Képernyő aktív állapotban tartása a kimenő munkamenetek során"),
|
("keep-awake-during-outgoing-sessions-label", "Képernyő aktív állapotban tartása a kimenő munkamenetek során"),
|
||||||
("keep-awake-during-incoming-sessions-label", "Képernyő aktív állapotban tartása a bejövő munkamenetek során"),
|
("keep-awake-during-incoming-sessions-label", "Képernyő aktív állapotban tartása a bejövő munkamenetek során"),
|
||||||
|
("Continue with {}", "Folytatás ezzel: {}"),
|
||||||
|
("Display Name", "Kijelző név"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user