mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-11 23:11:01 +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 '../../common/shared_state.dart';
|
||||
import '../../utils/image.dart';
|
||||
import '../../utils/cursor_size.dart';
|
||||
import '../widgets/remote_toolbar.dart';
|
||||
import '../widgets/kb_layout_type_chooser.dart';
|
||||
import '../widgets/tabbar_widget.dart';
|
||||
@@ -1088,6 +1089,21 @@ class ImagePaint extends StatefulWidget {
|
||||
|
||||
class _ImagePaintState extends State<ImagePaint> {
|
||||
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;
|
||||
RxBool get zoomCursor => widget.zoomCursor;
|
||||
@@ -1109,6 +1125,11 @@ class _ImagePaintState extends State<ImagePaint> {
|
||||
bool isViewOriginal() => c.viewStyle.style == kRemoteViewStyleOriginal;
|
||||
|
||||
mouseRegion({child}) => Obx(() {
|
||||
final useLocalSize = !isWeb &&
|
||||
(isLinux || isMacOS || isWindows) &&
|
||||
!zoomCursor.value &&
|
||||
isViewAdaptive();
|
||||
if (useLocalSize) _localCursorSize.ensureLoaded(dpr);
|
||||
double getCursorScale() {
|
||||
var c = Provider.of<CanvasModel>(context);
|
||||
var cursorScale = 1.0;
|
||||
@@ -1149,11 +1170,16 @@ class _ImagePaintState extends State<ImagePaint> {
|
||||
_firstEnterImage.value = true;
|
||||
}
|
||||
return _buildCustomCursor(
|
||||
context, getCursorScale());
|
||||
context, getCursorScale(),
|
||||
useLocalSize: useLocalSize);
|
||||
}
|
||||
}())
|
||||
: _buildDisabledCursor(context, getCursorScale())
|
||||
: _buildDisabledCursor(context, getCursorScale(),
|
||||
useLocalSize: useLocalSize)
|
||||
: MouseCursor.defer,
|
||||
onEnter: (_) {
|
||||
if (useLocalSize) _localCursorSize.refresh();
|
||||
},
|
||||
onHover: (evt) {},
|
||||
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 cache = cursor.cache ?? preDefaultCursor.cache;
|
||||
if (useLocalSize && _localCursorSize.value == null) return MouseCursor.defer;
|
||||
cache?.localSize = useLocalSize ? _localCursorSize.value : null;
|
||||
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 cache = preForbiddenCursor.cache;
|
||||
if (useLocalSize && _localCursorSize.value == null) return MouseCursor.defer;
|
||||
cache?.localSize = useLocalSize ? _localCursorSize.value : null;
|
||||
return buildCursorOfCache(cursor, scale, cache);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import 'package:vector_math/vector_math.dart' show Vector2;
|
||||
|
||||
import '../common.dart';
|
||||
import '../utils/image.dart' as img;
|
||||
import '../utils/cursor_size.dart';
|
||||
import '../common/widgets/dialog.dart';
|
||||
import 'input_model.dart';
|
||||
import 'platform_model.dart';
|
||||
@@ -2863,6 +2864,8 @@ class CursorData {
|
||||
double hoty;
|
||||
final int width;
|
||||
final int height;
|
||||
double? localSize;
|
||||
late final int _visibleSize = cursorVisibleSize(image);
|
||||
|
||||
CursorData({
|
||||
required this.peerId,
|
||||
@@ -2886,14 +2889,18 @@ class CursorData {
|
||||
int _scaledDimension(int dimension, double scale) {
|
||||
const minBitmapSize = 1;
|
||||
final pixels = dimension * scale;
|
||||
return _usesLogicalCursorPixels
|
||||
return _usesLogicalCursorPixels || localSize != null
|
||||
? max(minBitmapSize, pixels.round())
|
||||
: pixels.toInt();
|
||||
}
|
||||
|
||||
double _checkUpdateScale(double 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.
|
||||
final tgtWidth = (width * 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) {
|
||||
data = img2
|
||||
.copyResize(
|
||||
image,
|
||||
width: (width * scale).toInt(),
|
||||
height: (height * scale).toInt(),
|
||||
width: _scaledDimension(width, scale),
|
||||
height: _scaledDimension(height, scale),
|
||||
interpolation: img2.Interpolation.average,
|
||||
)
|
||||
.getBytes(order: img2.ChannelOrder.bgra);
|
||||
@@ -2936,7 +2947,7 @@ class CursorData {
|
||||
|
||||
String updateGetKey(double 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