Compare commits

...

15 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
25719ecd0d Improve code quality - module-level constant and better error handling
Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
2026-02-05 03:55:09 +00:00
copilot-swe-agent[bot]
5d490b2bdd Address code review feedback - remove unused imports and use const for backlog
Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
2026-02-05 03:54:00 +00:00
copilot-swe-agent[bot]
7de2390d23 Add documentation for network interface binding feature
Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
2026-02-05 03:52:52 +00:00
copilot-swe-agent[bot]
16dde51e23 Add unit test for bind interface IP address parsing
Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
2026-02-05 03:52:03 +00:00
copilot-swe-agent[bot]
780f99bd32 Implement bind-interface option for network binding without modifying submodule
Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
2026-02-05 03:50:48 +00:00
copilot-swe-agent[bot]
1b87c33fdc Add bind-interface configuration option for network binding
Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
2026-02-05 03:49:20 +00:00
copilot-swe-agent[bot]
ef59778720 Initial plan 2026-02-05 03:43:51 +00:00
Daniel Marschall
4fa5e99e65 Remove unused option_env!(...) (#13959) 2026-02-03 20:55:34 +08:00
bilimiyorum
5ee9dcf42d Update tr.rs (#14160)
The previous PR was reverted due to an incorrect file path. This PR applies the same updates to src/lang/tr.rs.
2026-02-02 22:18:36 +08:00
Copilot
6306f83316 Fix non-link text color in dialogs with links for dark theme (#14220)
* Initial plan

* Fix dialog text color for dark theme with links

Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>

* Keep original link color (blue), only fix non-link text color

Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>

* fix: dialog text color in dark theme

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

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
2026-02-01 12:18:07 +08:00
XLion
96075fdf49 Update tw.rs (#14138) 2026-01-31 16:38:09 +08:00
Copilot
8c6dcf53a6 iOS terminal: Add touch swipe and floating back button for exit (#14208)
* Initial plan

* Add iOS edge swipe gesture to exit terminal session

Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>

* Improve iOS edge swipe gesture with responsive thresholds and better gesture handling

Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>

* Fix: Reset _swipeCurrentX in onHorizontalDragStart to prevent stale state

Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>

* Add trackpad support documentation for iOS edge swipe gesture

Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>

* Add iOS-style circular back button to terminal page

Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>

* Remove trackpad support documentation - not needed with back button

Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>

* Filter edge swipe gesture to touch-only input (exclude mouse/trackpad)

Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>

* fix: missing import

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

* fix(ios): terminal swip exit gesture

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

* Update flutter/lib/mobile/pages/terminal_page.dart

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-31 16:37:45 +08:00
fufesou
e1b1a927b8 fix(ios): capsLock, workaround #5871 (#14194)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-01-30 17:32:18 +08:00
fufesou
1e6bfa7bb1 fix(iPad): Magic Mouse, click (#14188)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-01-29 15:25:44 +08:00
fufesou
79ef4c4501 Copilot/fix action run error (#14186)
* Initial plan

* Fix macOS build: Remove @available check causing linker error

The @available check in GetDisplayName was causing the linker to look for
__isPlatformVersionAtLeast symbol which is not available when targeting
macOS 10.14. Since this function is only used for logging, we simplify it
to return "Unknown" for all displays, avoiding the runtime availability check.

Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>

* fix(macOS): ___isPlatformVersionAtLeast is not available in macOS 10.14

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

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
2026-01-28 17:44:17 +08:00
10 changed files with 500 additions and 161 deletions

109
docs/NETWORK_BINDING.md Normal file
View File

@@ -0,0 +1,109 @@
# Network Interface Binding
RustDesk can be configured to bind to a specific network interface by IP address.
## Configuration
To bind RustDesk to a specific network interface, set the `bind-interface` option in your configuration.
### Option Name
`bind-interface`
### Supported Values
- Empty string (default): Bind to all available interfaces (0.0.0.0 for IPv4, :: for IPv6)
- IPv4 address: e.g., `192.168.1.100`, `10.0.0.1`
- IPv6 address: e.g., `::1`, `fe80::1`, `2001:db8::1`
## Usage Examples
### Bind to a specific IPv4 address
To bind RustDesk to only listen on interface with IP address `192.168.1.100`:
```json
{
"options": {
"bind-interface": "192.168.1.100"
}
}
```
### Bind to IPv6 localhost
```json
{
"options": {
"bind-interface": "::1"
}
}
```
### Bind to all interfaces (default)
```json
{
"options": {
"bind-interface": ""
}
}
```
Or simply omit the `bind-interface` option entirely.
## How It Works
When the `bind-interface` option is set:
1. RustDesk reads the configuration when starting the direct server
2. If `bind-interface` is empty or not set, it binds to all available network interfaces
3. If `bind-interface` contains a valid IP address:
- RustDesk validates the IP address format (supports both IPv4 and IPv6)
- Creates a TCP socket and binds it to the specified IP address
- Starts listening for connections on that interface only
## Use Cases
### Multiple Network Interfaces
If your machine has multiple network interfaces (e.g., Ethernet, Wi-Fi, VPN), you can force RustDesk to use a specific one:
- **Example**: Force RustDesk to use the Ethernet interface at `192.168.1.100` instead of the Wi-Fi interface at `192.168.2.50`
### Security
Restrict RustDesk to listen only on internal network interfaces:
- **Example**: Bind to `10.0.0.5` (internal network) instead of listening on all interfaces including public-facing ones
### VPN/Tunneling
Force RustDesk to use a VPN or tunnel interface:
- **Example**: Bind to the VPN interface IP address to ensure all traffic goes through the VPN
## Troubleshooting
### Invalid bind address error
If you see an error like "Invalid bind interface address", check that:
- The IP address format is correct (no typos)
- The IP address is valid (e.g., not `999.999.999.999`)
- The IP address exists on one of your machine's network interfaces
### Failed to start direct server
If RustDesk fails to start with a bind error, it could be because:
- The specified IP address doesn't exist on your machine
- Another application is already using the port on that interface
- You don't have permission to bind to that address
### Finding your network interface IP addresses
**Windows:**
```cmd
ipconfig
```
**Linux/macOS:**
```bash
ip addr show # Linux
ifconfig # macOS/Linux
```
Look for the `inet` (IPv4) or `inet6` (IPv6) addresses associated with your network interfaces.
## Related Discussion
This feature was implemented to address: https://github.com/rustdesk/rustdesk/discussions/2286

View File

@@ -1124,18 +1124,23 @@ class CustomAlertDialog extends StatelessWidget {
Widget createDialogContent(String text) {
final RegExp linkRegExp = RegExp(r'(https?://[^\s]+)');
bool hasLink = linkRegExp.hasMatch(text);
// Early return: no link, use default theme color
if (!hasLink) {
return SelectableText(text, style: const TextStyle(fontSize: 15));
}
final List<TextSpan> spans = [];
int start = 0;
bool hasLink = false;
linkRegExp.allMatches(text).forEach((match) {
hasLink = true;
if (match.start > start) {
spans.add(TextSpan(text: text.substring(start, match.start)));
}
spans.add(TextSpan(
text: match.group(0) ?? '',
style: TextStyle(
style: const TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
),
@@ -1153,13 +1158,9 @@ Widget createDialogContent(String text) {
spans.add(TextSpan(text: text.substring(start)));
}
if (!hasLink) {
return SelectableText(text, style: const TextStyle(fontSize: 15));
}
return SelectableText.rich(
TextSpan(
style: TextStyle(color: Colors.black, fontSize: 15),
style: const TextStyle(fontSize: 15),
children: spans,
),
);

View File

@@ -107,6 +107,8 @@ class _RawTouchGestureDetectorRegionState
// For mouse mode, we need to block the events when the cursor is in a blocked area.
// So we need to cache the last tap down position.
Offset? _lastTapDownPositionForMouseMode;
// Cache global position for onTap (which lacks position info).
Offset? _lastTapDownGlobalPosition;
FFI get ffi => widget.ffi;
FfiModel get ffiModel => widget.ffiModel;
@@ -136,6 +138,7 @@ class _RawTouchGestureDetectorRegionState
onTapDown(TapDownDetails d) async {
lastDeviceKind = d.kind;
_lastTapDownGlobalPosition = d.globalPosition;
if (isNotTouchBasedDevice()) {
return;
}
@@ -154,6 +157,10 @@ class _RawTouchGestureDetectorRegionState
if (isNotTouchBasedDevice()) {
return;
}
// Filter duplicate touch tap events on iOS (Magic Mouse issue).
if (inputModel.shouldIgnoreTouchTap(d.globalPosition)) {
return;
}
if (handleTouch) {
final isMoved =
await ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
@@ -171,6 +178,11 @@ class _RawTouchGestureDetectorRegionState
if (isNotTouchBasedDevice()) {
return;
}
// Filter duplicate touch tap events on iOS (Magic Mouse issue).
final lastPos = _lastTapDownGlobalPosition;
if (lastPos != null && inputModel.shouldIgnoreTouchTap(lastPos)) {
return;
}
if (!handleTouch) {
// Cannot use `_lastTapDownDetails` because Flutter calls `onTapUp` before `onTap`, clearing the cached details.
// Using `_lastTapDownPositionForMouseMode` instead.

View File

@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/common.dart';
@@ -41,6 +42,9 @@ class _TerminalPageState extends State<TerminalPage>
final GlobalKey _keyboardKey = GlobalKey();
double _keyboardHeight = 0;
late bool _showTerminalExtraKeys;
// For iOS edge swipe gesture
double _swipeStartX = 0;
double _swipeCurrentX = 0;
// For web only.
// 'monospace' does not work on web, use Google Fonts, `??` is only for null safety.
@@ -147,7 +151,7 @@ class _TerminalPageState extends State<TerminalPage>
}
Widget buildBody() {
return Scaffold(
final scaffold = Scaffold(
resizeToAvoidBottomInset: false, // Disable automatic layout adjustment; manually control UI updates to prevent flickering when the keyboard shows/hides
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
body: Stack(
@@ -192,9 +196,108 @@ class _TerminalPageState extends State<TerminalPage>
),
),
if (_showTerminalExtraKeys) _buildFloatingKeyboard(),
// iOS-style circular close button in top-right corner
if (isIOS) _buildCloseButton(),
],
),
);
// Add iOS edge swipe gesture to exit (similar to Android back button)
if (isIOS) {
return LayoutBuilder(
builder: (context, constraints) {
final screenWidth = constraints.maxWidth;
// Base thresholds on screen width but clamp to reasonable logical pixel ranges
// Edge detection region: ~10% of width, clamped between 20 and 80 logical pixels
final edgeThreshold = (screenWidth * 0.1).clamp(20.0, 80.0);
// Required horizontal movement: ~25% of width, clamped between 80 and 300 logical pixels
final swipeThreshold = (screenWidth * 0.25).clamp(80.0, 300.0);
return RawGestureDetector(
behavior: HitTestBehavior.translucent,
gestures: <Type, GestureRecognizerFactory>{
HorizontalDragGestureRecognizer: GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>(
() => HorizontalDragGestureRecognizer(
debugOwner: this,
// Only respond to touch input, exclude mouse/trackpad
supportedDevices: kTouchBasedDeviceKinds,
),
(HorizontalDragGestureRecognizer instance) {
instance
// Capture initial touch-down position (before touch slop)
..onDown = (details) {
_swipeStartX = details.localPosition.dx;
_swipeCurrentX = details.localPosition.dx;
}
..onUpdate = (details) {
_swipeCurrentX = details.localPosition.dx;
}
..onEnd = (details) {
// Check if swipe started from left edge and moved right
if (_swipeStartX < edgeThreshold && (_swipeCurrentX - _swipeStartX) > swipeThreshold) {
clientClose(sessionId, _ffi);
}
_swipeStartX = 0;
_swipeCurrentX = 0;
}
..onCancel = () {
_swipeStartX = 0;
_swipeCurrentX = 0;
};
},
),
},
child: scaffold,
);
},
);
}
return scaffold;
}
Widget _buildCloseButton() {
return Positioned(
top: 0,
right: 0,
child: SafeArea(
minimum: const EdgeInsets.only(
top: 16, // iOS standard margin
right: 16, // iOS standard margin
),
child: Semantics(
button: true,
label: translate('Close'),
child: Container(
width: 44, // iOS standard tap target size
height: 44,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5), // Half transparency
shape: BoxShape.circle,
),
child: Material(
color: Colors.transparent,
shape: const CircleBorder(),
clipBehavior: Clip.antiAlias,
child: InkWell(
customBorder: const CircleBorder(),
onTap: () {
clientClose(sessionId, _ffi);
},
child: Tooltip(
message: translate('Close'),
child: const Icon(
Icons.chevron_left, // iOS-style back arrow
color: Colors.white,
size: 28,
),
),
),
),
),
),
),
);
}
Widget _buildFloatingKeyboard() {

View File

@@ -59,7 +59,8 @@ class CanvasCoords {
model.scale = json['scale'];
model.scrollX = json['scrollX'];
model.scrollY = json['scrollY'];
model.scrollStyle = ScrollStyle.fromJson(json['scrollStyle'], ScrollStyle.scrollauto);
model.scrollStyle =
ScrollStyle.fromJson(json['scrollStyle'], ScrollStyle.scrollauto);
model.size = Size(json['size']['w'], json['size']['h']);
return model;
}
@@ -418,6 +419,74 @@ class InputModel {
});
}
// https://github.com/flutter/flutter/issues/157241
// Infer CapsLock state from the character output.
// This is needed because Flutter's HardwareKeyboard.lockModesEnabled may report
// incorrect CapsLock state on iOS.
bool _getIosCapsFromCharacter(KeyEvent e) {
if (!isIOS) return false;
final ch = e.character;
return _getIosCapsFromCharacterImpl(
ch, HardwareKeyboard.instance.isShiftPressed);
}
// RawKeyEvent version of _getIosCapsFromCharacter.
bool _getIosCapsFromRawCharacter(RawKeyEvent e) {
if (!isIOS) return false;
final ch = e.character;
return _getIosCapsFromCharacterImpl(ch, e.isShiftPressed);
}
// Shared implementation for inferring CapsLock state from character.
// Uses Unicode-aware case detection to support non-ASCII letters (e.g., ü/Ü, é/É).
//
// Limitations:
// 1. This inference assumes the client and server use the same keyboard layout.
// If layouts differ (e.g., client uses EN, server uses DE), the character output
// may not match expectations. For example, ';' on EN layout maps to 'ö' on DE
// layout, making it impossible to correctly infer CapsLock state from the
// character alone.
// 2. On iOS, CapsLock+Shift produces uppercase letters (unlike desktop where it
// produces lowercase). This method cannot handle that case correctly.
bool _getIosCapsFromCharacterImpl(String? ch, bool shiftPressed) {
if (ch == null || ch.length != 1) return false;
// Use Dart's built-in Unicode-aware case detection
final upper = ch.toUpperCase();
final lower = ch.toLowerCase();
final isUpper = upper == ch && lower != ch;
final isLower = lower == ch && upper != ch;
// Skip non-letter characters (e.g., numbers, symbols, CJK characters without case)
if (!isUpper && !isLower) return false;
return isUpper != shiftPressed;
}
int _buildLockModes(bool iosCapsLock) {
const capslock = 1;
const numlock = 2;
const scrolllock = 3;
int lockModes = 0;
if (isIOS) {
if (iosCapsLock) {
lockModes |= (1 << capslock);
}
// Ignore "NumLock/ScrollLock" on iOS for now.
} else {
if (HardwareKeyboard.instance.lockModesEnabled
.contains(KeyboardLockMode.capsLock)) {
lockModes |= (1 << capslock);
}
if (HardwareKeyboard.instance.lockModesEnabled
.contains(KeyboardLockMode.numLock)) {
lockModes |= (1 << numlock);
}
if (HardwareKeyboard.instance.lockModesEnabled
.contains(KeyboardLockMode.scrollLock)) {
lockModes |= (1 << scrolllock);
}
}
return lockModes;
}
// This function must be called after the peer info is received.
// Because `sessionGetKeyboardMode` relies on the peer version.
updateKeyboardMode() async {
@@ -550,6 +619,11 @@ class InputModel {
return KeyEventResult.handled;
}
bool iosCapsLock = false;
if (isIOS && e is RawKeyDownEvent) {
iosCapsLock = _getIosCapsFromRawCharacter(e);
}
final key = e.logicalKey;
if (e is RawKeyDownEvent) {
if (!e.repeat) {
@@ -586,7 +660,7 @@ class InputModel {
// * Currently mobile does not enable map mode
if ((isDesktop || isWebDesktop) && keyboardMode == kKeyMapMode) {
mapKeyboardModeRaw(e);
mapKeyboardModeRaw(e, iosCapsLock);
} else {
legacyKeyboardModeRaw(e);
}
@@ -622,6 +696,11 @@ class InputModel {
return KeyEventResult.handled;
}
bool iosCapsLock = false;
if (isIOS && (e is KeyDownEvent || e is KeyRepeatEvent)) {
iosCapsLock = _getIosCapsFromCharacter(e);
}
if (e is KeyUpEvent) {
handleKeyUpEventModifiers(e);
} else if (e is KeyDownEvent) {
@@ -667,7 +746,8 @@ class InputModel {
e.character ?? '',
e.physicalKey.usbHidUsage & 0xFFFF,
// Show repeat event be converted to "release+press" events?
e is KeyDownEvent || e is KeyRepeatEvent);
e is KeyDownEvent || e is KeyRepeatEvent,
iosCapsLock);
} else {
legacyKeyboardMode(e);
}
@@ -676,23 +756,9 @@ class InputModel {
}
/// Send Key Event
void newKeyboardMode(String character, int usbHid, bool down) {
const capslock = 1;
const numlock = 2;
const scrolllock = 3;
int lockModes = 0;
if (HardwareKeyboard.instance.lockModesEnabled
.contains(KeyboardLockMode.capsLock)) {
lockModes |= (1 << capslock);
}
if (HardwareKeyboard.instance.lockModesEnabled
.contains(KeyboardLockMode.numLock)) {
lockModes |= (1 << numlock);
}
if (HardwareKeyboard.instance.lockModesEnabled
.contains(KeyboardLockMode.scrollLock)) {
lockModes |= (1 << scrolllock);
}
void newKeyboardMode(
String character, int usbHid, bool down, bool iosCapsLock) {
final lockModes = _buildLockModes(iosCapsLock);
bind.sessionHandleFlutterKeyEvent(
sessionId: sessionId,
character: character,
@@ -701,7 +767,7 @@ class InputModel {
downOrUp: down);
}
void mapKeyboardModeRaw(RawKeyEvent e) {
void mapKeyboardModeRaw(RawKeyEvent e, bool iosCapsLock) {
int positionCode = -1;
int platformCode = -1;
bool down;
@@ -732,27 +798,14 @@ class InputModel {
} else {
down = false;
}
inputRawKey(e.character ?? '', platformCode, positionCode, down);
inputRawKey(
e.character ?? '', platformCode, positionCode, down, iosCapsLock);
}
/// Send raw Key Event
void inputRawKey(String name, int platformCode, int positionCode, bool down) {
const capslock = 1;
const numlock = 2;
const scrolllock = 3;
int lockModes = 0;
if (HardwareKeyboard.instance.lockModesEnabled
.contains(KeyboardLockMode.capsLock)) {
lockModes |= (1 << capslock);
}
if (HardwareKeyboard.instance.lockModesEnabled
.contains(KeyboardLockMode.numLock)) {
lockModes |= (1 << numlock);
}
if (HardwareKeyboard.instance.lockModesEnabled
.contains(KeyboardLockMode.scrollLock)) {
lockModes |= (1 << scrolllock);
}
void inputRawKey(String name, int platformCode, int positionCode, bool down,
bool iosCapsLock) {
final lockModes = _buildLockModes(iosCapsLock);
bind.sessionHandleFlutterRawKeyEvent(
sessionId: sessionId,
name: name,
@@ -826,6 +879,9 @@ class InputModel {
Map<String, dynamic> _getMouseEvent(PointerEvent evt, String type) {
final Map<String, dynamic> out = {};
bool hasStaleButtonsOnMouseUp =
type == _kMouseEventUp && evt.buttons == _lastButtons;
// Check update event type and set buttons to be sent.
int buttons = _lastButtons;
if (type == _kMouseEventMove) {
@@ -850,7 +906,7 @@ class InputModel {
buttons = evt.buttons;
}
}
_lastButtons = evt.buttons;
_lastButtons = hasStaleButtonsOnMouseUp ? 0 : evt.buttons;
out['buttons'] = buttons;
out['type'] = type;
@@ -1218,6 +1274,28 @@ class InputModel {
_trackpadLastDelta = Offset.zero;
}
// iOS Magic Mouse duplicate event detection.
// When using Magic Mouse on iPad, iOS may emit both mouse and touch events
// for the same click in certain areas (like top-left corner).
int _lastMouseDownTimeMs = 0;
ui.Offset _lastMouseDownPos = ui.Offset.zero;
/// Check if a touch tap event should be ignored because it's a duplicate
/// of a recent mouse event (iOS Magic Mouse issue).
bool shouldIgnoreTouchTap(ui.Offset pos) {
if (!isIOS) return false;
final nowMs = DateTime.now().millisecondsSinceEpoch;
final dt = nowMs - _lastMouseDownTimeMs;
final distance = (_lastMouseDownPos - pos).distance;
// If touch tap is within 2000ms and 80px of the last mouse down,
// it's likely a duplicate event from the same Magic Mouse click.
if (dt >= 0 && dt < 2000 && distance < 80.0) {
debugPrint("shouldIgnoreTouchTap: IGNORED (dt=$dt, dist=$distance)");
return true;
}
return false;
}
void onPointDownImage(PointerDownEvent e) {
debugPrint("onPointDownImage ${e.kind}");
_stopFling = true;
@@ -1227,6 +1305,13 @@ class InputModel {
if (isViewOnly && !showMyCursor) return;
if (isViewCamera) return;
// Track mouse down events for duplicate detection on iOS.
final nowMs = DateTime.now().millisecondsSinceEpoch;
if (e.kind == ui.PointerDeviceKind.mouse) {
_lastMouseDownTimeMs = nowMs;
_lastMouseDownPos = e.position;
}
if (_relativeMouse.enabled.value) {
_relativeMouse.updatePointerRegionTopLeftGlobal(e);
}
@@ -1768,9 +1853,9 @@ class InputModel {
// Simulate a key press event.
// `usbHidUsage` is the USB HID usage code of the key.
Future<void> tapHidKey(int usbHidUsage) async {
newKeyboardMode(kKeyFlutterKey, usbHidUsage, true);
newKeyboardMode(kKeyFlutterKey, usbHidUsage, true, false);
await Future.delayed(Duration(milliseconds: 100));
newKeyboardMode(kKeyFlutterKey, usbHidUsage, false);
newKeyboardMode(kKeyFlutterKey, usbHidUsage, false, false);
}
Future<void> onMobileVolumeUp() async =>

View File

@@ -1072,10 +1072,6 @@ fn get_api_server_(api: String, custom: String) -> String {
if !api.is_empty() {
return api.to_owned();
}
let api = option_env!("API_SERVER").unwrap_or_default();
if !api.is_empty() {
return api.into();
}
let s0 = get_custom_rendezvous_server(custom);
if !s0.is_empty() {
let s = crate::increase_port(&s0, -2);
@@ -1737,8 +1733,7 @@ pub fn create_symmetric_key_msg(their_pk_b: [u8; 32]) -> (Bytes, Bytes, secretbo
#[inline]
pub fn using_public_server() -> bool {
option_env!("RENDEZVOUS_SERVER").unwrap_or("").is_empty()
&& crate::get_custom_rendezvous_server(get_option("custom-rendezvous-server")).is_empty()
crate::get_custom_rendezvous_server(get_option("custom-rendezvous-server")).is_empty()
}
pub struct ThrottledInterval {

View File

@@ -3,8 +3,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
[
("Status", "Durum"),
("Your Desktop", "Sizin Masaüstünüz"),
("desk_tip", "Masaüstünüze bu ID ve şifre ile erişilebilir"),
("Password", "Şifre"),
("desk_tip", "Masaüstünüze bu ID ve parola ile erişilebilir"),
("Password", "Parola"),
("Ready", "Hazır"),
("Established", "Bağlantı sağlandı"),
("connecting_status", "Bağlanılıyor "),
@@ -13,16 +13,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Service is running", "Servis çalışıyor"),
("Service is not running", "Servis çalışmıyor"),
("not_ready_status", "Hazır değil. Bağlantınızı kontrol edin"),
("Control Remote Desktop", "Bağlanılacak Uzak Bağlantı ID"),
("Control Remote Desktop", "Uzak Masaüstünü Denetle"),
("Transfer file", "Dosya transferi"),
("Connect", "Bağlan"),
("Recent sessions", "Son Bağlanılanlar"),
("Recent sessions", "Son oturumlar"),
("Address book", "Adres Defteri"),
("Confirmation", "Onayla"),
("TCP tunneling", "TCP Tünelleri"),
("TCP tunneling", "TCP tünelleri"),
("Remove", "Kaldır"),
("Refresh random password", "Yeni rastgele şifre oluştur"),
("Set your own password", "Kendi şifreni oluştur"),
("Refresh random password", "Yeni rastgele parola oluştur"),
("Set your own password", "Kendi parolanı oluştur"),
("Enable keyboard/mouse", "Klavye ve Fareye izin ver"),
("Enable clipboard", "Kopyalanan geçici veriye izin ver"),
("Enable file transfer", "Dosya Transferine izin ver"),
@@ -47,9 +47,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Slogan_tip", "Bu kaotik dünyada gönülden yapıldı!"),
("Privacy Statement", "Gizlilik Beyanı"),
("Mute", "Sustur"),
("Build Date", "Yapım Tarihi"),
("Build Date", "Derleme Tarihi"),
("Version", "Sürüm"),
("Home", "Anasayfa"),
("Home", "Ana Sayfa"),
("Audio Input", "Ses Girişi"),
("Enhancements", "Geliştirmeler"),
("Hardware Codec", "Donanımsal Codec"),
@@ -64,18 +64,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Not available", "Erişilebilir değil"),
("Too frequent", "Çok sık"),
("Cancel", "İptal"),
("Skip", "Geç"),
("Skip", "Atla"),
("Close", "Kapat"),
("Retry", "Tekrar Dene"),
("OK", "Tamam"),
("Password Required", "Şifre Gerekli"),
("Please enter your password", "Lütfen şifrenizi giriniz"),
("Remember password", "Şifreyi hatırla"),
("Wrong Password", "Hatalı şifre"),
("Password Required", "Parola Gerekli"),
("Please enter your password", "Lütfen parolanızı giriniz"),
("Remember password", "Parolayı hatırla"),
("Wrong Password", "Hatalı parola"),
("Do you want to enter again?", "Tekrar giriş yapmak ister misiniz?"),
("Connection Error", "Bağlantı Hatası"),
("Error", "Hata"),
("Reset by the peer", "Eş tarafında sıfırla"),
("Reset by the peer", "Eş tarafından sıfırlandı"),
("Connecting...", "Bağlanılıyor..."),
("Connection in progress. Please wait.", "Bağlantı sağlanıyor. Lütfen bekleyiniz."),
("Please try 1 minute later", "Lütfen 1 dakika sonra tekrar deneyiniz"),
@@ -141,10 +141,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Timeout", "Zaman aşımı"),
("Failed to connect to relay server", "Relay sunucusuna bağlanılamadı"),
("Failed to connect via rendezvous server", "ID oluşturma sunucusuna bağlanılamadı"),
("Failed to connect via relay server", "Relay oluşturma sunucusuna bağlanılamadı"),
("Failed to connect via relay server", "Aktarma sunucusuna bağlanılamadı"),
("Failed to make direct connection to remote desktop", "Uzak masaüstüne doğrudan bağlantı kurulamadı"),
("Set Password", "Şifre ayarla"),
("OS Password", "İşletim Sistemi Şifresi"),
("Set Password", "Parola ayarla"),
("OS Password", "İşletim Sistemi Parolası"),
("install_tip", "Kullanıcı Hesabı Denetimi nedeniyle, RustDesk bir uzak masaüstü olarak düzgün çalışmayabilir. Bu sorunu önlemek için, RustDesk'i sistem seviyesinde kurmak için aşağıdaki butona tıklayın."),
("Click to upgrade", "Yükseltmek için tıklayınız"),
("Configure", "Ayarla"),
@@ -184,7 +184,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Direct and unencrypted connection", "Doğrudan ve şifrelenmemiş bağlantı"),
("Relayed and unencrypted connection", "Aktarmalı ve şifrelenmemiş bağlantı"),
("Enter Remote ID", "Uzak ID'yi Girin"),
("Enter your password", "Şifrenizi girin"),
("Enter your password", "Parolanızı girin"),
("Logging in...", "Giriş yapılıyor..."),
("Enable RDP session sharing", "RDP oturum paylaşımını etkinleştir"),
("Auto Login", "Otomatik giriş"),
@@ -208,8 +208,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the peer", "Eş tarafından manuel olarak kapatıldı"),
("Enable remote configuration modification", "Uzaktan yapılandırma değişikliğini etkinleştir"),
("Run without install", "Yüklemeden çalıştır"),
("Connect via relay", ""),
("Always connect via relay", "Always connect via relay"),
("Connect via relay", "Aktarmalı üzerinden bağlan"),
("Always connect via relay", "Her zaman aktarmalı üzerinden bağlan"),
("whitelist_tip", "Bu masaüstüne yalnızca yetkili IP adresleri bağlanabilir"),
("Login", "Giriş yap"),
("Verify", "Doğrula"),
@@ -226,11 +226,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Unselect all tags", "Tüm etiketlerin seçimini kaldır"),
("Network error", "Bağlantı hatası"),
("Username missed", "Kullanıcı adı boş"),
("Password missed", "Şifre boş"),
("Password missed", "Parola boş"),
("Wrong credentials", "Yanlış kimlik bilgileri"),
("The verification code is incorrect or has expired", "Doğrulama kodu hatalı veya süresi dolmuş"),
("Edit Tag", "Etiketi düzenle"),
("Forget Password", "Şifreyi Unut"),
("Forget Password", "Parolayı Unut"),
("Favorites", "Favoriler"),
("Add to Favorites", "Favorilere ekle"),
("Remove from Favorites", "Favorilerden çıkar"),
@@ -268,9 +268,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Share screen", "Ekranı Paylaş"),
("Chat", "Mesajlaş"),
("Total", "Toplam"),
("items", "öğeler"),
("items", "ögeler"),
("Selected", "Seçildi"),
("Screen Capture", "Ekran görüntüsü"),
("Screen Capture", "Ekran Görüntüsü"),
("Input Control", "Giriş Kontrolü"),
("Audio Capture", "Ses Yakalama"),
("Do you accept?", "Kabul ediyor musun?"),
@@ -285,7 +285,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("android_start_service_tip", "Ekran paylaşım hizmetini başlatmak için [Hizmeti başlat] ögesine dokunun veya [Ekran Görüntüsü] iznini etkinleştirin."),
("android_permission_may_not_change_tip", "Kurulan bağlantılara ait izinler, yeniden bağlantı kurulana kadar anında değiştirilemez."),
("Account", "Hesap"),
("Overwrite", "üzerine yaz"),
("Overwrite", "Üzerine yaz"),
("This file exists, skip or overwrite this file?", "Bu dosya var, bu dosya atlansın veya üzerine yazılsın mı?"),
("Quit", "Çıkış"),
("Help", "Yardım"),
@@ -295,8 +295,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Unsupported", "desteklenmiyor"),
("Peer denied", "eş reddedildi"),
("Please install plugins", "Lütfen eklentileri yükleyin"),
("Peer exit", "eş çıkışı"),
("Failed to turn off", "kapatılamadı"),
("Peer exit", "Eş çıkışı"),
("Failed to turn off", "Kapatılamadı"),
("Turned off", "Kapatıldı"),
("Language", "Dil"),
("Keep RustDesk background service", "RustDesk arka plan hizmetini sürdürün"),
@@ -308,32 +308,32 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Legacy mode", "Eski mod"),
("Map mode", "Haritalama modu"),
("Translate mode", "Çeviri modu"),
("Use permanent password", "Kalıcı şifre kullan"),
("Use both passwords", "İki şifreyi de kullan"),
("Set permanent password", "Kalıcı şifre oluştur"),
("Use permanent password", "Kalıcı parola kullan"),
("Use both passwords", "İki parolayı da kullan"),
("Set permanent password", "Kalıcı parola oluştur"),
("Enable remote restart", "Uzaktan yeniden başlatmayı aktif et"),
("Restart remote device", "Uzaktaki cihazı yeniden başlat"),
("Are you sure you want to restart", "Yeniden başlatmak istediğinize emin misin?"),
("Are you sure you want to restart", "Yeniden başlatmak istediğine emin misin?"),
("Restarting remote device", "Uzaktan yeniden başlatılıyor"),
("remote_restarting_tip", "Uzak cihaz yeniden başlatılıyor, lütfen bu mesaj kutusunu kapatın ve bir süre sonra kalıcı şifre ile yeniden bağlanın"),
("remote_restarting_tip", "Uzak cihaz yeniden başlatılıyor, lütfen bu mesaj kutusunu kapatın ve bir süre sonra kalıcı parola ile yeniden bağlanın"),
("Copied", "Kopyalandı"),
("Exit Fullscreen", "Tam ekrandan çık"),
("Fullscreen", "Tam ekran"),
("Exit Fullscreen", "Tam Ekrandan Çık"),
("Fullscreen", "Tam Ekran"),
("Mobile Actions", "Mobil İşlemler"),
("Select Monitor", "Monitörü Seç"),
("Control Actions", "Kontrol Eylemleri"),
("Display Settings", "Görüntü ayarları"),
("Display Settings", "Görüntü Ayarları"),
("Ratio", "Oran"),
("Image Quality", "Görüntü kalitesi"),
("Image Quality", "Görüntü Kalitesi"),
("Scroll Style", "Kaydırma Stili"),
("Show Toolbar", "Araç Çubuğunu Göster"),
("Hide Toolbar", "Araç Çubuğunu Gizle"),
("Direct Connection", "Doğrudan Bağlantı"),
("Relay Connection", "Röle Bağlantısı"),
("Relay Connection", "Aktarmalı Bağlantı"),
("Secure Connection", "Güvenli Bağlantı"),
("Insecure Connection", "Güvenli Olmayan Bağlantı"),
("Scale original", "Orijinali ölçeklendir"),
("Scale adaptive", "Ölçek uyarlanabilir"),
("Scale original", "Orijinal ölçekte"),
("Scale adaptive", "Uyarlanabilir ölçekte"),
("General", "Genel"),
("Security", "Güvenlik"),
("Theme", "Tema"),
@@ -347,18 +347,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable audio", "Sesi Aktif Et"),
("Unlock Network Settings", "Ağ Ayarlarını"),
("Server", "Sunucu"),
("Direct IP Access", "Direk IP Erişimi"),
("Direct IP Access", "Doğrudan IP Erişimi"),
("Proxy", "Vekil"),
("Apply", "Uygula"),
("Disconnect all devices?", "Tüm cihazların bağlantısını kes?"),
("Disconnect all devices?", "Tüm cihazların bağlantısı kesilsin mi?"),
("Clear", "Temizle"),
("Audio Input Device", "Ses Giriş Aygıtı"),
("Use IP Whitelisting", "IP Beyaz Listeyi Kullan"),
("Network", ""),
("Pin Toolbar", "Araç Çubuğunu Sabitle"),
("Unpin Toolbar", "Araç Çubuğunu Sabitlemeyi Kaldır"),
("Recording", "Kayıt Ediliyor"),
("Directory", "Klasör"),
("Recording", "Kaydediliyor"),
("Directory", "Dizin"),
("Automatically record incoming sessions", "Gelen oturumları otomatik olarak kaydet"),
("Automatically record outgoing sessions", "Giden oturumları otomatik olarak kaydet"),
("Change", "Değiştir"),
@@ -384,16 +384,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "RustDesk'i Göster"),
("This PC", "Bu PC"),
("or", "veya"),
("Continue with", "bununla devam et"),
("Continue with", "Bununla devam et"),
("Elevate", "Yükseltme"),
("Zoom cursor", "Yakınlaştırma imleci"),
("Accept sessions via password", "Oturumları parola ile kabul etme"),
("Accept sessions via click", "Tıklama yoluyla oturumları kabul edin"),
("Accept sessions via both", "Her ikisi aracılığıyla oturumları kabul edin"),
("Please wait for the remote side to accept your session request...", "Lütfen uzak tarafın oturum isteğinizi kabul etmesini bekleyin..."),
("One-time Password", "Tek Kullanımlık Şifre"),
("One-time Password", "Tek Kullanımlık Parola"),
("Use one-time password", "Tek seferlik parola kullanın"),
("One-time password length", "Tek seferlik şifre uzunluğu"),
("One-time password length", "Tek seferlik parola uzunluğu"),
("Request access to your device", "Cihazınıza erişim talep edin"),
("Hide connection management window", "Bağlantı yönetimi penceresini gizle"),
("hide_cm_tip", "Oturumları yalnızca parola ile kabul edebilir ve kalıcı parola kullanıyorsanız gizlemeye izin verin"),
@@ -442,7 +442,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Voice call", "Sesli görüşme"),
("Text chat", "Metin sohbeti"),
("Stop voice call", "Sesli görüşmeyi durdur"),
("relay_hint_tip", "Doğrudan bağlanmak mümkün olmayabilir; röle aracılığıyla bağlanmayı deneyebilirsiniz. Ayrıca, ilk denemenizde bir röle kullanmak istiyorsanız, ID'nin sonuna \"/r\" ekleyebilir veya son oturum kartındaki \"Her Zaman Röle Üzerinden Bağlan\" seçeneğini seçebilirsiniz."),
("relay_hint_tip", "Doğrudan bağlanmak mümkün olmayabilir; aktarmalı bağlanmayı deneyebilirsiniz. Ayrıca, ilk denemenizde aktarma sunucusu kullanmak istiyorsanız ID'nin sonuna \"/r\" ekleyebilir veya son oturum kartındaki \"Her Zaman Aktarmalı Üzerinden Bağlan\" seçeneğini seçebilirsiniz."),
("Reconnect", "Yeniden Bağlan"),
("Codec", "Kodlayıcı"),
("Resolution", "Çözünürlük"),
@@ -477,7 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("no_desktop_title_tip", "Masaüstü mevcut değil"),
("no_desktop_text_tip", "Lütfen GNOME masaüstünü yükleyin"),
("No need to elevate", "Yükseltmeye gerek yok"),
("System Sound", "Sistem Ses"),
("System Sound", "Sistem Sesi"),
("Default", "Varsayılan"),
("New RDP", "Yeni RDP"),
("Fingerprint", "Parmak İzi"),
@@ -495,7 +495,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("resolution_fit_local_tip", "Yerel çözünürlüğe sığdır"),
("resolution_custom_tip", "Özel çözünürlük"),
("Collapse toolbar", "Araç çubuğunu daralt"),
("Accept and Elevate", "Kabul et ve yükselt"),
("Accept and Elevate", "Kabul Et ve Yükselt"),
("accept_and_elevate_btn_tooltip", "Bağlantıyı kabul et ve UAC izinlerini yükselt."),
("clipboard_wait_response_timeout_tip", "Kopyalama yanıtı için zaman aşımına uğradı."),
("Incoming connection", "Gelen bağlantı"),
@@ -534,7 +534,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("scam_text1", "Eğer tanımadığınız ve güvenmediğiniz birisiyle telefonda konuşuyorsanız ve sizden RustDesk'i kullanmanızı ve hizmeti başlatmanızı istiyorsa devam etmeyin ve hemen telefonu kapatın."),
("scam_text2", "Muhtemelen paranızı veya diğer özel bilgilerinizi çalmaya çalışan dolandırıcılardır."),
("Don't show again", "Bir daha gösterme"),
("I Agree", "Kabul ediyorum"),
("I Agree", "Kabul Ediyorum"),
("Decline", "Reddet"),
("Timeout in minutes", "Zaman aşımı (dakika)"),
("auto_disconnect_option_tip", "Kullanıcı etkin olmadığında gelen oturumları otomatik olarak kapat"),
@@ -559,7 +559,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Plug out all", "Tümünü çıkar"),
("True color (4:4:4)", "Gerçek renk (4:4:4)"),
("Enable blocking user input", "Kullanıcı girişini engellemeyi etkinleştir"),
("id_input_tip", "Bir ID, doğrudan IP veya portlu bir etki alanı (<domain>:<port>) girebilirsiniz.\nBaşka bir sunucudaki bir cihaza erişmek istiyorsanız lütfen sunucu adresini (<id>@<server_address>?key=<key_value>) ekleyin, örneğin,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nGenel bir sunucudaki bir cihaza erişmek istiyorsanız lütfen \"<id>@public\" girin, genel sunucu için anahtara gerek yoktur.\n\nİlk bağlantıda bir röle bağlantısının kullanılmasını zorlamak istiyorsanız ID'nin sonuna \"/r\" ekleyin, örneğin, \"9123456234/r\"."),
("id_input_tip", "Bir ID, doğrudan IP veya portlu bir etki alanı (<domain>:<port>) girebilirsiniz.\nBaşka bir sunucudaki bir cihaza erişmek istiyorsanız lütfen sunucu adresini (<id>@<server_address>?key=<key_value>) ekleyin, örneğin,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nGenel bir sunucudaki bir cihaza erişmek istiyorsanız lütfen \"<id>@public\" girin, genel sunucu için anahtara gerek yoktur.\n\nİlk bağlantıda bir aktarma bağlantısının kullanılmasını zorlamak istiyorsanız ID'nin sonuna \"/r\" ekleyin, örneğin, \"9123456234/r\"."),
("privacy_mode_impl_mag_tip", "Mod 1"),
("privacy_mode_impl_virtual_display_tip", "Mod 2"),
("Enter privacy mode", "Gizlilik moduna gir"),
@@ -581,12 +581,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Please select the session you want to connect to", "Lütfen bağlanmak istediğiniz oturumu seçin"),
("powered_by_me", "RustDesk tarafından desteklenmektedir"),
("outgoing_only_desk_tip", "Bu özelleştirilmiş bir sürümdür.\nDiğer cihazlara bağlanabilirsiniz, ancak diğer cihazlar cihazınıza bağlanamaz."),
("preset_password_warning", "Bu özelleştirilmiş sürüm, önceden ayarlanmış bir şifre ile birlikte gelir. Bu parolayı bilen herkes cihazınızın tam kontrolünü ele geçirebilir. Bunu beklemiyorsanız yazılımı hemen kaldırın."),
("preset_password_warning", "Bu özelleştirilmiş sürüm, önceden ayarlanmış bir parola ile birlikte gelir. Bu parolayı bilen herkes cihazınızın tam kontrolünü ele geçirebilir. Bunu beklemiyorsanız yazılımı hemen kaldırın."),
("Security Alert", "Güvenlik Uyarısı"),
("My address book", "Adres defterim"),
("Personal", "Kişisel"),
("Owner", "Sahip"),
("Set shared password", "Paylaşılan şifreyi ayarla"),
("Set shared password", "Paylaşılan parolayı ayarla"),
("Exist in", "İçinde varolan"),
("Read-only", "Salt okunur"),
("Read/Write", "Okuma/Yazma"),
@@ -599,7 +599,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", "Uzak imleci takip et"),
("Follow remote window focus", "Uzak pencere odağını takip et"),
("default_proxy_tip", "Varsayılan protokol ve port Socks5 ve 1080'dir."),
("no_audio_input_device_tip", "Varsayılan protokol ve port, Socks5 ve 1080'dir"),
("no_audio_input_device_tip", "Ses girişi aygıtı bulunamadı."),
("Incoming", "Gelen"),
("Outgoing", "Giden"),
("Clear Wayland screen selection", "Wayland ekran seçimini temizle"),
@@ -612,7 +612,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("floating_window_tip", "RustDesk arka plan hizmetini açık tutmaya yardımcı olur"),
("Keep screen on", "Ekranıık tut"),
("Never", "Asla"),
("During controlled", "Kontrol sırasınd"),
("During controlled", "Kontrol sırasında"),
("During service is on", "Servis açıkken"),
("Capture screen using DirectX", "DirectX kullanarak ekran görüntüsü al"),
("Back", "Geri"),
@@ -620,7 +620,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Volume up", "Sesi yükselt"),
("Volume down", "Sesi azalt"),
("Power", "Güç"),
("Telegram bot", "Telegram bot"),
("Telegram bot", "Telegram botu"),
("enable-bot-tip", "Bu özelliği etkinleştirirseniz botunuzdan 2FA kodunu alabilirsiniz. Aynı zamanda bağlantı bildirimi işlevi de görebilir."),
("enable-bot-desc", "1. @BotFather ile bir sohbet açın.\n2. \"/newbot\" komutunu gönderin. Bu adımı tamamladıktan sonra bir jeton alacaksınız.\n3. Yeni oluşturduğunuz botla bir sohbet başlatın. Etkinleştirmek için eğik çizgiyle (\"/\") başlayan \"/merhaba\" gibi bir mesaj gönderin.\n"),
("cancel-2fa-confirm-tip", "2FA'yı iptal etmek istediğinizden emin misiniz?"),
@@ -642,7 +642,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Invalid file name", "Geçersiz dosya adı"),
("one-way-file-transfer-tip", "Kontrol edilen tarafta tek yönlü dosya transferi aktiftir."),
("Authentication Required", "Kimlik Doğrulama Gerekli"),
("Authenticate", "Kimlik doğrulaması"),
("Authenticate", "Kimlik Doğrula"),
("web_id_input_tip", "Aynı sunucuda bir kimlik girebilirsiniz, web istemcisinde doğrudan IP erişimi desteklenmez.\nBaşka bir sunucudaki bir cihaza erişmek istiyorsanız lütfen sunucu adresini (<id>@<server_address>?key=<key_value>) ekleyin, örneğin,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nGenel bir sunucudaki bir cihaza erişmek istiyorsanız, lütfen \"<id>@public\" girin, genel sunucu için anahtara gerek yoktur."),
("Download", "İndir"),
("Upload folder", "Klasör yükle"),
@@ -661,9 +661,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("printer-{}-not-installed-tip", "{} Yazıcısı yüklü değil."),
("printer-{}-ready-tip", "{} Yazıcısı kuruldu ve kullanıma hazır."),
("Install {} Printer", "{} Yazıcısını Yükle"),
("Outgoing Print Jobs", "Giden Baskı İşleri"),
("Incoming Print Jobs", "Gelen Baskı İşleri"),
("Incoming Print Job", "Gelen Baskı İşi"),
("Outgoing Print Jobs", "Giden Yazdırma İşleri"),
("Incoming Print Jobs", "Gelen Yazdırma İşleri"),
("Incoming Print Job", "Gelen Yazdırma İşi"),
("use-the-default-printer-tip", "Varsayılan yazıcıyı kullan"),
("use-the-selected-printer-tip", "Seçili yazıcıyı kullan"),
("auto-print-tip", "Seçili yazıcıyı kullanarak otomatik olarak yazdır."),
@@ -685,11 +685,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", "İndirme başarısız oldu. Tekrar deneyebilir veya 'İndir' düğmesine tıklayarak sürüm sayfasından manuel olarak indirip güncelleyebilirsiniz."),
("Auto update", "Otomatik güncelleme"),
("update-failed-check-msi-tip", "Kurulum yöntemi denetimi başarısız oldu. Sürüm sayfasından indirmek ve manuel olarak yükseltmek için lütfen \"İndir\" düğmesine tıklayın."),
("websocket_tip", "WebSocket kullanıldığında yalnızca röle bağlantıları desteklenir."),
("websocket_tip", "WebSocket kullanıldığında yalnızca aktarma bağlantıları desteklenir."),
("Use WebSocket", "WebSocket'ı kullan"),
("Trackpad speed", "İzleme paneli hızı"),
("Default trackpad speed", "Varsayılan izleme paneli hızı"),
("Numeric one-time password", "Sayısal tek seferlik şifre"),
("Numeric one-time password", "Sayısal tek seferlik parola"),
("Enable IPv6 P2P connection", "IPv6 P2P bağlantısını etkinleştir"),
("Enable UDP hole punching", "UDP delik açmayı etkinleştir"),
("View camera", "Kamerayı görüntüle"),
@@ -701,16 +701,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("New tab", "Yeni sekme"),
("Keep terminal sessions on disconnect", "Bağlantı kesildiğinde terminal oturumlarınıık tut"),
("Terminal (Run as administrator)", "Terminal (Yönetici olarak çalıştır)"),
("terminal-admin-login-tip", "Lütfen kontrol edilen tarafın yönetici kullanıcı adı ve şifresini giriniz."),
("terminal-admin-login-tip", "Lütfen kontrol edilen tarafın yönetici kullanıcı adı ve parolasını giriniz."),
("Failed to get user token.", "Kullanıcı belirteci alınamadı."),
("Incorrect username or password.", "Hatalı kullanıcı adı veya şifre."),
("Incorrect username or password.", "Hatalı kullanıcı adı veya parola."),
("The user is not an administrator.", "Kullanıcı bir yönetici değil."),
("Failed to check if the user is an administrator.", "Kullanıcının yönetici olup olmadığı kontrol edilemedi."),
("Supported only in the installed version.", "Sadece yüklü sürümde desteklenir."),
("elevation_username_tip", "Kullanıcı adı veya etki alanı\\kullanıcı adı girin"),
("Preparing for installation ...", "Kuruluma hazırlanıyor..."),
("Show my cursor", "İmlecimi göster"),
("Scale custom", "Özel boyutlandır"),
("Scale custom", "Özel ölçekte"),
("Custom scale slider", "Özel ölçek kaydırıcısı"),
("Decrease", "Azalt"),
("Increase", "Arttır"),

View File

@@ -729,15 +729,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "注意RustDesk 開源伺服器 (OSS server) 不包含此功能。"),
("input note here", "輸入備註"),
("note-at-conn-end-tip", "在連接結束時請求備註"),
("Show terminal extra keys", ""),
("Relative mouse mode", ""),
("rel-mouse-not-supported-peer-tip", ""),
("rel-mouse-not-ready-tip", ""),
("rel-mouse-lock-failed-tip", ""),
("rel-mouse-exit-{}-tip", ""),
("rel-mouse-permission-lost-tip", ""),
("Changelog", ""),
("keep-awake-during-outgoing-sessions-label", ""),
("keep-awake-during-incoming-sessions-label", ""),
("Show terminal extra keys", "顯示終端機額外按鍵"),
("Relative mouse mode", "相對滑鼠模式"),
("rel-mouse-not-supported-peer-tip", "被控端不支援相對滑鼠模式"),
("rel-mouse-not-ready-tip", "相對滑鼠模式尚未就緒,請稍候再試"),
("rel-mouse-lock-failed-tip", "無法鎖定游標,相對滑鼠模式已停用"),
("rel-mouse-exit-{}-tip", "按下 {} 退出"),
("rel-mouse-permission-lost-tip", "鍵盤權限被撤銷,相對滑鼠模式已被停用"),
("Changelog", "更新日誌"),
("keep-awake-during-outgoing-sessions-label", "在連出工作階段期間保持螢幕喚醒"),
("keep-awake-during-incoming-sessions-label", "在連入工作階段期間保持螢幕喚醒"),
].iter().cloned().collect();
}

View File

@@ -357,27 +357,6 @@ static std::string GetDisplayUUID(CGDirectDisplayID displayId) {
return "";
}
// Helper function to get display name from DisplayID
static std::string GetDisplayName(CGDirectDisplayID displayId) {
NSArray<NSScreen *> *screens = [NSScreen screens];
for (NSScreen *screen in screens) {
NSDictionary *deviceDescription = [screen deviceDescription];
NSNumber *screenNumber = [deviceDescription objectForKey:@"NSScreenNumber"];
CGDirectDisplayID screenDisplayID = [screenNumber unsignedIntValue];
if (screenDisplayID == displayId) {
// localizedName is available on macOS 10.15+
if (@available(macOS 10.15, *)) {
NSString *name = [screen localizedName];
if (name) {
return std::string([name UTF8String]);
}
}
break;
}
}
return "Unknown";
}
// Helper function to find DisplayID by UUID from current online displays
static CGDirectDisplayID FindDisplayIdByUUID(const std::string& targetUuid) {
uint32_t count = 0;
@@ -415,9 +394,7 @@ static bool RestoreAllGammas() {
const CGGammaValue* blue = green + sampleCount;
CGError error = CGSetDisplayTransferByTable(d, sampleCount, red, green, blue);
if (error != kCGErrorSuccess) {
std::string displayName = GetDisplayName(d);
NSLog(@"Failed to restore gamma for display (Name: %s, ID: %u, UUID: %s, error: %d)",
displayName.c_str(), (unsigned)d, uuid.c_str(), error);
NSLog(@"Failed to restore gamma for display (ID: %u, UUID: %s, error: %d)", (unsigned)d, uuid.c_str(), error);
allSuccess = false;
}
}
@@ -897,8 +874,7 @@ extern "C" bool MacSetPrivacyMode(bool on) {
blackoutAttemptCount++;
CGError error = CGSetDisplayTransferByTable(d, capacity, zeros.data(), zeros.data(), zeros.data());
if (error != kCGErrorSuccess) {
std::string displayName = GetDisplayName(d);
NSLog(@"MacSetPrivacyMode: Failed to blackout display (Name: %s, ID: %u, UUID: %s, error: %d)", displayName.c_str(), (unsigned)d, uuid.c_str(), error);
NSLog(@"MacSetPrivacyMode: Failed to blackout display (ID: %u, UUID: %s, error: %d)", (unsigned)d, uuid.c_str(), error);
} else {
blackoutSuccessCount++;
}

View File

@@ -11,7 +11,7 @@ use uuid::Uuid;
use hbb_common::{
allow_err,
anyhow::{self, bail},
anyhow::{self, bail, Context},
config::{
self, keys::*, option2bool, use_ws, Config, CONNECT_TIMEOUT, REG_INTERVAL, RENDEZVOUS_PORT,
},
@@ -749,6 +749,9 @@ impl RendezvousMediator {
}
}
// Socket backlog value used for TCP listeners, matching the value in hbb_common::tcp
const SOCKET_BACKLOG: u32 = 128;
fn get_direct_port() -> i32 {
let mut port = Config::get_option("direct-access-port")
.parse::<i32>()
@@ -759,6 +762,37 @@ fn get_direct_port() -> i32 {
port
}
async fn listen_with_bind_interface(port: u16) -> ResultType<hbb_common::tokio::net::TcpListener> {
use hbb_common::tokio::net::{TcpListener, TcpSocket};
use std::net::{IpAddr, SocketAddr};
let bind_interface = Config::get_option("bind-interface");
if bind_interface.is_empty() {
// Use the default listen_any behavior
hbb_common::tcp::listen_any(port).await
} else {
// Parse the bind interface address
let addr: IpAddr = bind_interface.parse()
.with_context(|| format!("Invalid bind interface address: {}", bind_interface))?;
let socket_addr = SocketAddr::new(addr, port);
// Create and bind socket
let socket = match socket_addr {
SocketAddr::V4(..) => TcpSocket::new_v4()?,
SocketAddr::V6(..) => TcpSocket::new_v6()?,
};
// Set socket options
#[cfg(all(unix, not(target_os = "illumos")))]
allow_err!(socket.set_reuseport(true));
allow_err!(socket.set_reuseaddr(true));
socket.bind(socket_addr)?;
Ok(socket.listen(SOCKET_BACKLOG)?)
}
}
async fn direct_server(server: ServerPtr) {
let mut listener = None;
let mut port = 0;
@@ -769,7 +803,7 @@ async fn direct_server(server: ServerPtr) {
) || option2bool("stop-service", &Config::get_option("stop-service"));
if !disabled && listener.is_none() {
port = get_direct_port();
match hbb_common::tcp::listen_any(port as _).await {
match listen_with_bind_interface(port as _).await {
Ok(l) => {
listener = Some(l);
log::info!(
@@ -904,3 +938,27 @@ async fn udp_nat_listen(
})?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::IpAddr;
#[test]
fn test_bind_interface_parsing() {
// Test valid IPv4 addresses
assert!("192.168.1.100".parse::<IpAddr>().is_ok());
assert!("10.0.0.1".parse::<IpAddr>().is_ok());
assert!("127.0.0.1".parse::<IpAddr>().is_ok());
// Test valid IPv6 addresses
assert!("::1".parse::<IpAddr>().is_ok());
assert!("fe80::1".parse::<IpAddr>().is_ok());
assert!("2001:db8::1".parse::<IpAddr>().is_ok());
// Test invalid addresses
assert!("invalid".parse::<IpAddr>().is_err());
assert!("999.999.999.999".parse::<IpAddr>().is_err());
assert!("".parse::<IpAddr>().is_err());
}
}