diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index effc26b39..0693554d9 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -219,17 +219,12 @@ class _GeneralState extends State<_General> { } Widget hwcodec() { - return _futureBuilder( - future: bind.mainHasHwcodec(), - hasData: (data) { - return Offstage( - offstage: !(data as bool), - child: _Card(title: 'Hardware Codec', children: [ - _OptionCheckBox( - context, 'Enable hardware codec', 'enable-hwcodec'), - ]), - ); - }); + return Offstage( + offstage: !bind.mainHasHwcodec(), + child: _Card(title: 'Hardware Codec', children: [ + _OptionCheckBox(context, 'Enable hardware codec', 'enable-hwcodec'), + ]), + ); } Widget audio(BuildContext context) { diff --git a/flutter/lib/desktop/widgets/remote_menubar.dart b/flutter/lib/desktop/widgets/remote_menubar.dart index ef20352dc..092ea7da8 100644 --- a/flutter/lib/desktop/widgets/remote_menubar.dart +++ b/flutter/lib/desktop/widgets/remote_menubar.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:io'; import 'dart:math' as math; @@ -298,25 +299,35 @@ class _RemoteMenubarState extends State { } Widget _buildDisplay(BuildContext context) { - return mod_menu.PopupMenuButton( - padding: EdgeInsets.zero, - icon: const Icon( - Icons.tv, - color: _MenubarTheme.commonColor, - ), - tooltip: translate('Display Settings'), - position: mod_menu.PopupMenuPosition.under, - itemBuilder: (BuildContext context) => _getDisplayMenu() - .map((entry) => entry.build( - context, - const MenuConfig( - commonColor: _MenubarTheme.commonColor, - height: _MenubarTheme.height, - dividerHeight: _MenubarTheme.dividerHeight, - ))) - .expand((i) => i) - .toList(), - ); + return FutureBuilder(future: () async { + final supportedHwcodec = + await bind.sessionSupportedHwcodec(id: widget.id); + return {'supportedHwcodec': supportedHwcodec}; + }(), builder: (context, snapshot) { + if (snapshot.hasData) { + return mod_menu.PopupMenuButton( + padding: EdgeInsets.zero, + icon: const Icon( + Icons.tv, + color: _MenubarTheme.commonColor, + ), + tooltip: translate('Display Settings'), + position: mod_menu.PopupMenuPosition.under, + itemBuilder: (BuildContext context) => _getDisplayMenu(snapshot.data!) + .map((entry) => entry.build( + context, + const MenuConfig( + commonColor: _MenubarTheme.commonColor, + height: _MenubarTheme.height, + dividerHeight: _MenubarTheme.dividerHeight, + ))) + .expand((i) => i) + .toList(), + ); + } else { + return const Offstage(); + } + }); } Widget _buildKeyboard(BuildContext context) { @@ -532,7 +543,7 @@ class _RemoteMenubarState extends State { return displayMenu; } - List> _getDisplayMenu() { + List> _getDisplayMenu(dynamic futureData) { final displayMenu = [ MenuEntryRadios( text: translate('Ratio'), @@ -653,33 +664,74 @@ class _RemoteMenubarState extends State { } }), MenuEntryDivider(), - // {show_codec ?
- // MenuEntryDivider(), - () { - final state = ShowRemoteCursorState.find(widget.id); - return MenuEntrySwitch2( - text: translate('Show remote cursor'), - getter: () { - return state; + ]; + + /// Show Codec Preference + if (bind.mainHasHwcodec()) { + final List codecs = []; + try { + final Map codecsJson = jsonDecode(futureData['supportedHwcodec']); + final h264 = codecsJson['h264'] ?? false; + final h265 = codecsJson['h265'] ?? false; + codecs.add(h264); + codecs.add(h265); + } finally {} + if (codecs.length == 2 && (codecs[0] || codecs[1])) { + displayMenu.add(MenuEntryRadios( + text: translate('Codec Preference'), + optionsGetter: () { + final list = [ + MenuEntryRadioOption(text: translate('Auto'), value: 'auto'), + MenuEntryRadioOption(text: 'VP9', value: 'vp9'), + ]; + if (codecs[0]) { + list.add(MenuEntryRadioOption(text: 'H264', value: 'h264')); + } + if (codecs[1]) { + list.add(MenuEntryRadioOption(text: 'H265', value: 'h265')); + } + return list; }, - setter: (bool v) async { - state.value = v; - await bind.sessionToggleOption( - id: widget.id, value: 'show-remote-cursor'); - }); - }(), - MenuEntrySwitch( - text: translate('Show quality monitor'), - getter: () async { - return bind.sessionGetToggleOptionSync( - id: widget.id, arg: 'show-quality-monitor'); + curOptionGetter: () async { + return await bind.sessionGetOption( + id: widget.id, arg: 'codec-preference') ?? + 'auto'; + }, + optionSetter: (String oldValue, String newValue) async { + await bind.sessionPeerOption( + id: widget.id, name: "codec-preference", value: newValue); + bind.sessionChangePreferCodec(id: widget.id); + })); + } + } + + /// Show remote cursor + displayMenu.add(() { + final state = ShowRemoteCursorState.find(widget.id); + return MenuEntrySwitch2( + text: translate('Show remote cursor'), + getter: () { + return state; }, setter: (bool v) async { + state.value = v; await bind.sessionToggleOption( - id: widget.id, value: 'show-quality-monitor'); - widget.ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id); - }), - ]; + id: widget.id, value: 'show-remote-cursor'); + }); + }()); + + /// Show quality monitor + displayMenu.add(MenuEntrySwitch( + text: translate('Show quality monitor'), + getter: () async { + return bind.sessionGetToggleOptionSync( + id: widget.id, arg: 'show-quality-monitor'); + }, + setter: (bool v) async { + await bind.sessionToggleOption( + id: widget.id, value: 'show-quality-monitor'); + widget.ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id); + })); final perms = widget.ffi.ffiModel.permissions; final pi = widget.ffi.ffiModel.pi; diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index e1a806bf4..d68d030be 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -754,8 +754,8 @@ pub fn main_remove_peer(id: String) { PeerConfig::remove(&id); } -pub fn main_has_hwcodec() -> bool { - has_hwcodec() +pub fn main_has_hwcodec() -> SyncReturn { + SyncReturn(has_hwcodec()) } pub fn session_send_mouse(id: String, msg: String) { @@ -816,6 +816,22 @@ pub fn session_send_note(id: String, note: String) { } } +pub fn session_supported_hwcodec(id: String) -> String { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + let (h264, h265) = session.supported_hwcodec(); + let msg = HashMap::from([("h264", h264), ("h265", h265)]); + serde_json::ser::to_string(&msg).unwrap_or("".to_owned()) + } else { + String::new() + } +} + +pub fn session_change_prefer_codec(id: String) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.change_prefer_codec(); + } +} + pub fn main_set_home_dir(home: String) { *config::APP_HOME_DIR.write().unwrap() = home; } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 76e1ee96c..97dfe1d02 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -449,27 +449,11 @@ impl SciterSession { } fn supported_hwcodec(&self) -> Value { - #[cfg(feature = "hwcodec")] - { - let mut v = Value::array(0); - let decoder = scrap::codec::Decoder::video_codec_state(&self.id); - let mut h264 = decoder.score_h264 > 0; - let mut h265 = decoder.score_h265 > 0; - if let Some((encoding_264, encoding_265)) = self.lc.read().unwrap().supported_encoding { - h264 = h264 && encoding_264; - h265 = h265 && encoding_265; - } - v.push(h264); - v.push(h265); - v - } - #[cfg(not(feature = "hwcodec"))] - { - let mut v = Value::array(0); - v.push(false); - v.push(false); - v - } + let (h264, h265) = self.0.supported_hwcodec(); + let mut v = Value::array(0); + v.push(h264); + v.push(h265); + v } fn save_size(&mut self, x: i32, y: i32, w: i32, h: i32) { @@ -721,4 +705,4 @@ pub fn make_fd(id: i32, entries: &Vec, only_count: bool) -> Value { m.set_item("num_entries", entries.len() as i32); m.set_item("total_size", n as f64); m -} \ No newline at end of file +} diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 692b592d4..c6254b719 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -11,9 +11,9 @@ use async_trait::async_trait; use hbb_common::config::{Config, LocalConfig, PeerConfig}; use hbb_common::rendezvous_proto::ConnType; use hbb_common::tokio::{self, sync::mpsc}; -use rdev::{Event, EventType::*, Key as RdevKey, KeyboardState}; #[cfg(not(any(target_os = "android", target_os = "ios")))] use rdev::Keyboard as RdevKeyboard; +use rdev::{Event, EventType::*, Key as RdevKey, KeyboardState}; use hbb_common::{allow_err, message_proto::*}; use hbb_common::{fs, get_version_number, log, Stream}; @@ -143,6 +143,25 @@ impl Session { return true; } + pub fn supported_hwcodec(&self) -> (bool, bool) { + #[cfg(feature = "hwcodec")] + { + let decoder = scrap::codec::Decoder::video_codec_state(&self.id); + let mut h264 = decoder.score_h264 > 0; + let mut h265 = decoder.score_h265 > 0; + if let Some((encoding_264, encoding_265)) = self.lc.read().unwrap().supported_encoding { + h264 = h264 && encoding_264; + h265 = h265 && encoding_265; + } + return (h264, h265); + } + #[cfg(feature = "mediacodec")] + { + todo!(); + } + (false, false) + } + pub fn change_prefer_codec(&self) { let msg = self.lc.write().unwrap().change_prefer_codec(); self.send(Data::Message(msg)); @@ -658,18 +677,20 @@ impl Session { } self.map_keyboard_mode(down_or_up, key, Some(evt)); } - KeyboardMode::Legacy => { + KeyboardMode::Legacy => + { #[cfg(not(any(target_os = "android", target_os = "ios")))] self.legacy_keyboard_mode(down_or_up, key, evt) - }, + } KeyboardMode::Translate => { #[cfg(not(any(target_os = "android", target_os = "ios")))] self.translate_keyboard_mode(down_or_up, key, evt); } - _ => { + _ => + { #[cfg(not(any(target_os = "android", target_os = "ios")))] self.legacy_keyboard_mode(down_or_up, key, evt) - }, + } } } @@ -1178,7 +1199,7 @@ impl Session { if self.is_port_forward() || self.is_file_transfer() { return; } - if !KEYBOARD_HOOKED.load(Ordering::SeqCst){ + if !KEYBOARD_HOOKED.load(Ordering::SeqCst) { return; } log::info!("keyboard hooked");