This commit is contained in:
csf 2023-01-09 14:51:58 +09:00
commit 6c577dc117
76 changed files with 1266 additions and 244 deletions

View File

@ -142,13 +142,42 @@ jobs:
job:
- {
target: x86_64-apple-darwin,
os: macos-10.15,
os: macos-latest,
extra-build-args: "",
}
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Import the codesign cert
uses: apple-actions/import-codesign-certs@v1
with:
p12-file-base64: ${{ secrets.MACOS_P12_BASE64 }}
p12-password: ${{ secrets.MACOS_P12_PASSWORD }}
keychain: rustdesk
- name: Check sign and import sign key
run: |
security default-keychain -s rustdesk.keychain
security find-identity -v
- name: Import notarize key
uses: timheuer/base64-to-file@v1.2
with:
# https://gregoryszorc.com/docs/apple-codesign/stable/apple_codesign_rcodesign.html#notarizing-and-stapling
fileName: rustdesk.json
fileDir: ${{ github.workspace }}
encodedString: ${{ secrets.MACOS_NOTARIZE_JSON }}
- name: Install rcodesign tool
shell: bash
run: |
pushd /tmp
wget https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz
tar -zxvf apple-codesign-0.22.0-macos-universal.tar.gz
mv apple-codesign-0.22.0-macos-universal/rcodesign /usr/local/bin
popd
- name: Install build runtime
run: |
brew install llvm create-dmg nasm yasm cmake gcc wget ninja
@ -158,7 +187,6 @@ jobs:
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
@ -177,8 +205,12 @@ jobs:
run: |
dart pub global activate ffigen --version 5.0.1
# flutter_rust_bridge
pushd /tmp && git clone https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge --depth=1 && popd
pushd /tmp/flutter_rust_bridge/frb_codegen && cargo install --path . && popd
pushd /tmp
wget https://github.com/Kingtous/flutter_rust_bridge/releases/download/1.32.0-rustdesk/flutter_rust_bridge_codegen-x86_64-darwin.tgz
tar -zxvf flutter_rust_bridge_codegen-x86_64-darwin.tgz
mkdir -p ~/.cargo/bin
mv flutter_rust_bridge_codegen ~/.cargo/bin; chmod +x ~/.cargo/bin/flutter_rust_bridge_codegen
popd
pushd flutter && flutter pub get && popd
~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart
@ -192,10 +224,6 @@ jobs:
run: |
$VCPKG_ROOT/vcpkg install libvpx libyuv opus
- name: Install cargo bundle tools
run: |
cargo install cargo-bundle
- name: Show version information (Rust, cargo, Clang)
shell: bash
run: |
@ -211,6 +239,18 @@ jobs:
# --hwcodec not supported on macos yet
./build.py --flutter ${{ matrix.job.extra-build-args }}
- name: Codesign app and create signed dmg
run: |
security default-keychain -s rustdesk.keychain
security unlock-keychain -p ${{ secrets.MACOS_P12_PASSWORD }} rustdesk.keychain
# start sign the rustdesk.app and dmg
rm rustdesk-${{ env.VERSION }}.dmg || true
codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep ./flutter/build/macos/Build/Products/Release/rustdesk.app -v
create-dmg --icon "rustdesk.app" 200 190 --hide-extension "rustdesk.app" --window-size 800 400 --app-drop-link 600 185 rustdesk-${{ env.VERSION }}.dmg ./flutter/build/macos/Build/Products/Release/rustdesk.app
codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep rustdesk-${{ env.VERSION }}.dmg -v
# notarize the rustdesk-${{ env.VERSION }}.dmg
rcodesign notary-submit --api-key-path ${{ github.workspace }}/rustdesk.json --staple rustdesk-${{ env.VERSION }}.dmg
- name: Rename rustdesk
run: |
for name in rustdesk*??.dmg; do
@ -559,6 +599,12 @@ jobs:
os: ubuntu-20.04,
extra-build-features: "flatpak",
}
- {
arch: x86_64,
target: x86_64-unknown-linux-gnu,
os: ubuntu-20.04,
extra-build-features: "appimage",
}
# - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
steps:
- name: Maximize build space
@ -1108,6 +1154,12 @@ jobs:
os: ubuntu-18.04,
extra-build-features: "flatpak",
}
- {
arch: x86_64,
target: x86_64-unknown-linux-gnu,
os: ubuntu-18.04,
extra-build-features: "appimage",
}
# - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
steps:
- name: Checkout source code
@ -1122,7 +1174,7 @@ jobs:
- name: Prepare env
run: |
sudo apt update -y
sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev
sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev libarchive-tools
mkdir -p ./target/release/
- name: Restore the rustdesk lib file
@ -1177,10 +1229,12 @@ jobs:
shell: bash
run: |
for name in rustdesk*??.deb; do
mv "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb"
# use cp to duplicate deb files to fit other packages.
cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb"
done
- name: Publish debian package
if: ${{ matrix.job.extra-build-features == '' }}
uses: softprops/action-gh-release@v1
with:
prerelease: true
@ -1244,6 +1298,29 @@ jobs:
files: |
res/rustdesk*.zst
- name: Build appimage package
if: ${{ matrix.job.extra-build-features == 'appimage' }}
shell: bash
run: |
# set-up appimage-builder
pushd /tmp
wget -O appimage-builder-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage
chmod +x appimage-builder-x86_64.AppImage
sudo mv appimage-builder-x86_64.AppImage /usr/local/bin/appimage-builder
popd
# run appimage-builder
pushd appimage
sudo appimage-builder --skip-tests
- name: Publish appimage package
if: ${{ matrix.job.extra-build-features == 'appimage' }}
uses: softprops/action-gh-release@v1
with:
prerelease: true
tag_name: ${{ env.TAG_NAME }}
files: |
./appimage/rustdesk-${{ env.VERSION }}-*.AppImage
- name: Publish fedora28/centos8 package
if: ${{ matrix.job.extra-build-features == '' }}
uses: softprops/action-gh-release@v1

1
Cargo.lock generated
View File

