From 5206933399775e5ff46baefbbb3a776c2a9bb84c Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Mon, 12 Oct 2026 10:53:22 +0300 Subject: feat: add type-safe native platform headers and portable drag support - Add include/yasl/native/{wayland,cocoa,x11}.h with type-safe handle accessors. - Unify drag-and-drop and window carry support across Wayland, X11, and Cocoa. - Preserve full ABI compatibility for capability flags. - Install native headers conditionally based on enabled backend. - Include missing tablet example and backend test suites. --- tests/cocoa/test_cursor.m | 53 +++++++++++++++++++ tests/cocoa/test_ownership.m | 4 ++ tests/cocoa/test_tablet.m | 123 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 tests/cocoa/test_cursor.m create mode 100644 tests/cocoa/test_tablet.m (limited to 'tests/cocoa') diff --git a/tests/cocoa/test_cursor.m b/tests/cocoa/test_cursor.m new file mode 100644 index 0000000..0f3ba06 --- /dev/null +++ b/tests/cocoa/test_cursor.m @@ -0,0 +1,53 @@ +#import +#include +#include + +static void assert_transparent(NSCursor *cursor) +{ + assert(cursor); + NSBitmapImageRep *bitmap = [NSBitmapImageRep imageRepWithData:cursor.image.TIFFRepresentation]; + assert(bitmap && bitmap.hasAlpha); + for (NSInteger y = 0; y < bitmap.pixelsHigh; y++) + for (NSInteger x = 0; x < bitmap.pixelsWide; x++) + assert([bitmap colorAtX:x y:y].alphaComponent == 0); +} + +int main(void) +{ + @autoreleasepool { + struct yasl_display *display = yasl_display_open(NULL, NULL); + assert(display); + struct yasl_surface_spec spec = {.title = "Cursor test", .size = {320, 240}}; + struct yasl_surface *surface = yasl_surface_create_toplevel(display, &spec); + assert(surface); + NSWindow *window = (NSWindow *)yasl_surface_get_native_handle(surface); + NSView *view = window.contentView; + yasl_surface_set_cursor(surface, YASL_CURSOR_HIDDEN); + [view cursorUpdate:nil]; + NSCursor *hidden = [NSCursor currentCursor]; + assert_transparent(hidden); + + NSEvent *motion = [NSEvent mouseEventWithType:NSEventTypeMouseMoved + location:NSMakePoint(100, 120) modifierFlags:0 timestamp:0 + windowNumber:window.windowNumber context:nil eventNumber:0 clickCount:0 pressure:0]; + [view mouseMoved:motion]; + [view cursorUpdate:nil]; + assert([NSCursor currentCursor] == hidden); + struct yasl_input_event event; + bool moved = false; + while (yasl_surface_input_poll(surface, &event)) + if (event.kind == YASL_INPUT_MOUSE && event.mouse.kind == YASL_PTR_MOTION) + moved = true; + assert(moved); + + [[NSCursor arrowCursor] set]; /* Simulate leaving and re-entering. */ + [view cursorUpdate:nil]; + assert([NSCursor currentCursor] == hidden); + yasl_surface_set_cursor(surface, YASL_CURSOR_ARROW); + [view cursorUpdate:nil]; + assert([NSCursor currentCursor] == [NSCursor arrowCursor]); + yasl_surface_destroy(surface); + yasl_display_close(display); + } + return 0; +} diff --git a/tests/cocoa/test_ownership.m b/tests/cocoa/test_ownership.m index 168a47b..2b2f52d 100644 --- a/tests/cocoa/test_ownership.m +++ b/tests/cocoa/test_ownership.m @@ -1,4 +1,5 @@ #include "yasl/native.h" +#include "yasl/native/cocoa.h" #import #include "yasl/yasl.h" #include @@ -13,7 +14,10 @@ int main(void) struct yasl_surface *s = yasl_surface_create_toplevel(d, &spec); assert(s); assert(yasl_display_get_native_handle(d) == (void *)NSApp); + assert(yasl_cocoa_display_get_application(d) == NSApp); NSWindow *window = (NSWindow *)yasl_surface_get_native_handle(s); + assert(yasl_cocoa_surface_get_window(s) == window); + assert(yasl_cocoa_surface_get_view(s) == window.contentView); assert([window isKindOfClass:[NSWindow class]] && window.contentView); struct yasl_input_event event; while (yasl_surface_input_poll(s, &event)) {} diff --git a/tests/cocoa/test_tablet.m b/tests/cocoa/test_tablet.m new file mode 100644 index 0000000..f1489a1 --- /dev/null +++ b/tests/cocoa/test_tablet.m @@ -0,0 +1,123 @@ +#import +#include +#include + +/* A strict event double catches querying tablet-only properties on ordinary + * mouse events, and proximity-only properties on tablet point events. */ +@interface TabletSample : NSObject +@property NSEventType type; +@property NSEventSubtype subtype; +@property NSEventModifierFlags modifierFlags; +@property NSPoint locationInWindow; +@property NSInteger buttonNumber; +@property NSUInteger buttonMask; +@property(getter=isEnteringProximity) BOOL enteringProximity; +@property NSPointingDeviceType tool; +@property float samplePressure; +@property NSPoint sampleTilt; +@end + +@implementation TabletSample +- (float)pressure +{ + assert(self.type == NSEventTypeTabletPoint || self.subtype == NSEventSubtypeTabletPoint); + return self.samplePressure; +} +- (NSPoint)tilt +{ + assert(self.type == NSEventTypeTabletPoint || self.subtype == NSEventSubtypeTabletPoint); + return self.sampleTilt; +} +- (NSPointingDeviceType)pointingDeviceType +{ + assert(self.type == NSEventTypeTabletProximity || self.subtype == NSEventSubtypeTabletProximity); + return self.tool; +} +@end + +static struct yasl_mouse_event next(struct yasl_surface *surface) +{ + struct yasl_input_event event; + while (yasl_surface_input_poll(surface, &event)) + if (event.kind == YASL_INPUT_MOUSE) return event.mouse; + assert(!"Expected a pointer event"); + return (struct yasl_mouse_event){0}; +} + +int main(void) +{ + @autoreleasepool { + struct yasl_display *display = yasl_display_open(NULL, NULL); + assert(display && (yasl_display_caps(display) & YASL_CAP_TABLET)); + struct yasl_surface_spec spec = {.title = "Tablet test", .size = {320, 240}}; + struct yasl_surface *surface = yasl_surface_create_toplevel(display, &spec); + assert(surface); + NSWindow *window = (NSWindow *)yasl_surface_get_native_handle(surface); + NSView *view = window.contentView; + struct yasl_input_event event; + while (yasl_surface_input_poll(surface, &event)) {} + TabletSample *sample = [TabletSample new]; + NSEvent *native = (NSEvent *)sample; + sample.type = NSEventTypeTabletProximity; + sample.enteringProximity = YES; + sample.tool = NSPointingDeviceTypePen; + [view tabletProximity:native]; + sample.type = NSEventTypeMouseMoved; + sample.subtype = NSEventSubtypeTabletPoint; + sample.locationInWindow = NSMakePoint(100, 120); + sample.sampleTilt = NSMakePoint(-0.5, 0.5); + [view mouseMoved:native]; + struct yasl_mouse_event ev = next(surface); + assert(ev.device == YASL_PTR_DEV_PEN && ev.kind == YASL_PTR_MOTION && !ev.pen.pressure); + assert(ev.pen.tilt_x == -45 && ev.pen.tilt_y == -45); + sample.type = NSEventTypeLeftMouseDown; + sample.samplePressure = 0.5; + [view mouseDown:native]; + ev = next(surface); + assert(ev.device == YASL_PTR_DEV_PEN && ev.kind == YASL_PTR_PRESS && ev.button == 0); + assert(ev.pen.pressure == 32767); + struct yasl_vector2i position = ev.pos; + /* Native tablet events cover stationary pressure changes. */ + sample.type = NSEventTypeTabletPoint; + sample.subtype = NSEventSubtypeMouseEvent; + sample.buttonMask = NSEventButtonMaskPenTip; + sample.samplePressure = 1.5; + [view tabletPoint:native]; + ev = next(surface); + assert(ev.kind == YASL_PTR_MOTION && ev.pen.pressure == 65535); + assert(ev.pos.x == position.x && ev.pos.y == position.y); + sample.samplePressure = 0; /* Zero does not release a held tip button. */ + [view tabletPoint:native]; + assert(next(surface).kind == YASL_PTR_MOTION); + sample.buttonMask = 0; + [view tabletPoint:native]; + ev = next(surface); + assert(ev.kind == YASL_PTR_RELEASE && !ev.pen.pressure && ev.device == YASL_PTR_DEV_PEN); + sample.type = NSEventTypeTabletProximity; + sample.enteringProximity = NO; + [view tabletProximity:native]; + assert(next(surface).kind == YASL_PTR_LEAVE); + sample.enteringProximity = YES; + sample.tool = NSPointingDeviceTypeEraser; + [view tabletProximity:native]; + sample.type = NSEventTypeTabletPoint; + sample.samplePressure = 0.75; + sample.buttonMask = NSEventButtonMaskPenTip; + [view tabletPoint:native]; + ev = next(surface); + assert(ev.device == YASL_PTR_DEV_ERASER && ev.kind == YASL_PTR_PRESS && ev.pen.pressure > 0); + sample.type = NSEventTypeTabletProximity; + sample.enteringProximity = NO; + [view tabletProximity:native]; + ev = next(surface); + assert(ev.device == YASL_PTR_DEV_ERASER && ev.kind == YASL_PTR_LEAVE && !ev.pen.pressure); + sample.type = NSEventTypeMouseMoved; + [view mouseMoved:native]; + ev = next(surface); + assert(ev.device == YASL_PTR_DEV_MOUSE && !ev.pen.pressure); + [sample release]; + yasl_surface_destroy(surface); + yasl_display_close(display); + } + return 0; +} -- cgit v1.2.3