summaryrefslogtreecommitdiff
path: root/src/cocoa/window.m
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-13 13:44:13 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-13 13:44:13 +0300
commitc059e3aa7f93da470f052a5cd8ee0d8dc969e679 (patch)
tree63ffa378287ed2c606eda2e940501b9edc3d2b8b /src/cocoa/window.m
parent87d0bc50d53dc216efb2b04495f090616318bc5c (diff)
rename and simplification
Diffstat (limited to 'src/cocoa/window.m')
-rw-r--r--src/cocoa/window.m2226
1 files changed, 0 insertions, 2226 deletions
diff --git a/src/cocoa/window.m b/src/cocoa/window.m
deleted file mode 100644
index 028ba64..0000000
--- a/src/cocoa/window.m
+++ /dev/null
@@ -1,2226 +0,0 @@
-#import <AppKit/AppKit.h>
-#import <Metal/Metal.h>
-#import <QuartzCore/QuartzCore.h>
-
-#include "cocoa/api.h"
-#include "yasl/native/cocoa.h"
-#include "common/timing.h"
-#include "common/loop.h"
-#include "cocoa/keycodes.h"
-#include "common/keyboard.h"
-
-#include <assert.h>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define LINK_IDLE_FRAMES 8
-#define SCROLL_POINTS_PER_LINE 10.0
-
-enum {
- DEVICE_LCTL = 0x0001,
- DEVICE_LSHIFT = 0x0002,
- DEVICE_RSHIFT = 0x0004,
- DEVICE_LCMD = 0x0008,
- DEVICE_RCMD = 0x0010,
- DEVICE_LALT = 0x0020,
- DEVICE_RALT = 0x0040,
- DEVICE_RCTL = 0x2000,
-};
-
-@interface YaslView : NSView <NSTextInputClient>
-@property (nonatomic, assign) struct cocoa_surface *surface;
-@end
-
-@interface YaslAppDelegate : NSObject <NSApplicationDelegate>
-@end
-
-@interface YaslWindow : NSWindow
-@property (nonatomic, assign) struct cocoa_surface *surface;
-@end
-
-@interface YaslWindowController : NSWindowController <NSWindowDelegate>
-@property (nonatomic, assign) struct cocoa_surface *surface;
-@property (nonatomic, assign) BOOL fullscreenTransition;
-@end
-
-#include "common/input.h"
-
-struct cocoa_surface {
- struct yasl_surface base;
- YaslWindow *window;
- YaslWindowController *controller;
- YaslView *view;
- CAMetalLayer *layer;
- id<MTLCommandQueue> software_queue;
- id<MTLBuffer> software_buffer;
- NSUInteger software_buffer_capacity;
- CADisplayLink *link;
-
- struct cocoa_surface *parent;
- bool is_popup;
- struct yasl_vector2i content_offset;
-
- const struct yasl_surface_cbs *cbs;
- void *ud;
- uint64_t frames_skipped;
-
- char *preedit;
- struct yasl_rect2i ime_caret;
-
- struct yasl_vector2i size;
- uint32_t scale_120;
- uint32_t bg_color;
- uint32_t *pixels;
- size_t pixel_capacity;
-
- id<CAMetalDrawable> drawable;
- bool transaction;
- struct yasl_frame frame;
- struct yasl_damage_list damage;
- bool did_present;
- unsigned idle_frames;
- bool want_draw;
- bool frame_due;
- bool unpaced;
- bool rendering;
- bool closed;
- bool destroyed;
- struct cocoa_surface *dead_next;
-
- struct input_queue input;
- struct yasl_vector2i pointer;
- double scroll_x;
- uint32_t state;
- uint32_t mods;
- uint32_t buttons;
- enum yasl_cursor cursor;
- enum yasl_pointer_device tablet_device;
- bool tablet_down;
-
- char title[160];
- struct yasl_frame_clock clock;
-};
-
-struct cocoa_display {
- struct yasl_display base;
- struct cocoa_surface **windows;
- int count;
- int capacity;
- const struct yasl_display_cbs *cbs;
- void *ud;
- char *app_id;
- struct yasl_loop *loop;
- id key_up_monitor;
- NSCursor *hidden_cursor;
- YaslAppDelegate *delegate;
- NSPoint cascade;
- struct cocoa_surface *dead;
- struct {
- bool active;
- struct cocoa_surface *origin;
- struct cocoa_surface *carried;
- struct yasl_vector2i carry_offset;
- struct cocoa_surface *hover;
- struct yasl_vector2i hover_pos;
- struct yasl_drag_listener listener;
- void *ud;
- NSImage *image;
- NSWindow *image_window;
- struct yasl_vector2i image_size;
- struct yasl_vector2i image_hotspot;
- } drag;
- int depth;
- bool open;
- bool launched;
- bool pumping;
- bool owns_loop;
-};
-
-static struct cocoa_display darwin_ctx;
-
-static void drag_end(struct cocoa_display *d, enum yasl_drag_end how)
-{
- if (!d || !d->drag.active) return;
- struct yasl_drag_listener l = d->drag.listener;
- void *ud = d->drag.ud;
- struct cocoa_surface *hover = d->drag.hover;
- struct yasl_vector2i pos = d->drag.hover_pos;
-
- d->drag.active = false;
- d->drag.listener = (struct yasl_drag_listener){0};
- d->drag.hover = NULL;
- d->drag.origin = NULL;
- d->drag.carried = NULL;
-
- if (d->drag.image_window) {
- [d->drag.image_window close];
- [d->drag.image_window release];
- d->drag.image_window = nil;
- }
- [d->drag.image release];
- d->drag.image = nil;
-
- if (hover && !hover->destroyed && hover->cbs && hover->cbs->drag) {
- if (how == YASL_DRAG_DROPPED)
- hover->cbs->drag(&hover->base, YASL_DRAG_DROP, pos, hover->ud);
- else
- hover->cbs->drag(&hover->base, YASL_DRAG_LEAVE, pos, hover->ud);
- }
- if (l.end)
- l.end(ud, how);
-}
-
-static void render_and_commit(struct cocoa_surface *w);
-static void reap_dead(struct cocoa_display *d);
-
-static void set_state(struct cocoa_surface *w, uint32_t bit, bool on)
-{
- if (!w || w->closed)
- return;
- uint32_t next = on ? (w->state | bit) : (w->state & ~bit);
- if (next == w->state)
- return;
- w->state = next;
- darwin_ctx.depth++;
- if (w->cbs->state)
- w->cbs->state(&w->base, w->state, w->ud);
- darwin_ctx.depth--;
-}
-
-static void check_pointer_reset(struct cocoa_surface *w)
-{
- if (!w || w->closed) return;
- if (w->buttons != 0 || w->tablet_down) {
- struct yasl_input_event ev = {
- .kind = YASL_INPUT_RESET,
- .surface = &w->base,
- };
- input_queue_push(&w->input, &ev);
- w->buttons = 0;
- w->tablet_down = false;
- }
-}
-
-static void push_focus(struct cocoa_surface *w, bool focused)
-{
- if (!w || w->closed)
- return;
- if (!focused)
- check_pointer_reset(w);
- struct yasl_input_event ev = {
- .kind = YASL_INPUT_FOCUS,
- .surface = &w->base,
- .focus = { .focused = focused },
- };
- input_queue_push(&w->input, &ev);
- darwin_ctx.depth++;
- set_state(w, YASL_STATE_ACTIVATED, focused);
- cocoa_surface_schedule_redraw(w);
- darwin_ctx.depth--;
-}
-
-static void sync_size(struct cocoa_surface *w, bool force)
-{
- if (!w || !w->layer || w->closed)
- return;
- uint64_t start = yasl_loop_now_ns();
- NSRect bounds = w->view.bounds;
- CGFloat scale = w->window.backingScaleFactor;
- if (scale < 1.0)
- scale = 1.0;
- struct yasl_vector2i size = {
- .x = (int)lround(bounds.size.width * scale),
- .y = (int)lround(bounds.size.height * scale),
- };
- uint32_t scale_120 = (uint32_t)lround(scale * YASL_SCALE_UNIT);
- bool rescaled = force || scale_120 != w->scale_120;
- bool resized = force || (size.x != w->size.x || size.y != w->size.y);
- if (!rescaled && !resized)
- return;
- darwin_ctx.depth++;
- w->size = size;
- w->scale_120 = scale_120;
- w->layer.contentsScale = scale;
- w->layer.drawableSize = CGSizeMake(size.x, size.y);
- if (w->cbs->configure)
- w->cbs->configure(&w->base, (struct yasl_geometry){
- .size = size, .scale_120 = scale_120,
- }, w->ud);
- cocoa_surface_schedule_redraw(w);
- if (w->view.inLiveResize)
- render_and_commit(w);
- yasl_clock_note_resize(&w->clock, yasl_loop_now_ns() - start);
- darwin_ctx.depth--;
-}
-
-static unsigned cocoa_mods(NSEventModifierFlags m)
-{
- unsigned mods = 0;
- if (m & NSEventModifierFlagShift) mods |= YASL_MOD_SHIFT;
- if (m & NSEventModifierFlagOption) mods |= YASL_MOD_ALT;
- if (m & NSEventModifierFlagControl) mods |= YASL_MOD_CTRL;
- if (m & NSEventModifierFlagCommand) mods |= YASL_MOD_SUPER;
- if (m & NSEventModifierFlagCapsLock) mods |= YASL_MOD_CAPS;
- return mods;
-}
-
-/* Decode a single Unicode scalar, never a lone UTF-16 surrogate. Layout labels
- * are optional: multi-scalar mappings and native function-key characters are 0. */
-static uint32_t cocoa_key_label(NSString *characters)
-{
- if (!characters.length) return 0;
- unichar first = [characters characterAtIndex:0];
- if (characters.length == 1)
- return yasl_key_label(first);
- if (characters.length == 2 && first >= 0xd800 && first <= 0xdbff) {
- unichar second = [characters characterAtIndex:1];
- if (second >= 0xdc00 && second <= 0xdfff)
- return yasl_key_label(0x10000 + ((first - 0xd800) << 10) + second - 0xdc00);
- }
- return 0;
-}
-
-static struct yasl_key_event cocoa_key_event(NSEvent *ev, enum yasl_key_action action)
-{
- struct yasl_key_event k = {
- .key = yasl_key_from_cocoa(ev.keyCode),
- .action = action,
- .mods = cocoa_mods(ev.modifierFlags),
- .native_code = ev.keyCode,
- };
- /* characters properties are invalid on flagsChanged events. */
- if (ev.type == NSEventTypeKeyDown || ev.type == NSEventTypeKeyUp) {
- k.codepoint = cocoa_key_label([ev charactersByApplyingModifiers:0]);
- k.shifted = cocoa_key_label([ev charactersByApplyingModifiers:NSEventModifierFlagShift]);
- }
- return k;
-}
-
-static void on_key_event(struct cocoa_surface *w, NSEvent *ev, enum yasl_key_action action)
-{
- w->mods = cocoa_mods(ev.modifierFlags);
- struct yasl_key_event k = cocoa_key_event(ev, action);
- struct yasl_input_event input_ev = {
- .kind = YASL_INPUT_KEY,
- .surface = &w->base,
- .key = k,
- };
- input_queue_push(&w->input, &input_ev);
-}
-
-static void on_text_input(struct cocoa_surface *w, const char *text, size_t len)
-{
- if (len) {
- struct yasl_input_event ev = {
- .kind = YASL_INPUT_TEXT,
- .surface = &w->base,
- .text = { .text = text, .len = len },
- };
- input_queue_push(&w->input, &ev);
- }
-}
-
-static bool modifier_down(NSEvent *ev)
-{
- NSEventModifierFlags f = ev.modifierFlags;
- switch (ev.keyCode) {
- case kVK_Shift: return f & DEVICE_LSHIFT;
- case kVK_RightShift: return f & DEVICE_RSHIFT;
- case kVK_Control: return f & DEVICE_LCTL;
- case kVK_RightControl: return f & DEVICE_RCTL;
- case kVK_Option: return f & DEVICE_LALT;
- case kVK_RightOption: return f & DEVICE_RALT;
- case kVK_Command: return f & DEVICE_LCMD;
- case kVK_RightCommand: return f & DEVICE_RCMD;
- case kVK_CapsLock: return f & NSEventModifierFlagCapsLock;
- default: return false;
- }
-}
-
-static int cocoa_button(NSInteger number)
-{
- switch (number) {
- case 0: return 0;
- case 1: return 2;
- case 2: return 1;
- default: return -1;
- }
-}
-
-static struct yasl_vector2i event_pos(struct cocoa_surface *w, NSEvent *ev)
-{
- NSPoint p = [w->view convertPoint:ev.locationInWindow fromView:nil];
- CGFloat scale = w->window.backingScaleFactor;
- if (scale < 1.0)
- scale = 1.0;
- return (struct yasl_vector2i){
- .x = (int)floor(p.x * scale),
- .y = (int)floor((w->view.bounds.size.height - p.y) * scale),
- };
-}
-
-static void update_drag_hover(struct cocoa_display *d, NSPoint screenPoint)
-{
- if (!d || !d->drag.active) return;
-
- NSInteger excludeNum = 0;
- if (d->drag.carried && d->drag.carried->window)
- excludeNum = d->drag.carried->window.windowNumber;
- else if (d->drag.image_window)
- excludeNum = d->drag.image_window.windowNumber;
-
- NSInteger winNum = [NSWindow windowNumberAtPoint:screenPoint belowWindowWithWindowNumber:excludeNum];
- struct cocoa_surface *target = NULL;
- struct yasl_vector2i pos = {0};
-
- if (winNum > 0) {
- NSWindow *nswin = [NSApp windowWithWindowNumber:winNum];
- if (nswin && [nswin isKindOfClass:[YaslWindow class]]) {
- struct cocoa_surface *candidate = ((YaslWindow *)nswin).surface;
- if (candidate && candidate != d->drag.carried && !candidate->closed && candidate->view) {
- NSPoint winPoint = [candidate->window convertPointFromScreen:screenPoint];
- NSPoint viewPoint = [candidate->view convertPoint:winPoint fromView:nil];
- if (NSPointInRect(viewPoint, candidate->view.bounds)) {
- CGFloat scale = candidate->window.backingScaleFactor;
- if (scale < 1.0) scale = 1.0;
- pos.x = (int)floor(viewPoint.x * scale);
- pos.y = (int)floor((candidate->view.bounds.size.height - viewPoint.y) * scale);
- target = candidate;
- }
- }
- }
- if (!target) {
- for (int i = 0; i < d->count; i++) {
- struct cocoa_surface *s = d->windows[i];
- if (s != d->drag.carried && !s->closed && s->window && s->window.windowNumber == winNum && s->view) {
- NSPoint winPoint = [s->window convertPointFromScreen:screenPoint];
- NSPoint viewPoint = [s->view convertPoint:winPoint fromView:nil];
- if (NSPointInRect(viewPoint, s->view.bounds)) {
- CGFloat scale = s->window.backingScaleFactor;
- if (scale < 1.0) scale = 1.0;
- pos.x = (int)floor(viewPoint.x * scale);
- pos.y = (int)floor((s->view.bounds.size.height - viewPoint.y) * scale);
- target = s;
- }
- break;
- }
- }
- }
- }
-
- if (target != d->drag.hover) {
- if (d->drag.hover) {
- struct cocoa_surface *old = d->drag.hover;
- struct yasl_vector2i old_pos = d->drag.hover_pos;
- d->drag.hover = NULL;
- if (old->cbs && old->cbs->drag && !old->destroyed)
- old->cbs->drag(&old->base, YASL_DRAG_LEAVE, old_pos, old->ud);
- if (!d->drag.active) return;
- }
- if (target && target->destroyed) target = NULL;
- d->drag.hover = target;
- d->drag.hover_pos = pos;
- if (target && !target->destroyed && target->cbs && target->cbs->drag)
- target->cbs->drag(&target->base, YASL_DRAG_OVER, pos, target->ud);
- } else if (target && !target->destroyed) {
- d->drag.hover_pos = pos;
- if (target->cbs && target->cbs->drag)
- target->cbs->drag(&target->base, YASL_DRAG_OVER, pos, target->ud);
- }
-}
-
-static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_pointer_kind kind,
- int button)
-{
- if (!w || w->closed || button < 0)
- return;
- bool proximity = ev.type == NSEventTypeTabletProximity ||
- ev.subtype == NSEventSubtypeTabletProximity;
- if (proximity) {
- if (ev.isEnteringProximity) {
- w->tablet_device = ev.pointingDeviceType == NSPointingDeviceTypeEraser ?
- YASL_PTR_DEV_ERASER : ev.pointingDeviceType == NSPointingDeviceTypePen ?
- YASL_PTR_DEV_PEN : YASL_PTR_DEV_MOUSE;
- return;
- }
- kind = YASL_PTR_LEAVE;
- button = 3;
- }
- w->mods = cocoa_mods(ev.modifierFlags);
- if (kind != YASL_PTR_LEAVE)
- w->pointer = event_pos(w, ev);
-
- enum yasl_pointer_device device = YASL_PTR_DEV_MOUSE;
- struct yasl_pointer_pen pen = {0};
-
- if (kind == YASL_PTR_LEAVE && w->tablet_device != YASL_PTR_DEV_MOUSE) {
- device = w->tablet_device;
- w->tablet_down = false;
- } else if (ev.type == NSEventTypeTabletPoint || ev.subtype == NSEventSubtypeTabletPoint) {
- /* pointingDeviceType belongs to proximity events. Remember that
- * event's tool so erasers and zero-pressure hover stay classified. */
- device = w->tablet_device == YASL_PTR_DEV_ERASER ? YASL_PTR_DEV_ERASER : YASL_PTR_DEV_PEN;
- float press = (float)ev.pressure;
- if (press > 1.0f) press = 1.0f;
- if (press > 0.0f)
- pen.pressure = (uint16_t)(press * 65535.0f);
-
- NSPoint tilt = ev.tilt;
- pen.tilt_x = (int8_t)lround(fmax(-1, fmin(1, tilt.x)) * 90.0);
- pen.tilt_y = (int8_t)lround(fmax(-1, fmin(1, -tilt.y)) * 90.0);
- if (button == 0 && kind == YASL_PTR_PRESS) w->tablet_down = true;
- if (button == 0 && kind == YASL_PTR_RELEASE) {
- w->tablet_down = false;
- pen.pressure = 0;
- }
- }
-
- if (kind == YASL_PTR_PRESS && button >= 0 && button < 32)
- w->buttons |= (1u << button);
- else if (kind == YASL_PTR_RELEASE && button >= 0 && button < 32)
- w->buttons &= ~(1u << button);
-
- struct yasl_input_event input_ev = {
- .surface = &w->base,
- .kind = YASL_INPUT_MOUSE,
- .mouse = {
- .pos = w->pointer,
- .button = (int16_t)button,
- .kind = kind,
- .device = device,
- .mods = w->mods,
- .pen = pen,
- },
- };
- input_queue_push(&w->input, &input_ev);
- if (kind == YASL_PTR_MOTION && darwin_ctx.drag.active) {
- if (darwin_ctx.drag.carried) {
- cocoa_surface_place_under_pointer(darwin_ctx.drag.carried, darwin_ctx.drag.carry_offset);
- } else if (darwin_ctx.drag.image_window) {
- CGFloat scale = darwin_ctx.drag.origin && darwin_ctx.drag.origin->window
- ? darwin_ctx.drag.origin->window.backingScaleFactor : 1.0;
- if (scale < 1.0) scale = 1.0;
- NSPoint mouse = [NSEvent mouseLocation];
- CGFloat pt_h = darwin_ctx.drag.image_size.y / scale;
- [darwin_ctx.drag.image_window setFrameOrigin:NSMakePoint(
- mouse.x - darwin_ctx.drag.image_hotspot.x / scale,
- mouse.y - pt_h + darwin_ctx.drag.image_hotspot.y / scale)];
- }
- update_drag_hover(&darwin_ctx, [NSEvent mouseLocation]);
- } else if (kind == YASL_PTR_RELEASE && darwin_ctx.drag.active) {
- update_drag_hover(&darwin_ctx, [NSEvent mouseLocation]);
- drag_end(&darwin_ctx, YASL_DRAG_DROPPED);
- }
- if (proximity) {
- w->tablet_device = YASL_PTR_DEV_MOUSE;
- w->tablet_down = false;
- }
-}
-
-static NSCursor *cocoa_cursor(enum yasl_cursor cursor)
-{
- switch (cursor) {
- case YASL_CURSOR_TEXT: return [NSCursor IBeamCursor];
- case YASL_CURSOR_LINK: return [NSCursor pointingHandCursor];
- case YASL_CURSOR_CROSSHAIR: return [NSCursor crosshairCursor];
- case YASL_CURSOR_HIDDEN:
- if (!darwin_ctx.hidden_cursor) {
- NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(1, 1)];
- [image lockFocus];
- [[NSColor clearColor] set];
- NSRectFillUsingOperation(NSMakeRect(0, 0, 1, 1), NSCompositingOperationCopy);
- [image unlockFocus];
- darwin_ctx.hidden_cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSZeroPoint];
- [image release];
- }
- return darwin_ctx.hidden_cursor;
- case YASL_CURSOR_ARROW:
- default: return [NSCursor arrowCursor];
- }
-}
-
-@implementation YaslAppDelegate
-- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
-{
- (void)sender;
- if (darwin_ctx.cbs && darwin_ctx.cbs->quit)
- darwin_ctx.cbs->quit(darwin_ctx.ud);
- return NSTerminateCancel;
-}
-- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)visible
-{
- (void)sender;
- if (!visible && darwin_ctx.cbs && darwin_ctx.cbs->reopen)
- darwin_ctx.cbs->reopen(darwin_ctx.ud);
- return YES;
-}
-- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
-{
- (void)sender;
- return NO;
-}
-@end
-
-static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> sender)
-{
- NSPoint p = [view convertPoint:[sender draggingLocation] fromView:nil];
- NSWindow *win = view.window;
- CGFloat scale = win ? win.backingScaleFactor : 1.0;
- if (scale < 1.0) scale = 1.0;
- return (struct yasl_vector2i){
- .x = (int)floor(p.x * scale),
- .y = (int)floor((view.bounds.size.height - p.y) * scale),
- };
-}
-
-@implementation YaslView
-
-- (instancetype)initWithFrame:(NSRect)frameRect
-{
- self = [super initWithFrame:frameRect];
- if (self) {
- self.wantsLayer = YES;
- self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawNever;
- self.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft;
- [self registerForDraggedTypes:@[NSPasteboardTypeFileURL]];
- }
- return self;
-}
-
-- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
-{
- NSPasteboard *pb = [sender draggingPasteboard];
- if ([pb canReadItemWithDataConformingToTypes:@[NSPasteboardTypeFileURL]]) {
- struct cocoa_surface *w = self.surface;
- if (w && w->cbs && w->cbs->drop) {
- struct yasl_vector2i pos = dragging_pos(self, sender);
- w->cbs->drop(&w->base, YASL_DROP_OVER, pos, w->ud);
- }
- return NSDragOperationCopy;
- }
- return NSDragOperationNone;
-}
-
-- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender
-{
- return [self draggingEntered:sender];
-}
-
-- (void)draggingExited:(id<NSDraggingInfo>)sender
-{
- NSPasteboard *pb = [sender draggingPasteboard];
- if ([pb canReadItemWithDataConformingToTypes:@[NSPasteboardTypeFileURL]]) {
- struct cocoa_surface *w = self.surface;
- if (w && w->cbs && w->cbs->drop) {
- struct yasl_vector2i pos = dragging_pos(self, sender);
- w->cbs->drop(&w->base, YASL_DROP_LEAVE, pos, w->ud);
- }
- }
-}
-
-- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
-{
- struct cocoa_surface *w = self.surface;
- if (!w) return NO;
- struct yasl_vector2i pos = dragging_pos(self, sender);
- BOOL ok = NO;
-
- NSPasteboard *pb = [sender draggingPasteboard];
- NSArray<NSURL *> *urls = [pb readObjectsForClasses:@[[NSURL class]]
- options:@{NSPasteboardURLReadingFileURLsOnlyKey: @YES}];
- if (urls.count) {
- NSMutableString *result = [NSMutableString string];
- for (NSURL *url in urls) {
- [result appendString:url.absoluteString];
- [result appendString:@"\r\n"];
- }
- const char *utf8 = result.UTF8String;
- if (utf8 && strlen(utf8)) {
- struct yasl_input_event ev = {
- .kind = YASL_INPUT_DROP_FILES,
- .surface = &w->base,
- .drop_files = { .files = utf8, .len = strlen(utf8) },
- };
- input_queue_push(&w->input, &ev);
- cocoa_surface_schedule_redraw(w);
- ok = YES;
- }
- }
- if (w->cbs && w->cbs->drop)
- w->cbs->drop(&w->base, YASL_DROP_LEAVE, pos, w->ud);
- return ok;
-}
-
-- (CALayer *)makeBackingLayer
-{
- CAMetalLayer *layer = [CAMetalLayer layer];
- layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
- layer.framebufferOnly = YES;
- layer.allowsNextDrawableTimeout = YES;
- layer.opaque = YES;
- layer.contentsGravity = kCAGravityTopLeft;
- CGColorSpaceRef srgb = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
- layer.colorspace = srgb;
- CGColorSpaceRelease(srgb);
- return layer;
-}
-
-- (BOOL)isOpaque { return !self.surface || !self.surface->is_popup; }
-- (BOOL)acceptsFirstResponder { return YES; }
-- (BOOL)becomeFirstResponder { return YES; }
-- (BOOL)canBecomeKeyView { return YES; }
-- (BOOL)wantsUpdateLayer { return YES; }
-
-- (void)setFrameSize:(NSSize)size
-{
- [super setFrameSize:size];
- sync_size(self.surface, false);
-}
-
-- (void)viewDidChangeBackingProperties
-{
- [super viewDidChangeBackingProperties];
- sync_size(self.surface, false);
-}
-
-- (void)viewWillStartLiveResize
-{
- [super viewWillStartLiveResize];
- struct cocoa_surface *w = self.surface;
- if (w)
- w->layer.presentsWithTransaction = YES;
-}
-
-- (void)viewDidEndLiveResize
-{
- [super viewDidEndLiveResize];
- struct cocoa_surface *w = self.surface;
- if (!w)
- return;
- w->layer.presentsWithTransaction = NO;
- cocoa_surface_schedule_redraw(w);
-}
-
-- (void)displayLinkFired:(CADisplayLink *)link
-{
- struct cocoa_surface *w = self.surface;
- if (!w || w->closed)
- return;
- CFTimeInterval period = link.targetTimestamp - link.timestamp;
- if (period > 0.0 && period < 0.25)
- w->clock.refresh_ns = period * 1e9;
- w->frame_due = true;
- if (!w->want_draw || (w->state & YASL_STATE_SUSPENDED)) {
- if (++w->idle_frames >= LINK_IDLE_FRAMES)
- link.paused = YES;
- return;
- }
- w->idle_frames = 0;
- if (darwin_ctx.loop)
- yasl_loop_kick(darwin_ctx.loop);
-}
-
-- (void)updateTrackingAreas
-{
- [super updateTrackingAreas];
- if (self.trackingAreas.count > 0)
- return;
- NSTrackingArea *area = [[NSTrackingArea alloc]
- initWithRect:NSZeroRect
- options:NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited |
- NSTrackingCursorUpdate | NSTrackingActiveAlways |
- NSTrackingInVisibleRect
- owner:self
- userInfo:nil];
- [self addTrackingArea:area];
- [area release];
-}
-
-- (void)cursorUpdate:(NSEvent *)event
-{
- (void)event;
- struct cocoa_surface *w = self.surface;
- [cocoa_cursor(w ? w->cursor : YASL_CURSOR_ARROW) set];
-}
-
-- (void)keyDown:(NSEvent *)event
-{
- struct cocoa_surface *w = self.surface;
- if (!w) return;
- on_key_event(w, event, event.isARepeat ? YASL_KEY_REPEAT : YASL_KEY_PRESS);
- [self interpretKeyEvents:@[event]];
-}
-
-- (void)keyUp:(NSEvent *)event
-{
- struct cocoa_surface *w = self.surface;
- if (w)
- on_key_event(w, event, YASL_KEY_RELEASE);
-}
-
-- (void)flagsChanged:(NSEvent *)event
-{
- struct cocoa_surface *w = self.surface;
- if (!w) return;
- w->mods = cocoa_mods(event.modifierFlags);
- on_key_event(w, event, modifier_down(event) ? YASL_KEY_PRESS : YASL_KEY_RELEASE);
-}
-
-- (void)mouseDown:(NSEvent *)event
-{
- [self.window makeFirstResponder:self];
- push_pointer(self.surface, event, YASL_PTR_PRESS, cocoa_button(event.buttonNumber));
-}
-- (void)rightMouseDown:(NSEvent *)event { [self mouseDown:event]; }
-- (void)otherMouseDown:(NSEvent *)event { [self mouseDown:event]; }
-
-- (void)mouseUp:(NSEvent *)event
-{
- push_pointer(self.surface, event, YASL_PTR_RELEASE, cocoa_button(event.buttonNumber));
-}
-- (void)rightMouseUp:(NSEvent *)event { [self mouseUp:event]; }
-- (void)otherMouseUp:(NSEvent *)event { [self mouseUp:event]; }
-
-- (void)mouseMoved:(NSEvent *)event
-{
- push_pointer(self.surface, event, YASL_PTR_MOTION, 3);
-}
-
-- (void)mouseDragged:(NSEvent *)event
-{
- push_pointer(self.surface, event, YASL_PTR_MOTION, 0);
-}
-- (void)rightMouseDragged:(NSEvent *)event
-{
- push_pointer(self.surface, event, YASL_PTR_MOTION, 2);
-}
-- (void)otherMouseDragged:(NSEvent *)event
-{
- push_pointer(self.surface, event, YASL_PTR_MOTION, cocoa_button(event.buttonNumber));
-}
-
-- (void)mouseExited:(NSEvent *)event
-{
- push_pointer(self.surface, event, YASL_PTR_LEAVE, 3);
-}
-
-- (void)tabletProximity:(NSEvent *)event
-{
- push_pointer(self.surface, event, YASL_PTR_MOTION, 3);
-}
-
-- (void)tabletPoint:(NSEvent *)event
-{
- struct cocoa_surface *w = self.surface;
- if (!w) return;
- bool down = (event.buttonMask & NSEventButtonMaskPenTip) != 0;
- enum yasl_pointer_kind kind = down == w->tablet_down ? YASL_PTR_MOTION :
- down ? YASL_PTR_PRESS : YASL_PTR_RELEASE;
- push_pointer(w, event, kind, 0);
-}
-
-- (void)scrollWheel:(NSEvent *)event
-{
- struct cocoa_surface *w = self.surface;
- if (!w) return;
- double dx = -event.scrollingDeltaX, dy = -event.scrollingDeltaY;
- if (event.hasPreciseScrollingDeltas) {
- dx /= SCROLL_POINTS_PER_LINE;
- dy /= SCROLL_POINTS_PER_LINE;
- }
- if (!isfinite(dx) || !isfinite(dy) || (dx == 0 && dy == 0)) return;
- w->mods = cocoa_mods(event.modifierFlags);
- w->pointer = event_pos(w, event);
- struct yasl_input_event input_ev = {
- .kind = YASL_INPUT_MOUSE,
- .surface = &w->base,
- .mouse = {
- .pos = w->pointer,
- .kind = YASL_PTR_SCROLL,
- .mods = w->mods,
- .scroll = {dx, dy},
- },
- };
- input_queue_push(&w->input, &input_ev);
-}
-
-static NSString *plain_string(id string)
-{
- return [string isKindOfClass:[NSAttributedString class]]
- ? [(NSAttributedString *)string string] : (NSString *)string;
-}
-
-- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
-{
- (void)replacementRange;
- struct cocoa_surface *w = self.surface;
- if (!w) return;
- const char *utf8 = plain_string(string).UTF8String;
- size_t len = utf8 ? strlen(utf8) : 0;
- free(w->preedit);
- w->preedit = NULL;
- on_text_input(w, utf8, len);
- cocoa_surface_schedule_redraw(w);
-}
-
-- (void)doCommandBySelector:(SEL)selector { (void)selector; }
-
-- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange
- replacementRange:(NSRange)replacementRange
-{
- (void)selectedRange;
- (void)replacementRange;
- struct cocoa_surface *w = self.surface;
- if (!w) return;
- NSString *str = plain_string(string);
- const char *utf8 = str.length ? str.UTF8String : NULL;
- free(w->preedit);
- w->preedit = utf8 ? strdup(utf8) : NULL;
- cocoa_surface_schedule_redraw(w);
-}
-
-- (void)unmarkText
-{
- struct cocoa_surface *w = self.surface;
- if (!w || !w->preedit) return;
- free(w->preedit);
- w->preedit = NULL;
- cocoa_surface_schedule_redraw(w);
-}
-
-- (NSRange)selectedRange { return NSMakeRange(NSNotFound, 0); }
-- (NSRange)markedRange
-{
- struct cocoa_surface *w = self.surface;
- if (!w || !w->preedit)
- return NSMakeRange(NSNotFound, 0);
- return NSMakeRange(0, [NSString stringWithUTF8String:w->preedit].length);
-}
-- (BOOL)hasMarkedText { return self.surface && self.surface->preedit != NULL; }
-- (nullable NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range
- actualRange:(nullable NSRangePointer)actualRange
-{
- (void)range; (void)actualRange;
- return nil;
-}
-- (NSArray<NSAttributedStringKey> *)validAttributesForMarkedText { return @[]; }
-- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange
-{
- (void)range; (void)actualRange;
- struct cocoa_surface *w = self.surface;
- if (!w || !w->window) return NSZeroRect;
- CGFloat scale = w->window.backingScaleFactor;
- if (scale < 1.0) scale = 1.0;
- NSRect r = NSMakeRect(w->ime_caret.x / scale,
- self.bounds.size.height - (w->ime_caret.y + w->ime_caret.height) / scale,
- w->ime_caret.width / scale, w->ime_caret.height / scale);
- return [w->window convertRectToScreen:[self convertRect:r toView:nil]];
-}
-- (NSUInteger)characterIndexForPoint:(NSPoint)point { (void)point; return 0; }
-
-@end
-
-@implementation YaslWindow
-
-- (void)sendEvent:(NSEvent *)event
-{
- darwin_ctx.depth++;
- @try {
- [super sendEvent:event];
- } @finally {
- darwin_ctx.depth--;
- }
-}
-
-@end
-
-@implementation YaslWindowController
-
-- (void)windowDidBecomeKey:(NSNotification *)notification
-{
- (void)notification;
- if (self.surface) push_focus(self.surface, true);
-}
-
-- (void)windowDidResignKey:(NSNotification *)notification
-{
- (void)notification;
- if (self.surface) push_focus(self.surface, false);
-}
-
-- (void)windowDidChangeOcclusionState:(NSNotification *)notification
-{
- (void)notification;
- struct cocoa_surface *w = self.surface;
- if (!w) return;
- bool visible = (self.window.occlusionState & NSWindowOcclusionStateVisible) != 0;
- darwin_ctx.depth++;
- set_state(w, YASL_STATE_SUSPENDED, !visible);
- if (visible)
- cocoa_surface_schedule_redraw(w);
- else
- w->link.paused = YES;
- darwin_ctx.depth--;
-}
-
-- (void)windowWillEnterFullScreen:(NSNotification *)notification
-{
- (void)notification;
- self.fullscreenTransition = YES;
-}
-
-- (void)windowWillExitFullScreen:(NSNotification *)notification
-{
- (void)notification;
- self.fullscreenTransition = YES;
-}
-
-- (void)syncWindowState
-{
- self.fullscreenTransition = NO;
- struct cocoa_surface *w = self.surface;
- if (!w || w->closed)
- return;
- darwin_ctx.depth++;
- bool fullscreen = (self.window.styleMask & NSWindowStyleMaskFullScreen) != 0;
- set_state(w, YASL_STATE_FULLSCREEN, fullscreen);
- set_state(w, YASL_STATE_MAXIMIZED, !fullscreen && self.window.isZoomed);
- sync_size(w, false);
- darwin_ctx.depth--;
-}
-
-- (void)windowDidEnterFullScreen:(NSNotification *)notification { [self syncWindowState]; }
-- (void)windowDidExitFullScreen:(NSNotification *)notification { [self syncWindowState]; }
-- (void)windowDidFailToEnterFullScreen:(NSWindow *)window { (void)window; [self syncWindowState]; }
-- (void)windowDidFailToExitFullScreen:(NSWindow *)window { (void)window; [self syncWindowState]; }
-
-- (void)windowDidResize:(NSNotification *)notification
-{
- (void)notification;
- if (!self.fullscreenTransition)
- [self syncWindowState];
-}
-
-- (void)windowDidChangeScreen:(NSNotification *)notification
-{
- (void)notification;
- sync_size(self.surface, false);
-}
-
-- (void)windowWillClose:(NSNotification *)notification
-{
- (void)notification;
- struct cocoa_surface *w = self.surface;
- if (!w || w->closed)
- return;
- darwin_ctx.depth++;
- w->closed = true;
- [w->link invalidate];
- if (w->cbs->close)
- w->cbs->close(&w->base, w->ud);
- darwin_ctx.depth--;
-}
-
-@end
-
-bool cocoa_surface_present(struct cocoa_surface *w, const struct yasl_framebuffer *fb,
- const struct yasl_damage_list *damage)
-{
- if (!w || w->closed || !w->rendering || w->did_present || !fb || !damage ||
- fb->pixels != w->frame.target.pixels || fb->size.x != w->size.x ||
- fb->size.y != w->size.y || !w->drawable)
- return false;
- if (w->software_queue) {
- if (fb->pixels != w->pixels || fb->size.x != w->size.x || fb->size.y != w->size.y)
- return false;
- NSUInteger row_bytes = ((NSUInteger)w->size.x * 4 + 255) & ~(NSUInteger)255;
- NSUInteger length = row_bytes * (NSUInteger)w->size.y;
- if (!w->software_buffer || w->software_buffer_capacity < length) {
- [w->software_buffer release];
- w->software_buffer = [w->layer.device newBufferWithLength:length
- options:MTLResourceStorageModeShared];
- w->software_buffer_capacity = w->software_buffer ? length : 0;
- }
- id<MTLBuffer> buffer = w->software_buffer;
- if (!buffer) return false;
- for (int y = 0; y < w->size.y; y++)
- memcpy((char *)buffer.contents + (NSUInteger)y * row_bytes,
- w->pixels + (size_t)y * w->size.x, (size_t)w->size.x * 4);
- id<MTLCommandBuffer> command = [w->software_queue commandBuffer];
- id<MTLBlitCommandEncoder> blit = [command blitCommandEncoder];
- if (!blit) return false;
- id<CAMetalDrawable> drawable = w->drawable;
- [blit copyFromBuffer:buffer sourceOffset:0 sourceBytesPerRow:row_bytes
- sourceBytesPerImage:length sourceSize:MTLSizeMake(w->size.x, w->size.y, 1)
- toTexture:drawable.texture destinationSlice:0 destinationLevel:0
- destinationOrigin:MTLOriginMake(0, 0, 0)];
- [blit endEncoding];
- [command presentDrawable:drawable];
- [command commit];
- }
- yasl_clock_presented(&w->clock, yasl_loop_now_ns(), false);
- w->did_present = true;
- return true;
-}
-
-static void render_and_commit(struct cocoa_surface *w)
-{
- if (w->closed || !w->cbs->draw || w->rendering || w->size.x <= 0 || w->size.y <= 0 ||
- (w->state & YASL_STATE_SUSPENDED))
- return;
-
- size_t count = (size_t)w->size.x * (size_t)w->size.y;
- if (w->software_queue && count > w->pixel_capacity) {
- uint32_t *pixels = realloc(w->pixels, count * sizeof(*pixels));
- if (!pixels) return;
- w->pixels = pixels;
- w->pixel_capacity = count;
- }
- darwin_ctx.depth++;
- w->rendering = true;
- w->want_draw = false;
- w->frame_due = false;
- w->did_present = false;
- id<CAMetalDrawable> drawable = [w->layer nextDrawable];
- w->drawable = drawable;
- w->transaction = w->layer.presentsWithTransaction;
- w->damage = (struct yasl_damage_list){0};
- w->frame = (struct yasl_frame){
- .surface = &w->base,
- .target = {
- .generation = yasl_frame_begin(&w->base),
- .pixels = w->software_queue ? w->pixels : NULL,
- .size = w->size,
- },
- };
- if (drawable)
- w->cbs->draw(&w->base, &w->frame, w->ud);
- if (!w->closed && !w->did_present) {
- w->want_draw = true;
- w->frames_skipped++;
- }
- w->drawable = nil;
- w->rendering = false;
- darwin_ctx.depth--;
-}
-
-static void pump_window(struct cocoa_surface *w)
-{
- if (!w || w->closed || !w->want_draw || !w->cbs->draw)
- return;
- if (w->link && !w->frame_due && !w->unpaced)
- return;
- @autoreleasepool { render_and_commit(w); }
-}
-
-int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms)
-{
- assert([NSThread isMainThread]);
- if (!d || d->pumping)
- return -1;
- for (int i = 0; i < d->count; i++) {
- struct cocoa_surface *w = d->windows[i];
- if (w->input.count || w->input.reset_pending ||
- (w->want_draw && (!w->link || w->frame_due || w->unpaced))) {
- timeout_ms = 0;
- break;
- }
- }
- d->pumping = true;
- d->depth++;
- int count = 0;
- @try {
- if (d->loop) {
- int timer_to = yasl_loop_next_timeout_ms(d->loop);
- if (timer_to >= 0 && (timeout_ms < 0 || timer_to < timeout_ms)) {
- timeout_ms = timer_to;
- }
- }
-
- NSDate *limitDate;
- if (timeout_ms == 0)
- limitDate = [NSDate distantPast];
- else if (timeout_ms < 0)
- limitDate = [NSDate distantFuture];
- else
- limitDate = [NSDate dateWithTimeIntervalSinceNow:(double)timeout_ms / 1000.0];
-
- @autoreleasepool {
- NSEvent *ev = [NSApp nextEventMatchingMask:NSEventMaskAny
- untilDate:limitDate
- inMode:NSDefaultRunLoopMode
- dequeue:YES];
- if (ev) {
- count++;
- [NSApp sendEvent:ev];
- [NSApp updateWindows];
-
- while ((ev = [NSApp nextEventMatchingMask:NSEventMaskAny
- untilDate:[NSDate distantPast]
- inMode:NSDefaultRunLoopMode
- dequeue:YES])) {
- count++;
- [NSApp sendEvent:ev];
- [NSApp updateWindows];
- }
- }
- }
-
- if (d->loop) {
- yasl_loop_step(d->loop, 0);
- }
- } @finally {
- d->depth--;
- d->pumping = false;
- }
- return count;
-}
-
-void cocoa_display_flush(struct cocoa_display *d)
-{
- assert([NSThread isMainThread]);
- if (!d) return;
- d->depth++;
- for (int i = d->count - 1; i >= 0; i--) {
- if (i < d->count)
- pump_window(d->windows[i]);
- }
- @autoreleasepool {
- [NSApp updateWindows];
- }
- if (--d->depth == 0)
- reap_dead(d);
-}
-
-void cocoa_surface_schedule_redraw(struct cocoa_surface *w)
-{
- if (!w || w->closed) return;
- w->want_draw = true;
- w->idle_frames = 0;
- if (w->link.paused) {
- w->frame_due = true;
- w->link.paused = NO;
- }
- if (darwin_ctx.loop)
- yasl_loop_kick(darwin_ctx.loop);
-}
-
-static void app_init(const char *app_name)
-{
- if (darwin_ctx.launched) return;
- [NSApplication sharedApplication];
- [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
- NSWindow.allowsAutomaticWindowTabbing = NO;
- darwin_ctx.delegate = [[YaslAppDelegate alloc] init];
- NSApp.delegate = darwin_ctx.delegate;
-
- NSString *ns_name = app_name ? [NSString stringWithUTF8String:app_name] : @"YASL";
- NSMenu *menu = [[NSMenu alloc] init];
- NSMenuItem *app_item = [[NSMenuItem alloc] init];
- NSMenu *app_menu = [[NSMenu alloc] initWithTitle:ns_name];
- [app_menu addItemWithTitle:[NSString stringWithFormat:@"About %@", ns_name]
- action:@selector(orderFrontStandardAboutPanel:)
- keyEquivalent:@""];
- [app_menu addItem:[NSMenuItem separatorItem]];
- [app_menu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", ns_name]
- action:@selector(terminate:)
- keyEquivalent:@"q"];
- app_item.submenu = app_menu;
- [menu addItem:app_item];
- [app_menu release];
- [app_item release];
- NSApp.mainMenu = menu;
- [menu release];
-
- darwin_ctx.key_up_monitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp
- handler:^NSEvent *(NSEvent *event) {
- if (!(event.modifierFlags & NSEventModifierFlagCommand) || !event.window)
- return event;
- [event.window sendEvent:event];
- return nil;
- }];
- [NSApp finishLaunching];
- darwin_ctx.launched = true;
-}
-
-static void place_window(NSWindow *window)
-{
- if (!darwin_ctx.count) {
- [window center];
- NSRect frame = window.frame;
- darwin_ctx.cascade = NSMakePoint(frame.origin.x, frame.origin.y + frame.size.height);
- return;
- }
- darwin_ctx.cascade = [window cascadeTopLeftFromPoint:darwin_ctx.cascade];
-}
-
-struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec)
-{
- assert([NSThread isMainThread]);
- if (darwin_ctx.open) return NULL;
- const char *name = spec && spec->app_id ? spec->app_id : "YASL";
- @autoreleasepool { app_init(name); }
- darwin_ctx.base.ops = &yasl_cocoa_backend;
- darwin_ctx.base.native_handle = (__bridge void *)[NSApplication sharedApplication];
- darwin_ctx.open = true;
- if (!loop) {
- darwin_ctx.loop = yasl_loop_create();
- darwin_ctx.owns_loop = true;
- } else {
- darwin_ctx.loop = loop;
- darwin_ctx.owns_loop = false;
- }
- darwin_ctx.base.loop = darwin_ctx.loop;
- if (spec) {
- darwin_ctx.base.callbacks = spec->cbs ? *spec->cbs : (struct yasl_display_cbs){0};
- darwin_ctx.cbs = &darwin_ctx.base.callbacks;
- darwin_ctx.ud = spec->ud;
- if (spec->app_id) darwin_ctx.app_id = strdup(spec->app_id);
- }
- return &darwin_ctx;
-}
-
-void cocoa_display_close(struct cocoa_display *d)
-{
- if (!d) return;
- drag_end(d, YASL_DRAG_CANCELLED);
- assert(!d->count && "destroy every surface before its display");
- reap_dead(d);
- if (d->key_up_monitor) {
- [NSEvent removeMonitor:d->key_up_monitor];
- d->key_up_monitor = nil;
- }
- if (d->owns_loop && d->loop) {
- yasl_loop_destroy(d->loop);
- d->loop = NULL;
- d->base.loop = NULL;
- }
- NSApp.delegate = nil;
- [d->delegate release];
- d->delegate = nil;
- [d->hidden_cursor release];
- free(d->app_id);
- free(d->windows);
- *d = (struct cocoa_display){0};
-}
-
-uint32_t cocoa_display_caps(const struct cocoa_display *d)
-{
- return d ? (YASL_CAP_POPUPS | YASL_CAP_DRAG | YASL_CAP_DRAG_CARRY |
- YASL_CAP_PLACE_WINDOW | YASL_CAP_PRIMARY_SELECTION |
- YASL_CAP_CSD | YASL_CAP_NATIVE_GRAPHICS |
- YASL_CAP_FRAME_PACING | YASL_CAP_TABLET) : 0;
-}
-
-struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d,
- const struct yasl_surface_spec *spec, void *graphics_device)
-{
- assert([NSThread isMainThread]);
- if (!d || !d->open || !spec)
- return NULL;
- @autoreleasepool {
- if (darwin_ctx.count >= darwin_ctx.capacity) {
- int cap = darwin_ctx.capacity ? darwin_ctx.capacity * 2 : 16;
- struct cocoa_surface **wins = realloc(darwin_ctx.windows, (size_t)cap * sizeof(*wins));
- if (!wins) return NULL;
- darwin_ctx.windows = wins;
- darwin_ctx.capacity = cap;
- }
-
- struct cocoa_surface *w = calloc(1, sizeof(*w));
- if (!w) return NULL;
- w->base.display = &d->base;
- cocoa_surface_set_callbacks(w, NULL, NULL);
- w->clock.refresh_ns = YASL_REFRESH_DEFAULT_NS;
- const char *t = spec->title ? spec->title : "YASL";
- strncpy(w->title, t, sizeof(w->title) - 1);
-
- int win_w = spec->size.x > 0 ? spec->size.x : 800;
- int win_h = spec->size.y > 0 ? spec->size.y : 480;
- NSWindowStyleMask style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
- NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable;
- w->window = [[YaslWindow alloc] initWithContentRect:NSMakeRect(0, 0, win_w, win_h)
- styleMask:style
- backing:NSBackingStoreBuffered
- defer:NO];
- w->window.releasedWhenClosed = NO;
- w->base.native_handle = (__bridge void *)w->window;
- w->base.size = w->size;
- w->base.scale_120 = w->scale_120;
- w->controller = [[YaslWindowController alloc] initWithWindow:w->window];
- w->controller.surface = w;
- w->window.delegate = w->controller;
- w->window.acceptsMouseMovedEvents = YES;
- w->window.collectionBehavior = NSWindowCollectionBehaviorFullScreenPrimary;
- w->window.titlebarAppearsTransparent = YES;
- w->window.title = [NSString stringWithUTF8String:w->title] ?: @"YASL";
- w->window.colorSpace = [NSColorSpace sRGBColorSpace];
-
- w->view = [[YaslView alloc] initWithFrame:w->window.contentView.bounds];
- w->window.contentView = w->view;
- w->layer = (CAMetalLayer *)w->view.layer;
- if (graphics_device) {
- w->layer.device = (id<MTLDevice>)graphics_device;
- } else {
- id<MTLDevice> device = MTLCreateSystemDefaultDevice();
- w->layer.device = device;
- w->software_queue = [device newCommandQueue];
- [device release];
- if (!w->software_queue) {
- w->window.delegate = nil;
- w->controller.surface = NULL;
- [w->window close];
- [w->view release];
- [w->controller release];
- [w->window release];
- free(w);
- return NULL;
- }
- w->layer.framebufferOnly = NO;
- }
-
- w->link = [[w->view displayLinkWithTarget:w->view
- selector:@selector(displayLinkFired:)] retain];
- [w->link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
-
- place_window(w->window);
- w->window.surface = w;
- w->view.surface = w;
- darwin_ctx.windows[darwin_ctx.count++] = w;
-
- [w->window makeKeyAndOrderFront:nil];
- [w->window makeFirstResponder:w->view];
- [NSApp activate];
-
- sync_size(w, true);
- if (spec->tearing) {
- w->layer.displaySyncEnabled = NO;
- w->unpaced = true;
- }
- if (spec->fullscreen)
- [w->window toggleFullScreen:nil];
- return w;
- }
-}
-
-static void surface_free(struct cocoa_surface *w)
-{
- [w->link release];
- [w->software_buffer release];
- [w->software_queue release];
- [w->view release];
- [w->controller release];
- [w->window release];
- free(w->preedit);
- free(w->pixels);
- free(w);
-}
-
-static void reap_dead(struct cocoa_display *d)
-{
- assert(d->depth == 0);
- while (d->dead) {
- struct cocoa_surface *w = d->dead;
- d->dead = w->dead_next;
- surface_free(w);
- }
-}
-
-void cocoa_surface_destroy(struct cocoa_surface *w)
-{
- assert([NSThread isMainThread]);
- if (!w || w->destroyed) return;
- if (darwin_ctx.drag.active) {
- if (darwin_ctx.drag.origin == w || darwin_ctx.drag.carried == w)
- drag_end(&darwin_ctx, YASL_DRAG_CANCELLED);
- else if (darwin_ctx.drag.hover == w)
- darwin_ctx.drag.hover = NULL;
- }
- if (w->is_popup && w->parent && w->parent->window) {
- [w->parent->window removeChildWindow:w->window];
- w->parent = NULL;
- }
- for (int i = 0; i < darwin_ctx.count; i++) {
- if (darwin_ctx.windows[i]->parent == w) {
- if (w->window && darwin_ctx.windows[i]->window)
- [w->window removeChildWindow:darwin_ctx.windows[i]->window];
- darwin_ctx.windows[i]->parent = NULL;
- }
- }
- w->buttons = 0;
- w->tablet_down = false;
- darwin_ctx.depth++;
- bool native_closed = w->closed;
- static const struct yasl_surface_cbs no_callbacks = {0};
- w->destroyed = true;
- w->closed = true;
- w->cbs = &no_callbacks;
- w->ud = NULL;
- [w->link invalidate];
- w->window.delegate = nil;
- w->controller.surface = NULL;
- w->window.surface = NULL;
- w->view.surface = NULL;
- if (!native_closed)
- [w->window close];
- input_queue_clear(&w->input);
- w->dead_next = darwin_ctx.dead;
- darwin_ctx.dead = w;
-
- for (int i = 0; i < darwin_ctx.count; i++) {
- if (darwin_ctx.windows[i] == w) {
- darwin_ctx.windows[i] = darwin_ctx.windows[--darwin_ctx.count];
- break;
- }
- }
- if (!darwin_ctx.count) {
- free(darwin_ctx.windows);
- darwin_ctx.windows = NULL;
- darwin_ctx.capacity = 0;
- darwin_ctx.cascade = NSZeroPoint;
- }
- darwin_ctx.depth--;
- if (darwin_ctx.loop)
- yasl_loop_kick(darwin_ctx.loop);
-}
-
-struct cocoa_display *cocoa_surface_display(const struct cocoa_surface *w)
-{
- return w ? &darwin_ctx : NULL;
-}
-
-void cocoa_surface_set_title(struct cocoa_surface *w, const char *title)
-{
- if (!w || w->closed || !title) return;
- strncpy(w->title, title, sizeof(w->title) - 1);
- w->window.title = [NSString stringWithUTF8String:w->title] ?: @"YASL";
-}
-
-void cocoa_surface_set_background_color(struct cocoa_surface *w, uint32_t bg)
-{
- if (!w || w->closed) return;
- w->bg_color = bg;
- double r = ((bg >> 16) & 0xff) / 255.0;
- double g = ((bg >> 8) & 0xff) / 255.0;
- double b = (bg & 0xff) / 255.0;
- w->window.backgroundColor = [NSColor colorWithSRGBRed:r green:g blue:b alpha:1.0];
- bool dark = 0.299 * r + 0.587 * g + 0.114 * b < 0.5;
- w->window.appearance = [NSAppearance appearanceNamed:
- dark ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua];
-}
-
-void cocoa_surface_set_cursor(struct cocoa_surface *w, enum yasl_cursor cursor)
-{
- if (!w || w->closed || cursor == w->cursor || (unsigned)cursor > YASL_CURSOR_HIDDEN) return;
- w->cursor = cursor;
- NSPoint p = [w->view convertPoint:w->window.mouseLocationOutsideOfEventStream fromView:nil];
- if (w->window.isKeyWindow && NSPointInRect(p, w->view.bounds))
- [cocoa_cursor(cursor) set];
-}
-
-bool cocoa_surface_place_under_pointer(struct cocoa_surface *w, struct yasl_vector2i at)
-{
- if (!w || w->closed || !w->window) return false;
- CGFloat scale = w->window.backingScaleFactor;
- if (scale < 1.0) scale = 1.0;
- NSPoint mouse = [NSEvent mouseLocation];
- NSRect content = [w->window contentRectForFrameRect:w->window.frame];
- NSPoint origin = NSMakePoint(mouse.x - at.x / scale,
- mouse.y - content.size.height + at.y / scale);
- [w->window setFrameOrigin:[w->window frameRectForContentRect:
- (NSRect){ .origin = origin, .size = content.size }].origin];
- return true;
-}
-
-void cocoa_surface_set_ime_caret(struct cocoa_surface *w, struct yasl_rect2i caret)
-{
- if (w) w->ime_caret = caret;
-}
-
-const char *cocoa_surface_ime_preedit(const struct cocoa_surface *w)
-{
- return w ? w->preedit : NULL;
-}
-
-bool cocoa_surface_ime_preedit_cursor(const struct cocoa_surface *w, int *begin, int *end)
-{
- (void)w; (void)begin; (void)end;
- return false;
-}
-
-static NSPasteboard *pasteboard_for_selection(enum yasl_selection sel)
-{
- switch (sel) {
- case YASL_CLIPBOARD:
- return [NSPasteboard generalPasteboard];
- case YASL_PRIMARY:
- return [NSPasteboard pasteboardWithName:@"dev.wsap.yasl.primary"];
- default:
- return nil;
- }
-}
-
-void cocoa_display_copy(struct cocoa_display *d, enum yasl_selection sel, const char *text)
-{
- (void)d;
- if (!text) return;
- NSPasteboard *pb = pasteboard_for_selection(sel);
- if (!pb) return;
- NSString *s = [NSString stringWithUTF8String:text];
- if (!s) return;
- [pb clearContents];
- [pb setString:s forType:NSPasteboardTypeString];
-}
-
-void cocoa_display_paste(struct cocoa_display *d, enum yasl_selection sel, struct cocoa_surface *target)
-{
- (void)d;
- if (!target) return;
- NSPasteboard *pb = pasteboard_for_selection(sel);
- if (!pb) return;
- NSString *s = [pb stringForType:NSPasteboardTypeString];
- const char *utf8 = s ? s.UTF8String : NULL;
- if (utf8) {
- struct yasl_input_event ev = {
- .kind = YASL_INPUT_PASTE,
- .surface = &target->base,
- .paste = { .text = utf8, .len = strlen(utf8) },
- };
- input_queue_push(&target->input, &ev);
- }
-}
-
-int cocoa_display_modifiers(const struct cocoa_display *d)
-{
- (void)d;
- return (int)cocoa_mods(NSEvent.modifierFlags);
-}
-
-struct yasl_geometry cocoa_surface_geometry(const struct cocoa_surface *w)
-{
- return (struct yasl_geometry){
- .size = w ? w->size : (struct yasl_vector2i){0},
- .scale_120 = w ? w->scale_120 : YASL_SCALE_UNIT,
- };
-}
-
-static bool popup_spec_valid(const struct cocoa_surface *parent,
- const struct yasl_popup_spec *spec)
-{
- if (!parent || !spec) return false;
- int parent_cx = parent->is_popup ? parent->content_offset.x : 0;
- int parent_cy = parent->is_popup ? parent->content_offset.y : 0;
- int64_t x = (int64_t)spec->anchor.x - parent_cx;
- int64_t y = (int64_t)spec->anchor.y - parent_cy;
- return x >= -32768 && x <= 32767 && y >= -32768 && y <= 32767 &&
- spec->anchor.width >= 0 && spec->anchor.height >= 0 &&
- spec->size.x > 0 && spec->size.y > 0 &&
- spec->size.x <= 32767 && spec->size.y <= 32767 &&
- spec->content_offset.x >= 0 && spec->content_offset.y >= 0 &&
- spec->content_size.x >= 0 && spec->content_size.y >= 0 &&
- (int64_t)spec->content_offset.x + spec->content_size.x <= spec->size.x &&
- (int64_t)spec->content_offset.y + spec->content_size.y <= spec->size.y;
-}
-
-static NSRect popup_calc_frame(struct cocoa_surface *parent, const struct yasl_popup_spec *spec)
-{
- CGFloat scale = parent->window ? parent->window.backingScaleFactor : 1.0;
- if (scale < 1.0) scale = 1.0;
-
- CGFloat pcx = (parent->is_popup ? parent->content_offset.x : 0) / scale;
- CGFloat pcy = (parent->is_popup ? parent->content_offset.y : 0) / scale;
-
- CGFloat pw = spec->size.x / scale;
- CGFloat ph = spec->size.y / scale;
- CGFloat ax = (spec->anchor.x / scale) - pcx;
- CGFloat ay = (spec->anchor.y / scale) - pcy;
- CGFloat aw = spec->anchor.width / scale;
- CGFloat ah = spec->anchor.height / scale;
- CGFloat ox = spec->content_offset.x / scale;
- CGFloat oy = spec->content_offset.y / scale;
-
- CGFloat top_left_x = spec->beside ? (ax + aw - ox) : (ax - ox);
- CGFloat top_left_y = spec->beside ? (ay - oy) : (ay + ah - oy);
-
- CGFloat view_h = parent->view.bounds.size.height;
- CGFloat view_y = view_h - top_left_y;
-
- NSPoint ptInView = NSMakePoint(top_left_x, view_y);
- NSPoint ptInWin = [parent->view convertPoint:ptInView toView:nil];
- NSPoint screen_tl = [parent->window convertPointToScreen:ptInWin];
-
- NSRect frame = NSMakeRect(screen_tl.x, screen_tl.y - ph, pw, ph);
-
- NSScreen *screen = parent->window.screen ?: [NSScreen mainScreen];
- if (screen) {
- NSRect visible = screen.visibleFrame;
- if (NSMaxX(frame) > NSMaxX(visible)) {
- if (spec->beside) {
- CGFloat flip_tl_x = ax - pw + ox;
- NSPoint flip_v = NSMakePoint(flip_tl_x, view_y);
- NSPoint flip_w = [parent->view convertPoint:flip_v toView:nil];
- NSPoint flip_s = [parent->window convertPointToScreen:flip_w];
- frame.origin.x = flip_s.x;
- }
- if (NSMaxX(frame) > NSMaxX(visible))
- frame.origin.x = NSMaxX(visible) - frame.size.width;
- }
- if (NSMinX(frame) < NSMinX(visible))
- frame.origin.x = NSMinX(visible);
-
- if (NSMinY(frame) < NSMinY(visible)) {
- if (!spec->beside) {
- CGFloat flip_top_y = ay - ph + oy;
- CGFloat flip_vy = view_h - flip_top_y;
- NSPoint flip_v = NSMakePoint(top_left_x, flip_vy);
- NSPoint flip_w = [parent->view convertPoint:flip_v toView:nil];
- NSPoint flip_s = [parent->window convertPointToScreen:flip_w];
- frame.origin.y = flip_s.y - ph;
- }
- if (NSMinY(frame) < NSMinY(visible))
- frame.origin.y = NSMinY(visible);
- }
- if (NSMaxY(frame) > NSMaxY(visible))
- frame.origin.y = NSMaxY(visible) - frame.size.height;
- }
- return frame;
-}
-
-struct cocoa_surface *cocoa_surface_create_popup(struct cocoa_surface *parent,
- const struct yasl_popup_spec *spec)
-{
- assert([NSThread isMainThread]);
- if (!parent || parent->closed || !parent->window || !parent->view ||
- !popup_spec_valid(parent, spec))
- return NULL;
-
- @autoreleasepool {
- if (darwin_ctx.count >= darwin_ctx.capacity) {
- int cap = darwin_ctx.capacity ? darwin_ctx.capacity * 2 : 16;
- struct cocoa_surface **wins = realloc(darwin_ctx.windows, (size_t)cap * sizeof(*wins));
- if (!wins) return NULL;
- darwin_ctx.windows = wins;
- darwin_ctx.capacity = cap;
- }
-
- struct cocoa_surface *w = calloc(1, sizeof(*w));
- if (!w) return NULL;
- w->base.display = parent->base.display;
- cocoa_surface_set_callbacks(w, NULL, NULL);
- w->clock.refresh_ns = parent->clock.refresh_ns ? parent->clock.refresh_ns : YASL_REFRESH_DEFAULT_NS;
- w->parent = parent;
- w->is_popup = true;
- w->content_offset = spec->content_offset;
-
- NSRect frame = popup_calc_frame(parent, spec);
- NSWindowStyleMask style = NSWindowStyleMaskBorderless;
- w->window = [[YaslWindow alloc] initWithContentRect:frame
- styleMask:style
- backing:NSBackingStoreBuffered
- defer:NO];
- w->window.releasedWhenClosed = NO;
- w->base.native_handle = (__bridge void *)w->window;
- w->base.size = w->size;
- w->base.scale_120 = w->scale_120;
- w->window.opaque = NO;
- w->window.backgroundColor = [NSColor clearColor];
- w->window.hasShadow = NO;
- w->controller = [[YaslWindowController alloc] initWithWindow:w->window];
- w->controller.surface = w;
- w->window.delegate = w->controller;
- w->window.acceptsMouseMovedEvents = YES;
- w->window.colorSpace = [NSColorSpace sRGBColorSpace];
-
- w->view = [[YaslView alloc] initWithFrame:w->window.contentView.bounds];
- w->window.contentView = w->view;
- w->layer = (CAMetalLayer *)w->view.layer;
- w->layer.opaque = NO;
- w->layer.device = parent->layer.device;
- if (parent->software_queue) {
- w->software_queue = [w->layer.device newCommandQueue];
- if (!w->software_queue) {
- w->window.delegate = nil;
- w->controller.surface = NULL;
- [w->view release];
- [w->controller release];
- [w->window release];
- free(w);
- return NULL;
- }
- w->layer.framebufferOnly = NO;
- } else {
- w->layer.framebufferOnly = parent->layer.framebufferOnly;
- }
-
- w->link = [[w->view displayLinkWithTarget:w->view
- selector:@selector(displayLinkFired:)] retain];
- [w->link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
-
- w->window.surface = w;
- w->view.surface = w;
- darwin_ctx.windows[darwin_ctx.count++] = w;
-
- [parent->window addChildWindow:w->window ordered:NSWindowAbove];
- [w->window orderFront:nil];
-
- sync_size(w, true);
- return w;
- }
-}
-
-bool cocoa_surface_popup_reposition(struct cocoa_surface *popup,
- const struct yasl_popup_spec *spec)
-{
- if (!popup || popup->closed || !popup->is_popup || !popup->parent ||
- !popup->parent->window || !popup->parent->view ||
- !popup_spec_valid(popup->parent, spec))
- return false;
-
- @autoreleasepool {
- popup->content_offset = spec->content_offset;
- NSRect frame = popup_calc_frame(popup->parent, spec);
- [popup->window setFrame:frame display:YES];
- sync_size(popup, false);
- return true;
- }
-}
-
-bool cocoa_surface_start_interactive_move(struct cocoa_surface *s)
-{
- if (!s || !s->window) return false;
- [s->window performWindowDragWithEvent:[NSApp currentEvent]];
- return true;
-}
-
-static NSImage *image_from_pixels(const uint32_t *pixels, int width, int height)
-{
- if (!pixels || width <= 0 || height <= 0)
- return nil;
- CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
- if (!cs) return nil;
- size_t bytes = (size_t)width * (size_t)height * 4;
- CFDataRef data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)pixels, (CFIndex)bytes);
- CGDataProviderRef provider = data ? CGDataProviderCreateWithCFData(data) : NULL;
- if (data)
- CFRelease(data);
- if (!provider) {
- CGColorSpaceRelease(cs);
- return nil;
- }
- CGImageRef cgImage = CGImageCreate((size_t)width, (size_t)height,
- 8, 32, (size_t)width * 4, cs,
- kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little,
- provider, NULL, NO, kCGRenderingIntentDefault);
- CGDataProviderRelease(provider);
- CGColorSpaceRelease(cs);
- if (!cgImage)
- return nil;
- NSImage *image = [[NSImage alloc] initWithCGImage:cgImage size:NSMakeSize(width, height)];
- CGImageRelease(cgImage);
- return image;
-}
-
-bool cocoa_display_drag_start(struct cocoa_display *d, struct cocoa_surface *origin,
- const struct yasl_drag_image *image,
- const struct yasl_drag_listener *l, void *ud)
-{
- if (!d || !origin || d->drag.active || !l)
- return false;
- d->drag.active = true;
- d->drag.origin = origin;
- d->drag.carried = NULL;
- d->drag.hover = NULL;
- d->drag.hover_pos = (struct yasl_vector2i){0};
- d->drag.listener = *l;
- d->drag.ud = ud;
- [d->drag.image release];
- d->drag.image = nil;
- if (d->drag.image_window) {
- [d->drag.image_window close];
- [d->drag.image_window release];
- d->drag.image_window = nil;
- }
- if (image && image->pixels && image->size.x > 0 && image->size.y > 0) {
- d->drag.image = image_from_pixels(image->pixels, image->size.x, image->size.y);
- d->drag.image_size = image->size;
- d->drag.image_hotspot = image->hotspot;
- if (d->drag.image) {
- CGFloat scale = origin->window ? origin->window.backingScaleFactor : 1.0;
- if (scale < 1.0) scale = 1.0;
- CGFloat pt_w = image->size.x / scale;
- CGFloat pt_h = image->size.y / scale;
- NSWindow *win = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, pt_w, pt_h)
- styleMask:NSWindowStyleMaskBorderless
- backing:NSBackingStoreBuffered
- defer:NO];
- win.releasedWhenClosed = NO;
- win.opaque = NO;
- win.backgroundColor = [NSColor clearColor];
- win.ignoresMouseEvents = YES;
- win.level = NSFloatingWindowLevel;
- win.hasShadow = YES;
- NSImageView *iv = [[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, pt_w, pt_h)];
- iv.image = d->drag.image;
- iv.imageScaling = NSImageScaleAxesIndependently;
- win.contentView = iv;
- [iv release];
- d->drag.image_window = win;
- NSPoint mouse = [NSEvent mouseLocation];
- [win setFrameOrigin:NSMakePoint(mouse.x - image->hotspot.x / scale,
- mouse.y - pt_h + image->hotspot.y / scale)];
- [win orderFront:nil];
- }
- }
- update_drag_hover(d, [NSEvent mouseLocation]);
- return true;
-}
-
-void cocoa_display_drag_cancel(struct cocoa_display *d)
-{
- drag_end(d, YASL_DRAG_CANCELLED);
-}
-
-bool cocoa_surface_drag_carry(struct cocoa_surface *w, struct yasl_vector2i offset)
-{
- if (!w || w->closed || !darwin_ctx.drag.active)
- return false;
- if (darwin_ctx.drag.image_window) {
- [darwin_ctx.drag.image_window close];
- [darwin_ctx.drag.image_window release];
- darwin_ctx.drag.image_window = nil;
- }
- darwin_ctx.drag.carried = w;
- darwin_ctx.drag.carry_offset = offset;
- return cocoa_surface_place_under_pointer(w, offset);
-}
-
-bool cocoa_surface_start_interactive_resize(struct cocoa_surface *s, enum yasl_resize_edge edge)
-{
- (void)s; (void)edge;
- return false;
-}
-
-bool cocoa_surface_show_window_menu(struct cocoa_surface *s, struct yasl_vector2i pos)
-{
- (void)s; (void)pos;
- return false;
-}
-
-void *cocoa_display_get_native_handle(struct cocoa_display *d)
-{
- return d ? (void *)NSApp : NULL;
-}
-
-void *cocoa_surface_get_native_handle(struct cocoa_surface *s)
-{
- return s ? (void *)s->window : NULL;
-}
-
-
-
-void cocoa_surface_set_callbacks(struct cocoa_surface *w,
- const struct yasl_surface_cbs *cbs, void *ud)
-{
- if (!w || w->closed) return;
- w->base.callbacks = cbs ? *cbs : (struct yasl_surface_cbs){0};
- w->cbs = &w->base.callbacks;
- w->ud = ud;
-}
-
-bool cocoa_surface_input_poll(struct cocoa_surface *w, struct yasl_input_event *ev)
-{
- if (!w || !ev || !input_queue_pop(&w->input, ev))
- return false;
- if (ev->kind == YASL_INPUT_RESET) {
- w->buttons = 0;
- w->tablet_down = false;
- w->mods = 0;
- if (w->preedit) {
- free(w->preedit);
- w->preedit = NULL;
- }
- }
- return true;
-}
-
-uint32_t cocoa_surface_state(const struct cocoa_surface *w)
-{
- return w ? w->state : 0;
-}
-
-struct yasl_timing cocoa_surface_timing(const struct cocoa_surface *w)
-{
- struct yasl_timing timing = {0};
- if (w) {
- timing.refresh_ns = w->clock.refresh_ns;
- timing.last_present_ns = w->clock.last_present_ns;
- timing.next_present_ns = yasl_clock_next_present_ns(&w->clock);
- timing.frames_skipped = w->frames_skipped;
- timing.source = w->clock.source;
- yasl_clock_resize_stats(&w->clock, &timing.resize);
- }
- return timing;
-}
-
-uint64_t cocoa_surface_frame_time_ns(struct cocoa_surface *w, uint64_t now)
-{
- return w ? yasl_clock_frame_timestamp_ns(&w->clock, now) : now;
-}
-
-static struct yasl_display *impl_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec)
-{
- struct cocoa_display *result = cocoa_display_open(loop, spec);
- return result ? &result->base : NULL;
-}
-
-static void impl_display_close(struct yasl_display *d)
-{
- cocoa_display_close((struct cocoa_display *)d);
-}
-
-static uint32_t impl_display_caps(const struct yasl_display *d)
-{
- return cocoa_display_caps((const struct cocoa_display *)d);
-}
-
-static int impl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms)
-{
- return cocoa_display_dispatch_timeout((struct cocoa_display *)d, timeout_ms);
-}
-
-static void impl_display_flush(struct yasl_display *d)
-{
- cocoa_display_flush((struct cocoa_display *)d);
-}
-
-static int impl_display_modifiers(const struct yasl_display *d)
-{
- return cocoa_display_modifiers((const struct cocoa_display *)d);
-}
-
-static void impl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text)
-{
- cocoa_display_copy((struct cocoa_display *)d, sel, text);
-}
-
-static void impl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target)
-{
- cocoa_display_paste((struct cocoa_display *)d, sel, (struct cocoa_surface *)target);
-}
-
-static bool impl_display_drag_start(struct yasl_display *d, struct yasl_surface *origin, const struct yasl_drag_image *image, const struct yasl_drag_listener *l, void *ud)
-{
- return cocoa_display_drag_start((struct cocoa_display *)d, (struct cocoa_surface *)origin, image, l, ud);
-}
-
-static void impl_display_drag_cancel(struct yasl_display *d)
-{
- cocoa_display_drag_cancel((struct cocoa_display *)d);
-}
-
-static struct yasl_surface *impl_surface_create_toplevel(struct yasl_display *d,
- const struct yasl_surface_spec *spec, void *graphics_device)
-{
- struct cocoa_surface *result = cocoa_surface_create_toplevel((struct cocoa_display *)d, spec, graphics_device);
- return result ? &result->base : NULL;
-}
-
-static struct yasl_surface *impl_surface_create_popup(struct yasl_surface *parent, const struct yasl_popup_spec *spec)
-{
- struct cocoa_surface *result = cocoa_surface_create_popup((struct cocoa_surface *)parent, spec);
- return result ? &result->base : NULL;
-}
-
-static bool impl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_popup_spec *spec)
-{
- return cocoa_surface_popup_reposition((struct cocoa_surface *)popup, spec);
-}
-
-static void impl_surface_destroy(struct yasl_surface *s)
-{
- cocoa_surface_destroy((struct cocoa_surface *)s);
-}
-
-static struct yasl_display *impl_surface_display(const struct yasl_surface *s)
-{
- struct cocoa_display *result = cocoa_surface_display((const struct cocoa_surface *)s);
- return result ? &result->base : NULL;
-}
-
-static void impl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_surface_cbs *cbs, void *ud)
-{
- cocoa_surface_set_callbacks((struct cocoa_surface *)s, cbs, ud);
-}
-
-static void impl_surface_set_title(struct yasl_surface *s, const char *title)
-{
- cocoa_surface_set_title((struct cocoa_surface *)s, title);
-}
-
-static void impl_surface_schedule_redraw(struct yasl_surface *s)
-{
- cocoa_surface_schedule_redraw((struct cocoa_surface *)s);
-}
-
-static bool impl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage)
-{
- return cocoa_surface_present((struct cocoa_surface *)s, fb, damage);
-}
-
-static bool impl_surface_input_poll(struct yasl_surface *s, struct yasl_input_event *ev)
-{
- return cocoa_surface_input_poll((struct cocoa_surface *)s, ev);
-}
-
-static struct yasl_geometry impl_surface_geometry(const struct yasl_surface *s)
-{
- return cocoa_surface_geometry((const struct cocoa_surface *)s);
-}
-
-static uint32_t impl_surface_state(const struct yasl_surface *s)
-{
- return cocoa_surface_state((const struct cocoa_surface *)s);
-}
-
-static struct yasl_timing impl_surface_timing(const struct yasl_surface *s)
-{
- return cocoa_surface_timing((const struct cocoa_surface *)s);
-}
-
-static uint64_t impl_surface_frame_time_ns(struct yasl_surface *s, uint64_t now)
-{
- return cocoa_surface_frame_time_ns((struct cocoa_surface *)s, now);
-}
-
-static void impl_surface_set_cursor(struct yasl_surface *s, enum yasl_cursor cursor)
-{
- cocoa_surface_set_cursor((struct cocoa_surface *)s, cursor);
-}
-
-static void impl_surface_set_background_color(struct yasl_surface *s, uint32_t bg)
-{
- cocoa_surface_set_background_color((struct cocoa_surface *)s, bg);
-}
-
-static const char *impl_surface_ime_preedit(const struct yasl_surface *s)
-{
- return cocoa_surface_ime_preedit((const struct cocoa_surface *)s);
-}
-
-static bool impl_surface_ime_preedit_cursor(const struct yasl_surface *s, int *begin, int *end)
-{
- return cocoa_surface_ime_preedit_cursor((const struct cocoa_surface *)s, begin, end);
-}
-
-static void impl_surface_set_ime_caret(struct yasl_surface *s, struct yasl_rect2i caret)
-{
- cocoa_surface_set_ime_caret((struct cocoa_surface *)s, caret);
-}
-
-static bool impl_surface_start_interactive_move(struct yasl_surface *s)
-{
- return cocoa_surface_start_interactive_move((struct cocoa_surface *)s);
-}
-
-static bool impl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_resize_edge edge)
-{
- return cocoa_surface_start_interactive_resize((struct cocoa_surface *)s, edge);
-}
-
-static bool impl_surface_show_window_menu(struct yasl_surface *s, struct yasl_vector2i pos)
-{
- return cocoa_surface_show_window_menu((struct cocoa_surface *)s, pos);
-}
-
-static bool impl_surface_drag_carry(struct yasl_surface *s, struct yasl_vector2i offset)
-{
- return cocoa_surface_drag_carry((struct cocoa_surface *)s, offset);
-}
-
-static bool impl_surface_place_under_pointer(struct yasl_surface *s, struct yasl_vector2i at)
-{
- return cocoa_surface_place_under_pointer((struct cocoa_surface *)s, at);
-}
-
-static void *impl_display_get_native_handle(struct yasl_display *d)
-{
- return cocoa_display_get_native_handle((struct cocoa_display *)d);
-}
-
-static void *impl_surface_get_native_handle(struct yasl_surface *s)
-{
- return cocoa_surface_get_native_handle((struct cocoa_surface *)s);
-}
-
-static bool impl_surface_get_native_frame(struct yasl_surface *s,
- const struct yasl_framebuffer *target, struct yasl_native_frame *out)
-{
- struct cocoa_surface *w = (struct cocoa_surface *)s;
- if (w->closed || !w->rendering || w->did_present || !w->drawable ||
- target->pixels != w->frame.target.pixels ||
- target->size.x != w->size.x || target->size.y != w->size.y)
- return false;
- *out = (struct yasl_native_frame){(void *)w->drawable, w->transaction};
- return true;
-}
-
-const struct yasl_backend_ops yasl_cocoa_backend = {
- .type = YASL_BACKEND_COCOA,
- .display_open = impl_display_open,
- .display_close = impl_display_close,
- .display_caps = impl_display_caps,
- .display_dispatch_timeout = impl_display_dispatch_timeout,
- .display_flush = impl_display_flush,
- .display_modifiers = impl_display_modifiers,
- .display_copy = impl_display_copy,
- .display_paste = impl_display_paste,
- .display_drag_start = impl_display_drag_start,
- .display_drag_cancel = impl_display_drag_cancel,
- .surface_create_toplevel = impl_surface_create_toplevel,
- .surface_create_popup = impl_surface_create_popup,
- .surface_popup_reposition = impl_surface_popup_reposition,
- .surface_destroy = impl_surface_destroy,
- .surface_display = impl_surface_display,
- .surface_set_callbacks = impl_surface_set_callbacks,
- .surface_set_title = impl_surface_set_title,
- .surface_schedule_redraw = impl_surface_schedule_redraw,
- .surface_present = impl_surface_present,
- .surface_input_poll = impl_surface_input_poll,
- .surface_geometry = impl_surface_geometry,
- .surface_state = impl_surface_state,
- .surface_timing = impl_surface_timing,
- .surface_frame_time_ns = impl_surface_frame_time_ns,
- .surface_set_cursor = impl_surface_set_cursor,
- .surface_set_background_color = impl_surface_set_background_color,
- .surface_ime_preedit = impl_surface_ime_preedit,
- .surface_ime_preedit_cursor = impl_surface_ime_preedit_cursor,
- .surface_set_ime_caret = impl_surface_set_ime_caret,
- .surface_start_interactive_move = impl_surface_start_interactive_move,
- .surface_start_interactive_resize = impl_surface_start_interactive_resize,
- .surface_show_window_menu = impl_surface_show_window_menu,
- .surface_drag_carry = impl_surface_drag_carry,
- .surface_place_under_pointer = impl_surface_place_under_pointer,
- .display_get_native_handle = impl_display_get_native_handle,
- .surface_get_native_handle = impl_surface_get_native_handle,
- .surface_get_native_frame = impl_surface_get_native_frame,
-};
-
-NSApplication *yasl_cocoa_display_get_application(struct yasl_display *d)
-{
- return (d && yasl_display_backend(d) == YASL_BACKEND_COCOA) ?
- (NSApplication *)yasl_display_get_native_handle(d) : nil;
-}
-
-NSWindow *yasl_cocoa_surface_get_window(struct yasl_surface *s)
-{
- return (s && s->display && yasl_display_backend(s->display) == YASL_BACKEND_COCOA) ?
- (NSWindow *)yasl_surface_get_native_handle(s) : nil;
-}
-
-NSView *yasl_cocoa_surface_get_view(struct yasl_surface *s)
-{
- NSWindow *w = yasl_cocoa_surface_get_window(s);
- return w ? w.contentView : nil;
-}