mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-04-07 03:21:28 +03:00
view camera (#11040)
* view camera Signed-off-by: 21pages <sunboeasy@gmail.com> * `No cameras` prompt if no cameras available, `peerGetSessionsCount` use connType as parameter Signed-off-by: 21pages <sunboeasy@gmail.com> * fix, use video_service_name rather than display_idx as key in qos,etc Signed-off-by: 21pages <sunboeasy@gmail.com> --------- Signed-off-by: 21pages <sunboeasy@gmail.com> Co-authored-by: Adwin White <adwinw01@gmail.com> Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
This commit is contained in:
@@ -488,6 +488,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
BuildContext context,
|
||||
String title, {
|
||||
bool isFileTransfer = false,
|
||||
bool isViewCamera = false,
|
||||
bool isTcpTunneling = false,
|
||||
bool isRDP = false,
|
||||
}) {
|
||||
@@ -502,6 +503,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
peer,
|
||||
tab,
|
||||
isFileTransfer: isFileTransfer,
|
||||
isViewCamera: isViewCamera,
|
||||
isTcpTunneling: isTcpTunneling,
|
||||
isRDP: isRDP,
|
||||
);
|
||||
@@ -530,6 +532,15 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
MenuEntryBase<String> _viewCameraAction(BuildContext context) {
|
||||
return _connectCommonAction(
|
||||
context,
|
||||
translate('View camera'),
|
||||
isViewCamera: true,
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
MenuEntryBase<String> _tcpTunnelingAction(BuildContext context) {
|
||||
return _connectCommonAction(
|
||||
@@ -880,6 +891,7 @@ class RecentPeerCard extends BasePeerCard {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
];
|
||||
|
||||
final List favs = (await bind.mainGetFav()).toList();
|
||||
@@ -939,6 +951,7 @@ class FavoritePeerCard extends BasePeerCard {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
];
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
@@ -992,6 +1005,7 @@ class DiscoveredPeerCard extends BasePeerCard {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
];
|
||||
|
||||
final List favs = (await bind.mainGetFav()).toList();
|
||||
@@ -1045,6 +1059,7 @@ class AddressBookPeerCard extends BasePeerCard {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
];
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
@@ -1177,6 +1192,7 @@ class MyGroupPeerCard extends BasePeerCard {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
];
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
@@ -1398,6 +1414,7 @@ class TagPainter extends CustomPainter {
|
||||
|
||||
void connectInPeerTab(BuildContext context, Peer peer, PeerTabIndex tab,
|
||||
{bool isFileTransfer = false,
|
||||
bool isViewCamera = false,
|
||||
bool isTcpTunneling = false,
|
||||
bool isRDP = false}) async {
|
||||
var password = '';
|
||||
@@ -1423,6 +1440,7 @@ void connectInPeerTab(BuildContext context, Peer peer, PeerTabIndex tab,
|
||||
password: password,
|
||||
isSharedPassword: isSharedPassword,
|
||||
isFileTransfer: isFileTransfer,
|
||||
isViewCamera: isViewCamera,
|
||||
isTcpTunneling: isTcpTunneling,
|
||||
isRDP: isRDP);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -53,13 +54,14 @@ class RawKeyFocusScope extends StatelessWidget {
|
||||
class RawTouchGestureDetectorRegion extends StatefulWidget {
|
||||
final Widget child;
|
||||
final FFI ffi;
|
||||
|
||||
final bool isCamera;
|
||||
late final InputModel inputModel = ffi.inputModel;
|
||||
late final FfiModel ffiModel = ffi.ffiModel;
|
||||
|
||||
RawTouchGestureDetectorRegion({
|
||||
required this.child,
|
||||
required this.ffi,
|
||||
this.isCamera = false,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -382,6 +384,7 @@ class _RawTouchGestureDetectorRegionState
|
||||
_scale = d.scale;
|
||||
|
||||
if (scale != 0) {
|
||||
if (widget.isCamera) return;
|
||||
await bind.sessionSendPointer(
|
||||
sessionId: sessionId,
|
||||
msg: json.encode(
|
||||
@@ -402,6 +405,7 @@ class _RawTouchGestureDetectorRegionState
|
||||
return;
|
||||
}
|
||||
if ((isDesktop || isWebDesktop)) {
|
||||
if (widget.isCamera) return;
|
||||
await bind.sessionSendPointer(
|
||||
sessionId: sessionId,
|
||||
msg: json.encode(
|
||||
@@ -536,3 +540,46 @@ class RawPointerMouseRegion extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CameraRawPointerMouseRegion extends StatelessWidget {
|
||||
final InputModel inputModel;
|
||||
final Widget child;
|
||||
final PointerEnterEventListener? onEnter;
|
||||
final PointerExitEventListener? onExit;
|
||||
final PointerDownEventListener? onPointerDown;
|
||||
final PointerUpEventListener? onPointerUp;
|
||||
|
||||
CameraRawPointerMouseRegion({
|
||||
this.onEnter,
|
||||
this.onExit,
|
||||
this.onPointerDown,
|
||||
this.onPointerUp,
|
||||
required this.inputModel,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Listener(
|
||||
onPointerHover: (evt) {
|
||||
final offset = evt.position;
|
||||
double x = offset.dx;
|
||||
double y = max(0.0, offset.dy);
|
||||
inputModel.handlePointerDevicePos(
|
||||
kPointerEventKindMouse, x, y, true, kMouseEventTypeDefault);
|
||||
},
|
||||
onPointerDown: (evt) {
|
||||
onPointerDown?.call(evt);
|
||||
},
|
||||
onPointerUp: (evt) {
|
||||
onPointerUp?.call(evt);
|
||||
},
|
||||
child: MouseRegion(
|
||||
cursor: MouseCursor.defer,
|
||||
onEnter: onEnter,
|
||||
onExit: onExit,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,10 +89,13 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
||||
final pi = ffiModel.pi;
|
||||
final perms = ffiModel.permissions;
|
||||
final sessionId = ffi.sessionId;
|
||||
final isDefaultConn = ffi.connType == ConnType.defaultConn;
|
||||
|
||||
List<TTextMenu> v = [];
|
||||
// elevation
|
||||
if (perms['keyboard'] != false && ffi.elevationModel.showRequestMenu) {
|
||||
if (isDefaultConn &&
|
||||
perms['keyboard'] != false &&
|
||||
ffi.elevationModel.showRequestMenu) {
|
||||
v.add(
|
||||
TTextMenu(
|
||||
child: Text(translate('Request Elevation')),
|
||||
@@ -101,7 +104,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
||||
);
|
||||
}
|
||||
// osAccount / osPassword
|
||||
if (perms['keyboard'] != false) {
|
||||
if (isDefaultConn && perms['keyboard'] != false) {
|
||||
v.add(
|
||||
TTextMenu(
|
||||
child: Row(children: [
|
||||
@@ -130,7 +133,9 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
||||
);
|
||||
}
|
||||
// paste
|
||||
if (pi.platform != kPeerPlatformAndroid && perms['keyboard'] != false) {
|
||||
if (isDefaultConn &&
|
||||
pi.platform != kPeerPlatformAndroid &&
|
||||
perms['keyboard'] != false) {
|
||||
v.add(TTextMenu(
|
||||
child: Text(translate('Send clipboard keystrokes')),
|
||||
onPressed: () async {
|
||||
@@ -142,43 +147,53 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
||||
}));
|
||||
}
|
||||
// reset canvas
|
||||
if (isMobile) {
|
||||
if (isDefaultConn && isMobile) {
|
||||
v.add(TTextMenu(
|
||||
child: Text(translate('Reset canvas')),
|
||||
onPressed: () => ffi.cursorModel.reset()));
|
||||
}
|
||||
|
||||
connectWithToken(
|
||||
{required bool isFileTransfer, required bool isTcpTunneling}) {
|
||||
{bool isFileTransfer = false,
|
||||
bool isViewCamera = false,
|
||||
bool isTcpTunneling = false}) {
|
||||
final connToken = bind.sessionGetConnToken(sessionId: ffi.sessionId);
|
||||
connect(context, id,
|
||||
isFileTransfer: isFileTransfer,
|
||||
isViewCamera: isViewCamera,
|
||||
isTcpTunneling: isTcpTunneling,
|
||||
connToken: connToken);
|
||||
}
|
||||
|
||||
// transferFile
|
||||
if (isDesktop) {
|
||||
if (isDefaultConn && isDesktop) {
|
||||
v.add(
|
||||
TTextMenu(
|
||||
child: Text(translate('Transfer file')),
|
||||
onPressed: () =>
|
||||
connectWithToken(isFileTransfer: true, isTcpTunneling: false)),
|
||||
onPressed: () => connectWithToken(isFileTransfer: true)),
|
||||
);
|
||||
}
|
||||
// viewCamera
|
||||
if (isDefaultConn && isDesktop) {
|
||||
v.add(
|
||||
TTextMenu(
|
||||
child: Text(translate('View camera')),
|
||||
onPressed: () => connectWithToken(isViewCamera: true)),
|
||||
);
|
||||
}
|
||||
// tcpTunneling
|
||||
if (isDesktop) {
|
||||
if (isDefaultConn && isDesktop) {
|
||||
v.add(
|
||||
TTextMenu(
|
||||
child: Text(translate('TCP tunneling')),
|
||||
onPressed: () =>
|
||||
connectWithToken(isFileTransfer: false, isTcpTunneling: true)),
|
||||
onPressed: () => connectWithToken(isTcpTunneling: true)),
|
||||
);
|
||||
}
|
||||
// note
|
||||
if (bind
|
||||
.sessionGetAuditServerSync(sessionId: sessionId, typ: "conn")
|
||||
.isNotEmpty) {
|
||||
if (isDefaultConn &&
|
||||
bind
|
||||
.sessionGetAuditServerSync(sessionId: sessionId, typ: "conn")
|
||||
.isNotEmpty) {
|
||||
v.add(
|
||||
TTextMenu(
|
||||
child: Text(translate('Note')),
|
||||
@@ -186,11 +201,12 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
||||
);
|
||||
}
|
||||
// divider
|
||||
if (isDesktop || isWebDesktop) {
|
||||
if (isDefaultConn && (isDesktop || isWebDesktop)) {
|
||||
v.add(TTextMenu(child: Offstage(), onPressed: () {}, divider: true));
|
||||
}
|
||||
// ctrlAltDel
|
||||
if (!ffiModel.viewOnly &&
|
||||
if (isDefaultConn &&
|
||||
!ffiModel.viewOnly &&
|
||||
ffiModel.keyboard &&
|
||||
(pi.platform == kPeerPlatformLinux || pi.sasEnabled)) {
|
||||
v.add(
|
||||
@@ -200,7 +216,8 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
||||
);
|
||||
}
|
||||
// restart
|
||||
if (perms['restart'] != false &&
|
||||
if (isDefaultConn &&
|
||||
perms['restart'] != false &&
|
||||
(pi.platform == kPeerPlatformLinux ||
|
||||
pi.platform == kPeerPlatformWindows ||
|
||||
pi.platform == kPeerPlatformMacOS)) {
|
||||
@@ -212,7 +229,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
||||
);
|
||||
}
|
||||
// insertLock
|
||||
if (!ffiModel.viewOnly && ffi.ffiModel.keyboard) {
|
||||
if (isDefaultConn && !ffiModel.viewOnly && ffi.ffiModel.keyboard) {
|
||||
v.add(
|
||||
TTextMenu(
|
||||
child: Text(translate('Insert Lock')),
|
||||
@@ -220,7 +237,8 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
||||
);
|
||||
}
|
||||
// blockUserInput
|
||||
if (ffi.ffiModel.keyboard &&
|
||||
if (isDefaultConn &&
|
||||
ffi.ffiModel.keyboard &&
|
||||
ffi.ffiModel.permissions['block_input'] != false &&
|
||||
pi.platform == kPeerPlatformWindows) // privacy-mode != true ??
|
||||
{
|
||||
@@ -236,12 +254,13 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
||||
}));
|
||||
}
|
||||
// switchSides
|
||||
if (isDesktop &&
|
||||
if (isDefaultConn &&
|
||||
isDesktop &&
|
||||
ffiModel.keyboard &&
|
||||
pi.platform != kPeerPlatformAndroid &&
|
||||
pi.platform != kPeerPlatformMacOS &&
|
||||
versionCmp(pi.version, '1.2.0') >= 0 &&
|
||||
bind.peerGetDefaultSessionsCount(id: id) == 1) {
|
||||
bind.peerGetSessionsCount(id: id, connType: ffi.connType.index) == 1) {
|
||||
v.add(TTextMenu(
|
||||
child: Text(translate('Switch Sides')),
|
||||
onPressed: () =>
|
||||
@@ -523,6 +542,7 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
|
||||
final pi = ffiModel.pi;
|
||||
final perms = ffiModel.permissions;
|
||||
final sessionId = ffi.sessionId;
|
||||
final isDefaultConn = ffi.connType == ConnType.defaultConn;
|
||||
|
||||
// show quality monitor
|
||||
final option = 'show-quality-monitor';
|
||||
@@ -535,7 +555,7 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
|
||||
},
|
||||
child: Text(translate('Show quality monitor'))));
|
||||
// mute
|
||||
if (perms['audio'] != false) {
|
||||
if (isDefaultConn && perms['audio'] != false) {
|
||||
final option = 'disable-audio';
|
||||
final value =
|
||||
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
|
||||
@@ -556,7 +576,8 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
|
||||
final isSupportIfPeer_1_2_4 = versionCmp(pi.version, '1.2.4') >= 0 &&
|
||||
bind.mainHasFileClipboard() &&
|
||||
pi.platformAdditions.containsKey(kPlatformAdditionsHasFileClipboard);
|
||||
if (ffiModel.keyboard &&
|
||||
if (isDefaultConn &&
|
||||
ffiModel.keyboard &&
|
||||
perms['file'] != false &&
|
||||
(isSupportIfPeer_1_2_3 || isSupportIfPeer_1_2_4)) {
|
||||
final enabled = !ffiModel.viewOnly;
|
||||
@@ -574,7 +595,7 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
|
||||
child: Text(translate('Enable file copy and paste'))));
|
||||
}
|
||||
// disable clipboard
|
||||
if (ffiModel.keyboard && perms['clipboard'] != false) {
|
||||
if (isDefaultConn && ffiModel.keyboard && perms['clipboard'] != false) {
|
||||
final enabled = !ffiModel.viewOnly;
|
||||
final option = 'disable-clipboard';
|
||||
var value =
|
||||
@@ -591,7 +612,7 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
|
||||
child: Text(translate('Disable clipboard'))));
|
||||
}
|
||||
// lock after session end
|
||||
if (ffiModel.keyboard && !ffiModel.isPeerAndroid) {
|
||||
if (isDefaultConn && ffiModel.keyboard && !ffiModel.isPeerAndroid) {
|
||||
final enabled = !ffiModel.viewOnly;
|
||||
final option = 'lock-after-session-end';
|
||||
final value =
|
||||
@@ -656,12 +677,12 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
|
||||
child: Text(translate('True color (4:4:4)'))));
|
||||
}
|
||||
|
||||
if (isMobile) {
|
||||
if (isDefaultConn && isMobile) {
|
||||
v.addAll(toolbarKeyboardToggles(ffi));
|
||||
}
|
||||
|
||||
// view mode (mobile only, desktop is in keyboard menu)
|
||||
if (isMobile && versionCmp(pi.version, '1.2.0') >= 0) {
|
||||
if (isDefaultConn && isMobile && versionCmp(pi.version, '1.2.0') >= 0) {
|
||||
v.add(TToggleMenu(
|
||||
value: ffiModel.viewOnly,
|
||||
onChanged: (value) async {
|
||||
|
||||
Reference in New Issue
Block a user