diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-13 13:44:13 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-13 13:44:13 +0300 |
| commit | c059e3aa7f93da470f052a5cd8ee0d8dc969e679 (patch) | |
| tree | 63ffa378287ed2c606eda2e940501b9edc3d2b8b /src/cocoa | |
| parent | 87d0bc50d53dc216efb2b04495f090616318bc5c (diff) | |
rename and simplification
Diffstat (limited to 'src/cocoa')
| -rw-r--r-- | src/cocoa/api.h | 47 | ||||
| -rw-r--r-- | src/cocoa/keycodes.h | 130 | ||||
| -rw-r--r-- | src/cocoa/meson.build | 1 | ||||
| -rw-r--r-- | src/cocoa/window.m | 2226 |
4 files changed, 0 insertions, 2404 deletions
diff --git a/src/cocoa/api.h b/src/cocoa/api.h deleted file mode 100644 index 59372cf..0000000 --- a/src/cocoa/api.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef YASL_COCOA_API_H -#define YASL_COCOA_API_H - -#include "common/backend.h" - -struct cocoa_display; -struct cocoa_surface; - -struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec); -void cocoa_display_close(struct cocoa_display *d); -uint32_t cocoa_display_caps(const struct cocoa_display *d); -int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms); -void cocoa_display_flush(struct cocoa_display *d); -int cocoa_display_modifiers(const struct cocoa_display *d); -void cocoa_display_copy(struct cocoa_display *d, enum yasl_selection sel, const char *text); -void cocoa_display_paste(struct cocoa_display *d, enum yasl_selection sel, struct cocoa_surface *target); -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); -void cocoa_display_drag_cancel(struct cocoa_display *d); -struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d, - const struct yasl_surface_spec *spec, void *graphics_device); -struct cocoa_surface *cocoa_surface_create_popup(struct cocoa_surface *parent, const struct yasl_popup_spec *spec); -bool cocoa_surface_popup_reposition(struct cocoa_surface *popup, const struct yasl_popup_spec *spec); -void cocoa_surface_destroy(struct cocoa_surface *s); -struct cocoa_display *cocoa_surface_display(const struct cocoa_surface *s); -void cocoa_surface_set_callbacks(struct cocoa_surface *s, const struct yasl_surface_cbs *cbs, void *ud); -void cocoa_surface_set_title(struct cocoa_surface *s, const char *title); -void cocoa_surface_schedule_redraw(struct cocoa_surface *s); -bool cocoa_surface_present(struct cocoa_surface *s, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage); -bool cocoa_surface_input_poll(struct cocoa_surface *s, struct yasl_input_event *ev); -struct yasl_geometry cocoa_surface_geometry(const struct cocoa_surface *s); -uint32_t cocoa_surface_state(const struct cocoa_surface *s); -struct yasl_timing cocoa_surface_timing(const struct cocoa_surface *s); -uint64_t cocoa_surface_frame_time_ns(struct cocoa_surface *s, uint64_t now); -void cocoa_surface_set_cursor(struct cocoa_surface *s, enum yasl_cursor cursor); -void cocoa_surface_set_background_color(struct cocoa_surface *s, uint32_t bg); -const char *cocoa_surface_ime_preedit(const struct cocoa_surface *s); -bool cocoa_surface_ime_preedit_cursor(const struct cocoa_surface *s, int *begin, int *end); -void cocoa_surface_set_ime_caret(struct cocoa_surface *s, struct yasl_rect2i caret); -bool cocoa_surface_start_interactive_move(struct cocoa_surface *s); -bool cocoa_surface_start_interactive_resize(struct cocoa_surface *s, enum yasl_resize_edge edge); -bool cocoa_surface_show_window_menu(struct cocoa_surface *s, struct yasl_vector2i pos); -bool cocoa_surface_drag_carry(struct cocoa_surface *s, struct yasl_vector2i offset); -bool cocoa_surface_place_under_pointer(struct cocoa_surface *s, struct yasl_vector2i at); -void *cocoa_display_get_native_handle(struct cocoa_display *d); -void *cocoa_surface_get_native_handle(struct cocoa_surface *s); - -#endif diff --git a/src/cocoa/keycodes.h b/src/cocoa/keycodes.h deleted file mode 100644 index f3c06be..0000000 --- a/src/cocoa/keycodes.h +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef SURFACE_DARWIN_KEYCODES_H -#define SURFACE_DARWIN_KEYCODES_H - -/* Virtual keycodes for standard physical keys on macOS. */ -enum { - kVK_ANSI_A = 0x00, - kVK_ANSI_S = 0x01, - kVK_ANSI_D = 0x02, - kVK_ANSI_F = 0x03, - kVK_ANSI_H = 0x04, - kVK_ANSI_G = 0x05, - kVK_ANSI_Z = 0x06, - kVK_ANSI_X = 0x07, - kVK_ANSI_C = 0x08, - kVK_ANSI_V = 0x09, - kVK_ANSI_B = 0x0B, - kVK_ANSI_Q = 0x0C, - kVK_ANSI_W = 0x0D, - kVK_ANSI_E = 0x0E, - kVK_ANSI_R = 0x0F, - kVK_ANSI_Y = 0x10, - kVK_ANSI_T = 0x11, - kVK_ANSI_1 = 0x12, - kVK_ANSI_2 = 0x13, - kVK_ANSI_3 = 0x14, - kVK_ANSI_4 = 0x15, - kVK_ANSI_6 = 0x16, - kVK_ANSI_5 = 0x17, - kVK_ANSI_Equal = 0x18, - kVK_ANSI_9 = 0x19, - kVK_ANSI_7 = 0x1A, - kVK_ANSI_Minus = 0x1B, - kVK_ANSI_8 = 0x1C, - kVK_ANSI_0 = 0x1D, - kVK_ANSI_RightBracket = 0x1E, - kVK_ANSI_O = 0x1F, - kVK_ANSI_U = 0x20, - kVK_ANSI_LeftBracket = 0x21, - kVK_ANSI_I = 0x22, - kVK_ANSI_P = 0x23, - kVK_ANSI_L = 0x25, - kVK_ANSI_J = 0x26, - kVK_ANSI_Quote = 0x27, - kVK_ANSI_K = 0x28, - kVK_ANSI_Semicolon = 0x29, - kVK_ANSI_Backslash = 0x2A, - kVK_ANSI_Comma = 0x2B, - kVK_ANSI_Slash = 0x2C, - kVK_ANSI_N = 0x2D, - kVK_ANSI_M = 0x2E, - kVK_ANSI_Period = 0x2F, - kVK_ANSI_Grave = 0x32, - kVK_ANSI_KeypadDecimal = 0x41, - kVK_ANSI_KeypadMultiply = 0x43, - kVK_ANSI_KeypadPlus = 0x45, - kVK_ANSI_KeypadClear = 0x47, - kVK_ANSI_KeypadDivide = 0x4B, - kVK_ANSI_KeypadEnter = 0x4C, - kVK_ANSI_KeypadMinus = 0x4E, - kVK_ANSI_KeypadEquals = 0x51, - kVK_ANSI_Keypad0 = 0x52, - kVK_ANSI_Keypad1 = 0x53, - kVK_ANSI_Keypad2 = 0x54, - kVK_ANSI_Keypad3 = 0x55, - kVK_ANSI_Keypad4 = 0x56, - kVK_ANSI_Keypad5 = 0x57, - kVK_ANSI_Keypad6 = 0x58, - kVK_ANSI_Keypad7 = 0x59, - kVK_ANSI_Keypad8 = 0x5B, - kVK_ANSI_Keypad9 = 0x5C, - - kVK_Return = 0x24, - kVK_Tab = 0x30, - kVK_Space = 0x31, - kVK_Delete = 0x33, - kVK_Escape = 0x35, - kVK_Command = 0x37, - kVK_Shift = 0x38, - kVK_CapsLock = 0x39, - kVK_Option = 0x3A, - kVK_Control = 0x3B, - kVK_RightCommand = 0x36, - kVK_RightShift = 0x3C, - kVK_RightOption = 0x3D, - kVK_RightControl = 0x3E, - kVK_Function = 0x3F, - kVK_F17 = 0x40, - kVK_VolumeUp = 0x48, - kVK_VolumeDown = 0x49, - kVK_Mute = 0x4A, - kVK_F18 = 0x4F, - kVK_F19 = 0x50, - kVK_F20 = 0x5A, - kVK_F5 = 0x60, - kVK_F6 = 0x61, - kVK_F7 = 0x62, - kVK_F3 = 0x63, - kVK_F8 = 0x64, - kVK_F9 = 0x65, - kVK_F11 = 0x67, - kVK_F13 = 0x69, - kVK_F16 = 0x6A, - kVK_F14 = 0x6B, - kVK_F10 = 0x6D, - kVK_ContextualMenu = 0x6E, - kVK_F12 = 0x6F, - kVK_F15 = 0x71, - kVK_Help = 0x72, - kVK_Home = 0x73, - kVK_PageUp = 0x74, - kVK_ForwardDelete = 0x75, - kVK_F4 = 0x76, - kVK_End = 0x77, - kVK_F2 = 0x78, - kVK_PageDown = 0x79, - kVK_F1 = 0x7A, - kVK_LeftArrow = 0x7B, - kVK_RightArrow = 0x7C, - kVK_DownArrow = 0x7D, - kVK_UpArrow = 0x7E, - - kVK_ISO_Section = 0x0A, - kVK_JIS_Yen = 0x5D, - kVK_JIS_Underscore = 0x5E, - kVK_JIS_KeypadComma = 0x5F, - kVK_JIS_Eisu = 0x66, - kVK_JIS_Kana = 0x68, -}; - -#endif diff --git a/src/cocoa/meson.build b/src/cocoa/meson.build deleted file mode 100644 index 0bd40a3..0000000 --- a/src/cocoa/meson.build +++ /dev/null @@ -1 +0,0 @@ -sources += files('window.m') 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; -} |
