Merge pull request #2051 from fufesou/save_remote_menubar_pin_2
Save remote menubar pin 2
This commit is contained in:
commit
03e7b48d6f
@ -15,7 +15,6 @@ import 'package:flutter_hbb/main.dart';
|
|||||||
import 'package:flutter_hbb/models/peer_model.dart';
|
import 'package:flutter_hbb/models/peer_model.dart';
|
||||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:uni_links/uni_links.dart';
|
import 'package:uni_links/uni_links.dart';
|
||||||
import 'package:uni_links_desktop/uni_links_desktop.dart';
|
import 'package:uni_links_desktop/uni_links_desktop.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
@ -205,18 +204,17 @@ class MyTheme {
|
|||||||
);
|
);
|
||||||
|
|
||||||
static ThemeMode getThemeModePreference() {
|
static ThemeMode getThemeModePreference() {
|
||||||
return themeModeFromString(
|
return themeModeFromString(bind.mainGetLocalOption(key: kCommConfKeyTheme));
|
||||||
Get.find<SharedPreferences>().getString("themeMode") ?? "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void changeDarkMode(ThemeMode mode) {
|
static void changeDarkMode(ThemeMode mode) {
|
||||||
final preference = getThemeModePreference();
|
final preference = getThemeModePreference();
|
||||||
if (preference != mode) {
|
if (preference != mode) {
|
||||||
if (mode == ThemeMode.system) {
|
if (mode == ThemeMode.system) {
|
||||||
Get.find<SharedPreferences>().setString("themeMode", "");
|
bind.mainSetLocalOption(key: kCommConfKeyTheme, value: '');
|
||||||
} else {
|
} else {
|
||||||
Get.find<SharedPreferences>()
|
bind.mainSetLocalOption(
|
||||||
.setString("themeMode", mode.toShortString());
|
key: kCommConfKeyTheme, value: mode.toShortString());
|
||||||
}
|
}
|
||||||
Get.changeThemeMode(mode);
|
Get.changeThemeMode(mode);
|
||||||
if (desktopType == DesktopType.main) {
|
if (desktopType == DesktopType.main) {
|
||||||
@ -1026,8 +1024,8 @@ Future<void> saveWindowPosition(WindowType type, {int? windowId}) async {
|
|||||||
final isMaximized = await windowManager.isMaximized();
|
final isMaximized = await windowManager.isMaximized();
|
||||||
final pos = LastWindowPosition(
|
final pos = LastWindowPosition(
|
||||||
sz.width, sz.height, position.dx, position.dy, isMaximized);
|
sz.width, sz.height, position.dx, position.dy, isMaximized);
|
||||||
await Get.find<SharedPreferences>()
|
await bind.setLocalFlutterConfig(
|
||||||
.setString(kWindowPrefix + type.name, pos.toString());
|
k: kWindowPrefix + type.name, v: pos.toString());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
final wc = WindowController.fromWindowId(windowId!);
|
final wc = WindowController.fromWindowId(windowId!);
|
||||||
@ -1037,9 +1035,10 @@ Future<void> saveWindowPosition(WindowType type, {int? windowId}) async {
|
|||||||
final isMaximized = await wc.isMaximized();
|
final isMaximized = await wc.isMaximized();
|
||||||
final pos = LastWindowPosition(
|
final pos = LastWindowPosition(
|
||||||
sz.width, sz.height, position.dx, position.dy, isMaximized);
|
sz.width, sz.height, position.dx, position.dy, isMaximized);
|
||||||
debugPrint("saving frame: ${windowId}: ${pos.width}/${pos.height}, offset:${pos.offsetWidth}/${pos.offsetHeight}");
|
debugPrint(
|
||||||
await Get.find<SharedPreferences>()
|
"saving frame: $windowId: ${pos.width}/${pos.height}, offset:${pos.offsetWidth}/${pos.offsetHeight}");
|
||||||
.setString(kWindowPrefix + type.name, pos.toString());
|
await bind.setLocalFlutterConfig(
|
||||||
|
k: kWindowPrefix + type.name, v: pos.toString());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1109,7 +1108,7 @@ Future<Offset?> _adjustRestoreMainWindowOffset(
|
|||||||
.toDouble();
|
.toDouble();
|
||||||
|
|
||||||
if (isDesktop || isWebDesktop) {
|
if (isDesktop || isWebDesktop) {
|
||||||
for(final screen in await window_size.getScreenList()) {
|
for (final screen in await window_size.getScreenList()) {
|
||||||
frameLeft = min(screen.visibleFrame.left, frameLeft);
|
frameLeft = min(screen.visibleFrame.left, frameLeft);
|
||||||
frameTop = min(screen.visibleFrame.top, frameTop);
|
frameTop = min(screen.visibleFrame.top, frameTop);
|
||||||
frameRight = max(screen.visibleFrame.right, frameRight);
|
frameRight = max(screen.visibleFrame.right, frameRight);
|
||||||
@ -1136,13 +1135,7 @@ Future<bool> restoreWindowPosition(WindowType type, {int? windowId}) async {
|
|||||||
debugPrint(
|
debugPrint(
|
||||||
"Error: windowId cannot be null when saving positions for sub window");
|
"Error: windowId cannot be null when saving positions for sub window");
|
||||||
}
|
}
|
||||||
final pos =
|
final pos = bind.getLocalFlutterConfig(k: kWindowPrefix + type.name);
|
||||||
Get.find<SharedPreferences>().getString(kWindowPrefix + type.name);
|
|
||||||
|
|
||||||
if (pos == null) {
|
|
||||||
debugPrint("no window position saved, ignore restore");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var lpos = LastWindowPosition.loadFromString(pos);
|
var lpos = LastWindowPosition.loadFromString(pos);
|
||||||
if (lpos == null) {
|
if (lpos == null) {
|
||||||
debugPrint("window position saved, but cannot be parsed");
|
debugPrint("window position saved, but cannot be parsed");
|
||||||
@ -1175,7 +1168,8 @@ Future<bool> restoreWindowPosition(WindowType type, {int? windowId}) async {
|
|||||||
await _adjustRestoreMainWindowSize(lpos.width, lpos.height);
|
await _adjustRestoreMainWindowSize(lpos.width, lpos.height);
|
||||||
final offset = await _adjustRestoreMainWindowOffset(
|
final offset = await _adjustRestoreMainWindowOffset(
|
||||||
lpos.offsetWidth, lpos.offsetHeight);
|
lpos.offsetWidth, lpos.offsetHeight);
|
||||||
debugPrint("restore lpos: ${size.width}/${size.height}, offset:${offset?.dx}/${offset?.dy}");
|
debugPrint(
|
||||||
|
"restore lpos: ${size.width}/${size.height}, offset:${offset?.dx}/${offset?.dy}");
|
||||||
if (offset == null) {
|
if (offset == null) {
|
||||||
await wc.center();
|
await wc.center();
|
||||||
} else {
|
} else {
|
||||||
@ -1327,8 +1321,7 @@ void connect(BuildContext context, String id,
|
|||||||
|
|
||||||
Future<Map<String, String>> getHttpHeaders() async {
|
Future<Map<String, String>> getHttpHeaders() async {
|
||||||
return {
|
return {
|
||||||
'Authorization':
|
'Authorization': 'Bearer ${bind.mainGetLocalOption(key: 'access_token')}'
|
||||||
'Bearer ${await bind.mainGetLocalOption(key: 'access_token')}'
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,27 +22,26 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
() async {
|
setPeer();
|
||||||
await bind.mainGetLocalOption(key: 'peer-tab-index').then((value) {
|
|
||||||
if (value == '') return;
|
|
||||||
final tab = int.parse(value);
|
|
||||||
_tabIndex.value = tab;
|
|
||||||
});
|
|
||||||
await bind.mainGetLocalOption(key: 'peer-card-ui-type').then((value) {
|
|
||||||
if (value == '') return;
|
|
||||||
final tab = int.parse(value);
|
|
||||||
peerCardUiType.value =
|
|
||||||
tab == PeerUiType.list.index ? PeerUiType.list : PeerUiType.grid;
|
|
||||||
});
|
|
||||||
}();
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPeer() {
|
||||||
|
final index = bind.getLocalFlutterConfig(k: 'peer-tab-index');
|
||||||
|
if (index == '') return;
|
||||||
|
_tabIndex.value = int.parse(index);
|
||||||
|
|
||||||
|
final uiType = bind.getLocalFlutterConfig(k: 'peer-card-ui-type');
|
||||||
|
if (uiType == '') return;
|
||||||
|
peerCardUiType.value = int.parse(uiType) == PeerUiType.list.index
|
||||||
|
? PeerUiType.list
|
||||||
|
: PeerUiType.grid;
|
||||||
|
}
|
||||||
|
|
||||||
// hard code for now
|
// hard code for now
|
||||||
Future<void> _handleTabSelection(int index) async {
|
Future<void> _handleTabSelection(int index) async {
|
||||||
_tabIndex.value = index;
|
_tabIndex.value = index;
|
||||||
await bind.mainSetLocalOption(
|
await bind.setLocalFlutterConfig(k: 'peer-tab-index', v: index.toString());
|
||||||
key: 'peer-tab-index', value: index.toString());
|
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 0:
|
case 0:
|
||||||
bind.mainLoadRecentPeers();
|
bind.mainLoadRecentPeers();
|
||||||
@ -148,9 +147,8 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
decoration: peerCardUiType.value == type ? activeDeco : null,
|
decoration: peerCardUiType.value == type ? activeDeco : null,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await bind.mainSetLocalOption(
|
await bind.setLocalFlutterConfig(
|
||||||
key: 'peer-card-ui-type',
|
k: 'peer-card-ui-type', v: type.index.toString());
|
||||||
value: type.index.toString());
|
|
||||||
peerCardUiType.value = type;
|
peerCardUiType.value = type;
|
||||||
},
|
},
|
||||||
child: Icon(
|
child: Icon(
|
||||||
|
@ -56,7 +56,11 @@ var kWindowEdgeSize = Platform.isWindows ? 1.0 : 5.0;
|
|||||||
const kWindowBorderWidth = 1.0;
|
const kWindowBorderWidth = 1.0;
|
||||||
const kDesktopMenuPadding = EdgeInsets.only(left: 12.0, right: 3.0);
|
const kDesktopMenuPadding = EdgeInsets.only(left: 12.0, right: 3.0);
|
||||||
|
|
||||||
const kInvalidValueStr = "InvalidValueStr";
|
const kInvalidValueStr = 'InvalidValueStr';
|
||||||
|
|
||||||
|
// Config key shared by flutter and other ui.
|
||||||
|
const kCommConfKeyTheme = 'theme';
|
||||||
|
const kCommConfKeyLang = 'lang';
|
||||||
|
|
||||||
const kMobilePageConstraints = BoxConstraints(maxWidth: 600);
|
const kMobilePageConstraints = BoxConstraints(maxWidth: 600);
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import 'package:file_picker/file_picker.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
|
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/login.dart';
|
import 'package:flutter_hbb/desktop/widgets/login.dart';
|
||||||
@ -30,8 +31,8 @@ const double _kListViewBottomMargin = 15;
|
|||||||
const double _kTitleFontSize = 20;
|
const double _kTitleFontSize = 20;
|
||||||
const double _kContentFontSize = 15;
|
const double _kContentFontSize = 15;
|
||||||
const Color _accentColor = MyTheme.accent;
|
const Color _accentColor = MyTheme.accent;
|
||||||
const String _kSettingPageControllerTag = "settingPageController";
|
const String _kSettingPageControllerTag = 'settingPageController';
|
||||||
const String _kSettingPageIndexTag = "settingPageIndex";
|
const String _kSettingPageIndexTag = 'settingPageIndex';
|
||||||
|
|
||||||
class _TabInfo {
|
class _TabInfo {
|
||||||
late final String label;
|
late final String label;
|
||||||
@ -250,19 +251,19 @@ class _GeneralState extends State<_General> {
|
|||||||
|
|
||||||
return _Card(title: 'Theme', children: [
|
return _Card(title: 'Theme', children: [
|
||||||
_Radio<String>(context,
|
_Radio<String>(context,
|
||||||
value: "light",
|
value: 'light',
|
||||||
groupValue: current,
|
groupValue: current,
|
||||||
label: "Light",
|
label: 'Light',
|
||||||
onChanged: onChanged),
|
onChanged: onChanged),
|
||||||
_Radio<String>(context,
|
_Radio<String>(context,
|
||||||
value: "dark",
|
value: 'dark',
|
||||||
groupValue: current,
|
groupValue: current,
|
||||||
label: "Dark",
|
label: 'Dark',
|
||||||
onChanged: onChanged),
|
onChanged: onChanged),
|
||||||
_Radio<String>(context,
|
_Radio<String>(context,
|
||||||
value: "system",
|
value: 'system',
|
||||||
groupValue: current,
|
groupValue: current,
|
||||||
label: "Follow System",
|
label: 'Follow System',
|
||||||
onChanged: onChanged),
|
onChanged: onChanged),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -286,8 +287,8 @@ class _GeneralState extends State<_General> {
|
|||||||
|
|
||||||
Widget audio(BuildContext context) {
|
Widget audio(BuildContext context) {
|
||||||
String getDefault() {
|
String getDefault() {
|
||||||
if (Platform.isWindows) return "System Sound";
|
if (Platform.isWindows) return 'System Sound';
|
||||||
return "";
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getValue() async {
|
Future<String> getValue() async {
|
||||||
@ -300,7 +301,7 @@ class _GeneralState extends State<_General> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setDevice(String device) {
|
setDevice(String device) {
|
||||||
if (device == getDefault()) device = "";
|
if (device == getDefault()) device = '';
|
||||||
bind.mainSetOption(key: 'audio-input', value: device);
|
bind.mainSetOption(key: 'audio-input', value: device);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,7 +354,7 @@ class _GeneralState extends State<_General> {
|
|||||||
'allow-auto-record-incoming'),
|
'allow-auto-record-incoming'),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text('${translate('Directory')}:'),
|
Text('${translate("Directory")}:'),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: canlaunch ? () => launchUrl(Uri.file(dir)) : null,
|
onTap: canlaunch ? () => launchUrl(Uri.file(dir)) : null,
|
||||||
@ -386,26 +387,26 @@ class _GeneralState extends State<_General> {
|
|||||||
Widget language() {
|
Widget language() {
|
||||||
return _futureBuilder(future: () async {
|
return _futureBuilder(future: () async {
|
||||||
String langs = await bind.mainGetLangs();
|
String langs = await bind.mainGetLangs();
|
||||||
String lang = await bind.mainGetLocalOption(key: "lang");
|
String lang = bind.mainGetLocalOption(key: kCommConfKeyLang);
|
||||||
return {"langs": langs, "lang": lang};
|
return {'langs': langs, 'lang': lang};
|
||||||
}(), hasData: (res) {
|
}(), hasData: (res) {
|
||||||
Map<String, String> data = res as Map<String, String>;
|
Map<String, String> data = res as Map<String, String>;
|
||||||
List<dynamic> langsList = jsonDecode(data["langs"]!);
|
List<dynamic> langsList = jsonDecode(data['langs']!);
|
||||||
Map<String, String> langsMap = {for (var v in langsList) v[0]: v[1]};
|
Map<String, String> langsMap = {for (var v in langsList) v[0]: v[1]};
|
||||||
List<String> keys = langsMap.keys.toList();
|
List<String> keys = langsMap.keys.toList();
|
||||||
List<String> values = langsMap.values.toList();
|
List<String> values = langsMap.values.toList();
|
||||||
keys.insert(0, "");
|
keys.insert(0, '');
|
||||||
values.insert(0, "Default");
|
values.insert(0, 'Default');
|
||||||
String currentKey = data["lang"]!;
|
String currentKey = data['lang']!;
|
||||||
if (!keys.contains(currentKey)) {
|
if (!keys.contains(currentKey)) {
|
||||||
currentKey = "";
|
currentKey = '';
|
||||||
}
|
}
|
||||||
return _ComboBox(
|
return _ComboBox(
|
||||||
keys: keys,
|
keys: keys,
|
||||||
values: values,
|
values: values,
|
||||||
initialKey: currentKey,
|
initialKey: currentKey,
|
||||||
onChanged: (key) async {
|
onChanged: (key) async {
|
||||||
await bind.mainSetLocalOption(key: "lang", value: key);
|
await bind.mainSetLocalOption(key: kCommConfKeyLang, value: key);
|
||||||
reloadAllWindows();
|
reloadAllWindows();
|
||||||
bind.mainChangeLanguage(lang: key);
|
bind.mainChangeLanguage(lang: key);
|
||||||
},
|
},
|
||||||
@ -585,9 +586,9 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
kUseBothPasswords,
|
kUseBothPasswords,
|
||||||
];
|
];
|
||||||
List<String> values = [
|
List<String> values = [
|
||||||
translate("Use temporary password"),
|
translate('Use temporary password'),
|
||||||
translate("Use permanent password"),
|
translate('Use permanent password'),
|
||||||
translate("Use both passwords"),
|
translate('Use both passwords'),
|
||||||
];
|
];
|
||||||
bool tmpEnabled = model.verificationMethod != kUsePermanentPassword;
|
bool tmpEnabled = model.verificationMethod != kUsePermanentPassword;
|
||||||
bool permEnabled = model.verificationMethod != kUseTemporaryPassword;
|
bool permEnabled = model.verificationMethod != kUseTemporaryPassword;
|
||||||
@ -830,12 +831,12 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin {
|
|||||||
// Setting page is not modal, oldOptions should only be used when getting options, never when setting.
|
// Setting page is not modal, oldOptions should only be used when getting options, never when setting.
|
||||||
Map<String, dynamic> oldOptions = jsonDecode(data! as String);
|
Map<String, dynamic> oldOptions = jsonDecode(data! as String);
|
||||||
old(String key) {
|
old(String key) {
|
||||||
return (oldOptions[key] ?? "").trim();
|
return (oldOptions[key] ?? '').trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
RxString idErrMsg = "".obs;
|
RxString idErrMsg = ''.obs;
|
||||||
RxString relayErrMsg = "".obs;
|
RxString relayErrMsg = ''.obs;
|
||||||
RxString apiErrMsg = "".obs;
|
RxString apiErrMsg = ''.obs;
|
||||||
var idController =
|
var idController =
|
||||||
TextEditingController(text: old('custom-rendezvous-server'));
|
TextEditingController(text: old('custom-rendezvous-server'));
|
||||||
var relayController = TextEditingController(text: old('relay-server'));
|
var relayController = TextEditingController(text: old('relay-server'));
|
||||||
@ -864,9 +865,9 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin {
|
|||||||
}
|
}
|
||||||
if (apiServer.isNotEmpty) {
|
if (apiServer.isNotEmpty) {
|
||||||
if (!apiServer.startsWith('http://') ||
|
if (!apiServer.startsWith('http://') ||
|
||||||
!apiServer.startsWith("https://")) {
|
!apiServer.startsWith('https://')) {
|
||||||
apiErrMsg.value =
|
apiErrMsg.value =
|
||||||
"${translate("API Server")}: ${translate("invalid_http")}";
|
'${translate("API Server")}: ${translate("invalid_http")}';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -893,7 +894,7 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin {
|
|||||||
import() {
|
import() {
|
||||||
Clipboard.getData(Clipboard.kTextPlain).then((value) {
|
Clipboard.getData(Clipboard.kTextPlain).then((value) {
|
||||||
TextEditingController mytext = TextEditingController();
|
TextEditingController mytext = TextEditingController();
|
||||||
String? aNullableString = "";
|
String? aNullableString = '';
|
||||||
aNullableString = value?.text;
|
aNullableString = value?.text;
|
||||||
mytext.text = aNullableString.toString();
|
mytext.text = aNullableString.toString();
|
||||||
if (mytext.text.isNotEmpty) {
|
if (mytext.text.isNotEmpty) {
|
||||||
@ -918,13 +919,13 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
showToast(translate("Invalid server configuration"));
|
showToast(translate('Invalid server configuration'));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showToast(translate("Invalid server configuration"));
|
showToast(translate('Invalid server configuration'));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
showToast(translate("Clipboard is empty"));
|
showToast(translate('Clipboard is empty'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -936,7 +937,7 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin {
|
|||||||
config['ApiServer'] = apiController.text.trim();
|
config['ApiServer'] = apiController.text.trim();
|
||||||
config['Key'] = keyController.text.trim();
|
config['Key'] = keyController.text.trim();
|
||||||
Clipboard.setData(ClipboardData(text: jsonEncode(config)));
|
Clipboard.setData(ClipboardData(text: jsonEncode(config)));
|
||||||
showToast(translate("Export server configuration successfully"));
|
showToast(translate('Export server configuration successfully'));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool secure = !enabled;
|
bool secure = !enabled;
|
||||||
@ -962,7 +963,7 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin {
|
|||||||
Obx(() => _LabeledTextField(context, 'API Server', apiController,
|
Obx(() => _LabeledTextField(context, 'API Server', apiController,
|
||||||
apiErrMsg.value, enabled, secure)),
|
apiErrMsg.value, enabled, secure)),
|
||||||
_LabeledTextField(
|
_LabeledTextField(
|
||||||
context, 'Key', keyController, "", enabled, secure),
|
context, 'Key', keyController, '', enabled, secure),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [_Button('Apply', submit, enabled: enabled)],
|
children: [_Button('Apply', submit, enabled: enabled)],
|
||||||
@ -1039,28 +1040,28 @@ class _AboutState extends State<_About> {
|
|||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
physics: NeverScrollableScrollPhysics(),
|
physics: NeverScrollableScrollPhysics(),
|
||||||
child: _Card(title: "About RustDesk", children: [
|
child: _Card(title: 'About RustDesk', children: [
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8.0,
|
height: 8.0,
|
||||||
),
|
),
|
||||||
Text("Version: $version").marginSymmetric(vertical: 4.0),
|
Text('Version: $version').marginSymmetric(vertical: 4.0),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
launchUrlString("https://rustdesk.com/privacy");
|
launchUrlString('https://rustdesk.com/privacy');
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: const Text(
|
||||||
"Privacy Statement",
|
'Privacy Statement',
|
||||||
style: linkStyle,
|
style: linkStyle,
|
||||||
).marginSymmetric(vertical: 4.0)),
|
).marginSymmetric(vertical: 4.0)),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
launchUrlString("https://rustdesk.com");
|
launchUrlString('https://rustdesk.com');
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: const Text(
|
||||||
"Website",
|
'Website',
|
||||||
style: linkStyle,
|
style: linkStyle,
|
||||||
).marginSymmetric(vertical: 4.0)),
|
).marginSymmetric(vertical: 4.0)),
|
||||||
Container(
|
Container(
|
||||||
@ -1074,11 +1075,11 @@ class _AboutState extends State<_About> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Copyright © 2022 Purslane Ltd.\n$license",
|
'Copyright © 2022 Purslane Ltd.\n$license',
|
||||||
style: const TextStyle(color: Colors.white),
|
style: const TextStyle(color: Colors.white),
|
||||||
),
|
),
|
||||||
const Text(
|
const Text(
|
||||||
"Made with heart in this chaotic world!",
|
'Made with heart in this chaotic world!',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w800,
|
fontWeight: FontWeight.w800,
|
||||||
color: Colors.white),
|
color: Colors.white),
|
||||||
@ -1472,10 +1473,10 @@ class _ComboBox extends StatelessWidget {
|
|||||||
void changeSocks5Proxy() async {
|
void changeSocks5Proxy() async {
|
||||||
var socks = await bind.mainGetSocks();
|
var socks = await bind.mainGetSocks();
|
||||||
|
|
||||||
String proxy = "";
|
String proxy = '';
|
||||||
String proxyMsg = "";
|
String proxyMsg = '';
|
||||||
String username = "";
|
String username = '';
|
||||||
String password = "";
|
String password = '';
|
||||||
if (socks.length == 3) {
|
if (socks.length == 3) {
|
||||||
proxy = socks[0];
|
proxy = socks[0];
|
||||||
username = socks[1];
|
username = socks[1];
|
||||||
@ -1489,7 +1490,7 @@ void changeSocks5Proxy() async {
|
|||||||
gFFI.dialogManager.show((setState, close) {
|
gFFI.dialogManager.show((setState, close) {
|
||||||
submit() async {
|
submit() async {
|
||||||
setState(() {
|
setState(() {
|
||||||
proxyMsg = "";
|
proxyMsg = '';
|
||||||
isInProgress = true;
|
isInProgress = true;
|
||||||
});
|
});
|
||||||
cancel() {
|
cancel() {
|
||||||
@ -1517,7 +1518,7 @@ void changeSocks5Proxy() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return CustomAlertDialog(
|
return CustomAlertDialog(
|
||||||
title: Text(translate("Socks5 Proxy")),
|
title: Text(translate('Socks5 Proxy')),
|
||||||
content: ConstrainedBox(
|
content: ConstrainedBox(
|
||||||
constraints: const BoxConstraints(minWidth: 500),
|
constraints: const BoxConstraints(minWidth: 500),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -1530,7 +1531,7 @@ void changeSocks5Proxy() async {
|
|||||||
children: [
|
children: [
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: const BoxConstraints(minWidth: 100),
|
constraints: const BoxConstraints(minWidth: 100),
|
||||||
child: Text("${translate('Hostname')}:")
|
child: Text('${translate("Hostname")}:')
|
||||||
.marginOnly(bottom: 16.0)),
|
.marginOnly(bottom: 16.0)),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 24.0,
|
width: 24.0,
|
||||||
@ -1553,7 +1554,7 @@ void changeSocks5Proxy() async {
|
|||||||
children: [
|
children: [
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: const BoxConstraints(minWidth: 100),
|
constraints: const BoxConstraints(minWidth: 100),
|
||||||
child: Text("${translate('Username')}:")
|
child: Text('${translate("Username")}:')
|
||||||
.marginOnly(bottom: 16.0)),
|
.marginOnly(bottom: 16.0)),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 24.0,
|
width: 24.0,
|
||||||
@ -1575,7 +1576,7 @@ void changeSocks5Proxy() async {
|
|||||||
children: [
|
children: [
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: const BoxConstraints(minWidth: 100),
|
constraints: const BoxConstraints(minWidth: 100),
|
||||||
child: Text("${translate('Password')}:")
|
child: Text('${translate("Password")}:')
|
||||||
.marginOnly(bottom: 16.0)),
|
.marginOnly(bottom: 16.0)),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 24.0,
|
width: 24.0,
|
||||||
@ -1599,8 +1600,8 @@ void changeSocks5Proxy() async {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: close, child: Text(translate("Cancel"))),
|
TextButton(onPressed: close, child: Text(translate('Cancel'))),
|
||||||
TextButton(onPressed: submit, child: Text(translate("OK"))),
|
TextButton(onPressed: submit, child: Text(translate('OK'))),
|
||||||
],
|
],
|
||||||
onSubmit: submit,
|
onSubmit: submit,
|
||||||
onCancel: close,
|
onCancel: close,
|
||||||
|
@ -28,14 +28,14 @@ class RemotePage extends StatefulWidget {
|
|||||||
RemotePage({
|
RemotePage({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.id,
|
required this.id,
|
||||||
|
required this.menubarState,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
|
final MenubarState menubarState;
|
||||||
final SimpleWrapper<State<RemotePage>?> _lastState = SimpleWrapper(null);
|
final SimpleWrapper<State<RemotePage>?> _lastState = SimpleWrapper(null);
|
||||||
|
|
||||||
FFI get ffi => (_lastState.value! as _RemotePageState)._ffi;
|
FFI get ffi => (_lastState.value! as _RemotePageState)._ffi;
|
||||||
RxBool get showMenubar =>
|
|
||||||
(_lastState.value! as _RemotePageState)._showMenubar;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RemotePage> createState() {
|
State<RemotePage> createState() {
|
||||||
@ -50,7 +50,6 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
String keyboardMode = "legacy";
|
String keyboardMode = "legacy";
|
||||||
final _cursorOverImage = false.obs;
|
final _cursorOverImage = false.obs;
|
||||||
final _showMenubar = false.obs;
|
|
||||||
late RxBool _showRemoteCursor;
|
late RxBool _showRemoteCursor;
|
||||||
late RxBool _remoteCursorMoved;
|
late RxBool _remoteCursorMoved;
|
||||||
late RxBool _keyboardEnabled;
|
late RxBool _keyboardEnabled;
|
||||||
@ -239,7 +238,7 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
paints.add(RemoteMenubar(
|
paints.add(RemoteMenubar(
|
||||||
id: widget.id,
|
id: widget.id,
|
||||||
ffi: _ffi,
|
ffi: _ffi,
|
||||||
show: _showMenubar,
|
state: widget.menubarState,
|
||||||
onEnterOrLeaveImageSetter: (func) => _onEnterOrLeaveImage4Menubar = func,
|
onEnterOrLeaveImageSetter: (func) => _onEnterOrLeaveImage4Menubar = func,
|
||||||
onEnterOrLeaveImageCleaner: () => _onEnterOrLeaveImage4Menubar = null,
|
onEnterOrLeaveImageCleaner: () => _onEnterOrLeaveImage4Menubar = null,
|
||||||
));
|
));
|
||||||
|
@ -9,6 +9,7 @@ import 'package:flutter_hbb/common/shared_state.dart';
|
|||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/models/state_model.dart';
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/remote_page.dart';
|
import 'package:flutter_hbb/desktop/pages/remote_page.dart';
|
||||||
|
import 'package:flutter_hbb/desktop/widgets/remote_menubar.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/material_mod_popup_menu.dart'
|
import 'package:flutter_hbb/desktop/widgets/material_mod_popup_menu.dart'
|
||||||
as mod_menu;
|
as mod_menu;
|
||||||
@ -43,9 +44,12 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
static const IconData selectedIcon = Icons.desktop_windows_sharp;
|
static const IconData selectedIcon = Icons.desktop_windows_sharp;
|
||||||
static const IconData unselectedIcon = Icons.desktop_windows_outlined;
|
static const IconData unselectedIcon = Icons.desktop_windows_outlined;
|
||||||
|
|
||||||
|
late MenubarState _menubarState;
|
||||||
|
|
||||||
var connectionMap = RxList<Widget>.empty(growable: true);
|
var connectionMap = RxList<Widget>.empty(growable: true);
|
||||||
|
|
||||||
_ConnectionTabPageState(Map<String, dynamic> params) {
|
_ConnectionTabPageState(Map<String, dynamic> params) {
|
||||||
|
_menubarState = MenubarState();
|
||||||
RemoteCountState.init();
|
RemoteCountState.init();
|
||||||
final peerId = params['id'];
|
final peerId = params['id'];
|
||||||
if (peerId != null) {
|
if (peerId != null) {
|
||||||
@ -59,6 +63,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
page: RemotePage(
|
page: RemotePage(
|
||||||
key: ValueKey(peerId),
|
key: ValueKey(peerId),
|
||||||
id: peerId,
|
id: peerId,
|
||||||
|
menubarState: _menubarState,
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
_update_remote_count();
|
_update_remote_count();
|
||||||
@ -88,7 +93,11 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
selectedIcon: selectedIcon,
|
selectedIcon: selectedIcon,
|
||||||
unselectedIcon: unselectedIcon,
|
unselectedIcon: unselectedIcon,
|
||||||
onTabCloseButton: () => tabController.closeBy(id),
|
onTabCloseButton: () => tabController.closeBy(id),
|
||||||
page: RemotePage(key: ValueKey(id), id: id),
|
page: RemotePage(
|
||||||
|
key: ValueKey(id),
|
||||||
|
id: id,
|
||||||
|
menubarState: _menubarState,
|
||||||
|
),
|
||||||
));
|
));
|
||||||
} else if (call.method == "onDestroy") {
|
} else if (call.method == "onDestroy") {
|
||||||
tabController.clear();
|
tabController.clear();
|
||||||
@ -99,6 +108,12 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_menubarState.save();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final tabWidget = Container(
|
final tabWidget = Container(
|
||||||
@ -177,7 +192,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// to-do: some dup code to ../widgets/remote_menubar
|
// Note: Some dup code to ../widgets/remote_menubar
|
||||||
Widget _tabMenuBuilder(String key, CancelFunc cancelFunc) {
|
Widget _tabMenuBuilder(String key, CancelFunc cancelFunc) {
|
||||||
final List<MenuEntryBase<String>> menu = [];
|
final List<MenuEntryBase<String>> menu = [];
|
||||||
const EdgeInsets padding = EdgeInsets.only(left: 8.0, right: 5.0);
|
const EdgeInsets padding = EdgeInsets.only(left: 8.0, right: 5.0);
|
||||||
@ -187,7 +202,6 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
final ffi = remotePage.ffi;
|
final ffi = remotePage.ffi;
|
||||||
final pi = ffi.ffiModel.pi;
|
final pi = ffi.ffiModel.pi;
|
||||||
final perms = ffi.ffiModel.permissions;
|
final perms = ffi.ffiModel.permissions;
|
||||||
final showMenuBar = remotePage.showMenubar;
|
|
||||||
menu.addAll([
|
menu.addAll([
|
||||||
MenuEntryButton<String>(
|
MenuEntryButton<String>(
|
||||||
childBuilder: (TextStyle? style) => Text(
|
childBuilder: (TextStyle? style) => Text(
|
||||||
@ -202,11 +216,12 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
),
|
),
|
||||||
MenuEntryButton<String>(
|
MenuEntryButton<String>(
|
||||||
childBuilder: (TextStyle? style) => Obx(() => Text(
|
childBuilder: (TextStyle? style) => Obx(() => Text(
|
||||||
translate(showMenuBar.isTrue ? 'Hide Menubar' : 'Show Menubar'),
|
translate(
|
||||||
|
_menubarState.show.isTrue ? 'Hide Menubar' : 'Show Menubar'),
|
||||||
style: style,
|
style: style,
|
||||||
)),
|
)),
|
||||||
proc: () {
|
proc: () {
|
||||||
showMenuBar.value = !showMenuBar.value;
|
_menubarState.switchShow();
|
||||||
cancelFunc();
|
cancelFunc();
|
||||||
},
|
},
|
||||||
padding: padding,
|
padding: padding,
|
||||||
|
@ -21,6 +21,68 @@ import '../../common/shared_state.dart';
|
|||||||
import './popup_menu.dart';
|
import './popup_menu.dart';
|
||||||
import './material_mod_popup_menu.dart' as mod_menu;
|
import './material_mod_popup_menu.dart' as mod_menu;
|
||||||
|
|
||||||
|
class MenubarState {
|
||||||
|
final kStoreKey = "remoteMenubarState";
|
||||||
|
late RxBool show;
|
||||||
|
late RxBool _pin;
|
||||||
|
|
||||||
|
MenubarState() {
|
||||||
|
final s = bind.getLocalFlutterConfig(k: kStoreKey);
|
||||||
|
if (s.isEmpty) {
|
||||||
|
_initSet(false, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final m = jsonDecode(s);
|
||||||
|
if (m == null) {
|
||||||
|
_initSet(false, false);
|
||||||
|
} else {
|
||||||
|
_initSet(m['pin'] ?? false, m['pin'] ?? false);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Failed to decode menubar state ${e.toString()}');
|
||||||
|
_initSet(false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_initSet(bool s, bool p) {
|
||||||
|
show = RxBool(s);
|
||||||
|
_pin = RxBool(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get pin => _pin.value;
|
||||||
|
|
||||||
|
switchShow() async {
|
||||||
|
show.value = !show.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setShow(bool v) async {
|
||||||
|
if (show.value != v) {
|
||||||
|
show.value = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switchPin() async {
|
||||||
|
_pin.value = !_pin.value;
|
||||||
|
// Save everytime changed, as this func will not be called frequently
|
||||||
|
await save();
|
||||||
|
}
|
||||||
|
|
||||||
|
setPin(bool v) async {
|
||||||
|
if (_pin.value != v) {
|
||||||
|
_pin.value = v;
|
||||||
|
// Save everytime changed, as this func will not be called frequently
|
||||||
|
await save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save() async {
|
||||||
|
bind.setLocalFlutterConfig(
|
||||||
|
k: kStoreKey, v: jsonEncode({'pin': _pin.value}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _MenubarTheme {
|
class _MenubarTheme {
|
||||||
static const Color commonColor = MyTheme.accent;
|
static const Color commonColor = MyTheme.accent;
|
||||||
// kMinInteractiveDimension
|
// kMinInteractiveDimension
|
||||||
@ -31,7 +93,7 @@ class _MenubarTheme {
|
|||||||
class RemoteMenubar extends StatefulWidget {
|
class RemoteMenubar extends StatefulWidget {
|
||||||
final String id;
|
final String id;
|
||||||
final FFI ffi;
|
final FFI ffi;
|
||||||
final RxBool show;
|
final MenubarState state;
|
||||||
final Function(Function(bool)) onEnterOrLeaveImageSetter;
|
final Function(Function(bool)) onEnterOrLeaveImageSetter;
|
||||||
final Function() onEnterOrLeaveImageCleaner;
|
final Function() onEnterOrLeaveImageCleaner;
|
||||||
|
|
||||||
@ -39,7 +101,7 @@ class RemoteMenubar extends StatefulWidget {
|
|||||||
Key? key,
|
Key? key,
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.ffi,
|
required this.ffi,
|
||||||
required this.show,
|
required this.state,
|
||||||
required this.onEnterOrLeaveImageSetter,
|
required this.onEnterOrLeaveImageSetter,
|
||||||
required this.onEnterOrLeaveImageCleaner,
|
required this.onEnterOrLeaveImageCleaner,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@ -51,7 +113,6 @@ class RemoteMenubar extends StatefulWidget {
|
|||||||
class _RemoteMenubarState extends State<RemoteMenubar> {
|
class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||||
final Rx<Color> _hideColor = Colors.white12.obs;
|
final Rx<Color> _hideColor = Colors.white12.obs;
|
||||||
final _rxHideReplay = rxdart.ReplaySubject<int>();
|
final _rxHideReplay = rxdart.ReplaySubject<int>();
|
||||||
final _pinMenubar = false.obs;
|
|
||||||
bool _isCursorOverImage = false;
|
bool _isCursorOverImage = false;
|
||||||
window_size.Screen? _screen;
|
window_size.Screen? _screen;
|
||||||
|
|
||||||
@ -63,7 +124,8 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
RxBool get show => widget.show;
|
RxBool get show => widget.state.show;
|
||||||
|
bool get pin => widget.state.pin;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
initState() {
|
initState() {
|
||||||
@ -82,7 +144,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
.throttleTime(const Duration(milliseconds: 5000),
|
.throttleTime(const Duration(milliseconds: 5000),
|
||||||
trailing: true, leading: false)
|
trailing: true, leading: false)
|
||||||
.listen((int v) {
|
.listen((int v) {
|
||||||
if (_pinMenubar.isFalse && show.isTrue && _isCursorOverImage) {
|
if (!pin && show.isTrue && _isCursorOverImage) {
|
||||||
show.value = false;
|
show.value = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -196,18 +258,15 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
|
|
||||||
Widget _buildPinMenubar(BuildContext context) {
|
Widget _buildPinMenubar(BuildContext context) {
|
||||||
return Obx(() => IconButton(
|
return Obx(() => IconButton(
|
||||||
tooltip:
|
tooltip: translate(pin ? 'Unpin menubar' : 'Pin menubar'),
|
||||||
translate(_pinMenubar.isTrue ? 'Unpin menubar' : 'Pin menubar'),
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_pinMenubar.value = !_pinMenubar.value;
|
widget.state.switchPin();
|
||||||
},
|
},
|
||||||
icon: Obx(() => Transform.rotate(
|
icon: Obx(() => Transform.rotate(
|
||||||
angle: _pinMenubar.isTrue ? math.pi / 4 : 0,
|
angle: pin ? math.pi / 4 : 0,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.push_pin,
|
Icons.push_pin,
|
||||||
color: _pinMenubar.isTrue
|
color: pin ? _MenubarTheme.commonColor : Colors.grey,
|
||||||
? _MenubarTheme.commonColor
|
|
||||||
: Colors.grey,
|
|
||||||
))),
|
))),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
|||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
import 'package:bot_toast/bot_toast.dart';
|
import 'package:bot_toast/bot_toast.dart';
|
||||||
|
|
||||||
@ -97,7 +96,6 @@ Future<void> main(List<String> args) async {
|
|||||||
|
|
||||||
Future<void> initEnv(String appType) async {
|
Future<void> initEnv(String appType) async {
|
||||||
// global shared preference
|
// global shared preference
|
||||||
await Get.putAsync(() => SharedPreferences.getInstance());
|
|
||||||
await platformFFI.init(appType);
|
await platformFFI.init(appType);
|
||||||
// global FFI, use this **ONLY** for global configuration
|
// global FFI, use this **ONLY** for global configuration
|
||||||
// for convenience, use global FFI on mobile platform
|
// for convenience, use global FFI on mobile platform
|
||||||
|
@ -16,7 +16,6 @@ import 'package:flutter_hbb/models/server_model.dart';
|
|||||||
import 'package:flutter_hbb/models/user_model.dart';
|
import 'package:flutter_hbb/models/user_model.dart';
|
||||||
import 'package:flutter_hbb/models/state_model.dart';
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
import 'package:image/image.dart' as img2;
|
import 'package:image/image.dart' as img2;
|
||||||
import 'package:flutter_custom_cursor/flutter_custom_cursor.dart';
|
import 'package:flutter_custom_cursor/flutter_custom_cursor.dart';
|
||||||
@ -1219,7 +1218,7 @@ class FFI {
|
|||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
chatModel.close();
|
chatModel.close();
|
||||||
if (imageModel.image != null && !isWebDesktop) {
|
if (imageModel.image != null && !isWebDesktop) {
|
||||||
await savePreference(id, cursorModel.x, cursorModel.y, canvasModel.x,
|
await setCanvasConfig(id, cursorModel.x, cursorModel.y, canvasModel.x,
|
||||||
canvasModel.y, canvasModel.scale, ffiModel.pi.currentDisplay);
|
canvasModel.y, canvasModel.scale, ffiModel.pi.currentDisplay);
|
||||||
}
|
}
|
||||||
bind.sessionClose(id: id);
|
bind.sessionClose(id: id);
|
||||||
@ -1267,9 +1266,10 @@ class PeerInfo {
|
|||||||
List<Display> displays = [];
|
List<Display> displays = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> savePreference(String id, double xCursor, double yCursor,
|
const canvasKey = 'canvas';
|
||||||
|
|
||||||
|
Future<void> setCanvasConfig(String id, double xCursor, double yCursor,
|
||||||
double xCanvas, double yCanvas, double scale, int currentDisplay) async {
|
double xCanvas, double yCanvas, double scale, int currentDisplay) async {
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
final p = <String, dynamic>{};
|
final p = <String, dynamic>{};
|
||||||
p['xCursor'] = xCursor;
|
p['xCursor'] = xCursor;
|
||||||
p['yCursor'] = yCursor;
|
p['yCursor'] = yCursor;
|
||||||
@ -1277,25 +1277,27 @@ Future<void> savePreference(String id, double xCursor, double yCursor,
|
|||||||
p['yCanvas'] = yCanvas;
|
p['yCanvas'] = yCanvas;
|
||||||
p['scale'] = scale;
|
p['scale'] = scale;
|
||||||
p['currentDisplay'] = currentDisplay;
|
p['currentDisplay'] = currentDisplay;
|
||||||
prefs.setString('peer$id', json.encode(p));
|
await bind.sessionSetFlutterConfig(id: id, k: canvasKey, v: jsonEncode(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>?> getPreference(String id) async {
|
Future<Map<String, dynamic>?> getCanvasConfig(String id) async {
|
||||||
if (!isWebDesktop) return null;
|
if (!isWebDesktop) return null;
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
var p = await bind.sessionGetFlutterConfig(id: id, k: canvasKey);
|
||||||
var p = prefs.getString('peer$id');
|
if (p == null || p.isEmpty) return null;
|
||||||
if (p == null) return null;
|
try {
|
||||||
Map<String, dynamic> m = json.decode(p);
|
Map<String, dynamic> m = json.decode(p);
|
||||||
return m;
|
return m;
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void removePreference(String id) async {
|
void removePreference(String id) async {
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
await bind.sessionSetFlutterConfig(id: id, k: canvasKey, v: '');
|
||||||
prefs.remove('peer$id');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initializeCursorAndCanvas(FFI ffi) async {
|
Future<void> initializeCursorAndCanvas(FFI ffi) async {
|
||||||
var p = await getPreference(ffi.id);
|
var p = await getCanvasConfig(ffi.id);
|
||||||
int currentDisplay = 0;
|
int currentDisplay = 0;
|
||||||
if (p != null) {
|
if (p != null) {
|
||||||
currentDisplay = p['currentDisplay'];
|
currentDisplay = p['currentDisplay'];
|
||||||
|
@ -8,6 +8,7 @@ class StateGlobal {
|
|||||||
bool _fullscreen = false;
|
bool _fullscreen = false;
|
||||||
final RxBool _showTabBar = true.obs;
|
final RxBool _showTabBar = true.obs;
|
||||||
final RxDouble _resizeEdgeSize = 8.0.obs;
|
final RxDouble _resizeEdgeSize = 8.0.obs;
|
||||||
|
final RxBool showRemoteMenuBar = false.obs;
|
||||||
|
|
||||||
int get windowId => _windowId;
|
int get windowId => _windowId;
|
||||||
bool get fullscreen => _fullscreen;
|
bool get fullscreen => _fullscreen;
|
||||||
|
@ -19,7 +19,7 @@ class UserModel {
|
|||||||
|
|
||||||
void refreshCurrentUser() async {
|
void refreshCurrentUser() async {
|
||||||
await getUserName();
|
await getUserName();
|
||||||
final token = await bind.mainGetLocalOption(key: 'access_token');
|
final token = bind.mainGetLocalOption(key: 'access_token');
|
||||||
if (token == '') return;
|
if (token == '') return;
|
||||||
final url = await bind.mainGetApiServer();
|
final url = await bind.mainGetApiServer();
|
||||||
final body = {
|
final body = {
|
||||||
@ -73,7 +73,7 @@ class UserModel {
|
|||||||
if (userName.isNotEmpty) {
|
if (userName.isNotEmpty) {
|
||||||
return userName.value;
|
return userName.value;
|
||||||
}
|
}
|
||||||
final userInfo = await bind.mainGetLocalOption(key: 'user_info');
|
final userInfo = bind.mainGetLocalOption(key: 'user_info');
|
||||||
if (userInfo.trim().isEmpty) {
|
if (userInfo.trim().isEmpty) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,6 @@ dependencies:
|
|||||||
#firebase_analytics: ^9.1.5
|
#firebase_analytics: ^9.1.5
|
||||||
package_info_plus: ^1.4.2
|
package_info_plus: ^1.4.2
|
||||||
url_launcher: ^6.0.9
|
url_launcher: ^6.0.9
|
||||||
shared_preferences: ^2.0.6
|
|
||||||
toggle_switch: ^1.4.0
|
toggle_switch: ^1.4.0
|
||||||
dash_chat_2: ^0.0.14
|
dash_chat_2: ^0.0.14
|
||||||
draggable_float_widget: ^0.0.2
|
draggable_float_widget: ^0.0.2
|
||||||
|
@ -167,9 +167,12 @@ pub struct PeerConfig {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub show_quality_monitor: bool,
|
pub show_quality_monitor: bool,
|
||||||
|
|
||||||
// the other scalar value must before this
|
// The other scalar value must before this
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub options: HashMap<String, String>,
|
pub options: HashMap<String, String>,
|
||||||
|
// Various data for flutter ui
|
||||||
|
#[serde(default)]
|
||||||
|
pub ui_flutter: HashMap<String, String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub info: PeerInfoSerde,
|
pub info: PeerInfoSerde,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -897,6 +900,9 @@ pub struct LocalConfig {
|
|||||||
pub fav: Vec<String>,
|
pub fav: Vec<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
options: HashMap<String, String>,
|
options: HashMap<String, String>,
|
||||||
|
// Various data for flutter ui
|
||||||
|
#[serde(default)]
|
||||||
|
ui_flutter: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocalConfig {
|
impl LocalConfig {
|
||||||
@ -968,6 +974,27 @@ impl LocalConfig {
|
|||||||
config.store();
|
config.store();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_flutter_config(k: &str) -> String {
|
||||||
|
if let Some(v) = LOCAL_CONFIG.read().unwrap().ui_flutter.get(k) {
|
||||||
|
v.clone()
|
||||||
|
} else {
|
||||||
|
"".to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_flutter_config(k: String, v: String) {
|
||||||
|
let mut config = LOCAL_CONFIG.write().unwrap();
|
||||||
|
let v2 = if v.is_empty() { None } else { Some(&v) };
|
||||||
|
if v2 != config.ui_flutter.get(&k) {
|
||||||
|
if v2.is_none() {
|
||||||
|
config.ui_flutter.remove(&k);
|
||||||
|
} else {
|
||||||
|
config.ui_flutter.insert(k, v);
|
||||||
|
}
|
||||||
|
config.store();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
@ -6,6 +6,7 @@ use cpal::{
|
|||||||
};
|
};
|
||||||
use magnum_opus::{Channels::*, Decoder as AudioDecoder};
|
use magnum_opus::{Channels::*, Decoder as AudioDecoder};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
@ -984,6 +985,32 @@ impl LoginConfigHandler {
|
|||||||
self.save_config(config);
|
self.save_config(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set a ui config of flutter for handler's [`PeerConfig`].
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `k` - key of option
|
||||||
|
/// * `v` - value of option
|
||||||
|
pub fn set_ui_flutter(&mut self, k: String, v: String) {
|
||||||
|
let mut config = self.load_config();
|
||||||
|
config.ui_flutter.insert(k, v);
|
||||||
|
self.save_config(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a ui config of flutter for handler's [`PeerConfig`].
|
||||||
|
/// Return String if the option is found, otherwise return "".
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `k` - key of option
|
||||||
|
pub fn get_ui_flutter(&self, k: &str) -> String {
|
||||||
|
if let Some(v) = self.config.ui_flutter.get(k) {
|
||||||
|
v.clone()
|
||||||
|
} else {
|
||||||
|
"".to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Toggle an option in the handler.
|
/// Toggle an option in the handler.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@ -1947,6 +1974,7 @@ fn decode_id_pk(signed: &[u8], key: &sign::PublicKey) -> ResultType<(String, [u8
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
pub fn disable_keyboard_listening() {
|
pub fn disable_keyboard_listening() {
|
||||||
crate::ui_session_interface::KEYBOARD_HOOKED.store(true, Ordering::SeqCst);
|
crate::ui_session_interface::KEYBOARD_HOOKED.store(true, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,10 @@ use hbb_common::rendezvous_proto::ConnType;
|
|||||||
use hbb_common::tokio::{
|
use hbb_common::tokio::{
|
||||||
self,
|
self,
|
||||||
sync::mpsc,
|
sync::mpsc,
|
||||||
sync::Mutex as TokioMutex,
|
|
||||||
time::{self, Duration, Instant, Interval},
|
time::{self, Duration, Instant, Interval},
|
||||||
};
|
};
|
||||||
|
#[cfg(windows)]
|
||||||
|
use hbb_common::tokio::sync::Mutex as TokioMutex;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
message_proto::*,
|
message_proto::*,
|
||||||
|
@ -122,7 +122,7 @@ pub fn core_main() -> Option<Vec<String>> {
|
|||||||
hbb_common::allow_err!(crate::rc::extract_resources(&args[1]));
|
hbb_common::allow_err!(crate::rc::extract_resources(&args[1]));
|
||||||
return None;
|
return None;
|
||||||
} else if args[0] == "--tray" {
|
} else if args[0] == "--tray" {
|
||||||
crate::tray::start_tray(crate::ui_interface::OPTIONS.clone());
|
crate::tray::start_tray();
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,12 +149,12 @@ pub fn core_main() -> Option<Vec<String>> {
|
|||||||
std::thread::spawn(move || crate::start_server(true));
|
std::thread::spawn(move || crate::start_server(true));
|
||||||
// to-do: for flutter, starting tray not ready yet, or we can reuse sciter's tray implementation.
|
// to-do: for flutter, starting tray not ready yet, or we can reuse sciter's tray implementation.
|
||||||
}
|
}
|
||||||
#[cfg(all(target_os = "linux"))]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
let handler = std::thread::spawn(move || crate::start_server(true));
|
let handler = std::thread::spawn(move || crate::start_server(true));
|
||||||
crate::tray::start_tray(crate::ui_interface::OPTIONS.clone());
|
crate::tray::start_tray();
|
||||||
// revent server exit when encountering errors from tray
|
// revent server exit when encountering errors from tray
|
||||||
handler.join();
|
hbb_common::allow_err!(handler.join());
|
||||||
}
|
}
|
||||||
} else if args[0] == "--import-config" {
|
} else if args[0] == "--import-config" {
|
||||||
if args.len() == 2 {
|
if args.len() == 2 {
|
||||||
@ -249,5 +249,6 @@ fn core_main_invoke_new_connection(mut args: std::env::Args) -> Option<Vec<Strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ pub extern "C" fn rustdesk_core_main(args_len: *mut c_int) -> *mut *mut c_char {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// https://gist.github.com/iskakaushik/1c5b8aa75c77479c33c4320913eebef6
|
// https://gist.github.com/iskakaushik/1c5b8aa75c77479c33c4320913eebef6
|
||||||
|
#[cfg(windows)]
|
||||||
fn rust_args_to_c_args(args: Vec<String>, outlen: *mut c_int) -> *mut *mut c_char {
|
fn rust_args_to_c_args(args: Vec<String>, outlen: *mut c_int) -> *mut *mut c_char {
|
||||||
let mut v = vec![];
|
let mut v = vec![];
|
||||||
|
|
||||||
@ -227,6 +228,7 @@ impl InvokeUiSession for FlutterHandler {
|
|||||||
id: i32,
|
id: i32,
|
||||||
entries: &Vec<FileEntry>,
|
entries: &Vec<FileEntry>,
|
||||||
path: String,
|
path: String,
|
||||||
|
#[allow(unused_variables)]
|
||||||
is_local: bool,
|
is_local: bool,
|
||||||
only_count: bool,
|
only_count: bool,
|
||||||
) {
|
) {
|
||||||
|
@ -19,12 +19,12 @@ use crate::flutter::{self, SESSIONS};
|
|||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
use crate::start_server;
|
use crate::start_server;
|
||||||
use crate::ui_interface::{self, *};
|
use crate::ui_interface::{self, *};
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
use crate::ui_session_interface::CUR_SESSION;
|
||||||
use crate::{
|
use crate::{
|
||||||
client::file_trait::FileManager,
|
client::file_trait::FileManager,
|
||||||
flutter::{make_fd_to_json, session_add, session_start_},
|
flutter::{make_fd_to_json, session_add, session_start_},
|
||||||
};
|
};
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
||||||
use crate::ui_session_interface::CUR_SESSION;
|
|
||||||
fn initialize(app_dir: &str) {
|
fn initialize(app_dir: &str) {
|
||||||
*config::APP_DIR.write().unwrap() = app_dir.to_owned();
|
*config::APP_DIR.write().unwrap() = app_dir.to_owned();
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
@ -162,6 +162,28 @@ pub fn session_toggle_option(id: String, value: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn session_get_flutter_config(id: String, k: String) -> Option<String> {
|
||||||
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
|
Some(session.get_flutter_config(k))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn session_set_flutter_config(id: String, k: String, v: String) {
|
||||||
|
if let Some(session) = SESSIONS.write().unwrap().get_mut(&id) {
|
||||||
|
session.set_flutter_config(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_local_flutter_config(k: String) -> SyncReturn<String> {
|
||||||
|
SyncReturn(ui_interface::get_local_flutter_config(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_local_flutter_config(k: String, v: String) {
|
||||||
|
ui_interface::set_local_flutter_config(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn session_get_image_quality(id: String) -> Option<String> {
|
pub fn session_get_image_quality(id: String) -> Option<String> {
|
||||||
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
Some(session.get_image_quality())
|
Some(session.get_image_quality())
|
||||||
@ -418,7 +440,7 @@ pub fn session_resume_job(id: String, act_id: i32, is_remote: bool) {
|
|||||||
pub fn main_get_sound_inputs() -> Vec<String> {
|
pub fn main_get_sound_inputs() -> Vec<String> {
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
return get_sound_inputs();
|
return get_sound_inputs();
|
||||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
vec![String::from("")]
|
vec![String::from("")]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,8 +559,8 @@ pub fn main_post_request(url: String, body: String, header: String) {
|
|||||||
post_request(url, body, header)
|
post_request(url, body, header)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_get_local_option(key: String) -> String {
|
pub fn main_get_local_option(key: String) -> SyncReturn<String> {
|
||||||
get_local_option(key)
|
SyncReturn(get_local_option(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_set_local_option(key: String, value: String) {
|
pub fn main_set_local_option(key: String, value: String) {
|
||||||
@ -1021,7 +1043,7 @@ pub fn main_is_installed() -> SyncReturn<bool> {
|
|||||||
SyncReturn(is_installed())
|
SyncReturn(is_installed())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_start_grab_keyboard(){
|
pub fn main_start_grab_keyboard() {
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
crate::ui_session_interface::global_grab_keyboard();
|
crate::ui_session_interface::global_grab_keyboard();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use super::HbbHttpResponse;
|
use super::HbbHttpResponse;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
config::{Config, LocalConfig},
|
config::{Config, LocalConfig},
|
||||||
log, sleep, tokio, ResultType,
|
log, ResultType,
|
||||||
};
|
};
|
||||||
use reqwest::blocking::Client;
|
use reqwest::blocking::Client;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
@ -6,7 +6,9 @@
|
|||||||
use dbus::blocking::Connection;
|
use dbus::blocking::Connection;
|
||||||
use dbus_crossroads::{Crossroads, IfaceBuilder};
|
use dbus_crossroads::{Crossroads, IfaceBuilder};
|
||||||
use hbb_common::{log};
|
use hbb_common::{log};
|
||||||
use std::{error::Error, fmt, time::Duration, collections::HashMap};
|
use std::{error::Error, fmt, time::Duration};
|
||||||
|
#[cfg(feature = "flutter")]
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
const DBUS_NAME: &str = "org.rustdesk.rustdesk";
|
const DBUS_NAME: &str = "org.rustdesk.rustdesk";
|
||||||
const DBUS_PREFIX: &str = "/dbus";
|
const DBUS_PREFIX: &str = "/dbus";
|
||||||
@ -65,7 +67,7 @@ fn handle_client_message(builder: &mut IfaceBuilder<()>) {
|
|||||||
DBUS_METHOD_NEW_CONNECTION,
|
DBUS_METHOD_NEW_CONNECTION,
|
||||||
(DBUS_METHOD_NEW_CONNECTION_ID,),
|
(DBUS_METHOD_NEW_CONNECTION_ID,),
|
||||||
(DBUS_METHOD_RETURN,),
|
(DBUS_METHOD_RETURN,),
|
||||||
move |_, _, (peer_id,): (String,)| {
|
move |_, _, (_peer_id,): (String,)| {
|
||||||
#[cfg(feature = "flutter")]
|
#[cfg(feature = "flutter")]
|
||||||
{
|
{
|
||||||
use crate::flutter::{self, APP_TYPE_MAIN};
|
use crate::flutter::{self, APP_TYPE_MAIN};
|
||||||
@ -77,7 +79,7 @@ fn handle_client_message(builder: &mut IfaceBuilder<()>) {
|
|||||||
{
|
{
|
||||||
let data = HashMap::from([
|
let data = HashMap::from([
|
||||||
("name", "new_connection"),
|
("name", "new_connection"),
|
||||||
("peer_id", peer_id.as_str())
|
("peer_id", _peer_id.as_str())
|
||||||
]);
|
]);
|
||||||
if !stream.add(serde_json::ser::to_string(&data).unwrap_or("".to_string())) {
|
if !stream.add(serde_json::ser::to_string(&data).unwrap_or("".to_string())) {
|
||||||
log::error!("failed to add dbus message to flutter global dbus stream.");
|
log::error!("failed to add dbus message to flutter global dbus stream.");
|
||||||
|
@ -373,7 +373,7 @@ fn fix_modifiers(modifiers: &[EnumOrUnknown<ControlKey>], en: &mut Enigo, ck: i3
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_mouse_active_by_conn(conn: i32) -> bool {
|
fn active_mouse_(conn: i32) -> bool {
|
||||||
// out of time protection
|
// out of time protection
|
||||||
if LATEST_CURSOR_POS.lock().unwrap().0.elapsed() > MOUSE_MOVE_PROTECTION_TIMEOUT {
|
if LATEST_CURSOR_POS.lock().unwrap().0.elapsed() > MOUSE_MOVE_PROTECTION_TIMEOUT {
|
||||||
return true;
|
return true;
|
||||||
@ -388,13 +388,13 @@ fn is_mouse_active_by_conn(conn: i32) -> bool {
|
|||||||
// check if input is in valid range
|
// check if input is in valid range
|
||||||
match crate::get_cursor_pos() {
|
match crate::get_cursor_pos() {
|
||||||
Some((x, y)) => {
|
Some((x, y)) => {
|
||||||
let is_same_input = (last_input.x - x).abs() < MOUSE_ACTIVE_DISTANCE
|
let can_active = (last_input.x - x).abs() < MOUSE_ACTIVE_DISTANCE
|
||||||
&& (last_input.y - y).abs() < MOUSE_ACTIVE_DISTANCE;
|
&& (last_input.y - y).abs() < MOUSE_ACTIVE_DISTANCE;
|
||||||
if !is_same_input {
|
if !can_active {
|
||||||
last_input.x = -MOUSE_ACTIVE_DISTANCE * 2;
|
last_input.x = -MOUSE_ACTIVE_DISTANCE * 2;
|
||||||
last_input.y = -MOUSE_ACTIVE_DISTANCE * 2;
|
last_input.y = -MOUSE_ACTIVE_DISTANCE * 2;
|
||||||
}
|
}
|
||||||
is_same_input
|
can_active
|
||||||
}
|
}
|
||||||
None => true,
|
None => true,
|
||||||
}
|
}
|
||||||
@ -405,7 +405,7 @@ fn handle_mouse_(evt: &MouseEvent, conn: i32) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_mouse_active_by_conn(conn) {
|
if !active_mouse_(conn) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,11 +225,7 @@ impl VideoQoS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_abr_config(&mut self) -> bool {
|
pub fn check_abr_config(&mut self) -> bool {
|
||||||
self.enable_abr = if let Some(v) = Config2::get().options.get("enable-abr") {
|
self.enable_abr = "N" != Config::get_option("enable-abr");
|
||||||
v != "N"
|
|
||||||
} else {
|
|
||||||
true // default is true
|
|
||||||
};
|
|
||||||
self.enable_abr
|
self.enable_abr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,10 +33,11 @@ use std::{
|
|||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
io::ErrorKind::WouldBlock,
|
io::ErrorKind::WouldBlock,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
sync::Once,
|
|
||||||
time::{self, Duration, Instant},
|
time::{self, Duration, Instant},
|
||||||
};
|
};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
use std::sync::Once;
|
||||||
|
#[cfg(windows)]
|
||||||
use virtual_display;
|
use virtual_display;
|
||||||
|
|
||||||
pub const SCRAP_UBUNTU_HIGHER_REQUIRED: &str = "Wayland requires Ubuntu 21.04 or higher version.";
|
pub const SCRAP_UBUNTU_HIGHER_REQUIRED: &str = "Wayland requires Ubuntu 21.04 or higher version.";
|
||||||
|
73
src/tray.rs
73
src/tray.rs
@ -1,14 +1,12 @@
|
|||||||
use hbb_common::log::debug;
|
use super::ui_interface::get_option_opt;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use hbb_common::log::{error, info};
|
use hbb_common::log::{debug, error, info};
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use libappindicator::AppIndicator;
|
use libappindicator::AppIndicator;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use std::env::temp_dir;
|
use std::env::temp_dir;
|
||||||
use std::{
|
#[cfg(target_os = "windows")]
|
||||||
collections::HashMap,
|
use std::sync::{Arc, Mutex};
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
use trayicon::{MenuBuilder, TrayIconBuilder};
|
use trayicon::{MenuBuilder, TrayIconBuilder};
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@ -17,6 +15,7 @@ use winit::{
|
|||||||
event_loop::{ControlFlow, EventLoop},
|
event_loop::{ControlFlow, EventLoop},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
enum Events {
|
enum Events {
|
||||||
DoubleClickTrayIcon,
|
DoubleClickTrayIcon,
|
||||||
@ -25,7 +24,7 @@ enum Events {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub fn start_tray(options: Arc<Mutex<HashMap<String, String>>>) {
|
pub fn start_tray() {
|
||||||
let event_loop = EventLoop::<Events>::with_user_event();
|
let event_loop = EventLoop::<Events>::with_user_event();
|
||||||
let proxy = event_loop.create_proxy();
|
let proxy = event_loop.create_proxy();
|
||||||
let icon = include_bytes!("../res/tray-icon.ico");
|
let icon = include_bytes!("../res/tray-icon.ico");
|
||||||
@ -39,23 +38,19 @@ pub fn start_tray(options: Arc<Mutex<HashMap<String, String>>>) {
|
|||||||
let old_state = Arc::new(Mutex::new(0));
|
let old_state = Arc::new(Mutex::new(0));
|
||||||
let _sender = crate::ui_interface::SENDER.lock().unwrap();
|
let _sender = crate::ui_interface::SENDER.lock().unwrap();
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
if options.lock().unwrap().get("ipc-closed").is_some() {
|
if get_option_opt("ipc-closed").is_some() {
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
*control_flow = ControlFlow::Wait;
|
*control_flow = ControlFlow::Wait;
|
||||||
}
|
}
|
||||||
let stopped = if let Some(v) = options.lock().unwrap().get("stop-service") {
|
let stopped = is_service_stoped();
|
||||||
!v.is_empty()
|
let state = if stopped { 2 } else { 1 };
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
let stopped = if stopped { 2 } else { 1 };
|
|
||||||
let old = *old_state.lock().unwrap();
|
let old = *old_state.lock().unwrap();
|
||||||
if stopped != old {
|
if state != old {
|
||||||
hbb_common::log::info!("State changed");
|
hbb_common::log::info!("State changed");
|
||||||
let mut m = MenuBuilder::new();
|
let mut m = MenuBuilder::new();
|
||||||
if stopped == 2 {
|
if state == 2 {
|
||||||
m = m.item(
|
m = m.item(
|
||||||
&crate::client::translate("Start Service".to_owned()),
|
&crate::client::translate("Start Service".to_owned()),
|
||||||
Events::StartService,
|
Events::StartService,
|
||||||
@ -67,7 +62,7 @@ pub fn start_tray(options: Arc<Mutex<HashMap<String, String>>>) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
tray_icon.set_menu(&m).ok();
|
tray_icon.set_menu(&m).ok();
|
||||||
*old_state.lock().unwrap() = stopped;
|
*old_state.lock().unwrap() = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
@ -92,10 +87,9 @@ pub fn start_tray(options: Arc<Mutex<HashMap<String, String>>>) {
|
|||||||
/// [Block]
|
/// [Block]
|
||||||
/// This function will block current execution, show the tray icon and handle events.
|
/// This function will block current execution, show the tray icon and handle events.
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn start_tray(options: Arc<Mutex<HashMap<String, String>>>) {
|
pub fn start_tray() {
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use gtk::traits::{GtkMenuItemExt, MenuShellExt, WidgetExt};
|
use gtk::traits::{GtkMenuItemExt, MenuShellExt, WidgetExt};
|
||||||
|
|
||||||
info!("configuring tray");
|
info!("configuring tray");
|
||||||
// init gtk context
|
// init gtk context
|
||||||
if let Err(err) = gtk::init() {
|
if let Err(err) = gtk::init() {
|
||||||
@ -104,17 +98,17 @@ pub fn start_tray(options: Arc<Mutex<HashMap<String, String>>>) {
|
|||||||
}
|
}
|
||||||
if let Some(mut appindicator) = get_default_app_indicator() {
|
if let Some(mut appindicator) = get_default_app_indicator() {
|
||||||
let mut menu = gtk::Menu::new();
|
let mut menu = gtk::Menu::new();
|
||||||
let running = get_service_status(options.clone());
|
let stoped = is_service_stoped();
|
||||||
// start/stop service
|
// start/stop service
|
||||||
let label = if !running {
|
let label = if stoped {
|
||||||
crate::client::translate("Start Service".to_owned())
|
crate::client::translate("Start Service".to_owned())
|
||||||
} else {
|
} else {
|
||||||
crate::client::translate("Stop service".to_owned())
|
crate::client::translate("Stop service".to_owned())
|
||||||
};
|
};
|
||||||
let menu_item_service = gtk::MenuItem::with_label(label.as_str());
|
let menu_item_service = gtk::MenuItem::with_label(label.as_str());
|
||||||
menu_item_service.connect_activate(move |item| {
|
menu_item_service.connect_activate(move |item| {
|
||||||
let lock = crate::ui_interface::SENDER.lock().unwrap();
|
let _lock = crate::ui_interface::SENDER.lock().unwrap();
|
||||||
update_tray_service_item(options.clone(), item);
|
update_tray_service_item(item);
|
||||||
});
|
});
|
||||||
menu.append(&menu_item_service);
|
menu.append(&menu_item_service);
|
||||||
// show tray item
|
// show tray item
|
||||||
@ -129,19 +123,17 @@ pub fn start_tray(options: Arc<Mutex<HashMap<String, String>>>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn update_tray_service_item(options: Arc<Mutex<HashMap<String, String>>>, item: >k::MenuItem) {
|
fn update_tray_service_item(item: >k::MenuItem) {
|
||||||
use gtk::{
|
use gtk::traits::GtkMenuItemExt;
|
||||||
traits::{GtkMenuItemExt, ListBoxRowExt},
|
|
||||||
MenuItem,
|
if is_service_stoped() {
|
||||||
};
|
|
||||||
if get_service_status(options.clone()) {
|
|
||||||
debug!("Now try to stop service");
|
|
||||||
item.set_label(&crate::client::translate("Start Service".to_owned()));
|
|
||||||
crate::ipc::set_option("stop-service", "Y");
|
|
||||||
} else {
|
|
||||||
debug!("Now try to start service");
|
debug!("Now try to start service");
|
||||||
item.set_label(&crate::client::translate("Stop service".to_owned()));
|
item.set_label(&crate::client::translate("Stop service".to_owned()));
|
||||||
crate::ipc::set_option("stop-service", "");
|
crate::ipc::set_option("stop-service", "");
|
||||||
|
} else {
|
||||||
|
debug!("Now try to stop service");
|
||||||
|
item.set_label(&crate::client::translate("Start Service".to_owned()));
|
||||||
|
crate::ipc::set_option("stop-service", "Y");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,14 +163,13 @@ fn get_default_app_indicator() -> Option<AppIndicator> {
|
|||||||
Some(appindicator)
|
Some(appindicator)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get service status
|
/// Check if service is stoped.
|
||||||
/// Return [`true`] if service is running, [`false`] otherwise.
|
/// Return [`true`] if service is stoped, [`false`] otherwise.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_service_status(options: Arc<Mutex<HashMap<String, String>>>) -> bool {
|
fn is_service_stoped() -> bool {
|
||||||
if let Some(v) = options.lock().unwrap().get("stop-service") {
|
if let Some(v) = get_option_opt("stop-service") {
|
||||||
debug!("service stopped: {}", v);
|
v == "Y"
|
||||||
v.is_empty()
|
|
||||||
} else {
|
} else {
|
||||||
true
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
src/ui.rs
12
src/ui.rs
@ -33,7 +33,8 @@ pub mod win_privacy;
|
|||||||
|
|
||||||
type Message = RendezvousMessage;
|
type Message = RendezvousMessage;
|
||||||
|
|
||||||
pub type Childs = Arc<Mutex<(bool, HashMap<(String, String), Child>)>>;
|
pub type Children = Arc<Mutex<(bool, HashMap<(String, String), Child>)>>;
|
||||||
|
#[allow(dead_code)]
|
||||||
type Status = (i32, bool, i64, String);
|
type Status = (i32, bool, i64, String);
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
@ -43,6 +44,7 @@ lazy_static::lazy_static! {
|
|||||||
|
|
||||||
struct UIHostHandler;
|
struct UIHostHandler;
|
||||||
|
|
||||||
|
// to-do: dead code?
|
||||||
fn check_connect_status(
|
fn check_connect_status(
|
||||||
reconnect: bool,
|
reconnect: bool,
|
||||||
) -> (
|
) -> (
|
||||||
@ -111,8 +113,8 @@ pub fn start(args: &mut [String]) {
|
|||||||
args[1] = id;
|
args[1] = id;
|
||||||
}
|
}
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
let child: Childs = Default::default();
|
let children: Children = Default::default();
|
||||||
std::thread::spawn(move || check_zombie(child));
|
std::thread::spawn(move || check_zombie(children));
|
||||||
crate::common::check_software_update();
|
crate::common::check_software_update();
|
||||||
frame.event_handler(UI {});
|
frame.event_handler(UI {});
|
||||||
frame.sciter_handler(UIHostHandler {});
|
frame.sciter_handler(UIHostHandler {});
|
||||||
@ -662,10 +664,10 @@ impl sciter::host::HostHandler for UIHostHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_zombie(childs: Childs) {
|
pub fn check_zombie(children: Children) {
|
||||||
let mut deads = Vec::new();
|
let mut deads = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
let mut lock = childs.lock().unwrap();
|
let mut lock = children.lock().unwrap();
|
||||||
let mut n = 0;
|
let mut n = 0;
|
||||||
for (id, c) in lock.1.iter_mut() {
|
for (id, c) in lock.1.iter_mut() {
|
||||||
if let Ok(Some(_)) = c.try_wait() {
|
if let Ok(Some(_)) = c.try_wait() {
|
||||||
|
@ -2,13 +2,14 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
iter::FromIterator,
|
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicI64, Ordering},
|
atomic::{AtomicI64, Ordering},
|
||||||
RwLock,
|
RwLock,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use clipboard::{cliprdr::CliprdrClientContext, empty_clipboard, set_conn_enabled, ContextSend};
|
use clipboard::{cliprdr::CliprdrClientContext, empty_clipboard, set_conn_enabled, ContextSend};
|
||||||
|
@ -9,32 +9,37 @@ use std::{
|
|||||||
use hbb_common::password_security;
|
use hbb_common::password_security;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
config::{self, Config, LocalConfig, PeerConfig, RENDEZVOUS_PORT, RENDEZVOUS_TIMEOUT},
|
config::{self, Config, LocalConfig, PeerConfig},
|
||||||
directories_next,
|
directories_next, log,
|
||||||
futures::future::join_all,
|
|
||||||
log,
|
|
||||||
protobuf::Message as _,
|
|
||||||
rendezvous_proto::*,
|
|
||||||
sleep,
|
sleep,
|
||||||
tcp::FramedStream,
|
|
||||||
tokio::{self, sync::mpsc, time},
|
tokio::{self, sync::mpsc, time},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{common::SOFTWARE_UPDATE_URL, ipc, platform};
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
|
use hbb_common::{
|
||||||
|
config::{RENDEZVOUS_PORT, RENDEZVOUS_TIMEOUT},
|
||||||
|
futures::future::join_all,
|
||||||
|
protobuf::Message as _,
|
||||||
|
rendezvous_proto::*,
|
||||||
|
tcp::FramedStream,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "flutter")]
|
#[cfg(feature = "flutter")]
|
||||||
use crate::hbbs_http::account;
|
use crate::hbbs_http::account;
|
||||||
|
use crate::{common::SOFTWARE_UPDATE_URL, ipc, platform};
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
type Message = RendezvousMessage;
|
type Message = RendezvousMessage;
|
||||||
|
|
||||||
pub type Childs = Arc<Mutex<(bool, HashMap<(String, String), Child>)>>;
|
pub type Children = Arc<Mutex<(bool, HashMap<(String, String), Child>)>>;
|
||||||
type Status = (i32, bool, i64, String); // (status_num, key_confirmed, mouse_time, id)
|
type Status = (i32, bool, i64, String); // (status_num, key_confirmed, mouse_time, id)
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref CHILDS : Childs = Default::default();
|
static ref CHILDREN : Children = Default::default();
|
||||||
pub static ref UI_STATUS : Arc<Mutex<Status>> = Arc::new(Mutex::new((0, false, 0, "".to_owned())));
|
static ref UI_STATUS : Arc<Mutex<Status>> = Arc::new(Mutex::new((0, false, 0, "".to_owned())));
|
||||||
pub static ref OPTIONS : Arc<Mutex<HashMap<String, String>>> = Arc::new(Mutex::new(Config::get_options()));
|
static ref OPTIONS : Arc<Mutex<HashMap<String, String>>> = Arc::new(Mutex::new(Config::get_options()));
|
||||||
pub static ref ASYNC_JOB_STATUS : Arc<Mutex<String>> = Default::default();
|
static ref ASYNC_JOB_STATUS : Arc<Mutex<String>> = Default::default();
|
||||||
pub static ref TEMPORARY_PASSWD : Arc<Mutex<String>> = Arc::new(Mutex::new("".to_owned()));
|
static ref TEMPORARY_PASSWD : Arc<Mutex<String>> = Arc::new(Mutex::new("".to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
@ -44,15 +49,16 @@ lazy_static::lazy_static! {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn recent_sessions_updated() -> bool {
|
pub fn recent_sessions_updated() -> bool {
|
||||||
let mut childs = CHILDS.lock().unwrap();
|
let mut children = CHILDREN.lock().unwrap();
|
||||||
if childs.0 {
|
if children.0 {
|
||||||
childs.0 = false;
|
children.0 = false;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_id() -> String {
|
pub fn get_id() -> String {
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
@ -143,10 +149,9 @@ pub fn get_license() -> String {
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
if let Some(lic) = crate::platform::windows::get_license() {
|
if let Some(lic) = crate::platform::windows::get_license() {
|
||||||
#[cfg(feature = "flutter")]
|
#[cfg(feature = "flutter")]
|
||||||
{
|
return format!("Key: {}\nHost: {}\nApi: {}", lic.key, lic.host, lic.api);
|
||||||
return format!("Key: {}\nHost: {}\nApi: {}", lic.key, lic.host, lic.api);
|
|
||||||
}
|
|
||||||
// default license format is html formed (sciter)
|
// default license format is html formed (sciter)
|
||||||
|
#[cfg(not(feature = "flutter"))]
|
||||||
return format!(
|
return format!(
|
||||||
"<br /> Key: {} <br /> Host: {} Api: {}",
|
"<br /> Key: {} <br /> Host: {} Api: {}",
|
||||||
lic.key, lic.host, lic.api
|
lic.key, lic.host, lic.api
|
||||||
@ -155,6 +160,11 @@ pub fn get_license() -> String {
|
|||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_option_opt(key: &str) -> Option<String> {
|
||||||
|
OPTIONS.lock().unwrap().get(key).map(|x| x.clone())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_option(key: String) -> String {
|
pub fn get_option(key: String) -> String {
|
||||||
get_option_(&key)
|
get_option_(&key)
|
||||||
@ -180,6 +190,18 @@ pub fn set_local_option(key: String, value: String) {
|
|||||||
LocalConfig::set_option(key, value);
|
LocalConfig::set_option(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
|
#[inline]
|
||||||
|
pub fn get_local_flutter_config(key: String) -> String {
|
||||||
|
LocalConfig::get_flutter_config(&key)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
|
#[inline]
|
||||||
|
pub fn set_local_flutter_config(key: String, value: String) {
|
||||||
|
LocalConfig::set_flutter_config(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn peer_has_password(id: String) -> bool {
|
pub fn peer_has_password(id: String) -> bool {
|
||||||
!PeerConfig::load(&id).password.is_empty()
|
!PeerConfig::load(&id).password.is_empty()
|
||||||
@ -230,7 +252,7 @@ pub fn test_if_valid_server(host: String) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(feature = "flutter")]
|
||||||
pub fn get_sound_inputs() -> Vec<String> {
|
pub fn get_sound_inputs() -> Vec<String> {
|
||||||
let mut a = Vec::new();
|
let mut a = Vec::new();
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
@ -345,12 +367,15 @@ pub fn set_socks(proxy: String, username: String, password: String) {
|
|||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_installed() -> bool {
|
pub fn is_installed() -> bool {
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
crate::platform::is_installed()
|
||||||
{
|
}
|
||||||
return crate::platform::is_installed();
|
|
||||||
}
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
|
#[inline]
|
||||||
|
pub fn is_installed() -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,7 +520,7 @@ pub fn remove_peer(id: String) {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new_remote(id: String, remote_type: String) {
|
pub fn new_remote(id: String, remote_type: String) {
|
||||||
let mut lock = CHILDS.lock().unwrap();
|
let mut lock = CHILDREN.lock().unwrap();
|
||||||
let args = vec![format!("--{}", remote_type), id.clone()];
|
let args = vec![format!("--{}", remote_type), id.clone()];
|
||||||
let key = (id.clone(), remote_type.clone());
|
let key = (id.clone(), remote_type.clone());
|
||||||
if let Some(c) = lock.1.get_mut(&key) {
|
if let Some(c) = lock.1.get_mut(&key) {
|
||||||
@ -612,6 +637,7 @@ pub fn get_version() -> String {
|
|||||||
crate::VERSION.to_owned()
|
crate::VERSION.to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_app_name() -> String {
|
pub fn get_app_name() -> String {
|
||||||
crate::get_app_name()
|
crate::get_app_name()
|
||||||
@ -650,6 +676,7 @@ pub fn create_shortcut(_id: String) {
|
|||||||
crate::platform::windows::create_shortcut(&_id).ok();
|
crate::platform::windows::create_shortcut(&_id).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn discover() {
|
pub fn discover() {
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
@ -694,6 +721,7 @@ pub fn open_url(url: String) {
|
|||||||
allow_err!(std::process::Command::new(p).arg(url).spawn());
|
allow_err!(std::process::Command::new(p).arg(url).spawn());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn change_id(id: String) {
|
pub fn change_id(id: String) {
|
||||||
*ASYNC_JOB_STATUS.lock().unwrap() = " ".to_owned();
|
*ASYNC_JOB_STATUS.lock().unwrap() = " ".to_owned();
|
||||||
@ -800,13 +828,19 @@ pub fn is_release() -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
#[inline]
|
||||||
|
pub fn is_root() -> bool {
|
||||||
|
crate::platform::is_root()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_root() -> bool {
|
pub fn is_root() -> bool {
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
||||||
return crate::platform::is_root();
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn check_super_user_permission() -> bool {
|
pub fn check_super_user_permission() -> bool {
|
||||||
#[cfg(feature = "flatpak")]
|
#[cfg(feature = "flatpak")]
|
||||||
@ -818,10 +852,10 @@ pub fn check_super_user_permission() -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn check_zombie(childs: Childs) {
|
pub fn check_zombie(children: Children) {
|
||||||
let mut deads = Vec::new();
|
let mut deads = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
let mut lock = childs.lock().unwrap();
|
let mut lock = children.lock().unwrap();
|
||||||
let mut n = 0;
|
let mut n = 0;
|
||||||
for (id, c) in lock.1.iter_mut() {
|
for (id, c) in lock.1.iter_mut() {
|
||||||
if let Ok(Some(_)) = c.try_wait() {
|
if let Ok(Some(_)) = c.try_wait() {
|
||||||
@ -928,6 +962,7 @@ pub(crate) async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedRe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
pub(crate) async fn send_to_cm(data: &ipc::Data) {
|
pub(crate) async fn send_to_cm(data: &ipc::Data) {
|
||||||
if let Ok(mut c) = ipc::connect(1000, "_cm").await {
|
if let Ok(mut c) = ipc::connect(1000, "_cm").await {
|
||||||
@ -935,9 +970,12 @@ pub(crate) async fn send_to_cm(data: &ipc::Data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
const INVALID_FORMAT: &'static str = "Invalid format";
|
const INVALID_FORMAT: &'static str = "Invalid format";
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
const UNKNOWN_ERROR: &'static str = "Unknown error";
|
const UNKNOWN_ERROR: &'static str = "Unknown error";
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn change_id_(id: String, old_id: String) -> &'static str {
|
async fn change_id_(id: String, old_id: String) -> &'static str {
|
||||||
if !hbb_common::is_valid_custom_id(&id) {
|
if !hbb_common::is_valid_custom_id(&id) {
|
||||||
@ -987,6 +1025,7 @@ async fn change_id_(id: String, old_id: String) -> &'static str {
|
|||||||
err
|
err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
async fn check_id(
|
async fn check_id(
|
||||||
rendezvous_server: String,
|
rendezvous_server: String,
|
||||||
old_id: String,
|
old_id: String,
|
||||||
|
@ -98,6 +98,14 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
self.lc.write().unwrap().save_view_style(value);
|
self.lc.write().unwrap().save_view_style(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_flutter_config(&mut self, k: String, v: String) {
|
||||||
|
self.lc.write().unwrap().set_ui_flutter(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_flutter_config(&self, k: String) -> String {
|
||||||
|
self.lc.write().unwrap().get_ui_flutter(&k)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn toggle_option(&mut self, name: String) {
|
pub fn toggle_option(&mut self, name: String) {
|
||||||
let msg = self.lc.write().unwrap().toggle_option(name.clone());
|
let msg = self.lc.write().unwrap().toggle_option(name.clone());
|
||||||
if name == "enable-file-transfer" {
|
if name == "enable-file-transfer" {
|
||||||
@ -1590,7 +1598,7 @@ pub fn global_grab_keyboard() {
|
|||||||
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let func = move |event: Event| match event.event_type {
|
let func = move |event: Event| match event.event_type {
|
||||||
EventType::KeyPress(key) | EventType::KeyRelease(key) => {
|
EventType::KeyPress(..) | EventType::KeyRelease(..) => {
|
||||||
// grab all keys
|
// grab all keys
|
||||||
if !IS_IN.load(Ordering::SeqCst) {
|
if !IS_IN.load(Ordering::SeqCst) {
|
||||||
return Some(event);
|
return Some(event);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user