mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge branch 'rustdesk:master' into feat/optional_update_check
This commit is contained in:
commit
dccc791c99
16
.github/workflows/vcpkg-deps-linux.yml
vendored
16
.github/workflows/vcpkg-deps-linux.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
||||
path: /opt/artifacts
|
||||
key: vcpkg-${{ matrix.job.arch }}
|
||||
|
||||
- uses: Kingtous/run-on-arch-action@amd64-support
|
||||
- uses: rustdesk-org/run-on-arch-action@amd64-support
|
||||
name: Run vcpkg install on ${{ matrix.job.arch }}
|
||||
id: vcpkg
|
||||
with:
|
||||
@ -40,12 +40,16 @@ jobs:
|
||||
apt update -y
|
||||
case "${{ matrix.job.arch }}" in
|
||||
x86_64)
|
||||
# CMake 3.15+
|
||||
apt install -y gpg wget ca-certificates
|
||||
echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ bionic main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null
|
||||
wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null
|
||||
apt update -y
|
||||
apt install -y curl zip unzip tar git cmake g++ gcc build-essential pkg-config wget nasm yasm ninja-build libjpeg8-dev
|
||||
apt install -y curl zip unzip tar git g++ gcc build-essential pkg-config wget nasm yasm ninja-build libjpeg8-dev libssl-dev
|
||||
wget https://github.com/Kitware/CMake/releases/download/v3.20.2/cmake-3.20.2.tar.gz
|
||||
apt remove -y --purge cmake
|
||||
tar -zxvf cmake-3.20.2.tar.gz
|
||||
cd cmake-3.20.2
|
||||
./bootstrap
|
||||
make
|
||||
make install
|
||||
cd -
|
||||
cmake --version
|
||||
gcc -v
|
||||
;;
|
||||
|
||||
BIN
flutter/assets/scam.png
Normal file
BIN
flutter/assets/scam.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 627 KiB |
@ -101,6 +101,7 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||
required this.highlight,
|
||||
required this.drag_indicator,
|
||||
required this.shadow,
|
||||
required this.errorBannerBg,
|
||||
});
|
||||
|
||||
final Color? border;
|
||||
@ -108,6 +109,7 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||
final Color? highlight;
|
||||
final Color? drag_indicator;
|
||||
final Color? shadow;
|
||||
final Color? errorBannerBg;
|
||||
|
||||
static final light = ColorThemeExtension(
|
||||
border: Color(0xFFCCCCCC),
|
||||
@ -115,6 +117,7 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||
highlight: Color(0xFFE5E5E5),
|
||||
drag_indicator: Colors.grey[800],
|
||||
shadow: Colors.black,
|
||||
errorBannerBg: Color(0xFFFDEEEB),
|
||||
);
|
||||
|
||||
static final dark = ColorThemeExtension(
|
||||
@ -123,6 +126,7 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||
highlight: Color(0xFF3F3F3F),
|
||||
drag_indicator: Colors.grey,
|
||||
shadow: Colors.grey,
|
||||
errorBannerBg: Color(0xFF470F2D),
|
||||
);
|
||||
|
||||
@override
|
||||
@ -132,6 +136,7 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||
Color? highlight,
|
||||
Color? drag_indicator,
|
||||
Color? shadow,
|
||||
Color? errorBannerBg,
|
||||
}) {
|
||||
return ColorThemeExtension(
|
||||
border: border ?? this.border,
|
||||
@ -139,6 +144,7 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||
highlight: highlight ?? this.highlight,
|
||||
drag_indicator: drag_indicator ?? this.drag_indicator,
|
||||
shadow: shadow ?? this.shadow,
|
||||
errorBannerBg: errorBannerBg ?? this.errorBannerBg,
|
||||
);
|
||||
}
|
||||
|
||||
@ -154,6 +160,7 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||
highlight: Color.lerp(highlight, other.highlight, t),
|
||||
drag_indicator: Color.lerp(drag_indicator, other.drag_indicator, t),
|
||||
shadow: Color.lerp(shadow, other.shadow, t),
|
||||
errorBannerBg: Color.lerp(shadow, other.errorBannerBg, t),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -258,6 +265,14 @@ class MyTheme {
|
||||
? EdgeInsets.only(left: dialogPadding)
|
||||
: EdgeInsets.only(left: dialogPadding / 3);
|
||||
|
||||
static ScrollbarThemeData scrollbarTheme = ScrollbarThemeData(
|
||||
thickness: MaterialStateProperty.all(kScrollbarThickness),
|
||||
);
|
||||
|
||||
static ScrollbarThemeData scrollbarThemeDark = scrollbarTheme.copyWith(
|
||||
thumbColor: MaterialStateProperty.all(Colors.grey[500]),
|
||||
);
|
||||
|
||||
static ThemeData lightTheme = ThemeData(
|
||||
brightness: Brightness.light,
|
||||
hoverColor: Color.fromARGB(255, 224, 224, 224),
|
||||
@ -273,6 +288,7 @@ class MyTheme {
|
||||
),
|
||||
),
|
||||
),
|
||||
scrollbarTheme: scrollbarTheme,
|
||||
inputDecorationTheme: isDesktop
|
||||
? InputDecorationTheme(
|
||||
fillColor: grayBg,
|
||||
@ -357,6 +373,7 @@ class MyTheme {
|
||||
),
|
||||
),
|
||||
),
|
||||
scrollbarTheme: scrollbarThemeDark,
|
||||
inputDecorationTheme: isDesktop
|
||||
? InputDecorationTheme(
|
||||
fillColor: Color(0xFF24252B),
|
||||
@ -383,9 +400,6 @@ class MyTheme {
|
||||
tabBarTheme: const TabBarTheme(
|
||||
labelColor: Colors.white70,
|
||||
),
|
||||
scrollbarTheme: ScrollbarThemeData(
|
||||
thumbColor: MaterialStateProperty.all(Colors.grey[500]),
|
||||
),
|
||||
tooltipTheme: tooltipTheme(),
|
||||
splashColor: isDesktop ? Colors.transparent : null,
|
||||
highlightColor: isDesktop ? Colors.transparent : null,
|
||||
|
||||
@ -76,7 +76,7 @@ class _AddressBookState extends State<AddressBook> {
|
||||
child: Center(
|
||||
child: Container(
|
||||
height: height,
|
||||
color: Color.fromARGB(255, 253, 238, 235),
|
||||
color: MyTheme.color(context).errorBannerBg,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
|
||||
@ -302,6 +302,53 @@ Future<String> changeDirectAccessPort(
|
||||
return controller.text;
|
||||
}
|
||||
|
||||
Future<String> changeAutoDisconnectTimeout(String old) async {
|
||||
final controller = TextEditingController(text: old);
|
||||
await gFFI.dialogManager.show((setState, close, context) {
|
||||
return CustomAlertDialog(
|
||||
title: Text(translate("Timeout in minutes")),
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 8.0),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
maxLines: null,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: InputDecoration(
|
||||
hintText: '10',
|
||||
isCollapsed: true,
|
||||
suffix: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: const Icon(Icons.clear, size: 16),
|
||||
onPressed: () => controller.clear())),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(
|
||||
r'^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$')),
|
||||
],
|
||||
controller: controller,
|
||||
autofocus: true),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
dialogButton("Cancel", onPressed: close, isOutline: true),
|
||||
dialogButton("OK", onPressed: () async {
|
||||
await bind.mainSetOption(
|
||||
key: 'auto-disconnect-timeout', value: controller.text);
|
||||
close();
|
||||
}),
|
||||
],
|
||||
onCancel: close,
|
||||
);
|
||||
});
|
||||
return controller.text;
|
||||
}
|
||||
|
||||
class DialogTextField extends StatelessWidget {
|
||||
final String title;
|
||||
final String? hintText;
|
||||
|
||||
@ -13,6 +13,8 @@ const String kPeerPlatformLinux = "Linux";
|
||||
const String kPeerPlatformMacOS = "Mac OS";
|
||||
const String kPeerPlatformAndroid = "Android";
|
||||
|
||||
const double kScrollbarThickness = 12.0;
|
||||
|
||||
/// [kAppTypeMain] used by 'Desktop Main Page' , 'Mobile (Client and Server)', "Install Page"
|
||||
const String kAppTypeMain = "main";
|
||||
|
||||
|
||||
@ -732,6 +732,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
reverse: true, enabled: enabled),
|
||||
...directIp(context),
|
||||
whitelist(),
|
||||
...autoDisconnect(context),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -910,6 +911,63 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
));
|
||||
}));
|
||||
}
|
||||
|
||||
List<Widget> autoDisconnect(BuildContext context) {
|
||||
TextEditingController controller = TextEditingController();
|
||||
update() => setState(() {});
|
||||
RxBool applyEnabled = false.obs;
|
||||
final optionKey = 'allow-auto-disconnect';
|
||||
final timeoutKey = 'auto-disconnect-timeout';
|
||||
return [
|
||||
_OptionCheckBox(context, 'auto_disconnect_option_tip', optionKey,
|
||||
update: update, enabled: !locked),
|
||||
() {
|
||||
bool enabled =
|
||||
option2bool(optionKey, bind.mainGetOptionSync(key: optionKey));
|
||||
if (!enabled) applyEnabled.value = false;
|
||||
controller.text = bind.mainGetOptionSync(key: timeoutKey);
|
||||
return Offstage(
|
||||
offstage: !enabled,
|
||||
child: _SubLabeledWidget(
|
||||
context,
|
||||
'Timeout in minutes',
|
||||
Row(children: [
|
||||
SizedBox(
|
||||
width: 95,
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
enabled: enabled && !locked,
|
||||
onChanged: (_) => applyEnabled.value = true,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(
|
||||
r'^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$')),
|
||||
],
|
||||
decoration: const InputDecoration(
|
||||
hintText: '10',
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(vertical: 12, horizontal: 12),
|
||||
),
|
||||
).marginOnly(right: 15),
|
||||
),
|
||||
Obx(() => ElevatedButton(
|
||||
onPressed: applyEnabled.value && enabled && !locked
|
||||
? () async {
|
||||
applyEnabled.value = false;
|
||||
await bind.mainSetOption(
|
||||
key: timeoutKey, value: controller.text);
|
||||
}
|
||||
: null,
|
||||
child: Text(
|
||||
translate('Apply'),
|
||||
),
|
||||
))
|
||||
]),
|
||||
enabled: enabled && !locked,
|
||||
),
|
||||
);
|
||||
}(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class _Network extends StatefulWidget {
|
||||
@ -1541,9 +1599,14 @@ Widget _OptionCheckBox(BuildContext context, String label, String key,
|
||||
isServer
|
||||
? await mainSetBoolOption(key, option)
|
||||
: await mainSetLocalBoolOption(key, option);
|
||||
ref.value = isServer
|
||||
final readOption = isServer
|
||||
? mainGetBoolOptionSync(key)
|
||||
: mainGetLocalBoolOptionSync(key);
|
||||
if (reverse) {
|
||||
ref.value = !readOption;
|
||||
} else {
|
||||
ref.value = readOption;
|
||||
}
|
||||
update?.call();
|
||||
}
|
||||
}
|
||||
|
||||
@ -694,6 +694,7 @@ class _ImagePaintState extends State<ImagePaint> {
|
||||
enableCustomMouseWheelScrolling: cursorOverImage.isFalse,
|
||||
customMouseWheelScrollConfig: scrollConfig,
|
||||
child: RawScrollbar(
|
||||
thickness: kScrollbarThickness,
|
||||
thumbColor: Colors.grey,
|
||||
controller: _horizontal,
|
||||
thumbVisibility: false,
|
||||
@ -711,6 +712,7 @@ class _ImagePaintState extends State<ImagePaint> {
|
||||
enableCustomMouseWheelScrolling: cursorOverImage.isFalse,
|
||||
customMouseWheelScrollConfig: scrollConfig,
|
||||
child: RawScrollbar(
|
||||
thickness: kScrollbarThickness,
|
||||
thumbColor: Colors.grey,
|
||||
controller: _vertical,
|
||||
thumbVisibility: false,
|
||||
|
||||
@ -210,11 +210,199 @@ class ServiceNotRunningNotification extends StatelessWidget {
|
||||
.marginOnly(bottom: 8),
|
||||
ElevatedButton.icon(
|
||||
icon: const Icon(Icons.play_arrow),
|
||||
onPressed: serverModel.toggleService,
|
||||
onPressed: () {
|
||||
if (gFFI.userModel.userName.value.isEmpty && bind.mainGetLocalOption(key: "show-scam-warning") != "N") {
|
||||
_showScamWarning(context, serverModel);
|
||||
} else {
|
||||
serverModel.toggleService();
|
||||
}
|
||||
},
|
||||
label: Text(translate("Start Service")))
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
void _showScamWarning(BuildContext context, ServerModel serverModel) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return ScamWarningDialog(serverModel: serverModel);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ScamWarningDialog extends StatefulWidget {
|
||||
final ServerModel serverModel;
|
||||
|
||||
ScamWarningDialog({required this.serverModel});
|
||||
|
||||
@override
|
||||
_ScamWarningDialogState createState() => _ScamWarningDialogState();
|
||||
}
|
||||
|
||||
class _ScamWarningDialogState extends State<ScamWarningDialog> {
|
||||
int _countdown = 12;
|
||||
bool show_warning = false;
|
||||
late Timer _timer;
|
||||
late ServerModel _serverModel;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_serverModel = widget.serverModel;
|
||||
startCountdown();
|
||||
}
|
||||
|
||||
void startCountdown() {
|
||||
const oneSecond = Duration(seconds: 1);
|
||||
_timer = Timer.periodic(oneSecond, (timer) {
|
||||
setState(() {
|
||||
_countdown--;
|
||||
if (_countdown <= 0) {
|
||||
timer.cancel();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_timer.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isButtonLocked = _countdown > 0;
|
||||
|
||||
return AlertDialog(
|
||||
content: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topRight,
|
||||
end: Alignment.bottomLeft,
|
||||
colors: [
|
||||
Color(0xffe242bc),
|
||||
Color(0xfff4727c),
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
padding: EdgeInsets.all(25.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.warning_amber_sharp,
|
||||
color: Colors.white,
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(
|
||||
translate("Warning"),
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 20.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Center(
|
||||
child: Image.asset('assets/scam.png',
|
||||
width: 180,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 18),
|
||||
Text(
|
||||
translate("scam_title"),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22.0,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 18),
|
||||
Text(
|
||||
translate("scam_text1")+"\n\n"
|
||||
+translate("scam_text2")+"\n",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16.0,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Checkbox(
|
||||
value: show_warning,
|
||||
onChanged: (value) {
|
||||
setState((){
|
||||
show_warning = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
Text(
|
||||
translate("Don't show again"),
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 15.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: isButtonLocked
|
||||
? null
|
||||
: () {
|
||||
Navigator.of(context).pop();
|
||||
_serverModel.toggleService();
|
||||
if (show_warning) {
|
||||
bind.mainSetLocalOption(key: "show-scam-warning", value: "N");
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
primary: Colors.blueAccent,
|
||||
),
|
||||
child: Text(
|
||||
isButtonLocked ? translate("I Agree")+" (${_countdown}s)" : translate("I Agree"),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 13.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 15),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
primary: Colors.blueAccent,
|
||||
),
|
||||
child: Text(
|
||||
translate("Decline"),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 13.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)])),
|
||||
contentPadding: EdgeInsets.all(0.0),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ServerInfo extends StatelessWidget {
|
||||
|
||||
@ -45,10 +45,12 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
var _enableDirectIPAccess = false;
|
||||
var _enableRecordSession = false;
|
||||
var _autoRecordIncomingSession = false;
|
||||
var _allowAutoDisconnect = false;
|
||||
var _localIP = "";
|
||||
var _directAccessPort = "";
|
||||
var _fingerprint = "";
|
||||
var _buildDate = "";
|
||||
var _autoDisconnectTimeout = "";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -151,6 +153,20 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
_buildDate = buildDate;
|
||||
}
|
||||
|
||||
final allowAutoDisconnect = option2bool('allow-auto-disconnect',
|
||||
await bind.mainGetOption(key: 'allow-auto-disconnect'));
|
||||
if (allowAutoDisconnect != _allowAutoDisconnect) {
|
||||
update = true;
|
||||
_allowAutoDisconnect = allowAutoDisconnect;
|
||||
}
|
||||
|
||||
final autoDisconnectTimeout =
|
||||
await bind.mainGetOption(key: 'auto-disconnect-timeout');
|
||||
if (autoDisconnectTimeout != _autoDisconnectTimeout) {
|
||||
update = true;
|
||||
_autoDisconnectTimeout = autoDisconnectTimeout;
|
||||
}
|
||||
|
||||
if (update) {
|
||||
setState(() {});
|
||||
}
|
||||
@ -306,6 +322,48 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
await bind.mainSetOption(key: 'direct-server', value: value);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
SettingsTile.switchTile(
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(translate("auto_disconnect_option_tip")),
|
||||
Offstage(
|
||||
offstage: !_allowAutoDisconnect,
|
||||
child: Text(
|
||||
'${_autoDisconnectTimeout.isEmpty ? '10' : _autoDisconnectTimeout} min',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
)),
|
||||
])),
|
||||
Offstage(
|
||||
offstage: !_allowAutoDisconnect,
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: Icon(
|
||||
Icons.edit,
|
||||
size: 20,
|
||||
),
|
||||
onPressed: () async {
|
||||
final timeout = await changeAutoDisconnectTimeout(
|
||||
_autoDisconnectTimeout);
|
||||
setState(() {
|
||||
_autoDisconnectTimeout = timeout;
|
||||
});
|
||||
}))
|
||||
]),
|
||||
initialValue: _allowAutoDisconnect,
|
||||
onToggle: (_) async {
|
||||
_allowAutoDisconnect = !_allowAutoDisconnect;
|
||||
String value =
|
||||
bool2option('allow-auto-disconnect', _allowAutoDisconnect);
|
||||
await bind.mainSetOption(key: 'allow-auto-disconnect', value: value);
|
||||
setState(() {});
|
||||
},
|
||||
)
|
||||
];
|
||||
if (_hasIgnoreBattery) {
|
||||
|
||||
@ -404,7 +404,7 @@ pub fn core_main() -> Option<Vec<String>> {
|
||||
crate::ui_interface::start_option_status_sync();
|
||||
} else if args[0] == "--cm-no-ui" {
|
||||
#[cfg(feature = "flutter")]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios", target_os = "windows")))]
|
||||
crate::flutter::connection_manager::start_cm_no_ui();
|
||||
return None;
|
||||
} else {
|
||||
|
||||
@ -157,6 +157,7 @@ fn handle_config_options(config_options: HashMap<String, String>) {
|
||||
Config::set_options(options);
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[cfg(not(any(target_os = "ios")))]
|
||||
pub fn is_pro() -> bool {
|
||||
PRO.lock().unwrap().clone()
|
||||
|
||||
12
src/ipc.rs
12
src/ipc.rs
@ -234,6 +234,8 @@ pub enum Data {
|
||||
#[cfg(windows)]
|
||||
SyncWinCpuUsage(Option<f64>),
|
||||
FileTransferLog(String),
|
||||
#[cfg(any(windows, target_os = "macos"))]
|
||||
ControlledSessionCount(usize),
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
@ -482,6 +484,16 @@ async fn handle(data: Data, stream: &mut Connection) {
|
||||
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
Data::Plugin(plugin) => crate::plugin::ipc::handle_plugin(plugin, stream).await,
|
||||
#[cfg(any(windows, target_os = "macos"))]
|
||||
Data::ControlledSessionCount(_) => {
|
||||
allow_err!(
|
||||
stream
|
||||
.send(&Data::ControlledSessionCount(
|
||||
crate::Connection::alive_conns().len()
|
||||
))
|
||||
.await
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
62
src/lang.rs
62
src/lang.rs
@ -1,5 +1,7 @@
|
||||
use hbb_common::regex::Regex;
|
||||
use std::ops::Deref;
|
||||
|
||||
mod ar;
|
||||
mod ca;
|
||||
mod cn;
|
||||
mod cs;
|
||||
@ -17,6 +19,7 @@ mod it;
|
||||
mod ja;
|
||||
mod ko;
|
||||
mod kz;
|
||||
mod lt;
|
||||
mod nl;
|
||||
mod pl;
|
||||
mod ptbr;
|
||||
@ -32,8 +35,6 @@ mod tr;
|
||||
mod tw;
|
||||
mod ua;
|
||||
mod vn;
|
||||
mod lt;
|
||||
mod ar;
|
||||
|
||||
pub const LANGS: &[(&str, &str)] = &[
|
||||
("en", "English"),
|
||||
@ -137,16 +138,67 @@ pub fn translate_locale(name: String, locale: &str) -> String {
|
||||
"ar" => ar::T.deref(),
|
||||
_ => en::T.deref(),
|
||||
};
|
||||
let (name, placeholder_value) = extract_placeholder(&name);
|
||||
let replace = |s: &&str| {
|
||||
let mut s = s.to_string();
|
||||
if let Some(value) = placeholder_value.as_ref() {
|
||||
s = s.replace("{}", &value);
|
||||
}
|
||||
s
|
||||
};
|
||||
if let Some(v) = m.get(&name as &str) {
|
||||
if v.is_empty() {
|
||||
if lang != "en" {
|
||||
if let Some(v) = en::T.get(&name as &str) {
|
||||
return v.to_string();
|
||||
return replace(v);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return v.to_string();
|
||||
return replace(v);
|
||||
}
|
||||
}
|
||||
name
|
||||
replace(&name.as_str())
|
||||
}
|
||||
|
||||
// Matching pattern is {}
|
||||
// Write {value} in the UI and {} in the translation file
|
||||
//
|
||||
// Example:
|
||||
// Write in the UI: translate("There are {24} hours in a day")
|
||||
// Write in the translation file: ("There are {} hours in a day", "{} hours make up a day")
|
||||
fn extract_placeholder(input: &str) -> (String, Option<String>) {
|
||||
if let Ok(re) = Regex::new(r#"\{(.*?)\}"#) {
|
||||
if let Some(captures) = re.captures(input) {
|
||||
if let Some(inner_match) = captures.get(1) {
|
||||
let name = re.replace(input, "{}").to_string();
|
||||
let value = inner_match.as_str().to_string();
|
||||
return (name, Some(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
(input.to_string(), None)
|
||||
}
|
||||
|
||||
mod test {
|
||||
#[test]
|
||||
fn test_extract_placeholders() {
|
||||
use super::extract_placeholder as f;
|
||||
|
||||
assert_eq!(f(""), ("".to_string(), None));
|
||||
assert_eq!(
|
||||
f("{3} sessions"),
|
||||
("{} sessions".to_string(), Some("3".to_string()))
|
||||
);
|
||||
assert_eq!(f(" } { "), (" } { ".to_string(), None));
|
||||
// Allow empty value
|
||||
assert_eq!(
|
||||
f("{} sessions"),
|
||||
("{} sessions".to_string(), Some("".to_string()))
|
||||
);
|
||||
// Match only the first one
|
||||
assert_eq!(
|
||||
f("{2} times {4} makes {8}"),
|
||||
("{} times {4} makes {8}".to_string(), Some("2".to_string()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", "安装成功!"),
|
||||
("Installation failed!", "安装失败!"),
|
||||
("Reverse mouse wheel", "鼠标滚轮反向"),
|
||||
("{} sessions", "{}个会话"),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", "超时(分钟)"),
|
||||
("auto_disconnect_option_tip", "自动关闭不活跃的会话"),
|
||||
("Connection failed due to inactivity", "由于长时间无操作, 连接被自动断开"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -543,6 +543,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("HSV Color", "HSV-Farbe"),
|
||||
("Installation Successful!", "Installation erfolgreich!"),
|
||||
("Installation failed!", "Installation fehlgeschlagen!"),
|
||||
("Reverse mouse wheel", ""),
|
||||
("Reverse mouse wheel", "Mausrad rückwärts drehen"),
|
||||
("{} sessions", "{} Sitzungen"),
|
||||
("scam_title", "Sie werden möglicherweise BETROGEN!"),
|
||||
("scam_text1", "Wenn Sie mit jemandem telefonieren, den Sie NICHT KENNEN, dem Sie NICHT VERTRAUEN und der Sie gebeten hat, RustDesk zu benutzen und den Dienst zu starten, fahren Sie nicht fort und legen Sie sofort auf."),
|
||||
("scam_text2", "Es handelt sich wahrscheinlich um einen Betrüger, der versucht, Ihr Geld oder andere private Informationen zu stehlen."),
|
||||
("Don't show again", "Nicht mehr anzeigen"),
|
||||
("I Agree", "Ich bin einverstanden"),
|
||||
("Decline", "Ablehnen"),
|
||||
("Timeout in minutes", "Zeitüberschreitung in Minuten"),
|
||||
("auto_disconnect_option_tip", "Automatisches Schließen eingehender Sitzungen bei Inaktivität des Benutzers"),
|
||||
("Connection failed due to inactivity", "Automatische Trennung der Verbindung aufgrund von Inaktivität"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -82,5 +82,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Start session recording", "Start Session Recording"),
|
||||
("Stop session recording", "Stop Session Recording"),
|
||||
("Enable remote configuration modification", "Enable Remote Configuration Modification"),
|
||||
("scam_title", "You May Be Being SCAMMED!"),
|
||||
("scam_text1", "If you are on the phone with someone you DON'T know AND TRUST who has asked you to use RustDesk and start the service, do not proceed and hang up immediately."),
|
||||
("scam_text2", "They are likely a scammer trying to steal your money or other private information."),
|
||||
("Don't show again", "Don't show again"),
|
||||
("I Agree", "I Agree"),
|
||||
("Decline", "Decline"),
|
||||
("auto_disconnect_option_tip", "Automatically close incoming sessions on user inactivity"),
|
||||
("Connection failed due to inactivity", "Automatically disconnected due to inactivity"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", "Instalación exitosa"),
|
||||
("Installation failed!", "La instalación ha fallado"),
|
||||
("Reverse mouse wheel", "Invertir rueda del ratón"),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -541,8 +541,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Change Color", "Ganti warna"),
|
||||
("Primary Color", "Warna utama"),
|
||||
("HSV Color", "Warna HSV"),
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("Installation Successful!", "Instalasi berhasil!"),
|
||||
("Installation failed!", "Instalasi gagal!"),
|
||||
("Reverse mouse wheel", "Balikkan arah scroll mouse!"),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -75,9 +75,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Do you want to enter again?", "Vuoi riprovare?"),
|
||||
("Connection Error", "Errore di connessione"),
|
||||
("Error", "Errore"),
|
||||
("Reset by the peer", "Reimpostata dal peer"),
|
||||
("Reset by the peer", "Reimpostata dal dispositivo remoto"),
|
||||
("Connecting...", "Connessione..."),
|
||||
("Connection in progress. Please wait.", "Connessione in corso..."),
|
||||
("Connection in progress. Please wait.", "Connessione..."),
|
||||
("Please try 1 minute later", "Riprova fra 1 minuto"),
|
||||
("Login Error", "Errore accesso"),
|
||||
("Successful", "Completato"),
|
||||
@ -210,7 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Settings", "Impostazioni"),
|
||||
("Username", "Nome utente"),
|
||||
("Invalid port", "Numero porta non valido"),
|
||||
("Closed manually by the peer", "Chiuso manualmente dal peer"),
|
||||
("Closed manually by the peer", "Chiuso manualmente dal dispositivo remoto"),
|
||||
("Enable remote configuration modification", "Abilita la modifica remota della configurazione"),
|
||||
("Run without install", "Esegui senza installare"),
|
||||
("Connect via relay", "Collegati tramite relay"),
|
||||
@ -301,9 +301,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Succeeded", "Completato"),
|
||||
("Someone turns on privacy mode, exit", "Qualcuno ha attivato la modalità privacy, uscita"),
|
||||
("Unsupported", "Non supportato"),
|
||||
("Peer denied", "Peer negato"),
|
||||
("Peer denied", "Acvesso negato al dispositivo remoto"),
|
||||
("Please install plugins", "Installa i plugin"),
|
||||
("Peer exit", "Uscita peer"),
|
||||
("Peer exit", "Uscita dal dispostivo remoto"),
|
||||
("Failed to turn off", "Impossibile spegnere"),
|
||||
("Turned off", "Spegni"),
|
||||
("In privacy mode", "In modalità privacy"),
|
||||
@ -392,7 +392,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Wayland requires Ubuntu 21.04 or higher version.", "Wayland richiede Ubuntu 21.04 o versione successiva."),
|
||||
("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland richiede una versione superiore della distribuzione Linux.\nProva X11 desktop o cambia il sistema operativo."),
|
||||
("JumpLink", "Vai a"),
|
||||
("Please Select the screen to be shared(Operate on the peer side).", "Seleziona lo schermo da condividere (opera sul lato peer)."),
|
||||
("Please Select the screen to be shared(Operate on the peer side).", "Seleziona lo schermo da condividere (opera sul lato dispositivo remoto)."),
|
||||
("Show RustDesk", "Visualizza RustDesk"),
|
||||
("This PC", "Questo PC"),
|
||||
("or", "O"),
|
||||
@ -477,7 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Empty Username", "Nome utente vuoto"),
|
||||
("Empty Password", "Password vuota"),
|
||||
("Me", "Io"),
|
||||
("identical_file_tip", "Questo file è identico a quello del peer."),
|
||||
("identical_file_tip", "Questo file è identico a quello nel dispositivo remoto."),
|
||||
("show_monitors_tip", "Visualizza schermi nella barra strumenti"),
|
||||
("View Mode", "Modalità visualizzazione"),
|
||||
("login_linux_tip", "Accedi all'account Linux remoto"),
|
||||
@ -498,8 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Fingerprint", "Firma digitale"),
|
||||
("Copy Fingerprint", "Copia firma digitale"),
|
||||
("no fingerprints", "Nessuna firma digitale"),
|
||||
("Select a peer", "Seleziona un peer"),
|
||||
("Select peers", "Seleziona peer"),
|
||||
("Select a peer", "Seleziona dispositivo remoto"),
|
||||
("Select peers", "Seleziona dispositivi remoti"),
|
||||
("Plugins", "Plugin"),
|
||||
("Uninstall", "Disinstalla"),
|
||||
("Update", "Aggiorna"),
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", "Installazione completata"),
|
||||
("Installation failed!", "Installazione fallita"),
|
||||
("Reverse mouse wheel", "Rotella mouse inversa"),
|
||||
("{} sessions", "{} sessioni"),
|
||||
("scam_title", "Potresti essere stato TRUFFATO!"),
|
||||
("scam_text1", "Se sei al telefono con qualcuno che NON conosci NON DI TUA FIDUCIA che ti ha chiesto di usare RustDesk e di avviare il servizio, non procedere e riattacca subito."),
|
||||
("scam_text2", "Probabilmente è un truffatore che cerca di rubare i tuoi soldi o altre informazioni private."),
|
||||
("Don't show again", "Non visualizzare più"),
|
||||
("I Agree", "Accetto"),
|
||||
("Decline", "Non accetto"),
|
||||
("Timeout in minutes", "Timeout in minuti"),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", "Connessione non riuscita a causa di inattività"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -528,21 +528,31 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Move tab to new window", "Przenieś zakładkę do nowego okna"),
|
||||
("Can not be empty", "Nie może być puste"),
|
||||
("Already exists", "Już istnieje"),
|
||||
("Change Password", ""),
|
||||
("Refresh Password", ""),
|
||||
("ID", ""),
|
||||
("Grid View", ""),
|
||||
("List View", ""),
|
||||
("Select", ""),
|
||||
("Toggle Tags", ""),
|
||||
("pull_ab_failed_tip", ""),
|
||||
("push_ab_failed_tip", ""),
|
||||
("synced_peer_readded_tip", ""),
|
||||
("Change Color", ""),
|
||||
("Primary Color", ""),
|
||||
("HSV Color", ""),
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("Change Password", "Zmień hasło"),
|
||||
("Refresh Password", "Odśwież hasło"),
|
||||
("ID", "ID"),
|
||||
("Grid View", "Widok siatki"),
|
||||
("List View", "Widok listy"),
|
||||
("Select", "Wybierz"),
|
||||
("Toggle Tags", "Przełącz tagi"),
|
||||
("pull_ab_failed_tip", "Aktualizacja książki adresowej nie powiodła się"),
|
||||
("push_ab_failed_tip", "Nie udało się zsynchronizować książki adresowej z serwerem"),
|
||||
("synced_peer_readded_tip", "Urządzenia, które były obecne w ostatnich sesjach, zostaną ponownie dodane do książki adresowej"),
|
||||
("Change Color", "Zmień kolor"),
|
||||
("Primary Color", "Kolor podstawowy"),
|
||||
("HSV Color", "Kolor HSV"),
|
||||
("Installation Successful!", "Instalacja zakończona!"),
|
||||
("Installation failed!", "Instalacja nie powiodła się"),
|
||||
("Reverse mouse wheel", "Odwróć rolkę myszki"),
|
||||
("{} sessions", "{} sesji"),
|
||||
("scam_title", "Prawdopodobnie zostałeś OSZUKANY!"),
|
||||
("scam_text1", "Jeżeli rozmawiasz przez telefon z osobą której NIE ZNASZ i NIE UFASZ, która prosi Cię o uruchomienie programu RustDesk i uruchomienia usługi - nie rób tego i natychmiast się rozłącz."),
|
||||
("scam_text2", "Wygląda to na oszusta, który próbuje ukraść Twoje pieniądze lub inne prywatne informacje."),
|
||||
("Don't show again", "Nie pokazuj więcej"),
|
||||
("I Agree", "Zgadzam się"),
|
||||
("Decline", "Odmawiam"),
|
||||
("Timeout in minutes", "Czas bezczynności w minutach"),
|
||||
("auto_disconnect_option_tip", "Automatycznie rozłącz sesje przychodzące przy braku aktywności użytkownika"),
|
||||
("Connection failed due to inactivity", "Automatycznie rozłącz przy bezczynności"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", "Установка выполнена успешно!"),
|
||||
("Installation failed!", "Установка не выполнена!"),
|
||||
("Reverse mouse wheel", "Реверсировать колесо мыши"),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -544,5 +544,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Installation Successful!", ""),
|
||||
("Installation failed!", ""),
|
||||
("Reverse mouse wheel", ""),
|
||||
("{} sessions", ""),
|
||||
("scam_title", ""),
|
||||
("scam_text1", ""),
|
||||
("scam_text2", ""),
|
||||
("Don't show again", ""),
|
||||
("I Agree", ""),
|
||||
("Decline", ""),
|
||||
("Timeout in minutes", ""),
|
||||
("auto_disconnect_option_tip", ""),
|
||||
("Connection failed due to inactivity", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -1183,6 +1183,7 @@ pub fn uninstall_service(show_new_window: bool) -> bool {
|
||||
}
|
||||
|
||||
pub fn install_service() -> bool {
|
||||
let _installing = crate::platform::InstallingService::new();
|
||||
if !has_cmd("systemctl") {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -23,9 +23,18 @@ pub mod linux_desktop_manager;
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
use hbb_common::{message_proto::CursorData, ResultType};
|
||||
use std::sync::{Arc, Mutex};
|
||||
#[cfg(not(any(target_os = "macos", target_os = "android", target_os = "ios")))]
|
||||
const SERVICE_INTERVAL: u64 = 300;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref INSTALLING_SERVICE: Arc<Mutex<bool>>= Default::default();
|
||||
}
|
||||
|
||||
pub fn installing_service() -> bool {
|
||||
INSTALLING_SERVICE.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn is_xfce() -> bool {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
@ -71,6 +80,21 @@ pub fn get_active_username() -> String {
|
||||
#[cfg(target_os = "android")]
|
||||
pub const PA_SAMPLE_RATE: u32 = 48000;
|
||||
|
||||
pub(crate) struct InstallingService; // please use new
|
||||
|
||||
impl InstallingService {
|
||||
pub fn new() -> Self {
|
||||
*INSTALLING_SERVICE.lock().unwrap() = true;
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for InstallingService {
|
||||
fn drop(&mut self) {
|
||||
*INSTALLING_SERVICE.lock().unwrap() = false;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@ -2167,6 +2167,7 @@ pub fn uninstall_service(show_new_window: bool) -> bool {
|
||||
|
||||
pub fn install_service() -> bool {
|
||||
log::info!("Installing service...");
|
||||
let _installing = crate::platform::InstallingService::new();
|
||||
let (_, _, _, exe) = get_install_info();
|
||||
let tmp_path = std::env::temp_dir().to_string_lossy().to_string();
|
||||
let tray_shortcut = get_tray_shortcut(&exe, &tmp_path).unwrap_or_default();
|
||||
|
||||
@ -79,7 +79,9 @@ impl RendezvousMediator {
|
||||
crate::platform::linux_desktop_manager::start_xdesktop();
|
||||
loop {
|
||||
Config::reset_online();
|
||||
if Config::get_option("stop-service").is_empty() {
|
||||
if Config::get_option("stop-service").is_empty()
|
||||
&& !crate::platform::installing_service()
|
||||
{
|
||||
if !nat_tested {
|
||||
crate::test_nat_type();
|
||||
nat_tested = true;
|
||||
|
||||
@ -204,6 +204,7 @@ pub struct Connection {
|
||||
delay_response_instant: Instant,
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
start_cm_ipc_para: Option<StartCmIpcPara>,
|
||||
auto_disconnect_timer: Option<(Instant, u64)>,
|
||||
}
|
||||
|
||||
impl ConnInner {
|
||||
@ -343,6 +344,7 @@ impl Connection {
|
||||
rx_desktop_ready,
|
||||
tx_cm_stream_ready,
|
||||
}),
|
||||
auto_disconnect_timer: None,
|
||||
};
|
||||
let addr = hbb_common::try_into_v4(addr);
|
||||
if !conn.on_open(addr).await {
|
||||
@ -605,6 +607,13 @@ impl Connection {
|
||||
_ = second_timer.tick() => {
|
||||
#[cfg(windows)]
|
||||
conn.portable_check();
|
||||
if let Some((instant, minute)) = conn.auto_disconnect_timer.as_ref() {
|
||||
if instant.elapsed().as_secs() > minute * 60 {
|
||||
conn.send_close_reason_no_retry("Connection failed due to inactivity").await;
|
||||
conn.on_close("auto disconnect", true).await;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = test_delay_timer.tick() => {
|
||||
if last_recv_time.elapsed() >= SEC30 {
|
||||
@ -1139,6 +1148,7 @@ impl Connection {
|
||||
let mut s = s.write().unwrap();
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let _h = try_start_record_cursor_pos();
|
||||
self.auto_disconnect_timer = Self::get_auto_disconenct_timer();
|
||||
s.add_connection(self.inner.clone(), &noperms);
|
||||
}
|
||||
}
|
||||
@ -1351,6 +1361,12 @@ impl Connection {
|
||||
log::error!("ipc to connection manager exit: {}", err);
|
||||
}
|
||||
});
|
||||
#[cfg(all(windows, feature = "flutter"))]
|
||||
std::thread::spawn(|| {
|
||||
if crate::is_server() && !crate::check_process("--tray", false) {
|
||||
crate::platform::run_as_user(vec!["--tray"]).ok();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1606,6 +1622,7 @@ impl Connection {
|
||||
}
|
||||
self.input_mouse(me, self.inner.id());
|
||||
}
|
||||
self.update_auto_disconnect_timer();
|
||||
}
|
||||
Some(message::Union::PointerDeviceEvent(pde)) => {
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
@ -1641,6 +1658,7 @@ impl Connection {
|
||||
MOUSE_MOVE_TIME.store(get_time(), Ordering::SeqCst);
|
||||
self.input_pointer(pde, self.inner.id());
|
||||
}
|
||||
self.update_auto_disconnect_timer();
|
||||
}
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
Some(message::Union::KeyEvent(..)) => {}
|
||||
@ -1696,6 +1714,7 @@ impl Connection {
|
||||
self.input_key(me, false);
|
||||
}
|
||||
}
|
||||
self.update_auto_disconnect_timer();
|
||||
}
|
||||
Some(message::Union::Clipboard(_cb)) =>
|
||||
{
|
||||
@ -1884,6 +1903,7 @@ impl Connection {
|
||||
Some(misc::Union::ChatMessage(c)) => {
|
||||
self.send_to_cm(ipc::Data::ChatMessage { text: c.text });
|
||||
self.chat_unanswered = true;
|
||||
self.update_auto_disconnect_timer();
|
||||
}
|
||||
Some(misc::Union::Option(o)) => {
|
||||
self.update_options(&o).await;
|
||||
@ -1892,6 +1912,7 @@ impl Connection {
|
||||
if r {
|
||||
super::video_service::refresh();
|
||||
}
|
||||
self.update_auto_disconnect_timer();
|
||||
}
|
||||
Some(misc::Union::VideoReceived(_)) => {
|
||||
video_service::notify_video_frame_fetched(
|
||||
@ -2021,6 +2042,7 @@ impl Connection {
|
||||
let mut msg = Message::new();
|
||||
msg.set_misc(misc);
|
||||
self.send(msg).await;
|
||||
self.update_auto_disconnect_timer();
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
@ -2278,7 +2300,7 @@ impl Connection {
|
||||
lock_screen().await;
|
||||
}
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let data = if self.chat_unanswered || self.file_transferred {
|
||||
let data = if self.chat_unanswered || self.file_transferred && cfg!(feature = "flutter") {
|
||||
ipc::Data::Disconnected
|
||||
} else {
|
||||
ipc::Data::Close
|
||||
@ -2378,6 +2400,26 @@ impl Connection {
|
||||
}
|
||||
self.pressed_modifiers.clear();
|
||||
}
|
||||
|
||||
fn get_auto_disconenct_timer() -> Option<(Instant, u64)> {
|
||||
if Config::get_option("allow-auto-disconnect") == "Y" {
|
||||
let mut minute: u64 = Config::get_option("auto-disconnect-timeout")
|
||||
.parse()
|
||||
.unwrap_or(10);
|
||||
if minute == 0 {
|
||||
minute = 10;
|
||||
}
|
||||
Some((Instant::now(), minute))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn update_auto_disconnect_timer(&mut self) {
|
||||
self.auto_disconnect_timer
|
||||
.as_mut()
|
||||
.map(|t| t.0 = Instant::now());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) {
|
||||
@ -2406,6 +2448,8 @@ async fn start_ipc(
|
||||
if let Ok(s) = crate::ipc::connect(1000, "_cm").await {
|
||||
stream = Some(s);
|
||||
} else {
|
||||
#[allow(unused_mut)]
|
||||
#[allow(unused_assignments)]
|
||||
let mut args = vec!["--cm"];
|
||||
if crate::hbbs_http::sync::is_pro() && password::hide_cm() {
|
||||
args.push("--hide");
|
||||
|
||||
98
src/tray.rs
98
src/tray.rs
@ -1,5 +1,11 @@
|
||||
use crate::{client::translate, ipc::Data};
|
||||
use hbb_common::{allow_err, log, tokio};
|
||||
use std::{
|
||||
sync::{Arc, Mutex},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
pub fn start_tray() {
|
||||
use hbb_common::{allow_err, log};
|
||||
allow_err!(make_tray());
|
||||
}
|
||||
|
||||
@ -40,29 +46,42 @@ pub fn make_tray() -> hbb_common::ResultType<()> {
|
||||
let event_loop = EventLoopBuilder::new().build();
|
||||
|
||||
let tray_menu = Menu::new();
|
||||
let quit_i = MenuItem::new(crate::client::translate("Exit".to_owned()), true, None);
|
||||
let open_i = MenuItem::new(crate::client::translate("Open".to_owned()), true, None);
|
||||
let quit_i = MenuItem::new(translate("Exit".to_owned()), true, None);
|
||||
let open_i = MenuItem::new(translate("Open".to_owned()), true, None);
|
||||
tray_menu.append_items(&[&open_i, &quit_i]);
|
||||
|
||||
let _tray_icon = Some(
|
||||
TrayIconBuilder::new()
|
||||
.with_menu(Box::new(tray_menu))
|
||||
.with_tooltip(format!(
|
||||
let tooltip = |count: usize| {
|
||||
if count == 0 {
|
||||
format!(
|
||||
"{} {}",
|
||||
crate::get_app_name(),
|
||||
crate::lang::translate("Service is running".to_owned())
|
||||
))
|
||||
translate("Service is running".to_owned()),
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{} - {}\n{}",
|
||||
crate::get_app_name(),
|
||||
translate("Ready".to_owned()),
|
||||
translate("{".to_string() + &format!("{count}") + "} sessions"),
|
||||
)
|
||||
}
|
||||
};
|
||||
let tray_icon = Some(
|
||||
TrayIconBuilder::new()
|
||||
.with_menu(Box::new(tray_menu))
|
||||
.with_tooltip(tooltip(0))
|
||||
.with_icon(icon)
|
||||
.build()?,
|
||||
);
|
||||
let tray_icon = Arc::new(Mutex::new(tray_icon));
|
||||
|
||||
let menu_channel = MenuEvent::receiver();
|
||||
let tray_channel = TrayEvent::receiver();
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let (ipc_sender, ipc_receiver) = std::sync::mpsc::channel::<Data>();
|
||||
let mut docker_hiden = false;
|
||||
|
||||
let open_func = move || {
|
||||
if cfg!(not(feature = "flutter"))
|
||||
{
|
||||
if cfg!(not(feature = "flutter")) {
|
||||
crate::run_me::<&str>(vec![]).ok();
|
||||
return;
|
||||
}
|
||||
@ -89,6 +108,11 @@ pub fn make_tray() -> hbb_common::ResultType<()> {
|
||||
}
|
||||
};
|
||||
|
||||
// ubuntu 22.04 can't see tooltip
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
std::thread::spawn(move || {
|
||||
start_query_session_count(ipc_sender.clone());
|
||||
});
|
||||
event_loop.run(move |_event, _, control_flow| {
|
||||
if !docker_hiden {
|
||||
#[cfg(target_os = "macos")]
|
||||
@ -121,5 +145,55 @@ pub fn make_tray() -> hbb_common::ResultType<()> {
|
||||
open_func();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
if let Ok(data) = ipc_receiver.try_recv() {
|
||||
match data {
|
||||
Data::ControlledSessionCount(count) => {
|
||||
tray_icon
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.map(|t| t.set_tooltip(Some(tooltip(count))));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn start_query_session_count(sender: std::sync::mpsc::Sender<Data>) {
|
||||
let mut last_count = 0;
|
||||
loop {
|
||||
if let Ok(mut c) = crate::ipc::connect(1000, "").await {
|
||||
let mut timer = tokio::time::interval(Duration::from_secs(1));
|
||||
loop {
|
||||
tokio::select! {
|
||||
res = c.next() => {
|
||||
match res {
|
||||
Err(err) => {
|
||||
log::error!("ipc connection closed: {}", err);
|
||||
break;
|
||||
}
|
||||
|
||||
Ok(Some(Data::ControlledSessionCount(count))) => {
|
||||
if count != last_count {
|
||||
last_count = count;
|
||||
sender.send(Data::ControlledSessionCount(count)).ok();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
_ = timer.tick() => {
|
||||
c.send(&Data::ControlledSessionCount(0)).await.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hbb_common::sleep(1.).await;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user