mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-04-09 07:41:29 +03: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).
|
||||
|
||||
Reference in New Issue
Block a user