Feat: Follow remote cursor and window focus | Auto display switch (#7717)
* feat: auto switch display on follow remote cursor Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * feat: auto switch display on follow remote window focus Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build and remove unused imports Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix linux get_focused_window_id Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * lock show remote cursor when follow remote cursor is enabled Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix config Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * prevent auto display switch on show all display and displays as individual windows Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix options Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix options Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove unused function Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove unwraps and improve iterations Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * set updateCursorPos to false to avoid interrupting remote cursor Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * update lang Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix web build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * update checks for options and enable in view mode Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use focused display index for window focus service Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use window center for windows display focused Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove unused imports Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use libxdo instead of xdotool Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix multi monitor check Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * enable show cursor when follow cursor is default Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove show_all_displays,use runtime state instead Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix show cursor lock state on default Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove view mode with follow options Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use separate message for follow current display Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix options Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * sciter support for follow remote cursor and window Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * add check for ui session handlers count Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use cached displays and remove peer info write Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * No follow options when show all displays Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * No follow options when multi ui session Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * turn off follow options when not used|prevent msgs Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use window center for switch in linux Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use subbed display count to prevent switch msgs Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix web build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * move subbed displays count Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * add noperms for window focus Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * add subscribe for window focus Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove window_focus message and unsub on multi ui Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * add multi ui session field Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> --------- Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
This commit is contained in:
parent
43a0a4f8e0
commit
3811f41076
@ -168,6 +168,29 @@ class ShowRemoteCursorState {
|
|||||||
static RxBool find(String id) => Get.find<RxBool>(tag: tag(id));
|
static RxBool find(String id) => Get.find<RxBool>(tag: tag(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ShowRemoteCursorLockState {
|
||||||
|
static String tag(String id) => 'show_remote_cursor_lock_$id';
|
||||||
|
|
||||||
|
static void init(String id) {
|
||||||
|
final key = tag(id);
|
||||||
|
if (!Get.isRegistered(tag: key)) {
|
||||||
|
final RxBool state = false.obs;
|
||||||
|
Get.put(state, tag: key);
|
||||||
|
} else {
|
||||||
|
Get.find<RxBool>(tag: key).value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void delete(String id) {
|
||||||
|
final key = tag(id);
|
||||||
|
if (Get.isRegistered(tag: key)) {
|
||||||
|
Get.delete(tag: key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static RxBool find(String id) => Get.find<RxBool>(tag: tag(id));
|
||||||
|
}
|
||||||
|
|
||||||
class KeyboardEnabledState {
|
class KeyboardEnabledState {
|
||||||
static String tag(String id) => 'keyboard_enabled_$id';
|
static String tag(String id) => 'keyboard_enabled_$id';
|
||||||
|
|
||||||
@ -315,6 +338,7 @@ initSharedStates(String id) {
|
|||||||
CurrentDisplayState.init(id);
|
CurrentDisplayState.init(id);
|
||||||
KeyboardEnabledState.init(id);
|
KeyboardEnabledState.init(id);
|
||||||
ShowRemoteCursorState.init(id);
|
ShowRemoteCursorState.init(id);
|
||||||
|
ShowRemoteCursorLockState.init(id);
|
||||||
RemoteCursorMovedState.init(id);
|
RemoteCursorMovedState.init(id);
|
||||||
FingerprintState.init(id);
|
FingerprintState.init(id);
|
||||||
PeerBoolOption.init(id, 'zoom-cursor', () => false);
|
PeerBoolOption.init(id, 'zoom-cursor', () => false);
|
||||||
@ -327,6 +351,7 @@ removeSharedStates(String id) {
|
|||||||
BlockInputState.delete(id);
|
BlockInputState.delete(id);
|
||||||
CurrentDisplayState.delete(id);
|
CurrentDisplayState.delete(id);
|
||||||
ShowRemoteCursorState.delete(id);
|
ShowRemoteCursorState.delete(id);
|
||||||
|
ShowRemoteCursorLockState.delete(id);
|
||||||
KeyboardEnabledState.delete(id);
|
KeyboardEnabledState.delete(id);
|
||||||
RemoteCursorMovedState.delete(id);
|
RemoteCursorMovedState.delete(id);
|
||||||
FingerprintState.delete(id);
|
FingerprintState.delete(id);
|
||||||
|
@ -212,6 +212,8 @@ List<(String, String)> otherDefaultSettings() {
|
|||||||
if ((isDesktop || isWebDesktop)) ('show_monitors_tip', kKeyShowMonitorsToolbar),
|
if ((isDesktop || isWebDesktop)) ('show_monitors_tip', kKeyShowMonitorsToolbar),
|
||||||
if ((isDesktop || isWebDesktop)) ('Collapse toolbar', 'collapse_toolbar'),
|
if ((isDesktop || isWebDesktop)) ('Collapse toolbar', 'collapse_toolbar'),
|
||||||
('Show remote cursor', 'show_remote_cursor'),
|
('Show remote cursor', 'show_remote_cursor'),
|
||||||
|
('Follow remote cursor', 'follow_remote_cursor'),
|
||||||
|
('Follow remote window focus', 'follow_remote_window'),
|
||||||
if ((isDesktop || isWebDesktop)) ('Zoom cursor', 'zoom-cursor'),
|
if ((isDesktop || isWebDesktop)) ('Zoom cursor', 'zoom-cursor'),
|
||||||
('Show quality monitor', 'show_quality_monitor'),
|
('Show quality monitor', 'show_quality_monitor'),
|
||||||
('Mute', 'disable_audio'),
|
('Mute', 'disable_audio'),
|
||||||
|
@ -371,7 +371,7 @@ Future<List<TRadioMenu<String>>> toolbarCodec(
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<TToggleMenu>> toolbarDisplayToggle(
|
Future<List<TToggleMenu>> toolbarCursor(
|
||||||
BuildContext context, String id, FFI ffi) async {
|
BuildContext context, String id, FFI ffi) async {
|
||||||
List<TToggleMenu> v = [];
|
List<TToggleMenu> v = [];
|
||||||
final ffiModel = ffi.ffiModel;
|
final ffiModel = ffi.ffiModel;
|
||||||
@ -384,12 +384,17 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
|
|||||||
!ffi.canvasModel.cursorEmbedded &&
|
!ffi.canvasModel.cursorEmbedded &&
|
||||||
!pi.isWayland) {
|
!pi.isWayland) {
|
||||||
final state = ShowRemoteCursorState.find(id);
|
final state = ShowRemoteCursorState.find(id);
|
||||||
|
final lockState = ShowRemoteCursorLockState.find(id);
|
||||||
final enabled = !ffiModel.viewOnly;
|
final enabled = !ffiModel.viewOnly;
|
||||||
final option = 'show-remote-cursor';
|
final option = 'show-remote-cursor';
|
||||||
|
if (pi.currentDisplay == kAllDisplayValue ||
|
||||||
|
bind.sessionIsMultiUiSession(sessionId: sessionId)) {
|
||||||
|
lockState.value = false;
|
||||||
|
}
|
||||||
v.add(TToggleMenu(
|
v.add(TToggleMenu(
|
||||||
child: Text(translate('Show remote cursor')),
|
child: Text(translate('Show remote cursor')),
|
||||||
value: state.value,
|
value: state.value,
|
||||||
onChanged: enabled
|
onChanged: enabled && !lockState.value
|
||||||
? (value) async {
|
? (value) async {
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
await bind.sessionToggleOption(
|
await bind.sessionToggleOption(
|
||||||
@ -399,6 +404,67 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
|
|||||||
}
|
}
|
||||||
: null));
|
: null));
|
||||||
}
|
}
|
||||||
|
// follow remote cursor
|
||||||
|
if (pi.platform != kPeerPlatformAndroid &&
|
||||||
|
!ffi.canvasModel.cursorEmbedded &&
|
||||||
|
!pi.isWayland &&
|
||||||
|
versionCmp(pi.version, "1.2.4") >= 0 &&
|
||||||
|
pi.displays.length > 1 &&
|
||||||
|
pi.currentDisplay != kAllDisplayValue &&
|
||||||
|
!bind.sessionIsMultiUiSession(sessionId: sessionId)) {
|
||||||
|
final option = 'follow-remote-cursor';
|
||||||
|
final value =
|
||||||
|
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
|
||||||
|
final showCursorOption = 'show-remote-cursor';
|
||||||
|
final showCursorState = ShowRemoteCursorState.find(id);
|
||||||
|
final showCursorLockState = ShowRemoteCursorLockState.find(id);
|
||||||
|
final showCursorEnabled = bind.sessionGetToggleOptionSync(
|
||||||
|
sessionId: sessionId, arg: showCursorOption);
|
||||||
|
showCursorLockState.value = value;
|
||||||
|
if (value && !showCursorEnabled) {
|
||||||
|
await bind.sessionToggleOption(
|
||||||
|
sessionId: sessionId, value: showCursorOption);
|
||||||
|
showCursorState.value = bind.sessionGetToggleOptionSync(
|
||||||
|
sessionId: sessionId, arg: showCursorOption);
|
||||||
|
}
|
||||||
|
v.add(TToggleMenu(
|
||||||
|
child: Text(translate('Follow remote cursor')),
|
||||||
|
value: value,
|
||||||
|
onChanged: (value) async {
|
||||||
|
if (value == null) return;
|
||||||
|
await bind.sessionToggleOption(sessionId: sessionId, value: option);
|
||||||
|
value = bind.sessionGetToggleOptionSync(
|
||||||
|
sessionId: sessionId, arg: option);
|
||||||
|
showCursorLockState.value = value;
|
||||||
|
if (!showCursorEnabled) {
|
||||||
|
await bind.sessionToggleOption(
|
||||||
|
sessionId: sessionId, value: showCursorOption);
|
||||||
|
showCursorState.value = bind.sessionGetToggleOptionSync(
|
||||||
|
sessionId: sessionId, arg: showCursorOption);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// follow remote window focus
|
||||||
|
if (pi.platform != kPeerPlatformAndroid &&
|
||||||
|
!ffi.canvasModel.cursorEmbedded &&
|
||||||
|
!pi.isWayland &&
|
||||||
|
versionCmp(pi.version, "1.2.4") >= 0 &&
|
||||||
|
pi.displays.length > 1 &&
|
||||||
|
pi.currentDisplay != kAllDisplayValue &&
|
||||||
|
!bind.sessionIsMultiUiSession(sessionId: sessionId)) {
|
||||||
|
final option = 'follow-remote-window';
|
||||||
|
final value =
|
||||||
|
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
|
||||||
|
v.add(TToggleMenu(
|
||||||
|
child: Text(translate('Follow remote window focus')),
|
||||||
|
value: value,
|
||||||
|
onChanged: (value) async {
|
||||||
|
if (value == null) return;
|
||||||
|
await bind.sessionToggleOption(sessionId: sessionId, value: option);
|
||||||
|
value = bind.sessionGetToggleOptionSync(
|
||||||
|
sessionId: sessionId, arg: option);
|
||||||
|
}));
|
||||||
|
}
|
||||||
// zoom cursor
|
// zoom cursor
|
||||||
final viewStyle = await bind.sessionGetViewStyle(sessionId: sessionId) ?? '';
|
final viewStyle = await bind.sessionGetViewStyle(sessionId: sessionId) ?? '';
|
||||||
if (!isMobile &&
|
if (!isMobile &&
|
||||||
@ -417,6 +483,17 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<TToggleMenu>> toolbarDisplayToggle(
|
||||||
|
BuildContext context, String id, FFI ffi) async {
|
||||||
|
List<TToggleMenu> v = [];
|
||||||
|
final ffiModel = ffi.ffiModel;
|
||||||
|
final pi = ffiModel.pi;
|
||||||
|
final perms = ffiModel.permissions;
|
||||||
|
final sessionId = ffi.sessionId;
|
||||||
|
|
||||||
// show quality monitor
|
// show quality monitor
|
||||||
final option = 'show-quality-monitor';
|
final option = 'show-quality-monitor';
|
||||||
v.add(TToggleMenu(
|
v.add(TToggleMenu(
|
||||||
|
@ -1045,7 +1045,6 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
_screenAdjustor.updateScreen();
|
_screenAdjustor.updateScreen();
|
||||||
|
|
||||||
menuChildrenGetter() {
|
menuChildrenGetter() {
|
||||||
final menuChildren = <Widget>[
|
final menuChildren = <Widget>[
|
||||||
_screenAdjustor.adjustWindow(context),
|
_screenAdjustor.adjustWindow(context),
|
||||||
@ -1069,6 +1068,8 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
ffi: widget.ffi,
|
ffi: widget.ffi,
|
||||||
),
|
),
|
||||||
Divider(),
|
Divider(),
|
||||||
|
cursorToggles(),
|
||||||
|
Divider(),
|
||||||
toggles(),
|
toggles(),
|
||||||
];
|
];
|
||||||
// privacy mode
|
// privacy mode
|
||||||
@ -1212,6 +1213,23 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cursorToggles() {
|
||||||
|
return futureBuilder(
|
||||||
|
future: toolbarCursor(context, id, ffi),
|
||||||
|
hasData: (data) {
|
||||||
|
final v = data as List<TToggleMenu>;
|
||||||
|
if (v.isEmpty) return Offstage();
|
||||||
|
return Column(
|
||||||
|
children: v
|
||||||
|
.map((e) => CkbMenuButton(
|
||||||
|
value: e.value,
|
||||||
|
onChanged: e.onChanged,
|
||||||
|
child: e.child,
|
||||||
|
ffi: ffi))
|
||||||
|
.toList());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
toggles() {
|
toggles() {
|
||||||
return futureBuilder(
|
return futureBuilder(
|
||||||
future: toolbarDisplayToggle(context, id, ffi),
|
future: toolbarDisplayToggle(context, id, ffi),
|
||||||
|
@ -836,6 +836,7 @@ void showOptions(
|
|||||||
List<TRadioMenu<String>> imageQualityRadios =
|
List<TRadioMenu<String>> imageQualityRadios =
|
||||||
await toolbarImageQuality(context, id, gFFI);
|
await toolbarImageQuality(context, id, gFFI);
|
||||||
List<TRadioMenu<String>> codecRadios = await toolbarCodec(context, id, gFFI);
|
List<TRadioMenu<String>> codecRadios = await toolbarCodec(context, id, gFFI);
|
||||||
|
List<TToggleMenu> cursorToggles = await toolbarCursor(context, id, gFFI);
|
||||||
List<TToggleMenu> displayToggles =
|
List<TToggleMenu> displayToggles =
|
||||||
await toolbarDisplayToggle(context, id, gFFI);
|
await toolbarDisplayToggle(context, id, gFFI);
|
||||||
|
|
||||||
@ -876,8 +877,23 @@ void showOptions(
|
|||||||
})),
|
})),
|
||||||
if (codecRadios.isNotEmpty) const Divider(color: MyTheme.border),
|
if (codecRadios.isNotEmpty) const Divider(color: MyTheme.border),
|
||||||
];
|
];
|
||||||
|
final rxCursorToggleValues = cursorToggles.map((e) => e.value.obs).toList();
|
||||||
|
final cursorTogglesList = cursorToggles
|
||||||
|
.asMap()
|
||||||
|
.entries
|
||||||
|
.map((e) => Obx(() => CheckboxListTile(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
value: rxCursorToggleValues[e.key].value,
|
||||||
|
onChanged: (v) {
|
||||||
|
e.value.onChanged?.call(v);
|
||||||
|
if (v != null) rxCursorToggleValues[e.key].value = v;
|
||||||
|
},
|
||||||
|
title: e.value.child)))
|
||||||
|
.toList();
|
||||||
|
|
||||||
final rxToggleValues = displayToggles.map((e) => e.value.obs).toList();
|
final rxToggleValues = displayToggles.map((e) => e.value.obs).toList();
|
||||||
final toggles = displayToggles
|
final displayTogglesList = displayToggles
|
||||||
.asMap()
|
.asMap()
|
||||||
.entries
|
.entries
|
||||||
.map((e) => Obx(() => CheckboxListTile(
|
.map((e) => Obx(() => CheckboxListTile(
|
||||||
@ -890,6 +906,11 @@ void showOptions(
|
|||||||
},
|
},
|
||||||
title: e.value.child)))
|
title: e.value.child)))
|
||||||
.toList();
|
.toList();
|
||||||
|
final toggles = [
|
||||||
|
...cursorTogglesList,
|
||||||
|
if (cursorToggles.isNotEmpty) const Divider(color: MyTheme.border),
|
||||||
|
...displayTogglesList,
|
||||||
|
];
|
||||||
|
|
||||||
Widget privacyModeWidget = Offstage();
|
Widget privacyModeWidget = Offstage();
|
||||||
if (privacyModeList.length > 1) {
|
if (privacyModeList.length > 1) {
|
||||||
|
@ -367,6 +367,8 @@ class FfiModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
} else if (name == 'sync_peer_option') {
|
} else if (name == 'sync_peer_option') {
|
||||||
_handleSyncPeerOption(evt, peerId);
|
_handleSyncPeerOption(evt, peerId);
|
||||||
|
} else if (name == 'follow_current_display') {
|
||||||
|
handleFollowCurrentDisplay(evt, sessionId, peerId);
|
||||||
} else {
|
} else {
|
||||||
debugPrint('Unknown event name: $name');
|
debugPrint('Unknown event name: $name');
|
||||||
}
|
}
|
||||||
@ -440,7 +442,7 @@ class FfiModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCurDisplay(SessionID sessionId, {updateCursorPos = true}) {
|
updateCurDisplay(SessionID sessionId, {updateCursorPos = false}) {
|
||||||
final newRect = displaysRect();
|
final newRect = displaysRect();
|
||||||
if (newRect == null) {
|
if (newRect == null) {
|
||||||
return;
|
return;
|
||||||
@ -1040,9 +1042,30 @@ class FfiModel with ChangeNotifier {
|
|||||||
json.encode(_pi.platformAdditions);
|
json.encode(_pi.platformAdditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleFollowCurrentDisplay(
|
||||||
|
Map<String, dynamic> evt, SessionID sessionId, String peerId) async {
|
||||||
|
if (evt['display_idx'] != null) {
|
||||||
|
if (pi.currentDisplay == kAllDisplayValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_pi.currentDisplay = int.parse(evt['display_idx']);
|
||||||
|
try {
|
||||||
|
CurrentDisplayState.find(peerId).value = _pi.currentDisplay;
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
bind.sessionSwitchDisplay(
|
||||||
|
isDesktop: isDesktop,
|
||||||
|
sessionId: sessionId,
|
||||||
|
value: Int32List.fromList([_pi.currentDisplay]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
// Directly switch to the new display without waiting for the response.
|
// Directly switch to the new display without waiting for the response.
|
||||||
switchToNewDisplay(int display, SessionID sessionId, String peerId,
|
switchToNewDisplay(int display, SessionID sessionId, String peerId,
|
||||||
{bool updateCursorPos = true}) {
|
{bool updateCursorPos = false}) {
|
||||||
// VideoHandler creation is upon when video frames are received, so either caching commands(don't know next width/height) or stopping recording when switching displays.
|
// VideoHandler creation is upon when video frames are received, so either caching commands(don't know next width/height) or stopping recording when switching displays.
|
||||||
parent.target?.recordingModel.onClose();
|
parent.target?.recordingModel.onClose();
|
||||||
// no need to wait for the response
|
// no need to wait for the response
|
||||||
|
@ -346,6 +346,10 @@ class RustdeskImpl {
|
|||||||
return mode == kKeyLegacyMode;
|
return mode == kKeyLegacyMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool sessionIsMultiUiSession({required UuidValue sessionId, dynamic hint}) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> sessionSetCustomImageQuality(
|
Future<void> sessionSetCustomImageQuality(
|
||||||
{required UuidValue sessionId, required int value, dynamic hint}) {
|
{required UuidValue sessionId, required int value, dynamic hint}) {
|
||||||
return Future(() => js.context.callMethod('setByName', [
|
return Future(() => js.context.callMethod('setByName', [
|
||||||
|
@ -603,6 +603,8 @@ message OptionMessage {
|
|||||||
// Resolution custom_resolution = 13;
|
// Resolution custom_resolution = 13;
|
||||||
// BoolOption support_windows_specific_session = 14;
|
// BoolOption support_windows_specific_session = 14;
|
||||||
// starting from 15 please, do not use removed fields
|
// starting from 15 please, do not use removed fields
|
||||||
|
BoolOption follow_remote_cursor = 15;
|
||||||
|
BoolOption follow_remote_window = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
message TestDelay {
|
message TestDelay {
|
||||||
@ -765,6 +767,7 @@ message Misc {
|
|||||||
uint32 selected_sid = 35;
|
uint32 selected_sid = 35;
|
||||||
DisplayResolution change_display_resolution = 36;
|
DisplayResolution change_display_resolution = 36;
|
||||||
MessageQuery message_query = 37;
|
MessageQuery message_query = 37;
|
||||||
|
int32 follow_current_display = 38;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,6 +281,10 @@ pub struct PeerConfig {
|
|||||||
pub enable_file_transfer: EnableFileTransfer,
|
pub enable_file_transfer: EnableFileTransfer,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub show_quality_monitor: ShowQualityMonitor,
|
pub show_quality_monitor: ShowQualityMonitor,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub follow_remote_cursor: FollowRemoteCursor,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub follow_remote_window: FollowRemoteWindow,
|
||||||
#[serde(
|
#[serde(
|
||||||
default,
|
default,
|
||||||
deserialize_with = "deserialize_string",
|
deserialize_with = "deserialize_string",
|
||||||
@ -353,6 +357,8 @@ impl Default for PeerConfig {
|
|||||||
disable_clipboard: Default::default(),
|
disable_clipboard: Default::default(),
|
||||||
enable_file_transfer: Default::default(),
|
enable_file_transfer: Default::default(),
|
||||||
show_quality_monitor: Default::default(),
|
show_quality_monitor: Default::default(),
|
||||||
|
follow_remote_cursor: Default::default(),
|
||||||
|
follow_remote_window: Default::default(),
|
||||||
keyboard_mode: Default::default(),
|
keyboard_mode: Default::default(),
|
||||||
view_only: Default::default(),
|
view_only: Default::default(),
|
||||||
reverse_mouse_wheel: Self::default_reverse_mouse_wheel(),
|
reverse_mouse_wheel: Self::default_reverse_mouse_wheel(),
|
||||||
@ -1258,6 +1264,19 @@ serde_field_bool!(
|
|||||||
default_show_remote_cursor,
|
default_show_remote_cursor,
|
||||||
"ShowRemoteCursor::default_show_remote_cursor"
|
"ShowRemoteCursor::default_show_remote_cursor"
|
||||||
);
|
);
|
||||||
|
serde_field_bool!(
|
||||||
|
FollowRemoteCursor,
|
||||||
|
"follow_remote_cursor",
|
||||||
|
default_follow_remote_cursor,
|
||||||
|
"FollowRemoteCursor::default_follow_remote_cursor"
|
||||||
|
);
|
||||||
|
|
||||||
|
serde_field_bool!(
|
||||||
|
FollowRemoteWindow,
|
||||||
|
"follow_remote_window",
|
||||||
|
default_follow_remote_window,
|
||||||
|
"FollowRemoteWindow::default_follow_remote_window"
|
||||||
|
);
|
||||||
serde_field_bool!(
|
serde_field_bool!(
|
||||||
ShowQualityMonitor,
|
ShowQualityMonitor,
|
||||||
"show_quality_monitor",
|
"show_quality_monitor",
|
||||||
|
@ -1456,6 +1456,22 @@ impl LoginConfigHandler {
|
|||||||
BoolOption::No
|
BoolOption::No
|
||||||
})
|
})
|
||||||
.into();
|
.into();
|
||||||
|
} else if name == "follow-remote-cursor" {
|
||||||
|
config.follow_remote_cursor.v = !config.follow_remote_cursor.v;
|
||||||
|
option.follow_remote_cursor = (if config.follow_remote_cursor.v {
|
||||||
|
BoolOption::Yes
|
||||||
|
} else {
|
||||||
|
BoolOption::No
|
||||||
|
})
|
||||||
|
.into();
|
||||||
|
} else if name == "follow-remote-window" {
|
||||||
|
config.follow_remote_window.v = !config.follow_remote_window.v;
|
||||||
|
option.follow_remote_window = (if config.follow_remote_window.v {
|
||||||
|
BoolOption::Yes
|
||||||
|
} else {
|
||||||
|
BoolOption::No
|
||||||
|
})
|
||||||
|
.into();
|
||||||
} else if name == "disable-audio" {
|
} else if name == "disable-audio" {
|
||||||
config.disable_audio.v = !config.disable_audio.v;
|
config.disable_audio.v = !config.disable_audio.v;
|
||||||
option.disable_audio = (if config.disable_audio.v {
|
option.disable_audio = (if config.disable_audio.v {
|
||||||
@ -1601,6 +1617,12 @@ impl LoginConfigHandler {
|
|||||||
if view_only || self.get_toggle_option("show-remote-cursor") {
|
if view_only || self.get_toggle_option("show-remote-cursor") {
|
||||||
msg.show_remote_cursor = BoolOption::Yes.into();
|
msg.show_remote_cursor = BoolOption::Yes.into();
|
||||||
}
|
}
|
||||||
|
if self.get_toggle_option("follow-remote-cursor") {
|
||||||
|
msg.follow_remote_cursor = BoolOption::Yes.into();
|
||||||
|
}
|
||||||
|
if self.get_toggle_option("follow-remote-window") {
|
||||||
|
msg.follow_remote_window = BoolOption::Yes.into();
|
||||||
|
}
|
||||||
if !view_only && self.get_toggle_option("lock-after-session-end") {
|
if !view_only && self.get_toggle_option("lock-after-session-end") {
|
||||||
msg.lock_after_session_end = BoolOption::Yes.into();
|
msg.lock_after_session_end = BoolOption::Yes.into();
|
||||||
}
|
}
|
||||||
@ -1692,6 +1714,10 @@ impl LoginConfigHandler {
|
|||||||
self.config.allow_swap_key.v
|
self.config.allow_swap_key.v
|
||||||
} else if name == "view-only" {
|
} else if name == "view-only" {
|
||||||
self.config.view_only.v
|
self.config.view_only.v
|
||||||
|
} else if name == "follow-remote-cursor" {
|
||||||
|
self.config.follow_remote_cursor.v
|
||||||
|
} else if name == "follow-remote-window" {
|
||||||
|
self.config.follow_remote_window.v
|
||||||
} else {
|
} else {
|
||||||
!self.get_option(name).is_empty()
|
!self.get_option(name).is_empty()
|
||||||
}
|
}
|
||||||
|
@ -1504,7 +1504,9 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
log::info!("update supported encoding:{:?}", e);
|
log::info!("update supported encoding:{:?}", e);
|
||||||
self.handler.lc.write().unwrap().supported_encoding = e;
|
self.handler.lc.write().unwrap().supported_encoding = e;
|
||||||
}
|
}
|
||||||
|
Some(misc::Union::FollowCurrentDisplay(d_idx)) => {
|
||||||
|
self.handler.set_current_display(d_idx);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Some(message::Union::TestDelay(t)) => {
|
Some(message::Union::TestDelay(t)) => {
|
||||||
|
@ -882,6 +882,21 @@ impl InvokeUiSession for FlutterHandler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_multi_ui_session(&self) -> bool {
|
||||||
|
self.session_handlers.read().unwrap().len() > 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_current_display(&self, disp_idx: i32) {
|
||||||
|
if self.is_multi_ui_session() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.push_event(
|
||||||
|
"follow_current_display",
|
||||||
|
&[("display_idx", &disp_idx.to_string())],
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn on_connected(&self, _conn_type: ConnType) {}
|
fn on_connected(&self, _conn_type: ConnType) {}
|
||||||
|
|
||||||
fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str, retry: bool) {
|
fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str, retry: bool) {
|
||||||
|
@ -213,6 +213,14 @@ pub fn session_refresh(session_id: SessionID, display: usize) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn session_is_multi_ui_session(session_id: SessionID) -> SyncReturn<bool> {
|
||||||
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
|
SyncReturn(session.is_multi_ui_session())
|
||||||
|
} else {
|
||||||
|
SyncReturn(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn session_record_screen(
|
pub fn session_record_screen(
|
||||||
session_id: SessionID,
|
session_id: SessionID,
|
||||||
start: bool,
|
start: bool,
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -604,5 +604,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "打开 Web 控制台以执行更多操作"),
|
("ab_web_console_tip", "打开 Web 控制台以执行更多操作"),
|
||||||
("allow-only-conn-window-open-tip", "仅当 RustDesk 窗口打开时允许连接"),
|
("allow-only-conn-window-open-tip", "仅当 RustDesk 窗口打开时允许连接"),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", "没有物理显示器,没必要使用隐私模式。"),
|
("no_need_privacy_mode_no_physical_displays_tip", "没有物理显示器,没必要使用隐私模式。"),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "Více na webové konzoli"),
|
("ab_web_console_tip", "Více na webové konzoli"),
|
||||||
("allow-only-conn-window-open-tip", "Povolit připojení pouze v případě, že je otevřené okno RustDesk"),
|
("allow-only-conn-window-open-tip", "Povolit připojení pouze v případě, že je otevřené okno RustDesk"),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", "Žádné fyzické displeje, není třeba používat režim soukromí."),
|
("no_need_privacy_mode_no_physical_displays_tip", "Žádné fyzické displeje, není třeba používat režim soukromí."),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "Mehr über Webkonsole"),
|
("ab_web_console_tip", "Mehr über Webkonsole"),
|
||||||
("allow-only-conn-window-open-tip", "Verbindung nur zulassen, wenn das RustDesk-Fenster geöffnet ist"),
|
("allow-only-conn-window-open-tip", "Verbindung nur zulassen, wenn das RustDesk-Fenster geöffnet ist"),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", "Keine physischen Bildschirme; keine Notwendigkeit, den Datenschutzmodus zu verwenden."),
|
("no_need_privacy_mode_no_physical_displays_tip", "Keine physischen Bildschirme; keine Notwendigkeit, den Datenschutzmodus zu verwenden."),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -222,5 +222,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "More on web console"),
|
("ab_web_console_tip", "More on web console"),
|
||||||
("allow-only-conn-window-open-tip", "Only allow connection if RustDesk window is open"),
|
("allow-only-conn-window-open-tip", "Only allow connection if RustDesk window is open"),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", "No physical displays, no need to use the privacy mode."),
|
("no_need_privacy_mode_no_physical_displays_tip", "No physical displays, no need to use the privacy mode."),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "Más en consola web"),
|
("ab_web_console_tip", "Más en consola web"),
|
||||||
("allow-only-conn-window-open-tip", "Permitir la conexión solo si la ventana RusDesk está abierta"),
|
("allow-only-conn-window-open-tip", "Permitir la conexión solo si la ventana RusDesk está abierta"),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", "No hay pantallas físicas, no es necesario usar el modo privado."),
|
("no_need_privacy_mode_no_physical_displays_tip", "No hay pantallas físicas, no es necesario usar el modo privado."),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "اطلاعات بیشتر در کنسول وب"),
|
("ab_web_console_tip", "اطلاعات بیشتر در کنسول وب"),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -602,5 +602,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "Više na web konzoli"),
|
("ab_web_console_tip", "Više na web konzoli"),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "Altre info sulla console web"),
|
("ab_web_console_tip", "Altre info sulla console web"),
|
||||||
("allow-only-conn-window-open-tip", "Consenti la connessione solo se la finestra RustDesk è aperta"),
|
("allow-only-conn-window-open-tip", "Consenti la connessione solo se la finestra RustDesk è aperta"),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", "Nessun display fisico, nessuna necessità di usare la modalità privacy."),
|
("no_need_privacy_mode_no_physical_displays_tip", "Nessun display fisico, nessuna necessità di usare la modalità privacy."),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "Vairāk par tīmekļa konsoli"),
|
("ab_web_console_tip", "Vairāk par tīmekļa konsoli"),
|
||||||
("allow-only-conn-window-open-tip", "Atļaut savienojumu tikai tad, ja ir atvērts RustDesk logs"),
|
("allow-only-conn-window-open-tip", "Atļaut savienojumu tikai tad, ja ir atvērts RustDesk logs"),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", "Nav fizisku displeju, nav jāizmanto privātuma režīms."),
|
("no_need_privacy_mode_no_physical_displays_tip", "Nav fizisku displeju, nav jāizmanto privātuma režīms."),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "Meer over de webconsole"),
|
("ab_web_console_tip", "Meer over de webconsole"),
|
||||||
("allow-only-conn-window-open-tip", "Alleen verbindingen toestaan als het RustDesk-venster geopend is"),
|
("allow-only-conn-window-open-tip", "Alleen verbindingen toestaan als het RustDesk-venster geopend is"),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "Więcej w konsoli web"),
|
("ab_web_console_tip", "Więcej w konsoli web"),
|
||||||
("allow-only-conn-window-open-tip", "Zezwalaj na połączenie tylko wtedy, gdy okno RustDesk jest otwarte"),
|
("allow-only-conn-window-open-tip", "Zezwalaj na połączenie tylko wtedy, gdy okno RustDesk jest otwarte"),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "Больше в веб-консоли"),
|
("ab_web_console_tip", "Больше в веб-консоли"),
|
||||||
("allow-only-conn-window-open-tip", "Разрешать подключение только при открытом окне RustDesk"),
|
("allow-only-conn-window-open-tip", "Разрешать подключение только при открытом окне RustDesk"),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", "Физические дисплеи отсутствуют, нет необходимости использовать режим конфиденциальности."),
|
("no_need_privacy_mode_no_physical_displays_tip", "Физические дисплеи отсутствуют, нет необходимости использовать режим конфиденциальности."),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "Viac na webovej konzole"),
|
("ab_web_console_tip", "Viac na webovej konzole"),
|
||||||
("allow-only-conn-window-open-tip", "Povoliť pripojenie iba vtedy, ak je otvorené okno aplikácie RustDesk"),
|
("allow-only-conn-window-open-tip", "Povoliť pripojenie iba vtedy, ak je otvorené okno aplikácie RustDesk"),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", "Žiadne fyzické displeje, nie je potrebné používať režim ochrany osobných údajov."),
|
("no_need_privacy_mode_no_physical_displays_tip", "Žiadne fyzické displeje, nie je potrebné používať režim ochrany osobných údajov."),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "打開 Web 控制台以進行更多操作"),
|
("ab_web_console_tip", "打開 Web 控制台以進行更多操作"),
|
||||||
("allow-only-conn-window-open-tip", "只在 RustDesk 視窗開啟時允許連接"),
|
("allow-only-conn-window-open-tip", "只在 RustDesk 視窗開啟時允許連接"),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", "Детальніше про веб-консоль"),
|
("ab_web_console_tip", "Детальніше про веб-консоль"),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -603,5 +603,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
|
("Follow remote cursor", ""),
|
||||||
|
("Follow remote window focus", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,9 @@ mod keyboard;
|
|||||||
/// cbindgen:ignore
|
/// cbindgen:ignore
|
||||||
pub mod platform;
|
pub mod platform;
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
pub use platform::{get_cursor, get_cursor_data, get_cursor_pos, start_os_service};
|
pub use platform::{
|
||||||
|
get_cursor, get_cursor_data, get_cursor_pos, get_focused_display, start_os_service,
|
||||||
|
};
|
||||||
#[cfg(not(any(target_os = "ios")))]
|
#[cfg(not(any(target_os = "ios")))]
|
||||||
/// cbindgen:ignore
|
/// cbindgen:ignore
|
||||||
mod server;
|
mod server;
|
||||||
@ -36,15 +38,15 @@ pub mod flutter;
|
|||||||
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
pub mod flutter_ffi;
|
pub mod flutter_ffi;
|
||||||
use common::*;
|
use common::*;
|
||||||
|
mod auth_2fa;
|
||||||
#[cfg(feature = "cli")]
|
#[cfg(feature = "cli")]
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))]
|
||||||
pub mod core_main;
|
pub mod core_main;
|
||||||
mod lang;
|
|
||||||
mod custom_server;
|
mod custom_server;
|
||||||
|
mod lang;
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
mod port_forward;
|
mod port_forward;
|
||||||
mod auth_2fa;
|
|
||||||
|
|
||||||
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
|
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
@ -11,7 +11,7 @@ use hbb_common::{
|
|||||||
config::Config,
|
config::Config,
|
||||||
libc::{c_char, c_int, c_long, c_void},
|
libc::{c_char, c_int, c_long, c_void},
|
||||||
log,
|
log,
|
||||||
message_proto::Resolution,
|
message_proto::{DisplayInfo, Resolution},
|
||||||
regex::{Captures, Regex},
|
regex::{Captures, Regex},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
@ -53,6 +53,20 @@ extern "C" {
|
|||||||
screen_num: *mut c_int,
|
screen_num: *mut c_int,
|
||||||
) -> c_int;
|
) -> c_int;
|
||||||
fn xdo_new(display: *const c_char) -> Xdo;
|
fn xdo_new(display: *const c_char) -> Xdo;
|
||||||
|
fn xdo_get_active_window(xdo: Xdo, window: *mut *mut c_void) -> c_int;
|
||||||
|
fn xdo_get_window_location(
|
||||||
|
xdo: Xdo,
|
||||||
|
window: *mut c_void,
|
||||||
|
x: *mut c_int,
|
||||||
|
y: *mut c_int,
|
||||||
|
screen_num: *mut c_int,
|
||||||
|
) -> c_int;
|
||||||
|
fn xdo_get_window_size(
|
||||||
|
xdo: Xdo,
|
||||||
|
window: *mut c_void,
|
||||||
|
width: *mut c_int,
|
||||||
|
height: *mut c_int,
|
||||||
|
) -> c_int;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[link(name = "X11")]
|
#[link(name = "X11")]
|
||||||
@ -119,6 +133,50 @@ pub fn get_cursor_pos() -> Option<(i32, i32)> {
|
|||||||
|
|
||||||
pub fn reset_input_cache() {}
|
pub fn reset_input_cache() {}
|
||||||
|
|
||||||
|
pub fn get_focused_display(displays: Vec<DisplayInfo>) -> Option<usize> {
|
||||||
|
let mut res = None;
|
||||||
|
XDO.with(|xdo| {
|
||||||
|
if let Ok(xdo) = xdo.try_borrow_mut() {
|
||||||
|
if xdo.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut x: c_int = 0;
|
||||||
|
let mut y: c_int = 0;
|
||||||
|
let mut width: c_int = 0;
|
||||||
|
let mut height: c_int = 0;
|
||||||
|
let mut window: *mut c_void = std::ptr::null_mut();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if xdo_get_active_window(*xdo, &mut window) != 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if xdo_get_window_location(
|
||||||
|
*xdo,
|
||||||
|
window,
|
||||||
|
&mut x as _,
|
||||||
|
&mut y as _,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
) != 0
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if xdo_get_window_size(*xdo, window, &mut width as _, &mut height as _) != 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let center_x = x + width / 2;
|
||||||
|
let center_y = y + height / 2;
|
||||||
|
res = displays.iter().position(|d| {
|
||||||
|
center_x >= d.x
|
||||||
|
&& center_x < d.x + d.width
|
||||||
|
&& center_y >= d.y
|
||||||
|
&& center_y < d.y + d.height
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_cursor() -> ResultType<Option<u64>> {
|
pub fn get_cursor() -> ResultType<Option<u64>> {
|
||||||
let mut res = None;
|
let mut res = None;
|
||||||
DISPLAY.with(|conn| {
|
DISPLAY.with(|conn| {
|
||||||
|
@ -17,8 +17,13 @@ use core_graphics::{
|
|||||||
display::{kCGNullWindowID, kCGWindowListOptionOnScreenOnly, CGWindowListCopyWindowInfo},
|
display::{kCGNullWindowID, kCGWindowListOptionOnScreenOnly, CGWindowListCopyWindowInfo},
|
||||||
window::{kCGWindowName, kCGWindowOwnerPID},
|
window::{kCGWindowName, kCGWindowOwnerPID},
|
||||||
};
|
};
|
||||||
use hbb_common::sysinfo::{Pid, Process, ProcessRefreshKind, System};
|
use hbb_common::{
|
||||||
use hbb_common::{anyhow::anyhow, bail, log, message_proto::Resolution};
|
allow_err,
|
||||||
|
anyhow::anyhow,
|
||||||
|
bail, log,
|
||||||
|
message_proto::{DisplayInfo, Resolution},
|
||||||
|
sysinfo::{Pid, Process, ProcessRefreshKind, System},
|
||||||
|
};
|
||||||
use include_dir::{include_dir, Dir};
|
use include_dir::{include_dir, Dir};
|
||||||
use objc::{class, msg_send, sel, sel_impl};
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
use scrap::{libc::c_void, quartz::ffi::*};
|
use scrap::{libc::c_void, quartz::ffi::*};
|
||||||
@ -302,6 +307,20 @@ pub fn get_cursor_pos() -> Option<(i32, i32)> {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_focused_display(displays: Vec<DisplayInfo>) -> Option<usize> {
|
||||||
|
unsafe {
|
||||||
|
let main_screen: id = msg_send![class!(NSScreen), mainScreen];
|
||||||
|
let screen: id = msg_send![main_screen, deviceDescription];
|
||||||
|
let id: id =
|
||||||
|
msg_send![screen, objectForKey: NSString::alloc(nil).init_str("NSScreenNumber")];
|
||||||
|
let display_name: u32 = msg_send![id, unsignedIntValue];
|
||||||
|
|
||||||
|
displays
|
||||||
|
.iter()
|
||||||
|
.position(|d| d.name == display_name.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_cursor() -> ResultType<Option<u64>> {
|
pub fn get_cursor() -> ResultType<Option<u64>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let seed = CGSCurrentCursorSeed();
|
let seed = CGSCurrentCursorSeed();
|
||||||
|
@ -12,7 +12,7 @@ use hbb_common::{
|
|||||||
bail,
|
bail,
|
||||||
config::{self, Config},
|
config::{self, Config},
|
||||||
log,
|
log,
|
||||||
message_proto::{Resolution, WindowsSession},
|
message_proto::{DisplayInfo, Resolution, WindowsSession},
|
||||||
sleep, timeout, tokio,
|
sleep, timeout, tokio,
|
||||||
};
|
};
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
@ -65,6 +65,24 @@ use windows_service::{
|
|||||||
use winreg::enums::*;
|
use winreg::enums::*;
|
||||||
use winreg::RegKey;
|
use winreg::RegKey;
|
||||||
|
|
||||||
|
pub fn get_focused_display(displays: Vec<DisplayInfo>) -> Option<usize> {
|
||||||
|
unsafe {
|
||||||
|
let hWnd = GetForegroundWindow();
|
||||||
|
let mut rect: RECT = mem::zeroed();
|
||||||
|
if GetWindowRect(hWnd, &mut rect as *mut RECT) == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
displays.iter().position(|display| {
|
||||||
|
let center_x = rect.left + (rect.right - rect.left) / 2;
|
||||||
|
let center_y = rect.top + (rect.bottom - rect.top) / 2;
|
||||||
|
center_x >= display.x
|
||||||
|
&& center_x <= display.x + display.width
|
||||||
|
&& center_y >= display.y
|
||||||
|
&& center_y <= display.y + display.height
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_cursor_pos() -> Option<(i32, i32)> {
|
pub fn get_cursor_pos() -> Option<(i32, i32)> {
|
||||||
unsafe {
|
unsafe {
|
||||||
#[allow(invalid_value)]
|
#[allow(invalid_value)]
|
||||||
|
@ -50,6 +50,7 @@ pub const NAME: &'static str = "";
|
|||||||
pub mod input_service {
|
pub mod input_service {
|
||||||
pub const NAME_CURSOR: &'static str = "";
|
pub const NAME_CURSOR: &'static str = "";
|
||||||
pub const NAME_POS: &'static str = "";
|
pub const NAME_POS: &'static str = "";
|
||||||
|
pub const NAME_WINDOW_FOCUS: &'static str = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,6 +106,7 @@ pub fn new() -> ServerPtr {
|
|||||||
if !display_service::capture_cursor_embedded() {
|
if !display_service::capture_cursor_embedded() {
|
||||||
server.add_service(Box::new(input_service::new_cursor()));
|
server.add_service(Box::new(input_service::new_cursor()));
|
||||||
server.add_service(Box::new(input_service::new_pos()));
|
server.add_service(Box::new(input_service::new_pos()));
|
||||||
|
server.add_service(Box::new(input_service::new_window_focus()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Arc::new(RwLock::new(server))
|
Arc::new(RwLock::new(server))
|
||||||
@ -354,6 +356,15 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_subbed_displays_count(&self, conn_id: i32) -> usize {
|
||||||
|
self.services
|
||||||
|
.keys()
|
||||||
|
.filter(|k| {
|
||||||
|
Self::is_video_service_name(k) && self.services.get(*k).unwrap().is_subed(conn_id)
|
||||||
|
})
|
||||||
|
.count()
|
||||||
|
}
|
||||||
|
|
||||||
fn capture_displays(
|
fn capture_displays(
|
||||||
&mut self,
|
&mut self,
|
||||||
conn: ConnInner,
|
conn: ConnInner,
|
||||||
|
@ -31,8 +31,7 @@ use hbb_common::platform::linux::run_cmds;
|
|||||||
use hbb_common::protobuf::EnumOrUnknown;
|
use hbb_common::protobuf::EnumOrUnknown;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
config::Config,
|
config::Config,
|
||||||
fs,
|
fs::{self, can_enable_overwrite_detection},
|
||||||
fs::can_enable_overwrite_detection,
|
|
||||||
futures::{SinkExt, StreamExt},
|
futures::{SinkExt, StreamExt},
|
||||||
get_time, get_version_number,
|
get_time, get_version_number,
|
||||||
message_proto::{option_message::BoolOption, permission_info::Permission},
|
message_proto::{option_message::BoolOption, permission_info::Permission},
|
||||||
@ -241,6 +240,9 @@ pub struct Connection {
|
|||||||
delayed_read_dir: Option<(String, bool)>,
|
delayed_read_dir: Option<(String, bool)>,
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
retina: Retina,
|
retina: Retina,
|
||||||
|
follow_remote_cursor: bool,
|
||||||
|
follow_remote_window: bool,
|
||||||
|
multi_ui_session: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnInner {
|
impl ConnInner {
|
||||||
@ -348,6 +350,9 @@ impl Connection {
|
|||||||
network_delay: 0,
|
network_delay: 0,
|
||||||
lock_after_session_end: false,
|
lock_after_session_end: false,
|
||||||
show_remote_cursor: false,
|
show_remote_cursor: false,
|
||||||
|
follow_remote_cursor: false,
|
||||||
|
follow_remote_window: false,
|
||||||
|
multi_ui_session: false,
|
||||||
ip: "".to_owned(),
|
ip: "".to_owned(),
|
||||||
disable_audio: false,
|
disable_audio: false,
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
@ -666,8 +671,14 @@ impl Connection {
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
conn.retina.set_displays(&_pi.displays);
|
conn.retina.set_displays(&_pi.displays);
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
Some(message::Union::CursorPosition(pos)) => {
|
Some(message::Union::CursorPosition(pos)) => {
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
{
|
||||||
|
if conn.follow_remote_cursor {
|
||||||
|
conn.handle_cursor_switch_display(pos.clone()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
if let Some(new_msg) = conn.retina.on_cursor_pos(&pos, conn.display_idx) {
|
if let Some(new_msg) = conn.retina.on_cursor_pos(&pos, conn.display_idx) {
|
||||||
msg = Arc::new(new_msg);
|
msg = Arc::new(new_msg);
|
||||||
}
|
}
|
||||||
@ -1308,6 +1319,9 @@ impl Connection {
|
|||||||
if !self.show_remote_cursor {
|
if !self.show_remote_cursor {
|
||||||
noperms.push(NAME_POS);
|
noperms.push(NAME_POS);
|
||||||
}
|
}
|
||||||
|
if !self.follow_remote_window {
|
||||||
|
noperms.push(NAME_WINDOW_FOCUS);
|
||||||
|
}
|
||||||
if !self.clipboard_enabled() || !self.peer_keyboard_enabled() {
|
if !self.clipboard_enabled() || !self.peer_keyboard_enabled() {
|
||||||
noperms.push(super::clipboard_service::NAME);
|
noperms.push(super::clipboard_service::NAME);
|
||||||
}
|
}
|
||||||
@ -2581,6 +2595,14 @@ impl Connection {
|
|||||||
} else {
|
} else {
|
||||||
lock.capture_displays(self.inner.clone(), set, true, true);
|
lock.capture_displays(self.inner.clone(), set, true, true);
|
||||||
}
|
}
|
||||||
|
self.multi_ui_session = lock.get_subbed_displays_count(self.inner.id()) > 1;
|
||||||
|
if self.follow_remote_window {
|
||||||
|
lock.subscribe(
|
||||||
|
NAME_WINDOW_FOCUS,
|
||||||
|
self.inner.clone(),
|
||||||
|
!self.multi_ui_session,
|
||||||
|
);
|
||||||
|
}
|
||||||
drop(lock);
|
drop(lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2766,6 +2788,24 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
if let Ok(q) = o.follow_remote_cursor.enum_value() {
|
||||||
|
if q != BoolOption::NotSet {
|
||||||
|
self.follow_remote_cursor = q == BoolOption::Yes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(q) = o.follow_remote_window.enum_value() {
|
||||||
|
if q != BoolOption::NotSet {
|
||||||
|
self.follow_remote_window = q == BoolOption::Yes;
|
||||||
|
if let Some(s) = self.server.upgrade() {
|
||||||
|
s.write().unwrap().subscribe(
|
||||||
|
NAME_WINDOW_FOCUS,
|
||||||
|
self.inner.clone(),
|
||||||
|
self.follow_remote_window,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if let Ok(q) = o.disable_audio.enum_value() {
|
if let Ok(q) = o.disable_audio.enum_value() {
|
||||||
if q != BoolOption::NotSet {
|
if q != BoolOption::NotSet {
|
||||||
self.disable_audio = q == BoolOption::Yes;
|
self.disable_audio = q == BoolOption::Yes;
|
||||||
@ -3126,6 +3166,30 @@ impl Connection {
|
|||||||
self.inner.send(msg.into());
|
self.inner.send(msg.into());
|
||||||
self.supported_encoding_flag = (true, not_use);
|
self.supported_encoding_flag = (true, not_use);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
async fn handle_cursor_switch_display(&mut self, pos: CursorPosition) {
|
||||||
|
if self.multi_ui_session {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let displays = super::display_service::get_sync_displays();
|
||||||
|
let d_index = displays.iter().position(|d| {
|
||||||
|
let scale = d.scale;
|
||||||
|
pos.x >= d.x
|
||||||
|
&& pos.y >= d.y
|
||||||
|
&& (pos.x - d.x) as f64 * scale < d.width as f64
|
||||||
|
&& (pos.y - d.y) as f64 * scale < d.height as f64
|
||||||
|
});
|
||||||
|
if let Some(d_index) = d_index {
|
||||||
|
if self.display_idx != d_index {
|
||||||
|
let mut misc = Misc::new();
|
||||||
|
misc.set_follow_current_display(d_index as i32);
|
||||||
|
let mut msg_out = Message::new();
|
||||||
|
msg_out.set_misc(misc);
|
||||||
|
self.send(msg_out).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) {
|
pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) {
|
||||||
|
@ -258,7 +258,6 @@ pub(super) fn get_original_resolution(
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
pub(super) fn get_sync_displays() -> Vec<DisplayInfo> {
|
pub(super) fn get_sync_displays() -> Vec<DisplayInfo> {
|
||||||
SYNC_DISPLAYS.lock().unwrap().displays.clone()
|
SYNC_DISPLAYS.lock().unwrap().displays.clone()
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ use std::{
|
|||||||
use winapi::um::winuser::WHEEL_DELTA;
|
use winapi::um::winuser::WHEEL_DELTA;
|
||||||
|
|
||||||
const INVALID_CURSOR_POS: i32 = i32::MIN;
|
const INVALID_CURSOR_POS: i32 = i32::MIN;
|
||||||
|
const INVALID_DISPLAY_IDX: i32 = -1;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct StateCursor {
|
struct StateCursor {
|
||||||
@ -74,6 +75,29 @@ impl StatePos {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct StateWindowFocus {
|
||||||
|
display_idx: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::service::Reset for StateWindowFocus {
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.display_idx = INVALID_DISPLAY_IDX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StateWindowFocus {
|
||||||
|
#[inline]
|
||||||
|
fn is_valid(&self) -> bool {
|
||||||
|
self.display_idx != INVALID_DISPLAY_IDX
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_changed(&self, disp_idx: i32) -> bool {
|
||||||
|
self.is_valid() && self.display_idx != disp_idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy)]
|
#[derive(Default, Clone, Copy)]
|
||||||
struct Input {
|
struct Input {
|
||||||
conn: i32,
|
conn: i32,
|
||||||
@ -238,6 +262,7 @@ fn should_disable_numlock(evt: &KeyEvent) -> bool {
|
|||||||
|
|
||||||
pub const NAME_CURSOR: &'static str = "mouse_cursor";
|
pub const NAME_CURSOR: &'static str = "mouse_cursor";
|
||||||
pub const NAME_POS: &'static str = "mouse_pos";
|
pub const NAME_POS: &'static str = "mouse_pos";
|
||||||
|
pub const NAME_WINDOW_FOCUS: &'static str = "window_focus";
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MouseCursorService {
|
pub struct MouseCursorService {
|
||||||
pub sp: ServiceTmpl<MouseCursorSub>,
|
pub sp: ServiceTmpl<MouseCursorSub>,
|
||||||
@ -277,6 +302,12 @@ pub fn new_pos() -> GenericService {
|
|||||||
svc.sp
|
svc.sp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_window_focus() -> GenericService {
|
||||||
|
let svc = EmptyExtraFieldService::new(NAME_WINDOW_FOCUS.to_owned(), false);
|
||||||
|
GenericService::repeat::<StateWindowFocus, _, _>(&svc.clone(), 33, run_window_focus);
|
||||||
|
svc.sp
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn update_last_cursor_pos(x: i32, y: i32) {
|
fn update_last_cursor_pos(x: i32, y: i32) {
|
||||||
let mut lock = LATEST_SYS_CURSOR_POS.lock().unwrap();
|
let mut lock = LATEST_SYS_CURSOR_POS.lock().unwrap();
|
||||||
@ -352,6 +383,22 @@ fn run_cursor(sp: MouseCursorService, state: &mut StateCursor) -> ResultType<()>
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_window_focus(sp: EmptyExtraFieldService, state: &mut StateWindowFocus) -> ResultType<()> {
|
||||||
|
let displays = super::display_service::get_sync_displays();
|
||||||
|
let disp_idx = crate::get_focused_display(displays);
|
||||||
|
if let Some(disp_idx) = disp_idx.map(|id| id as i32) {
|
||||||
|
if state.is_changed(disp_idx) {
|
||||||
|
let mut misc = Misc::new();
|
||||||
|
misc.set_follow_current_display(disp_idx as i32);
|
||||||
|
let mut msg_out = Message::new();
|
||||||
|
msg_out.set_misc(misc);
|
||||||
|
sp.send(msg_out);
|
||||||
|
}
|
||||||
|
state.display_idx = disp_idx;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
enum KeysDown {
|
enum KeysDown {
|
||||||
RdevKey(RawKey),
|
RdevKey(RawKey),
|
||||||
@ -424,12 +471,15 @@ struct VirtualInputState {
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
impl VirtualInputState {
|
impl VirtualInputState {
|
||||||
fn new() -> Option<Self> {
|
fn new() -> Option<Self> {
|
||||||
VirtualInput::new(CGEventSourceStateID::CombinedSessionState, CGEventTapLocation::Session)
|
VirtualInput::new(
|
||||||
.map(|virtual_input| Self {
|
CGEventSourceStateID::CombinedSessionState,
|
||||||
virtual_input,
|
CGEventTapLocation::Session,
|
||||||
capslock_down: false,
|
)
|
||||||
})
|
.map(|virtual_input| Self {
|
||||||
.ok()
|
virtual_input,
|
||||||
|
capslock_down: false,
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -194,6 +194,8 @@ class Header: Reactor.Component {
|
|||||||
</div> : ""}
|
</div> : ""}
|
||||||
<div .separator />
|
<div .separator />
|
||||||
{!cursor_embedded && <li #show-remote-cursor .toggle-option><span>{svg_checkmark}</span>{translate('Show remote cursor')}</li>}
|
{!cursor_embedded && <li #show-remote-cursor .toggle-option><span>{svg_checkmark}</span>{translate('Show remote cursor')}</li>}
|
||||||
|
{<li #follow-remote-cursor .toggle-option><span>{svg_checkmark}</span>{translate('Follow remote cursor')}</li>}
|
||||||
|
{<li #follow-remote-window .toggle-option><span>{svg_checkmark}</span>{translate('Follow remote window focus')}</li>}
|
||||||
<li #show-quality-monitor .toggle-option><span>{svg_checkmark}</span>{translate('Show quality monitor')}</li>
|
<li #show-quality-monitor .toggle-option><span>{svg_checkmark}</span>{translate('Show quality monitor')}</li>
|
||||||
{audio_enabled ? <li #disable-audio .toggle-option><span>{svg_checkmark}</span>{translate('Mute')}</li> : ""}
|
{audio_enabled ? <li #disable-audio .toggle-option><span>{svg_checkmark}</span>{translate('Mute')}</li> : ""}
|
||||||
{(is_win && pi.platform == "Windows") && file_enabled ? <li #enable-file-transfer .toggle-option><span>{svg_checkmark}</span>{translate('Enable file copy and paste')}</li> : ""}
|
{(is_win && pi.platform == "Windows") && file_enabled ? <li #enable-file-transfer .toggle-option><span>{svg_checkmark}</span>{translate('Enable file copy and paste')}</li> : ""}
|
||||||
@ -479,7 +481,7 @@ function toggleMenuState() {
|
|||||||
for (var el in $$(menu#keyboard-options>li)) {
|
for (var el in $$(menu#keyboard-options>li)) {
|
||||||
el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0);
|
el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0);
|
||||||
}
|
}
|
||||||
for (var id in ["show-remote-cursor", "show-quality-monitor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end", "allow_swap_key", "i444"]) {
|
for (var id in ["show-remote-cursor", "follow-remote-cursor", "follow-remote-window", "show-quality-monitor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end", "allow_swap_key", "i444"]) {
|
||||||
var el = self.select('#' + id);
|
var el = self.select('#' + id);
|
||||||
if (el) {
|
if (el) {
|
||||||
var value = handler.get_toggle_option(id);
|
var value = handler.get_toggle_option(id);
|
||||||
@ -538,6 +540,15 @@ handler.setMultipleWindowsSession = function(sessions) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handler.setCurrentDisplay = function(v) {
|
||||||
|
pi.current_display = v;
|
||||||
|
handler.switch_display(v);
|
||||||
|
header.update();
|
||||||
|
if (is_port_forward) {
|
||||||
|
view.windowState = View.WINDOW_MINIMIZED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updatePrivacyMode() {
|
function updatePrivacyMode() {
|
||||||
var el = $(li#privacy-mode);
|
var el = $(li#privacy-mode);
|
||||||
if (el) {
|
if (el) {
|
||||||
|
@ -259,6 +259,10 @@ impl InvokeUiSession for SciterHandler {
|
|||||||
// Ignore for sciter version.
|
// Ignore for sciter version.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_current_display(&self, _disp_idx: i32) {
|
||||||
|
self.call("setCurrentDisplay", &make_args!(_disp_idx));
|
||||||
|
}
|
||||||
|
|
||||||
fn set_multiple_windows_session(&self, sessions: Vec<WindowsSession>) {
|
fn set_multiple_windows_session(&self, sessions: Vec<WindowsSession>) {
|
||||||
let mut v = Value::array(0);
|
let mut v = Value::array(0);
|
||||||
let mut sessions = sessions;
|
let mut sessions = sessions;
|
||||||
|
@ -192,6 +192,11 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
self.lc.read().unwrap().conn_type.eq(&ConnType::RDP)
|
self.lc.read().unwrap().conn_type.eq(&ConnType::RDP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "flutter")]
|
||||||
|
pub fn is_multi_ui_session(&self) -> bool {
|
||||||
|
self.ui_handler.is_multi_ui_session()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_view_style(&self) -> String {
|
pub fn get_view_style(&self) -> String {
|
||||||
self.lc.read().unwrap().view_style.clone()
|
self.lc.read().unwrap().view_style.clone()
|
||||||
}
|
}
|
||||||
@ -1378,6 +1383,9 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {
|
|||||||
#[cfg(all(feature = "vram", feature = "flutter"))]
|
#[cfg(all(feature = "vram", feature = "flutter"))]
|
||||||
fn on_texture(&self, display: usize, texture: *mut c_void);
|
fn on_texture(&self, display: usize, texture: *mut c_void);
|
||||||
fn set_multiple_windows_session(&self, sessions: Vec<WindowsSession>);
|
fn set_multiple_windows_session(&self, sessions: Vec<WindowsSession>);
|
||||||
|
fn set_current_display(&self, disp_idx: i32);
|
||||||
|
#[cfg(feature = "flutter")]
|
||||||
|
fn is_multi_ui_session(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: InvokeUiSession> Deref for Session<T> {
|
impl<T: InvokeUiSession> Deref for Session<T> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user