mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-14 08:21:02 +03:00
fix(flutter): size unzoomed remote cursors to the local system
This commit is contained in:
@@ -19,6 +19,7 @@ import '../../models/input_model.dart';
|
|||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import '../../common/shared_state.dart';
|
import '../../common/shared_state.dart';
|
||||||
import '../../utils/image.dart';
|
import '../../utils/image.dart';
|
||||||
|
import '../../utils/cursor_size.dart';
|
||||||
import '../widgets/remote_toolbar.dart';
|
import '../widgets/remote_toolbar.dart';
|
||||||
import '../widgets/kb_layout_type_chooser.dart';
|
import '../widgets/kb_layout_type_chooser.dart';
|
||||||
import '../widgets/tabbar_widget.dart';
|
import '../widgets/tabbar_widget.dart';
|
||||||
@@ -1088,6 +1089,21 @@ class ImagePaint extends StatefulWidget {
|
|||||||
|
|
||||||
class _ImagePaintState extends State<ImagePaint> {
|
class _ImagePaintState extends State<ImagePaint> {
|
||||||
bool _lastRemoteCursorMoved = false;
|
bool _lastRemoteCursorMoved = false;
|
||||||
|
final _localCursorSize = LocalCursorSize();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_localCursorSize.addListener(_cursorSizeChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cursorSizeChanged() => setState(() {});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_localCursorSize.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
String get id => widget.id;
|
String get id => widget.id;
|
||||||
RxBool get zoomCursor => widget.zoomCursor;
|
RxBool get zoomCursor => widget.zoomCursor;
|
||||||
@@ -1109,6 +1125,11 @@ class _ImagePaintState extends State<ImagePaint> {
|
|||||||
bool isViewOriginal() => c.viewStyle.style == kRemoteViewStyleOriginal;
|
bool isViewOriginal() => c.viewStyle.style == kRemoteViewStyleOriginal;
|
||||||
|
|
||||||
mouseRegion({child}) => Obx(() {
|
mouseRegion({child}) => Obx(() {
|
||||||
|
final useLocalSize = !isWeb &&
|
||||||
|
(isLinux || isMacOS || isWindows) &&
|
||||||
|
!zoomCursor.value &&
|
||||||
|
isViewAdaptive();
|
||||||
|
if (useLocalSize) _localCursorSize.ensureLoaded(dpr);
|
||||||
double getCursorScale() {
|
double getCursorScale() {
|
||||||
var c = Provider.of<CanvasModel>(context);
|
var c = Provider.of<CanvasModel>(context);
|
||||||
var cursorScale = 1.0;
|
var cursorScale = 1.0;
|
||||||
@@ -1149,11 +1170,16 @@ class _ImagePaintState extends State<ImagePaint> {
|
|||||||
_firstEnterImage.value = true;
|
_firstEnterImage.value = true;
|
||||||
}
|
}
|
||||||
return _buildCustomCursor(
|
return _buildCustomCursor(
|
||||||
context, getCursorScale());
|
context, getCursorScale(),
|
||||||
|
useLocalSize: useLocalSize);
|
||||||
}
|
}
|
||||||
}())
|
}())
|
||||||
: _buildDisabledCursor(context, getCursorScale())
|
: _buildDisabledCursor(context, getCursorScale(),
|
||||||
|
useLocalSize: useLocalSize)
|
||||||
: MouseCursor.defer,
|
: MouseCursor.defer,
|
||||||
|
onEnter: (_) {
|
||||||
|
if (useLocalSize) _localCursorSize.refresh();
|
||||||
|
},
|
||||||
onHover: (evt) {},
|
onHover: (evt) {},
|
||||||
child: child);
|
child: child);
|
||||||
});
|
});
|
||||||
@@ -1266,15 +1292,21 @@ class _ImagePaintState extends State<ImagePaint> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseCursor _buildCustomCursor(BuildContext context, double scale) {
|
MouseCursor _buildCustomCursor(BuildContext context, double scale,
|
||||||
|
{bool useLocalSize = false}) {
|
||||||
final cursor = Provider.of<CursorModel>(context);
|
final cursor = Provider.of<CursorModel>(context);
|
||||||
final cache = cursor.cache ?? preDefaultCursor.cache;
|
final cache = cursor.cache ?? preDefaultCursor.cache;
|
||||||
|
if (useLocalSize && _localCursorSize.value == null) return MouseCursor.defer;
|
||||||
|
cache?.localSize = useLocalSize ? _localCursorSize.value : null;
|
||||||
return buildCursorOfCache(cursor, scale, cache);
|
return buildCursorOfCache(cursor, scale, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseCursor _buildDisabledCursor(BuildContext context, double scale) {
|
MouseCursor _buildDisabledCursor(BuildContext context, double scale,
|
||||||
|
{bool useLocalSize = false}) {
|
||||||
final cursor = Provider.of<CursorModel>(context);
|
final cursor = Provider.of<CursorModel>(context);
|
||||||
final cache = preForbiddenCursor.cache;
|
final cache = preForbiddenCursor.cache;
|
||||||
|
if (useLocalSize && _localCursorSize.value == null) return MouseCursor.defer;
|
||||||
|
cache?.localSize = useLocalSize ? _localCursorSize.value : null;
|
||||||
return buildCursorOfCache(cursor, scale, cache);
|
return buildCursorOfCache(cursor, scale, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import 'package:vector_math/vector_math.dart' show Vector2;
|
|||||||
|
|
||||||
import '../common.dart';
|
import '../common.dart';
|
||||||
import '../utils/image.dart' as img;
|
import '../utils/image.dart' as img;
|
||||||
|
import '../utils/cursor_size.dart';
|
||||||
import '../common/widgets/dialog.dart';
|
import '../common/widgets/dialog.dart';
|
||||||
import 'input_model.dart';
|
import 'input_model.dart';
|
||||||
import 'platform_model.dart';
|
import 'platform_model.dart';
|
||||||
@@ -2863,6 +2864,8 @@ class CursorData {
|
|||||||
double hoty;
|
double hoty;
|
||||||
final int width;
|
final int width;
|
||||||
final int height;
|
final int height;
|
||||||
|
double? localSize;
|
||||||
|
late final int _visibleSize = cursorVisibleSize(image);
|
||||||
|
|
||||||
CursorData({
|
CursorData({
|
||||||
required this.peerId,
|
required this.peerId,
|
||||||
@@ -2886,14 +2889,18 @@ class CursorData {
|
|||||||
int _scaledDimension(int dimension, double scale) {
|
int _scaledDimension(int dimension, double scale) {
|
||||||
const minBitmapSize = 1;
|
const minBitmapSize = 1;
|
||||||
final pixels = dimension * scale;
|
final pixels = dimension * scale;
|
||||||
return _usesLogicalCursorPixels
|
return _usesLogicalCursorPixels || localSize != null
|
||||||
? max(minBitmapSize, pixels.round())
|
? max(minBitmapSize, pixels.round())
|
||||||
: pixels.toInt();
|
: pixels.toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
double _checkUpdateScale(double scale) {
|
double _checkUpdateScale(double scale) {
|
||||||
double oldScale = this.scale;
|
double oldScale = this.scale;
|
||||||
if (scale != 1.0) {
|
if (localSize != null) {
|
||||||
|
scale = _visibleSize == 0
|
||||||
|
? 1.0
|
||||||
|
: max(localSize!, kMinCursorSize) / _visibleSize;
|
||||||
|
} else if (scale != 1.0) {
|
||||||
// Update data if scale changed.
|
// Update data if scale changed.
|
||||||
final tgtWidth = (width * scale).toInt();
|
final tgtWidth = (width * scale).toInt();
|
||||||
final tgtHeight = (height * scale).toInt();
|
final tgtHeight = (height * scale).toInt();
|
||||||
@@ -2904,13 +2911,17 @@ class CursorData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_doubleToInt(oldScale) != _doubleToInt(scale)) {
|
const bytesPerPixel = 4;
|
||||||
|
final byteLength = _scaledDimension(width, scale) *
|
||||||
|
_scaledDimension(height, scale) * bytesPerPixel;
|
||||||
|
if (_doubleToInt(oldScale) != _doubleToInt(scale) ||
|
||||||
|
(isWindows && data != null && data!.length != byteLength)) {
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
data = img2
|
data = img2
|
||||||
.copyResize(
|
.copyResize(
|
||||||
image,
|
image,
|
||||||
width: (width * scale).toInt(),
|
width: _scaledDimension(width, scale),
|
||||||
height: (height * scale).toInt(),
|
height: _scaledDimension(height, scale),
|
||||||
interpolation: img2.Interpolation.average,
|
interpolation: img2.Interpolation.average,
|
||||||
)
|
)
|
||||||
.getBytes(order: img2.ChannelOrder.bgra);
|
.getBytes(order: img2.ChannelOrder.bgra);
|
||||||
@@ -2936,7 +2947,7 @@ class CursorData {
|
|||||||
|
|
||||||
String updateGetKey(double scale) {
|
String updateGetKey(double scale) {
|
||||||
scale = _checkUpdateScale(scale);
|
scale = _checkUpdateScale(scale);
|
||||||
return '${peerId}_${id}_${_doubleToInt(width * scale)}_${_doubleToInt(height * scale)}';
|
return '${peerId}_${id}_${_doubleToInt(width * scale)}_${_doubleToInt(height * scale)}${localSize == null ? '' : '_local'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
55
flutter/lib/utils/cursor_size.dart
Normal file
55
flutter/lib/utils/cursor_size.dart
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:image/image.dart' as img;
|
||||||
|
|
||||||
|
int cursorVisibleSize(img.Image image) {
|
||||||
|
var left = image.width;
|
||||||
|
var top = image.height;
|
||||||
|
var right = -1;
|
||||||
|
var bottom = -1;
|
||||||
|
for (final pixel in image) {
|
||||||
|
if (pixel.a == 0) continue;
|
||||||
|
if (pixel.x < left) left = pixel.x;
|
||||||
|
if (pixel.x > right) right = pixel.x;
|
||||||
|
if (pixel.y < top) top = pixel.y;
|
||||||
|
if (pixel.y > bottom) bottom = pixel.y;
|
||||||
|
}
|
||||||
|
if (right < left) return 0;
|
||||||
|
final width = right - left + 1;
|
||||||
|
final height = bottom - top + 1;
|
||||||
|
return width > height ? width : height;
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalCursorSize extends ValueNotifier<double?> {
|
||||||
|
LocalCursorSize() : super(null);
|
||||||
|
|
||||||
|
static const _channel = MethodChannel('org.rustdesk.rustdesk/cursor');
|
||||||
|
double? _devicePixelRatio;
|
||||||
|
int _generation = 0;
|
||||||
|
|
||||||
|
void ensureLoaded(double devicePixelRatio) {
|
||||||
|
if (_devicePixelRatio == devicePixelRatio) return;
|
||||||
|
_devicePixelRatio = devicePixelRatio;
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> refresh() async {
|
||||||
|
final generation = ++_generation;
|
||||||
|
try {
|
||||||
|
final size = await _channel.invokeMethod<double>('getSystemCursorSize');
|
||||||
|
if (size == null || !size.isFinite || size <= 0) {
|
||||||
|
throw const FormatException('Invalid local system cursor size');
|
||||||
|
}
|
||||||
|
if (generation == _generation) value = size;
|
||||||
|
} catch (error, stack) {
|
||||||
|
FlutterError.reportError(FlutterErrorDetails(
|
||||||
|
exception: error, stack: stack, library: 'local cursor size'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
++_generation;
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user