diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 5b0a3d54e..a20d35ed2 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1318,6 +1318,7 @@ class _DisplayState extends State<_Display> { Widget other(BuildContext context) { return _Card(title: 'Other Default Options', children: [ + otherRow('View Mode', 'view-only'), otherRow('show_monitors_tip', 'show_monitors_toolbar'), otherRow('Show remote cursor', 'show_remote_cursor'), otherRow('Zoom cursor', 'zoom-cursor'), diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 2b1999b42..fdd2d05b4 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -139,8 +139,9 @@ class _RemotePageState extends State _ffi.ffiModel.updateEventListener(widget.id); _ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id); // Session option should be set after models.dart/FFI.start - _showRemoteCursor.value = bind.sessionGetToggleOptionSync( - id: widget.id, arg: 'show-remote-cursor'); + // _showRemoteCursor has been set by setViewOnly + _ffi.ffiModel.setViewOnly(widget.id, + bind.sessionGetToggleOptionSync(id: widget.id, arg: 'view-only')); _zoomCursor.value = bind.sessionGetToggleOptionSync(id: widget.id, arg: 'zoom-cursor'); DesktopMultiWindow.addListener(this); diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 41c2ef21f..65d763013 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -385,6 +385,7 @@ class _RemoteMenubarState extends State { } Widget _buildToolbar(BuildContext context) { + final ffiModel = Provider.of(context); final List toolbarItems = []; if (!isWebDesktop) { toolbarItems.add(_PinMenu(state: widget.state)); @@ -410,7 +411,9 @@ class _RemoteMenubarState extends State { state: widget.state, setFullscreen: _setFullscreen, )); - toolbarItems.add(_KeyboardMenu(id: widget.id, ffi: widget.ffi)); + if (!ffiModel.viewOnly) { + toolbarItems.add(_KeyboardMenu(id: widget.id, ffi: widget.ffi)); + } if (!isWeb) { toolbarItems.add(_ChatMenu(id: widget.id, ffi: widget.ffi)); toolbarItems.add(_VoiceCallMenu(id: widget.id, ffi: widget.ffi)); @@ -820,8 +823,10 @@ class _ControlMenu extends StatelessWidget { ctrlAltDel() { final perms = ffi.ffiModel.permissions; + final viewOnly = ffi.ffiModel.viewOnly; final pi = ffi.ffiModel.pi; - final visible = perms['keyboard'] != false && + final visible = !viewOnly && + perms['keyboard'] != false && (pi.platform == kPeerPlatformLinux || pi.sasEnabled); if (!visible) return Offstage(); return _MenuItemButton( @@ -846,7 +851,8 @@ class _ControlMenu extends StatelessWidget { insertLock() { final perms = ffi.ffiModel.permissions; - final visible = perms['keyboard'] != false; + final viewOnly = ffi.ffiModel.viewOnly; + final visible = !viewOnly && perms['keyboard'] != false; if (!visible) return Offstage(); return _MenuItemButton( child: Text(translate('Insert Lock')), @@ -963,6 +969,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { codec(), resolutions(), Divider(), + view_only(), showRemoteCursor(), zoomCursor(), showQualityMonitor(), @@ -1456,11 +1463,28 @@ class _DisplayMenuState extends State<_DisplayMenu> { child: Text(translate("Resolution"))); } + view_only() { + final visible = version_cmp(pi.version, '1.2.0') >= 0; + if (!visible) return Offstage(); + final ffiModel = widget.ffi.ffiModel; + return _CheckboxMenuButton( + value: ffiModel.viewOnly, + onChanged: (value) async { + if (value == null) return; + bind.sessionSetViewOnly(id: widget.id, viewOnly: value); + ffiModel.setViewOnly(widget.id, value); + }, + ffi: widget.ffi, + child: Text(translate('View Mode'))); + } + showRemoteCursor() { if (widget.ffi.ffiModel.pi.platform == kPeerPlatformAndroid) { return Offstage(); } - final visible = !widget.ffi.canvasModel.cursorEmbedded; + final ffiModel = widget.ffi.ffiModel; + final visible = + !ffiModel.viewOnly && !widget.ffi.canvasModel.cursorEmbedded; if (!visible) return Offstage(); final state = ShowRemoteCursorState.find(widget.id); final option = 'show-remote-cursor'; @@ -1543,7 +1567,10 @@ class _DisplayMenuState extends State<_DisplayMenu> { } disableClipboard() { - final visible = perms['keyboard'] != false && perms['clipboard'] != false; + final ffiModel = widget.ffi.ffiModel; + final visible = perms['keyboard'] != false && + perms['clipboard'] != false && + !ffiModel.viewOnly; if (!visible) return Offstage(); final option = 'disable-clipboard'; final value = bind.sessionGetToggleOptionSync(id: widget.id, arg: option); diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index fd88a5332..50922c52c 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -47,6 +47,7 @@ class FfiModel with ChangeNotifier { bool _touchMode = false; Timer? _timer; var _reconnects = 1; + bool _viewOnly = false; WeakReference parent; Map get permissions => _permissions; @@ -65,6 +66,8 @@ class FfiModel with ChangeNotifier { bool get isPeerAndroid => _pi.platform == kPeerPlatformAndroid; + bool get viewOnly => _viewOnly; + set inputBlocked(v) { _inputBlocked = v; } @@ -373,7 +376,8 @@ class FfiModel with ChangeNotifier { _updateSessionWidthHeight(String id) { parent.target?.canvasModel.updateViewStyle(); if (display.width <= 0 || display.height <= 0) { - debugPrintStack(label: 'invalid display size (${display.width},${display.height})'); + debugPrintStack( + label: 'invalid display size (${display.width},${display.height})'); } else { bind.sessionSetSize(id: id, width: display.width, height: display.height); } @@ -516,6 +520,19 @@ class FfiModel with ChangeNotifier { // } } + + void setViewOnly(String id, bool value) { + if (value) { + ShowRemoteCursorState.find(id).value = value; + } else { + ShowRemoteCursorState.find(id).value = + bind.sessionGetToggleOptionSync(id: id, arg: 'show-remote-cursor'); + } + if (_viewOnly != value) { + _viewOnly = value; + notifyListeners(); + } + } } class ImageModel with ChangeNotifier { diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 0c29a5f19..ed704f91e 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -487,6 +487,7 @@ message OptionMessage { BoolOption enable_file_transfer = 9; VideoCodecState video_codec_state = 10; int32 custom_fps = 11; + BoolOption disable_keyboard = 12; } message TestDelay { diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 4c4f065ab..a54c08ce7 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -1061,6 +1061,10 @@ impl PeerConfig { if !mp.contains_key(key) { mp.insert(key.to_owned(), UserDefaultConfig::read().get(key)); } + key = "view-only"; + if !mp.contains_key(key) { + mp.insert(key.to_owned(), UserDefaultConfig::read().get(key)); + } Ok(mp) } } diff --git a/src/client.rs b/src/client.rs index 705b387a8..66bdc623d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1296,7 +1296,12 @@ impl LoginConfigHandler { if let Some(custom_fps) = self.options.get("custom-fps") { msg.custom_fps = custom_fps.parse().unwrap_or(30); } - if self.get_toggle_option("show-remote-cursor") { + let view_only = self.get_toggle_option("view-only"); + if view_only { + msg.disable_keyboard = BoolOption::Yes.into(); + n += 1; + } + if view_only || self.get_toggle_option("show-remote-cursor") { msg.show_remote_cursor = BoolOption::Yes.into(); n += 1; } @@ -1312,7 +1317,7 @@ impl LoginConfigHandler { msg.enable_file_transfer = BoolOption::Yes.into(); n += 1; } - if self.get_toggle_option("disable-clipboard") { + if view_only || self.get_toggle_option("disable-clipboard") { msg.disable_clipboard = BoolOption::Yes.into(); n += 1; } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 2a3baad95..4b8bf43d3 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -543,6 +543,12 @@ pub fn session_set_size(_id: String, _width: i32, _height: i32) { } } +pub fn session_set_view_only(id: String, view_only: bool) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.set_view_only(view_only); + } +} + pub fn main_get_sound_inputs() -> Vec { #[cfg(not(any(target_os = "android", target_os = "ios")))] return get_sound_inputs(); diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 7a8e5e437..c77d73944 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 0781a2a97..14dc440ae 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", "此文件与对方的一致"), ("show_monitors_tip", ""), + ("View Mode", "浏览模式"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 03f8d6240..5e8bdba5f 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index cfb66528d..542543155 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", "Mig"), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index ab1e2976b..58ea77d6d 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", "Ich"), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index e7defa943..e5366e2f9 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 2eb430d32..6d697528d 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 12a537216..21df9c914 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", "Yo"), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 2b2091c90..ffdf22a50 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", "من"), ("identical_file_tip", "این فایل با فایل همتا یکسان است."), ("show_monitors_tip", "نمایش مانیتورها در نوار ابزار"), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index b327972b7..6aa7f12f6 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index b4df936bb..474b3a70d 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 465f2082c..3bdef56bb 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 94fea4e03..7dc3b70ef 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", "Io"), ("identical_file_tip", "Questo file è identico a quello del peer."), ("show_monitors_tip", "Mostra schermi nella barra degli strumenti"), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index d5bd2e973..925ccebde 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 83ba01915..89c65a332 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index de33f7e57..0ec4d099b 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 05fc9a65c..46ae3799a 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index e93635ca5..aabf14fc7 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index a2189a7b0..25561d43a 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 19db9e989..6a6df5078 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 382844fba..886f27bd4 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index ee35e598a..44b6eb62b 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 43d1539a9..ceed59b43 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 56539fa74..14e50fc1d 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 65e18b517..142074539 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index c16a0c039..7ea384def 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index b5529c05b..31db07b30 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 4f29f7dc3..343d6c1ea 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 72355ee5d..a05f8ff88 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index ba4c33bea..e00877118 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 90cb0bf61..d562cdfb4 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", "瀏覽模式"), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 68b46e700..7abfd4188 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 43e339331..918106363 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -479,5 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", ""), ("identical_file_tip", ""), ("show_monitors_tip", ""), + ("View Mode", ""), ].iter().cloned().collect(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 7899009c4..b531a2add 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -103,6 +103,9 @@ pub struct Connection { show_remote_cursor: bool, // by peer ip: String, + // by peer + disable_keyboard: bool, + // by peer disable_clipboard: bool, // by peer disable_audio: bool, @@ -219,6 +222,7 @@ impl Connection { disable_audio: false, enable_file_transfer: false, disable_clipboard: false, + disable_keyboard: false, tx_input, video_ack_required: false, peer_info: Default::default(), @@ -327,7 +331,7 @@ impl Connection { if let Some(s) = conn.server.upgrade() { s.write().unwrap().subscribe( super::clipboard_service::NAME, - conn.inner.clone(), conn.clipboard_enabled() && conn.keyboard); + conn.inner.clone(), conn.clipboard_enabled() && conn.peer_keyboard_enabled()); } } else if &name == "audio" { conn.audio = enabled; @@ -939,13 +943,13 @@ impl Connection { } else if sub_service { if let Some(s) = self.server.upgrade() { let mut noperms = Vec::new(); - if !self.keyboard && !self.show_remote_cursor { + if !self.peer_keyboard_enabled() && !self.show_remote_cursor { noperms.push(NAME_CURSOR); } if !self.show_remote_cursor { noperms.push(NAME_POS); } - if !self.clipboard_enabled() || !self.keyboard { + if !self.clipboard_enabled() || !self.peer_keyboard_enabled() { noperms.push(super::clipboard_service::NAME); } if !self.audio_enabled() { @@ -959,6 +963,10 @@ impl Connection { } } + fn peer_keyboard_enabled(&self) -> bool { + self.keyboard && !self.disable_keyboard + } + fn clipboard_enabled(&self) -> bool { self.clipboard && !self.disable_clipboard } @@ -1312,7 +1320,7 @@ impl Connection { log::debug!("call_main_service_mouse_input fail:{}", e); } #[cfg(not(any(target_os = "android", target_os = "ios")))] - if self.keyboard { + if self.peer_keyboard_enabled() { if is_left_up(&me) { CLICK_TIME.store(get_time(), Ordering::SeqCst); } else { @@ -1323,7 +1331,7 @@ impl Connection { } Some(message::Union::KeyEvent(me)) => { #[cfg(not(any(target_os = "android", target_os = "ios")))] - if self.keyboard { + if self.peer_keyboard_enabled() { if is_enter(&me) { CLICK_TIME.store(get_time(), Ordering::SeqCst); } @@ -1750,7 +1758,7 @@ impl Connection { s.write().unwrap().subscribe( NAME_CURSOR, self.inner.clone(), - self.keyboard || self.show_remote_cursor, + self.peer_keyboard_enabled() || self.show_remote_cursor, ); s.write().unwrap().subscribe( NAME_POS, @@ -1788,7 +1796,24 @@ impl Connection { s.write().unwrap().subscribe( super::clipboard_service::NAME, self.inner.clone(), - self.clipboard_enabled() && self.keyboard, + self.clipboard_enabled() && self.peer_keyboard_enabled(), + ); + } + } + } + if let Ok(q) = o.disable_keyboard.enum_value() { + if q != BoolOption::NotSet { + self.disable_keyboard = q == BoolOption::Yes; + if let Some(s) = self.server.upgrade() { + s.write().unwrap().subscribe( + super::clipboard_service::NAME, + self.inner.clone(), + self.clipboard_enabled() && self.peer_keyboard_enabled(), + ); + s.write().unwrap().subscribe( + NAME_CURSOR, + self.inner.clone(), + self.peer_keyboard_enabled() || self.show_remote_cursor, ); } } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index b90f5fbea..735f963e8 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -10,6 +10,7 @@ use std::time::{Duration, SystemTime}; use async_trait::async_trait; use bytes::Bytes; +use hbb_common::message_proto::option_message::BoolOption; use rdev::{Event, EventType::*}; use uuid::Uuid; @@ -374,7 +375,6 @@ impl Session { } pub fn swab_modifier_key(&self, msg: &mut KeyEvent) { - let allow_swap_key = self.get_toggle_option("allow_swap_key".to_string()); if allow_swap_key { if let Some(key_event::Union::ControlKey(ck)) = msg.union { @@ -388,19 +388,22 @@ impl Session { }; msg.set_control_key(ck); } - msg.modifiers = msg.modifiers.iter().map(|ck| { - let ck = ck.enum_value_or_default(); - let ck = match ck { - ControlKey::Control => ControlKey::Meta, - ControlKey::Meta => ControlKey::Control, - ControlKey::RControl => ControlKey::Meta, - ControlKey::RWin => ControlKey::Control, - _ => ck, - }; - hbb_common::protobuf::EnumOrUnknown::new(ck) - }).collect(); - - + msg.modifiers = msg + .modifiers + .iter() + .map(|ck| { + let ck = ck.enum_value_or_default(); + let ck = match ck { + ControlKey::Control => ControlKey::Meta, + ControlKey::Meta => ControlKey::Control, + ControlKey::RControl => ControlKey::Meta, + ControlKey::RWin => ControlKey::Control, + _ => ck, + }; + hbb_common::protobuf::EnumOrUnknown::new(ck) + }) + .collect(); + let code = msg.chr(); if code != 0 { let mut peer = self.peer_platform().to_lowercase(); @@ -444,7 +447,6 @@ impl Session { msg.set_chr(key); } } - } pub fn send_key_event(&self, evt: &KeyEvent) { @@ -841,6 +843,37 @@ impl Session { } false } + + pub fn set_view_only(&self, view_only: bool) { + let mut option = OptionMessage::default(); + let f = |b: bool| { + if b { + BoolOption::Yes.into() + } else { + BoolOption::No.into() + } + }; + if view_only { + option.disable_keyboard = f(true); + option.disable_clipboard = f(true); + option.show_remote_cursor = f(true); + } else { + option.disable_keyboard = f(false); + option.disable_clipboard = f(self.get_toggle_option("disable-clipboard".to_string())); + option.show_remote_cursor = f(self.get_toggle_option("show-remote-cursor".to_string())); + } + let mut misc = Misc::new(); + misc.set_option(option); + let mut msg = Message::new(); + msg.set_misc(misc); + self.send(Data::Message(msg)); + if self.get_toggle_option("view-only".to_string()) != view_only { + self.lc + .write() + .unwrap() + .toggle_option("view-only".to_string()); + } + } } pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default { @@ -1011,21 +1044,25 @@ impl Interface for Session { handle_test_delay(t, peer).await; } } - - fn swap_modifier_mouse(&self, msg : &mut hbb_common::protos::message::MouseEvent) { + + fn swap_modifier_mouse(&self, msg: &mut hbb_common::protos::message::MouseEvent) { let allow_swap_key = self.get_toggle_option("allow_swap_key".to_string()); - if allow_swap_key { - msg.modifiers = msg.modifiers.iter().map(|ck| { - let ck = ck.enum_value_or_default(); - let ck = match ck { - ControlKey::Control => ControlKey::Meta, - ControlKey::Meta => ControlKey::Control, - ControlKey::RControl => ControlKey::Meta, - ControlKey::RWin => ControlKey::Control, - _ => ck, - }; - hbb_common::protobuf::EnumOrUnknown::new(ck) - }).collect(); + if allow_swap_key { + msg.modifiers = msg + .modifiers + .iter() + .map(|ck| { + let ck = ck.enum_value_or_default(); + let ck = match ck { + ControlKey::Control => ControlKey::Meta, + ControlKey::Meta => ControlKey::Control, + ControlKey::RControl => ControlKey::Meta, + ControlKey::RWin => ControlKey::Control, + _ => ck, + }; + hbb_common::protobuf::EnumOrUnknown::new(ck) + }) + .collect(); }; } }