Compare commits

...

67 Commits

Author SHA1 Message Date
rustdesk
5fc0367abd 1. GUID key (...Uninstall\{GUID}) is MSI-native metadata generated by Windows Installer.
2. Non-GUID key (...Uninstall\RustDesk) is explicitly written by RustDesk’s MSI compatibility component in res/msi/Package/Components/Regs.wxs:44, populated by preprocess.py --arp from .github/workflows/
     flutter-build.yml:262.

  So they were not using the same EstimatedSize logic:

  - MSI GUID key: MSI-calculated size (KB).
  - RustDesk key: custom script value from res/msi/preprocess.py:339 (previously bytes, now fixed to KB).

  That mismatch is exactly why you saw different sizes.
2026-02-19 17:52:17 +08:00
rustdesk
9111bfc1de - UI display: display_name first
- Fallback: name
  - Technical identity: still name

  ### What changed

  - Added account display helpers and display_name state in user model:
      - flutter/lib/models/user_model.dart:16
  - Account/logout label now uses display_name (@name) when both exist:
      - flutter/lib/mobile/pages/settings_page.dart:689
      - flutter/lib/desktop/pages/desktop_setting_page.dart:2016
      - flutter/lib/desktop/pages/desktop_setting_page.dart:2135
  - Desktop Account info now shows both when applicable:
      - Display Name: ...
      - Username: ...
      - flutter/lib/desktop/pages/desktop_setting_page.dart:2039
  - Previously done group-list behavior remains:
      - group user list displays display_name with name fallback
      - flutter/lib/common/widgets/my_group.dart:187
  - Persistence path for display_name remains enabled (including group cache/submodule field):
      - libs/hbb_common/src/config.rs:2347
  - src/client.rs:2630
  - LoginRequest.my_name now resolves as:
      1. OPTION_DISPLAY_NAME (manual override)
      2. user_info.display_name
      3. user_info.name
      4. OS username fallback
