mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
@@ -1,6 +1,7 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
@@ -9,6 +10,7 @@ import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import 'package:flutter_hbb/models/server_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
|
||||
|
||||
@@ -199,6 +201,7 @@ class _GeneralState extends State<_General> {
|
||||
abr(),
|
||||
hwcodec(),
|
||||
audio(context),
|
||||
record(context),
|
||||
_Card(title: 'Language', children: [language()]),
|
||||
],
|
||||
).marginOnly(bottom: _kListViewBottomMargin));
|
||||
@@ -290,6 +293,59 @@ class _GeneralState extends State<_General> {
|
||||
});
|
||||
}
|
||||
|
||||
Widget record(BuildContext context) {
|
||||
return _futureBuilder(future: () async {
|
||||
String customDirectory =
|
||||
await bind.mainGetOption(key: 'video-save-directory');
|
||||
String defaultDirectory = await bind.mainDefaultVideoSaveDirectory();
|
||||
String dir;
|
||||
if (customDirectory.isNotEmpty) {
|
||||
dir = customDirectory;
|
||||
} else {
|
||||
dir = defaultDirectory;
|
||||
}
|
||||
final canlaunch = await canLaunchUrl(Uri.file(dir));
|
||||
return {'dir': dir, 'canlaunch': canlaunch};
|
||||
}(), hasData: (data) {
|
||||
Map<String, dynamic> map = data as Map<String, dynamic>;
|
||||
String dir = map['dir']!;
|
||||
bool canlaunch = map['canlaunch']! as bool;
|
||||
|
||||
return _Card(title: 'Recording', children: [
|
||||
_OptionCheckBox(context, 'Automatically record incoming sessions',
|
||||
'allow-auto-record-incoming'),
|
||||
Row(
|
||||
children: [
|
||||
Text('${translate('Directory')}:'),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: canlaunch ? () => launchUrl(Uri.file(dir)) : null,
|
||||
child: Text(
|
||||
dir,
|
||||
softWrap: true,
|
||||
style:
|
||||
const TextStyle(decoration: TextDecoration.underline),
|
||||
)).marginOnly(left: 10),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
String? selectedDirectory = await FilePicker.platform
|
||||
.getDirectoryPath(initialDirectory: dir);
|
||||
if (selectedDirectory != null) {
|
||||
await bind.mainSetOption(
|
||||
key: 'video-save-directory',
|
||||
value: selectedDirectory);
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
child: Text(translate('Change')))
|
||||
.marginOnly(left: 5),
|
||||
],
|
||||
).marginOnly(left: _kContentHMargin),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
Widget language() {
|
||||
return _futureBuilder(future: () async {
|
||||
String langs = await bind.mainGetLangs();
|
||||
|
||||
@@ -166,6 +166,7 @@ class _RemotePageState extends State<RemotePage>
|
||||
ChangeNotifierProvider.value(value: _ffi.imageModel),
|
||||
ChangeNotifierProvider.value(value: _ffi.cursorModel),
|
||||
ChangeNotifierProvider.value(value: _ffi.canvasModel),
|
||||
ChangeNotifierProvider.value(value: _ffi.recordingModel),
|
||||
], child: buildBody(context)));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/models/chat_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:rxdart/rxdart.dart' as rxdart;
|
||||
|
||||
import '../../common.dart';
|
||||
@@ -134,6 +135,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
if (!isWeb) {
|
||||
menubarItems.add(_buildChat(context));
|
||||
}
|
||||
menubarItems.add(_buildRecording(context));
|
||||
menubarItems.add(_buildClose(context));
|
||||
return PopupMenuTheme(
|
||||
data: const PopupMenuThemeData(
|
||||
@@ -351,6 +353,24 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRecording(BuildContext context) {
|
||||
return Consumer<RecordingModel>(
|
||||
builder: (context, value, child) => IconButton(
|
||||
tooltip: value.start
|
||||
? translate('Stop session recording')
|
||||
: translate('Start session recording'),
|
||||
onPressed: () async {
|
||||
await value.toggle();
|
||||
},
|
||||
icon: Icon(
|
||||
value.start
|
||||
? Icons.pause_circle_filled
|
||||
: Icons.videocam_outlined,
|
||||
color: _MenubarTheme.commonColor,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
Widget _buildClose(BuildContext context) {
|
||||
return IconButton(
|
||||
tooltip: translate('Close'),
|
||||
|
||||
@@ -197,6 +197,7 @@ class FfiModel with ChangeNotifier {
|
||||
_display.height = int.parse(evt['height']);
|
||||
if (old != _pi.currentDisplay) {
|
||||
parent.target?.cursorModel.updateDisplayOrigin(_display.x, _display.y);
|
||||
parent.target?.recordingModel.switchDisplay();
|
||||
}
|
||||
|
||||
// remote is mobile, and orientation changed
|
||||
@@ -972,6 +973,41 @@ class QualityMonitorModel with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
class RecordingModel with ChangeNotifier {
|
||||
WeakReference<FFI> parent;
|
||||
RecordingModel(this.parent);
|
||||
bool _start = false;
|
||||
get start => _start;
|
||||
|
||||
switchDisplay() {
|
||||
if (!isDesktop || !_start) return;
|
||||
var id = parent.target?.id;
|
||||
int? width = parent.target?.canvasModel.getDisplayWidth();
|
||||
int? height = parent.target?.canvasModel.getDisplayWidth();
|
||||
if (id == null || width == null || height == null) return;
|
||||
bind.sessionRecordScreen(
|
||||
id: id, start: _start, width: width, height: height);
|
||||
}
|
||||
|
||||
Future<void> toggle() async {
|
||||
if (!isDesktop) return;
|
||||
var id = parent.target?.id;
|
||||
int? width = parent.target?.canvasModel.getDisplayWidth();
|
||||
int? height = parent.target?.canvasModel.getDisplayWidth();
|
||||
if (id == null || width == null || height == null) return;
|
||||
|
||||
await bind.sessionRecordScreen(
|
||||
id: id, start: !_start, width: width, height: height);
|
||||
_start = !_start;
|
||||
notifyListeners();
|
||||
if (_start) {
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
bind.sessionRefresh(id: id);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Mouse button enum.
|
||||
enum MouseButtons { left, right, wheel }
|
||||
|
||||
@@ -1013,6 +1049,7 @@ class FFI {
|
||||
late final AbModel abModel; // global
|
||||
late final UserModel userModel; // global
|
||||
late final QualityMonitorModel qualityMonitorModel; // session
|
||||
late final RecordingModel recordingModel; // recording
|
||||
|
||||
FFI() {
|
||||
imageModel = ImageModel(WeakReference(this));
|
||||
@@ -1025,6 +1062,7 @@ class FFI {
|
||||
abModel = AbModel(WeakReference(this));
|
||||
userModel = UserModel(WeakReference(this));
|
||||
qualityMonitorModel = QualityMonitorModel(WeakReference(this));
|
||||
recordingModel = RecordingModel(WeakReference(this));
|
||||
}
|
||||
|
||||
/// Send a mouse tap event(down and up).
|
||||
|
||||
@@ -140,7 +140,7 @@ packages:
|
||||
name: characters
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.2.1"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -161,7 +161,7 @@ packages:
|
||||
name: clock
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.1.1"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -325,6 +325,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.1.4"
|
||||
file_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_picker
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.1.0"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -588,7 +595,7 @@ packages:
|
||||
name: material_color_utilities
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.4"
|
||||
version: "0.1.5"
|
||||
menu_base:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -602,7 +609,7 @@ packages:
|
||||
name: meta
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.7.0"
|
||||
version: "1.8.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -679,7 +686,7 @@ packages:
|
||||
name: path
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.1"
|
||||
version: "1.8.2"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -80,6 +80,7 @@ dependencies:
|
||||
desktop_drop: ^0.3.3
|
||||
scroll_pos: ^0.3.0
|
||||
rxdart: ^0.27.5
|
||||
file_picker: ^5.1.0
|
||||
flutter_improved_scrolling: ^0.0.3
|
||||
# currently, we use flutter 3.0.5 for windows build, latest for other builds.
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user