mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-10-14 15:59:39 +00:00
The shape shown before stays while the one in use decodes, so a switch does not flash the default cursor. It also stayed once the shape in use was known to be lost (rejected by the core, or not decoding): the controller kept drawing, say, an I-beam for a pointer it never got, until the peer changed shape. Both the native and the painted cursor now leave the default one, as master did for the native cursor. A shape that does not decode is marked in updateCursorData, whether it was sent or fetched again, so both paths agree. The client also selects a shape it rejects as it arrives. It showed the shape before until the peer selected the rejected one again, and the default cursor from then on; it now looks the same both times. Sciter ignores an id it has no shape for, as it did. The default and forbidden cursors are made with the model. They were made the first time a build asked for one, and their decode lands later with nothing to draw again, so that first build showed the shape before in their place. A fetch failing after the session was cleared marks nothing in the new one, and a fetch that throws at once tells the listeners after the build that asked for it, not during it. The forbidden cursor, shown while input is off, became the cursor shown last, so a raster made after input came back showed it until the shape's pixels were fetched, or for good if they never came. Only a shape stands in for a shape now; CursorModel.shown keeps the rule for both builders. _checkView asks for a single registration again: every native cursor is made once per session, and one made twice leaks an HCURSOR on Windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
79 lines
2.8 KiB
Dart
79 lines
2.8 KiB
Dart
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:flutter_hbb/common.dart' show isLinux;
|
|
import 'package:flutter_hbb/models/model.dart';
|
|
import 'package:image/image.dart' as img;
|
|
|
|
deleteCustomCursor(String key) =>
|
|
custom_cursor_manager.CursorManager.instance.deleteCursor(key);
|
|
resetSystemCursor() {}
|
|
|
|
MouseCursor buildCursorOfCache(
|
|
CursorModel cursor, double scale, CursorData? cache) {
|
|
if (cache == null) {
|
|
return _shownCursor(cursor);
|
|
} else {
|
|
final key = cursor.nativeKey(cache, scale);
|
|
if (!cursor.cachedKeys.contains(key)) {
|
|
// data should be checked here, because it may be changed after `updateGetKey()`
|
|
final data = cache.data;
|
|
if (data == null) {
|
|
cursor.restorePixels(cache.id);
|
|
return _shownCursor(cursor);
|
|
}
|
|
// Square canvases avoid clipping or stray edge pixels on Linux.
|
|
final width = isLinux && cache.rasterWidth < cache.rasterHeight
|
|
? cache.rasterHeight
|
|
: cache.rasterWidth;
|
|
final height = isLinux ? width : cache.rasterHeight;
|
|
debugPrint(
|
|
"Register custom cursor with key $key (${cache.hotx},${cache.hoty})");
|
|
// [Safety]
|
|
// It's ok to call async registerCursor in current synchronous context,
|
|
// because activating the cursor is also an async call and will always
|
|
// be executed after this.
|
|
custom_cursor_manager.CursorManager.instance
|
|
.registerCursor(custom_cursor_manager.CursorData()
|
|
..name = key
|
|
..buffer =
|
|
width == cache.rasterWidth && height == cache.rasterHeight
|
|
? data
|
|
: _padCursor(data, width)
|
|
..width = width
|
|
..height = height
|
|
..hotX = cache.hotx
|
|
..hotY = cache.hoty);
|
|
cursor.addKey(key);
|
|
cursor.registered(cache, key);
|
|
}
|
|
cursor.shown(cache, key);
|
|
return FlutterCustomMemoryImageCursor(key: key);
|
|
}
|
|
}
|
|
|
|
// The last shape's cursor stays while the one in use is made, rather than the system one.
|
|
MouseCursor _shownCursor(CursorModel cursor) {
|
|
final key = cursor.shownKey;
|
|
return key == null
|
|
? MouseCursor.defer
|
|
: FlutterCustomMemoryImageCursor(key: key);
|
|
}
|
|
|
|
Uint8List _padCursor(Uint8List data, int size) {
|
|
final bitmap = img.decodePng(data);
|
|
if (bitmap == null) {
|
|
throw const FormatException('Invalid native cursor PNG');
|
|
}
|
|
final padded = img.copyExpandCanvas(bitmap,
|
|
newWidth: size,
|
|
newHeight: size,
|
|
position: img.ExpandCanvasPosition.topLeft,
|
|
toImage: img.Image(width: size, height: size, numChannels: 4));
|
|
return Uint8List.fromList(img.encodePng(padded));
|
|
}
|