@ -4562,6 +4562,7 @@ dependencies = [
"arboard",
"async-process",
"async-trait",
"backtrace",
"base64",
"bytes",
"cc",

View File

@ -119,6 +119,7 @@ dbus-crossroads = "0.5"
gtk = "0.15"
libappindicator = "0.7"
glib = "0.16.5"
backtrace = "0.3"
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.11"

View File

@ -0,0 +1,87 @@
# appimage-builder recipe see https://appimage-builder.readthedocs.io for details
version: 1
script:
- rm -rf ./AppDir || true
- bsdtar -zxvf ../rustdesk-1.2.0.deb
- tar -xvf ./data.tar.xz
- mkdir ./AppDir
- mv ./usr ./AppDir/usr
# 32x32 icon
- for i in {32,64,128}; do mkdir -p ./AppDir/usr/share/icons/hicolor/$i\x$i/apps/; cp ../res/$i\x$i.png ./AppDir/usr/share/icons/hicolor/$i\x$i/apps/rustdesk.png; done
# desktop file
# - sed -i "s/Icon=\/usr\/share\/rustdesk\/files\/rustdesk.png/Icon=rustdesk/g" ./AppDir/usr/share/applications/rustdesk.desktop
- rm -rf ./AppDir/usr/share/applications
AppDir:
path: ./AppDir
app_info:
id: rustdesk
name: rustdesk
icon: rustdesk
version: 1.2.0
exec: usr/lib/rustdesk/rustdesk
exec_args: $@
apt:
arch:
- amd64
allow_unauthenticated: true
sources:
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-backports main restricted
universe multiverse
- sourceline: deb http://ppa.launchpad.net/pipewire-debian/pipewire-upstream/ubuntu
bionic main
include:
- libc6:amd64
- libgtk-3-0
- libxcb-randr0
- libxdo3
- libxfixes3
- libxcb-shape0
- libxcb-xfixes0
- libasound2
- libsystemd0
- curl
- libva-drm2
- libva-x11-2
- libvdpau1
- libgstreamer-plugins-base1.0-0
exclude:
- humanity-icon-theme
- hicolor-icon-theme
- adwaita-icon-theme
- ubuntu-mono
files:
include: []
exclude:
- usr/share/man
- usr/share/doc/*/README.*
- usr/share/doc/*/changelog.*
- usr/share/doc/*/NEWS.*
- usr/share/doc/*/TODO.*
runtime:
env:
GIO_MODULE_DIR: $APPDIR/usr/lib/x86_64-linux-gnu/gio/modules/
test:
fedora-30:
image: appimagecrafters/tests-env:fedora-30
command: ./AppRun
debian-stable:
image: appimagecrafters/tests-env:debian-stable
command: ./AppRun
archlinux-latest:
image: appimagecrafters/tests-env:archlinux-latest
command: ./AppRun
centos-7:
image: appimagecrafters/tests-env:centos-7
command: ./AppRun
ubuntu-xenial:
image: appimagecrafters/tests-env:ubuntu-xenial
command: ./AppRun
AppImage:
arch: x86_64
update-information: guess

View File

@ -99,6 +99,11 @@ def make_parser():
action='store_true',
help='Build rustdesk libs with the flatpak feature enabled'
)
parser.add_argument(
'--appimage',
action='store_true',
help='Build rustdesk libs with the appimage feature enabled'
)
parser.add_argument(
'--skip-cargo',
action='store_true',
@ -236,6 +241,8 @@ def get_features(args):
features.append('flutter')
if args.flatpak:
features.append('flatpak')
if args.appimage:
features.append('appimage')
print("features:", features)
return features
@ -305,7 +312,8 @@ def build_flutter_deb(version, features):
def build_flutter_dmg(version, features):
if not skip_cargo:
os.system(f'cargo build --features {features} --lib --release')
# set minimum osx build target, now is 10.14, which is the same as the flutter xcode project
os.system(f'MACOSX_DEPLOYMENT_TARGET=10.14 cargo build --features {features} --lib --release')
# copy dylib
os.system(
"cp target/release/liblibrustdesk.dylib target/release/librustdesk.dylib")
@ -469,6 +477,7 @@ def main():
if pa:
os.system('''
# buggy: rcodesign sign ... path/*, have to sign one by one
# install rcodesign via cargo install apple-codesign
#rcodesign sign --p12-file ~/.p12/rustdesk-developer-id.p12 --p12-password-file ~/.p12/.cert-pass --code-signature-flags runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/rustdesk
#rcodesign sign --p12-file ~/.p12/rustdesk-developer-id.p12 --p12-password-file ~/.p12/.cert-pass --code-signature-flags runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/libsciter.dylib
#rcodesign sign --p12-file ~/.p12/rustdesk-developer-id.p12 --p12-password-file ~/.p12/.cert-pass --code-signature-flags runtime ./target/release/bundle/osx/RustDesk.app
@ -481,9 +490,15 @@ def main():
version, 'rustdesk-%s.dmg' % version)
if pa:
os.system('''
# https://pyoxidizer.readthedocs.io/en/apple-codesign-0.14.0/apple_codesign.html
# https://pyoxidizer.readthedocs.io/en/stable/tugger_code_signing.html
# https://developer.apple.com/developer-id/
# goto xcode and login with apple id, manager certificates (Developer ID Application and/or Developer ID Installer) online there (only download and double click (install) cer file can not export p12 because no private key)
#rcodesign sign --p12-file ~/.p12/rustdesk-developer-id.p12 --p12-password-file ~/.p12/.cert-pass --code-signature-flags runtime ./rustdesk-{1}.dmg
codesign -s "Developer ID Application: {0}" --force --options runtime ./rustdesk-{1}.dmg
# https://pyoxidizer.readthedocs.io/en/latest/apple_codesign_rcodesign.html
# https://appstoreconnect.apple.com/access/api
# https://gregoryszorc.com/docs/apple-codesign/0.16.0/apple_codesign_rcodesign.html#notarizing-and-stapling
# p8 file is generated when you generate api key, download and put it under ~/.private_keys/
rcodesign notarize --api-issuer {2} --api-key {3} --staple ./rustdesk-{1}.dmg
# verify: spctl -a -t exec -v /Applications/RustDesk.app
'''.format(pa, version, os.environ.get('api-issuer'), os.environ.get('api-key')))

View File

@ -1,9 +1,16 @@
#[cfg(windows)]
fn build_windows() {
cc::Build::new().file("src/windows.cc").compile("windows");
let file = "src/platform/windows.cc";
cc::Build::new().file(file).compile("windows");
println!("cargo:rustc-link-lib=WtsApi32");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=windows.cc");
println!("cargo:rerun-if-changed={}", file);
}
#[cfg(target_os = "macos")]
fn build_mac() {
let file = "src/platform/macos.mm";
cc::Build::new().file(file).compile("macos");
println!("cargo:rerun-if-changed={}", file);
}
#[cfg(all(windows, feature = "inline"))]
@ -117,5 +124,8 @@ fn main() {
#[cfg(windows)]
build_windows();
#[cfg(target_os = "macos")]
build_mac();
#[cfg(target_os = "macos")]
println!("cargo:rustc-link-lib=framework=ApplicationServices");
println!("cargo:rerun-if-changed=build.rs");
}

1
flutter/.gitignore vendored
View File

@ -54,3 +54,4 @@ lib/generated_bridge.freezed.dart
flutter_export_environment.sh
Flutter-Generated.xcconfig
key.jks
macos/rustdesk.xcodeproj/project.xcworkspace/

View File

@ -1418,7 +1418,7 @@ bool isRunningInPortableMode() {
}
/// Window status callback
void onActiveWindowChanged() async {
Future<void> onActiveWindowChanged() async {
print(
"[MultiWindowHandler] active window changed: ${rustDeskWinManager.getActiveWindows()}");
if (rustDeskWinManager.getActiveWindows().isEmpty) {

View File

@ -100,6 +100,8 @@ const kRemoteImageQualityLow = 'low';
/// [kRemoteImageQualityCustom] Custom image quality.
const kRemoteImageQualityCustom = 'custom';
const kIgnoreDpi = true;
/// flutter/packages/flutter/lib/src/services/keyboard_key.dart -> _keyLabels
/// see [LogicalKeyboardKey.keyLabel]
const Map<int, String> logicalKeyMap = <int, String>{

View File

@ -6,7 +6,6 @@ import 'dart:io';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/widgets/address_book.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
import 'package:get/get.dart';
@ -16,7 +15,6 @@ import 'package:window_manager/window_manager.dart';
import '../../common.dart';
import '../../common/formatter/id_formatter.dart';
import '../../common/widgets/peer_tab_page.dart';
import '../../common/widgets/peers_view.dart';
import '../../models/platform_model.dart';
import '../widgets/button.dart';
@ -172,6 +170,7 @@ class _ConnectionPageState extends State<ConnectionPage>
Expanded(
child: Obx(
() => TextField(
maxLength: 90,
autocorrect: false,
enableSuggestions: false,
keyboardType: TextInputType.visiblePassword,
@ -179,12 +178,13 @@ class _ConnectionPageState extends State<ConnectionPage>
style: const TextStyle(
fontFamily: 'WorkSans',
fontSize: 22,
height: 1,
height: 1.25,
),
maxLines: 1,
cursorColor:
Theme.of(context).textTheme.titleLarge?.color,
decoration: InputDecoration(
counterText: '',
hintText: _idInputFocused.value
? null
: translate('Enter Remote ID'),

View File

@ -42,6 +42,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
var svcStopped = false.obs;
var watchIsCanScreenRecording = false;
var watchIsProcessTrust = false;
var watchIsInputMonitoring = false;
Timer? _updateTimer;
@override
@ -334,6 +335,12 @@ class _DesktopHomePageState extends State<DesktopHomePage>
bind.mainIsProcessTrusted(prompt: true);
watchIsProcessTrust = true;
}, help: 'Help', link: translate("doc_mac_permission"));
} else if (!bind.mainIsCanInputMonitoring(prompt: false)) {
return buildInstallCard("Permissions", "config_input", "Configure",
() async {
bind.mainIsCanInputMonitoring(prompt: true);
watchIsInputMonitoring = true;
}, help: 'Help', link: translate("doc_mac_permission"));
} else if (!svcStopped.value &&
bind.mainIsInstalled() &&
!bind.mainIsInstalledDaemon(prompt: false)) {
@ -467,6 +474,12 @@ class _DesktopHomePageState extends State<DesktopHomePage>
setState(() {});
}
}
if (watchIsInputMonitoring) {
if (bind.mainIsCanInputMonitoring(prompt: false)) {
watchIsInputMonitoring = false;
setState(() {});
}
}
});
Get.put<RxBool>(svcStopped, tag: 'stop-service');
rustDeskWinManager.registerActiveWindowListener(onActiveWindowChanged);
@ -500,9 +513,9 @@ class _DesktopHomePageState extends State<DesktopHomePage>
} else if (call.method == kWindowActionRebuild) {
reloadCurrentWindow();
} else if (call.method == kWindowEventShow) {
rustDeskWinManager.registerActiveWindow(call.arguments["id"]);
await rustDeskWinManager.registerActiveWindow(call.arguments["id"]);
} else if (call.method == kWindowEventHide) {
rustDeskWinManager.unregisterActiveWindow(call.arguments["id"]);
await rustDeskWinManager.unregisterActiveWindow(call.arguments["id"]);
} else if (call.method == kWindowConnect) {
await connectMainDesktop(
call.arguments['id'],

View File

@ -274,6 +274,15 @@ class _GeneralState extends State<_General> {
_OptionCheckBox(context, 'Confirm before closing multiple tabs',
'enable-confirm-closing-tabs'),
_OptionCheckBox(context, 'Adaptive Bitrate', 'enable-abr'),
if (Platform.isLinux)
Tooltip(
message: translate('software_render_tip'),
child: _OptionCheckBox(
context,
"Always use software rendering",
'allow-always-software-render',
),
)
]);
}
@ -1223,7 +1232,7 @@ Widget _OptionCheckBox(BuildContext context, String label, String key,
ref.value = option;
if (reverse) option = !option;
String value = bool2option(key, option);
bind.mainSetOption(key: key, value: value);
await bind.mainSetOption(key: key, value: value);
update?.call();
}
}

View File

@ -402,35 +402,36 @@ class _ImagePaintState extends State<ImagePaint> {
onHover: (evt) {},
child: child));
if (c.scrollStyle == ScrollStyle.scrollbar) {
if (c.imageOverflow.isTrue && c.scrollStyle == ScrollStyle.scrollbar) {
final imageWidth = c.getDisplayWidth() * s;
final imageHeight = c.getDisplayHeight() * s;
final imageSize = Size(imageWidth, imageHeight);
final imageWidget = CustomPaint(
size: Size(imageWidth, imageHeight),
size: imageSize,
painter: ImagePainter(image: m.image, x: 0, y: 0, scale: s),
);
return NotificationListener<ScrollNotification>(
onNotification: (notification) {
final percentX = _horizontal.hasClients
? _horizontal.position.extentBefore /
(_horizontal.position.extentBefore +
_horizontal.position.extentInside +
_horizontal.position.extentAfter)
: 0.0;
final percentY = _vertical.hasClients
? _vertical.position.extentBefore /
(_vertical.position.extentBefore +
_vertical.position.extentInside +
_vertical.position.extentAfter)
: 0.0;
c.setScrollPercent(percentX, percentY);
return false;
},
child: mouseRegion(
child: _buildCrossScrollbar(context, _buildListener(imageWidget),
Size(imageWidth, imageHeight))),
);
onNotification: (notification) {
final percentX = _horizontal.hasClients
? _horizontal.position.extentBefore /
(_horizontal.position.extentBefore +
_horizontal.position.extentInside +
_horizontal.position.extentAfter)
: 0.0;
final percentY = _vertical.hasClients
? _vertical.position.extentBefore /
(_vertical.position.extentBefore +
_vertical.position.extentInside +
_vertical.position.extentAfter)
: 0.0;
c.setScrollPercent(percentX, percentY);
return false;
},
child: mouseRegion(
child: Obx(() => _buildCrossScrollbarFromLayout(
context, _buildListener(imageWidget), c.size, imageSize)),
));
} else {
final imageWidget = CustomPaint(
size: Size(c.size.width, c.size.height),
@ -565,24 +566,6 @@ class _ImagePaintState extends State<ImagePaint> {
return widget;
}
Widget _buildCrossScrollbar(BuildContext context, Widget child, Size size) {
var layoutSize = MediaQuery.of(context).size;
// If minimized, w or h may be negative here.
final w = layoutSize.width - kWindowBorderWidth * 2;
final h =
layoutSize.height - kWindowBorderWidth * 2 - kDesktopRemoteTabBarHeight;
layoutSize = Size(
w < 0 ? 0 : w,
h < 0 ? 0 : h,
);
bool overflow =
layoutSize.width < size.width || layoutSize.height < size.height;
return overflow
? Obx(() =>
_buildCrossScrollbarFromLayout(context, child, layoutSize, size))
: _buildCrossScrollbarFromLayout(context, child, layoutSize, size);
}
Widget _buildListener(Widget child) {
if (listenerBuilder != null) {
return listenerBuilder!(child);

View File

@ -118,6 +118,15 @@ abstract class MenuEntryBase<T> {
this.enabled,
});
List<mod_menu.PopupMenuEntry<T>> build(BuildContext context, MenuConfig conf);
enabledStyle(BuildContext context) => TextStyle(
color: Theme.of(context).textTheme.titleLarge?.color,
fontSize: MenuConfig.fontSize,
fontWeight: FontWeight.normal);
disabledStyle() => TextStyle(
color: Colors.grey,
fontSize: MenuConfig.fontSize,
fontWeight: FontWeight.normal);
}
class MenuEntryDivider<T> extends MenuEntryBase<T> {
@ -189,54 +198,76 @@ class MenuEntryRadios<T> extends MenuEntryBase<T> {
mod_menu.PopupMenuEntry<T> _buildMenuItem(
BuildContext context, MenuConfig conf, MenuEntryRadioOption opt) {
Widget getTextChild() {
final enabledTextChild = Text(
opt.text,
style: enabledStyle(context),
);
final disabledTextChild = Text(
opt.text,
style: disabledStyle(),
);
if (opt.enabled == null) {
return enabledTextChild;
} else {
return Obx(
() => opt.enabled!.isTrue ? enabledTextChild : disabledTextChild);
}
}
final child = Container(
padding: padding,
alignment: AlignmentDirectional.centerStart,
constraints:
BoxConstraints(minHeight: conf.height, maxHeight: conf.height),
child: Row(
children: [
getTextChild(),
Expanded(
child: Align(
alignment: Alignment.centerRight,
child: Transform.scale(
scale: MenuConfig.iconScale,
child: Obx(() => opt.value == curOption.value
? IconButton(
padding:
const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 0.0),
hoverColor: Colors.transparent,
focusColor: Colors.transparent,
onPressed: () {},
icon: Icon(
Icons.check,
color: (opt.enabled ?? true.obs).isTrue
? conf.commonColor
: Colors.grey,
))
: const SizedBox.shrink()),
))),
],
),
);
onPressed() {
if (opt.dismissOnClicked && Navigator.canPop(context)) {
Navigator.pop(context);
}
setOption(opt.value);
}
return mod_menu.PopupMenuItem(
padding: EdgeInsets.zero,
height: conf.height,
child: Container(
width: conf.boxWidth,
child: TextButton(
child: Container(
padding: padding,
alignment: AlignmentDirectional.centerStart,
constraints: BoxConstraints(
minHeight: conf.height, maxHeight: conf.height),
child: Row(
children: [
Text(
opt.text,
style: TextStyle(
color: Theme.of(context).textTheme.titleLarge?.color,
fontSize: MenuConfig.fontSize,
fontWeight: FontWeight.normal),
),
Expanded(
child: Align(
alignment: Alignment.centerRight,
child: Transform.scale(
scale: MenuConfig.iconScale,
child: Obx(() => opt.value == curOption.value
? IconButton(
padding: const EdgeInsets.fromLTRB(
8.0, 0.0, 8.0, 0.0),
hoverColor: Colors.transparent,
focusColor: Colors.transparent,
onPressed: () {},
icon: Icon(
Icons.check,
color: conf.commonColor,
))
: const SizedBox.shrink()),
))),
],
),
),
onPressed: () {
if (opt.dismissOnClicked && Navigator.canPop(context)) {
Navigator.pop(context);
}
setOption(opt.value);
},
)),
width: conf.boxWidth,
child: opt.enabled == null
? TextButton(
child: child,
onPressed: onPressed,
)
: Obx(() => TextButton(
child: child,
onPressed: opt.enabled!.isTrue ? onPressed : null,
)),
),
);
}
@ -567,12 +598,9 @@ class MenuEntrySubMenu<T> extends MenuEntryBase<T> {
const SizedBox(width: MenuConfig.midPadding),
Obx(() => Text(
text,
style: TextStyle(
color: super.enabled!.value
? Theme.of(context).textTheme.titleLarge?.color
: Colors.grey,
fontSize: MenuConfig.fontSize,
fontWeight: FontWeight.normal),
style: super.enabled!.value
? enabledStyle(context)
: disabledStyle(),
)),
Expanded(
child: Align(
@ -605,14 +633,6 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
);
Widget _buildChild(BuildContext context, MenuConfig conf) {
final enabledStyle = TextStyle(
color: Theme.of(context).textTheme.titleLarge?.color,
fontSize: MenuConfig.fontSize,
fontWeight: FontWeight.normal);
const disabledStyle = TextStyle(
color: Colors.grey,
fontSize: MenuConfig.fontSize,
fontWeight: FontWeight.normal);
super.enabled ??= true.obs;
return Obx(() => Container(
width: conf.boxWidth,
@ -631,7 +651,7 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
constraints:
BoxConstraints(minHeight: conf.height, maxHeight: conf.height),
child: childBuilder(
super.enabled!.value ? enabledStyle : disabledStyle),
super.enabled!.value ? enabledStyle(context) : disabledStyle()),
),
)));
}

View File

@ -699,7 +699,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
if (_screen == null) {
return false;
}
double scale = _screen!.scaleFactor;
final scale = kIgnoreDpi ? 1.0 : _screen!.scaleFactor;
double selfWidth = _screen!.visibleFrame.width;
double selfHeight = _screen!.visibleFrame.height;
if (isFullscreen) {
@ -936,11 +936,13 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
text: translate('ScrollAuto'),
value: kRemoteScrollStyleAuto,
dismissOnClicked: true,
enabled: widget.ffi.canvasModel.imageOverflow,
),
MenuEntryRadioOption(
text: translate('Scrollbar'),
value: kRemoteScrollStyleBar,
dismissOnClicked: true,
enabled: widget.ffi.canvasModel.imageOverflow,
),
],
curOptionGetter: () async =>
@ -986,15 +988,17 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
wndRect.bottom - wndRect.top - mediaSize.height * scale;
final canvasModel = widget.ffi.canvasModel;
final width = (canvasModel.getDisplayWidth() +
canvasModel.windowBorderWidth * 2) *
scale +
magicWidth;
final height = (canvasModel.getDisplayHeight() +
canvasModel.tabBarHeight +
canvasModel.windowBorderWidth * 2) *
scale +
magicHeight;
final width =
(canvasModel.getDisplayWidth() * canvasModel.scale +
canvasModel.windowBorderWidth * 2) *
scale +
magicWidth;
final height =
(canvasModel.getDisplayHeight() * canvasModel.scale +
canvasModel.tabBarHeight +
canvasModel.windowBorderWidth * 2) *
scale +
magicHeight;
double left = wndRect.left + (wndRect.width - width) / 2;
double top = wndRect.top + (wndRect.height - height) / 2;
@ -1198,7 +1202,6 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
},
optionSetter: (String oldValue, String newValue) async {
await bind.sessionSetKeyboardMode(id: widget.id, value: newValue);
widget.ffi.canvasModel.updateViewStyle();
},
)
];

View File

@ -527,13 +527,19 @@ class WindowActionPanelState extends State<WindowActionPanel>
void onWindowClose() async {
// hide window on close
if (widget.isMainWindow) {
await rustDeskWinManager.unregisterActiveWindow(0);
// `hide` must be placed after unregisterActiveWindow, because once all windows are hidden,
// flutter closes the application on macOS. We should ensure the post-run logic has ran successfully.
// e.g.: saving window position.
await windowManager.hide();
rustDeskWinManager.unregisterActiveWindow(0);
} else {
widget.onClose?.call();
// it's safe to hide the subwindow
await WindowController.fromWindowId(windowId!).hide();
rustDeskWinManager
.call(WindowType.Main, kWindowEventHide, {"id": windowId!});
await Future.wait([
rustDeskWinManager
.call(WindowType.Main, kWindowEventHide, {"id": windowId!}),
widget.onClose?.call() ?? Future.microtask(() => null)
]);
}
super.onWindowClose();
}

View File

@ -196,6 +196,8 @@ void runMultiWindow(
// no such appType
exit(0);
}
// show window from hidden status
WindowController.fromWindowId(windowId!).show();
}
void runConnectionManagerScreen(bool hide) async {

View File

@ -466,15 +466,21 @@ class InputModel {
evt['y'] = '${y.round()}';
var buttons = '';
switch (evt['buttons']) {
case 1:
case kPrimaryMouseButton:
buttons = 'left';
break;
case 2:
case kSecondaryMouseButton:
buttons = 'right';
break;
case 4:
case kMiddleMouseButton:
buttons = 'wheel';
break;
case kBackMouseButton:
buttons = 'back';
break;
case kForwardMouseButton:
buttons = 'forward';
break;
}
evt['buttons'] = buttons;
bind.sessionSendMouse(id: id, msg: json.encode(evt));

View File

@ -22,6 +22,7 @@ import 'package:tuple/tuple.dart';
import 'package:image/image.dart' as img2;
import 'package:flutter_custom_cursor/cursor_manager.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../common.dart';
import '../common/shared_state.dart';
@ -528,6 +529,7 @@ class CanvasModel with ChangeNotifier {
double _y = 0;
// image scale
double _scale = 1.0;
Size _size = Size.zero;
// the tabbar over the image
// double tabBarHeight = 0.0;
// the window border's width
@ -541,6 +543,8 @@ class CanvasModel with ChangeNotifier {
ScrollStyle _scrollStyle = ScrollStyle.scrollauto;
ViewStyle _lastViewStyle = ViewStyle();
final _imageOverflow = false.obs;
WeakReference<FFI> parent;
CanvasModel(this.parent);
@ -548,8 +552,10 @@ class CanvasModel with ChangeNotifier {
double get x => _x;
double get y => _y;
double get scale => _scale;
Size get size => _size;
ScrollStyle get scrollStyle => _scrollStyle;
ViewStyle get viewStyle => _lastViewStyle;
RxBool get imageOverflow => _imageOverflow;
_resetScroll() => setScrollPercent(0.0, 0.0);
@ -562,18 +568,26 @@ class CanvasModel with ChangeNotifier {
double get scrollY => _scrollY;
updateViewStyle() async {
Size getSize() {
final size = MediaQueryData.fromWindow(ui.window).size;
// If minimized, w or h may be negative here.
double w = size.width - windowBorderWidth * 2;
double h = size.height - tabBarHeight - windowBorderWidth * 2;
return Size(w < 0 ? 0 : w, h < 0 ? 0 : h);
}
final style = await bind.sessionGetViewStyle(id: id);
if (style == null) {
return;
}
final sizeWidth = size.width;
final sizeHeight = size.height;
_size = getSize();
final displayWidth = getDisplayWidth();
final displayHeight = getDisplayHeight();
final viewStyle = ViewStyle(
style: style,
width: sizeWidth,
height: sizeHeight,
width: size.width,
height: size.height,
displayWidth: displayWidth,
displayHeight: displayHeight,
);
@ -585,8 +599,13 @@ class CanvasModel with ChangeNotifier {
}
_lastViewStyle = viewStyle;
_scale = viewStyle.scale;
_x = (sizeWidth - displayWidth * _scale) / 2;
_y = (sizeHeight - displayHeight * _scale) / 2;
if (kIgnoreDpi && style == kRemoteViewStyleOriginal) {
_scale = 1.0 / ui.window.devicePixelRatio;
}
_x = (size.width - displayWidth * _scale) / 2;
_y = (size.height - displayHeight * _scale) / 2;
_imageOverflow.value = _x < 0 || y < 0;
notifyListeners();
}
@ -628,14 +647,6 @@ class CanvasModel with ChangeNotifier {
double get windowBorderWidth => stateGlobal.windowBorderWidth.value;
double get tabBarHeight => stateGlobal.tabBarHeight;
Size get size {
final size = MediaQueryData.fromWindow(ui.window).size;
// If minimized, w or h may be negative here.
double w = size.width - windowBorderWidth * 2;
double h = size.height - tabBarHeight - windowBorderWidth * 2;
return Size(w < 0 ? 0 : w, h < 0 ? 0 : h);
}
moveDesktopMouse(double x, double y) {
// On mobile platforms, move the canvas with the cursor.
final dw = getDisplayWidth() * _scale;

View File

@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/common.dart';
@ -34,7 +35,7 @@ class RustDeskMultiWindowManager {
static final instance = RustDeskMultiWindowManager._();
final List<int> _activeWindows = List.empty(growable: true);
final List<VoidCallback> _windowActiveCallbacks = List.empty(growable: true);
final List<AsyncCallback> _windowActiveCallbacks = List.empty(growable: true);
int? _remoteDesktopWindowId;
int? _fileTransferWindowId;
int? _portForwardWindowId;
@ -191,19 +192,19 @@ class RustDeskMultiWindowManager {
return _activeWindows;
}
void _notifyActiveWindow() {
Future<void> _notifyActiveWindow() async {
for (final callback in _windowActiveCallbacks) {
callback.call();
await callback.call();
}
}
void registerActiveWindow(int windowId) {
Future<void> registerActiveWindow(int windowId) async {
if (_activeWindows.contains(windowId)) {
// ignore
} else {
_activeWindows.add(windowId);
}
_notifyActiveWindow();
await _notifyActiveWindow();
}
/// Remove active window which has [`windowId`]
@ -212,20 +213,20 @@ class RustDeskMultiWindowManager {
/// This function should only be called from main window.
/// For other windows, please post a unregister(hide) event to main window handler:
/// `rustDeskWinManager.call(WindowType.Main, kWindowEventHide, {"id": windowId!});`
void unregisterActiveWindow(int windowId) {
Future<void> unregisterActiveWindow(int windowId) async {
if (!_activeWindows.contains(windowId)) {
// ignore
} else {
_activeWindows.remove(windowId);
}
_notifyActiveWindow();
await _notifyActiveWindow();
}
void registerActiveWindowListener(VoidCallback callback) {
void registerActiveWindowListener(AsyncCallback callback) {
_windowActiveCallbacks.add(callback);
}
void unregisterActiveWindowListener(VoidCallback callback) {
void unregisterActiveWindowListener(AsyncCallback callback) {
_windowActiveCallbacks.remove(callback);
}
}

View File

@ -23,7 +23,15 @@ static void my_application_activate(GApplication* application) {
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
// we have custom window frame
gtk_window_set_decorated(window, FALSE);
// try setting icon for rustdesk, which uses the system cache
GtkIconTheme* theme = gtk_icon_theme_get_default();
gint icons[4] = {256, 128, 64, 32};
for (int i = 0; i < 4; i++) {
GdkPixbuf* icon = gtk_icon_theme_load_icon(theme, "rustdesk", icons[i], GTK_ICON_LOOKUP_NO_SVG, NULL);
if (icon != nullptr) {
gtk_window_set_icon(window, icon);
}
}
// Use a header bar when running in GNOME as this is the common style used
// by applications and is the setup most users will be using (e.g. Ubuntu
// desktop).

View File

@ -411,6 +411,7 @@
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
@ -436,8 +437,11 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -546,6 +550,7 @@
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
@ -558,6 +563,12 @@
MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = NO;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = (
"-sectcreate",
__CGPreLoginApp,
__cgpreloginapp,
/dev/null,
);
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
@ -571,8 +582,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -598,8 +611,11 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -610,6 +626,12 @@
../../target/release,
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
OTHER_LDFLAGS = (
"-sectcreate",
__CGPreLoginApp,
__cgpreloginapp,
/dev/null,
);
PRODUCT_BUNDLE_IDENTIFIER = com.carriez.rustdesk;
PROVISIONING_PROFILE_SPECIFIER = "";
"SWIFT_OBJC_BRIDGING_HEADER[arch=*]" = Runner/bridge_generated.h;

View File

@ -6,6 +6,8 @@
<false/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>

View File

@ -49,7 +49,8 @@ class MainFlutterWindow: NSWindow {
super.awakeFromNib()
}
// override func bitsdojo_window_configure() -> UInt {
// return BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP
// }
override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) {
super.order(place, relativeTo: otherWin)
hiddenWindowAtLaunch()
}
}

View File

@ -4,6 +4,10 @@
<dict>
<key>com.apple.security.app-sandbox</key>
<false/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>

View File

@ -108,6 +108,12 @@
PRODUCT_NAME = rustdesk;
SDKROOT = macosx;
SUPPORTS_MACCATALYST = YES;
OTHER_LDFLAGS = (
"-sectcreate",
__CGPreLoginApp,
__cgpreloginapp,
/dev/null,
);
};
name = Release;
};

View File

@ -35,7 +35,7 @@ packages:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "3.3.1"
version: "3.3.5"
args:
dependency: transitive
description:
@ -63,7 +63,7 @@ packages:
name: back_button_interceptor
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.1"
version: "6.0.2"
bot_toast:
dependency: "direct main"
description:
@ -84,7 +84,7 @@ packages:
name: build_config
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.1.1"
build_daemon:
dependency: transitive
description:
@ -105,14 +105,14 @@ packages:
name: build_runner
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
version: "2.3.3"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
url: "https://pub.dartlang.org"
source: hosted
version: "7.2.4"
version: "7.2.7"
built_collection:
dependency: transitive
description:
@ -126,7 +126,7 @@ packages:
name: built_value
url: "https://pub.dartlang.org"
source: hosted
version: "8.4.1"
version: "8.4.2"
cached_network_image:
dependency: transitive
description:
@ -154,7 +154,7 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
version: "1.2.0"
charcode:
dependency: transitive
description:
@ -175,14 +175,14 @@ packages:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
version: "1.1.0"
code_builder:
dependency: transitive
description:
name: code_builder
url: "https://pub.dartlang.org"
source: hosted
version: "4.3.0"
version: "4.4.0"
collection:
dependency: transitive
description:
@ -203,7 +203,7 @@ packages:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
version: "3.1.0"
cross_file:
dependency: transitive
description:
@ -352,7 +352,7 @@ packages:
name: file_picker
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.3"
version: "5.2.4"
fixnum:
dependency: transitive
description:
@ -441,7 +441,7 @@ packages:
name: flutter_svg
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.5"
version: "1.1.6"
flutter_web_plugins:
dependency: transitive
description: flutter
@ -467,7 +467,7 @@ packages:
name: frontend_server_client
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
version: "3.2.0"
get:
dependency: "direct main"
description:
@ -481,21 +481,21 @@ packages:
name: glob
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
graphs:
dependency: transitive
description:
name: graphs
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.2.0"
html:
dependency: transitive
description:
name: html
url: "https://pub.dartlang.org"
source: hosted
version: "0.15.0"
version: "0.15.1"
http:
dependency: "direct main"
description:
@ -516,7 +516,7 @@ packages:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.1"
version: "4.0.2"
icons_launcher:
dependency: "direct dev"
description:
@ -530,7 +530,7 @@ packages:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.0"
version: "3.2.2"
image_picker:
dependency: "direct main"
description:
@ -544,7 +544,7 @@ packages:
name: image_picker_android
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.5+3"
version: "0.8.5+4"
image_picker_for_web:
dependency: transitive
description:
@ -558,7 +558,7 @@ packages:
name: image_picker_ios
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.6+1"
version: "0.8.6+3"
image_picker_platform_interface:
dependency: transitive
description:
@ -600,7 +600,7 @@ packages:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
version: "2.0.1"
logging:
dependency: transitive
description:
@ -621,14 +621,14 @@ packages:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.5"
version: "0.1.4"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
version: "1.7.0"
mime:
dependency: transitive
description:
@ -705,7 +705,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.2"
version: "1.8.1"
path_drawing:
dependency: transitive
description:
@ -733,7 +733,7 @@ packages:
name: path_provider_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.20"
version: "2.0.22"
path_provider_ios:
dependency: transitive
description:
@ -797,6 +797,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
pointycastle:
dependency: transitive
description:
name: pointycastle
url: "https://pub.dartlang.org"
source: hosted
version: "3.6.2"
pool:
dependency: transitive
description:
@ -817,14 +824,14 @@ packages:
name: provider
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.3"
version: "6.0.5"
pub_semver:
dependency: transitive
description:
name: pub_semver
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
version: "2.1.3"
pubspec_parse:
dependency: transitive
description:
@ -845,7 +852,7 @@ packages:
name: rxdart
url: "https://pub.dartlang.org"
source: hosted
version: "0.27.5"
version: "0.27.7"
screen_retriever:
dependency: transitive
description:
@ -882,7 +889,7 @@ packages:
name: shelf_web_socket
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
version: "1.0.3"
simple_observable:
dependency: transitive
description:
@ -901,7 +908,7 @@ packages:
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.5"
version: "1.2.6"
source_span:
dependency: transitive
description:
@ -922,7 +929,7 @@ packages:
name: sqflite_common
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.0"
version: "2.4.0"
stack_trace:
dependency: transitive
description:
@ -943,7 +950,7 @@ packages:
name: stream_transform
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
version: "2.1.0"
string_scanner:
dependency: transitive
description:
@ -1034,14 +1041,14 @@ packages:
name: url_launcher
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.6"
version: "6.1.7"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.19"
version: "6.0.22"
url_launcher_ios:
dependency: transitive
description:
@ -1090,7 +1097,7 @@ packages:
name: uuid
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.6"
version: "3.0.7"
vector_math:
dependency: transitive
description:
@ -1104,35 +1111,35 @@ packages:
name: video_player
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.9"
version: "2.4.10"
video_player_android:
dependency: transitive
description:
name: video_player_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.9"
version: "2.3.10"
video_player_avfoundation:
dependency: transitive
description:
name: video_player_avfoundation
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.7"
version: "2.3.8"
video_player_platform_interface:
dependency: transitive
description:
name: video_player_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.4"
version: "6.0.1"
video_player_web:
dependency: transitive
description:
name: video_player_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.12"
version: "2.0.13"
visibility_detector:
dependency: "direct main"
description:
@ -1181,7 +1188,7 @@ packages:
name: watcher
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "1.0.2"
web_socket_channel:
dependency: transitive
description:
@ -1195,7 +1202,7 @@ packages:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
version: "3.1.3"
win32_registry:
dependency: transitive
description:

View File

@ -63,7 +63,7 @@ dependencies:
desktop_multi_window:
git:
url: https://github.com/Kingtous/rustdesk_desktop_multi_window
ref: 82f9eab81cb2c7bfb938def7a1b399a6279bbc75
ref: 057e6eb1bc7dcbcf9dafd1384274a611e4fe7124
freezed_annotation: ^2.0.3
flutter_custom_cursor: ^0.0.2
window_size:

View File

@ -104,6 +104,10 @@ pub enum MouseButton {
Middle,
/// Right mouse button
Right,
/// Back mouse button
Back,
/// Forward mouse button
Forward,
/// Scroll up button
ScrollUp,

View File

@ -57,6 +57,8 @@ fn mousebutton(button: MouseButton) -> c_int {
MouseButton::ScrollDown => 5,
MouseButton::ScrollLeft => 6,
MouseButton::ScrollRight => 7,
MouseButton::Back => 8,
MouseButton::Forward => 9,
}
}

View File

@ -226,7 +226,10 @@ impl MouseControllable for Enigo {
MouseButton::Left => (CGMouseButton::Left, CGEventType::LeftMouseDown),
MouseButton::Middle => (CGMouseButton::Center, CGEventType::OtherMouseDown),
MouseButton::Right => (CGMouseButton::Right, CGEventType::RightMouseDown),
_ => unimplemented!(),
_ => {
log::info!("Unsupported button {:?}", button);
return Ok(());
},
};
let dest = CGPoint::new(current_x as f64, current_y as f64);
if let Some(src) = self.event_source.as_ref() {
@ -249,7 +252,10 @@ impl MouseControllable for Enigo {
MouseButton::Left => (CGMouseButton::Left, CGEventType::LeftMouseUp),
MouseButton::Middle => (CGMouseButton::Center, CGEventType::OtherMouseUp),
MouseButton::Right => (CGMouseButton::Right, CGEventType::RightMouseUp),
_ => unimplemented!(),
_ => {
log::info!("Unsupported button {:?}", button);
return;
},
};
let dest = CGPoint::new(current_x as f64, current_y as f64);
if let Some(src) = self.event_source.as_ref() {

View File

@ -56,6 +56,20 @@ fn keybd_event(flags: u32, vk: u16, scan: u16) -> DWORD {
input.type_ = INPUT_KEYBOARD;
unsafe {
let dst_ptr = (&mut input.u as *mut _) as *mut u8;
let flags = match vk as _ {
winapi::um::winuser::VK_HOME |
winapi::um::winuser::VK_UP |
winapi::um::winuser::VK_PRIOR |
winapi::um::winuser::VK_LEFT |
winapi::um::winuser::VK_RIGHT |
winapi::um::winuser::VK_END |
winapi::um::winuser::VK_DOWN |
winapi::um::winuser::VK_NEXT |
winapi::um::winuser::VK_INSERT |
winapi::um::winuser::VK_DELETE => flags | winapi::um::winuser::KEYEVENTF_EXTENDEDKEY,
_ => flags,
};
let k = KEYBDINPUT {
wVk: vk,
wScan: scan,
@ -134,9 +148,18 @@ impl MouseControllable for Enigo {
MouseButton::Left => MOUSEEVENTF_LEFTDOWN,
MouseButton::Middle => MOUSEEVENTF_MIDDLEDOWN,
MouseButton::Right => MOUSEEVENTF_RIGHTDOWN,
_ => unimplemented!(),
MouseButton::Back => MOUSEEVENTF_XDOWN,
MouseButton::Forward => MOUSEEVENTF_XDOWN,
_ => {
log::info!("Unsupported button {:?}", button);
return Ok(());
}
},
match button {
MouseButton::Back => XBUTTON1 as _,
MouseButton::Forward => XBUTTON2 as _,
_ => 0,
},
0,
0,
0,
);
@ -155,9 +178,18 @@ impl MouseControllable for Enigo {
MouseButton::Left => MOUSEEVENTF_LEFTUP,
MouseButton::Middle => MOUSEEVENTF_MIDDLEUP,
MouseButton::Right => MOUSEEVENTF_RIGHTUP,
_ => unimplemented!(),
MouseButton::Back => MOUSEEVENTF_XUP,
MouseButton::Forward => MOUSEEVENTF_XUP,
_ => {
log::info!("Unsupported button {:?}", button);
return;
}
},
match button {
MouseButton::Back => XBUTTON1 as _,
MouseButton::Forward => XBUTTON2 as _,
_ => 0,
},
0,
0,
0,
);

View File

@ -4,7 +4,6 @@ use std::{io, sync::RwLock, time::Duration};
pub struct Capturer(Display, Box<dyn Recorder>, bool, Vec<u8>);
#[allow(non_upper_case_globals)]
pub const IS_CURSOR_EMBEDDED: bool = true;
lazy_static::lazy_static! {

View File

@ -3,7 +3,6 @@ use std::{io, ops, time::Duration};
pub struct Capturer(x11::Capturer);
#[allow(non_upper_case_globals)]
pub const IS_CURSOR_EMBEDDED: bool = false;
impl Capturer {

BIN
res/64x64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -38,6 +38,17 @@ pub fn core_main() -> Option<Vec<String>> {
}
i += 1;
}
#[cfg(target_os = "linux")]
#[cfg(feature = "flutter")]
{
crate::platform::linux::register_breakdown_handler();
let (k, v) = ("LIBGL_ALWAYS_SOFTWARE", "true");
if !hbb_common::config::Config::get_option("allow-always-software-render").is_empty() {
std::env::set_var(k, v);
} else {
std::env::remove_var(k);
}
}
#[cfg(feature = "flutter")]
if _is_flutter_connect {
return core_main_invoke_new_connection(std::env::args());

View File

@ -885,9 +885,11 @@ pub fn session_send_mouse(id: String, msg: String) {
}
if let Some(buttons) = m.get("buttons") {
mask |= match buttons.as_str() {
"left" => 1,
"right" => 2,
"wheel" => 4,
"left" => 0x01,
"right" => 0x02,
"wheel" => 0x04,
"back" => 0x08,
"forward" => 0x10,
_ => 0,
} << 3;
}
@ -1111,6 +1113,10 @@ pub fn main_is_can_screen_recording(prompt: bool) -> SyncReturn<bool> {
SyncReturn(is_can_screen_recording(prompt))
}
pub fn main_is_can_input_monitoring(prompt: bool) -> SyncReturn<bool> {
SyncReturn(is_can_input_monitoring(prompt))
}
pub fn main_is_share_rdp() -> SyncReturn<bool> {
SyncReturn(is_share_rdp())
}

View File

@ -30,6 +30,7 @@ mod sv;
mod sq;
mod sr;
mod th;
mod sl;
lazy_static::lazy_static! {
pub static ref LANGS: Value =
@ -63,6 +64,7 @@ lazy_static::lazy_static! {
("sq", "Shqip"),
("sr", "Srpski"),
("th", "ภาษาไทย"),
("sl", "Slovenščina"),
]);
}
@ -120,6 +122,7 @@ pub fn translate_locale(name: String, locale: &str) -> String {
"sq" => sq::T.deref(),
"sr" => sr::T.deref(),
"th" => th::T.deref(),
"sl" => sl::T.deref(),
_ => en::T.deref(),
};
if let Some(v) = m.get(&name as &str) {

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -7,7 +7,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Password", "密码"),
("Ready", "就绪"),
("Established", "已建立"),
("connecting_status", "正在接入RustDesk网络..."),
("connecting_status", "正在接入 RustDesk 网络..."),
("Enable Service", "允许服务"),
("Start Service", "启动服务"),
("Service is running", "服务正在运行"),
@ -138,13 +138,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Failed to make direct connection to remote desktop", "无法建立直接连接"),
("Set Password", "设置密码"),
("OS Password", "操作系统密码"),
("install_tip", "你正在运行未安装版本由于UAC限制作为被控端会在某些情况下无法控制鼠标键盘或者录制屏幕请点击下面的按钮将RustDesk安装到系统从而规避上述问题。"),
("install_tip", "你正在运行未安装版本由于UAC限制作为被控端会在某些情况下无法控制鼠标键盘或者录制屏幕请点击下面的按钮将 RustDesk 安装到系统,从而规避上述问题。"),
("Click to upgrade", "点击这里升级"),
("Click to download", "点击这里下载"),
("Click to update", "点击这里更新"),
("Configure", "配置"),
("config_acc", "为了能够远程控制你的桌面, 请给予RustDesk\"辅助功能\" 权限。"),
("config_screen", "为了能够远程访问你的桌面, 请给予RustDesk\"屏幕录制\" 权限。"),
("config_acc", "为了能够远程控制你的桌面, 请给予 RustDesk \"辅助功能\" 权限。"),
("config_screen", "为了能够远程访问你的桌面, 请给予 RustDesk \"屏幕录制\" 权限。"),
("Installing ...", "安装 ..."),
("Install", "安装"),
("Installation", "安装"),
@ -303,9 +303,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("In privacy mode", "进入隐私模式"),
("Out privacy mode", "退出隐私模式"),
("Language", "语言"),
("Keep RustDesk background service", "保持RustDesk后台服务"),
("Keep RustDesk background service", "保持 RustDesk 后台服务"),
("Ignore Battery Optimizations", "忽略电池优化"),
("android_open_battery_optimizations_tip", "如需关闭此功能,请在接下来的RustDesk应用设置页面中找到并进入 [电源] 页面,取消勾选 [不受限制]"),
("android_open_battery_optimizations_tip", "如需关闭此功能,请在接下来的 RustDesk 应用设置页面中,找到并进入 [电源] 页面,取消勾选 [不受限制]"),
("Connection not allowed", "对方不允许连接"),
("Legacy mode", "传统模式"),
("Map mode", "11传输"),
@ -385,7 +385,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland 需要更高版本的 linux 发行版。 请尝试 X11 桌面或更改您的操作系统。"),
("JumpLink", "查看"),
("Please Select the screen to be shared(Operate on the peer side).", "请选择要分享的画面(对端操作)。"),
("Show RustDesk", "显示rustdesk"),
("Show RustDesk", "显示 RustDesk"),
("This PC", "此电脑"),
("or", ""),
("Continue with", "使用"),
@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", "被web控制台手动关闭"),
("Local keyboard type", "本地键盘类型"),
("Select local keyboard type", "请选择本地键盘类型"),
("software_render_tip", "如果你使用英伟达显卡, 并且远程窗口在会话建立后会立刻关闭, 那么安装nouveau驱动并且选择使用软件渲染可能会有帮助。重启软件后生效。"),
("Always use software rendering", "使用软件渲染"),
("config_input", "为了能够通过键盘控制远程桌面, 请给予 RustDesk \"输入监控\" 权限。"),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -9,7 +9,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Established", "Verbunden"),
("connecting_status", "Verbinden mit dem RustDesk-Netzwerk..."),
("Enable Service", "Vermittlungsdienst aktivieren"),
("Start Service", "Starte Vermittlungsdienst"),
("Start Service", "Vermittlungsdienst starten"),
("Service is running", "Vermittlungsdienst aktiv"),
("Service is not running", "Vermittlungsdienst deaktiviert"),
("not_ready_status", "Nicht bereit. Bitte überprüfen Sie Ihre Netzwerkverbindung."),
@ -35,7 +35,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Export server configuration successfully", "Serverkonfiguration erfolgreich exportiert"),
("Invalid server configuration", "Ungültige Serverkonfiguration"),
("Clipboard is empty", "Zwischenablage ist leer"),
("Stop service", "Vermittlungsdienst deaktivieren"),
("Stop service", "Vermittlungsdienst stoppen"),
("Change ID", "ID ändern"),
("Website", "Webseite"),
("About", "Über"),
@ -165,7 +165,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Local Port", "Lokaler Port"),
("Local Address", "Lokale Adresse"),
("Change Local Port", "Lokalen Port ändern"),
("setup_server_tip", "für eine schnellere Verbindung richten Sie bitte Ihren eigenen Verbindungsserver ein."),
("setup_server_tip", "für eine schnellere Verbindung richten Sie bitte Ihren eigenen Server ein."),
("Too short, at least 6 characters.", "Zu kurz, mindestens 6 Zeichen."),
("The confirmation is not identical.", "Die Passwörter stimmen nicht überein."),
("Permissions", "Berechtigungen"),
@ -244,7 +244,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Are you sure to close the connection?", "Möchten Sie diese Verbindung wirklich trennen?"),
("Download new version", "Neue Version herunterladen"),
("Touch mode", "Touch-Modus"),
("Mouse mode", "Maus-Modus"),
("Mouse mode", "Mausmodus"),
("One-Finger Tap", "1-Finger-Tipp"),
("Left Mouse", "Linksklick"),
("One-Long Tap", "1-Finger-Halten"),
@ -260,8 +260,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Pinch to Zoom", "2-Finger-Zoom"),
("Canvas Zoom", "Sichtfeld-Zoom"),
("Reset canvas", "Sichtfeld zurücksetzen"),
("No permission of file transfer", "Keine Berechtigung für den Dateizugriff"),
("Note", "Anmerkung"),
("No permission of file transfer", "Keine Berechtigung für die Dateiübertragung"),
("Note", "Hinweis"),
("Connection", "Verbindung"),
("Share Screen", "Bildschirm freigeben"),
("CLOSE", "DEAKTIV."),
@ -278,7 +278,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Do you accept?", "Verbindung zulassen?"),
("Open System Setting", "Systemeinstellung öffnen"),
("How to get Android input permission?", "Wie erhalte ich eine Android-Eingabeberechtigung?"),
("android_input_permission_tip1", "Damit ein Remote-Gerät Ihr Android-Gerät steuern kann, müssen Sie RustDesk erlauben, den Dienst \"Barrierefreiheit\" zu verwenden."),
("android_input_permission_tip1", "Damit ein entferntes Gerät Ihr Android-Gerät steuern kann, müssen Sie RustDesk erlauben, den Dienst \"Barrierefreiheit\" zu verwenden."),
("android_input_permission_tip2", "Bitte gehen Sie zur nächsten Systemeinstellungsseite, suchen Sie [Installierte Dienste] und schalten Sie den Dienst [RustDesk Input] ein."),
("android_new_connection_tip", "möchte ihr Gerät steuern."),
("android_service_will_start_tip", "Durch das Aktivieren der Bildschirmfreigabe wird der Dienst automatisch gestartet, sodass andere Geräte dieses Android-Gerät steuern können."),
@ -295,9 +295,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Succeeded", "Erfolgreich"),
("Someone turns on privacy mode, exit", "Jemand hat den Datenschutzmodus aktiviert, beende..."),
("Unsupported", "Nicht unterstützt"),
("Peer denied", "Die Gegenstelle hat die Verbindung abgelehnt"),
("Peer denied", "Die Gegenstelle hat die Verbindung abgelehnt."),
("Please install plugins", "Bitte installieren Sie Plugins"),
("Peer exit", "Die Gegenstelle hat die Verbindung getrennt"),
("Peer exit", "Die Gegenstelle hat die Verbindung getrennt."),
("Failed to turn off", "Ausschalten fehlgeschlagen"),
("Turned off", "Ausgeschaltet"),
("In privacy mode", "Datenschutzmodus aktivieren"),
@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", "Manuell über die Webkonsole beendet"),
("Local keyboard type", "Lokaler Tastaturtyp"),
("Select local keyboard type", "Lokalen Tastaturtyp auswählen"),
("software_render_tip", "Wenn Sie eine Nvidia-Grafikkarte haben und sich das entfernte Fenster sofort nach dem Herstellen der Verbindung schließt, kann es helfen, den Nouveau-Treiber zu installieren und Software-Rendering zu verwenden. Ein Neustart der Software ist erforderlich."),
("Always use software rendering", "Software-Rendering immer verwenden"),
("config_input", "Um den entfernten Desktop mit der Tastatur steuern zu können, müssen Sie RustDesk \"Input Monitoring\"-Rechte erteilen."),
].iter().cloned().collect();
}

View File

@ -37,5 +37,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("wayland_experiment_tip", "Wayland support is in experimental stage, please use X11 if you require unattended access."),
("Slogan_tip", "Made with heart in this chaotic world!"),
("verification_tip", "A new device has been detected, and a verification code has been sent to the registered email address, enter the verification code to continue logging in."),
("software_render_tip", "If you have an Nvidia graphics card and the remote window closes immediately after connecting, installing the nouveau driver and choosing to use software rendering may help. A software restart is required."),
("config_input", "In order to control remote desktop with keyboard, you need to grant RustDesk \"Input Monitoring\" permissions."),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -39,7 +39,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Change ID", "Cambiar ID"),
("Website", "Sitio web"),
("About", "Acerca de"),
("Slogan_tip", ""),
("Slogan_tip", "Hecho con corazón en este mundo caótico!"),
("Privacy Statement", ""),
("Mute", "Silenciar"),
("Audio Input", "Entrada de audio"),
@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", "Cerrado manualmente por la consola web"),
("Local keyboard type", "Tipo de teclado local"),
("Select local keyboard type", "Seleccionar tipo de teclado local"),
("software_render_tip", "Si tienes una gráfica Nvidia y la ventana remota se cierra inmediatamente, instalar el driver nouveau y elegir renderizado por software podría ayudar. Se requiere reiniciar la aplicación."),
("Always use software rendering", "Usar siempre renderizado por software"),
("config_input", "Para controlar el escritorio remoto con el teclado necesitas dar a RustDesk permisos de \"Monitorización de entrada\"."),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", "Fermé manuellement par la console Web"),
("Local keyboard type", "Disposition du clavier local"),
("Select local keyboard type", "Selectionner la disposition du clavier local"),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", "Chiudi manualmente dalla console Web"),
("Local keyboard type", "Tipo di tastiera locale"),
("Select local keyboard type", "Seleziona il tipo di tastiera locale"),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -206,7 +206,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the peer", "Закрыто удалённым узлом вручную"),
("Enable remote configuration modification", "Разрешить удалённое изменение конфигурации"),
("Run without install", "Запустить без установки"),
("Always connected via relay", "Всегда подключён через ретрансляционный сервер"),
("Always connected via relay", "Всегда подключается через ретрансляционный сервер"),
("Always connect via relay", "Всегда подключаться через ретрансляционный сервер"),
("whitelist_tip", "Только IP-адреса из белого списка могут получить доступ ко мне"),
("Login", "Войти"),
@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", "Закрыто вручную через веб-консоль"),
("Local keyboard type", "Тип локальной клавиатуры"),
("Select local keyboard type", "Выберите тип локальной клавиатуры"),
("software_render_tip", "Если у вас видеокарта Nvidia и удалённое окно закрывается сразу после подключения, может помочь установка драйвера Nouveau и выбор использования программной визуализации. Потребуется перезапуск."),
("Always use software rendering", "Использовать программную визуализацию"),
("config_input", "Чтобы управлять удалённым рабочим столом с помощью клавиатуры, необходимо предоставить RustDesk разрешения \"Мониторинг ввода\"."),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

417
src/lang/sl.rs Executable file
View File

@ -0,0 +1,417 @@
lazy_static::lazy_static! {
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
[
("Status", "Stanje"),
("Your Desktop", "Vaše namizje"),
("desk_tip", "Do vašega namizja lahko dostopate s spodnjim IDjem in geslom"),
("Password", "Geslo"),
("Ready", "Pripravljen"),
("Established", "Povezava vzpostavljena"),
("connecting_status", "Vzpostavljanje povezave z omrežjem RustDesk..."),
("Enable Service", "Omogoči storitev"),
("Start Service", "Zaženi storitev"),
("Service is running", "Storitev se izvaja"),
("Service is not running", "Storitev se ne izvaja"),
("not_ready_status", "Ni pripravljeno, preverite vašo mrežno povezavo"),
("Control Remote Desktop", "Nadzoruj oddaljeno namizje"),
("Transfer File", "Prenos datotek"),
("Connect", "Poveži"),
("Recent Sessions", "Nedavne seje"),
("Address Book", "Adresar"),
("Confirmation", "Potrditev"),
("TCP Tunneling", "TCP tuneliranje"),
("Remove", "Odstrani"),
("Refresh random password", "Osveži naključno geslo"),
("Set your own password", "Nastavi lastno geslo"),
("Enable Keyboard/Mouse", "Omogoči tipkovnico in miško"),
("Enable Clipboard", "Omogoči odložišče"),
("Enable File Transfer", "Omogoči prenos datotek"),
("Enable TCP Tunneling", "Omogoči TCP tuneliranje"),
("IP Whitelisting", "Omogoči seznam dovoljenih IPjev"),
("ID/Relay Server", "Strežnik za ID/posredovanje"),
("Import Server Config", "Uvozi nastavitve strežnika"),
("Export Server Config", "Izvozi nastavitve strežnika"),
("Import server configuration successfully", "Nastavitve strežnika uspešno uvožene"),
("Export server configuration successfully", "Nastavitve strežnika uspešno izvožene"),
("Invalid server configuration", "Neveljavne nastavitve strežnika"),
("Clipboard is empty", "Odložišče je prazno"),
("Stop service", "Ustavi storitev"),
("Change ID", "Spremeni ID"),
("Website", "Spletna stran"),
("About", "O programu"),
("Slogan_tip", ""),
("Privacy Statement", ""),
("Mute", "Izklopi zvok"),
("Audio Input", "Avdio vhod"),
("Enhancements", "Izboljšave"),
("Hardware Codec", "Strojni kodek"),
("Adaptive Bitrate", "Prilagodljiva bitna hitrost"),
("ID Server", "ID strežnik"),
("Relay Server", "Posredniški strežnik"),
("API Server", "API strežnik"),
("invalid_http", "mora se začeti s http:// ali https://"),
("Invalid IP", "Neveljaven IP"),
("id_change_tip", "Dovoljeni znaki so a-z, A-Z (brez šumnikov), 0-9 in _. Prvi znak mora biti črka, dolžina od 6 do 16 znakov."),
("Invalid format", "Neveljavna oblika"),
("server_not_support", "Strežnik še ne podpira"),
("Not available", "Ni na voljo"),
("Too frequent", "Prepogosto"),
("Cancel", "Prekliči"),
("Skip", "Izpusti"),
("Close", "Zapri"),
("Retry", "Ponovi"),
("OK", "V redu"),
("Password Required", "Potrebno je geslo"),
("Please enter your password", "Vnesite vaše geslo"),
("Remember password", "Zapomni si geslo"),
("Wrong Password", "Napačno geslo"),
("Do you want to enter again?", "Želite znova vnesti?"),
("Connection Error", "Napaka pri povezavi"),
("Error", "Napaka"),
("Reset by the peer", "Povezava prekinjena"),
("Connecting...", "Povezovanje..."),
("Connection in progress. Please wait.", "Vzpostavljanje povezave, prosim počakajte."),
("Please try 1 minute later", "Poizkusite čez 1 minuto"),
("Login Error", "Napaka pri prijavi"),
("Successful", "Uspešno"),
("Connected, waiting for image...", "Povezava vzpostavljena, čakam na sliko..."),
("Name", "Ime"),
("Type", "Vrsta"),
("Modified", "Čas spremembe"),
("Size", "Velikost"),
("Show Hidden Files", "Prikaži skrite datoteke"),
("Receive", "Prejmi"),
("Send", "Pošlji"),
("Refresh File", "Osveži datoteko"),
("Local", "Lokalno"),
("Remote", "Oddaljeno"),
("Remote Computer", "Oddaljeni računalnik"),
("Local Computer", "Lokalni računalnik"),
("Confirm Delete", "Potrdi izbris"),
("Delete", "Izbriši"),
("Properties", "Lastnosti"),
("Multi Select", "Večkratna izbira"),
("Select All", "Izberi vse"),
("Unselect All", "Počisti vse"),
("Empty Directory", "Prazen imenik"),
("Not an empty directory", "Imenik ni prazen"),
("Are you sure you want to delete this file?", "Ali res želite izbrisati to datoteko?"),
("Are you sure you want to delete this empty directory?", "Ali res želite izbrisati to prazno mapo?"),
("Are you sure you want to delete the file of this directory?", "Ali res želite datoteko iz mape?"),
("Do this for all conflicts", "Naredi to za vse"),
("This is irreversible!", "Tega dejanja ni mogoče razveljaviti!"),
("Deleting", "Brisanje"),
("files", "datoteke"),
("Waiting", "Čakanje"),
("Finished", "Opravljeno"),
("Speed", "Hitrost"),
("Custom Image Quality", "Kakovost slike po meri"),
("Privacy mode", "Zasebni način"),
("Block user input", "Onemogoči uporabnikov vnos"),
("Unblock user input", "Omogoči uporabnikov vnos"),
("Adjust Window", "Prilagodi okno"),
("Original", "Originalno"),
("Shrink", "Skrči"),
("Stretch", "Raztegni"),
("Scrollbar", "Drsenje z drsniki"),
("ScrollAuto", "Samodejno drsenje"),
("Good image quality", "Visoka kakovost slike"),
("Balanced", "Uravnoteženo"),
("Optimize reaction time", "Optimiraj odzivni čas"),
("Custom", "Po meri"),
("Show remote cursor", "Prikaži oddaljeni kazalec miške"),
("Show quality monitor", "Prikaži nadzornik kakovosti"),
("Disable clipboard", "Onemogoči odložišče"),
("Lock after session end", "Zakleni ob koncu seje"),
("Insert", "Vstavi"),
("Insert Lock", "Zakleni oddaljeni računalnik"),
("Refresh", "Osveži"),
("ID does not exist", "ID ne obstaja"),
("Failed to connect to rendezvous server", "Ni se bilo mogoče povezati na povezovalni strežnik"),
("Please try later", "Poizkusite znova kasneje"),
("Remote desktop is offline", "Oddaljeno namizje ni dosegljivo"),
("Key mismatch", "Ključ ni ustrezen"),
("Timeout", "Časovna omejitev"),
("Failed to connect to relay server", "Ni se bilo mogoče povezati na posredniški strežnik"),
("Failed to connect via rendezvous server", "Ni se bilo mogoče povezati preko povezovalnega strežnika"),
("Failed to connect via relay server", "Ni se bilo mogoče povezati preko posredniškega strežnika"),
("Failed to make direct connection to remote desktop", "Ni bilo mogoče vzpostaviti neposredne povezave z oddaljenim namizjem"),
("Set Password", "Nastavi geslo"),
("OS Password", "Geslo operacijskega sistema"),
("install_tip", "Zaradi nadzora uporabniškega računa, RustDesk v nekaterih primerih na oddaljeni strani ne deluje pravilno. Temu se lahko izognete z namestitvijo."),
("Click to upgrade", "Klikni za nadgradnjo"),
("Click to download", "Klikni za prenos"),
("Click to update", "Klikni za posodobitev"),
("Configure", "Nastavi"),
("config_acc", "Za oddaljeni nadzor namizja morate RustDesku dodeliti pravico za dostopnost"),
("config_screen", "Za oddaljeni dostop do namizja morate RustDesku dodeliti pravico snemanje zaslona"),
("Installing ...", "Nameščanje..."),
("Install", "Namesti"),
("Installation", "Namestitev"),
("Installation Path", "Pot za namestitev"),
("Create start menu shortcuts", "Ustvari bližnjice v meniju Začetek"),
("Create desktop icon", "Ustvari ikono na namizju"),
("agreement_tip", "Z namestitvijo se strinjate z licenčno pogodbo"),
("Accept and Install", "Sprejmi in namesti"),
("End-user license agreement", "Licenčna pogodba za končnega uporabnika"),
("Generating ...", "Ustvarjanje ..."),
("Your installation is lower version.", "Vaša namestitev je starejša"),
("not_close_tcp_tip", "Med uporabo tunela ne zaprite tega okna"),
("Listening ...", "Poslušam ..."),
("Remote Host", "Oddaljeni gostitelj"),
("Remote Port", "Oddaljena vrata"),
("Action", "Dejanje"),
("Add", "Dodaj"),
("Local Port", "Lokalna vrata"),
("Local Address", "Lokalni naslov"),
("Change Local Port", "Spremeni lokalna vrata"),
("setup_server_tip", "Za hitrejšo povezavo uporabite lasten strežnik"),
("Too short, at least 6 characters.", "Prekratek, mora biti najmanj 6 znakov."),
("The confirmation is not identical.", "Potrditev ni enaka."),
("Permissions", "Dovoljenja"),
("Accept", "Sprejmi"),
("Dismiss", "Opusti"),
("Disconnect", "Prekini povezavo"),
("Allow using keyboard and mouse", "Dovoli uporabo tipkovnice in miške"),
("Allow using clipboard", "Dovoli uporabo odložišča"),
("Allow hearing sound", "Dovoli prenos zvoka"),
("Allow file copy and paste", "Dovoli kopiranje in lepljenje datotek"),
("Connected", "Povezan"),
("Direct and encrypted connection", "Neposredna šifrirana povezava"),
("Relayed and encrypted connection", "Posredovana šifrirana povezava"),
("Direct and unencrypted connection", "Neposredna nešifrirana povezava"),
("Relayed and unencrypted connection", "Posredovana šifrirana povezava"),
("Enter Remote ID", "Vnesi oddaljeni ID"),
("Enter your password", "Vnesi geslo"),
("Logging in...", "Prijavljanje..."),
("Enable RDP session sharing", "Omogoči deljenje RDP seje"),
("Auto Login", "Samodejna prijava"),
("Enable Direct IP Access", "Omogoči neposredni dostop preko IP"),
("Rename", "Preimenuj"),
("Space", "Prazno"),
("Create Desktop Shortcut", "Ustvari bližnjico na namizju"),
("Change Path", "Spremeni pot"),
("Create Folder", "Ustvari mapo"),
("Please enter the folder name", "Vnesite ime mape"),
("Fix it", "Popravi"),
("Warning", "Opozorilo"),
("Login screen using Wayland is not supported", "Prijava z Waylandom ni podprta"),
("Reboot required", "Potreben je ponovni zagon"),
("Unsupported display server ", "Nepodprt zaslonski strežnik"),
("x11 expected", "Pričakovan X11"),
("Port", "Vrata"),
("Settings", "Nastavitve"),
("Username", "Uporabniško ime"),
("Invalid port", "Neveljavno geslo"),
("Closed manually by the peer", "Povezavo ročno prekinil odjemalec"),
("Enable remote configuration modification", "Omogoči oddaljeno spreminjanje nastavitev"),
("Run without install", "Zaženi brez namestitve"),
("Always connected via relay", "Vedno povezan preko posrednika"),
("Always connect via relay", "Vedno poveži preko posrednika"),
("whitelist_tip", "Dostop je možen samo iz dovoljenih IPjev"),
("Login", "Prijavi"),
("Verify", ""),
("Remember me", ""),
("Trust this device", ""),
("Verification code", ""),
("verification_tip", ""),
("Logout", "Odjavi"),
("Tags", "Oznake"),
("Search ID", "Išči ID"),
("Current Wayland display server is not supported", "Trenutni Wayland zaslonski strežnik ni podprt"),
("whitelist_sep", "Naslovi ločeni z vejico, podpičjem, presledkom ali novo vrstico"),
("Add ID", "Dodaj ID"),
("Add Tag", "Dodaj oznako"),
("Unselect all tags", ""),
("Network error", "Omrežna napaka"),
("Username missed", "Up. ime izpuščeno"),
("Password missed", "Geslo izpuščeno"),
("Wrong credentials", "Napačne poverilnice"),
("Edit Tag", "Uredi oznako"),
("Unremember Password", "Pozabi geslo"),
("Favorites", "Priljubljene"),
("Add to Favorites", "Dodaj med priljubljene"),
("Remove from Favorites", "Odstrani iz priljubljenih"),
("Empty", "Prazno"),
("Invalid folder name", "Napačno ime mape"),
("Socks5 Proxy", "Socks5 posredniški strežnik"),
("Hostname", "Ime gostitelja"),
("Discovered", "Odkriti"),
("install_daemon_tip", "Za samodejni zagon ob vklopu računalnika je potrebno dodati sistemsko storitev"),
("Remote ID", "Oddaljeni ID"),
("Paste", "Prilepi"),
("Paste here?", "Prilepi tu?"),
("Are you sure to close the connection?", "Ali želite prekiniti povezavo?"),
("Download new version", "Prenesi novo različico"),
("Touch mode", "Način dotika"),
("Mouse mode", "Način mišle"),
("One-Finger Tap", "Tap z enim prstom"),
("Left Mouse", "Leva tipka miške"),
("One-Long Tap", "Dolg tap z enim prstom"),
("Two-Finger Tap", "Tap z dvema prstoma"),
("Right Mouse", "Desna tipka miške"),
("One-Finger Move", "Premik z enim prstom"),
("Double Tap & Move", "Dvojni tap in premik"),
("Mouse Drag", "Vlečenje z miško"),
("Three-Finger vertically", "Triprstno navpično"),
("Mouse Wheel", "Miškino kolesce"),
("Two-Finger Move", "Premik z dvema prstoma"),
("Canvas Move", "Premik platna"),
("Pinch to Zoom", "Povečava s približevanjem prstov"),
("Canvas Zoom", "Povečava platna"),
("Reset canvas", "Ponastavi platno"),
("No permission of file transfer", "Ni pravic za prenos datotek"),
("Note", "Opomba"),
("Connection", "Povezava"),
("Share Screen", "Deli zaslon"),
("CLOSE", "ZAPRI"),
("OPEN", "ODPRI"),
("Chat", "Pogovor"),
("Total", "Skupaj"),
("items", "elementi"),
("Selected", "Izbrano"),
("Screen Capture", "Zajem zaslona"),
("Input Control", "Nadzor vnosa"),
("Audio Capture", "Zajem zvoka"),
("File Connection", "Datotečna povezava"),
("Screen Connection", "Zaslonska povezava"),
("Do you accept?", "Ali sprejmete?"),
("Open System Setting", "Odpri sistemske nastavitve"),
("How to get Android input permission?", "Kako pridobiti dovoljenje za vnos na Androidu?"),
("android_input_permission_tip1", "Za oddaljeni nadzor vaše naprave Android, je potrebno RustDesku dodeliti pravico za dostopnost."),
("android_input_permission_tip2", "Pojdite v sistemske nastavitve, poiščite »Nameščene storitve« in vklopite storitev »RustDesk Input«."),
("android_new_connection_tip", "Prejeta je bila zahteva za oddaljeni nadzor vaše naprave."),
("android_service_will_start_tip", "Z vklopom zajema zaslona se bo samodejno zagnala storitev, ki omogoča da oddaljene naprave pošljejo zahtevo za povezavo na vašo napravo."),
("android_stop_service_tip", "Z zaustavitvijo storitve bodo samodejno prekinjene vse oddaljene povezave."),
("android_version_audio_tip", "Trenutna različica Androida ne omogoča zajema zvoka. Za zajem zvoka nadgradite na Android 10 ali novejši."),
("android_start_service_tip", "Tapnite »Zaženi storitev« ali »ODPRI« pri dovoljenju za zajem zaslona da zaženete storitev deljenja zaslona."),
("Account", "Račun"),
("Overwrite", "Prepiši"),
("This file exists, skip or overwrite this file?", "Datoteka obstaja, izpusti ali prepiši?"),
("Quit", "Izhod"),
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
("Help", "Pomoč"),
("Failed", "Ni uspelo"),
("Succeeded", "Uspelo"),
("Someone turns on privacy mode, exit", "Vklopljen je zasebni način, izhod"),
("Unsupported", "Ni podprto"),
("Peer denied", "Odjemalec zavrnil"),
("Please install plugins", "Namestite vključke"),
("Peer exit", "Odjemalec se je zaprl"),
("Failed to turn off", "Ni bilo mogoče izklopiti"),
("Turned off", "Izklopljeno"),
("In privacy mode", "V zasebnem načinu"),
("Out privacy mode", "Iz zasebnega načina"),
("Language", "Jezik"),
("Keep RustDesk background service", "Ohrani RustDeskovo storitev v ozadju"),
("Ignore Battery Optimizations", "Prezri optimizacije baterije"),
("android_open_battery_optimizations_tip", "Če želite izklopiti to možnost, pojdite v nastavitve aplikacije RustDesk, poiščite »Baterija« in izklopite »Neomejeno«"),
("Connection not allowed", "Povezava ni dovoljena"),
("Legacy mode", "Stari način"),
("Map mode", "Način preslikave"),
("Translate mode", "Način prevajanja"),
("Use permanent password", "Uporabi stalno geslo"),
("Use both passwords", "Uporabi obe gesli"),
("Set permanent password", "Nastavi stalno geslo"),
("Enable Remote Restart", "Omogoči oddaljeni ponovni zagon"),
("Allow remote restart", "Dovoli oddaljeni ponovni zagon"),
("Restart Remote Device", "Znova zaženi oddaljeno napravo"),
("Are you sure you want to restart", "Ali ste prepričani, da želite znova zagnati"),
("Restarting Remote Device", "Ponovni zagon oddaljene naprave"),
("remote_restarting_tip", "Oddaljena naprava se znova zaganja, prosim zaprite to sporočilo in se čez nekaj časa povežite s stalnim geslom."),
("Copied", "Kopirano"),
("Exit Fullscreen", "Izhod iz celozaslonskega načina"),
("Fullscreen", "Celozaslonski način"),
("Mobile Actions", "Dejanja za prenosne naprave"),
("Select Monitor", "Izberite zaslon"),
("Control Actions", "Dejanja za nadzor"),
("Display Settings", "Nastavitve zaslona"),
("Ratio", "Razmerje"),
("Image Quality", "Kakovost slike"),
("Scroll Style", "Način drsenja"),
("Show Menubar", "Prikaži meni"),
("Hide Menubar", "Skrij meni"),
("Direct Connection", "Neposredna povezava"),
("Relay Connection", "Posredovana povezava"),
("Secure Connection", "Zavarovana povezava"),
("Insecure Connection", "Nezavarovana povezava"),
("Scale original", "Originalna velikost"),
("Scale adaptive", "Prilagojena velikost"),
("General", "Splošno"),
("Security", "Varnost"),
("Theme", "Tema"),
("Dark Theme", "Temna tema"),
("Dark", "Temna"),
("Light", "Svetla"),
("Follow System", "Sistemska"),
("Enable hardware codec", "Omogoči strojno pospeševanje"),
("Unlock Security Settings", "Odkleni varnostne nastavitve"),
("Enable Audio", "Omogoči zvok"),
("Unlock Network Settings", "Odkleni mrežne nastavitve"),
("Server", "Strežnik"),
("Direct IP Access", "Neposredni dostop preko IPja"),
("Proxy", "Posredniški strežnik"),
("Apply", "Uveljavi"),
("Disconnect all devices?", "Odklopi vse naprave?"),
("Clear", "Počisti"),
("Audio Input Device", "Vhodna naprava za zvok"),
("Deny remote access", "Onemogoči oddaljeni dostop"),
("Use IP Whitelisting", "Omogoči seznam dovoljenih IP naslovov"),
("Network", "Mreža"),
("Enable RDP", "Omogoči RDP"),
("Pin menubar", "Pripni menijsko vrstico"),
("Unpin menubar", "Odpni menijsko vrstico"),
("Recording", "Snemanje"),
("Directory", "Imenik"),
("Automatically record incoming sessions", "Samodejno snemaj vhodne seje"),
("Change", "Spremeni"),
("Start session recording", "Začni snemanje seje"),
("Stop session recording", "Ustavi snemanje seje"),
("Enable Recording Session", "Omogoči snemanje seje"),
("Allow recording session", "Dovoli snemanje seje"),
("Enable LAN Discovery", "Omogoči odkrivanje lokalnega omrežja"),
("Deny LAN Discovery", "Onemogoči odkrivanje lokalnega omrežja"),
("Write a message", "Napiši spoorčilo"),
("Prompt", "Poziv"),
("Please wait for confirmation of UAC...", "Počakajte za potrditev nadzora uporabniškega računa"),
("elevated_foreground_window_tip", "Trenutno aktivno okno na oddaljenem računalniku zahteva višje pravice za upravljanje. Oddaljenega uporabnika lahko prosite, da okno minimizira, ali pa kliknite gumb za povzdig pravic v oknu za upravljanje povezave. Če se želite izogniti temu problemu, na oddaljenem računalniku RustDesk namestite."),
("Disconnected", "Brez povezave"),
("Other", "Drugo"),
("Confirm before closing multiple tabs", "Zahtevajte potrditev pred zapiranjem večih zavihkov"),
("Keyboard Settings", "Nastavitve tipkovnice"),
("Full Access", "Poln dostop"),
("Screen Share", "Deljenje zaslona"),
("Wayland requires Ubuntu 21.04 or higher version.", "Wayland zahteva Ubuntu 21.04 ali novejši"),
("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Zahtevana je novejša različica Waylanda. Posodobite vašo distribucijo ali pa uporabite X11."),
("JumpLink", "Pogled"),
("Please Select the screen to be shared(Operate on the peer side).", "Izberite zaslon za delitev (na oddaljeni strani)."),
("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"),
("Accept sessions via click", "Sprejmi seje s potrditvijo"),
("Accept sessions via both", "Sprejmi seje z geslom ali potrditvijo"),
("Please wait for the remote side to accept your session request...", "Počakajte, da oddaljeni računalnik sprejme povezavo..."),
("One-time Password", "Enkratno geslo"),
("Use one-time password", "Uporabi enkratno geslo"),
("One-time password length", "Dolžina enkratnega gesla"),
("Request access to your device", "Zahtevaj dostop do svoje naprave"),
("Hide connection management window", "Skrij okno za upravljanje povezave"),
("hide_cm_tip", "Dovoli skrivanje samo pri sprejemanju sej z geslom"),
("wayland_experiment_tip", "Podpora za Wayland je v preizkusni fazi. Uporabite X11, če rabite nespremljan dostop."),
("Right click to select tabs", "Desno-kliknite za izbiro zavihkov"),
("Skipped", "Izpuščeno"),
("Add to Address Book", "Dodaj v adresar"),
("Group", "Skupina"),
("Search", "Iskanje"),
("Closed manually by the web console", "Ročno zaprto iz spletne konzole"),
("Local keyboard type", "Lokalna vrsta tipkovnice"),
("Select local keyboard type", "Izberite lokalno vrsto tipkovnice"),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", "ถูกปิดโดยเว็บคอนโซล"),
("Local keyboard type", "ประเภทคีย์บอร์ด"),
("Select local keyboard type", "เลือกประเภทคีย์บอร์ด"),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", "被web控制台手動關閉"),
("Local keyboard type", "本地鍵盤類型"),
("Select local keyboard type", "請選擇本地鍵盤類型"),
("software_render_tip", "如果你使用英偉達顯卡, 並且遠程窗口在會話建立後會立刻關閉, 那麼安裝nouveau驅動並且選擇使用軟件渲染可能會有幫助。重啟軟件後生效。"),
("Always use software rendering", "使用軟件渲染"),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -410,5 +410,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Closed manually by the web console", ""),
("Local keyboard type", ""),
("Select local keyboard type", ""),
("software_render_tip", ""),
("Always use software rendering", ""),
("config_input", ""),
].iter().cloned().collect();
}

View File

@ -717,3 +717,92 @@ pub fn get_double_click_time() -> u32 {
}
}
/// forever: may not work
pub fn system_message(title: &str, msg: &str, forever: bool) -> ResultType<()> {
if std::process::Command::new("notify-send")
.arg(title)
.arg(msg)
.spawn()
.is_ok()
{
return Ok(());
}
if std::process::Command::new("zenity")
.arg("--info")
.arg("--timeout")
.arg(if forever { "0" } else { "3" })
.arg("--title")
.arg(title)
.arg("--text")
.arg(msg)
.spawn()
.is_ok()
{
return Ok(());
}
if std::process::Command::new("kdialog")
.arg("--title")
.arg(title)
.arg("--msgbox")
.arg(msg)
.spawn()
.is_ok()
{
return Ok(());
}
if std::process::Command::new("xmessage")
.arg("-center")
.arg("-timeout")
.arg(if forever { "0" } else { "3" })
.arg(title)
.arg(msg)
.spawn()
.is_ok()
{
return Ok(());
}
bail!("failed to post system message");
}
extern "C" fn breakdown_signal_handler(sig: i32) {
let mut stack = vec![];
backtrace::trace(|frame| {
backtrace::resolve_frame(frame, |symbol| {
if let Some(name) = symbol.name() {
stack.push(name.to_string());
}
});
true // keep going to the next frame
});
let mut info = String::default();
if stack.iter().any(|s| {
s.contains(&"nouveau_pushbuf_kick")
|| s.to_lowercase().contains("nvidia")
|| s.contains("gdk_window_end_draw_frame")
}) {
hbb_common::config::Config::set_option(
"allow-always-software-render".to_string(),
"Y".to_string(),
);
info = "Always use software rendering will be set.".to_string();
log::info!("{}", info);
}
log::error!(
"Got signal {} and exit. stack:\n{}",
sig,
stack.join("\n").to_string()
);
system_message(
"RustDesk",
&format!("Got signal {} and exit.{}", sig, info),
true,
)
.ok();
std::process::exit(0);
}
pub fn register_breakdown_handler() {
unsafe {
libc::signal(libc::SIGSEGV, breakdown_signal_handler as _);
}
}

36
src/platform/macos.mm Normal file
View File

@ -0,0 +1,36 @@
#import <AVFoundation/AVFoundation.h>
#import <AppKit/AppKit.h>
#import <IOKit/hidsystem/IOHIDLib.h>
// https://github.com/codebytere/node-mac-permissions/blob/main/permissions.mm
extern "C" bool InputMonitoringAuthStatus(bool prompt) {
if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_15) {
IOHIDAccessType theType = IOHIDCheckAccess(kIOHIDRequestTypeListenEvent);
NSLog(@"IOHIDCheckAccess = %d, kIOHIDAccessTypeGranted = %d", theType, kIOHIDAccessTypeGranted);
switch (theType) {
case kIOHIDAccessTypeGranted:
return true;
break;
case kIOHIDAccessTypeDenied: {
if (prompt) {
NSString *urlString = @"x-apple.systempreferences:com.apple.preference.security?Privacy_ListenEvent";
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]];
}
break;
}
case kIOHIDAccessTypeUnknown: {
if (prompt) {
bool result = IOHIDRequestAccess(kIOHIDRequestTypeListenEvent);
NSLog(@"IOHIDRequestAccess result = %d", result);
}
break;
}
default:
break;
}
} else {
return true;
}
return false;
}

View File

@ -32,6 +32,7 @@ extern "C" {
fn CGEventGetLocation(e: *const c_void) -> CGPoint;
static kAXTrustedCheckOptionPrompt: CFStringRef;
fn AXIsProcessTrustedWithOptions(options: CFDictionaryRef) -> BOOL;
fn InputMonitoringAuthStatus(_: BOOL) -> BOOL;
}
pub fn is_process_trusted(prompt: bool) -> bool {
@ -47,6 +48,13 @@ pub fn is_process_trusted(prompt: bool) -> bool {
}
}
pub fn is_can_input_monitoring(prompt: bool) -> bool {
unsafe {
let value = if prompt { YES } else { NO };
InputMonitoringAuthStatus(value) == YES
}
}
// macOS >= 10.15
// https://stackoverflow.com/questions/56597221/detecting-screen-recording-settings-on-macos-catalina/
// remove just one app from all the permissions: tccutil reset All com.carriez.rustdesk

View File

@ -556,27 +556,39 @@ pub fn handle_mouse_(evt: &MouseEvent) {
en.mouse_move_to(evt.x, evt.y);
}
1 => match buttons {
1 => {
0x01 => {
allow_err!(en.mouse_down(MouseButton::Left));
}
2 => {
0x02 => {
allow_err!(en.mouse_down(MouseButton::Right));
}
4 => {
0x04 => {
allow_err!(en.mouse_down(MouseButton::Middle));
}
0x08 => {
allow_err!(en.mouse_down(MouseButton::Back));
}
0x10 => {
allow_err!(en.mouse_down(MouseButton::Forward));
}
_ => {}
},
2 => match buttons {
1 => {
0x01 => {
en.mouse_up(MouseButton::Left);
}
2 => {
0x02 => {
en.mouse_up(MouseButton::Right);
}
4 => {
0x04 => {
en.mouse_up(MouseButton::Middle);
}
0x08 => {
en.mouse_up(MouseButton::Back);
}
0x10 => {
en.mouse_up(MouseButton::Forward);
}
_ => {}
},
3 | 4 => {

View File

@ -582,6 +582,14 @@ pub fn is_installed_daemon(_prompt: bool) -> bool {
return true;
}
#[inline]
pub fn is_can_input_monitoring(_prompt: bool) -> bool {
#[cfg(target_os = "macos")]
return crate::platform::macos::is_can_input_monitoring(_prompt);
#[cfg(not(target_os = "macos"))]
return true;
}
#[inline]
pub fn get_error() -> String {
#[cfg(not(any(feature = "cli")))]