mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-08-26 05:15:45 +08:00
[fix] detect macOS remote cursor shapes
This commit is contained in:
@@ -5,42 +5,126 @@
|
|||||||
#import <AppKit/AppKit.h>
|
#import <AppKit/AppKit.h>
|
||||||
#import <CoreGraphics/CGRemoteOperation.h>
|
#import <CoreGraphics/CGRemoteOperation.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
bool SameCursor(NSCursor* left, NSCursor* right) {
|
struct CursorFingerprint {
|
||||||
if (left == right || [left isEqual:right]) return true;
|
uint64_t pixel_hash = 0;
|
||||||
return left && right && NSEqualPoints(left.hotSpot, right.hotSpot) &&
|
size_t width = 0;
|
||||||
[left.image isEqual:right.image];
|
size_t height = 0;
|
||||||
|
size_t bytes_per_row = 0;
|
||||||
|
CFIndex pixel_bytes = 0;
|
||||||
|
NSPoint hot_spot = NSZeroPoint;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct KnownCursor {
|
||||||
|
CursorFingerprint fingerprint;
|
||||||
|
CursorShape shape = CursorShape::default_cursor;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool FingerprintCursor(NSCursor* cursor, CursorFingerprint* fingerprint) {
|
||||||
|
if (!cursor || !fingerprint || !cursor.image) return false;
|
||||||
|
|
||||||
|
NSRect proposed_rect = {NSZeroPoint, cursor.image.size};
|
||||||
|
CGImageRef image =
|
||||||
|
[cursor.image CGImageForProposedRect:&proposed_rect
|
||||||
|
context:nil
|
||||||
|
hints:nil];
|
||||||
|
if (!image) return false;
|
||||||
|
|
||||||
|
CGDataProviderRef provider = CGImageGetDataProvider(image);
|
||||||
|
if (!provider) return false;
|
||||||
|
|
||||||
|
CFDataRef data = CGDataProviderCopyData(provider);
|
||||||
|
if (!data) return false;
|
||||||
|
|
||||||
|
// NSCursor.currentSystemCursor returns a fresh NSCursor/NSImage wrapper on
|
||||||
|
// each query. Hash the decoded CGImage pixels so it can still be matched to
|
||||||
|
// AppKit's semantic cursor instances without encoding a large TIFF every
|
||||||
|
// frame.
|
||||||
|
constexpr uint64_t kFnvOffsetBasis = 1469598103934665603ULL;
|
||||||
|
constexpr uint64_t kFnvPrime = 1099511628211ULL;
|
||||||
|
uint64_t hash = kFnvOffsetBasis;
|
||||||
|
const UInt8* bytes = CFDataGetBytePtr(data);
|
||||||
|
const CFIndex length = CFDataGetLength(data);
|
||||||
|
for (CFIndex index = 0; index < length; ++index) {
|
||||||
|
hash ^= bytes[index];
|
||||||
|
hash *= kFnvPrime;
|
||||||
|
}
|
||||||
|
|
||||||
|
fingerprint->pixel_hash = hash;
|
||||||
|
fingerprint->width = CGImageGetWidth(image);
|
||||||
|
fingerprint->height = CGImageGetHeight(image);
|
||||||
|
fingerprint->bytes_per_row = CGImageGetBytesPerRow(image);
|
||||||
|
fingerprint->pixel_bytes = length;
|
||||||
|
fingerprint->hot_spot = cursor.hotSpot;
|
||||||
|
CFRelease(data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SameCursor(const CursorFingerprint& left,
|
||||||
|
const CursorFingerprint& right) {
|
||||||
|
return left.pixel_hash == right.pixel_hash && left.width == right.width &&
|
||||||
|
left.height == right.height &&
|
||||||
|
left.bytes_per_row == right.bytes_per_row &&
|
||||||
|
left.pixel_bytes == right.pixel_bytes &&
|
||||||
|
NSEqualPoints(left.hot_spot, right.hot_spot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddKnownCursor(std::vector<KnownCursor>* cursors, NSCursor* cursor,
|
||||||
|
CursorShape shape) {
|
||||||
|
CursorFingerprint fingerprint;
|
||||||
|
if (FingerprintCursor(cursor, &fingerprint)) {
|
||||||
|
cursors->push_back({fingerprint, shape});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<KnownCursor> BuildKnownCursors() {
|
||||||
|
std::vector<KnownCursor> cursors;
|
||||||
|
cursors.reserve(16);
|
||||||
|
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
AddKnownCursor(&cursors, NSCursor.arrowCursor, CursorShape::default_cursor);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.pointingHandCursor, CursorShape::pointer);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.crosshairCursor, CursorShape::crosshair);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.IBeamCursor, CursorShape::text);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.IBeamCursorForVerticalLayout,
|
||||||
|
CursorShape::text);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.operationNotAllowedCursor,
|
||||||
|
CursorShape::not_allowed);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.dragLinkCursor, CursorShape::alias);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.dragCopyCursor, CursorShape::copy);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.openHandCursor, CursorShape::grab);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.closedHandCursor, CursorShape::grabbing);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.resizeLeftRightCursor,
|
||||||
|
CursorShape::ew_resize);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.resizeUpDownCursor, CursorShape::ns_resize);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.resizeUpCursor, CursorShape::n_resize);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.resizeRightCursor, CursorShape::e_resize);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.resizeDownCursor, CursorShape::s_resize);
|
||||||
|
AddKnownCursor(&cursors, NSCursor.resizeLeftCursor, CursorShape::w_resize);
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
|
return cursors;
|
||||||
}
|
}
|
||||||
|
|
||||||
CursorShape ShapeFromMacCursor(NSCursor* cursor) {
|
CursorShape ShapeFromMacCursor(NSCursor* cursor) {
|
||||||
if (SameCursor(cursor, NSCursor.pointingHandCursor))
|
CursorFingerprint fingerprint;
|
||||||
return CursorShape::pointer;
|
if (!FingerprintCursor(cursor, &fingerprint)) {
|
||||||
if (SameCursor(cursor, NSCursor.crosshairCursor))
|
return CursorShape::default_cursor;
|
||||||
return CursorShape::crosshair;
|
}
|
||||||
if (SameCursor(cursor, NSCursor.IBeamCursor) ||
|
|
||||||
SameCursor(cursor, NSCursor.IBeamCursorForVerticalLayout))
|
// Sampling begins from the UI tick, after AppKit has initialized its cursor
|
||||||
return CursorShape::text;
|
// catalog. Cache those fingerprints because only the current cursor needs
|
||||||
if (SameCursor(cursor, NSCursor.operationNotAllowedCursor))
|
// to be decoded on subsequent frames.
|
||||||
return CursorShape::not_allowed;
|
static const std::vector<KnownCursor> known_cursors = BuildKnownCursors();
|
||||||
if (SameCursor(cursor, NSCursor.dragLinkCursor)) return CursorShape::alias;
|
for (const KnownCursor& known : known_cursors) {
|
||||||
if (SameCursor(cursor, NSCursor.dragCopyCursor)) return CursorShape::copy;
|
if (SameCursor(fingerprint, known.fingerprint)) return known.shape;
|
||||||
if (SameCursor(cursor, NSCursor.openHandCursor)) return CursorShape::grab;
|
}
|
||||||
if (SameCursor(cursor, NSCursor.closedHandCursor))
|
|
||||||
return CursorShape::grabbing;
|
|
||||||
if (SameCursor(cursor, NSCursor.resizeLeftRightCursor))
|
|
||||||
return CursorShape::ew_resize;
|
|
||||||
if (SameCursor(cursor, NSCursor.resizeUpDownCursor))
|
|
||||||
return CursorShape::ns_resize;
|
|
||||||
if (SameCursor(cursor, NSCursor.resizeUpCursor))
|
|
||||||
return CursorShape::n_resize;
|
|
||||||
if (SameCursor(cursor, NSCursor.resizeRightCursor))
|
|
||||||
return CursorShape::e_resize;
|
|
||||||
if (SameCursor(cursor, NSCursor.resizeDownCursor))
|
|
||||||
return CursorShape::s_resize;
|
|
||||||
if (SameCursor(cursor, NSCursor.resizeLeftCursor))
|
|
||||||
return CursorShape::w_resize;
|
|
||||||
return CursorShape::default_cursor;
|
return CursorShape::default_cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,8 +146,7 @@ bool CursorStateProvider::Sample(CursorState* state) {
|
|||||||
|
|
||||||
state->seq = 0;
|
state->seq = 0;
|
||||||
state->visible = visible && cursor != nil;
|
state->visible = visible && cursor != nil;
|
||||||
state->shape = state->visible ? ShapeFromMacCursor(cursor)
|
state->shape = state->visible ? ShapeFromMacCursor(cursor) : CursorShape::none;
|
||||||
: CursorShape::none;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user