resolution, mid commit, to debug

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou 2023-05-18 16:17:51 +08:00
parent a603e046e3
commit 07500013ff
7 changed files with 394 additions and 162 deletions

View File

@ -19,8 +19,6 @@ import '../../common/widgets/peer_tab_page.dart';
import '../../models/platform_model.dart';
import '../widgets/button.dart';
import 'package:flutter_hbb/common/widgets/dialog.dart';
/// Connection page for connecting to a remote peer.
class ConnectionPage extends StatefulWidget {
const ConnectionPage({Key? key}) : super(key: key);

View File

@ -664,71 +664,24 @@ class _ControlMenu extends StatelessWidget {
}
}
class _DisplayMenu extends StatefulWidget {
class ScreenAdjustor {
final String id;
final FFI ffi;
final MenubarState state;
final Function(bool) setFullscreen;
final Widget pluginItem;
_DisplayMenu(
{Key? key,
required this.id,
required this.ffi,
required this.state,
required this.setFullscreen})
: pluginItem = LocationItem.createLocationItem(
id,
ffi,
kLocationClientRemoteToolbarDisplay,
true,
),
super(key: key);
@override
State<_DisplayMenu> createState() => _DisplayMenuState();
}
class _DisplayMenuState extends State<_DisplayMenu> {
final VoidCallback cbExitFullscreen;
window_size.Screen? _screen;
ScreenAdjustor({
required this.id,
required this.ffi,
required this.cbExitFullscreen,
});
bool get isFullscreen => stateGlobal.fullscreen;
int get windowId => stateGlobal.windowId;
Map<String, bool> get perms => widget.ffi.ffiModel.permissions;
RxBool _isOrignalResolution = true.obs;
RxBool _isFitLocalResolution = false.obs;
PeerInfo get pi => widget.ffi.ffiModel.pi;
FfiModel get ffiModel => widget.ffi.ffiModel;
FFI get ffi => widget.ffi;
String get id => widget.id;
@override
Widget build(BuildContext context) {
_updateScreen();
return _IconSubmenuButton(
tooltip: 'Display Settings',
svg: "assets/display.svg",
ffi: widget.ffi,
color: _MenubarTheme.blueColor,
hoverColor: _MenubarTheme.hoverBlueColor,
menuChildren: [
adjustWindow(),
viewStyle(),
scrollStyle(),
imageQuality(),
codec(),
resolutions(),
Divider(),
toggles(),
widget.pluginItem,
]);
}
adjustWindow() {
return futureBuilder(
future: _isWindowCanBeAdjusted(),
future: isWindowCanBeAdjusted(),
hasData: (data) {
final visible = data as bool;
if (!visible) return Offstage();
@ -736,18 +689,18 @@ class _DisplayMenuState extends State<_DisplayMenu> {
children: [
MenuButton(
child: Text(translate('Adjust Window')),
onPressed: _doAdjustWindow,
ffi: widget.ffi),
onPressed: doAdjustWindow,
ffi: ffi),
Divider(),
],
);
});
}
_doAdjustWindow() async {
await _updateScreen();
doAdjustWindow() async {
await updateScreen();
if (_screen != null) {
widget.setFullscreen(false);
cbExitFullscreen();
double scale = _screen!.scaleFactor;
final wndRect = await WindowController.fromWindowId(windowId).getFrame();
final mediaSize = MediaQueryData.fromWindow(ui.window).size;
@ -758,7 +711,7 @@ class _DisplayMenuState extends State<_DisplayMenu> {
double magicHeight =
wndRect.bottom - wndRect.top - mediaSize.height * scale;
final canvasModel = widget.ffi.canvasModel;
final canvasModel = ffi.canvasModel;
final width = (canvasModel.getDisplayWidth() * canvasModel.scale +
CanvasModel.leftToEdge +
CanvasModel.rightToEdge) *
@ -793,7 +746,7 @@ class _DisplayMenuState extends State<_DisplayMenu> {
}
}
_updateScreen() async {
updateScreen() async {
final v = await rustDeskWinManager.call(
WindowType.Main, kWindowGetWindowInfo, '');
final String valueStr = v;
@ -813,8 +766,8 @@ class _DisplayMenuState extends State<_DisplayMenu> {
}
}
Future<bool> _isWindowCanBeAdjusted() async {
final viewStyle = await bind.sessionGetViewStyle(id: widget.id) ?? '';
Future<bool> isWindowCanBeAdjusted() async {
final viewStyle = await bind.sessionGetViewStyle(id: id) ?? '';
if (viewStyle != kRemoteViewStyleOriginal) {
return false;
}
@ -833,7 +786,7 @@ class _DisplayMenuState extends State<_DisplayMenu> {
selfHeight = _screen!.frame.height;
}
final canvasModel = widget.ffi.canvasModel;
final canvasModel = ffi.canvasModel;
final displayWidth = canvasModel.getDisplayWidth();
final displayHeight = canvasModel.getDisplayHeight();
final requiredWidth =
@ -843,6 +796,77 @@ class _DisplayMenuState extends State<_DisplayMenu> {
return selfWidth > (requiredWidth * scale) &&
selfHeight > (requiredHeight * scale);
}
}
class _DisplayMenu extends StatefulWidget {
final String id;
final FFI ffi;
final MenubarState state;
final Function(bool) setFullscreen;
final Widget pluginItem;
_DisplayMenu(
{Key? key,
required this.id,
required this.ffi,
required this.state,
required this.setFullscreen})
: pluginItem = LocationItem.createLocationItem(
id,
ffi,
kLocationClientRemoteToolbarDisplay,
true,
),
super(key: key);
@override
State<_DisplayMenu> createState() => _DisplayMenuState();
}
class _DisplayMenuState extends State<_DisplayMenu> {
late final ScreenAdjustor _screenAdjustor = ScreenAdjustor(
id: widget.id,
ffi: widget.ffi,
cbExitFullscreen: () => widget.setFullscreen(false),
);
bool get isFullscreen => stateGlobal.fullscreen;
int get windowId => stateGlobal.windowId;
Map<String, bool> get perms => widget.ffi.ffiModel.permissions;
PeerInfo get pi => widget.ffi.ffiModel.pi;
FfiModel get ffiModel => widget.ffi.ffiModel;
FFI get ffi => widget.ffi;
String get id => widget.id;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
_screenAdjustor.updateScreen();
return _IconSubmenuButton(
tooltip: 'Display Settings',
svg: "assets/display.svg",
ffi: widget.ffi,
color: _MenubarTheme.blueColor,
hoverColor: _MenubarTheme.hoverBlueColor,
menuChildren: [
_screenAdjustor.adjustWindow(),
viewStyle(),
scrollStyle(),
imageQuality(),
codec(),
_ResolutionsMenu(
id: widget.id,
ffi: widget.ffi,
screenAdjustor: _screenAdjustor,
),
Divider(),
toggles(),
widget.pluginItem,
]);
}
viewStyle() {
return futureBuilder(
@ -941,70 +965,6 @@ class _DisplayMenuState extends State<_DisplayMenu> {
});
}
resolutions() {
final resolutions = pi.resolutions;
final visible = ffiModel.keyboard && resolutions.length > 1;
if (!visible) return Offstage();
final display = ffiModel.display;
final groupValue = "${display.width}x${display.height}";
onChanged(String? value) async {
if (value == null) return;
final list = value.split('x');
if (list.length == 2) {
final w = int.tryParse(list[0]);
final h = int.tryParse(list[1]);
if (w != null && h != null) {
await bind.sessionChangeResolution(
id: widget.id, width: w, height: h);
Future.delayed(Duration(seconds: 3), () async {
final display = ffiModel.display;
if (w == display.width && h == display.height) {
if (await _isWindowCanBeAdjusted()) {
_doAdjustWindow();
}
}
});
}
}
}
return _SubmenuButton(
ffi: widget.ffi,
menuChildren: [
RdoMenuButton(
value: _kResolutionOrigin,
groupValue: groupValue,
onChanged: onChanged,
ffi: widget.ffi,
child: Text('Origin'),
),
RdoMenuButton(
value: _kResolutionFitLocal,
groupValue: groupValue,
onChanged: onChanged,
ffi: widget.ffi,
child: Text('Fit local'),
),
// RdoMenuButton(
// value: _kResolutionCustom,
// groupValue: groupValue,
// onChanged: onChanged,
// ffi: widget.ffi,
// child: Text('Custom resolution'),
// ),
] +
resolutions
.map((e) => RdoMenuButton(
value: '${e.width}x${e.height}',
groupValue: groupValue,
onChanged: onChanged,
ffi: widget.ffi,
child: Text('${e.width}x${e.height}')))
.toList(),
child: Text(translate("Resolution")));
}
toggles() {
return futureBuilder(
future: toolbarDisplayToggle(context, id, ffi),
@ -1023,6 +983,242 @@ class _DisplayMenuState extends State<_DisplayMenu> {
}
}
class _ResolutionsMenu extends StatefulWidget {
final String id;
final FFI ffi;
final ScreenAdjustor screenAdjustor;
_ResolutionsMenu({
Key? key,
required this.id,
required this.ffi,
required this.screenAdjustor,
}) : super(key: key);
@override
State<_ResolutionsMenu> createState() => _ResolutionsMenuState();
}
class _ResolutionsMenuState extends State<_ResolutionsMenu> {
String _groupValue = '';
Resolution? _localResolution;
late final _customWidth =
TextEditingController(text: display.width.toString());
late final _customHeight =
TextEditingController(text: display.height.toString());
PeerInfo get pi => widget.ffi.ffiModel.pi;
FfiModel get ffiModel => widget.ffi.ffiModel;
Display get display => ffiModel.display;
List<Resolution> get resolutions => pi.resolutions;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
final visible = ffiModel.keyboard && resolutions.length > 1;
if (!visible) return Offstage();
_groupValue = "${display.width}x${display.height}";
_getLocalResolution();
return _SubmenuButton(
ffi: widget.ffi,
menuChildren: <Widget>[
_OriginalResolutionMenuButton(),
_FitLocalResolutionMenuButton(),
_customResolutionMenuButton(),
] +
_supportedResolutionMenuButtons(),
child: Text(translate("Resolution")));
}
_getLocalResolution() {
_localResolution = null;
final String currentDisplay = bind.mainGetCurrentDisplay();
if (currentDisplay.isNotEmpty) {
try {
final display = json.decode(currentDisplay);
if (display['w'] != null && display['h'] != null) {
_localResolution = Resolution(display['w'], display['h']);
}
} catch (e) {
debugPrint('Failed to decode $currentDisplay, $e');
}
}
}
_onChanged(String? value) async {
if (value == null) return;
int? w;
int? h;
if (value == _kResolutionOrigin) {
w = display.originalWidth;
h = display.originalHeight;
} else if (value == _kResolutionFitLocal) {
final resolution = _getBestFitResolution();
if (resolution != null) {
w = resolution.width;
h = resolution.height;
}
} else if (value == _kResolutionCustom) {
debugPrint(
'REMOVE ME ======================= ${_customWidth.value} ${_customHeight.value}');
w = int.tryParse(_customWidth.value as String);
h = int.tryParse(_customHeight.value as String);
} else {
final list = value.split('x');
if (list.length == 2) {
w = int.tryParse(list[0]);
h = int.tryParse(list[1]);
}
}
if (w != null && h != null) {
await bind.sessionChangeResolution(
id: widget.id,
width: w,
height: h,
);
Future.delayed(Duration(seconds: 3), () async {
final display = ffiModel.display;
if (w == display.width && h == display.height) {
if (await widget.screenAdjustor.isWindowCanBeAdjusted()) {
widget.screenAdjustor.doAdjustWindow();
}
}
});
}
}
Widget _OriginalResolutionMenuButton() {
return Offstage(
offstage: display.isOriginalResolution,
child: RdoMenuButton(
value: _kResolutionOrigin,
groupValue: _groupValue,
onChanged: _onChanged,
ffi: widget.ffi,
child: Text(
'${translate('Original')} ${display.originalWidth}x${display.originalHeight}'),
),
);
}
Widget _FitLocalResolutionMenuButton() {
return Offstage(
offstage: _isRemoteResolutionFitLocal(),
child: RdoMenuButton(
value: _kResolutionFitLocal,
groupValue: _groupValue,
onChanged: _onChanged,
ffi: widget.ffi,
child: Text(
'${translate('Fit Local')} ${display.originalWidth}x${display.originalHeight}'),
),
);
}
List<Widget> _supportedResolutionMenuButtons() => resolutions
.map((e) => RdoMenuButton(
value: '${e.width}x${e.height}',
groupValue: _groupValue,
onChanged: _onChanged,
ffi: widget.ffi,
child: Text('${e.width}x${e.height}')))
.toList();
Widget _customResolutionMenuButton() {
return Offstage(
offstage: _isRemoteResolutionFitLocal(),
child: RdoMenuButton(
value: _kResolutionCustom,
groupValue: _groupValue,
onChanged: _onChanged,
ffi: widget.ffi,
child: _customResolutionWidget(),
),
);
}
Widget _customResolutionWidget() {
return Column(
children: [
Text(translate('Custom')),
SizedBox(
width: 5,
),
_resolutionInput(_customWidth),
SizedBox(
width: 3,
),
Text('x'),
SizedBox(
width: 3,
),
_resolutionInput(_customHeight),
],
);
}
TextField _resolutionInput(TextEditingController controller) {
return TextField(
decoration: InputDecoration(
border: InputBorder.none,
isDense: true,
),
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(4),
FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
],
controller: controller,
);
}
Resolution? _getBestFitResolution() {
if (_localResolution == null) {
return null;
}
squareDistance(Resolution lhs, Resolution rhs) =>
(lhs.width - rhs.width) * (lhs.width - rhs.width) +
(lhs.height - rhs.height) * (lhs.height - rhs.height);
Resolution? res;
for (final r in resolutions) {
if (r.width <= _localResolution!.width &&
r.height <= _localResolution!.height) {
if (res == null) {
res = r;
} else {
if (squareDistance(r, _localResolution!) <
squareDistance(res, _localResolution!)) {
res = r;
}
}
}
}
return res;
}
bool _isRemoteResolutionFitLocal() {
if (_localResolution == null) {
return true;
}
final bestFitResolution = _getBestFitResolution();
if (bestFitResolution == null) {
return true;
}
return bestFitResolution.width == display.width &&
bestFitResolution.height == display.height;
}
}
class _KeyboardMenu extends StatelessWidget {
final String id;
final FFI ffi;

View File

@ -295,11 +295,15 @@ class FfiModel with ChangeNotifier {
handleSwitchDisplay(Map<String, dynamic> evt, String peerId) {
_pi.currentDisplay = int.parse(evt['display']);
var newDisplay = Display();
newDisplay.x = double.parse(evt['x']);
newDisplay.y = double.parse(evt['y']);
newDisplay.width = int.parse(evt['width']);
newDisplay.height = int.parse(evt['height']);
newDisplay.cursorEmbedded = int.parse(evt['cursor_embedded']) == 1;
newDisplay.x = double.tryParse(evt['x']) ?? newDisplay.x;
newDisplay.y = double.tryParse(evt['y']) ?? newDisplay.y;
newDisplay.width = int.tryParse(evt['width']) ?? newDisplay.width;
newDisplay.height = int.tryParse(evt['height']) ?? newDisplay.height;
newDisplay.cursorEmbedded = int.tryParse(evt['cursor_embedded']) == 1;
newDisplay.originalWidth =
int.tryParse(evt['original_width']) ?? newDisplay.originalWidth;
newDisplay.originalHeight =
int.tryParse(evt['original_height']) ?? newDisplay.originalHeight;
_updateCurDisplay(peerId, newDisplay);
@ -466,14 +470,7 @@ class FfiModel with ChangeNotifier {
_pi.displays = [];
List<dynamic> displays = json.decode(evt['displays']);
for (int i = 0; i < displays.length; ++i) {
Map<String, dynamic> d0 = displays[i];
var d = Display();
d.x = d0['x'].toDouble();
d.y = d0['y'].toDouble();
d.width = d0['width'];
d.height = d0['height'];
d.cursorEmbedded = d0['cursor_embedded'] == 1;
_pi.displays.add(d);
_pi.displays.add(evtToDisplay(displays[i]));
}
stateGlobal.displaysCount.value = _pi.displays.length;
if (_pi.currentDisplay < _pi.displays.length) {
@ -533,20 +530,25 @@ class FfiModel with ChangeNotifier {
}
}
Display evtToDisplay(Map<String, dynamic> evt) {
var d = Display();
d.x = evt['x']?.toDouble() ?? d.x;
d.y = evt['y']?.toDouble() ?? d.y;
d.width = evt['width'] ?? d.width;
d.height = evt['height'] ?? d.height;
d.cursorEmbedded = evt['cursor_embedded'] == 1;
d.originalWidth = evt['original_width'] ?? d.originalWidth;
d.originalHeight = evt['original_height'] ?? d.originalHeight;
return d;
}
/// Handle the peer info synchronization event based on [evt].
handleSyncPeerInfo(Map<String, dynamic> evt, String peerId) async {
if (evt['displays'] != null) {
List<dynamic> displays = json.decode(evt['displays']);
List<Display> newDisplays = [];
for (int i = 0; i < displays.length; ++i) {
Map<String, dynamic> d0 = displays[i];
var d = Display();
d.x = d0['x'].toDouble();
d.y = d0['y'].toDouble();
d.width = d0['width'];
d.height = d0['height'];
d.cursorEmbedded = d0['cursor_embedded'] == 1;
newDisplays.add(d);
newDisplays.add(evtToDisplay(displays[i]));
}
_pi.displays = newDisplays;
stateGlobal.displaysCount.value = _pi.displays.length;
@ -1718,6 +1720,8 @@ class Display {
int width = 0;
int height = 0;
bool cursorEmbedded = false;
int originalWidth = 0;
int originalHeight = 0;
Display() {
width = (isDesktop || isWebDesktop)
@ -1740,6 +1744,9 @@ class Display {
other.width == width &&
other.height == height &&
other.cursorEmbedded == cursorEmbedded;
bool get isOriginalResolution =>
width == originalWidth && height == originalHeight;
}
class Resolution {

View File

@ -1211,6 +1211,14 @@ impl<T: InvokeUiSession> Remote<T> {
s.cursor_embedded,
);
}
let custom_resolution = if s.width != s.original_resolution.width
|| s.height != s.original_resolution.height
{
Some((s.width, s.height))
} else {
None
};
self.handler.set_custom_resolution(custom_resolution);
}
Some(misc::Union::CloseReason(c)) => {
self.handler.msgbox("error", "Connection Error", &c, "");

View File

@ -286,6 +286,8 @@ impl FlutterHandler {
h.insert("width", d.width);
h.insert("height", d.height);
h.insert("cursor_embedded", if d.cursor_embedded { 1 } else { 0 });
h.insert("original_width", d.original_resolution.width);
h.insert("original_height", d.original_resolution.height);
msg_vec.push(h);
}
serde_json::ser::to_string(&msg_vec).unwrap_or("".to_owned())
@ -618,6 +620,14 @@ impl InvokeUiSession for FlutterHandler {
.to_string(),
),
("resolutions", &resolutions),
(
"original_width",
&display.original_resolution.width.to_string(),
),
(
"original_height",
&display.original_resolution.height.to_string(),
),
],
);
}

View File

@ -880,6 +880,18 @@ pub fn main_handle_relay_id(id: String) -> String {
handle_relay_id(id)
}
pub fn main_get_current_display() -> SyncReturn<String> {
let display_info = match crate::video_service::get_current_display() {
Ok((_, _, display)) => serde_json::to_string(&HashMap::from([
("w", display.width()),
("h", display.height()),
]))
.unwrap_or_default(),
Err(..) => "".to_string(),
};
SyncReturn(display_info)
}
pub fn session_add_port_forward(
id: String,
local_port: i32,
@ -1426,10 +1438,10 @@ pub fn plugin_event(_id: String, _peer: String, _event: Vec<u8>) {
}
}
pub fn plugin_register_event_stream(id: String, event2ui: StreamSink<EventToUI>) {
pub fn plugin_register_event_stream(_id: String, _event2ui: StreamSink<EventToUI>) {
#[cfg(feature = "plugin_framework")]
{
crate::plugin::native_handlers::session::session_register_event_stream(id, event2ui);
crate::plugin::native_handlers::session::session_register_event_stream(_id, _event2ui);
}
}
@ -1577,16 +1589,16 @@ pub fn plugin_list_reload() {
}
}
pub fn plugin_install(id: String, b: bool) {
pub fn plugin_install(_id: String, _b: bool) {
#[cfg(feature = "plugin_framework")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
if b {
if _b {
if let Err(e) = crate::plugin::install_plugin(&id) {
log::error!("Failed to install plugin '{}': {}", id, e);
}
} else {
crate::plugin::uninstall_plugin(&id, true);
crate::plugin::uninstall_plugin(&_id, true);
}
}
}

View File

@ -829,11 +829,12 @@ impl<T: InvokeUiSession> Session<T> {
}
}
#[inline]
pub fn set_custom_resolution(&mut self, wh: Option<(i32, i32)>) {
self.lc.write().unwrap().set_custom_resolution(wh);
}
pub fn change_resolution(&self, width: i32, height: i32) {
self.lc
.write()
.unwrap()
.set_custom_resolution(Some((width, height)));
let mut misc = Misc::new();
misc.set_change_resolution(Resolution {
width,