2026-02-19 09:43:55 +08:00
Nicola Spieser Buiss
9345fb754a fix: correct typos and improve code clarity (#14341)
- Fix 'clipbard' typos in clipboard.rs (function names, comments, strings)
- Fix 'seperate' typo in x11/server.rs comment
- Replace !is_ok() with idiomatic is_err() in updater.rs
- Fix double backtick typo in updater.rs comment

Co-authored-by: Ocean <ocean@Mac-mini-von-Ocean.local>
2026-02-17 14:29:50 +08:00
fufesou
779b7aaf02 feat(wayland): keyboard mode, legacy translate (#14317)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-02-15 16:43:21 +08:00
21pages
b268aa1061 Fix some single device multiple ids scenarios on MacOS (#14196)
* fix(macos): sync config to root when root config is empty

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(server): gate startup on initial config sync; document CheckIfResendPk limitation

  - wait up to 3s for initial root->local config sync before starting server services
  - continue startup when timeout is hit, while keeping sync/watch running in background
  - avoid blocking non-server process startup
  - clarify that CheckIfResendPk only re-registers PK for current ID and does not solve multi-ID when root uses a non-default mac-generated ID

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2026-02-15 16:12:26 +08:00
Vance
40f86fa639 fix(mobile): account for safe area padding in canvas size calculation (#14285)
* fix(mobile): account for safe area padding in canvas size calculation

* fix(mobile): differentiate safe area handling for portrait vs landscape

* refact(ios): Simple refactor

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

* fix(ios): canvas getSize, test -> Android

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

* fix: comments

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

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
2026-02-15 14:52:27 +08:00
rustdesk
980bc11e68 update common 2026-02-14 17:48:53 +08:00
Shaikh Naasir
85db677982 docs: fix typos in clipboard documentation (#13521)
Signed-off-by: Naasir <yoursdeveloper@protonmail.com>
2026-02-14 01:06:25 +08:00
fufesou
2842315b1d Fix/linux shortcuts inhibit (#14302)
* feat: Inhibit system shortcuts on Linux

Fixes #13013.

Signed-off-by: Max von Forell <max@vonforell.de>

* fix(linux): shortcuts inhibit

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

---------

Signed-off-by: Max von Forell <max@vonforell.de>
Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: Max von Forell <max@vonforell.de>
2026-02-11 16:11:47 +08:00
fufesou
6c541f7bfd fix(xdo): deb, libxdo3 | libxdo4 (#14314)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-02-11 16:11:15 +08:00
VenusGirl❤
067fab2b73 Update Korean (#14298)
Correct spacing and spelling
2026-02-10 18:48:30 +08:00
fufesou
de6bf9dc7e fix(ios): Add defensive timer cancellation for keyboard visibility (#14301)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-02-09 15:54:22 +08:00
fufesou
54eae37038 fix(ios): workaround physical keyboard after virtual keyboard hidden (#14207)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-02-09 00:36:25 +08:00
Hugo Breda
0118e16132 PT-BR language update (#14295)
* PT-BR language update

@rustdesk
Please merge. Thanks

* Update ptbr.rs

* Update ptbr.rs

Please submit, i will get back soon and finish all other stuff.

* PT-BR language update

Completed all missing PT-BR translations.
2026-02-09 00:31:47 +08:00
fufesou
626a091f55 fix(translation): OIDC, Continue with (#14271)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-02-06 14:18:48 +08: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
twprh
5f3ceef592 Update de.rs (#14139)
zum Zeitpunkt der Anzeige ist der Datenschutz aktiviert bzw. schon beendet.

alternativ ginge auch:

Datenschutzmodus wurde aktiviert  
bzw.
 Datenschutzmodus wurde beendet
2026-01-28 15:16:27 +08:00
Lynilia
1a90e6b6c7 Update fr.rs (#14151) 2026-01-28 15:16:06 +08:00
John Fowler
f112d097dc Replacing incorrect quotation marks (#14144)
* Update Hungarian translations in hu.rs

Translation of new strings and some fixes.
John Fowler.

* Escape quotes in Hungarian language strings

Replacing Hungarian quotation marks

* Update Hungarian translations for various terms

Upload a new translation (hu.rs) file.
2026-01-28 15:15:29 +08:00
ThallesWS
45cab7f808 fix issue: #13911 'Double Click' bug on iPad with Magic Mouse (#14086)
* fix issue: #13911 'Double Click' bug on iPad with Magic Mouse

* remote_input.dart comments - gestures.dart organization and clean states of all interrupted gestures
2026-01-28 15:14:06 +08:00
fufesou
216ec9d52b fix(terminal): ios delete (#14147)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-01-28 15:12:42 +08:00
fufesou
56a8f6b97b fix(iOS): Unexpected mouse movement to (0,0) on idle (#14180)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-01-28 15:11:44 +08:00
Bin Li
c76d10a438 feat(macos): initial privacy mode support [a simple try] (#14102)
* feat(macos): add privacy mode support for macOS

## Summary
Add privacy mode functionality for macOS platform, allowing remote
desktop sessions to hide the screen content from local users.

## Changes

### Core Implementation (src/platform/macos.mm)
- Implement screen blackout using CGDisplayGammaTable API
- Implement input blocking using CGEventTap to intercept keyboard/mouse
- Store and restore original gamma values for proper cleanup

### Privacy Mode Integration (src/privacy_mode.rs, src/privacy_mode/macos.rs)
- Add macOS privacy mode implementation with PrivacyMode trait
- Register macOS privacy mode in PRIVACY_MODE_CREATOR
- Set DEFAULT_PRIVACY_MODE_IMPL for macOS platform
- Implement get_supported_privacy_mode_impl() for macOS

### Connection Handling (src/server/connection.rs)
- Add supported_privacy_mode_impl to platform_additions for macOS
- Enable privacy mode toggle in client UI when connecting via LAN IP

### Localization (src/lang/*.rs)
- Add "privacy_mode_impl_macos_tip" translation for en/cn/tw

## Safety & Security
- Implements Drop trait to ensure cleanup on normal exit
- macOS system automatically restores gamma table on process termination
- CGEventTap is automatically released when process terminates
- Tested with SIGKILL to verify crash recovery

## Testing
- Verified privacy mode toggle works via both ID and LAN IP connection
- Verified screen recovery after process crash (kill -9)
- Verified input restoration after process termination

* refactor: use existing 'Privacy mode' translation key

* refactor: rename gamma channel variables for better readability - rename r/g/b to red/green/blue to avoid variable shadowing confusion

* fix: add error handling for gamma table restoration with fallback to system reset

* fix: add error handling for CGEventTapCreate failure in privacy mode

* fix: only set display to black if original gamma was saved successfully

* fix: add error handling for CGSetDisplayTransferByTable when setting display to black

* fix: improve event tap callback to properly distinguish remote input from local input

* fix: missing macos.rs

* Fix: Add display validation before restoring gamma values

* Fix: Add mutex lock for thread safety in MacSetPrivacyMode

* Fix: Handle return values and add missing mouse events in macos privacy mode

* fix: only set conn_id after privacy mode is successfully turned on

* fix: reimplement privacy mode with stable display identification

Address code review concern: original gamma values stored with DisplayID
as key could become stale if display list changes between privacy mode
activations (e.g., display reconnected with different ID).

Solution:
- Use UUID instead of DisplayID as storage key (stable across reconnections)
- Clear g_originalGammas when privacy mode is turned off
- Register CGDisplayReconfigurationCallback to handle hot-plug events
- Validate display state via FindDisplayIdByUUID() before restoration

Key features:
- UUID-based display identification (stable across reconnections)
- Hot-plug support via CGDisplayReconfigurationCallback
- EventTap auto re-enable on system timeout
- Fallback to CGDisplayRestoreColorSyncSettings() for recovery
- Detailed error logging with display name/ID/UUID

* fix: ensure EventTap runs on main thread and improve gamma restore error handling

- Add SetupEventTapOnMainThread() to create EventTap on main thread using dispatch_sync, avoiding potential issues when called from background threads

- Add TeardownEventTapOnMainThread() for consistent cleanup on main thread

- Check [NSThread isMainThread] to avoid deadlock when already on main thread

- Add error tracking for gamma restoration during cleanup

- Use CGDisplayRestoreColorSyncSettings() as fallback when individual gamma restoration fails

* fix: remove invalid eventMask bits that caused undefined behavior in input blocking

* fix: address code review comments for macos privacy mode implementation

Changes to src/privacy_mode/macos.rs:
- Add check_on_conn_id() in turn_on_privacy() to prevent duplicate activation
- Add check_off_conn_id() in turn_off_privacy() to validate connection ID
- Add self.conn_id = 0 in clear() to reset connection state

Changes to src/platform/macos.mm:
- Add link comment for ENIGO_INPUT_EXTRA_VALUE referencing libs/enigo/src/macos/macos_impl.rs
- Fix NSLog format string mismatch (5 placeholders vs 4 values)
- Make ApplyBlackoutToDisplay() return bool for proper error handling
- Return false when UUID is empty since privacy mode requires ALL displays
- Add else branches with logging for:
  - CGGetDisplayTransferByTable failures
  - Zero gamma table capacity (not supported)
  - Zero blackout capacity
- Remove unused g_uuidToDisplayId variable (was only written, never read)

* fix(macos): add early return with privacy mode exit on display hotplug failures

Why large-scale changes are needed:

The code review suggested adding early return when errors occur in
DisplayReconfigurationCallback. However, simply returning early is not
enough - when a newly connected display cannot be blacked out, we must
exit privacy mode entirely to maintain security guarantees.

The challenge is that DisplayReconfigurationCallback already holds
g_privacyModeMutex, so calling MacSetPrivacyMode(false) directly would
cause a deadlock. This necessitated:

1. Extract TurnOffPrivacyModeInternal() - a lock-free internal function
   that can be safely called from within the callback
2. Refactor MacSetPrivacyMode(false) branch to use this internal function
3. Add early returns with TurnOffPrivacyModeInternal() calls at each
   failure point in DisplayReconfigurationCallback

Changes in DisplayReconfigurationCallback:
- UUID empty: log + exit privacy mode + early return
- Gamma table capacity zero: log + exit privacy mode + early return
- CGGetDisplayTransferByTable fails: log + exit privacy mode + early return
- ApplyBlackoutToDisplay fails: log + exit privacy mode + early return

* fix(macos): address code review feedback and improve privacy mode stability

Code Review Fixes:
- Add detailed comments for potential deadlock scenarios in dispatch_sync
  with g_privacyModeMutex (SetupEventTapOnMainThread/TeardownEventTapOnMainThread)
- Use async dispatch for privacy mode shutdown from DisplayReconfigurationCallback
  to avoid unregistering callback from within itself
- Extract RestoreAllGammas() helper function to reduce code duplication
- Fix Drop implementation in macos.rs to call self.clear() for consistency
- Add comment explaining why _state parameter is ignored on macOS
- Define DISPLAY_RECONFIG_MONITOR_DURATION_MS and GAMMA_CHECK_INTERVAL_MS constants
- Add gamma restoration when UUID retrieval fails during privacy mode activation

Privacy Mode Stability Improvements (Continuous Resolution Changes):
- Implement continuous gamma value monitoring with timer polling after display
  reconfiguration to handle rapid successive resolution changes
- Monitor gamma values every 200ms for 5 seconds after each resolution change
- Automatically reapply blackout if system (ColorSync) restores gamma
- Add IsDisplayBlackedOut() to detect if display gamma has been restored
- Use timestamp-based debouncing: monitoring period automatically extends
  when new reconfig events occur during active monitoring
- Ensure blackout remains effective even under continuous resolution changes
  where macOS may asynchronously restore gamma values multiple times

This ensures privacy mode remains stable and effective when users rapidly
change display resolution multiple times in succession.

---------

Co-authored-by: libin <libin.chat@outlook.com>
2026-01-27 16:38:37 +08:00
Alex Rijckaert
f05f2178e5 Update Dutch translations (#14136) 2026-01-26 14:16:21 +08:00
Hugo Breda
226d7417b2 PT-BR language update (#14135)
* PT-BR language update

@rustdesk
Please merge. Thanks

* Update ptbr.rs

* Update ptbr.rs

Please submit, i will get back soon and finish all other stuff.
2026-01-26 14:15:58 +08:00
bovirus
b0c8e65c6e Italian language update (#14129) 2026-01-26 14:15:45 +08:00
RustDesk
4ae577c3c4 Revert "Updated tr.rs (#14115)" (#14158)
This reverts commit 204e81a700.
2026-01-26 14:14:35 +08:00
bilimiyorum
204e81a700 Updated tr.rs (#14115)
Translation improvements have been made.
2026-01-26 14:11:58 +08:00
hatterp
1f35830570 Update pl.rs (#14112)
updated PL translation
2026-01-26 14:11:41 +08:00
VenusGirl❤
6b334f2977 Update ko.rs (#14110)
Update Korean
2026-01-25 16:37:34 +08:00
Mr-Update
0dc3c12aa5 Update de.rs (#14108) 2026-01-24 12:50:18 +08:00
RustDesk
ceffcce20e fix hide-tray=Y causing The application “RustDesk.app” is not open anymore. https://github.com/rustdesk/rustdesk/discussions/10210 (#14127) 2026-01-23 19:09:33 +08:00
21pages
e4b06dadf5 auto retry on offline when already connected (#14124)
When controlled peer is reconnecting after signout/switch user, auto retry for 30s (matches server's peer offline threshold) instead of immediately showing "Remote desktop is offline" error.

Ref: https://github.com/rustdesk/rustdesk/discussions/14048

Signed-off-by: 21pages <sunboeasy@gmail.com>
2026-01-23 15:05:11 +08:00
Yavuz Selim YAZICI
087eb55299 Update tr.rs, Missing Turkish translations added (#14103)
* Update tr.rs

* Update tr.rs

---------

Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2026-01-22 14:15:14 +08:00
bilimiyorum
341eb0c671 Updated tr.rs (#14100)
New string entries
Minor typo corrections
2026-01-22 14:13:38 +08:00
solokot
43b39102a4 Update ru.rs (#14099) 2026-01-22 14:12:26 +08:00
fufesou
be4bbd018d fix(install): linux xdo (#14096)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-01-21 20:43:15 +08:00
RustDesk
21a7cef98a keep-awake-during-incoming-sessions (#14082)
* keep-awake-during-incoming-sessions

* Update flutter/lib/desktop/pages/desktop_setting_page.dart

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

* Update flutter/lib/common.dart

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

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

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

* Update common.dart

* wakelock

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix build

Signed-off-by: 21pages <sunboeasy@gmail.com>

* Update server_model.dart

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: 21pages <sunboeasy@gmail.com>
2026-01-21 16:25:57 +08:00
fufesou
a6724b1c07 fix: build (#14093)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-01-20 22:53:18 +08:00
Cody Kim
7437593ee7 Update ko.rs (#14055)
* update: correct Korean translations (typo/grammar)

- typo: 인중 -> 인증
- grammar: 중 입니다 -> 중입니다

Signed-off-by: 0-Chan <kyc4421194@gmail.com>

* update: improve Korean translations

Signed-off-by: 0-Chan <kyc4421194@gmail.com>

---------

Signed-off-by: 0-Chan <kyc4421194@gmail.com>
2026-01-20 17:08:54 +08:00
VenusGirl❤
f21829b075 Update Korean (#14057) 2026-01-20 17:08:02 +08:00
hatterp
b4f60e6057 Update pl.rs (#14054)
improve Polish translation

Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2026-01-19 13:41:10 +08:00
hatterp
b9ebddff0c Update pl.rs (#14053)
Add and improve Polish translation.
2026-01-18 19:34:26 +08:00
hatterp
a2243484a3 Update README-PL.md (#14052) 2026-01-17 18:31:41 +08:00
21pages
c4a9835ae5 change quick support filename detection (#14050)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2026-01-15 13:47:39 +08:00
Alex Rijckaert
92ad279324 Dutch Translation up to date (#14033) 2026-01-14 13:05:01 +08:00
Kratos
7276025cf9 Update hu.rs (#14032)
Fix translated strings.
2026-01-13 11:00:29 +08:00
minh
9808d585cf Update vi.rs file (#14027) 2026-01-13 11:00:16 +08:00
John Fowler
dab9ed711c Update Hungarian translations in hu.rs (#14014)
Translation of new strings and some fixes.
John Fowler.
2026-01-12 20:57:05 +08:00
Mr-Update
b27a93fc77 Update de.rs (#14013) 2026-01-12 20:56:50 +08:00
Lynilia
e3f66973b7 Update fr.rs (#14012) 2026-01-12 20:56:35 +08:00
bilimiyorum
21529d6ca2 Current tr.rs (#14008)
New string entries
2026-01-12 20:56:19 +08:00
solokot
775b0a3c93 Update ru.rs (#14004) 2026-01-12 20:56:02 +08:00
Anatolij Vasilev
070d4d029f synchronized german translation with the current english readme (#14001) 2026-01-12 10:59:11 +08:00
bovirus
5355702e9c Italian language update (#13998) 2026-01-12 10:58:42 +08:00
VenusGirl❤
a97997952d Update Korean (#13996)
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2026-01-12 10:58:23 +08:00
Sunev
b0c12bd86b Update signing conditions for rustdesk files (#14010)
Now ```env.SIGN_BASE_URL``` would never be ```''```, yet could be ```'-2'``` while ```secrets.SIGN_BASE_URL``` was undefined.
2026-01-10 15:29:59 +08:00
fufesou
82fcab26b1 refact(sign): skip signed files (#14006)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-01-10 02:01:32 +08:00
fufesou
f3bbcc4f55 refact(sign): skip signed files (#14005)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-01-10 00:55:00 +08:00
21pages
98362eaca0 add Changelog link in update help card (#13997)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2026-01-09 15:34:51 +08:00
112 changed files with 4538 additions and 1167 deletions

View File

@@ -234,7 +234,7 @@ jobs:
path: rustdesk
- name: Sign rustdesk files
if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != ''
if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2'
shell: bash
run: |
pip3 install requests argparse
@@ -266,7 +266,7 @@ jobs:
sha256sum ../../SignOutput/rustdesk-*.msi
- name: Sign rustdesk self-extracted file
if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != ''
if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2'
shell: bash
run: |
BASE_URL=${{ env.SIGN_BASE_URL }} SECRET_KEY=${{ secrets.SIGN_SECRET_KEY }} python3 res/job.py sign_files ./SignOutput
@@ -400,7 +400,7 @@ jobs:
path: Release
- name: Sign rustdesk files
if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != ''
if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2'
shell: bash
run: |
pip3 install requests argparse
@@ -418,7 +418,7 @@ jobs:
mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.exe
- name: Sign rustdesk self-extracted file
if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != ''
if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2'
shell: bash
run: |
BASE_URL=${{ env.SIGN_BASE_URL }} SECRET_KEY=${{ secrets.SIGN_SECRET_KEY }} python3 res/job.py sign_files ./SignOutput/

10
Cargo.lock generated
View File

@@ -2517,6 +2517,7 @@ version = "0.0.14"
dependencies = [
"core-graphics 0.22.3",
"hbb_common",
"libxdo-sys",
"log",
"objc",
"pkg-config",
@@ -3720,6 +3721,7 @@ dependencies = [
"httparse",
"lazy_static",
"libc",
"libloading 0.8.4",
"log",
"mac_address",
"machine-uid",
@@ -3755,6 +3757,7 @@ dependencies = [
"webrtc",
"whoami",
"winapi 0.3.9",
"x11 2.21.0",
"zstd 0.13.1",
]
@@ -4546,11 +4549,8 @@ dependencies = [
[[package]]
name = "libxdo-sys"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212"
dependencies = [
"libc",
"x11 2.21.0",
"hbb_common",
]
[[package]]
@@ -7181,9 +7181,9 @@ dependencies = [
"kcp-sys",
"keepawake",
"lazy_static",
"libloading 0.8.4",
"libpulse-binding",
"libpulse-simple-binding",
"libxdo-sys",
"mac_address",
"magnum-opus",
"nix 0.29.0",

View File

@@ -76,7 +76,6 @@ crossbeam-queue = "0.3"
hex = "0.4"
chrono = "0.4"
cidr-utils = "0.5"
libloading = "0.8"
fon = "0.6"
zip = "0.6"
shutdown_hooks = "0.1"
@@ -177,6 +176,7 @@ bytemuck = "1.23"
ttf-parser = "0.25"
[target.'cfg(target_os = "linux")'.dependencies]
libxdo-sys = "0.11"
psimple = { package = "libpulse-simple-binding", version = "2.27" }
pulse = { package = "libpulse-binding", version = "2.27" }
rust-pulsectl = { git = "https://github.com/rustdesk-org/pulsectl" }
@@ -207,6 +207,11 @@ android-wakelock = { git = "https://github.com/rustdesk-org/android-wakelock" }
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/portable", "libs/remote_printer"]
exclude = ["vdi/host", "examples/custom_plugin"]
# Patch libxdo-sys to use a stub implementation that doesn't require libxdo
# This allows building and running on systems without libxdo installed (e.g., Wayland-only)
[patch.crates-io]
libxdo-sys = { path = "libs/libxdo-sys-stub" }
[package.metadata.winres]
LegalCopyright = "Copyright © 2025 Purslane Ltd. All rights reserved."
ProductName = "RustDesk"

View File

@@ -299,7 +299,7 @@ Version: %s
Architecture: %s
Maintainer: rustdesk <info@rustdesk.com>
Homepage: https://rustdesk.com
Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva2, libva-drm2, libva-x11-2, libgstreamer-plugins-base1.0-0, libpam0g, gstreamer1.0-pipewire%s
Depends: libgtk-3-0, libxcb-randr0, libxdo3 | libxdo4, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva2, libva-drm2, libva-x11-2, libgstreamer-plugins-base1.0-0, libpam0g, gstreamer1.0-pipewire%s
Recommends: libayatana-appindicator3-1
Description: A remote control software.

View File

@@ -1,15 +1,14 @@
<p align="center">
<img src="../res/logo-header.svg" alt="RustDesk - Your remote desktop"><br>
<a href="#freie-öffentliche-server">Server</a> •
<img src="../res/logo-header.svg" alt="RustDesk - Dein Remote-Desktop"><br>
<a href="#grobe-schritte-zum-kompilieren">Kompilieren</a> •
<a href="#auf-docker-kompilieren">Docker</a> •
<a href="#dateistruktur">Dateistruktur</a> •
<a href="#screenshots">Screenshots</a><br>
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-DA.md">Dansk</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
[<a href="docs/README-UA.md">Українська</a>] | [<a href="docs/README-CS.md">česky</a>] | [<a href="docs/README-ZH.md">中文</a>] | [<a href="docs/README-HU.md">Magyar</a>] | [<a href="docs/README-ES.md">Español</a>] | [<a href="docs/README-FA.md">فارسی</a>] | [<a href="docs/README-FR.md">Français</a>] | [<a href="docs/README-DE.md">Deutsch</a>] | [<a href="docs/README-PL.md">Polski</a>] | [<a href="docs/README-ID.md">Indonesian</a>] | [<a href="docs/README-FI.md">Suomi</a>] | [<a href="docs/README-ML.md">മലയാളം</a>] | [<a href="docs/README-JP.md">日本語</a>] | [<a href="docs/README-NL.md">Nederlands</a>] | [<a href="docs/README-IT.md">Italiano</a>] | [<a href="docs/README-RU.md">Русский</a>] | [<a href="docs/README-PTBR.md">Português (Brasil)</a>] | [<a href="docs/README-EO.md">Esperanto</a>] | [<a href="docs/README-KR.md">한국어</a>] | [<a href="docs/README-AR.md">العربي</a>] | [<a href="docs/README-VN.md">Tiếng Việt</a>] | [<a href="docs/README-DA.md">Dansk</a>] | [<a href="docs/README-GR.md">Ελληνικά</a>] | [<a href="docs/README-TR.md">Türkçe</a>] | [<a href="docs/README-NO.md">Norsk</a>] | [<a href="docs/README-RO.md">Română</a>]<br>
<b>Wir brauchen Ihre Hilfe, um dieses README, die <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk-Benutzeroberfläche</a> und die <a href="https://github.com/rustdesk/doc.rustdesk.com">Dokumentation</a> in Ihre Muttersprache zu übersetzen.</b>
</p>
> [!Vorsicht]
> [!Caution]
> **Haftungsausschluss bei Missbrauch::** <br>
> Die Entwickler von RustDesk billigen oder unterstützen keine unethische oder illegale Nutzung dieser Software. Missbrauch, wie unbefugter Zugriff, unbefugte Kontrolle oder Verletzung der Privatsphäre, verstößt strikt gegen unsere Richtlinien. Die Autoren sind nicht verantwortlich für jeglichen Missbrauch der Anwendung.
@@ -28,11 +27,14 @@ RustDesk heißt jegliche Mitarbeit willkommen. Schauen Sie sich [CONTRIBUTING-DE
[**Programm herunterladen**](https://github.com/rustdesk/rustdesk/releases)
[**Nächtliche Erstellung**](https://github.com/rustdesk/rustdesk/releases/tag/nightly)
[**Nightly Builds**](https://github.com/rustdesk/rustdesk/releases/tag/nightly)
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
[<img src="https://f-droid.org/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
[<img src="https://flathub.org/api/badge?svg&locale=en"
alt="Get it on Flathub"
height="80">](https://flathub.org/apps/com.rustdesk.RustDesk)
## Abhängigkeiten
@@ -64,18 +66,19 @@ Bitte laden Sie die dynamische Bibliothek Sciter selbst herunter.
```sh
sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev \
libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake make \
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libpam0g-dev
```
### openSUSE Tumbleweed
```sh
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel pam-devel
```
### Fedora 28 (CentOS 8)
```sh
sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libxdo-devel libXfixes-devel pulseaudio-libs-devel cmake alsa-lib-devel
sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libxdo-devel libXfixes-devel pulseaudio-libs-devel cmake alsa-lib-devel gstreamer1-devel gstreamer1-plugins-base-devel pam-devel
```
### Arch (Manjaro)
@@ -114,7 +117,7 @@ cd
```sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
git clone https://github.com/rustdesk/rustdesk
git clone --recurse-submodules https://github.com/rustdesk/rustdesk
cd rustdesk
mkdir -p target/debug
wget https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so
@@ -129,6 +132,7 @@ Beginnen Sie damit, das Repository zu klonen und den Docker-Container zu bauen:
```sh
git clone https://github.com/rustdesk/rustdesk
cd rustdesk
git submodule update --init --recursive
docker build -t "rustdesk-builder" .
```
@@ -157,6 +161,7 @@ Bitte stellen Sie sicher, dass Sie diese Befehle im Stammverzeichnis des RustDes
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: Video-Codec, Konfiguration, TCP/UDP-Wrapper, Protokoll-Puffer, fs-Funktionen für Dateitransfer und ein paar andere nützliche Funktionen
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: Bildschirmaufnahme
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: Plattformspezifische Maus- und Tastatursteuerung
- **[libs/clipboard](https://github.com/rustdesk/rustdesk/tree/master/libs/clipboard)**: Datei kopieren und einfügen Implementierung für Windows, Linux, macOS.
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: Audio/Zwischenablage/Eingabe/Videodienste und Netzwerkverbindungen
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: Starten einer Peer-Verbindung
@@ -167,10 +172,11 @@ Bitte stellen Sie sicher, dass Sie diese Befehle im Stammverzeichnis des RustDes
## Screenshots
![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png)
![Verbindungsmanager](https://github.com/rustdesk/rustdesk/assets/28412477/db82d4e7-c4bc-4823-8e6f-6af7eadf7651)
![image](https://user-images.githubusercontent.com/71636191/113112619-f705a480-923b-11eb-911d-97e984ef52b6.png)
![Verbunden zu einem Windows PC](https://github.com/rustdesk/rustdesk/assets/28412477/9baa91e9-3362-4d06-aa1a-7518edcbd7ea)
![image](https://user-images.githubusercontent.com/71636191/113112857-3fbd5d80-923c-11eb-9836-768325faf906.png)
![Dateiübertragung](https://github.com/rustdesk/rustdesk/assets/28412477/39511ad3-aa9a-4f8c-8947-1cce286a46ad)
![TCP-Tunneling](https://github.com/rustdesk/rustdesk/assets/28412477/78e8708f-e87e-4570-8373-1360033ea6c5)
![image](https://user-images.githubusercontent.com/71636191/135385039-38fdbd72-379a-422d-b97f-33df71fb1cec.png)

View File

@@ -13,7 +13,9 @@ Porozmawiaj z nami na: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](http
[![RustDesk Server Pro](https://img.shields.io/badge/RustDesk%20Server%20Pro-Zaawansowane%20Funkcje-blue)](https://rustdesk.com/pricing.html)
Kolejny program do zdalnego pulpitu, napisany w Rust. Działa od samego początku, nie wymaga konfiguracji. Masz pełną kontrolę nad swoimi danymi, bez obaw o bezpieczeństwo. Możesz skorzystać z naszego darmowego serwera publicznego, [skonfigurować własny](https://rustdesk.com/server), lub [napisać własny serwer](https://github.com/rustdesk/rustdesk-server-demo).
## O projekcie
RustDesk to wieloplatformowe oprogramowanie do zdalnego pulpitu, napisane w języku Rust, zaprojektowane z myślą o prostocie wdrożenia, bezpieczeństwie i pełnej kontroli użytkownika nad danymi. Aplikacja działa od razu po uruchomieniu i nie wymaga skomplikowanej konfiguracji. Możesz skorzystać z naszego darmowego serwera publicznego, [skonfigurować własny](https://rustdesk.com/server), lub [napisać własny serwer](https://github.com/rustdesk/rustdesk-server-demo).
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
@@ -31,7 +33,7 @@ RustDesk zaprasza do współpracy każdego. Zobacz [`docs/CONTRIBUTING-PL.md`](C
## Zależności
Wersje desktopowe używają [sciter](https://sciter.com/) dla GUI, proszę pobrać samodzielnie bibliotekę sciter.
Wersje desktopowe korzystają z biblioteki [sciter](https://sciter.com/) jako silnika GUI. Bibliotekę Sciter należy pobrać i zainstalować samodzielnie.
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |

View File

@@ -55,6 +55,7 @@
],
"finish-args": [
"--share=ipc",
"--socket=wayland",
"--socket=x11",
"--share=network",
"--filesystem=home",

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,
),
);
@@ -1578,7 +1579,7 @@ bool option2bool(String option, String value) {
option == kOptionForceAlwaysRelay) {
res = value == "Y";
} else {
assert(false);
// "" is true
res = value != "N";
}
return res;
@@ -1596,9 +1597,6 @@ String bool2option(String option, bool b) {
option == kOptionForceAlwaysRelay) {
res = b ? 'Y' : defaultOptionNo;
} else {
if (option != kOptionEnableUdpPunch && option != kOptionEnableIpv6Punch) {
assert(false);
}
res = b ? 'Y' : 'N';
}
return res;
@@ -2684,20 +2682,44 @@ class SimpleWrapper<T> {
/// This manager handles multiple tabs within the same isolate.
class WakelockManager {
static final Set<UniqueKey> _enabledKeys = {};
// Don't use WakelockPlus.enabled, it causes error on Android:
// Unhandled Exception: FormatException: Message corrupted
//
// On Linux, multiple enable() calls create only one inhibit, but each disable()
// only releases if _cookie != null. So we need our own _enabled state to avoid
// calling disable() when not enabled.
// See: https://github.com/fluttercommunity/wakelock_plus/blob/0c74e5bbc6aefac57b6c96bb7ef987705ed559ec/wakelock_plus/lib/src/wakelock_plus_linux_plugin.dart#L48
static bool _enabled = false;
static void enable(UniqueKey key) {
if (isLinux) return;
_enabledKeys.add(key);
WakelockPlus.enable();
static void enable(UniqueKey key, {bool isServer = false}) {
// Check if we should keep awake during outgoing sessions
if (!isServer) {
final keepAwake =
mainGetLocalBoolOptionSync(kOptionKeepAwakeDuringOutgoingSessions);
if (!keepAwake) {
return; // Don't enable wakelock if user disabled keep awake
}
}
if (isDesktop) {
_enabledKeys.add(key);
}
if (!_enabled) {
_enabled = true;
WakelockPlus.enable();
}
}
static void disable(UniqueKey key) {
if (isLinux) return;
if (_enabledKeys.remove(key)) {
if (_enabledKeys.isEmpty) {
WakelockPlus.disable();
if (isDesktop) {
_enabledKeys.remove(key);
if (_enabledKeys.isNotEmpty) {
return;
}
}
if (_enabled) {
WakelockPlus.disable();
_enabled = false;
}
}
}

View File

@@ -25,6 +25,7 @@ enum UserStatus { kDisabled, kNormal, kUnverified }
// Is all the fields of the user needed?
class UserPayload {
String name = '';
String displayName = '';
String email = '';
String note = '';
String? verifier;
@@ -33,6 +34,7 @@ class UserPayload {
UserPayload.fromJson(Map<String, dynamic> json)
: name = json['name'] ?? '',
displayName = json['display_name'] ?? '',
email = json['email'] ?? '',
note = json['note'] ?? '',
verifier = json['verifier'],
@@ -46,6 +48,7 @@ class UserPayload {
Map<String, dynamic> toJson() {
final Map<String, dynamic> map = {
'name': name,
'display_name': displayName,
'status': status == UserStatus.kDisabled
? 0
: status == UserStatus.kUnverified
@@ -58,9 +61,14 @@ class UserPayload {
Map<String, dynamic> toGroupCacheJson() {
final Map<String, dynamic> map = {
'name': name,
'display_name': displayName,
};
return map;
}
String get displayNameOrName {
return displayName.trim().isEmpty ? name : displayName;
}
}
class PeerPayload {

View File

@@ -25,6 +25,7 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
GestureDragStartCallback? onOneFingerPanStart;
GestureDragUpdateCallback? onOneFingerPanUpdate;
GestureDragEndCallback? onOneFingerPanEnd;
GestureDragCancelCallback? onOneFingerPanCancel;
// twoFingerScale : scale + pan event
GestureScaleStartCallback? onTwoFingerScaleStart;
@@ -169,6 +170,27 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
DragEndDetails _getDragEndDetails(ScaleEndDetails d) =>
DragEndDetails(velocity: d.velocity);
@override
void rejectGesture(int pointer) {
super.rejectGesture(pointer);
switch (_currentState) {
case GestureState.oneFingerPan:
if (onOneFingerPanCancel != null) {
onOneFingerPanCancel!();
}
break;
case GestureState.twoFingerScale:
// Reset scale state if needed, currently self-contained
break;
case GestureState.threeFingerVerticalDrag:
// Reset drag state if needed, currently self-contained
break;
default:
break;
}
_currentState = GestureState.none;
}
}
class HoldTapMoveGestureRecognizer extends GestureRecognizer {
@@ -717,6 +739,7 @@ RawGestureDetector getMixinGestureDetector({
GestureDragStartCallback? onOneFingerPanStart,
GestureDragUpdateCallback? onOneFingerPanUpdate,
GestureDragEndCallback? onOneFingerPanEnd,
GestureDragCancelCallback? onOneFingerPanCancel,
GestureScaleUpdateCallback? onTwoFingerScaleUpdate,
GestureScaleEndCallback? onTwoFingerScaleEnd,
GestureDragUpdateCallback? onThreeFingerVerticalDragUpdate,
@@ -765,6 +788,7 @@ RawGestureDetector getMixinGestureDetector({
..onOneFingerPanStart = onOneFingerPanStart
..onOneFingerPanUpdate = onOneFingerPanUpdate
..onOneFingerPanEnd = onOneFingerPanEnd
..onOneFingerPanCancel = onOneFingerPanCancel
..onTwoFingerScaleUpdate = onTwoFingerScaleUpdate
..onTwoFingerScaleEnd = onTwoFingerScaleEnd
..onThreeFingerVerticalDragUpdate = onThreeFingerVerticalDragUpdate;

View File

@@ -103,7 +103,7 @@ class ButtonOP extends StatelessWidget {
child: FittedBox(
fit: BoxFit.scaleDown,
child: Center(
child: Text('${translate("Continue with")} $opLabel')),
child: Text(translate("Continue with {$opLabel}"))),
),
),
],

View File

@@ -158,9 +158,9 @@ class _MyGroupState extends State<MyGroup> {
return Obx(() {
final userItems = gFFI.groupModel.users.where((p0) {
if (searchAccessibleItemNameText.isNotEmpty) {
return p0.name
.toLowerCase()
.contains(searchAccessibleItemNameText.value.toLowerCase());
final search = searchAccessibleItemNameText.value.toLowerCase();
return p0.name.toLowerCase().contains(search) ||
p0.displayNameOrName.toLowerCase().contains(search);
}
return true;
}).toList();
@@ -187,6 +187,7 @@ class _MyGroupState extends State<MyGroup> {
Widget _buildUserItem(UserPayload user) {
final username = user.name;
final displayName = user.displayNameOrName;
return InkWell(onTap: () {
isSelectedDeviceGroup.value = false;
if (selectedAccessibleItemName.value != username) {
@@ -229,7 +230,7 @@ class _MyGroupState extends State<MyGroup> {
),
),
).marginOnly(right: 4),
if (isMe) Flexible(child: Text(username)),
if (isMe) Flexible(child: Text(displayName)),
if (isMe)
Flexible(
child: Container(
@@ -246,7 +247,7 @@ class _MyGroupState extends State<MyGroup> {
),
),
),
if (!isMe) Expanded(child: Text(username)),
if (!isMe) Expanded(child: Text(displayName)),
],
).paddingSymmetric(vertical: 4),
),

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,11 +157,16 @@ 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);
if (isMoved) {
if (lastTapDownDetails != null) {
// If pan already handled 'down', don't send it again.
if (lastTapDownDetails != null && !_touchModePanStarted) {
await inputModel.tapDown(MouseButtons.left);
}
await inputModel.tapUp(MouseButtons.left);
@@ -170,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.
@@ -424,6 +437,14 @@ class _RawTouchGestureDetectorRegionState
}
}
// Reset `_touchModePanStarted` if the one-finger pan gesture is cancelled
// or rejected by the gesture arena. Without this, the flag can remain
// stuck in the "started" state and cause issues such as the Magic Mouse
// double-click problem on iPad with magic mouse.
onOneFingerPanCancel() {
_touchModePanStarted = false;
}
// scale + pan event
onTwoFingerScaleStart(ScaleStartDetails d) {
_lastTapDownDetails = null;
@@ -557,6 +578,7 @@ class _RawTouchGestureDetectorRegionState
instance
..onOneFingerPanUpdate = onOneFingerPanUpdate
..onOneFingerPanEnd = onOneFingerPanEnd
..onOneFingerPanCancel = onOneFingerPanCancel
..onTwoFingerScaleStart = onTwoFingerScaleStart
..onTwoFingerScaleUpdate = onTwoFingerScaleUpdate
..onTwoFingerScaleEnd = onTwoFingerScaleEnd

View File

@@ -194,6 +194,9 @@ 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";

View File

@@ -450,7 +450,11 @@ class _DesktopHomePageState extends State<DesktopHomePage>
"${translate("new-version-of-{${bind.mainGetAppNameSync()}}-tip")} (${bind.mainGetNewVersion()}).",
btnText,
onPressed,
closeButton: true);
closeButton: true,
help: isToUpdate ? 'Changelog' : null,
link: isToUpdate
? 'https://github.com/rustdesk/rustdesk/releases/tag/${bind.mainGetNewVersion()}'
: null);
}
if (systemError.isNotEmpty) {
return buildInstallCard("", systemError, "", () {});

View File

@@ -557,6 +557,17 @@ class _GeneralState extends State<_General> {
],
],
];
// Add client-side wakelock option for desktop platforms
if (!bind.isIncomingOnly()) {
children.add(_OptionCheckBox(
context,
'keep-awake-during-outgoing-sessions-label',
kOptionKeepAwakeDuringOutgoingSessions,
isServer: false,
));
}
if (!isWeb && bind.mainShowOption(key: kOptionAllowLinuxHeadless)) {
children.add(_OptionCheckBox(
context, 'Allow linux headless', kOptionAllowLinuxHeadless));
@@ -1219,6 +1230,9 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
...directIp(context),
whitelist(),
...autoDisconnect(context),
_OptionCheckBox(context, 'keep-awake-during-incoming-sessions-label',
kOptionKeepAwakeDuringIncomingSessions,
reverse: false, enabled: enabled),
if (bind.mainIsInstalled())
_OptionCheckBox(context, 'allow-only-conn-window-open-tip',
'allow-only-conn-window-open',
@@ -2002,7 +2016,9 @@ class _AccountState extends State<_Account> {
Widget accountAction() {
return Obx(() => _Button(
gFFI.userModel.userName.value.isEmpty ? 'Login' : 'Logout',
gFFI.userModel.userName.value.isEmpty
? 'Login'
: 'Logout (${gFFI.userModel.accountLabelWithHandle})',
() => {
gFFI.userModel.userName.value.isEmpty
? loginDialog()
@@ -2023,6 +2039,9 @@ class _AccountState extends State<_Account> {
offstage: gFFI.userModel.userName.value.isEmpty,
child: Column(
children: [
if (gFFI.userModel.displayName.value.trim().isNotEmpty &&
gFFI.userModel.displayName.value != gFFI.userModel.userName.value)
text('Display Name', gFFI.userModel.displayName.value),
text('Username', gFFI.userModel.userName.value),
// text('Group', gFFI.groupModel.groupName.value),
],
@@ -2116,7 +2135,9 @@ class _PluginState extends State<_Plugin> {
Widget accountAction() {
return Obx(() => _Button(
gFFI.userModel.userName.value.isEmpty ? 'Login' : 'Logout',
gFFI.userModel.userName.value.isEmpty
? 'Login'
: 'Logout (${gFFI.userModel.accountLabelWithHandle})',
() => {
gFFI.userModel.userName.value.isEmpty
? loginDialog()
@@ -2524,6 +2545,49 @@ class WaylandCard extends StatefulWidget {
class _WaylandCardState extends State<WaylandCard> {
final restoreTokenKey = 'wayland-restore-token';
static const _kClearShortcutsInhibitorEventKey =
'clear-gnome-shortcuts-inhibitor-permission-res';
final _clearShortcutsInhibitorFailedMsg = ''.obs;
// Don't show the shortcuts permission reset button for now.
// Users can change it manually:
// "Settings" -> "Apps" -> "RustDesk" -> "Permissions" -> "Inhibit Shortcuts".
// For resetting(clearing) the permission from the portal permission store, you can
// use (replace <desktop-id> with the RustDesk desktop file ID):
// busctl --user call org.freedesktop.impl.portal.PermissionStore \
// /org/freedesktop/impl/portal/PermissionStore org.freedesktop.impl.portal.PermissionStore \
// DeletePermission sss "gnome" "shortcuts-inhibitor" "<desktop-id>"
// On a native install this is typically "rustdesk.desktop"; on Flatpak it is usually
// the exported desktop ID derived from the Flatpak app-id (e.g. "com.rustdesk.RustDesk.desktop").
//
// We may add it back in the future if needed.
final showResetInhibitorPermission = false;
@override
void initState() {
super.initState();
if (showResetInhibitorPermission) {
platformFFI.registerEventHandler(
_kClearShortcutsInhibitorEventKey, _kClearShortcutsInhibitorEventKey,
(evt) async {
if (!mounted) return;
if (evt['success'] == true) {
setState(() {});
} else {
_clearShortcutsInhibitorFailedMsg.value =
evt['msg'] as String? ?? 'Unknown error';
}
});
}
}
@override
void dispose() {
if (showResetInhibitorPermission) {
platformFFI.unregisterEventHandler(
_kClearShortcutsInhibitorEventKey, _kClearShortcutsInhibitorEventKey);
}
super.dispose();
}
@override
Widget build(BuildContext context) {
@@ -2531,9 +2595,16 @@ class _WaylandCardState extends State<WaylandCard> {
future: bind.mainHandleWaylandScreencastRestoreToken(
key: restoreTokenKey, value: "get"),
hasData: (restoreToken) {
final hasShortcutsPermission = showResetInhibitorPermission &&
bind.mainGetCommonSync(
key: "has-gnome-shortcuts-inhibitor-permission") ==
"true";
final children = [
if (restoreToken.isNotEmpty)
_buildClearScreenSelection(context, restoreToken),
if (hasShortcutsPermission)
_buildClearShortcutsInhibitorPermission(context),
];
return Offstage(
offstage: children.isEmpty,
@@ -2578,6 +2649,50 @@ class _WaylandCardState extends State<WaylandCard> {
),
);
}
Widget _buildClearShortcutsInhibitorPermission(BuildContext context) {
onConfirm() {
_clearShortcutsInhibitorFailedMsg.value = '';
bind.mainSetCommon(
key: "clear-gnome-shortcuts-inhibitor-permission", value: "");
gFFI.dialogManager.dismissAll();
}
showConfirmMsgBox() => msgBoxCommon(
gFFI.dialogManager,
'Confirmation',
Text(
translate('confirm-clear-shortcuts-inhibitor-permission-tip'),
),
[
dialogButton('OK', onPressed: onConfirm),
dialogButton('Cancel',
onPressed: () => gFFI.dialogManager.dismissAll())
]);
return Column(children: [
Obx(
() => _clearShortcutsInhibitorFailedMsg.value.isEmpty
? Offstage()
: Align(
alignment: Alignment.topLeft,
child: Text(_clearShortcutsInhibitorFailedMsg.value,
style: DefaultTextStyle.of(context)
.style
.copyWith(color: Colors.red))
.marginOnly(bottom: 10.0)),
),
_Button(
'Reset keyboard shortcuts permission',
showConfirmMsgBox,
tip: 'clear-shortcuts-inhibitor-permission-tip',
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
Theme.of(context).colorScheme.error.withOpacity(0.75)),
),
),
]);
}
}
// ignore: non_constant_identifier_names

View File

@@ -1861,8 +1861,18 @@ class _KeyboardMenu extends StatelessWidget {
continue;
}
if (pi.isWayland && mode.key != kKeyMapMode) {
continue;
if (pi.isWayland) {
// Legacy mode is hidden on desktop control side because dead keys
// don't work properly on Wayland. When the control side is mobile,
// Legacy mode is used automatically (mobile always sends Legacy events).
if (mode.key == kKeyLegacyMode) {
continue;
}
// Translate mode requires server >= 1.4.6.
if (mode.key == kKeyTranslateMode &&
versionCmp(pi.version, '1.4.6') < 0) {
continue;
}
}
var text = translate(mode.menu);

View File

@@ -5,7 +5,6 @@ import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
import 'package:flutter_hbb/models/file_model.dart';
import 'package:get/get.dart';
import 'package:toggle_switch/toggle_switch.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import '../../common.dart';
import '../../common/widgets/dialog.dart';
@@ -72,6 +71,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
showLocal ? model.localController : model.remoteController;
FileDirectory get currentDir => currentFileController.directory.value;
DirectoryOptions get currentOptions => currentFileController.options.value;
final _uniqueKey = UniqueKey();
@override
void initState() {
@@ -86,7 +86,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
.showLoading(translate('Connecting...'), onCancel: closeConnection);
});
gFFI.ffiModel.updateEventListener(gFFI.sessionId, widget.id);
WakelockPlus.enable();
WakelockManager.enable(_uniqueKey);
}
@override
@@ -94,7 +94,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
model.close().whenComplete(() {
gFFI.close();
gFFI.dialogManager.dismissAll();
WakelockPlus.disable();
WakelockManager.disable(_uniqueKey);
});
model.jobController.clear();
super.dispose();

View File

@@ -14,7 +14,6 @@ import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
import 'package:flutter_svg/svg.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import '../../common.dart';
import '../../common/widgets/overlay.dart';
@@ -67,8 +66,9 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
String _value = '';
Orientation? _currentOrientation;
double _viewInsetsBottom = 0;
final _uniqueKey = UniqueKey();
Timer? _timerDidChangeMetrics;
Timer? _iosKeyboardWorkaroundTimer;
final _blockableOverlayState = BlockableOverlayState();
@@ -105,9 +105,7 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
gFFI.dialogManager
.showLoading(translate('Connecting...'), onCancel: closeConnection);
});
if (!isWeb) {
WakelockPlus.enable();
}
WakelockManager.enable(_uniqueKey);
_physicalFocusNode.requestFocus();
gFFI.inputModel.listenToMouse(true);
gFFI.qualityMonitorModel.checkShowQualityMonitor(sessionId);
@@ -143,12 +141,11 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
await gFFI.close();
_timer?.cancel();
_timerDidChangeMetrics?.cancel();
_iosKeyboardWorkaroundTimer?.cancel();
gFFI.dialogManager.dismissAll();
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values);
if (!isWeb) {
await WakelockPlus.disable();
}
WakelockManager.disable(_uniqueKey);
await keyboardSubscription.cancel();
removeSharedStates(widget.id);
// `on_voice_call_closed` should be called when the connection is ended.
@@ -211,7 +208,24 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
gFFI.ffiModel.pi.version.isNotEmpty) {
gFFI.invokeMethod("enable_soft_keyboard", false);
}
// Workaround for iOS: physical keyboard input fails after virtual keyboard is hidden
// https://github.com/flutter/flutter/issues/39900
// https://github.com/rustdesk/rustdesk/discussions/11843#discussioncomment-13499698 - Virtual keyboard issue
if (isIOS) {
_iosKeyboardWorkaroundTimer?.cancel();
_iosKeyboardWorkaroundTimer = Timer(Duration(milliseconds: 100), () {
if (!mounted) return;
_physicalFocusNode.unfocus();
_iosKeyboardWorkaroundTimer = Timer(Duration(milliseconds: 50), () {
if (!mounted) return;
_physicalFocusNode.requestFocus();
});
});
}
} else {
_iosKeyboardWorkaroundTimer?.cancel();
_iosKeyboardWorkaroundTimer = null;
_timer?.cancel();
_timer = Timer(kMobileDelaySoftKeyboardFocus, () {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,

View File

@@ -100,6 +100,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
var _enableIpv6Punch = false;
var _isUsingPublicServer = false;
var _allowAskForNoteAtEndOfConnection = false;
var _preventSleepWhileConnected = true;
_SettingsState() {
_enableAbr = option2bool(
@@ -140,6 +141,8 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
_enableIpv6Punch = mainGetLocalBoolOptionSync(kOptionEnableIpv6Punch);
_allowAskForNoteAtEndOfConnection =
mainGetLocalBoolOptionSync(kOptionAllowAskForNoteAtEndOfConnection);
_preventSleepWhileConnected =
mainGetLocalBoolOptionSync(kOptionKeepAwakeDuringOutgoingSessions);
_showTerminalExtraKeys =
mainGetLocalBoolOptionSync(kOptionEnableShowTerminalExtraKeys);
}
@@ -685,7 +688,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
SettingsTile(
title: Obx(() => Text(gFFI.userModel.userName.value.isEmpty
? translate('Login')
: '${translate('Logout')} (${gFFI.userModel.userName.value})')),
: '${translate('Logout')} (${gFFI.userModel.accountLabelWithHandle})')),
leading: Icon(Icons.person),
onPressed: (context) {
if (gFFI.userModel.userName.value.isEmpty) {
@@ -823,7 +826,18 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
_allowAskForNoteAtEndOfConnection = newValue;
});
},
)
),
if (!incomingOnly)
SettingsTile.switchTile(
title: Text(translate('keep-awake-during-outgoing-sessions-label')),
initialValue: _preventSleepWhileConnected,
onToggle: (v) async {
await mainSetLocalBoolOption(kOptionKeepAwakeDuringOutgoingSessions, v);
setState(() {
_preventSleepWhileConnected = v;
});
},
),
]),
if (isAndroid)
SettingsSection(title: Text(translate('Hardware Codec')), tiles: [

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(
@@ -164,6 +168,13 @@ class _TerminalPageState extends State<TerminalPage>
autofocus: true,
textStyle: _getTerminalStyle(),
backgroundOpacity: 0.7,
// The following comment is from xterm.dart source code:
// Workaround to detect delete key for platforms and IMEs that do not
// emit a hardware delete event. Preferred on mobile platforms. [false] by
// default.
//
// Android works fine without this workaround.
deleteDetection: isIOS,
padding: _calculatePadding(heightPx),
onSecondaryTapDown: (details, offset) async {
final selection = _terminalModel.terminalController.selection;
@@ -185,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

@@ -11,7 +11,6 @@ import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
import 'package:flutter_svg/svg.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import '../../common.dart';
import '../../common/widgets/overlay.dart';
@@ -62,7 +61,7 @@ class _ViewCameraPageState extends State<ViewCameraPage>
bool _showGestureHelp = false;
Orientation? _currentOrientation;
double _viewInsetsBottom = 0;
final _uniqueKey = UniqueKey();
Timer? _timerDidChangeMetrics;
final _blockableOverlayState = BlockableOverlayState();
@@ -100,9 +99,7 @@ class _ViewCameraPageState extends State<ViewCameraPage>
gFFI.dialogManager
.showLoading(translate('Connecting...'), onCancel: closeConnection);
});
if (!isWeb) {
WakelockPlus.enable();
}
WakelockManager.enable(_uniqueKey);
_physicalFocusNode.requestFocus();
gFFI.inputModel.listenToMouse(true);
gFFI.qualityMonitorModel.checkShowQualityMonitor(sessionId);
@@ -139,9 +136,7 @@ class _ViewCameraPageState extends State<ViewCameraPage>
gFFI.dialogManager.dismissAll();
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values);
if (!isWeb) {
await WakelockPlus.disable();
}
WakelockManager.disable(_uniqueKey);
removeSharedStates(widget.id);
// `on_voice_call_closed` should be called when the connection is ended.
// The inner logic of `on_voice_call_closed` will check if the voice call is active.

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;
@@ -1048,6 +1104,14 @@ class InputModel {
if (isViewOnly && !showMyCursor) return;
if (e.kind != ui.PointerDeviceKind.mouse) return;
// May fix https://github.com/rustdesk/rustdesk/issues/13009
if (isIOS && e.synthesized && e.position == Offset.zero && e.buttons == 0) {
// iOS may emit a synthesized hover event at (0,0) when the mouse is disconnected.
// Ignore this event to prevent cursor jumping.
debugPrint('Ignored synthesized hover at (0,0) on iOS');
return;
}
// Only update pointer region when relative mouse mode is enabled.
// This avoids unnecessary tracking when not in relative mode.
if (_relativeMouse.enabled.value) {
@@ -1210,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;
@@ -1219,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);
}
@@ -1760,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

@@ -120,6 +120,7 @@ class FfiModel with ChangeNotifier {
late VirtualMouseMode virtualMouseMode;
Timer? _timer;
var _reconnects = 1;
DateTime? _offlineReconnectStartTime;
bool _viewOnly = false;
bool _showMyCursor = false;
WeakReference<FFI> parent;
@@ -783,7 +784,8 @@ class FfiModel with ChangeNotifier {
}
}
Future<void> updateCurDisplay(SessionID sessionId, {updateCursorPos = false}) async {
Future<void> updateCurDisplay(SessionID sessionId,
{updateCursorPos = false}) async {
final newRect = displaysRect();
if (newRect == null) {
return;
@@ -939,11 +941,46 @@ class FfiModel with ChangeNotifier {
showPrivacyFailedDialog(
sessionId, type, title, text, link, hasRetry, dialogManager);
} else {
final hasRetry = evt['hasRetry'] == 'true';
var hasRetry = evt['hasRetry'] == 'true';
if (!hasRetry) {
hasRetry = shouldAutoRetryOnOffline(type, title, text);
}
showMsgBox(sessionId, type, title, text, link, hasRetry, dialogManager);
}
}
/// Auto-retry check for "Remote desktop is offline" error.
/// returns true to auto-retry, false otherwise.
bool shouldAutoRetryOnOffline(
String type,
String title,
String text,
) {
if (type == 'error' &&
title == 'Connection Error' &&
text == 'Remote desktop is offline' &&
_pi.isSet.isTrue) {
// Auto retry for ~30s (server's peer offline threshold) when controlled peer's account changes
// (e.g., signout, switch user, login into OS) causes temporary offline via websocket/tcp connection.
// The actual wait may exceed 30s (e.g., 20s elapsed + 16s next retry = 36s), which is acceptable
// since the controlled side reconnects quickly after account changes.
// Uses time-based check instead of _reconnects count because user can manually retry.
// https://github.com/rustdesk/rustdesk/discussions/14048
if (_offlineReconnectStartTime == null) {
// First offline, record time and start retry
_offlineReconnectStartTime = DateTime.now();
return true;
} else {
final elapsed =
DateTime.now().difference(_offlineReconnectStartTime!).inSeconds;
if (elapsed < 30) {
return true;
}
}
}
return false;
}
handleToast(Map<String, dynamic> evt, SessionID sessionId, String peerId) {
final type = evt['type'] ?? 'info';
final text = evt['text'] ?? '';
@@ -1001,6 +1038,7 @@ class FfiModel with ChangeNotifier {
_reconnects *= 2;
} else {
_reconnects = 1;
_offlineReconnectStartTime = null;
}
}
@@ -1323,6 +1361,7 @@ class FfiModel with ChangeNotifier {
}
if (displays.isNotEmpty) {
_reconnects = 1;
_offlineReconnectStartTime = null;
waitForFirstImage.value = true;
isRefreshing = false;
}
@@ -2176,10 +2215,32 @@ class CanvasModel with ChangeNotifier {
double w = size.width - leftToEdge - rightToEdge;
double h = size.height - topToEdge - bottomToEdge;
if (isMobile) {
// Account for horizontal safe area insets on both orientations.
w = w - mediaData.padding.left - mediaData.padding.right;
// Vertically, subtract the bottom keyboard inset (viewInsets.bottom) and any
// bottom overlay (e.g. key-help tools) so the canvas is not covered.
h = h -
mediaData.viewInsets.bottom -
(parent.target?.cursorModel.keyHelpToolsRectToAdjustCanvas?.bottom ??
0);
// Orientation-specific handling:
// - Portrait: additionally subtract top padding (e.g. status bar / notch)
// - Landscape: does not subtract mediaData.padding.top/bottom (home indicator auto-hides)
final isPortrait = size.height > size.width;
if (isPortrait) {
// In portrait mode, subtract the top safe-area padding (e.g. status bar / notch)
// so the remote image is not truncated, while keeping the bottom inset to avoid
// introducing unnecessary blank space around the canvas.
//
// iOS -> Android, portrait, adjust mode:
// h = h (no padding subtracted): top and bottom are truncated
// https://github.com/user-attachments/assets/30ed4559-c27e-432b-847f-8fec23c9f998
// h = h - top - bottom: extra blank spaces appear
// https://github.com/user-attachments/assets/12a98817-3b4e-43aa-be0f-4b03cf364b7e
// h = h - top (current): works fine
// https://github.com/user-attachments/assets/95f047f2-7f47-4a36-8113-5023989a0c81
h = h - mediaData.padding.top;
}
}
return Size(w < 0 ? 0 : w, h < 0 ? 0 : h);
}

View File

@@ -8,7 +8,6 @@ import 'package:flutter_hbb/mobile/pages/settings_page.dart';
import 'package:flutter_hbb/models/chat_model.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:window_manager/window_manager.dart';
import '../common.dart';
@@ -51,6 +50,8 @@ class ServerModel with ChangeNotifier {
Timer? cmHiddenTimer;
final _wakelockKey = UniqueKey();
bool get isStart => _isStart;
bool get mediaOk => _mediaOk;
@@ -466,10 +467,8 @@ class ServerModel with ChangeNotifier {
await parent.target?.invokeMethod("stop_service");
await bind.mainStopService();
notifyListeners();
if (!isLinux) {
// current linux is not supported
WakelockPlus.disable();
}
// for androidUpdatekeepScreenOn only
WakelockManager.disable(_wakelockKey);
}
Future<bool> setPermanentPassword(String newPW) async {
@@ -613,12 +612,12 @@ class ServerModel with ChangeNotifier {
void showLoginDialog(Client client) {
showClientDialog(
client,
client.isFileTransfer
? "Transfer file"
client.isFileTransfer
? "Transfer file"
: client.isViewCamera
? "View camera"
: client.isTerminal
? "Terminal"
: client.isTerminal
? "Terminal"
: "Share screen",
'Do you accept?',
'android_new_connection_tip',
@@ -797,12 +796,10 @@ class ServerModel with ChangeNotifier {
final on = ((keepScreenOn == KeepScreenOn.serviceOn) && _isStart) ||
(keepScreenOn == KeepScreenOn.duringControlled &&
_clients.map((e) => !e.disconnected).isNotEmpty);
if (on != await WakelockPlus.enabled) {
if (on) {
WakelockPlus.enable();
} else {
WakelockPlus.disable();
}
if (on) {
WakelockManager.enable(_wakelockKey, isServer: true);
} else {
WakelockManager.disable(_wakelockKey);
}
}
}

View File

@@ -16,9 +16,23 @@ bool refreshingUser = false;
class UserModel {
final RxString userName = ''.obs;
final RxString displayName = ''.obs;
final RxBool isAdmin = false.obs;
final RxString networkError = ''.obs;
bool get isLogin => userName.isNotEmpty;
String get displayNameOrUserName =>
displayName.value.trim().isEmpty ? userName.value : displayName.value;
String get accountLabelWithHandle {
final username = userName.value.trim();
if (username.isEmpty) {
return '';
}
final preferred = displayName.value.trim();
if (preferred.isEmpty || preferred == username) {
return username;
}
return '$preferred (@$username)';
}
WeakReference<FFI> parent;
UserModel(this.parent) {
@@ -98,7 +112,8 @@ class UserModel {
_updateLocalUserInfo() {
final userInfo = getLocalUserInfo();
if (userInfo != null) {
userName.value = userInfo['name'];
userName.value = (userInfo['name'] ?? '').toString();
displayName.value = (userInfo['display_name'] ?? '').toString();
}
}
@@ -110,10 +125,12 @@ class UserModel {
await gFFI.groupModel.reset();
}
userName.value = '';
displayName.value = '';
}
_parseAndUpdateUser(UserPayload user) {
userName.value = user.name;
displayName.value = user.displayName;
isAdmin.value = user.isAdmin;
bind.mainSetLocalOption(key: 'user_info', value: jsonEncode(user));
if (isWeb) {

View File

@@ -1,6 +1,6 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
project(runner LANGUAGES C CXX)
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
@@ -54,6 +54,55 @@ add_subdirectory(${FLUTTER_MANAGED_DIR})
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
# Wayland protocol for keyboard shortcuts inhibit
pkg_check_modules(WAYLAND_CLIENT IMPORTED_TARGET wayland-client)
pkg_check_modules(WAYLAND_PROTOCOLS_PKG QUIET wayland-protocols)
pkg_check_modules(WAYLAND_SCANNER_PKG QUIET wayland-scanner)
if(WAYLAND_PROTOCOLS_PKG_FOUND)
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
endif()
if(WAYLAND_SCANNER_PKG_FOUND)
pkg_get_variable(WAYLAND_SCANNER wayland-scanner wayland_scanner)
endif()
if(WAYLAND_CLIENT_FOUND AND WAYLAND_PROTOCOLS_DIR AND WAYLAND_SCANNER)
set(KEYBOARD_SHORTCUTS_INHIBIT_PROTOCOL
"${WAYLAND_PROTOCOLS_DIR}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml")
if(EXISTS ${KEYBOARD_SHORTCUTS_INHIBIT_PROTOCOL})
set(WAYLAND_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/wayland-protocols")
file(MAKE_DIRECTORY ${WAYLAND_GENERATED_DIR})
# Generate client header
add_custom_command(
OUTPUT "${WAYLAND_GENERATED_DIR}/keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
COMMAND ${WAYLAND_SCANNER} client-header
${KEYBOARD_SHORTCUTS_INHIBIT_PROTOCOL}
"${WAYLAND_GENERATED_DIR}/keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
DEPENDS ${KEYBOARD_SHORTCUTS_INHIBIT_PROTOCOL}
VERBATIM
)
# Generate protocol code
add_custom_command(
OUTPUT "${WAYLAND_GENERATED_DIR}/keyboard-shortcuts-inhibit-unstable-v1-protocol.c"
COMMAND ${WAYLAND_SCANNER} private-code
${KEYBOARD_SHORTCUTS_INHIBIT_PROTOCOL}
"${WAYLAND_GENERATED_DIR}/keyboard-shortcuts-inhibit-unstable-v1-protocol.c"
DEPENDS ${KEYBOARD_SHORTCUTS_INHIBIT_PROTOCOL}
VERBATIM
)
set(WAYLAND_PROTOCOL_SOURCES
"${WAYLAND_GENERATED_DIR}/keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
"${WAYLAND_GENERATED_DIR}/keyboard-shortcuts-inhibit-unstable-v1-protocol.c"
)
set(HAS_KEYBOARD_SHORTCUTS_INHIBIT TRUE)
endif()
endif()
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Define the application target. To change its name, change BINARY_NAME above,
@@ -63,9 +112,11 @@ add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"wayland_shortcuts_inhibit.cc"
"bump_mouse.cc"
"bump_mouse_x11.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
${WAYLAND_PROTOCOL_SOURCES}
)
# Apply the standard set of build settings. This can be removed for applications
@@ -78,6 +129,13 @@ target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
target_link_libraries(${BINARY_NAME} PRIVATE ${CMAKE_DL_LIBS})
# target_link_libraries(${BINARY_NAME} PRIVATE librustdesk)
# Wayland support for keyboard shortcuts inhibit
if(HAS_KEYBOARD_SHORTCUTS_INHIBIT)
target_compile_definitions(${BINARY_NAME} PRIVATE HAS_KEYBOARD_SHORTCUTS_INHIBIT)
target_include_directories(${BINARY_NAME} PRIVATE ${WAYLAND_GENERATED_DIR})
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::WAYLAND_CLIENT)
endif()
# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)

View File

@@ -6,6 +6,11 @@
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#if defined(GDK_WINDOWING_WAYLAND) && defined(HAS_KEYBOARD_SHORTCUTS_INHIBIT)
#include "wayland_shortcuts_inhibit.h"
#endif
#include <desktop_multi_window/desktop_multi_window_plugin.h>
#include "flutter/generated_plugin_registrant.h"
@@ -91,6 +96,13 @@ static void my_application_activate(GApplication* application) {
gtk_widget_show(GTK_WIDGET(window));
gtk_widget_show(GTK_WIDGET(view));
#if defined(GDK_WINDOWING_WAYLAND) && defined(HAS_KEYBOARD_SHORTCUTS_INHIBIT)
// Register callback for sub-windows created by desktop_multi_window plugin
// Only sub-windows (remote windows) need keyboard shortcuts inhibition
desktop_multi_window_plugin_set_window_created_callback(
(WindowCreatedCallback)wayland_shortcuts_inhibit_init_for_subwindow);
#endif
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();

View File

@@ -0,0 +1,244 @@
// Wayland keyboard shortcuts inhibit implementation
// Uses the zwp_keyboard_shortcuts_inhibit_manager_v1 protocol to request
// the compositor to disable system shortcuts for specific windows.
#include "wayland_shortcuts_inhibit.h"
#if defined(GDK_WINDOWING_WAYLAND) && defined(HAS_KEYBOARD_SHORTCUTS_INHIBIT)
#include <cstring>
#include <gdk/gdkwayland.h>
#include <wayland-client.h>
#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
// Data structure to hold inhibitor state for each window
typedef struct {
struct zwp_keyboard_shortcuts_inhibit_manager_v1* manager;
struct zwp_keyboard_shortcuts_inhibitor_v1* inhibitor;
} ShortcutsInhibitData;
// Cleanup function for ShortcutsInhibitData
static void shortcuts_inhibit_data_free(gpointer data) {
ShortcutsInhibitData* inhibit_data = static_cast<ShortcutsInhibitData*>(data);
if (inhibit_data->inhibitor != NULL) {
zwp_keyboard_shortcuts_inhibitor_v1_destroy(inhibit_data->inhibitor);
}
if (inhibit_data->manager != NULL) {
zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(inhibit_data->manager);
}
g_free(inhibit_data);
}
// Wayland registry handler to find the shortcuts inhibit manager
static void registry_handle_global(void* data, struct wl_registry* registry,
uint32_t name, const char* interface,
uint32_t /*version*/) {
ShortcutsInhibitData* inhibit_data = static_cast<ShortcutsInhibitData*>(data);
if (strcmp(interface,
zwp_keyboard_shortcuts_inhibit_manager_v1_interface.name) == 0) {
inhibit_data->manager =
static_cast<zwp_keyboard_shortcuts_inhibit_manager_v1*>(wl_registry_bind(
registry, name, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface,
1));
}
}
static void registry_handle_global_remove(void* /*data*/, struct wl_registry* /*registry*/,
uint32_t /*name*/) {
// Not needed for this use case
}
static const struct wl_registry_listener registry_listener = {
registry_handle_global,
registry_handle_global_remove,
};
// Inhibitor event handlers
static void inhibitor_active(void* /*data*/,
struct zwp_keyboard_shortcuts_inhibitor_v1* /*inhibitor*/) {
// Inhibitor is now active, shortcuts are being captured
}
static void inhibitor_inactive(void* /*data*/,
struct zwp_keyboard_shortcuts_inhibitor_v1* /*inhibitor*/) {
// Inhibitor is now inactive, shortcuts restored to compositor
}
static const struct zwp_keyboard_shortcuts_inhibitor_v1_listener inhibitor_listener = {
inhibitor_active,
inhibitor_inactive,
};
// Forward declaration
static void uninhibit_keyboard_shortcuts(GtkWindow* window);
// Inhibit keyboard shortcuts on Wayland for a specific window
static void inhibit_keyboard_shortcuts(GtkWindow* window) {
GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(window));
if (!GDK_IS_WAYLAND_DISPLAY(display)) {
return;
}
// Check if already inhibited for this window
if (g_object_get_data(G_OBJECT(window), "shortcuts-inhibit-data") != NULL) {
return;
}
ShortcutsInhibitData* inhibit_data = g_new0(ShortcutsInhibitData, 1);
struct wl_display* wl_display = gdk_wayland_display_get_wl_display(display);
if (wl_display == NULL) {
shortcuts_inhibit_data_free(inhibit_data);
return;
}
struct wl_registry* registry = wl_display_get_registry(wl_display);
if (registry == NULL) {
shortcuts_inhibit_data_free(inhibit_data);
return;
}
wl_registry_add_listener(registry, &registry_listener, inhibit_data);
wl_display_roundtrip(wl_display);
if (inhibit_data->manager == NULL) {
wl_registry_destroy(registry);
shortcuts_inhibit_data_free(inhibit_data);
return;
}
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
if (gdk_window == NULL) {
wl_registry_destroy(registry);
shortcuts_inhibit_data_free(inhibit_data);
return;
}
struct wl_surface* surface = gdk_wayland_window_get_wl_surface(gdk_window);
if (surface == NULL) {
wl_registry_destroy(registry);
shortcuts_inhibit_data_free(inhibit_data);
return;
}
GdkSeat* gdk_seat = gdk_display_get_default_seat(display);
if (gdk_seat == NULL) {
wl_registry_destroy(registry);
shortcuts_inhibit_data_free(inhibit_data);
return;
}
struct wl_seat* seat = gdk_wayland_seat_get_wl_seat(gdk_seat);
if (seat == NULL) {
wl_registry_destroy(registry);
shortcuts_inhibit_data_free(inhibit_data);
return;
}
inhibit_data->inhibitor =
zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
inhibit_data->manager, surface, seat);
if (inhibit_data->inhibitor == NULL) {
wl_registry_destroy(registry);
shortcuts_inhibit_data_free(inhibit_data);
return;
}
// Add listener to monitor active/inactive state
zwp_keyboard_shortcuts_inhibitor_v1_add_listener(
inhibit_data->inhibitor, &inhibitor_listener, window);
wl_display_roundtrip(wl_display);
wl_registry_destroy(registry);
// Associate the inhibit data with the window for cleanup on destroy
g_object_set_data_full(G_OBJECT(window), "shortcuts-inhibit-data",
inhibit_data, shortcuts_inhibit_data_free);
}
// Remove keyboard shortcuts inhibitor from a window
static void uninhibit_keyboard_shortcuts(GtkWindow* window) {
ShortcutsInhibitData* inhibit_data = static_cast<ShortcutsInhibitData*>(
g_object_get_data(G_OBJECT(window), "shortcuts-inhibit-data"));
if (inhibit_data == NULL) {
return;
}
// This will trigger shortcuts_inhibit_data_free via g_object_set_data
g_object_set_data(G_OBJECT(window), "shortcuts-inhibit-data", NULL);
}
// Focus event handlers for dynamic inhibitor management
static gboolean on_window_focus_in(GtkWidget* widget, GdkEventFocus* /*event*/, gpointer /*user_data*/) {
if (GTK_IS_WINDOW(widget)) {
inhibit_keyboard_shortcuts(GTK_WINDOW(widget));
}
return FALSE; // Continue event propagation
}
static gboolean on_window_focus_out(GtkWidget* widget, GdkEventFocus* /*event*/, gpointer /*user_data*/) {
if (GTK_IS_WINDOW(widget)) {
uninhibit_keyboard_shortcuts(GTK_WINDOW(widget));
}
return FALSE; // Continue event propagation
}
// Key for marking window as having focus handlers connected
static const char* const kFocusHandlersConnectedKey = "shortcuts-inhibit-focus-handlers-connected";
// Key for marking window as having a pending realize handler
static const char* const kRealizeHandlerConnectedKey = "shortcuts-inhibit-realize-handler-connected";
// Callback when window is realized (mapped to screen)
// Sets up focus-based inhibitor management
static void on_window_realize(GtkWidget* widget, gpointer /*user_data*/) {
if (GTK_IS_WINDOW(widget)) {
// Check if focus handlers are already connected to avoid duplicates
if (g_object_get_data(G_OBJECT(widget), kFocusHandlersConnectedKey) != NULL) {
return;
}
// Connect focus events for dynamic inhibitor management
g_signal_connect(widget, "focus-in-event",
G_CALLBACK(on_window_focus_in), NULL);
g_signal_connect(widget, "focus-out-event",
G_CALLBACK(on_window_focus_out), NULL);
// Mark as connected to prevent duplicate connections
g_object_set_data(G_OBJECT(widget), kFocusHandlersConnectedKey, GINT_TO_POINTER(1));
// If window already has focus, create inhibitor now
if (gtk_window_has_toplevel_focus(GTK_WINDOW(widget))) {
inhibit_keyboard_shortcuts(GTK_WINDOW(widget));
}
}
}
// Public API: Initialize shortcuts inhibit for a sub-window
void wayland_shortcuts_inhibit_init_for_subwindow(void* view) {
GtkWidget* widget = GTK_WIDGET(view);
GtkWidget* toplevel = gtk_widget_get_toplevel(widget);
if (toplevel != NULL && GTK_IS_WINDOW(toplevel)) {
// Check if already initialized to avoid duplicate realize handlers
if (g_object_get_data(G_OBJECT(toplevel), kFocusHandlersConnectedKey) != NULL ||
g_object_get_data(G_OBJECT(toplevel), kRealizeHandlerConnectedKey) != NULL) {
return;
}
if (gtk_widget_get_realized(toplevel)) {
// Window is already realized, set up focus handlers now
on_window_realize(toplevel, NULL);
} else {
// Mark realize handler as connected to prevent duplicate connections
// if called again before window is realized
g_object_set_data(G_OBJECT(toplevel), kRealizeHandlerConnectedKey, GINT_TO_POINTER(1));
// Wait for window to be realized
g_signal_connect(toplevel, "realize",
G_CALLBACK(on_window_realize), NULL);
}
}
}
#endif // defined(GDK_WINDOWING_WAYLAND) && defined(HAS_KEYBOARD_SHORTCUTS_INHIBIT)

View File

@@ -0,0 +1,22 @@
// Wayland keyboard shortcuts inhibit support
// This module provides functionality to inhibit system keyboard shortcuts
// on Wayland compositors, allowing remote desktop windows to capture all
// key events including Super, Alt+Tab, etc.
#ifndef WAYLAND_SHORTCUTS_INHIBIT_H_
#define WAYLAND_SHORTCUTS_INHIBIT_H_
#include <gtk/gtk.h>
#if defined(GDK_WINDOWING_WAYLAND) && defined(HAS_KEYBOARD_SHORTCUTS_INHIBIT)
// Initialize shortcuts inhibit for a sub-window created by desktop_multi_window plugin.
// This sets up focus-based inhibitor management: inhibitor is created when
// the window gains focus and destroyed when it loses focus.
//
// @param view The FlView of the sub-window
void wayland_shortcuts_inhibit_init_for_subwindow(void* view);
#endif // defined(GDK_WINDOWING_WAYLAND) && defined(HAS_KEYBOARD_SHORTCUTS_INHIBIT)
#endif // WAYLAND_SHORTCUTS_INHIBIT_H_

View File

@@ -10,7 +10,7 @@ TODO: Move this lib to a separate project.
## How it works
Terminalogies:
Terminologies:
- cliprdr: this module
- local: the endpoint which initiates a file copy events
@@ -50,7 +50,7 @@ sequenceDiagram
r ->> l: Format List Response (notified)
r ->> l: Format Data Request (requests file list)
activate l
note left of l: Retrive file list from system clipboard
note left of l: Retrieve file list from system clipboard
l ->> r: Format Data Response (containing file list)
deactivate l
note over r: Update system clipboard with received file list
@@ -84,10 +84,10 @@ and copy files to remote.
The protocol was originally designed as an extension of the Windows RDP,
so the specific message packages fits windows well.
When starting cliprdr, a thread is spawn to create a invisible window
When starting cliprdr, a thread is spawned to create an invisible window
and to subscribe to OLE clipboard events.
The window's callback (see `cliprdr_proc` in `src/windows/wf_cliprdr.c`) was
set to handle a variaty of events.
set to handle a variety of events.
Detailed implementation is shown in pictures above.
@@ -108,18 +108,18 @@ after filtering out those pointing to our FUSE directory or duplicated,
send format list directly to remote.
The cliprdr server also uses clipboard client for setting clipboard,
or retrive paths from system.
or retrieve paths from system.
#### Local File List
The local file list is a temperary list of file metadata.
The local file list is a temporary list of file metadata.
When receiving file contents PDU from peer, the server picks
out the file requested and open it for reading if necessary.
Also when receiving Format Data Request PDU from remote asking for file list,
the local file list should be rebuilt from file list retrieved from Clipboard Client.
Some caching and preloading could done on it since applications are likely to read
Some caching and preloading could be done on it since applications are likely to read
on the list sequentially.
#### FUSE server

View File

@@ -37,5 +37,8 @@ core-graphics = "0.22"
objc = "0.2"
unicode-segmentation = "1.10"
[target.'cfg(target_os = "linux")'.dependencies]
libxdo-sys = "0.11"
[build-dependencies]
pkg-config = "0.3"

View File

@@ -261,6 +261,8 @@ impl KeyboardControllable for Enigo {
} else {
if let Some(keyboard) = &mut self.custom_keyboard {
keyboard.key_sequence(sequence)
} else {
log::warn!("Enigo::key_sequence: no custom_keyboard set for Wayland!");
}
}
}
@@ -277,6 +279,7 @@ impl KeyboardControllable for Enigo {
if let Some(keyboard) = &mut self.custom_keyboard {
keyboard.key_down(key)
} else {
log::warn!("Enigo::key_down: no custom_keyboard set for Wayland!");
Ok(())
}
}
@@ -290,13 +293,24 @@ impl KeyboardControllable for Enigo {
} else {
if let Some(keyboard) = &mut self.custom_keyboard {
keyboard.key_up(key)
} else {
log::warn!("Enigo::key_up: no custom_keyboard set for Wayland!");
}
}
}
fn key_click(&mut self, key: Key) {
if self.tfc_key_click(key).is_err() {
self.key_down(key).ok();
self.key_up(key);
if self.is_x11 {
// X11: try tfc first, then fallback to key_down/key_up
if self.tfc_key_click(key).is_err() {
self.key_down(key).ok();
self.key_up(key);
}
} else {
if let Some(keyboard) = &mut self.custom_keyboard {
keyboard.key_click(key);
} else {
log::warn!("Enigo::key_click: no custom_keyboard set for Wayland!");
}
}
}
}

View File

@@ -1,50 +1,22 @@
//! XDO-based input emulation for Linux.
//!
//! This module uses libxdo-sys (patched to use dynamic loading stub) for input emulation.
//! The stub handles dynamic loading of libxdo, so we just call the functions directly.
//!
//! If libxdo is not available at runtime, all operations become no-ops.
use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
use hbb_common::libc::{c_char, c_int, c_void, useconds_t};
use std::{borrow::Cow, ffi::CString, ptr};
use hbb_common::libc::c_int;
use libxdo_sys::{self, xdo_t, CURRENTWINDOW};
use std::{borrow::Cow, ffi::CString};
const CURRENT_WINDOW: c_int = 0;
/// Default delay per keypress in microseconds.
/// This value is passed to libxdo functions and must fit in `useconds_t` (u32).
const DEFAULT_DELAY: u64 = 12000;
type Window = c_int;
type Xdo = *const c_void;
#[link(name = "xdo")]
extern "C" {
fn xdo_free(xdo: Xdo);
fn xdo_new(display: *const c_char) -> Xdo;
fn xdo_click_window(xdo: Xdo, window: Window, button: c_int) -> c_int;
fn xdo_mouse_down(xdo: Xdo, window: Window, button: c_int) -> c_int;
fn xdo_mouse_up(xdo: Xdo, window: Window, button: c_int) -> c_int;
fn xdo_move_mouse(xdo: Xdo, x: c_int, y: c_int, screen: c_int) -> c_int;
fn xdo_move_mouse_relative(xdo: Xdo, x: c_int, y: c_int) -> c_int;
fn xdo_enter_text_window(
xdo: Xdo,
window: Window,
string: *const c_char,
delay: useconds_t,
) -> c_int;
fn xdo_send_keysequence_window(
xdo: Xdo,
window: Window,
string: *const c_char,
delay: useconds_t,
) -> c_int;
fn xdo_send_keysequence_window_down(
xdo: Xdo,
window: Window,
string: *const c_char,
delay: useconds_t,
) -> c_int;
fn xdo_send_keysequence_window_up(
xdo: Xdo,
window: Window,
string: *const c_char,
delay: useconds_t,
) -> c_int;
fn xdo_get_input_state(xdo: Xdo) -> u32;
}
/// Maximum allowed delay value (u32::MAX as u64).
const MAX_DELAY: u64 = u32::MAX as u64;
fn mousebutton(button: MouseButton) -> c_int {
match button {
@@ -62,7 +34,7 @@ fn mousebutton(button: MouseButton) -> c_int {
/// The main struct for handling the event emitting
pub(super) struct EnigoXdo {
xdo: Xdo,
xdo: *mut xdo_t,
delay: u64,
}
// This is safe, we have a unique pointer.
@@ -70,37 +42,61 @@ pub(super) struct EnigoXdo {
unsafe impl Send for EnigoXdo {}
impl Default for EnigoXdo {
/// Create a new EnigoXdo instance
/// Create a new EnigoXdo instance.
///
/// If libxdo is not available, the xdo pointer will be null and all
/// input operations will be no-ops.
fn default() -> Self {
let xdo = unsafe { libxdo_sys::xdo_new(std::ptr::null()) };
if xdo.is_null() {
log::warn!("Failed to create xdo context, xdo functions will be disabled");
} else {
log::info!("xdo context created successfully");
}
Self {
xdo: unsafe { xdo_new(ptr::null()) },
xdo,
delay: DEFAULT_DELAY,
}
}
}
impl EnigoXdo {
/// Get the delay per keypress.
/// Default value is 12000.
/// This is Linux-specific.
/// Get the delay per keypress in microseconds.
///
/// Default value is 12000 (12ms). This is Linux-specific.
pub fn delay(&self) -> u64 {
self.delay
}
/// Set the delay per keypress.
/// This is Linux-specific.
/// Set the delay per keypress in microseconds.
///
/// This is Linux-specific. The value is clamped to `u32::MAX` (approximately
/// 4295 seconds) because libxdo uses `useconds_t` which is typically `u32`.
///
/// # Arguments
/// * `delay` - Delay in microseconds. Values exceeding `u32::MAX` will be clamped.
pub fn set_delay(&mut self, delay: u64) {
self.delay = delay;
self.delay = delay.min(MAX_DELAY);
if delay > MAX_DELAY {
log::warn!(
"delay value {} exceeds maximum {}, clamped",
delay,
MAX_DELAY
);
}
}
}
impl Drop for EnigoXdo {
fn drop(&mut self) {
if self.xdo.is_null() {
return;
}
unsafe {
xdo_free(self.xdo);
if !self.xdo.is_null() {
unsafe {
libxdo_sys::xdo_free(self.xdo);
}
}
}
}
impl MouseControllable for EnigoXdo {
fn as_any(&self) -> &dyn std::any::Any {
self
@@ -115,42 +111,47 @@ impl MouseControllable for EnigoXdo {
return;
}
unsafe {
xdo_move_mouse(self.xdo, x as c_int, y as c_int, 0);
libxdo_sys::xdo_move_mouse(self.xdo as *const _, x, y, 0);
}
}
fn mouse_move_relative(&mut self, x: i32, y: i32) {
if self.xdo.is_null() {
return;
}
unsafe {
xdo_move_mouse_relative(self.xdo, x as c_int, y as c_int);
libxdo_sys::xdo_move_mouse_relative(self.xdo as *const _, x, y);
}
}
fn mouse_down(&mut self, button: MouseButton) -> crate::ResultType {
if self.xdo.is_null() {
return Ok(());
}
unsafe {
xdo_mouse_down(self.xdo, CURRENT_WINDOW, mousebutton(button));
libxdo_sys::xdo_mouse_down(self.xdo as *const _, CURRENTWINDOW, mousebutton(button));
}
Ok(())
}
fn mouse_up(&mut self, button: MouseButton) {
if self.xdo.is_null() {
return;
}
unsafe {
xdo_mouse_up(self.xdo, CURRENT_WINDOW, mousebutton(button));
libxdo_sys::xdo_mouse_up(self.xdo as *const _, CURRENTWINDOW, mousebutton(button));
}
}
fn mouse_click(&mut self, button: MouseButton) {
if self.xdo.is_null() {
return;
}
unsafe {
xdo_click_window(self.xdo, CURRENT_WINDOW, mousebutton(button));
libxdo_sys::xdo_click_window(self.xdo as *const _, CURRENTWINDOW, mousebutton(button));
}
}
fn mouse_scroll_x(&mut self, length: i32) {
let button;
let mut length = length;
@@ -169,6 +170,7 @@ impl MouseControllable for EnigoXdo {
self.mouse_click(button);
}
}
fn mouse_scroll_y(&mut self, length: i32) {
let button;
let mut length = length;
@@ -188,6 +190,7 @@ impl MouseControllable for EnigoXdo {
}
}
}
fn keysequence<'a>(key: Key) -> Cow<'a, str> {
if let Key::Layout(c) = key {
return Cow::Owned(format!("U{:X}", c as u32));
@@ -284,6 +287,7 @@ fn keysequence<'a>(key: Key) -> Cow<'a, str> {
_ => "",
})
}
impl KeyboardControllable for EnigoXdo {
fn as_any(&self) -> &dyn std::any::Any {
self
@@ -314,7 +318,7 @@ impl KeyboardControllable for EnigoXdo {
let mod_alt = 1 << 3;
let mod_numlock = 1 << 4;
let mod_meta = 1 << 6;
let mask = unsafe { xdo_get_input_state(self.xdo) };
let mask = unsafe { libxdo_sys::xdo_get_input_state(self.xdo as *const _) };
match key {
Key::Shift => mask & mod_shift != 0,
Key::CapsLock => mask & mod_lock != 0,
@@ -332,56 +336,59 @@ impl KeyboardControllable for EnigoXdo {
}
if let Ok(string) = CString::new(sequence) {
unsafe {
xdo_enter_text_window(
self.xdo,
CURRENT_WINDOW,
libxdo_sys::xdo_enter_text_window(
self.xdo as *const _,
CURRENTWINDOW,
string.as_ptr(),
self.delay as useconds_t,
self.delay as libxdo_sys::useconds_t,
);
}
}
}
fn key_down(&mut self, key: Key) -> crate::ResultType {
if self.xdo.is_null() {
return Ok(());
}
let string = CString::new(&*keysequence(key))?;
unsafe {
xdo_send_keysequence_window_down(
self.xdo,
CURRENT_WINDOW,
libxdo_sys::xdo_send_keysequence_window_down(
self.xdo as *const _,
CURRENTWINDOW,
string.as_ptr(),
self.delay as useconds_t,
self.delay as libxdo_sys::useconds_t,
);
}
Ok(())
}
fn key_up(&mut self, key: Key) {
if self.xdo.is_null() {
return;
}
if let Ok(string) = CString::new(&*keysequence(key)) {
unsafe {
xdo_send_keysequence_window_up(
self.xdo,
CURRENT_WINDOW,
libxdo_sys::xdo_send_keysequence_window_up(
self.xdo as *const _,
CURRENTWINDOW,
string.as_ptr(),
self.delay as useconds_t,
self.delay as libxdo_sys::useconds_t,
);
}
}
}
fn key_click(&mut self, key: Key) {
if self.xdo.is_null() {
return;
}
if let Ok(string) = CString::new(&*keysequence(key)) {
unsafe {
xdo_send_keysequence_window(
self.xdo,
CURRENT_WINDOW,
libxdo_sys::xdo_send_keysequence_window(
self.xdo as *const _,
CURRENTWINDOW,
string.as_ptr(),
self.delay as useconds_t,
self.delay as libxdo_sys::useconds_t,
);
}
}

View File

@@ -0,0 +1,9 @@
[package]
name = "libxdo-sys"
version = "0.11.0"
edition = "2021"
publish = false
description = "Dynamic loading wrapper for libxdo-sys that doesn't require libxdo at compile/link time"
[dependencies]
hbb_common = { path = "../hbb_common" }

View File

@@ -0,0 +1,505 @@
//! Dynamic loading wrapper for libxdo.
//!
//! Provides the same API as libxdo-sys but loads libxdo at runtime,
//! allowing the program to run on systems without libxdo installed
//! (e.g., Wayland-only environments).
use hbb_common::{
libc::{c_char, c_int, c_uint},
libloading::{Library, Symbol},
log,
};
use std::sync::OnceLock;
pub use hbb_common::x11::xlib::{Display, Screen, Window};
#[repr(C)]
pub struct xdo_t {
_private: [u8; 0],
}
#[repr(C)]
pub struct charcodemap_t {
_private: [u8; 0],
}
#[repr(C)]
pub struct xdo_search_t {
_private: [u8; 0],
}
pub type useconds_t = c_uint;
pub const CURRENTWINDOW: Window = 0;
type FnXdoNew = unsafe extern "C" fn(*const c_char) -> *mut xdo_t;
type FnXdoNewWithOpenedDisplay =
unsafe extern "C" fn(*mut Display, *const c_char, c_int) -> *mut xdo_t;
type FnXdoFree = unsafe extern "C" fn(*mut xdo_t);
type FnXdoSendKeysequenceWindow =
unsafe extern "C" fn(*const xdo_t, Window, *const c_char, useconds_t) -> c_int;
type FnXdoSendKeysequenceWindowDown =
unsafe extern "C" fn(*const xdo_t, Window, *const c_char, useconds_t) -> c_int;
type FnXdoSendKeysequenceWindowUp =
unsafe extern "C" fn(*const xdo_t, Window, *const c_char, useconds_t) -> c_int;
type FnXdoEnterTextWindow =
unsafe extern "C" fn(*const xdo_t, Window, *const c_char, useconds_t) -> c_int;
type FnXdoClickWindow = unsafe extern "C" fn(*const xdo_t, Window, c_int) -> c_int;
type FnXdoMouseDown = unsafe extern "C" fn(*const xdo_t, Window, c_int) -> c_int;
type FnXdoMouseUp = unsafe extern "C" fn(*const xdo_t, Window, c_int) -> c_int;
type FnXdoMoveMouse = unsafe extern "C" fn(*const xdo_t, c_int, c_int, c_int) -> c_int;
type FnXdoMoveMouseRelative = unsafe extern "C" fn(*const xdo_t, c_int, c_int) -> c_int;
type FnXdoMoveMouseRelativeToWindow =
unsafe extern "C" fn(*const xdo_t, Window, c_int, c_int) -> c_int;
type FnXdoGetMouseLocation =
unsafe extern "C" fn(*const xdo_t, *mut c_int, *mut c_int, *mut c_int) -> c_int;
type FnXdoGetMouseLocation2 =
unsafe extern "C" fn(*const xdo_t, *mut c_int, *mut c_int, *mut c_int, *mut Window) -> c_int;
type FnXdoGetActiveWindow = unsafe extern "C" fn(*const xdo_t, *mut Window) -> c_int;
type FnXdoGetFocusedWindow = unsafe extern "C" fn(*const xdo_t, *mut Window) -> c_int;
type FnXdoGetFocusedWindowSane = unsafe extern "C" fn(*const xdo_t, *mut Window) -> c_int;
type FnXdoGetWindowLocation =
unsafe extern "C" fn(*const xdo_t, Window, *mut c_int, *mut c_int, *mut *mut Screen) -> c_int;
type FnXdoGetWindowSize =
unsafe extern "C" fn(*const xdo_t, Window, *mut c_uint, *mut c_uint) -> c_int;
type FnXdoGetInputState = unsafe extern "C" fn(*const xdo_t) -> c_uint;
type FnXdoActivateWindow = unsafe extern "C" fn(*const xdo_t, Window) -> c_int;
type FnXdoWaitForMouseMoveFrom = unsafe extern "C" fn(*const xdo_t, c_int, c_int) -> c_int;
type FnXdoWaitForMouseMoveTo = unsafe extern "C" fn(*const xdo_t, c_int, c_int) -> c_int;
type FnXdoSetWindowClass =
unsafe extern "C" fn(*const xdo_t, Window, *const c_char, *const c_char) -> c_int;
type FnXdoSearchWindows =
unsafe extern "C" fn(*const xdo_t, *const xdo_search_t, *mut *mut Window, *mut c_uint) -> c_int;
struct XdoLib {
_lib: Library,
xdo_new: FnXdoNew,
xdo_new_with_opened_display: Option<FnXdoNewWithOpenedDisplay>,
xdo_free: FnXdoFree,
xdo_send_keysequence_window: FnXdoSendKeysequenceWindow,
xdo_send_keysequence_window_down: Option<FnXdoSendKeysequenceWindowDown>,
xdo_send_keysequence_window_up: Option<FnXdoSendKeysequenceWindowUp>,
xdo_enter_text_window: Option<FnXdoEnterTextWindow>,
xdo_click_window: Option<FnXdoClickWindow>,
xdo_mouse_down: Option<FnXdoMouseDown>,
xdo_mouse_up: Option<FnXdoMouseUp>,
xdo_move_mouse: Option<FnXdoMoveMouse>,
xdo_move_mouse_relative: Option<FnXdoMoveMouseRelative>,
xdo_move_mouse_relative_to_window: Option<FnXdoMoveMouseRelativeToWindow>,
xdo_get_mouse_location: Option<FnXdoGetMouseLocation>,
xdo_get_mouse_location2: Option<FnXdoGetMouseLocation2>,
xdo_get_active_window: Option<FnXdoGetActiveWindow>,
xdo_get_focused_window: Option<FnXdoGetFocusedWindow>,
xdo_get_focused_window_sane: Option<FnXdoGetFocusedWindowSane>,
xdo_get_window_location: Option<FnXdoGetWindowLocation>,
xdo_get_window_size: Option<FnXdoGetWindowSize>,
xdo_get_input_state: Option<FnXdoGetInputState>,
xdo_activate_window: Option<FnXdoActivateWindow>,
xdo_wait_for_mouse_move_from: Option<FnXdoWaitForMouseMoveFrom>,
xdo_wait_for_mouse_move_to: Option<FnXdoWaitForMouseMoveTo>,
xdo_set_window_class: Option<FnXdoSetWindowClass>,
xdo_search_windows: Option<FnXdoSearchWindows>,
}
impl XdoLib {
fn load() -> Option<Self> {
// https://github.com/rustdesk/rustdesk/issues/13711
const LIB_NAMES: [&str; 3] = ["libxdo.so.4", "libxdo.so.3", "libxdo.so"];
unsafe {
let (lib, lib_name) = LIB_NAMES
.iter()
.find_map(|name| Library::new(name).ok().map(|lib| (lib, *name)))?;
log::info!("libxdo-sys Loaded {}", lib_name);
let xdo_new: FnXdoNew = *lib.get(b"xdo_new").ok()?;
let xdo_free: FnXdoFree = *lib.get(b"xdo_free").ok()?;
let xdo_send_keysequence_window: FnXdoSendKeysequenceWindow =
*lib.get(b"xdo_send_keysequence_window").ok()?;
let xdo_new_with_opened_display = lib
.get(b"xdo_new_with_opened_display")
.ok()
.map(|s: Symbol<FnXdoNewWithOpenedDisplay>| *s);
let xdo_send_keysequence_window_down = lib
.get(b"xdo_send_keysequence_window_down")
.ok()
.map(|s: Symbol<FnXdoSendKeysequenceWindowDown>| *s);
let xdo_send_keysequence_window_up = lib
.get(b"xdo_send_keysequence_window_up")
.ok()
.map(|s: Symbol<FnXdoSendKeysequenceWindowUp>| *s);
let xdo_enter_text_window = lib
.get(b"xdo_enter_text_window")
.ok()
.map(|s: Symbol<FnXdoEnterTextWindow>| *s);
let xdo_click_window = lib
.get(b"xdo_click_window")
.ok()
.map(|s: Symbol<FnXdoClickWindow>| *s);
let xdo_mouse_down = lib
.get(b"xdo_mouse_down")
.ok()
.map(|s: Symbol<FnXdoMouseDown>| *s);
let xdo_mouse_up = lib
.get(b"xdo_mouse_up")
.ok()
.map(|s: Symbol<FnXdoMouseUp>| *s);
let xdo_move_mouse = lib
.get(b"xdo_move_mouse")
.ok()
.map(|s: Symbol<FnXdoMoveMouse>| *s);
let xdo_move_mouse_relative = lib
.get(b"xdo_move_mouse_relative")
.ok()
.map(|s: Symbol<FnXdoMoveMouseRelative>| *s);
let xdo_move_mouse_relative_to_window = lib
.get(b"xdo_move_mouse_relative_to_window")
.ok()
.map(|s: Symbol<FnXdoMoveMouseRelativeToWindow>| *s);
let xdo_get_mouse_location = lib
.get(b"xdo_get_mouse_location")
.ok()
.map(|s: Symbol<FnXdoGetMouseLocation>| *s);
let xdo_get_mouse_location2 = lib
.get(b"xdo_get_mouse_location2")
.ok()
.map(|s: Symbol<FnXdoGetMouseLocation2>| *s);
let xdo_get_active_window = lib
.get(b"xdo_get_active_window")
.ok()
.map(|s: Symbol<FnXdoGetActiveWindow>| *s);
let xdo_get_focused_window = lib
.get(b"xdo_get_focused_window")
.ok()
.map(|s: Symbol<FnXdoGetFocusedWindow>| *s);
let xdo_get_focused_window_sane = lib
.get(b"xdo_get_focused_window_sane")
.ok()
.map(|s: Symbol<FnXdoGetFocusedWindowSane>| *s);
let xdo_get_window_location = lib
.get(b"xdo_get_window_location")
.ok()
.map(|s: Symbol<FnXdoGetWindowLocation>| *s);
let xdo_get_window_size = lib
.get(b"xdo_get_window_size")
.ok()
.map(|s: Symbol<FnXdoGetWindowSize>| *s);
let xdo_get_input_state = lib
.get(b"xdo_get_input_state")
.ok()
.map(|s: Symbol<FnXdoGetInputState>| *s);
let xdo_activate_window = lib
.get(b"xdo_activate_window")
.ok()
.map(|s: Symbol<FnXdoActivateWindow>| *s);
let xdo_wait_for_mouse_move_from = lib
.get(b"xdo_wait_for_mouse_move_from")
.ok()
.map(|s: Symbol<FnXdoWaitForMouseMoveFrom>| *s);
let xdo_wait_for_mouse_move_to = lib
.get(b"xdo_wait_for_mouse_move_to")
.ok()
.map(|s: Symbol<FnXdoWaitForMouseMoveTo>| *s);
let xdo_set_window_class = lib
.get(b"xdo_set_window_class")
.ok()
.map(|s: Symbol<FnXdoSetWindowClass>| *s);
let xdo_search_windows = lib
.get(b"xdo_search_windows")
.ok()
.map(|s: Symbol<FnXdoSearchWindows>| *s);
Some(Self {
_lib: lib,
xdo_new,
xdo_new_with_opened_display,
xdo_free,
xdo_send_keysequence_window,
xdo_send_keysequence_window_down,
xdo_send_keysequence_window_up,
xdo_enter_text_window,
xdo_click_window,
xdo_mouse_down,
xdo_mouse_up,
xdo_move_mouse,
xdo_move_mouse_relative,
xdo_move_mouse_relative_to_window,
xdo_get_mouse_location,
xdo_get_mouse_location2,
xdo_get_active_window,
xdo_get_focused_window,
xdo_get_focused_window_sane,
xdo_get_window_location,
xdo_get_window_size,
xdo_get_input_state,
xdo_activate_window,
xdo_wait_for_mouse_move_from,
xdo_wait_for_mouse_move_to,
xdo_set_window_class,
xdo_search_windows,
})
}
}
}
static XDO_LIB: OnceLock<Option<XdoLib>> = OnceLock::new();
fn get_lib() -> Option<&'static XdoLib> {
XDO_LIB
.get_or_init(|| {
let lib = XdoLib::load();
if lib.is_none() {
log::info!("libxdo-sys libxdo not found, xdo functions will be disabled");
}
lib
})
.as_ref()
}
pub unsafe extern "C" fn xdo_new(display: *const c_char) -> *mut xdo_t {
get_lib().map_or(std::ptr::null_mut(), |lib| (lib.xdo_new)(display))
}
pub unsafe extern "C" fn xdo_new_with_opened_display(
xdpy: *mut Display,
display: *const c_char,
close_display_when_freed: c_int,
) -> *mut xdo_t {
get_lib()
.and_then(|lib| lib.xdo_new_with_opened_display)
.map_or(std::ptr::null_mut(), |f| {
f(xdpy, display, close_display_when_freed)
})
}
pub unsafe extern "C" fn xdo_free(xdo: *mut xdo_t) {
if xdo.is_null() {
return;
}
if let Some(lib) = get_lib() {
(lib.xdo_free)(xdo);
}
}
pub unsafe extern "C" fn xdo_send_keysequence_window(
xdo: *const xdo_t,
window: Window,
keysequence: *const c_char,
delay: useconds_t,
) -> c_int {
get_lib().map_or(1, |lib| {
(lib.xdo_send_keysequence_window)(xdo, window, keysequence, delay)
})
}
pub unsafe extern "C" fn xdo_send_keysequence_window_down(
xdo: *const xdo_t,
window: Window,
keysequence: *const c_char,
delay: useconds_t,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_send_keysequence_window_down)
.map_or(1, |f| f(xdo, window, keysequence, delay))
}
pub unsafe extern "C" fn xdo_send_keysequence_window_up(
xdo: *const xdo_t,
window: Window,
keysequence: *const c_char,
delay: useconds_t,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_send_keysequence_window_up)
.map_or(1, |f| f(xdo, window, keysequence, delay))
}
pub unsafe extern "C" fn xdo_enter_text_window(
xdo: *const xdo_t,
window: Window,
string: *const c_char,
delay: useconds_t,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_enter_text_window)
.map_or(1, |f| f(xdo, window, string, delay))
}
pub unsafe extern "C" fn xdo_click_window(
xdo: *const xdo_t,
window: Window,
button: c_int,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_click_window)
.map_or(1, |f| f(xdo, window, button))
}
pub unsafe extern "C" fn xdo_mouse_down(xdo: *const xdo_t, window: Window, button: c_int) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_mouse_down)
.map_or(1, |f| f(xdo, window, button))
}
pub unsafe extern "C" fn xdo_mouse_up(xdo: *const xdo_t, window: Window, button: c_int) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_mouse_up)
.map_or(1, |f| f(xdo, window, button))
}
pub unsafe extern "C" fn xdo_move_mouse(
xdo: *const xdo_t,
x: c_int,
y: c_int,
screen: c_int,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_move_mouse)
.map_or(1, |f| f(xdo, x, y, screen))
}
pub unsafe extern "C" fn xdo_move_mouse_relative(xdo: *const xdo_t, x: c_int, y: c_int) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_move_mouse_relative)
.map_or(1, |f| f(xdo, x, y))
}
pub unsafe extern "C" fn xdo_move_mouse_relative_to_window(
xdo: *const xdo_t,
window: Window,
x: c_int,
y: c_int,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_move_mouse_relative_to_window)
.map_or(1, |f| f(xdo, window, x, y))
}
pub unsafe extern "C" fn xdo_get_mouse_location(
xdo: *const xdo_t,
x: *mut c_int,
y: *mut c_int,
screen_num: *mut c_int,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_get_mouse_location)
.map_or(1, |f| f(xdo, x, y, screen_num))
}
pub unsafe extern "C" fn xdo_get_mouse_location2(
xdo: *const xdo_t,
x: *mut c_int,
y: *mut c_int,
screen_num: *mut c_int,
window: *mut Window,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_get_mouse_location2)
.map_or(1, |f| f(xdo, x, y, screen_num, window))
}
pub unsafe extern "C" fn xdo_get_active_window(
xdo: *const xdo_t,
window_ret: *mut Window,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_get_active_window)
.map_or(1, |f| f(xdo, window_ret))
}
pub unsafe extern "C" fn xdo_get_focused_window(
xdo: *const xdo_t,
window_ret: *mut Window,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_get_focused_window)
.map_or(1, |f| f(xdo, window_ret))
}
pub unsafe extern "C" fn xdo_get_focused_window_sane(
xdo: *const xdo_t,
window_ret: *mut Window,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_get_focused_window_sane)
.map_or(1, |f| f(xdo, window_ret))
}
pub unsafe extern "C" fn xdo_get_window_location(
xdo: *const xdo_t,
window: Window,
x: *mut c_int,
y: *mut c_int,
screen_ret: *mut *mut Screen,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_get_window_location)
.map_or(1, |f| f(xdo, window, x, y, screen_ret))
}
pub unsafe extern "C" fn xdo_get_window_size(
xdo: *const xdo_t,
window: Window,
width: *mut c_uint,
height: *mut c_uint,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_get_window_size)
.map_or(1, |f| f(xdo, window, width, height))
}
pub unsafe extern "C" fn xdo_get_input_state(xdo: *const xdo_t) -> c_uint {
get_lib()
.and_then(|lib| lib.xdo_get_input_state)
.map_or(0, |f| f(xdo))
}
pub unsafe extern "C" fn xdo_activate_window(xdo: *const xdo_t, wid: Window) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_activate_window)
.map_or(1, |f| f(xdo, wid))
}
pub unsafe extern "C" fn xdo_wait_for_mouse_move_from(
xdo: *const xdo_t,
origin_x: c_int,
origin_y: c_int,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_wait_for_mouse_move_from)
.map_or(1, |f| f(xdo, origin_x, origin_y))
}
pub unsafe extern "C" fn xdo_wait_for_mouse_move_to(
xdo: *const xdo_t,
dest_x: c_int,
dest_y: c_int,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_wait_for_mouse_move_to)
.map_or(1, |f| f(xdo, dest_x, dest_y))
}
pub unsafe extern "C" fn xdo_set_window_class(
xdo: *const xdo_t,
wid: Window,
name: *const c_char,
class: *const c_char,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_set_window_class)
.map_or(1, |f| f(xdo, wid, name, class))
}
pub unsafe extern "C" fn xdo_search_windows(
xdo: *const xdo_t,
search: *const xdo_search_t,
windowlist_ret: *mut *mut Window,
nwindows_ret: *mut c_uint,
) -> c_int {
get_lib()
.and_then(|lib| lib.xdo_search_windows)
.map_or(1, |f| f(xdo, search, windowlist_ret, nwindows_ret))
}

View File

@@ -187,7 +187,10 @@ fn main() {
i += 1;
}
let click_setup = args.is_empty() && arg_exe.to_lowercase().ends_with("install.exe");
let quick_support = args.is_empty() && arg_exe.to_lowercase().ends_with("qs.exe");
#[cfg(windows)]
let quick_support = args.is_empty() && win::is_quick_support_exe(&arg_exe);
#[cfg(not(windows))]
let quick_support = false;
let mut ui = false;
let reader = BinaryReader::default();
@@ -234,4 +237,12 @@ mod win {
.output();
let _allow_err = std::fs::copy(src, &format!("{}\\{}", dir.to_string_lossy(), tgt));
}
/// Check if the executable is a Quick Support version.
/// Note: This function must be kept in sync with `src/core_main.rs`.
#[inline]
pub(super) fn is_quick_support_exe(exe: &str) -> bool {
let exe = exe.to_lowercase();
exe.contains("-qs-") || exe.contains("-qs.exe") || exe.contains("_qs.exe")
}
}

View File

@@ -98,7 +98,7 @@ unsafe fn check_x11_shm_available(c: *mut xcb_connection_t) -> Result<(), Error>
let mut e: *mut xcb_generic_error_t = std::ptr::null_mut();
let reply = xcb_shm_query_version_reply(c, cookie, &mut e as _);
if reply.is_null() {
// TODO: Should seperate SHM disabled from SHM not supported?
// TODO: Should separate SHM disabled from SHM not supported?
return Err(Error::UnsupportedExtension);
} else {
// https://github.com/FFmpeg/FFmpeg/blob/6229e4ac425b4566446edefb67d5c225eb397b58/libavdevice/xcbgrab.c#L229

View File

@@ -205,9 +205,13 @@ def sign_files(dir_path, only_ext=None):
if not only_ext[i].startswith("."):
only_ext[i] = "." + only_ext[i]
for root, dirs, files in os.walk(dir_path):
is_signed_dir = "RustDeskPrinterDriver" in root or "usbmmidd_v2" in root
for file in files:
file_path = os.path.join(root, file)
_, ext = os.path.splitext(file_path)
# only sign the exe files in signed dirs
if is_signed_dir and ext not in [".exe"]:
continue
if only_ext and ext not in only_ext:
continue
if ext in SIGN_EXTENSIONS:

View File

@@ -336,7 +336,9 @@ def gen_custom_ARPSYSTEMCOMPONENT_True(args, dist_dir):
f'{indent}<RegistryValue Type="integer" Name="Language" Value="[ProductLanguage]" />\n'
)
estimated_size = get_folder_size(dist_dir)
# EstimatedSize in uninstall registry must be in KB.
estimated_size_bytes = get_folder_size(dist_dir)
estimated_size = max(1, (estimated_size_bytes + 1023) // 1024)
lines_new.append(
f'{indent}<RegistryValue Type="integer" Name="EstimatedSize" Value="{estimated_size}" />\n'
)

View File

@@ -5,8 +5,8 @@ Summary: RPM package
License: GPL-3.0
URL: https://rustdesk.com
Vendor: rustdesk <info@rustdesk.com>
Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils libXtst6 libva2 pam gstreamer-plugins-base gstreamer-plugin-pipewire
Recommends: libayatana-appindicator3-1
Requires: gtk3 libxcb1 libXfixes3 alsa-utils libXtst6 libva2 pam gstreamer-plugins-base gstreamer-plugin-pipewire
Recommends: libayatana-appindicator3-1 xdotool
Provides: libdesktop_drop_plugin.so()(64bit), libdesktop_multi_window_plugin.so()(64bit), libfile_selector_linux_plugin.so()(64bit), libflutter_custom_cursor_plugin.so()(64bit), libflutter_linux_gtk.so()(64bit), libscreen_retriever_plugin.so()(64bit), libtray_manager_plugin.so()(64bit), liburl_launcher_linux_plugin.so()(64bit), libwindow_manager_plugin.so()(64bit), libwindow_size_plugin.so()(64bit), libtexture_rgba_renderer_plugin.so()(64bit)
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/

View File

@@ -5,8 +5,8 @@ Summary: RPM package
License: GPL-3.0
URL: https://rustdesk.com
Vendor: rustdesk <info@rustdesk.com>
Requires: gtk3 libxcb libxdo libXfixes alsa-lib libva pam gstreamer1-plugins-base
Recommends: libayatana-appindicator-gtk3
Requires: gtk3 libxcb libXfixes alsa-lib libva pam gstreamer1-plugins-base
Recommends: libayatana-appindicator-gtk3 libxdo
Provides: libdesktop_drop_plugin.so()(64bit), libdesktop_multi_window_plugin.so()(64bit), libfile_selector_linux_plugin.so()(64bit), libflutter_custom_cursor_plugin.so()(64bit), libflutter_linux_gtk.so()(64bit), libscreen_retriever_plugin.so()(64bit), libtray_manager_plugin.so()(64bit), liburl_launcher_linux_plugin.so()(64bit), libwindow_manager_plugin.so()(64bit), libwindow_size_plugin.so()(64bit), libtexture_rgba_renderer_plugin.so()(64bit)
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/

View File

@@ -3,8 +3,8 @@ Version: 1.1.9
Release: 0
Summary: RPM package
License: GPL-3.0
Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils libXtst6 libva2 pam gstreamer-plugins-base gstreamer-plugin-pipewire
Recommends: libayatana-appindicator3-1
Requires: gtk3 libxcb1 libXfixes3 alsa-utils libXtst6 libva2 pam gstreamer-plugins-base gstreamer-plugin-pipewire
Recommends: libayatana-appindicator3-1 xdotool
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/

View File

@@ -5,8 +5,8 @@ Summary: RPM package
License: GPL-3.0
URL: https://rustdesk.com
Vendor: rustdesk <info@rustdesk.com>
Requires: gtk3 libxcb libxdo libXfixes alsa-lib libva2 pam gstreamer1-plugins-base
Recommends: libayatana-appindicator-gtk3
Requires: gtk3 libxcb libXfixes alsa-lib libva2 pam gstreamer1-plugins-base
Recommends: libayatana-appindicator-gtk3 libxdo
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/

View File

@@ -2630,10 +2630,12 @@ impl LoginConfigHandler {
display_name =
serde_json::from_str::<serde_json::Value>(&LocalConfig::get_option("user_info"))
.map(|x| {
x.get("name")
.map(|x| x.as_str().unwrap_or_default())
x.get("display_name")
.and_then(|x| x.as_str())
.filter(|x| !x.is_empty())
.or_else(|| x.get("name").and_then(|x| x.as_str()))
.map(|x| x.to_owned())
.unwrap_or_default()
.to_owned()
})
.unwrap_or_default();
}

View File

@@ -197,7 +197,7 @@ pub fn check_clipboard_cm() -> ResultType<MultiClipboards> {
#[cfg(not(target_os = "android"))]
fn update_clipboard_(multi_clipboards: Vec<Clipboard>, side: ClipboardSide) {
let to_update_data = proto::from_multi_clipbards(multi_clipboards);
let to_update_data = proto::from_multi_clipboards(multi_clipboards);
if to_update_data.is_empty() {
return;
}
@@ -432,7 +432,7 @@ impl ClipboardContext {
#[cfg(target_os = "macos")]
let is_kde_x11 = false;
let clear_holder_text = if is_kde_x11 {
"RustDesk placeholder to clear the file clipbard"
"RustDesk placeholder to clear the file clipboard"
} else {
""
}
@@ -672,7 +672,7 @@ mod proto {
}
#[cfg(not(target_os = "android"))]
pub fn from_multi_clipbards(multi_clipboards: Vec<Clipboard>) -> Vec<ClipboardData> {
pub fn from_multi_clipboards(multi_clipboards: Vec<Clipboard>) -> Vec<ClipboardData> {
multi_clipboards
.into_iter()
.filter_map(from_clipboard)
@@ -814,7 +814,7 @@ pub mod clipboard_listener {
subscribers: listener_lock.subscribers.clone(),
};
let (tx_start_res, rx_start_res) = channel();
let h = start_clipbard_master_thread(handler, tx_start_res);
let h = start_clipboard_master_thread(handler, tx_start_res);
let shutdown = match rx_start_res.recv() {
Ok((Some(s), _)) => s,
Ok((None, err)) => {
@@ -854,7 +854,7 @@ pub mod clipboard_listener {
log::info!("Clipboard listener unsubscribed: {}", name);
}
fn start_clipbard_master_thread(
fn start_clipboard_master_thread(
handler: impl ClipboardHandler + Send + 'static,
tx_start_res: Sender<(Option<Shutdown>, String)>,
) -> JoinHandle<()> {

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

@@ -140,7 +140,7 @@ pub fn core_main() -> Option<Vec<String>> {
{
_is_quick_support |= !crate::platform::is_installed()
&& args.is_empty()
&& (arg_exe.to_lowercase().contains("-qs-")
&& (is_quick_support_exe(&arg_exe)
|| config::LocalConfig::get_option("pre-elevate-service") == "Y"
|| (!click_setup && crate::platform::is_elevated(None).unwrap_or(false)));
crate::portable_service::client::set_quick_support(_is_quick_support);
@@ -829,3 +829,12 @@ fn is_root() -> bool {
#[allow(unreachable_code)]
crate::platform::is_root()
}
/// Check if the executable is a Quick Support version.
/// Note: This function must be kept in sync with `libs/portable/src/main.rs`.
#[cfg(windows)]
#[inline]
fn is_quick_support_exe(exe: &str) -> bool {
let exe = exe.to_lowercase();
exe.contains("-qs-") || exe.contains("-qs.exe") || exe.contains("_qs.exe")
}

View File

@@ -2759,6 +2759,11 @@ pub fn main_get_common(key: String) -> String {
None => "",
}
.to_string();
} else if key == "has-gnome-shortcuts-inhibitor-permission" {
#[cfg(target_os = "linux")]
return crate::platform::linux::has_gnome_shortcuts_inhibitor_permission().to_string();
#[cfg(not(target_os = "linux"))]
return false.to_string();
} else {
if key.starts_with("download-data-") {
let id = key.replace("download-data-", "");
@@ -2920,6 +2925,29 @@ pub fn main_set_common(_key: String, _value: String) {
} else if _key == "cancel-downloader" {
crate::hbbs_http::downloader::cancel(&_value);
}
#[cfg(target_os = "linux")]
if _key == "clear-gnome-shortcuts-inhibitor-permission" {
std::thread::spawn(move || {
let (success, msg) =
match crate::platform::linux::clear_gnome_shortcuts_inhibitor_permission() {
Ok(_) => (true, "".to_owned()),
Err(e) => (false, e.to_string()),
};
let data = HashMap::from([
(
"name",
serde_json::json!("clear-gnome-shortcuts-inhibitor-permission-res"),
),
("success", serde_json::json!(success)),
("msg", serde_json::json!(msg)),
]);
let _res = flutter::push_global_event(
flutter::APP_TYPE_MAIN,
serde_json::ser::to_string(&data).unwrap_or("".to_owned()),
);
});
}
}
pub fn session_get_common_sync(

View File

@@ -80,6 +80,8 @@ pub enum UserStatus {
pub struct UserPayload {
pub name: String,
#[serde(default)]
pub display_name: Option<String>,
#[serde(default)]
pub email: Option<String>,
#[serde(default)]
pub note: Option<String>,
@@ -268,7 +270,12 @@ impl OidcSession {
);
LocalConfig::set_option(
"user_info".to_owned(),
serde_json::json!({ "name": auth_body.user.name, "status": auth_body.user.status }).to_string(),
serde_json::json!({
"name": auth_body.user.name,
"display_name": auth_body.user.display_name,
"status": auth_body.user.status
})
.to_string(),
);
}
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "عرض RustDesk"),
("This PC", "هذا الحاسب"),
("or", "او"),
("Continue with", "متابعة مع"),
("Elevate", "ارتقاء"),
("Zoom cursor", "تكبير المؤشر"),
("Accept sessions via password", "قبول الجلسات عبر كلمة المرور"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "متابعة مع {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Паказаць RustDesk"),
("This PC", "Гэты кампутар"),
("or", "або"),
("Continue with", "Працягнуць з"),
("Elevate", "Павысіць"),
("Zoom cursor", "Павялічэнне курсора"),
("Accept sessions via password", "Прымаць сеансы па паролю"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Працягнуць з {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Покажи RustDesk"),
("This PC", "Този компютър"),
("or", "или"),
("Continue with", "Продължи с"),
("Elevate", "Повишаване"),
("Zoom cursor", "Уголемяване курсор"),
("Accept sessions via password", "Приемане сесии чрез парола"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Продължи с {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Mostra el RustDesk"),
("This PC", "Aquest equip"),
("or", "o"),
("Continue with", "Continua amb"),
("Elevate", "Permisos ampliats"),
("Zoom cursor", "Escala del ratolí"),
("Accept sessions via password", "Accepta les sessions mitjançant una contrasenya"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Continua amb {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "显示 RustDesk"),
("This PC", "此电脑"),
("or", ""),
("Continue with", "使用"),
("Elevate", "提权"),
("Zoom cursor", "缩放光标"),
("Accept sessions via password", "只允许密码访问"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "使用 {} 登录"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Zobrazit RustDesk"),
("This PC", "Tento počítač"),
("or", "nebo"),
("Continue with", "Pokračovat s"),
("Elevate", "Zvýšit"),
("Zoom cursor", "Kurzor přiblížení"),
("Accept sessions via password", "Přijímat relace pomocí hesla"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Pokračovat s {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Vis RustDesk"),
("This PC", "Denne PC"),
("or", "eller"),
("Continue with", "Fortsæt med"),
("Elevate", "Elevér"),
("Zoom cursor", "Zoom markør"),
("Accept sessions via password", "Acceptér sessioner via adgangskode"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Fortsæt med {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "RustDesk anzeigen"),
("This PC", "Dieser PC"),
("or", "oder"),
("Continue with", "Fortfahren mit"),
("Elevate", "Zugriff gewähren"),
("Zoom cursor", "Cursor vergrößern"),
("Accept sessions via password", "Sitzung mit Passwort bestätigen"),
@@ -562,8 +561,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("id_input_tip", "Sie können eine ID, eine direkte IP oder eine Domäne mit einem Port (<domain>:<port>) eingeben.\nWenn Sie auf ein Gerät auf einem anderen Server zugreifen wollen, fügen Sie bitte die Serveradresse (<id>@<server_address>?key=<key_value>) hinzu, zum Beispiel\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nWenn Sie auf ein Gerät auf einem öffentlichen Server zugreifen wollen, geben Sie bitte \"<id>@public\" ein. Der Schlüssel wird für öffentliche Server nicht benötigt.\n\nWenn Sie bei der ersten Verbindung die Verwendung einer Relay-Verbindung erzwingen wollen, fügen Sie \"/r\" am Ende der ID hinzu, zum Beispiel \"9123456234/r\"."),
("privacy_mode_impl_mag_tip", "Modus 1"),
("privacy_mode_impl_virtual_display_tip", "Modus 2"),
("Enter privacy mode", "Datenschutzmodus aktivieren"),
("Exit privacy mode", "Datenschutzmodus beenden"),
("Enter privacy mode", "Datenschutzmodus aktiviert"),
("Exit privacy mode", "Datenschutzmodus beendet"),
("idd_not_support_under_win10_2004_tip", "Indirekter Grafiktreiber wird nicht unterstützt. Windows 10, Version 2004 oder neuer ist erforderlich."),
("input_source_1_tip", "Eingangsquelle 1"),
("input_source_2_tip", "Eingangsquelle 2"),
@@ -730,11 +729,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("input note here", "Hier eine Notiz eingeben"),
("note-at-conn-end-tip", "Am Ende der Verbindung um eine Notiz bitten."),
("Show terminal extra keys", "Zusätzliche Tasten des Terminals anzeigen"),
("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", ""),
("Relative mouse mode", "Relativer Mausmodus"),
("rel-mouse-not-supported-peer-tip", "Der relative Mausmodus wird von der verbundenen Gegenstelle nicht unterstützt."),
("rel-mouse-not-ready-tip", "Der relative Mausmodus ist noch nicht bereit. Bitte versuchen Sie es erneut."),
("rel-mouse-lock-failed-tip", "Cursor konnte nicht gesperrt werden. Der relative Mausmodus wurde deaktiviert."),
("rel-mouse-exit-{}-tip", "Drücken Sie {} zum Beenden."),
("rel-mouse-permission-lost-tip", "Die Tastaturberechtigung wurde widerrufen. Der relative Mausmodus wurde deaktiviert."),
("Changelog", "Änderungsprotokoll"),
("keep-awake-during-outgoing-sessions-label", "Bildschirm während ausgehender Sitzungen aktiv halten"),
("keep-awake-during-incoming-sessions-label", "Bildschirm während eingehender Sitzungen aktiv halten"),
("Continue with {}", "Fortfahren mit {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Εμφάνιση RustDesk"),
("This PC", "Αυτός ο υπολογιστής"),
("or", "ή"),
("Continue with", "Συνέχεια με"),
("Elevate", "Ανύψωση"),
("Zoom cursor", "ρσορας μεγέθυνσης"),
("Accept sessions via password", "Αποδοχή συνεδριών με κωδικό πρόσβασης"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Συνέχεια με {}"),
].iter().cloned().collect();
}

View File

@@ -220,7 +220,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("default_proxy_tip", "Default protocol and port are Socks5 and 1080"),
("no_audio_input_device_tip", "No audio input device found."),
("clear_Wayland_screen_selection_tip", "After clearing the screen selection, you can reselect the screen to share."),
("confirm_clear_Wayland_screen_selection_tip", "Are you sure to clear the Wayland screen selection?"),
("confirm_clear_Wayland_screen_selection_tip", "Are you sure you want to clear the Wayland screen selection?"),
("android_new_voice_call_tip", "A new voice call request was received. If you accept, the audio will switch to voice communication."),
("texture_render_tip", "Use texture rendering to make the pictures smoother. You could try disabling this option if you encounter rendering issues."),
("floating_window_tip", "It helps to keep RustDesk background service"),
@@ -267,5 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("rel-mouse-lock-failed-tip", "Failed to lock cursor. Relative Mouse Mode has been disabled."),
("rel-mouse-exit-{}-tip", "Press {} to exit."),
("rel-mouse-permission-lost-tip", "Keyboard permission was revoked. Relative Mouse Mode has been disabled."),
("keep-awake-during-outgoing-sessions-label", "Keep screen awake during outgoing sessions"),
("keep-awake-during-incoming-sessions-label", "Keep screen awake during incoming sessions"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", ""),
("This PC", ""),
("or", ""),
("Continue with", ""),
("Elevate", ""),
("Zoom cursor", ""),
("Accept sessions via password", ""),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", ""),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Mostrar RustDesk"),
("This PC", "Este PC"),
("or", "o"),
("Continue with", "Continuar con"),
("Elevate", "Elevar privilegios"),
("Zoom cursor", "Ampliar cursor"),
("Accept sessions via password", "Aceptar sesiones a través de contraseña"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Continuar con {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Kuva RustDesk"),
("This PC", "See arvuti"),
("or", "või"),
("Continue with", "Jätka koos"),
("Elevate", "Tõsta"),
("Zoom cursor", "Suumi kursorit"),
("Accept sessions via password", "Aktsepteeri seansid parooli kaudu"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Jätka koos {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Erakutsi RustDesk"),
("This PC", "PC hau"),
("or", "edo"),
("Continue with", "Jarraitu honekin"),
("Elevate", "Igo maila"),
("Zoom cursor", "Handitu kurtsorea"),
("Accept sessions via password", "Onartu saioak pasahitzaren bidez"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "{} honekin jarraitu"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "RustDesk نمایش"),
("This PC", "This PC"),
("or", "یا"),
("Continue with", "ادامه با"),
("Elevate", "ارتقاء"),
("Zoom cursor", " بزرگنمایی نشانگر ماوس"),
("Accept sessions via password", "قبول درخواست با رمز عبور"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "ادامه با {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Näytä RustDesk"),
("This PC", "Tämä tietokone"),
("or", "tai"),
("Continue with", "Jatka käyttäen"),
("Elevate", "Korota oikeudet"),
("Zoom cursor", "Suurennusosoitin"),
("Accept sessions via password", "Hyväksy istunnot salasanalla"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Jatka käyttäen {}"),
].iter().cloned().collect();
}

View File

@@ -106,7 +106,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Are you sure you want to delete this empty directory?", "Voulez-vous vraiment supprimer ce répertoire vide ?"),
("Are you sure you want to delete the file of this directory?", "Voulez-vous vraiment supprimer le fichier de ce répertoire ?"),
("Do this for all conflicts", "Appliquer à tous les conflits"),
("This is irreversible!", "Ceci est irréversible !"),
("This is irreversible!", "Cette action est irréversible !"),
("Deleting", "Suppression"),
("files", "fichiers"),
("Waiting", "En attente"),
@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Afficher RustDesk"),
("This PC", "Ce PC"),
("or", "ou"),
("Continue with", "Continuer avec"),
("Elevate", "Élever les privilèges"),
("Zoom cursor", "Augmenter la taille du curseur"),
("Accept sessions via password", "Accepter les sessions via mot de passe"),
@@ -730,11 +729,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("input note here", "saisir la note ici"),
("note-at-conn-end-tip", "Proposer de rédiger une note une fois la connexion terminée"),
("Show terminal extra keys", "Afficher les touches supplémentaires du terminal"),
("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", ""),
("Relative mouse mode", "Mode souris relative"),
("rel-mouse-not-supported-peer-tip", "Le mode souris relative nest pas pris en charge par lappareil distant."),
("rel-mouse-not-ready-tip", "Le mode souris relative nest pas encore prêt ; veuillez réessayer."),
("rel-mouse-lock-failed-tip", "Échec du verrouillage du curseur. Le mode souris relative a été désactivé."),
("rel-mouse-exit-{}-tip", "Appuyez sur {} pour quitter."),
("rel-mouse-permission-lost-tip", "Lautorisation de contrôle du clavier a été révoquée. Le mode souris relative a été désactivé."),
("Changelog", "Journal des modifications"),
("keep-awake-during-outgoing-sessions-label", "Maintenir lécran allumé lors des sessions sortantes"),
("keep-awake-during-incoming-sessions-label", "Maintenir lécran allumé lors des sessions entrantes"),
("Continue with {}", "Continuer avec {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "RustDesk-ის ჩვენება"),
("This PC", "ეს კომპიუტერი"),
("or", "ან"),
("Continue with", "გაგრძელება"),
("Elevate", "უფლებების აწევა"),
("Zoom cursor", "კურსორის მასშტაბირება"),
("Accept sessions via password", "სესიების მიღება პაროლით"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "{}-ით გაგრძელება"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "הצג את RustDesk"),
("This PC", "מחשב זה"),
("or", "או"),
("Continue with", "המשך עם"),
("Elevate", "הפעל הרשאות מורחבות"),
("Zoom cursor", "הגדל סמן"),
("Accept sessions via password", "קבל הפעלות באמצעות סיסמה"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "המשך עם {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Prikaži RustDesk"),
("This PC", "Ovo računalo"),
("or", "ili"),
("Continue with", "Nastavi sa"),
("Elevate", "Izdigni"),
("Zoom cursor", "Zumiraj kursor"),
("Accept sessions via password", "Prihvati sesije preko lozinke"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Nastavi sa {}"),
].iter().cloned().collect();
}

View File

@@ -149,7 +149,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Click to upgrade", "Kattintson ide a frissítés telepítéséhez"),
("Configure", "Beállítás"),
("config_acc", "A számítógép távoli vezérléséhez a RustDesknek hozzáférési jogokat kell adnia."),
("config_screen", "Ahhoz, hogy távolról hozzáférhessen a számítógépéhez, meg kell adnia a RustDesknek a Képernyőfelvétel jogosultságot."),
("config_screen", "Ahhoz, hogy távolról hozzáférhessen a számítógépéhez, meg kell adnia a RustDesknek a \"Képernyőfelvétel\" jogosultságot."),
("Installing ...", "Telepítés…"),
("Install", "Telepítés"),
("Installation", "Telepítés"),
@@ -276,13 +276,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Do you accept?", "Elfogadás?"),
("Open System Setting", "Rendszerbeállítások megnyitása"),
("How to get Android input permission?", "Hogyan állítható be az Androidos beviteli engedély?"),
("android_input_permission_tip1", "Ahhoz, hogy egy távoli eszköz vezérelhesse Android készülékét, engedélyeznie kell a RustDesk számára a Hozzáférhetőség szolgáltatás használatát."),
("android_input_permission_tip1", "Ahhoz, hogy egy távoli eszköz vezérelhesse Android készülékét, engedélyeznie kell a RustDesk számára a \"Hozzáférhetőség\" szolgáltatás használatát."),
("android_input_permission_tip2", "A következő rendszerbeállítások oldalon a letöltött alkalmazások menüponton belül, kapcsolja be a [RustDesk Input] szolgáltatást."),
("android_new_connection_tip", "Új kérés érkezett, mely vezérelni szeretné az eszközét"),
("android_service_will_start_tip", "A képernyőmegosztás aktiválása automatikusan elindítja a szolgáltatást, így más eszközök is vezérelhetik ezt az Android-eszközt."),
("android_stop_service_tip", "A szolgáltatás leállítása automatikusan szétkapcsol minden létező kapcsolatot."),
("android_version_audio_tip", "A jelenlegi Android verzió nem támogatja a hangrögzítést, frissítsen legalább Android 10-re, vagy egy újabb verzióra."),
("android_start_service_tip", "A képernyőmegosztó szolgáltatás elindításához koppintson a Kapcsolási szolgáltatás indítása gombra, vagy aktiválja a Képernyőfelvétel engedélyt."),
("android_start_service_tip", "A képernyőmegosztó szolgáltatás elindításához koppintson a \"Kapcsolási szolgáltatás indítása\" gombra, vagy aktiválja a \"Képernyőfelvétel\" engedélyt."),
("android_permission_may_not_change_tip", "A meglévő kapcsolatok engedélyei csak új kapcsolódás után módosulnak."),
("Account", "Fiók"),
("Overwrite", "Felülírás"),
@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "A RustDesk megjelenítése"),
("This PC", "Ez a számítógép"),
("or", "vagy"),
("Continue with", "Folytatás a következővel"),
("Elevate", "Hozzáférés engedélyezése"),
("Zoom cursor", "Kurzor nagyítása"),
("Accept sessions via password", "Munkamenetek elfogadása jelszóval"),
@@ -408,15 +407,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Select local keyboard type", "Helyi billentyűzet típusának kiválasztása"),
("software_render_tip", "Ha Nvidia grafikus kártyát használ Linux alatt, és a távoli ablak a kapcsolat létrehozása után azonnal bezáródik, akkor a Nouveau nyílt forráskódú illesztőprogramra való váltás és a szoftveres leképezés alkalmazása segíthet. A szoftvert újra kell indítani."),
("Always use software rendering", "Mindig szoftveres leképezést használjon"),
("config_input", "Ahhoz, hogy a távoli asztalt a billentyűzettel vezérelhesse, a RustDesknek meg kell adnia a Bemenet figyelése jogosultságot."),
("config_microphone", "Ahhoz, hogy távolról beszélhessen, meg kell adnia a RustDesknek a Hangfelvétel jogosultságot."),
("config_input", "Ahhoz, hogy a távoli asztalt a billentyűzettel vezérelhesse, a RustDesknek meg kell adnia a \"Bemenet figyelése\" jogosultságot."),
("config_microphone", "Ahhoz, hogy távolról beszélhessen, meg kell adnia a RustDesknek a \"Hangfelvétel\" jogosultságot."),
("request_elevation_tip", "Akkor is kérhet megnövelt jogokat, ha valaki a partneroldalon van."),
("Wait", "Várjon"),
("Elevation Error", "Emelt szintű hozzáférési hiba"),
("Ask the remote user for authentication", "Hitelesítés kérése a távoli felhasználótól"),
("Choose this if the remote account is administrator", "Akkor válassza ezt, ha a távoli fiók rendszergazda"),
("Transmit the username and password of administrator", "Küldje el a rendszergazda felhasználónevét és jelszavát"),
("still_click_uac_tip", "A távoli felhasználónak továbbra is az „Igen” gombra kell kattintania a RustDesk UAC ablakában. Kattintson!"),
("still_click_uac_tip", "A távoli felhasználónak továbbra is az \"Igen\" gombra kell kattintania a RustDesk UAC ablakában. Kattintson!"),
("Request Elevation", "Emelt szintű jogok igénylése"),
("wait_accept_uac_tip", "Várjon, amíg a távoli felhasználó elfogadja az UAC párbeszédet."),
("Elevate successfully", "Emelt szintű jogok megadva"),
@@ -442,7 +441,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Voice call", "Hanghívás"),
("Text chat", "Szöveges csevegés"),
("Stop voice call", "Hanghívás leállítása"),
("relay_hint_tip", "Ha a közvetlen kapcsolat nem lehetséges, megpróbálhat kapcsolatot létesíteni egy továbbító-kiszolgálón keresztül.\nHa az első próbálkozáskor továbbító-kiszolgálón keresztüli kapcsolatot szeretne létrehozni, használhatja az „/r” utótagot. Az azonosítóhoz vagy a Mindig továbbító-kiszolgálón keresztül kapcsolódom opcióhoz a legutóbbi munkamenetek listájában, ha van ilyen."),
("relay_hint_tip", "Ha a közvetlen kapcsolat nem lehetséges, megpróbálhat kapcsolatot létesíteni egy továbbító-kiszolgálón keresztül.\nHa az első próbálkozáskor továbbító-kiszolgálón keresztüli kapcsolatot szeretne létrehozni, használhatja az \"/r\" utótagot. Az azonosítóhoz vagy a \"Mindig továbbító-kiszolgálón keresztül kapcsolódom\" opcióhoz a legutóbbi munkamenetek listájában, ha van ilyen."),
("Reconnect", "Újrakapcsolódás"),
("Codec", "Kodek"),
("Resolution", "Felbontás"),
@@ -490,7 +489,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Update", "Frissítés"),
("Enable", "Engedélyezés"),
("Disable", "Letiltás"),
("Options", "Beállítások"),
("Options", "Opciók"),
("resolution_original_tip", "Eredeti felbontás"),
("resolution_fit_local_tip", "Helyi felbontás beállítása"),
("resolution_custom_tip", "Testre szabható felbontás"),
@@ -559,7 +558,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Plug out all", "Kapcsolja ki az összeset"),
("True color (4:4:4)", "Valódi szín (4:4:4)"),
("Enable blocking user input", "Engedélyezze a felhasználói bevitel blokkolását"),
("id_input_tip", "Megadhat egy azonosítót, egy közvetlen IP-címet vagy egy tartományt egy porttal (<domain>:<port>).\nHa egy másik kiszolgálón lévő eszközhöz szeretne hozzáférni, adja meg a kiszolgáló címét (<id>@<kiszolgáló_cím>?key=<key_value>), például\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nHa egy nyilvános kiszolgálón lévő eszközhöz szeretne hozzáférni, adja meg a <id>@public lehetőséget. A kulcsra nincs szükség nyilvános kiszolgálók esetén.\n\nHa az első kapcsolathoz továbbító-kiszolgálón keresztüli kapcsolatot akar kényszeríteni, adja hozzá az „/r” az azonosítót a végén, például 9123456234/r."),
("id_input_tip", "Megadhat egy azonosítót, egy közvetlen IP-címet vagy egy tartományt egy porttal (<domain>:<port>).\nHa egy másik kiszolgálón lévő eszközhöz szeretne hozzáférni, adja meg a kiszolgáló címét (<id>@<kiszolgáló_cím>?key=<key_value>), például\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nHa egy nyilvános kiszolgálón lévő eszközhöz szeretne hozzáférni, adja meg a \"<id>@public\" lehetőséget. A kulcsra nincs szükség nyilvános kiszolgálók esetén.\n\nHa az első kapcsolathoz továbbító-kiszolgálón keresztüli kapcsolatot akar kényszeríteni, adja hozzá az \"/r\" az azonosítót a végén, például \"9123456234/r\"."),
("privacy_mode_impl_mag_tip", "1. mód"),
("privacy_mode_impl_virtual_display_tip", "2. mód"),
("Enter privacy mode", "Lépjen be az adatvédelmi módba"),
@@ -622,7 +621,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Power", "Főkapcsoló"),
("Telegram bot", "Telegram bot"),
("enable-bot-tip", "Ha aktiválja ezt a funkciót, akkor a 2FA-kódot a botjától kaphatja meg. Kapcsolati értesítésként is használható."),
("enable-bot-desc", "1. Nyisson csevegést @BotFather.\n2. Küldje el a /newbot parancsot. Miután ezt a lépést elvégezte, kap egy tokent.\n3. Indítson csevegést az újonnan létrehozott botjával. Küldjön egy olyan üzenetet, amely egy perjel („/”) kezdetű, pl. „/hello” az aktiváláshoz.\n"),
("enable-bot-desc", "1. Nyisson csevegést @BotFather.\n2. Küldje el a \"/newbot\" parancsot. Miután ezt a lépést elvégezte, kap egy tokent.\n3. Indítson csevegést az újonnan létrehozott botjával. Küldjön egy olyan üzenetet, amely egy perjel (\"/\") kezdetű, pl. \"/hello\" az aktiváláshoz.\n"),
("cancel-2fa-confirm-tip", "Biztosan vissza akarja vonni a 2FA-hitelesítést?"),
("cancel-bot-confirm-tip", "Biztosan le akarja mondani a Telegram botot?"),
("About RustDesk", "A RustDesk névjegye"),
@@ -643,7 +642,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", "Az egyirányú fájlátvitel engedélyezve van a vezérelt oldalon."),
("Authentication Required", "Hitelesítés szükséges"),
("Authenticate", "Hitelesítés"),
("web_id_input_tip", "Azonos kiszolgálón lévő azonosítót adhat meg, a közvetlen IP elérés nem támogatott a webkliensben.\nHa egy másik kiszolgálón lévő eszközhöz szeretne hozzáférni, adja meg a kiszolgáló címét (<id>@<kiszolgáló_cím>?key=<key_value>), például\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nHa egy nyilvános kiszolgálón lévő eszközhöz szeretne hozzáférni, adja meg a <id>@public betűt. A kulcsra nincs szükség a nyilvános kiszolgálók esetében."),
("web_id_input_tip", "Azonos kiszolgálón lévő azonosítót adhat meg, a közvetlen IP elérés nem támogatott a webkliensben.\nHa egy másik kiszolgálón lévő eszközhöz szeretne hozzáférni, adja meg a kiszolgáló címét (<id>@<kiszolgáló_cím>?key=<key_value>), például\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nHa egy nyilvános kiszolgálón lévő eszközhöz szeretne hozzáférni, adja meg a \"<id>@public\" betűt. A kulcsra nincs szükség a nyilvános kiszolgálók esetében."),
("Download", "Letöltés"),
("Upload folder", "Mappa feltöltése"),
("Upload files", "Fájlok feltöltése"),
@@ -682,9 +681,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Downloading {}", "{} letöltése"),
("{} Update", "{} frissítés"),
("{}-to-update-tip", "A(z) {} bezárása és az új verzió telepítése."),
("download-new-version-failed-tip", "Ha a letöltés sikertelen, akkor vagy újrapróbálkozhat, vagy a Letöltés gombra kattintva letöltheti a kiadási oldalról, és manuálisan frissíthet."),
("download-new-version-failed-tip", "Ha a letöltés sikertelen, akkor vagy újrapróbálkozhat, vagy a \"Letöltés\" gombra kattintva letöltheti a kiadási oldalról, és manuálisan frissíthet."),
("Auto update", "Automatikus frissítés"),
("update-failed-check-msi-tip", "A telepítési módszer felismerése nem sikerült. Kattintson a Letöltés gombra, hogy letöltse a kiadási oldalról, és manuálisan frissítse."),
("update-failed-check-msi-tip", "A telepítési módszer felismerése nem sikerült. Kattintson a \"Letöltés\" gombra, hogy letöltse a kiadási oldalról, és manuálisan frissítse."),
("websocket_tip", "WebSocket használatakor csak a relé-kapcsolatok támogatottak."),
("Use WebSocket", "WebSocket használata"),
("Trackpad speed", "Érintőpad sebessége"),
@@ -727,14 +726,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Disable UDP", "UDP letiltása"),
("disable-udp-tip", "Meghatározza, hogy csak TCP-t használjon-e. Ha ez az beállítás engedélyezve van, a RustDesk nem fogja többé használni a 21116-os UDP-portot, helyette a 21116-os TCP-portot fogja használni."),
("server-oss-not-support-tip", "MEGJEGYZÉS: Az OSS RustDesk kiszolgáló nem támogatja ezt a funkciót."),
("input note here", "Megjegyzés bevitele"),
("note-at-conn-end-tip", "Megjegyzés a kapcsolat végén"),
("input note here", "Megjegyzés beírása"),
("note-at-conn-end-tip", "Kérjen megjegyzést a kapcsolat végén"),
("Show terminal extra keys", "További terminálgombok megjelenítése"),
("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", ""),
("Relative mouse mode", "Relatív egér mód"),
("rel-mouse-not-supported-peer-tip", "A kapcsolódott partner nem támogatja a relatív egér módot."),
("rel-mouse-not-ready-tip", "A relatív egér mód még nem elérhető. Próbálja meg újra."),
("rel-mouse-lock-failed-tip", "Nem sikerült zárolni a kurzort. A relatív egér mód le lett tiltva."),
("rel-mouse-exit-{}-tip", "A kilépéshez nyomja meg a(z) {} gombot."),
("rel-mouse-permission-lost-tip", "A billentyűzet-hozzáférés vissza lett vonva. A relatív egér mód le lett tilva."),
("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-incoming-sessions-label", "Képernyő aktív állapotban tartása a bejövő munkamenetek során"),
("Continue with {}", "Folytatás a következővel: {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Tampilkan RustDesk"),
("This PC", "PC ini"),
("or", "atau"),
("Continue with", "Lanjutkan dengan"),
("Elevate", "Elevasi"),
("Zoom cursor", "Perbersar Kursor"),
("Accept sessions via password", "Izinkan sesi dengan kata sandi"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Lanjutkan dengan {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Visualizza RustDesk"),
("This PC", "Questo PC"),
("or", "O"),
("Continue with", "Continua con"),
("Elevate", "Eleva"),
("Zoom cursor", "Cursore zoom"),
("Accept sessions via password", "Accetta sessioni via password"),
@@ -730,11 +729,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("input note here", "Inserisci nota qui"),
("note-at-conn-end-tip", "Visualizza nota alla fine della connessione"),
("Show terminal extra keys", "Visualizza tasti aggiuntivi terminale"),
("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", ""),
("Relative mouse mode", "Modalità relativa mouse"),
("rel-mouse-not-supported-peer-tip", "La modalità mouse relativa non è supportata dal peer connesso."),
("rel-mouse-not-ready-tip", "La modalità mouse relativa non è ancora pronta. Riprova."),
("rel-mouse-lock-failed-tip", "Impossibile bloccare il cursore. La modalità mouse relativa è stata disabilitata."),
("rel-mouse-exit-{}-tip", "Premi {} per uscire."),
("rel-mouse-permission-lost-tip", "È stata revocato l'accesso alla tastiera. La modalità mouse relativa è stata disabilitata."),
("Changelog", "Novità programma"),
("keep-awake-during-outgoing-sessions-label", "Mantieni lo schermo attivo durante le sessioni in uscita"),
("keep-awake-during-incoming-sessions-label", "Mantieni lo schermo attivo durante le sessioni in ingresso"),
("Continue with {}", "Continua con {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "RustDesk を表示"),
("This PC", "この PC"),
("or", "または"),
("Continue with", "で続行"),
("Elevate", "昇格"),
("Zoom cursor", "カーソルを拡大する"),
("Accept sessions via password", "パスワードでセッションを承認"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "{} で続行"),
].iter().cloned().collect();
}

View File

@@ -3,30 +3,30 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
[
("Status", "상태"),
("Your Desktop", "내 데스크탑"),
("desk_tip", "이 ID와 비밀번호로 데스크에 액세스할 수 있습니다."),
("desk_tip", "이 ID와 비밀번호로 데스크에 액세스할 수 있습니다."),
("Password", "비밀번호"),
("Ready", "준비 완료"),
("Established", "연결됨"),
("connecting_status", "RustDesk 네트워크에 연결 중..."),
("Enable service", "서비스 활성화"),
("Start service", "서비스 시작"),
("Service is running", "서비스가 실행 중 입니다"),
("Service is running", "서비스가 실행 중입니다"),
("Service is not running", "서비스가 실행되지 않았습니다"),
("not_ready_status", "준비되지 않았습니다. 연결을 확인해 주세요"),
("Control Remote Desktop", "원격 데스크탑 제어"),
("Transfer file", "파일 전송"),
("Connect", "연결"),
("Recent sessions", "최근 세션"),
("Address book", "세션 주소록"),
("Address book", "주소록"),
("Confirmation", "확인"),
("TCP tunneling", "TCP 터널링"),
("Remove", "삭제"),
("Refresh random password", "임의의 비밀번호 새로 고침"),
("Set your own password", "자신만의 비밀번호 설정"),
("Enable keyboard/mouse", "키보드/마우스 사용함"),
("Enable clipboard", "클립보드 사용함"),
("Enable file transfer", "파일 전송 사용함"),
("Enable TCP tunneling", "TCP 터널링 사용함"),
("Enable keyboard/mouse", "키보드/마우스 허용"),
("Enable clipboard", "클립보드 허용"),
("Enable file transfer", "파일 전송 허용"),
("Enable TCP tunneling", "TCP 터널링 허용"),
("IP Whitelisting", "IP 화이트리스트"),
("ID/Relay Server", "ID/릴레이 서버"),
("Import server config", "서버 구성 가져오기"),
@@ -81,7 +81,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Please try 1 minute later", "1분 후에 다시 시도하세요"),
("Login Error", "로그인 오류"),
("Successful", "성공"),
("Connected, waiting for image...", "연결되었습니다, 이미지를 기다리는 중..."),
("Connected, waiting for image...", "연결됨, 화면을 기다리는 중..."),
("Name", "이름"),
("Type", "유형"),
("Modified", "수정 날짜"),
@@ -136,20 +136,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("ID does not exist", "ID가 존재하지 않습니다"),
("Failed to connect to rendezvous server", "랑데부 서버 연결에 실패했습니다"),
("Please try later", "나중에 시도해 주세요"),
("Remote desktop is offline", "원격 데스크이 오프라인입니다"),
("Remote desktop is offline", "원격 데스크이 오프라인입니다"),
("Key mismatch", "키가 일치하지 않습니다"),
("Timeout", "시간 초과"),
("Failed to connect to relay server", "릴레이 서버 연결에 실패했습니다"),
("Failed to connect via rendezvous server", "랑데부 서버를 통한 연결에 실패했습니다"),
("Failed to connect via relay server", "릴레이 서버를 통한 연결에 실패했습니다"),
("Failed to make direct connection to remote desktop", "원격 데스크톱에 직접 연결에 실패했습니다"),
("Failed to make direct connection to remote desktop", "원격 데스크 직접 연결에 실패했습니다"),
("Set Password", "비밀번호 설정"),
("OS Password", "OS 비밀번호"),
("install_tip", "UAC로 인해 경우에 따라 RustDesk가 원격 쪽에서 제대로 작동하지 않을 수 있습니다. UAC를 피하려면 아래 버튼을 클릭하여 시스템에 RustDesk를 설치하세요."),
("Click to upgrade", "업그레이드"),
("Configure", "구성"),
("config_acc", "데스크을 원격으로 제어하려면 RustDesk에 \"접근성\" 권한을 부여해야 합니다."),
("config_screen", "데스크에 원격으로 액세스하려면 RustDesk에 \"화면 녹화\" 권한을 부여해야 합니다."),
("config_acc", "데스크을 원격으로 제어하려면 RustDesk에 \"접근성\" 권한을 부여해야 합니다."),
("config_screen", "데스크에 원격으로 액세스하려면 RustDesk에 \"화면 녹화\" 권한을 부여해야 합니다."),
("Installing ...", "설치 중..."),
("Install", "설치하기"),
("Installation", "설치"),
@@ -162,7 +162,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Generating ...", "생성 중 ..."),
("Your installation is lower version.", "설치된 버전이 낮습니다."),
("not_close_tcp_tip", "터널을 사용하는 동안에는 이 창을 닫지 마세요"),
("Listening ...", "청취 중 ..."),
("Listening ...", "수신 대기 중 ..."),
("Remote Host", "원격 호스트"),
("Remote Port", "원격 포트"),
("Action", "동작"),
@@ -177,7 +177,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Accept", "수락"),
("Dismiss", "거부"),
("Disconnect", "연결 해제"),
("Enable file copy and paste", "파일 복사 및 붙여넣기 사용함"),
("Enable file copy and paste", "파일 복사 및 붙여넣기 허용"),
("Connected", "연결됨"),
("Direct and encrypted connection", "직접 및 암호화된 연결"),
("Relayed and encrypted connection", "릴레이 및 암호화된 연결"),
@@ -186,9 +186,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enter Remote ID", "원격 ID 입력"),
("Enter your password", "비밀번호 입력"),
("Logging in...", "로그인 중..."),
("Enable RDP session sharing", "RDP 세션 공유 사용함"),
("Enable RDP session sharing", "RDP 세션 공유 허용"),
("Auto Login", "자동 로그인"),
("Enable direct IP access", "직접 IP 액세스 사용함"),
("Enable direct IP access", "직접 IP 액세스 허용"),
("Rename", "이름 바꾸기"),
("Space", "공백"),
("Create desktop shortcut", "바탕 화면 바로가기 만들기"),
@@ -200,13 +200,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Login screen using Wayland is not supported", "Wayland를 사용한 로그인 화면은 지원되지 않습니다"),
("Reboot required", "재부팅이 필요합니다"),
("Unsupported display server", "지원하지 않는 디스플레이 서버"),
("x11 expected", "x11 예상"),
("x11 expected", "x11 환경이 필요합니다"),
("Port", "포트"),
("Settings", "설정"),
("Username", "사용자 이름"),
("Invalid port", "유효하지 않은 포트입니다"),
("Closed manually by the peer", "피어가 수동으로 닫았습니다"),
("Enable remote configuration modification", "원격 구성 수정 사용함"),
("Enable remote configuration modification", "원격 구성 수정 허용"),
("Run without install", "설치 없이 실행"),
("Connect via relay", "릴레이를 통해 연결"),
("Always connect via relay", "항상 릴레이를 통해 연결"),
@@ -214,7 +214,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Login", "로그인"),
("Verify", "확인"),
("Remember me", "기억하기"),
("Trust this device", "이 장치 신뢰"),
("Trust this device", "이 장치 신뢰"),
("Verification code", "인증 코드"),
("verification_tip", "등록한 이메일 주소로 인증 코드가 전송되었으니 인증 코드를 입력하여 로그인을 계속하세요."),
("Logout", "로그아웃"),
@@ -291,7 +291,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Help", "도움말"),
("Failed", "실패"),
("Succeeded", "성공"),
("Someone turns on privacy mode, exit", "누군가 개인정보 보호 모드를 니다, 종료합니다"),
("Someone turns on privacy mode, exit", "누군가 개인정보 보호 모드를 켰습니다, 연결을 종료합니다"),
("Unsupported", "지원되지 않음"),
("Peer denied", "연결 거부됨"),
("Please install plugins", "플러그인을 설치해주세요"),
@@ -311,7 +311,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use permanent password", "영구 비밀번호 사용"),
("Use both passwords", "두 가지 비밀번호 모두 사용"),
("Set permanent password", "영구 비밀번호 설정"),
("Enable remote restart", "원격 재시작 사용함"),
("Enable remote restart", "원격 재시작 허용"),
("Restart remote device", "원격 장치 다시 시작"),
("Are you sure you want to restart", "다시 시작하시겠습니까"),
("Restarting remote device", "원격 장치를 다시 시작하는 중"),
@@ -344,7 +344,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow System", "시스템 설정 따름"),
("Enable hardware codec", "하드웨어 코덱 활성화"),
("Unlock Security Settings", "보안 설정 잠금 해제"),
("Enable audio", "오디오 사용함"),
("Enable audio", "오디오 허용"),
("Unlock Network Settings", "네트워크 설정 잠금 해제"),
("Server", "서버"),
("Direct IP Access", "직접 IP 연결"),
@@ -364,13 +364,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Change", "변경"),
("Start session recording", "세션 녹화 시작"),
("Stop session recording", "세션 녹화 중지"),
("Enable recording session", "세션 녹화 사용함"),
("Enable LAN discovery", "LAN 검색 사용함"),
("Enable recording session", "세션 녹화 허용"),
("Enable LAN discovery", "LAN 검색 허용"),
("Deny LAN discovery", "LAN 검색 거부"),
("Write a message", "메시지 쓰기"),
("Prompt", "프롬프트"),
("Please wait for confirmation of UAC...", "UAC 확인을 기다려주세요..."),
("elevated_foreground_window_tip", "원격 데스크의 현재 창을 작동하려면 더 높은 권한이 필요하므로 일시적으로 마우스와 키보드를 사용할 수 없습니다. 원격 사용자에게 현재 창을 최소화하도록 요청하거나 연결 관리 창에서 권한 상승 버튼을 클릭할 수 있습니다. 이 문제를 방지하려면 원격 장치에 소프트웨어를 설치하는 것이 좋습니다."),
("elevated_foreground_window_tip", "원격 데스크의 현재 창을 작동하려면 더 높은 권한이 필요하므로 일시적으로 마우스와 키보드를 사용할 수 없습니다. 원격 사용자에게 현재 창을 최소화하도록 요청하거나 연결 관리 창에서 권한 상승 버튼을 클릭할 수 있습니다. 이 문제를 방지하려면 원격 장치에 소프트웨어를 설치하는 것이 좋습니다."),
("Disconnected", "연결 끊김"),
("Other", "기타"),
("Confirm before closing multiple tabs", "여러 탭을 닫기 전에 확인"),
@@ -378,13 +378,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Full Access", "전체 액세스"),
("Screen Share", "화면 공유"),
("Wayland requires Ubuntu 21.04 or higher version.", "Wayland는 Ubuntu 21.04 이상 버전이 필요합니다."),
("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland는 상위 버전의 Linux 배포판이 필요합니다. X11 데스크을 사용하거나 OS를 변경하세요."),
("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland는 상위 버전의 Linux 배포판이 필요합니다. X11 데스크을 사용하거나 OS를 변경하세요."),
("JumpLink", "점프 링크"),
("Please Select the screen to be shared(Operate on the peer side).", "공유할 화면을 선택하세요 (피어 측에서 작동)"),
("Show RustDesk", "RustDesk 표시"),
("This PC", "이 PC"),
("or", "또는"),
("Continue with", "계속"),
("Elevate", "권한 상승"),
("Zoom cursor", "커서 확대/축소"),
("Accept sessions via password", "비밀번호를 통해 세션 수락"),
@@ -408,7 +407,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Select local keyboard type", "로컬 키보드 유형 선택"),
("software_render_tip", "Linux에서 Nvidia 그래픽 카드를 사용 중인데 원격 창이 연결 즉시 닫히는 경우 오픈 소스 Nouveau 드라이버로 전환하고 소프트웨어 렌더링을 사용하기로 선택하는 것이 도움이 될 수 있습니다. 소프트웨어를 재시작해야 합니다."),
("Always use software rendering", "항상 소프트웨어 렌더링 사용"),
("config_input", "키보드로 원격 데스크을 제어하려면 RustDesk에 \"입력 모니터링\" 권한을 부여해야 합니다."),
("config_input", "키보드로 원격 데스크을 제어하려면 RustDesk에 \"입력 모니터링\" 권한을 부여해야 합니다."),
("config_microphone", "원격으로 통화하려면 RustDesk에 \"오디오 녹음\" 권한을 부여해야 합니다."),
("request_elevation_tip", "원격 측에 사람이 있는 경우 권한 상승을 요청할 수도 있습니다."),
("Wait", "대기"),
@@ -428,7 +427,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Weak", "약함"),
("Medium", "보통"),
("Strong", "강력"),
("Switch Sides", "측면 전환"),
("Switch Sides", "역할 전환"),
("Please confirm if you want to share your desktop?", "데스크탑을 공유하시겠습니까?"),
("Display", "디스플레이"),
("Default View Style", "기본 보기 스타일"),
@@ -468,14 +467,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("login_linux_tip", "X 데스크탑을 활성화하려면 제어되는 터미널의 Linux 계정에 로그인하세요"),
("verify_rustdesk_password_tip", "RustDesk 비밀번호 확인"),
("remember_account_tip", "이 계정 기억하기"),
("os_account_desk_tip", "이 계정은 원격 OS에 로그인하고 헤드리스에서 데스크 세션을 활성화하는 데 사용됩니다."),
("os_account_desk_tip", "이 계정은 원격 OS에 로그인하고 헤드리스에서 데스크 세션을 활성화하는 데 사용됩니다."),
("OS Account", "OS 계정"),
("another_user_login_title_tip", "다른 사용자가 이미 로그인했습니다"),
("another_user_login_text_tip", "연결 끊기"),
("xorg_not_found_title_tip", "Xorg를 찾을 수 없습니다"),
("xorg_not_found_text_tip", "Xorg를 설치해 주세요"),
("no_desktop_title_tip", "사용 가능한 데스크 환경이 없습니다"),
("no_desktop_text_tip", "GNOME 데스크을 설치해 주세요"),
("no_desktop_title_tip", "사용 가능한 데스크 환경이 없습니다"),
("no_desktop_text_tip", "GNOME 데스크을 설치해 주세요"),
("No need to elevate", "권한 상승이 필요없습니다"),
("System Sound", "시스템 소리"),
("Default", "기본"),
@@ -488,7 +487,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Plugins", "플러그인"),
("Uninstall", "설치 제거"),
("Update", "업데이트"),
("Enable", "사용함"),
("Enable", "허용"),
("Disable", "사용 안 함"),
("Options", "옵션"),
("resolution_original_tip", "원본 해상도"),
@@ -558,7 +557,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Virtual display", "가상 디스플레이"),
("Plug out all", "모든 플러그를 뽑으세요"),
("True color (4:4:4)", "트루컬러 (4:4:4)"),
("Enable blocking user input", "사용자 입력 차단 사용함"),
("Enable blocking user input", "사용자 입력 차단 허용"),
("id_input_tip", "ID, 직접 IP 또는 포트가 있는 도메인 (<domain>:<port>)을 입력할 수 있습니다.\n다른 서버에 있는 장치에 액세스하려면 서버 주소 (<id>@<server_address>?key=<key_value>)를 추가하세요. 예를들어 \n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\n공용 서버의 장치에 액세스하려면 \"<id>@public\"을 입력하세요. 공용 서버에서는 키가 필요하지 않습니다.\n\n첫 번째 연결에서 릴레이 연결을 강제로 사용하려면 ID 끝에 \"/r\"을 추가합니다, 예를들면 \"9123456234/r\"."),
("privacy_mode_impl_mag_tip", "모드 1"),
("privacy_mode_impl_virtual_display_tip", "모드 2"),
@@ -571,8 +570,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("swap-left-right-mouse", "마우스 왼쪽 버튼과 오른쪽 버튼 교체"),
("2FA code", "이중 인증 코드"),
("More", "더 많은"),
("enable-2fa-title", "이중 인증 사용함"),
("enable-2fa-desc", "지금 인증앱을 설정해 주세요. 휴대폰이나 데스크에서 Authy, Microsoft 또는 Google 인증기와 같은 인증기 앱을 사용할 수 있습니다.\n\n앱으로 QR 코드를 스캔하고 앱에 표시된 코드를 입력하면 이중 인증이 가능합니다."),
("enable-2fa-title", "이중 인증 허용"),
("enable-2fa-desc", "지금 인증앱을 설정해 주세요. 휴대폰이나 데스크에서 Authy, Microsoft 또는 Google 인증기와 같은 인증기 앱을 사용할 수 있습니다.\n\n앱으로 QR 코드를 스캔하고 앱에 표시된 코드를 입력하면 이중 인증이 가능합니다."),
("wrong-2fa-code", "코드를 확인할 수 없습니다. 코드와 현지 시간 설정이 올바른지 확인합니다"),
("enter-2fa-title", "이중 인증"),
("Email verification code must be 6 characters.", "이메일 인증 코드는 6자여야 합니다."),
@@ -621,7 +620,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Volume down", "볼륨 낮추기"),
("Power", "전원"),
("Telegram bot", "Telegram 봇"),
("enable-bot-tip", "이 기능을 활성화하면 봇에서 이중 인 코드를 받을 수 있습니다. 또한 연결 알림 기능도 할 수 있습니다."),
("enable-bot-tip", "이 기능을 활성화하면 봇에서 이중 인 코드를 받을 수 있습니다. 또한 연결 알림 기능도 할 수 있습니다."),
("enable-bot-desc", "1. @BotFather와 채팅을 시작합니다.\n2. \"/newbot\" 명령을 보내주세요. 이 단계를 완료하면 토큰을 받게 됩니다.\n3. 새로 만든 봇과 채팅을 시작합니다. \"/hello\"와 같이 앞에 슬래시 (\"/\")로 시작하는 메시지를 보내 활성화합니다."),
("cancel-2fa-confirm-tip", "이중 인증을 취소하시겠습니까?"),
("cancel-bot-confirm-tip", "Telegram 봇을 취소하시겠습니까?"),
@@ -632,7 +631,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", "최소 {}자 이상 필요합니다."),
("Wrong PIN", "잘못된 PIN"),
("Set PIN", "PIN 설정"),
("Enable trusted devices", "신뢰할 수 있는 장치 사용함"),
("Enable trusted devices", "신뢰할 수 있는 장치 허용"),
("Manage trusted devices", "신뢰할 수 있는 장치 관리"),
("Platform", "플랫폼"),
("Days remaining", "일 남음"),
@@ -678,7 +677,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("screenshot-action-tip", "스크린샷을 계속 진행할 방법을 선택해 주세요."),
("Save as", "다른 이름으로 저장"),
("Copy to clipboard", "클립보드에 복사"),
("Enable remote printer", "원격 프린터 사용함"),
("Enable remote printer", "원격 프린터 허용"),
("Downloading {}", "{} 다운로드 중"),
("{} Update", "{} 업데이트"),
("{}-to-update-tip", "{}가 지금 닫히고 새 버전을 설치합니다."),
@@ -693,11 +692,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable IPv6 P2P connection", "IPv6 P2P 연결 사용"),
("Enable UDP hole punching", "UDP 홀 펀칭 사용"),
("View camera", "카메라 보기"),
("Enable camera", "카메라 사용함"),
("Enable camera", "카메라 허용"),
("No cameras", "카메라 없음"),
("view_camera_unsupported_tip", "원격 장치가 카메라 보기를 지원하지 않습니다."),
("Terminal", "터미널"),
("Enable terminal", "터미널 사용함"),
("Enable terminal", "터미널 허용"),
("New tab", "새 탭"),
("Keep terminal sessions on disconnect", "연결이 끊어져도 터미널 세션 유지"),
("Terminal (Run as administrator)", "터미널 (관리자 권한으로 실행)"),
@@ -730,11 +729,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("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", "수신 세션 중 화면 켜짐 유지"),
("Continue with {}", "{}(으)로 계속"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", ""),
("This PC", ""),
("or", ""),
("Continue with", ""),
("Elevate", ""),
("Zoom cursor", ""),
("Accept sessions via password", ""),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", ""),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Rodyti RustDesk"),
("This PC", "Šis kompiuteris"),
("or", "arba"),
("Continue with", "Tęsti su"),
("Elevate", "Pakelti"),
("Zoom cursor", "Mastelio keitimo žymeklis"),
("Accept sessions via password", "Priimti seansus naudojant slaptažodį"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Tęsti su {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Rādīt RustDesk"),
("This PC", "Šis dators"),
("or", "vai"),
("Continue with", "Turpināt ar"),
("Elevate", "Pacelt"),
("Zoom cursor", "Tālummaiņas kursors"),
("Accept sessions via password", "Pieņemt sesijas, izmantojot paroli"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Turpināt ar {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Vis RustDesk"),
("This PC", "Denne PC"),
("or", "eller"),
("Continue with", "Fortsett med"),
("Elevate", "Elever"),
("Zoom cursor", "Zoom markør"),
("Accept sessions via password", "Aksepter sesjoner via passord"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Fortsett med {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Toon RustDesk"),
("This PC", "Deze PC"),
("or", "of"),
("Continue with", "Ga verder met"),
("Elevate", "Verhoog"),
("Zoom cursor", "Zoom cursor"),
("Accept sessions via password", "Sessies accepteren via wachtwoord"),
@@ -730,11 +729,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("input note here", "voeg hier een opmerking toe"),
("note-at-conn-end-tip", "Vraag om een opmerking aan het einde van de verbinding"),
("Show terminal extra keys", "Toon extra toetsen voor terminal"),
("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", ""),
("Relative mouse mode", "Relatieve muismodus"),
("rel-mouse-not-supported-peer-tip", "De relatieve muismodus wordt niet ondersteund door het externe apparaat."),
("rel-mouse-not-ready-tip", "De relatieve muismodus was nog niet klaar, probeer het later opnieuw."),
("rel-mouse-lock-failed-tip", "Het vergrendelen van de cursor is mislukt. De relatieve muismodus is uitgeschakeld."),
("rel-mouse-exit-{}-tip", "Druk op {} om af te sluiten."),
("rel-mouse-permission-lost-tip", "De toetsenbordcontrole is uitgeschakeld. De relatieve muismodus is uitgeschakeld."),
("Changelog", "Wijzigingenlogboek"),
("keep-awake-during-outgoing-sessions-label", "Houd het scherm open tijdens de uitgaande sessies."),
("keep-awake-during-incoming-sessions-label", "Houd het scherm open tijdens de inkomende sessies."),
("Continue with {}", "Ga verder met {}"),
].iter().cloned().collect();
}

View File

@@ -13,7 +13,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Service is running", "Usługa uruchomiona"),
("Service is not running", "Usługa nie jest uruchomiona"),
("not_ready_status", "Brak gotowości"),
("Control Remote Desktop", "Połącz się z"),
("Control Remote Desktop", "Steruj pulpitem zdalnym"),
("Transfer file", "Transfer plików"),
("Connect", "Połącz"),
("Recent sessions", "Ostatnie sesje"),
@@ -75,7 +75,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Do you want to enter again?", "Czy chcesz wprowadzić ponownie?"),
("Connection Error", "Błąd połączenia"),
("Error", "Błąd"),
("Reset by the peer", "Połączenie zresetowanie przez zdalne urządzenie"),
("Reset by the peer", "Połączenie zresetowane przez zdalne urządzenie"),
("Connecting...", "Łączenie..."),
("Connection in progress. Please wait.", "Trwa łączenie. Proszę czekać."),
("Please try 1 minute later", "Spróbuj za minutę"),
@@ -120,7 +120,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Original", "Oryginalny"),
("Shrink", "Zmniejsz"),
("Stretch", "Rozciągnij"),
("Scrollbar", "Przewijanie ręczne"),
("Scrollbar", "Pasek przewijania"),
("ScrollAuto", "Przewijanie automatyczne"),
("Good image quality", "Wysoka jakość obrazu"),
("Balanced", "Tryb zbalansowany"),
@@ -161,7 +161,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("End-user license agreement", "Umowa licencyjna użytkownika końcowego"),
("Generating ...", "Trwa generowanie..."),
("Your installation is lower version.", "Twoja instalacja jest w niższej wersji"),
("not_close_tcp_tip", "Podczas korzystanie z tunelowania, nie zamykaj tego okna."),
("not_close_tcp_tip", "Podczas korzystania z tunelowania, nie zamykaj tego okna."),
("Listening ...", "Nasłuchiwanie..."),
("Remote Host", "Host zdalny"),
("Remote Port", "Port zdalny"),
@@ -198,7 +198,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fix it", "Napraw to"),
("Warning", "Ostrzeżenie"),
("Login screen using Wayland is not supported", "Ekran logowania korzystający z Wayland nie jest obsługiwany"),
("Reboot required", "Wymagany ponowne uruchomienie"),
("Reboot required", "Wymagane ponowne uruchomienie"),
("Unsupported display server", "Nieobsługiwany serwer wyświetlania"),
("x11 expected", "Wymagany jest X11"),
("Port", "Port"),
@@ -225,7 +225,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Add Tag", "Dodaj Tag"),
("Unselect all tags", "Odznacz wszystkie tagi"),
("Network error", "Błąd sieci"),
("Username missed", "Nieprawidłowe nazwa użytkownika"),
("Username missed", "Nieprawidłowa nazwa użytkownika"),
("Password missed", "Nieprawidłowe hasło"),
("Wrong credentials", "Błędne dane uwierzytelniające"),
("The verification code is incorrect or has expired", "Kod weryfikacyjny jest niepoprawny lub wygasł"),
@@ -265,7 +265,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No permission of file transfer", "Brak uprawnień na przesyłanie plików"),
("Note", "Notatka"),
("Connection", "Połączenie"),
("Share screen", "Udostępnij ekran"),
("Share screen", "Udostępnianie ekranu"),
("Chat", "Czat"),
("Total", "Łącznie"),
("items", "elementów"),
@@ -314,10 +314,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable remote restart", "Włącz zdalne restartowanie"),
("Restart remote device", "Zrestartuj zdalne urządzenie"),
("Are you sure you want to restart", "Czy na pewno uruchomić ponownie"),
("Restarting remote device", "Trwa restartowanie Zdalnego Urządzenia"),
("Restarting remote device", "Trwa restartowanie zdalnego urządzenia"),
("remote_restarting_tip", "Trwa ponownie uruchomienie zdalnego urządzenia, zamknij ten komunikat i ponownie nawiąż za chwilę połączenie używając hasła permanentnego"),
("Copied", "Skopiowano"),
("Exit Fullscreen", "Wyłącz tryb pełnoekranowy"),
("Exit Fullscreen", "Wyłącz tryb pełnoekranowy"),
("Fullscreen", "Tryb pełnoekranowy"),
("Mobile Actions", "Dostępne mobilne polecenia"),
("Select Monitor", "Wybierz ekran"),
@@ -379,12 +379,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Screen Share", "Udostępnianie ekranu"),
("Wayland requires Ubuntu 21.04 or higher version.", "Wayland wymaga Ubuntu 21.04 lub nowszego."),
("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland wymaga nowszej dystrybucji Linuksa. Wypróbuj pulpit X11 lub zmień system operacyjny."),
("JumpLink", "View"),
("JumpLink", "Podgląd"),
("Please Select the screen to be shared(Operate on the peer side).", "Wybierz ekran do udostępnienia (działaj po zdalnego urządzenia)."),
("Show RustDesk", "Pokaż RustDesk"),
("This PC", "Ten komputer"),
("or", "lub"),
("Continue with", "Kontynuuj z"),
("Elevate", "Uzyskaj uprawnienia"),
("Zoom cursor", "Powiększenie kursora"),
("Accept sessions via password", "Uwierzytelnij sesję używając hasła"),
@@ -403,13 +402,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Add to address book", "Dodaj do Książki Adresowej"),
("Group", "Grupy"),
("Search", "Szukaj"),
("Closed manually by web console", "Zakończone manualnie z konsoli Web"),
("Closed manually by web console", "Zakończone ręcznie z poziomu konsoli webowej"),
("Local keyboard type", "Lokalny typ klawiatury"),
("Select local keyboard type", "Wybierz lokalny typ klawiatury"),
("software_render_tip", "Jeżeli posiadasz kartę graficzną Nvidia i okno zamyka się natychmiast po nawiązaniu połączenia, instalacja sterownika nouveau i wybór renderowania programowego mogą pomóc. Restart aplikacji jest wymagany."),
("Always use software rendering", "Zawsze używaj renderowania programowego"),
("config_input", "By kontrolować zdalne urządzenie przy pomocy klawiatury, musisz udzielić aplikacji RustDesk uprawnień do \"Urządzeń Wejściowych\"."),
("config_microphone", "Aby umożliwić zdalne rozmowy należy przyznać RuskDesk uprawnienia do \"Nagrań audio\"."),
("config_microphone", "Aby umożliwić zdalne rozmowy należy przyznać RustDesk uprawnienia do \"Nagrań audio\"."),
("request_elevation_tip", "Możesz poprosić o podniesienie uprawnień jeżeli ktoś posiada dostęp do zdalnego urządzenia."),
("Wait", "Czekaj"),
("Elevation Error", "Błąd przy podnoszeniu uprawnień"),
@@ -459,8 +458,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("empty_favorite_tip", "Brak ulubionych?\nZnajdźmy kogoś, z kim możesz się połączyć i dodaj Go do ulubionych!"),
("empty_lan_tip", "Ojej, wygląda na to, że nie odkryliśmy żadnych urządzeń z RustDesk w Twojej sieci."),
("empty_address_book_tip", "Ojej, wygląda na to, że nie ma żadnych wpisów w Twojej książce adresowej."),
("Empty Username", "Pusty użytkownik"),
("Empty Password", "Puste hasło"),
("Empty Username", "Pole nazwy użytkownika jest puste"),
("Empty Password", "Pole hasła jest puste"),
("Me", "Ja"),
("identical_file_tip", "Ten plik jest identyczny z plikiem na drugim komputerze."),
("show_monitors_tip", "Pokaż monitory w zasobniku"),
@@ -729,12 +728,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "UWAGA: Serwer OSS RustDesk nie obsługuje tej funkcji."),
("input note here", "Wstaw tutaj notatkę"),
("note-at-conn-end-tip", "Poproś o notatkę po zakończeniu połączenia."),
("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", ""),
("Show terminal extra keys", "Pokaż dodatkowe klawisze terminala"),
("Relative mouse mode", "Tryb przechwytywania myszy"),
("rel-mouse-not-supported-peer-tip", "Zdalne urządzenie nie obsługuje trybu przechwytywania myszy"),
("rel-mouse-not-ready-tip", "Tryb przechwytywania myszy nie jest gotowy"),
("rel-mouse-lock-failed-tip", "Nie udało się przechwycić kursora myszy"),
("rel-mouse-exit-{}-tip", "Aby wyłączyć tryb przechwytywania myszy, naciśnij {}"),
("rel-mouse-permission-lost-tip", "Utracono uprawnienia do trybu przechwytywania myszy"),
("Changelog", "Dziennik zmian"),
("keep-awake-during-outgoing-sessions-label", "Utrzymuj urządzenie w stanie aktywnym podczas sesji wychodzących"),
("keep-awake-during-incoming-sessions-label", "Utrzymuj urządzenie w stanie aktywnym podczas sesji przychodzących"),
("Continue with {}", "Kontynuuj z {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", ""),
("This PC", ""),
("or", ""),
("Continue with", ""),
("Elevate", ""),
("Zoom cursor", ""),
("Accept sessions via password", ""),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", ""),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Exibir RustDesk"),
("This PC", "Este Computador"),
("or", "ou"),
("Continue with", "Continuar com"),
("Elevate", "Elevar"),
("Zoom cursor", "Aumentar cursor"),
("Accept sessions via password", "Aceitar sessões via senha"),
@@ -672,69 +671,73 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("remote-printing-disallowed-text-tip", "As configurações do dispositivo controlado não permitem impressão remota."),
("save-settings-tip", "Salvar configurações"),
("dont-show-again-tip", "Não mostrar novamente"),
("Take screenshot", ""),
("Taking screenshot", ""),
("screenshot-merged-screen-not-supported-tip", ""),
("screenshot-action-tip", ""),
("Save as", ""),
("Copy to clipboard", ""),
("Enable remote printer", ""),
("Downloading {}", ""),
("{} Update", ""),
("{}-to-update-tip", ""),
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
("Trackpad speed", ""),
("Default trackpad speed", ""),
("Numeric one-time password", ""),
("Enable IPv6 P2P connection", ""),
("Enable UDP hole punching", ""),
("Take screenshot", "Capturar de tela"),
("Taking screenshot", "Capturando tela"),
("screenshot-merged-screen-not-supported-tip", "Mesclar a captura de tela de múltiplos monitores não é suportada no momento. Por favor, alterne para um único monitor e tente novamente."),
("screenshot-action-tip", "Por favor, selecione como seguir com a captura de tela."),
("Save as", "Salvar como"),
("Copy to clipboard", "Copiar para área de transferência"),
("Enable remote printer", "Habilitar impressora remota"),
("Downloading {}", "Baixando {}"),
("{} Update", "Atualização do {}"),
("{}-to-update-tip", "{} será fechado agora para instalar a nova versão."),
("download-new-version-failed-tip", "Falha no download. Você pode tentar novamente ou clicar no botão \"Download\" para baixar da página releases e atualizar manualmente."),
("Auto update", "Atualização automática"),
("update-failed-check-msi-tip", "Falha na verificação do método de instalação. Clique no botão \"Download\" para baixar da página releases e atualizar manualmente."),
("websocket_tip", "Usando WebSocket, apenas conexões via relay são suportadas."),
("Use WebSocket", "Usar WebSocket"),
("Trackpad speed", "Velocidade do trackpad"),
("Default trackpad speed", "Velocidade padrão do trackpad"),
("Numeric one-time password", "Senha numérica de uso único"),
("Enable IPv6 P2P connection", "Habilitar conexão IPv6 P2P"),
("Enable UDP hole punching", "Habilitar UDP hole punching"),
("View camera", "Visualizar câmera"),
("Enable camera", "Ativar câmera"),
("No cameras", "Sem câmeras"),
("view_camera_unsupported_tip", "O dispositivo remoto não suporta visualização da câmera."),
("Terminal", ""),
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only in the installed version.", ""),
("elevation_username_tip", ""),
("Preparing for installation ...", ""),
("Show my cursor", ""),
("Terminal", "Terminal"),
("Enable terminal", "Habilitar Terminal"),
("New tab", "Nova aba"),
("Keep terminal sessions on disconnect", "Manter sessões de terminal ao desconectar"),
("Terminal (Run as administrator)", "Terminal (Executar como administrador)"),
("terminal-admin-login-tip", "Insira o nome do usuário e senha de administrador do dispositivo controlado."),
("Failed to get user token.", "Falha ao obter token do usuário."),
("Incorrect username or password.", "Usuário ou senha incorretos"),
("The user is not an administrator.", "O usuário não é administrador"),
("Failed to check if the user is an administrator.", "Falha ao verificar se o usuário é administrador"),
("Supported only in the installed version.", "Funciona somente na versão instalada"),
("elevation_username_tip", "Insira o nome do usuário ou domínio\\usuário"),
("Preparing for installation ...", "Preparando para instalação ..."),
("Show my cursor", "Mostrar meu cursor"),
("Scale custom", "Escala personalizada"),
("Custom scale slider", "Controle deslizante de escala personalizada"),
("Decrease", "Diminuir"),
("Increase", "Aumentar"),
("Show virtual mouse", ""),
("Virtual mouse size", ""),
("Small", ""),
("Large", ""),
("Show virtual joystick", ""),
("Edit note", ""),
("Alias", ""),
("ScrollEdge", ""),
("Allow insecure TLS fallback", ""),
("allow-insecure-tls-fallback-tip", ""),
("Disable UDP", ""),
("disable-udp-tip", ""),
("server-oss-not-support-tip", ""),
("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", ""),
("Show virtual mouse", "Mostrar mouse virtual"),
("Virtual mouse size", "Tamanho do mouse virtual"),
("Small", "Pequeno"),
("Large", "Grande"),
("Show virtual joystick", "Mostrar joystick virtual"),
("Edit note", "Editar nota"),
("Alias", "Apelido"),
("ScrollEdge", "Rolagem nas bordas"),
("Allow insecure TLS fallback", "Permitir fallback TLS inseguro"),
("allow-insecure-tls-fallback-tip", "Por padrão, o RustDesk verifica o certificado do servidor para protocolos que usam TLS.\nCom esta opção habilitada, o RustDesk ignorará a verificação e prosseguirá em caso de falha."),
("Disable UDP", "Desabilitar UDP"),
("disable-udp-tip", "Controla se deve usar somente TCP.\nCom esta opção habilitada, o RustDesk não usará mais UDP 21116, TCP 21116 será usado no lugar."),
("server-oss-not-support-tip", "NOTA: O servidor RustDesk OSS não inclui este recurso."),
("input note here", "Insira uma nota aqui"),
("note-at-conn-end-tip", "Solicitar nota ao final da conexão"),
("Show terminal extra keys", "Mostrar teclas extras do terminal"),
("Relative mouse mode", "Modo de Mouse Relativo"),
("rel-mouse-not-supported-peer-tip", "O Modo de Mouse Relativo não é suportado pelo parceiro conectado."),
("rel-mouse-not-ready-tip", "O Modo de Mouse Relativo ainda não está pronto. Por favor, tente novamente."),
("rel-mouse-lock-failed-tip", "Falha ao bloquear o cursor. O Modo de Mouse Relativo foi desabilitado."),
("rel-mouse-exit-{}-tip", "Pressione {} para sair."),
("rel-mouse-permission-lost-tip", "Permissão de teclado revogada. O Modo Mouse Relativo foi desabilitado."),
("Changelog", "Registro de alterações"),
("keep-awake-during-outgoing-sessions-label", "Manter tela ativa durante sessões de saída"),
("keep-awake-during-incoming-sessions-label", "Manter tela ativa durante sessões de entrada"),
("Continue with {}", "Continuar com {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Afișează RustDesk"),
("This PC", "Acest PC"),
("or", "sau"),
("Continue with", "Continuă cu"),
("Elevate", "Sporește privilegii"),
("Zoom cursor", "Cursor lupă"),
("Accept sessions via password", "Acceptă începerea sesiunii folosind parola"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Continuă cu {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Показать RustDesk"),
("This PC", "Этот компьютер"),
("or", "или"),
("Continue with", "Продолжить с"),
("Elevate", "Повысить"),
("Zoom cursor", "Масштабировать курсор"),
("Accept sessions via password", "Принимать сеансы по паролю"),
@@ -730,11 +729,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("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", "Не отключать экран во время входящих сеансов"),
("Continue with {}", "Продолжить с {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Mustra RustDesk"),
("This PC", "Custu PC"),
("or", "O"),
("Continue with", "Sighi cun"),
("Elevate", "Cresche"),
("Zoom cursor", "Cursore de ismanniamentu"),
("Accept sessions via password", "Atzeta sessiones cun sa crae"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Sighi cun {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Zobraziť RustDesk"),
("This PC", "Tento počítač"),
("or", "alebo"),
("Continue with", "Pokračovať s"),
("Elevate", "Zvýšiť"),
("Zoom cursor", "Kurzor priblíženia"),
("Accept sessions via password", "Prijímanie relácií pomocou hesla"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Pokračovať s {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Prikaži RustDesk"),
("This PC", "Ta računalnik"),
("or", "ali"),
("Continue with", "Nadaljuj z"),
("Elevate", "Povzdig pravic"),
("Zoom cursor", "Prilagodi velikost miškinega kazalca"),
("Accept sessions via password", "Sprejmi seje z geslom"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Nadaljuj z {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Shfaq RustDesk"),
("This PC", "Ky PC"),
("or", "ose"),
("Continue with", "Vazhdo me"),
("Elevate", "Ngritja"),
("Zoom cursor", "Zmadho kursorin"),
("Accept sessions via password", "Prano sesionin nëpërmjet fjalëkalimit"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Vazhdo me {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Prikazi RustDesk"),
("This PC", "Ovaj PC"),
("or", "ili"),
("Continue with", "Nastavi sa"),
("Elevate", "Izdigni"),
("Zoom cursor", "Zumiraj kursor"),
("Accept sessions via password", "Prihvati sesije preko lozinke"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Nastavi sa {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Visa RustDesk"),
("This PC", "Denna dator"),
("or", "eller"),
("Continue with", "Fortsätt med"),
("Elevate", "Höj upp"),
("Zoom cursor", "Zoom"),
("Accept sessions via password", "Acceptera sessioner via lösenord"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Fortsätt med {}"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "RustDesk ஐ காட்டு"),
("This PC", "இந்த PC"),
("or", "அல்லது"),
("Continue with", "உடன் தொடர்"),
("Elevate", "உயர்த்து"),
("Zoom cursor", "கர்சரை பெரிதாக்கு"),
("Accept sessions via password", "கடவுச்சொல் வழியாக அமர்வுகளை ஏற்று"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "{} உடன் தொடர்"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", ""),
("This PC", ""),
("or", ""),
("Continue with", ""),
("Elevate", ""),
("Zoom cursor", ""),
("Accept sessions via password", ""),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", ""),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "แสดง RustDesk"),
("This PC", "พีซีเครื่องนี้"),
("or", "หรือ"),
("Continue with", "ทำต่อด้วย"),
("Elevate", "ยกระดับ"),
("Zoom cursor", "ขยายเคอร์เซอร์"),
("Accept sessions via password", "ยอมรับการเชื่อมต่อด้วยรหัสผ่าน"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "ทำต่อด้วย {}"),
].iter().cloned().collect();
}

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", ""),
("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 şifreyide 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"),
@@ -366,7 +366,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop session recording", "Oturum kaydını sonlandır"),
("Enable recording session", "Kayıt Oturumunu Aktif Et"),
("Enable LAN discovery", "Yerel Ağ Keşfine İzin Ver"),
("Deny LAN discovery", "Yerl Ağ Keşfine İzin Verme"),
("Deny LAN discovery", "Yerel Ağ Keşfine İzin Verme"),
("Write a message", "Bir mesaj yazın"),
("Prompt", "İstem"),
("Please wait for confirmation of UAC...", "UAC onayı için lütfen bekleyiniz..."),
@@ -384,16 +384,15 @@ 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"),
("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 +441,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 +476,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 +494,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 +533,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 +558,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"),
@@ -568,7 +567,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("input_source_1_tip", "Giriş kaynağı 1"),
("input_source_2_tip", "Giriş kaynağı 2"),
("Swap control-command key", "Kontrol-komut tuşunu değiştir"),
("swap-left-right-mouse", "sol-sağ fareyi değiştir"),
("swap-left-right-mouse", "Sol-sağ fare tuşlarını değiştir"),
("2FA code", "2FA kodu"),
("More", "Daha"),
("enable-2fa-title", "İki faktörlü kimlik doğrulamayı etkinleştir"),
@@ -581,12 +580,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"),
@@ -598,8 +597,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("no_need_privacy_mode_no_physical_displays_tip", "Fiziksel ekran yok, gizlilik modunu kullanmaya gerek yok."),
("Follow remote cursor", "Uzak imleci takip et"),
("Follow remote window focus", "Uzak pencere odağını takip et"),
("default_proxy_tip", ""),
("no_audio_input_device_tip", "Varsayılan protokol ve port, Socks5 ve 1080'dir"),
("default_proxy_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 +611,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 +619,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 +641,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 +660,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,32 +684,32 @@ 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"),
("Enable camera", "Kamerayı etkinleştir"),
("No cameras", "Kamera yok"),
("view_camera_unsupported_tip", "Uzak cihaz, kameranın görüntülenmesini desteklemiyor."),
("Terminal", ""),
("Enable terminal", ""),
("Terminal", "Terminal"),
("Enable terminal", "Terminali etkinleştir"),
("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"),
@@ -730,11 +729,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("input note here", "Notu buraya girin"),
("note-at-conn-end-tip", "Bağlantı bittiğinde not sorulsun"),
("Show terminal extra keys", "Terminal ek tuşlarını göster"),
("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", ""),
("Relative mouse mode", "Fareyi göreli modda kullan"),
("rel-mouse-not-supported-peer-tip", "Karşı taraf göreli fare modunu desteklemiyor"),
("rel-mouse-not-ready-tip", "Göreli fare modu henüz hazır değil"),
("rel-mouse-lock-failed-tip", "Göreli fare kilitlenemedi"),
("rel-mouse-exit-{}-tip", "Göreli fare modundan çıkmak için {}"),
("rel-mouse-permission-lost-tip", "Göreli fare izinleri geçerli değil"),
("Changelog", "Değişiklik Günlüğü"),
("keep-awake-during-outgoing-sessions-label", "Giden oturumlar süresince ekranıık tutun"),
("keep-awake-during-incoming-sessions-label", "Gelen oturumlar süresince ekranıık tutun"),
("Continue with {}", "{} ile devam et"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "顯示 RustDesk"),
("This PC", "此電腦"),
("or", ""),
("Continue with", "繼續"),
("Elevate", "提升權限"),
("Zoom cursor", "縮放游標"),
("Accept sessions via password", "只允許透過輸入密碼進行連線"),
@@ -729,12 +728,16 @@ 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", ""),
("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", "在連入工作階段期間保持螢幕喚醒"),
("Continue with {}", "使用 {} 登入"),
].iter().cloned().collect();
}

View File

@@ -384,7 +384,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Show RustDesk", "Показати RustDesk"),
("This PC", "Цей ПК"),
("or", "чи"),
("Continue with", "Продовжити з"),
("Elevate", "Розширення прав"),
("Zoom cursor", "Збільшити вказівник"),
("Accept sessions via password", "Підтверджувати сеанси паролем"),
@@ -736,5 +735,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("Continue with {}", "Продовжити з {}"),
].iter().cloned().collect();
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,29 +6,26 @@ use hbb_common::{
anyhow::anyhow,
bail,
config::{keys::OPTION_ALLOW_LINUX_HEADLESS, Config},
libc::{c_char, c_int, c_long, c_void},
libc::{c_char, c_int, c_long, c_uint, c_void},
log,
message_proto::{DisplayInfo, Resolution},
regex::{Captures, Regex},
users::{get_user_by_name, os::unix::UserExt},
};
use libxdo_sys::{self, xdo_t, Window};
use std::{
cell::RefCell,
ffi::{OsStr, OsString},
path::{Path, PathBuf},
process::{Child, Command},
string::String,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
sync::atomic::{AtomicBool, Ordering},
sync::Arc,
time::{Duration, Instant},
};
use terminfo::{capability as cap, Database};
use wallpaper;
type Xdo = *const c_void;
pub const PA_SAMPLE_RATE: u32 = 48000;
static mut UNMODIFIED: bool = true;
@@ -86,35 +83,20 @@ lazy_static::lazy_static! {
}
thread_local! {
static XDO: RefCell<Xdo> = RefCell::new(unsafe { xdo_new(std::ptr::null()) });
// XDO context - created via libxdo-sys (which uses dynamic loading stub).
// If libxdo is not available, xdo will be null and xdo-based functions become no-ops.
static XDO: RefCell<*mut xdo_t> = RefCell::new({
let xdo = unsafe { libxdo_sys::xdo_new(std::ptr::null()) };
if xdo.is_null() {
log::warn!("Failed to create xdo context, xdo functions will be disabled");
} else {
log::info!("xdo context created successfully");
}
xdo
});
static DISPLAY: RefCell<*mut c_void> = RefCell::new(unsafe { XOpenDisplay(std::ptr::null())});
}
extern "C" {
fn xdo_get_mouse_location(
xdo: Xdo,
x: *mut c_int,
y: *mut c_int,
screen_num: *mut c_int,
) -> c_int;
fn xdo_move_mouse(xdo: Xdo, x: c_int, y: c_int, screen: c_int) -> c_int;
fn xdo_new(display: *const c_char) -> Xdo;
fn xdo_get_active_window(xdo: Xdo, window: *mut *mut c_void) -> c_int;
fn xdo_get_window_location(
xdo: Xdo,
window: *mut c_void,
x: *mut c_int,
y: *mut c_int,
screen_num: *mut c_int,
) -> c_int;
fn xdo_get_window_size(
xdo: Xdo,
window: *mut c_void,
width: *mut c_int,
height: *mut c_int,
) -> c_int;
}
#[link(name = "X11")]
extern "C" {
fn XOpenDisplay(display_name: *const c_char) -> *mut c_void;
@@ -160,14 +142,19 @@ fn sleep_millis(millis: u64) {
pub fn get_cursor_pos() -> Option<(i32, i32)> {
let mut res = None;
XDO.with(|xdo| {
if let Ok(xdo) = xdo.try_borrow_mut() {
if let Ok(xdo) = xdo.try_borrow() {
if xdo.is_null() {
return;
}
let mut x: c_int = 0;
let mut y: c_int = 0;
unsafe {
xdo_get_mouse_location(*xdo, &mut x as _, &mut y as _, std::ptr::null_mut());
libxdo_sys::xdo_get_mouse_location(
*xdo as *const _,
&mut x as _,
&mut y as _,
std::ptr::null_mut(),
);
}
res = Some((x, y));
}
@@ -178,14 +165,14 @@ pub fn get_cursor_pos() -> Option<(i32, i32)> {
pub fn set_cursor_pos(x: i32, y: i32) -> bool {
let mut res = false;
XDO.with(|xdo| {
match xdo.try_borrow_mut() {
match xdo.try_borrow() {
Ok(xdo) => {
if xdo.is_null() {
log::debug!("set_cursor_pos: xdo is null");
return;
}
unsafe {
let ret = xdo_move_mouse(*xdo, x, y, 0);
let ret = libxdo_sys::xdo_move_mouse(*xdo as *const _, x, y, 0);
if ret != 0 {
log::debug!(
"set_cursor_pos: xdo_move_mouse failed with code {} for coordinates ({}, {})",
@@ -230,22 +217,22 @@ pub fn reset_input_cache() {}
pub fn get_focused_display(displays: Vec<DisplayInfo>) -> Option<usize> {
let mut res = None;
XDO.with(|xdo| {
if let Ok(xdo) = xdo.try_borrow_mut() {
if let Ok(xdo) = xdo.try_borrow() {
if xdo.is_null() {
return;
}
let mut x: c_int = 0;
let mut y: c_int = 0;
let mut width: c_int = 0;
let mut height: c_int = 0;
let mut window: *mut c_void = std::ptr::null_mut();
let mut width: c_uint = 0;
let mut height: c_uint = 0;
let mut window: Window = 0;
unsafe {
if xdo_get_active_window(*xdo, &mut window) != 0 {
if libxdo_sys::xdo_get_active_window(*xdo as *const _, &mut window) != 0 {
return;
}
if xdo_get_window_location(
*xdo,
if libxdo_sys::xdo_get_window_location(
*xdo as *const _,
window,
&mut x as _,
&mut y as _,
@@ -254,11 +241,17 @@ pub fn get_focused_display(displays: Vec<DisplayInfo>) -> Option<usize> {
{
return;
}
if xdo_get_window_size(*xdo, window, &mut width as _, &mut height as _) != 0 {
if libxdo_sys::xdo_get_window_size(
*xdo as *const _,
window,
&mut width,
&mut height,
) != 0
{
return;
}
let center_x = x + width / 2;
let center_y = y + height / 2;
let center_x = x + (width / 2) as c_int;
let center_y = y + (height / 2) as c_int;
res = displays.iter().position(|d| {
center_x >= d.x
&& center_x < d.x + d.width
@@ -497,7 +490,10 @@ fn get_all_term_values(uid: &str) -> Vec<String> {
let Ok(cmdline) = std::fs::read(&cmdline_path) else {
continue;
};
let exe_end = cmdline.iter().position(|&b| b == 0).unwrap_or(cmdline.len());
let exe_end = cmdline
.iter()
.position(|&b| b == 0)
.unwrap_or(cmdline.len());
let exe_str = String::from_utf8_lossy(&cmdline[..exe_end]);
if !re.is_match(&exe_str) {
continue;
@@ -2092,3 +2088,122 @@ pub fn is_selinux_enforcing() -> bool {
},
}
}
/// Get the app ID for shortcuts inhibitor permission.
/// Returns different ID based on whether running in Flatpak or native.
/// The ID must match the installed .desktop filename, as GNOME Shell's
/// inhibitShortcutsDialog uses `Shell.WindowTracker.get_window_app(window).get_id()`.
fn get_shortcuts_inhibitor_app_id() -> String {
if is_flatpak() {
// In Flatpak, FLATPAK_ID is set automatically by the runtime to the app ID
// (e.g., "com.rustdesk.RustDesk"). This is the most reliable source.
// Fall back to constructing from app name if not available.
match std::env::var("FLATPAK_ID") {
Ok(id) if !id.is_empty() => format!("{}.desktop", id),
_ => {
let app_name = crate::get_app_name();
format!("com.{}.{}.desktop", app_name.to_lowercase(), app_name)
}
}
} else {
format!("{}.desktop", crate::get_app_name().to_lowercase())
}
}
const PERMISSION_STORE_DEST: &str = "org.freedesktop.impl.portal.PermissionStore";
const PERMISSION_STORE_PATH: &str = "/org/freedesktop/impl/portal/PermissionStore";
const PERMISSION_STORE_IFACE: &str = "org.freedesktop.impl.portal.PermissionStore";
/// Clear GNOME shortcuts inhibitor permission via D-Bus.
/// This allows the permission dialog to be shown again.
pub fn clear_gnome_shortcuts_inhibitor_permission() -> ResultType<()> {
let app_id = get_shortcuts_inhibitor_app_id();
log::info!(
"Clearing shortcuts inhibitor permission for app_id: {}, is_flatpak: {}",
app_id,
is_flatpak()
);
let conn = dbus::blocking::Connection::new_session()?;
let proxy = conn.with_proxy(
PERMISSION_STORE_DEST,
PERMISSION_STORE_PATH,
std::time::Duration::from_secs(3),
);
// DeletePermission(s table, s id, s app) -> ()
let result: Result<(), dbus::Error> = proxy.method_call(
PERMISSION_STORE_IFACE,
"DeletePermission",
("gnome", "shortcuts-inhibitor", app_id.as_str()),
);
match result {
Ok(()) => {
log::info!("Successfully cleared GNOME shortcuts inhibitor permission");
Ok(())
}
Err(e) => {
let err_name = e.name().unwrap_or("");
// If the permission doesn't exist, that's also fine
if err_name == "org.freedesktop.portal.Error.NotFound"
|| err_name == "org.freedesktop.DBus.Error.UnknownObject"
|| err_name == "org.freedesktop.DBus.Error.ServiceUnknown"
{
log::info!("GNOME shortcuts inhibitor permission was not set ({})", err_name);
Ok(())
} else {
bail!("Failed to clear permission: {}", e)
}
}
}
}
/// Check if GNOME shortcuts inhibitor permission exists.
pub fn has_gnome_shortcuts_inhibitor_permission() -> bool {
let app_id = get_shortcuts_inhibitor_app_id();
let conn = match dbus::blocking::Connection::new_session() {
Ok(c) => c,
Err(e) => {
log::debug!("Failed to connect to session bus: {}", e);
return false;
}
};
let proxy = conn.with_proxy(
PERMISSION_STORE_DEST,
PERMISSION_STORE_PATH,
std::time::Duration::from_secs(3),
);
// Lookup(s table, s id) -> (a{sas} permissions, v data)
// We only need the permissions dict; check if app_id is a key.
let result: Result<
(
std::collections::HashMap<String, Vec<String>>,
dbus::arg::Variant<Box<dyn dbus::arg::RefArg>>,
),
dbus::Error,
> = proxy.method_call(
PERMISSION_STORE_IFACE,
"Lookup",
("gnome", "shortcuts-inhibitor"),
);
match result {
Ok((permissions, _)) => {
let found = permissions.contains_key(&app_id);
log::debug!(
"Shortcuts inhibitor permission lookup: app_id={}, found={}, keys={:?}",
app_id,
found,
permissions.keys().collect::<Vec<_>>()
);
found
}
Err(e) => {
log::debug!("Failed to query shortcuts inhibitor permission: {}", e);
false
}
}
}

Some files were not shown because too many files have changed in this diff Show More