diff --git a/lib/model.dart b/lib/model.dart index 209163670..b6eca7642 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -16,6 +16,7 @@ class FfiModel with ChangeNotifier { var _decoding = false; bool _waitForImage; bool _initialized = false; + var _inputBlocked = false; final _permissions = Map(); bool _secure; bool _direct; @@ -26,6 +27,11 @@ class FfiModel with ChangeNotifier { get secure => _secure; get direct => _direct; get pi => _pi; + get inputBlocked => _inputBlocked; + + set inputBlocked(v) { + _inputBlocked = v; + } FfiModel() { Translator.call = translate; @@ -54,6 +60,7 @@ class FfiModel with ChangeNotifier { _waitForImage = false; _secure = null; _direct = null; + _inputBlocked = false; clearPermissions(); } @@ -79,6 +86,7 @@ class FfiModel with ChangeNotifier { } void clearPermissions() { + _inputBlocked = false; _permissions.clear(); } diff --git a/lib/remote_page.dart b/lib/remote_page.dart index dea7b4a56..34be3e745 100644 --- a/lib/remote_page.dart +++ b/lib/remote_page.dart @@ -266,48 +266,63 @@ class _RemotePageState extends State { mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row(children: [ - IconButton( - color: Colors.white, - icon: Icon(Icons.clear), - onPressed: () { - close(); - }, - ), - IconButton( - color: Colors.white, - icon: Icon(Icons.keyboard), - onPressed: openKeyboard), - IconButton( - color: Colors.white, - icon: Icon(Icons.tv), - onPressed: () { - setState(() => _showEdit = false); - showOptions(context); - }, - ), - Container( - color: _mouseTools ? Colors.blue[500] : null, - child: IconButton( - color: Colors.white, - icon: Icon(Icons.mouse), - onPressed: () { - setState(() { - _mouseTools = !_mouseTools; - resetTool(); - if (_mouseTools) _drag = true; - }); - }, - )), - IconButton( - color: Colors.white, - icon: Icon(Icons.more_vert), - onPressed: () { - setState(() => _showEdit = false); - showActions(context); - }, - ), - ]), + Row( + children: [ + IconButton( + color: Colors.white, + icon: Icon(Icons.clear), + onPressed: () { + close(); + }, + ) + ] + + (isDesktop + ? [] + : [ + IconButton( + color: Colors.white, + icon: Icon(Icons.keyboard), + onPressed: openKeyboard) + ]) + + [ + IconButton( + color: Colors.white, + icon: Icon(Icons.tv), + onPressed: () { + setState(() => _showEdit = false); + showOptions(context); + }, + ) + ] + + (isDesktop + ? [] + : [ + Container( + color: _mouseTools + ? Colors.blue[500] + : null, + child: IconButton( + color: Colors.white, + icon: Icon(Icons.mouse), + onPressed: () { + setState(() { + _mouseTools = !_mouseTools; + resetTool(); + if (_mouseTools) _drag = true; + }); + }, + )) + ]) + + [ + IconButton( + color: Colors.white, + icon: Icon(Icons.more_vert), + onPressed: () { + setState(() => _showEdit = false); + showActions(context); + }, + ), + ]), IconButton( color: Colors.white, icon: Icon(Icons.expand_more), @@ -418,15 +433,12 @@ class _RemotePageState extends State { final x = 120.0; final y = size.height; final more = >[]; - if (FFI.ffiModel.pi.version.isNotEmpty) { + final pi = FFI.ffiModel.pi; + final perms = FFI.ffiModel.permissions; + if (pi.version.isNotEmpty) { more.add(PopupMenuItem( child: Text(translate('Refresh')), value: 'refresh')); } - if (FFI.ffiModel.permissions['keyboard'] != false && - FFI.ffiModel.permissions['clipboard'] != false) { - more.add(PopupMenuItem( - child: Text(translate('Paste')), value: 'paste')); - } more.add(PopupMenuItem( child: Row( children: ([ @@ -441,38 +453,57 @@ class _RemotePageState extends State { ) ])), value: 'enter_os_password')); - more.add(PopupMenuItem( - child: Row( - children: ([ - Container(width: 100.0, child: Text(translate('Touch mode'))), - Padding(padding: EdgeInsets.symmetric(horizontal: 16.0)), - Icon( - _touchMode - ? Icons.check_box_outlined - : Icons.check_box_outline_blank, - color: MyTheme.accent) - ])), - value: 'touch_mode')); - more.add(PopupMenuItem( - child: Text(translate('Reset canvas')), value: 'reset_canvas')); + if (!isDesktop) { + if (perms['keyboard'] != false && perms['clipboard'] != false) { + more.add(PopupMenuItem( + child: Text(translate('Paste')), value: 'paste')); + } + more.add(PopupMenuItem( + child: Row( + children: ([ + Container(width: 100.0, child: Text(translate('Touch mode'))), + Padding(padding: EdgeInsets.symmetric(horizontal: 16.0)), + Icon( + _touchMode + ? Icons.check_box_outlined + : Icons.check_box_outline_blank, + color: MyTheme.accent) + ])), + value: 'touch_mode')); + more.add(PopupMenuItem( + child: Text(translate('Reset canvas')), value: 'reset_canvas')); + } + if (perms['keyboard'] != false) { + if (pi.platform == 'Linux' || pi.sasEnabled) { + more.add(PopupMenuItem( + child: Text(translate('Insert') + ' Ctrl + Alt + Del'), + value: 'cad')); + } + more.add(PopupMenuItem( + child: Text(translate('Insert Lock')), value: 'lock')); + if (pi.platform == 'Windows' && + FFI.getByName('toggle_option', 'privacy-mode') != 'true') { + more.add(PopupMenuItem( + child: Text(translate( + (FFI.ffiModel.inputBlocked ? 'Unb' : 'B') + 'lock user input')), + value: 'block-input')); + } + } () async { var value = await showMenu( context: context, position: RelativeRect.fromLTRB(x, y, x, y), - items: [ - PopupMenuItem( - child: Text(translate('Insert') + ' Ctrl + Alt + Del'), - value: 'cad'), - PopupMenuItem( - child: Text(translate('Insert Lock')), value: 'lock'), - ] + - more, + items: more, elevation: 8, ); if (value == 'cad') { FFI.setByName('ctrl_alt_del'); } else if (value == 'lock') { FFI.setByName('lock_screen'); + } else if (value == 'block-input') { + FFI.setByName('toggle_option', + (FFI.ffiModel.inputBlocked ? 'un' : '') + 'block-inpu'); + FFI.ffiModel.inputBlocked = !FFI.ffiModel.inputBlocked; } else if (value == 'refresh') { FFI.setByName('refresh'); } else if (value == 'paste') { @@ -796,9 +827,36 @@ void wrongPasswordDialog(String id, BuildContext context) { ])); } +CheckboxListTile getToggle( + void Function(void Function()) setState, option, name) { + return CheckboxListTile( + value: FFI.getByName('toggle_option', option) == 'true', + onChanged: (v) { + setState(() { + FFI.setByName('toggle_option', option); + }); + }, + dense: true, + title: Text(translate(name))); +} + +RadioListTile getRadio(String name, String toValue, String curValue, + void Function(String) onChange) { + return RadioListTile( + controlAffinity: ListTileControlAffinity.trailing, + title: Text(translate(name)), + value: toValue, + groupValue: curValue, + onChanged: onChange, + dense: true, + ); +} + void showOptions(BuildContext context) { String quality = FFI.getByName('image_quality'); if (quality == '') quality = 'balanced'; + String viewStyle = FFI.getByName('peer_option', 'view-style'); + if (viewStyle == '') viewStyle = 'original'; var displays = []; final pi = FFI.ffiModel.pi; final image = FFI.ffiModel.getConnectionImage(); @@ -835,82 +893,56 @@ void showOptions(BuildContext context) { if (displays.isNotEmpty) { displays.add(Divider(color: MyTheme.border)); } + final perms = FFI.ffiModel.permissions; showAlertDialog(context, (setState) { final more = []; - if (FFI.ffiModel.permissions['audio'] != false) { - more.add(CheckboxListTile( - value: FFI.getByName('toggle_option', 'disable-audio') == 'true', - onChanged: (v) { - setState(() { - FFI.setByName('toggle_option', 'disable-audio'); - }); - }, - title: Text(translate('Mute')))); + if (perms['audio'] != false) { + more.add(getToggle(setState, 'disable-audio', 'Mute')); } - if (FFI.ffiModel.permissions['keyboard'] != false) { - more.add(CheckboxListTile( - value: FFI.getByName('toggle_option', 'lock-after-session-end') == - 'true', - onChanged: (v) { - setState(() { - FFI.setByName('toggle_option', 'lock-after-session-end'); - }); - }, - title: Text(translate('Lock after session end')))); + if (perms['keyboard'] != false) { + if (perms['clipboard'] != false) + more.add(getToggle(setState, 'disable-clipboard', 'Disable clipboard')); + more.add(getToggle( + setState, 'lock-after-session-end', 'Lock after session end')); + if (pi.platform == 'Windows') { + more.add(getToggle(setState, 'privacy-mode', 'Privacy mode')); + } } + var setQuality = (String value) { + setState(() { + quality = value; + FFI.setByName('image_quality', value); + }); + }; + var setViewStyle = (String value) { + setState(() { + viewStyle = value; + FFI.setByName( + 'peer_option', '{"name": "view-style", "value": "$value"}'); + }); + }; return Tuple3( null, Column( mainAxisSize: MainAxisSize.min, children: displays + + (isDesktop + ? [ + getRadio( + 'Original', 'original', viewStyle, setViewStyle), + getRadio('Shrink', 'shrink', viewStyle, setViewStyle), + getRadio('Stretch', 'stretch', viewStyle, setViewStyle), + Divider(color: MyTheme.border), + ] + : {}) + [ - RadioListTile( - controlAffinity: ListTileControlAffinity.trailing, - title: Text(translate('Good image quality')), - value: 'best', - groupValue: quality, - onChanged: (String value) { - setState(() { - quality = value; - FFI.setByName('image_quality', value); - }); - }, - ), - RadioListTile( - controlAffinity: ListTileControlAffinity.trailing, - title: Text(translate('Balanced')), - value: 'balanced', - groupValue: quality, - onChanged: (String value) { - setState(() { - quality = value; - FFI.setByName('image_quality', value); - }); - }, - ), - RadioListTile( - controlAffinity: ListTileControlAffinity.trailing, - title: Text(translate('Optimize reaction time')), - value: 'low', - groupValue: quality, - onChanged: (String value) { - setState(() { - quality = value; - FFI.setByName('image_quality', value); - }); - }, - ), + getRadio('Good image quality', 'best', quality, setQuality), + getRadio('Balanced', 'balanced', quality, setQuality), + getRadio( + 'Optimize reaction time', 'low', quality, setQuality), Divider(color: MyTheme.border), - CheckboxListTile( - value: FFI.getByName( - 'toggle_option', 'show-remote-cursor') == - 'true', - onChanged: (v) { - setState(() { - FFI.setByName('toggle_option', 'show-remote-cursor'); - }); - }, - title: Text(translate('Show remote cursor'))), + getToggle( + setState, 'show-remote-cursor', 'Show remote cursor'), ] + more), null);