mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-19 10:51:01 +03:00
revert(cursor): return to f5b98b32 before splitting plugin changes
This commit is contained in:
@@ -19,7 +19,6 @@ 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';
|
||||
@@ -1089,21 +1088,6 @@ 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;
|
||||
@@ -1121,29 +1105,22 @@ class _ImagePaintState extends State<ImagePaint> {
|
||||
// changes, so read it live to follow the window across monitors.
|
||||
final dpr = MediaQuery.devicePixelRatioOf(context);
|
||||
|
||||
bool isViewScaled() =>
|
||||
c.viewStyle.style == kRemoteViewStyleAdaptive ||
|
||||
c.viewStyle.style == kRemoteViewStyleCustom;
|
||||
bool isViewAdaptive() => c.viewStyle.style == kRemoteViewStyleAdaptive;
|
||||
bool isViewOriginal() => c.viewStyle.style == kRemoteViewStyleOriginal;
|
||||
|
||||
mouseRegion({child}) => Obx(() {
|
||||
final useLocalSize = !isWeb &&
|
||||
(isLinux || isMacOS || isWindows) &&
|
||||
!zoomCursor.value &&
|
||||
isViewScaled();
|
||||
if (useLocalSize) _localCursorSize.ensureLoaded(dpr);
|
||||
double getCursorScale() {
|
||||
var c = Provider.of<CanvasModel>(context);
|
||||
var cursorScale = 1.0;
|
||||
if (isWindows) {
|
||||
// debug win10
|
||||
if (zoomCursor.value && isViewScaled()) {
|
||||
if (zoomCursor.value && isViewAdaptive()) {
|
||||
cursorScale = s * c.devicePixelRatio;
|
||||
}
|
||||
} else {
|
||||
if (zoomCursor.value || isViewOriginal()) {
|
||||
cursorScale = s;
|
||||
} else if (isLinux || isMacOS) {
|
||||
} else {
|
||||
// NSCursor and GdkCursor treat the bitmap size as logical
|
||||
// pixels, so an unzoomed cursor must be shrunk by the DPR to
|
||||
// keep 1 remote px == 1 physical px, the size Original view
|
||||
@@ -1172,16 +1149,11 @@ class _ImagePaintState extends State<ImagePaint> {
|
||||
_firstEnterImage.value = true;
|
||||
}
|
||||
return _buildCustomCursor(
|
||||
context, getCursorScale(),
|
||||
useLocalSize: useLocalSize);
|
||||
context, getCursorScale());
|
||||
}
|
||||
}())
|
||||
: _buildDisabledCursor(context, getCursorScale(),
|
||||
useLocalSize: useLocalSize)
|
||||
: _buildDisabledCursor(context, getCursorScale())
|
||||
: MouseCursor.defer,
|
||||
onEnter: (_) {
|
||||
if (useLocalSize) _localCursorSize.refresh();
|
||||
},
|
||||
onHover: (evt) {},
|
||||
child: child);
|
||||
});
|
||||
@@ -1294,19 +1266,15 @@ class _ImagePaintState extends State<ImagePaint> {
|
||||
);
|
||||
}
|
||||
|
||||
MouseCursor _buildCustomCursor(BuildContext context, double scale,
|
||||
{bool useLocalSize = false}) {
|
||||
MouseCursor _buildCustomCursor(BuildContext context, double scale) {
|
||||
final cursor = Provider.of<CursorModel>(context);
|
||||
final cache = cursor.cache ?? preDefaultCursor.cache;
|
||||
cache?.localSize = useLocalSize ? _localCursorSize.value : null;
|
||||
return buildCursorOfCache(cursor, scale, cache);
|
||||
}
|
||||
|
||||
MouseCursor _buildDisabledCursor(BuildContext context, double scale,
|
||||
{bool useLocalSize = false}) {
|
||||
MouseCursor _buildDisabledCursor(BuildContext context, double scale) {
|
||||
final cursor = Provider.of<CursorModel>(context);
|
||||
final cache = preForbiddenCursor.cache;
|
||||
cache?.localSize = useLocalSize ? _localCursorSize.value : null;
|
||||
return buildCursorOfCache(cursor, scale, cache);
|
||||
}
|
||||
|
||||
@@ -1425,9 +1393,8 @@ class CursorPaint extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
final imageOffset = _softwareImageOffset(c);
|
||||
double cx = imageOffset?.dx ?? c.x;
|
||||
double cy = imageOffset?.dy ?? c.y;
|
||||
double cx = c.x;
|
||||
double cy = c.y;
|
||||
if (c.viewStyle.style == kRemoteViewStyleOriginal &&
|
||||
c.scrollStyle == ScrollStyle.scrollbar) {
|
||||
final rect = c.parent.target!.ffiModel.rect;
|
||||
@@ -1446,44 +1413,38 @@ class CursorPaint extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
final image = m.image ?? preDefaultCursor.image;
|
||||
final nativePixels = isWindows ? MediaQuery.devicePixelRatioOf(context) : 1.0;
|
||||
double scale = c.scale;
|
||||
if (image != null && scale * nativePixels != 1.0) {
|
||||
final sx = kMinCursorSize / (image.width * nativePixels);
|
||||
final sy = kMinCursorSize / (image.height * nativePixels);
|
||||
final minimumScale = sx > sy ? sx : sy;
|
||||
if (scale < minimumScale) scale = minimumScale;
|
||||
double x = m.x * c.scale + cx - hotx;
|
||||
double y = m.y * c.scale + cy - hoty;
|
||||
double scale = 1.0;
|
||||
final isViewOriginal = c.viewStyle.style == kRemoteViewStyleOriginal;
|
||||
if (zoomCursor.value || isViewOriginal) {
|
||||
x = m.x - hotx + cx / c.scale;
|
||||
y = m.y - hoty + cy / c.scale;
|
||||
scale = c.scale;
|
||||
} else if (!isWindows) {
|
||||
// Keep the painted cursor the same physical size as the native one
|
||||
// built by getCursorScale() above, including its min-size clamp.
|
||||
scale = 1.0 / MediaQuery.devicePixelRatioOf(context);
|
||||
final image = m.image ?? preDefaultCursor.image;
|
||||
if (scale != 1.0 &&
|
||||
image != null &&
|
||||
((image.width * scale).toInt() < kMinCursorSize ||
|
||||
(image.height * scale).toInt() < kMinCursorSize)) {
|
||||
final sw = kMinCursorSize / image.width;
|
||||
final sh = kMinCursorSize / image.height;
|
||||
scale = sw < sh ? sh : sw;
|
||||
}
|
||||
x = (m.x * c.scale + cx) / scale - hotx;
|
||||
y = (m.y * c.scale + cy) / scale - hoty;
|
||||
}
|
||||
final x = (m.x * c.scale + cx) / scale - hotx;
|
||||
final y = (m.y * c.scale + cy) / scale - hoty;
|
||||
|
||||
return CustomPaint(
|
||||
painter: ImagePainter(
|
||||
image: image,
|
||||
image: m.image ?? preDefaultCursor.image,
|
||||
x: x,
|
||||
y: y,
|
||||
scale: scale,
|
||||
useIntegerPosition: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Offset? _softwareImageOffset(CanvasModel canvas) {
|
||||
if (canvas.imageOverflow.isTrue &&
|
||||
canvas.scrollStyle != ScrollStyle.scrollauto) {
|
||||
return null;
|
||||
}
|
||||
final ffi = canvas.parent.target!;
|
||||
final peer = ffi.ffiModel;
|
||||
if (ffi.imageModel.useTextureRender || peer.pi.forceTextureRender) {
|
||||
return null;
|
||||
}
|
||||
var scale = canvas.scale;
|
||||
final displays = peer.pi.getCurDisplays();
|
||||
if (peer.isPeerLinux && displays.isNotEmpty) scale /= displays[0].scale;
|
||||
// Match the origin used by _buildScrollAutoNonTextureRender's ImagePainter.
|
||||
return Offset(
|
||||
(canvas.x / scale).toInt() * scale, (canvas.y / scale).toInt() * scale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ 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';
|
||||
@@ -2864,8 +2863,6 @@ class CursorData {
|
||||
double hoty;
|
||||
final int width;
|
||||
final int height;
|
||||
double? localSize;
|
||||
late final int _visibleSize = cursorVisibleSize(image);
|
||||
|
||||
CursorData({
|
||||
required this.peerId,
|
||||
@@ -2882,34 +2879,9 @@ class CursorData {
|
||||
|
||||
int _doubleToInt(double v) => (v * 10e6).round().toInt();
|
||||
|
||||
bool get _usesLogicalCursorPixels => isLinux || isMacOS || isWeb;
|
||||
int get scaledWidth => _scaledDimension(width, scale);
|
||||
int get scaledHeight => _scaledDimension(height, scale);
|
||||
|
||||
int _scaledDimension(int dimension, double scale) {
|
||||
const minBitmapSize = 1;
|
||||
final pixels = dimension * scale;
|
||||
return _usesLogicalCursorPixels || localSize != null
|
||||
? max(minBitmapSize, pixels.round())
|
||||
: pixels.toInt();
|
||||
}
|
||||
|
||||
double _checkUpdateScale(double scale) {
|
||||
double oldScale = this.scale;
|
||||
if (localSize != null) {
|
||||
scale = _visibleSize == 0
|
||||
? 1.0
|
||||
: max(localSize!, kMinCursorSize) / _visibleSize;
|
||||
// Sparse peer artwork must not amplify the native bitmap allocation.
|
||||
const maxNativeCursorBitmapSize = 512;
|
||||
final maxScale = maxNativeCursorBitmapSize / max(width, height);
|
||||
if (scale > maxScale) {
|
||||
if (oldScale != maxScale) {
|
||||
debugPrint('Cursor $id exceeds the native bitmap limit; reducing scale');
|
||||
}
|
||||
scale = maxScale;
|
||||
}
|
||||
} else if (scale != 1.0) {
|
||||
if (scale != 1.0) {
|
||||
// Update data if scale changed.
|
||||
final tgtWidth = (width * scale).toInt();
|
||||
final tgtHeight = (height * scale).toInt();
|
||||
@@ -2920,17 +2892,13 @@ class CursorData {
|
||||
}
|
||||
}
|
||||
|
||||
const bytesPerPixel = 4;
|
||||
final byteLength = _scaledDimension(width, scale) *
|
||||
_scaledDimension(height, scale) * bytesPerPixel;
|
||||
if (_doubleToInt(oldScale) != _doubleToInt(scale) ||
|
||||
(isWindows && data != null && data!.length != byteLength)) {
|
||||
if (_doubleToInt(oldScale) != _doubleToInt(scale)) {
|
||||
if (isWindows) {
|
||||
data = img2
|
||||
.copyResize(
|
||||
image,
|
||||
width: _scaledDimension(width, scale),
|
||||
height: _scaledDimension(height, scale),
|
||||
width: (width * scale).toInt(),
|
||||
height: (height * scale).toInt(),
|
||||
interpolation: img2.Interpolation.average,
|
||||
)
|
||||
.getBytes(order: img2.ChannelOrder.bgra);
|
||||
@@ -2939,8 +2907,8 @@ class CursorData {
|
||||
img2.encodePng(
|
||||
img2.copyResize(
|
||||
image,
|
||||
width: _scaledDimension(width, scale),
|
||||
height: _scaledDimension(height, scale),
|
||||
width: (width * scale).toInt(),
|
||||
height: (height * scale).toInt(),
|
||||
interpolation: img2.Interpolation.average,
|
||||
),
|
||||
),
|
||||
@@ -2949,14 +2917,14 @@ class CursorData {
|
||||
}
|
||||
|
||||
this.scale = scale;
|
||||
hotx = hotxOrigin * scaledWidth / width;
|
||||
hoty = hotyOrigin * scaledHeight / height;
|
||||
hotx = hotxOrigin * scale;
|
||||
hoty = hotyOrigin * scale;
|
||||
return scale;
|
||||
}
|
||||
|
||||
String updateGetKey(double scale) {
|
||||
scale = _checkUpdateScale(scale);
|
||||
return '${peerId}_${id}_${_doubleToInt(width * scale)}_${_doubleToInt(height * scale)}${localSize == null ? '' : '_local'}';
|
||||
return '${peerId}_${id}_${_doubleToInt(width * scale)}_${_doubleToInt(height * scale)}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3470,8 +3438,6 @@ class CursorModel with ChangeNotifier {
|
||||
if (await _updateCache(rgba, image, id, hotx, hoty, width, height)) {
|
||||
_images[id]?.item1.dispose();
|
||||
_images[id] = Tuple3(image, hotx, hoty);
|
||||
} else {
|
||||
image.dispose();
|
||||
}
|
||||
|
||||
// Update last cursor data.
|
||||
@@ -3492,32 +3458,14 @@ class CursorModel with ChangeNotifier {
|
||||
img2.Image imgOrigin = img2.Image.fromBytes(
|
||||
width: w, height: h, bytes: rgba.buffer, order: img2.ChannelOrder.rgba);
|
||||
if (isWindows) {
|
||||
final pixels =
|
||||
await image.toByteData(format: ui.ImageByteFormat.rawStraightRgba);
|
||||
if (pixels == null) {
|
||||
debugPrint('Could not read straight-alpha cursor pixels: $id');
|
||||
return false;
|
||||
}
|
||||
imgOrigin = img2.Image.fromBytes(
|
||||
width: w, height: h, bytes: pixels.buffer, order: img2.ChannelOrder.rgba);
|
||||
data = imgOrigin.getBytes(order: img2.ChannelOrder.bgra);
|
||||
} else {
|
||||
ByteData? imgBytes =
|
||||
await image.toByteData(format: ui.ImageByteFormat.png);
|
||||
if (imgBytes == null) {
|
||||
debugPrint('Could not encode cursor PNG: $id');
|
||||
return false;
|
||||
}
|
||||
data = imgBytes.buffer.asUint8List();
|
||||
if (isLinux || isMacOS) {
|
||||
// Preserve the PNG's straight-alpha colors when resizing native cursors.
|
||||
final decoded = img2.decodePng(data);
|
||||
if (decoded == null) {
|
||||
debugPrint('Invalid native cursor PNG: $id');
|
||||
return false;
|
||||
}
|
||||
imgOrigin = decoded;
|
||||
}
|
||||
}
|
||||
final cache = CursorData(
|
||||
peerId: peerId,
|
||||
|
||||
@@ -1,39 +1,16 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter_custom_cursor/cursor_manager.dart'
|
||||
as custom_cursor_manager;
|
||||
import 'package:flutter_custom_cursor/flutter_custom_cursor.dart';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/models/model.dart';
|
||||
|
||||
deleteCustomCursor(String key) =>
|
||||
custom_cursor_manager.CursorManager.instance.deleteCursor(key);
|
||||
resetSystemCursor() {}
|
||||
|
||||
double _nativeHotspot(double hotspot, int bitmapSize) {
|
||||
if (!isLinux && !isWindows) return hotspot;
|
||||
// GDK and Win32 take integer hotspots inside the bitmap; use the nearest pixel.
|
||||
return min(hotspot.round(), bitmapSize - 1).toDouble();
|
||||
}
|
||||
|
||||
Uint8List _padCursorWidth(Uint8List data, int width) {
|
||||
final bitmap = img.decodePng(data);
|
||||
if (bitmap == null) {
|
||||
throw const FormatException('Invalid native cursor PNG');
|
||||
}
|
||||
final padded = img.copyExpandCanvas(bitmap,
|
||||
newWidth: width,
|
||||
newHeight: bitmap.height,
|
||||
position: img.ExpandCanvasPosition.topLeft,
|
||||
toImage: img.Image(width: width, height: bitmap.height, numChannels: 4));
|
||||
return Uint8List.fromList(img.encodePng(padded));
|
||||
}
|
||||
|
||||
MouseCursor buildCursorOfCache(
|
||||
CursorModel cursor, double scale, CursorData? cache) {
|
||||
if (cache == null) {
|
||||
@@ -46,12 +23,6 @@ MouseCursor buildCursorOfCache(
|
||||
if (data == null) {
|
||||
return MouseCursor.defer;
|
||||
}
|
||||
// Pad tall Linux buffers to prevent clipping in the hardware cursor plane.
|
||||
final width = isLinux
|
||||
? max(cache.scaledWidth, cache.scaledHeight)
|
||||
: cache.scaledWidth;
|
||||
final buffer =
|
||||
width == cache.scaledWidth ? data : _padCursorWidth(data, width);
|
||||
debugPrint(
|
||||
"Register custom cursor with key $key (${cache.hotx},${cache.hoty})");
|
||||
// [Safety]
|
||||
@@ -61,11 +32,11 @@ MouseCursor buildCursorOfCache(
|
||||
custom_cursor_manager.CursorManager.instance
|
||||
.registerCursor(custom_cursor_manager.CursorData()
|
||||
..name = key
|
||||
..buffer = buffer
|
||||
..width = width
|
||||
..height = cache.scaledHeight
|
||||
..hotX = _nativeHotspot(cache.hotx, cache.scaledWidth)
|
||||
..hotY = _nativeHotspot(cache.hoty, cache.scaledHeight));
|
||||
..buffer = data
|
||||
..width = (cache.width * cache.scale).toInt()
|
||||
..height = (cache.height * cache.scale).toInt()
|
||||
..hotX = cache.hotx
|
||||
..hotY = cache.hoty);
|
||||
cursor.addKey(key);
|
||||
}
|
||||
return FlutterCustomMemoryImageCursor(key: key);
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -96,14 +96,12 @@ class ImagePainter extends CustomPainter {
|
||||
required this.x,
|
||||
required this.y,
|
||||
required this.scale,
|
||||
this.useIntegerPosition = true,
|
||||
});
|
||||
|
||||
ui.Image? image;
|
||||
double x;
|
||||
double y;
|
||||
double scale;
|
||||
final bool useIntegerPosition;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
@@ -124,10 +122,8 @@ class ImagePainter extends CustomPainter {
|
||||
if (isWeb) {
|
||||
paint.filterQuality = FilterQuality.high;
|
||||
}
|
||||
final position = useIntegerPosition
|
||||
? Offset(x.toInt().toDouble(), y.toInt().toDouble())
|
||||
: Offset(x, y);
|
||||
canvas.drawImage(image!, position, paint);
|
||||
canvas.drawImage(
|
||||
image!, Offset(x.toInt().toDouble(), y.toInt().toDouble()), paint);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -52,8 +52,8 @@ class CursorManager {
|
||||
'cursor',
|
||||
jsonEncode({
|
||||
'url': cursorData.url,
|
||||
'hotx': cursorData.hotX.round(),
|
||||
'hoty': cursorData.hotY.round(),
|
||||
'hotx': cursorData.hotX.toInt(),
|
||||
'hoty': cursorData.hotY.toInt(),
|
||||
})
|
||||
]);
|
||||
}
|
||||
@@ -116,8 +116,8 @@ MouseCursor buildCursorOfCache(
|
||||
CursorManager.instance.registerCursor(CursorData(
|
||||
key: key,
|
||||
url: 'data:image/rgba;base64,${base64Encode(data)}',
|
||||
width: cache.scaledWidth,
|
||||
height: cache.scaledHeight,
|
||||
width: (cache.width * cache.scale).toInt(),
|
||||
height: (cache.height * cache.scale).toInt(),
|
||||
hotX: cache.hotx,
|
||||
hotY: cache.hoty));
|
||||
cursor.addKey(key);
|
||||
|
||||
Reference in New Issue
Block a user