diff options
Diffstat (limited to 'src/platform/cocoa/window.m')
| -rw-r--r-- | src/platform/cocoa/window.m | 275 |
1 files changed, 205 insertions, 70 deletions
diff --git a/src/platform/cocoa/window.m b/src/platform/cocoa/window.m index 2f9f8e5..60b20b0 100644 --- a/src/platform/cocoa/window.m +++ b/src/platform/cocoa/window.m @@ -12,6 +12,7 @@ #include "keymap.h" #include <assert.h> +#include <errno.h> #include <math.h> #include <poll.h> #include <stdio.h> @@ -172,11 +173,13 @@ static void drag_end(struct cocoa_display *d, enum yasl_drag_end how) [d->drag.image release]; d->drag.image = nil; - if (hover && !hover->destroyed && hover->cbs && hover->cbs->drag) { + if (hover && !hover->destroyed) { + darwin_ctx.depth++; if (how == YASL_DRAG_DROPPED) - hover->cbs->drag(&hover->base, YASL_DRAG_DROP, pos, hover->ud); + yasl_surface_notify_drag(&hover->base, YASL_DRAG_DROP, pos); else - hover->cbs->drag(&hover->base, YASL_DRAG_LEAVE, pos, hover->ud); + yasl_surface_notify_drag(&hover->base, YASL_DRAG_LEAVE, pos); + darwin_ctx.depth--; } if (l.end) l.end(ud, how); @@ -187,15 +190,14 @@ 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) + if (!w || w->destroyed || w->closed) return; uint32_t next = on ? (w->base.state | bit) : (w->base.state & ~bit); if (next == w->base.state) return; w->base.state = next; darwin_ctx.depth++; - if (w->cbs->state) - w->cbs->state(&w->base, next, w->ud); + yasl_surface_notify_state(&w->base, next); darwin_ctx.depth--; } @@ -227,13 +229,14 @@ static void push_focus(struct cocoa_surface *w, bool focused) input_queue_push(&w->input, &ev); darwin_ctx.depth++; set_state(w, YASL_STATE_ACTIVATED, focused); - cocoa_surface_schedule_redraw(w); + if (!w->destroyed && !w->closed) + cocoa_surface_schedule_redraw(w); darwin_ctx.depth--; } static void sync_size(struct cocoa_surface *w, bool force) { - if (!w || !w->layer || w->closed) + if (!w || w->destroyed || !w->layer || w->closed) return; uint64_t start = yasl_loop_now_ns(); NSRect bounds = w->view.bounds; @@ -254,14 +257,15 @@ static void sync_size(struct cocoa_surface *w, bool force) 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){ + /* Common configure notification already requests a frame before invoking + * the legacy callback. Finish local bookkeeping before that callback can + * destroy this surface. */ + yasl_clock_note_resize(&w->clock, yasl_loop_now_ns() - start); + yasl_surface_notify_configure(&w->base, (struct yasl_geometry){ .size = size, .scale_120 = scale_120, - }, w->ud); - cocoa_surface_schedule_redraw(w); - if (w->view.inLiveResize) + }); + if (!w->destroyed && !w->closed && w->view.inLiveResize) render_and_commit(w); - yasl_clock_note_resize(&w->clock, yasl_loop_now_ns() - start); darwin_ctx.depth--; } @@ -427,19 +431,26 @@ static void update_drag_hover(struct cocoa_display *d, NSPoint screenPoint) 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 (!old->destroyed) { + darwin_ctx.depth++; + yasl_surface_notify_drag(&old->base, YASL_DRAG_LEAVE, old_pos); + darwin_ctx.depth--; + } 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); + if (target && !target->destroyed) { + darwin_ctx.depth++; + yasl_surface_notify_drag(&target->base, YASL_DRAG_OVER, pos); + darwin_ctx.depth--; + } } 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); + darwin_ctx.depth++; + yasl_surface_notify_drag(&target->base, YASL_DRAG_OVER, pos); + darwin_ctx.depth--; } } @@ -559,15 +570,14 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor) - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { (void)sender; - if (darwin_ctx.cbs && darwin_ctx.cbs->quit) - darwin_ctx.cbs->quit(darwin_ctx.ud); + yasl_display_notify_event(&darwin_ctx.base, YASL_EVENT_DISPLAY_QUIT); 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); + if (!visible) + yasl_display_notify_event(&darwin_ctx.base, YASL_EVENT_DISPLAY_REOPEN); return YES; } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender @@ -608,9 +618,11 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send NSPasteboard *pb = [sender draggingPasteboard]; if ([pb canReadItemWithDataConformingToTypes:@[NSPasteboardTypeFileURL]]) { struct cocoa_surface *w = self.surface; - if (w && w->cbs && w->cbs->drop) { + if (w && !w->closed) { struct yasl_vector2i pos = dragging_pos(self, sender); - w->cbs->drop(&w->base, YASL_DROP_OVER, pos, w->ud); + darwin_ctx.depth++; + yasl_surface_notify_drop(&w->base, YASL_DROP_OVER, pos); + darwin_ctx.depth--; } return NSDragOperationCopy; } @@ -627,9 +639,11 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send NSPasteboard *pb = [sender draggingPasteboard]; if ([pb canReadItemWithDataConformingToTypes:@[NSPasteboardTypeFileURL]]) { struct cocoa_surface *w = self.surface; - if (w && w->cbs && w->cbs->drop) { + if (w && !w->closed) { struct yasl_vector2i pos = dragging_pos(self, sender); - w->cbs->drop(&w->base, YASL_DROP_LEAVE, pos, w->ud); + darwin_ctx.depth++; + yasl_surface_notify_drop(&w->base, YASL_DROP_LEAVE, pos); + darwin_ctx.depth--; } } } @@ -662,8 +676,11 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send ok = YES; } } - if (w->cbs && w->cbs->drop) - w->cbs->drop(&w->base, YASL_DROP_LEAVE, pos, w->ud); + if (!w->destroyed) { + darwin_ctx.depth++; + yasl_surface_notify_drop(&w->base, YASL_DROP_LEAVE, pos); + darwin_ctx.depth--; + } return ok; } @@ -869,13 +886,19 @@ static NSString *plain_string(id string) { (void)replacementRange; struct cocoa_surface *w = self.surface; - if (!w) return; + if (!w || w->destroyed || w->closed) return; const char *utf8 = plain_string(string).UTF8String; size_t len = utf8 ? strlen(utf8) : 0; + bool had_preedit = w->preedit != NULL; free(w->preedit); w->preedit = NULL; + darwin_ctx.depth++; on_text_input(w, utf8, len); - cocoa_surface_schedule_redraw(w); + if (had_preedit) + yasl_surface_notify_composition(&w->base, NULL, -1, -1); + if (!w->destroyed && !w->closed) + cocoa_surface_schedule_redraw(w); + darwin_ctx.depth--; } - (void)doCommandBySelector:(SEL)selector { (void)selector; } @@ -883,24 +906,43 @@ static NSString *plain_string(id string) - (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange { - (void)selectedRange; (void)replacementRange; struct cocoa_surface *w = self.surface; - if (!w) return; + if (!w || w->destroyed || w->closed) 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); + int begin = -1, end = -1; + if (w->preedit && selectedRange.location != NSNotFound && + selectedRange.location <= str.length && selectedRange.length <= str.length - selectedRange.location) { + NSString *prefix = [str substringToIndex:selectedRange.location]; + NSString *selection = [str substringWithRange:selectedRange]; + const char *prefix_utf8 = prefix.UTF8String; + const char *selection_utf8 = selection.UTF8String; + if (prefix_utf8 && selection_utf8) { + begin = (int)strlen(prefix_utf8); + end = begin + (int)strlen(selection_utf8); + } + } + darwin_ctx.depth++; + yasl_surface_notify_composition(&w->base, w->preedit, begin, end); + if (!w->destroyed && !w->closed) + cocoa_surface_schedule_redraw(w); + darwin_ctx.depth--; } - (void)unmarkText { struct cocoa_surface *w = self.surface; - if (!w || !w->preedit) return; + if (!w || w->destroyed || w->closed || !w->preedit) return; free(w->preedit); w->preedit = NULL; - cocoa_surface_schedule_redraw(w); + darwin_ctx.depth++; + yasl_surface_notify_composition(&w->base, NULL, -1, -1); + if (!w->destroyed && !w->closed) + cocoa_surface_schedule_redraw(w); + darwin_ctx.depth--; } - (NSRange)selectedRange { return NSMakeRange(NSNotFound, 0); } @@ -971,10 +1013,12 @@ static NSString *plain_string(id string) 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; + if (!w->destroyed && !w->closed) { + if (visible) + cocoa_surface_schedule_redraw(w); + else + w->link.paused = YES; + } darwin_ctx.depth--; } @@ -1031,8 +1075,7 @@ static NSString *plain_string(id string) darwin_ctx.depth++; w->closed = true; [w->link invalidate]; - if (w->cbs->close) - w->cbs->close(&w->base, w->ud); + yasl_surface_notify_close(&w->base); darwin_ctx.depth--; } @@ -1081,7 +1124,7 @@ bool cocoa_surface_present(struct cocoa_surface *w, const struct yasl_framebuffe 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 || + if (w->destroyed || w->closed || !w->cbs->draw || w->rendering || w->size.x <= 0 || w->size.y <= 0 || (w->base.state & YASL_STATE_SUSPENDED)) return; @@ -1110,8 +1153,8 @@ static void render_and_commit(struct cocoa_surface *w) }, }; if (drawable) - w->cbs->draw(&w->base, &w->frame, w->ud); - if (!w->closed && !w->did_present) { + yasl_surface_draw(&w->base, &w->frame); + if (!w->destroyed && !w->closed && !w->did_present) { w->want_draw = true; w->frames_skipped++; } @@ -1122,7 +1165,7 @@ static void render_and_commit(struct cocoa_surface *w) static void pump_window(struct cocoa_surface *w) { - if (!w || w->closed || !w->want_draw || !w->cbs->draw) + if (!w || w->destroyed || w->closed || !w->want_draw || !w->cbs->draw) return; if (w->link && !w->frame_due && !w->unpaced) return; @@ -1368,6 +1411,7 @@ struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct ya 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; + darwin_ctx.base.callback_ud = spec->ud; if (spec->app_id) darwin_ctx.app_id = strdup(spec->app_id); darwin_ctx.base.app_id = darwin_ctx.app_id; } @@ -1585,6 +1629,57 @@ void cocoa_surface_set_title(struct cocoa_surface *w, const char *title) w->window.title = [NSString stringWithUTF8String:w->title] ?: @"YASL"; } +static bool impl_surface_set_size(struct yasl_surface *surface, struct yasl_vector2i size) +{ + struct cocoa_surface *w = (struct cocoa_surface *)surface; + if (!w || w->closed || w->is_popup || size.x <= 0 || size.y <= 0) { + errno = EINVAL; + return false; + } + [w->window setContentSize:NSMakeSize(size.x, size.y)]; + errno = 0; + return true; +} + +static bool impl_surface_set_fullscreen(struct yasl_surface *surface, bool enabled) +{ + struct cocoa_surface *w = (struct cocoa_surface *)surface; + if (!w || w->closed || w->is_popup || !w->window) { + errno = EINVAL; + return false; + } + bool fullscreen = (w->window.styleMask & NSWindowStyleMaskFullScreen) != 0; + if (fullscreen != enabled) + [w->window toggleFullScreen:nil]; + errno = 0; + return true; +} + +static bool impl_surface_set_maximized(struct yasl_surface *surface, bool enabled) +{ + struct cocoa_surface *w = (struct cocoa_surface *)surface; + if (!w || w->closed || w->is_popup || !w->window) { + errno = EINVAL; + return false; + } + if (w->window.isZoomed != enabled) + [w->window zoom:nil]; + errno = 0; + return true; +} + +static bool impl_surface_minimize(struct yasl_surface *surface) +{ + struct cocoa_surface *w = (struct cocoa_surface *)surface; + if (!w || w->closed || w->is_popup || !w->window) { + errno = EINVAL; + return false; + } + [w->window miniaturize:nil]; + errno = 0; + return true; +} + void cocoa_surface_set_background_color(struct cocoa_surface *w, uint32_t bg) { if (!w || w->closed) return; @@ -1644,34 +1739,54 @@ static NSPasteboard *pasteboard_for_selection(enum yasl_selection sel) } } +static bool cocoa_display_copy_text(struct cocoa_display *d, enum yasl_selection sel, const char *text); + void cocoa_display_copy(struct cocoa_display *d, enum yasl_selection sel, const char *text) { - (void)d; - if (!text) return; + (void)cocoa_display_copy_text(d, sel, text); +} + +static bool cocoa_display_copy_text(struct cocoa_display *d, enum yasl_selection sel, const char *text) +{ + assert([NSThread isMainThread]); + if (!d || !text) { errno = EINVAL; return false; } NSPasteboard *pb = pasteboard_for_selection(sel); - if (!pb) return; + if (!pb) { errno = EINVAL; return false; } NSString *s = [NSString stringWithUTF8String:text]; - if (!s) return; + if (!s) { errno = EINVAL; return false; } [pb clearContents]; - [pb setString:s forType:NSPasteboardTypeString]; + if (![pb setString:s forType:NSPasteboardTypeString]) { + errno = EIO; + return false; + } + errno = 0; + return true; } -void cocoa_display_paste(struct cocoa_display *d, enum yasl_selection sel, struct cocoa_surface *target) +static bool cocoa_display_request_paste(struct cocoa_display *d, enum yasl_selection sel, + struct cocoa_surface *target, uint64_t id) { - (void)d; - if (!target) return; + assert([NSThread isMainThread]); + if (!d || !target || target->destroyed || target->closed || + target->base.display != &d->base) { + errno = EINVAL; + return false; + } NSPasteboard *pb = pasteboard_for_selection(sel); - if (!pb) return; + if (!pb) { errno = EINVAL; return false; } NSString *s = [pb stringForType:NSPasteboardTypeString]; const char *utf8 = s ? s.UTF8String : NULL; - if (utf8) { - struct yasl_event ev = { - .type = YASL_EVENT_PASTE, - .surface = &target->base, - .text = { .data = utf8, .len = strlen(utf8) }, - }; - input_queue_push(&target->input, &ev); - } + enum yasl_transfer_status status = !s ? YASL_TRANSFER_UNAVAILABLE : + utf8 ? YASL_TRANSFER_OK : YASL_TRANSFER_FAILED; + yasl_surface_complete_paste(&target->base, id, status, + utf8 ? utf8 : NULL, utf8 ? strlen(utf8) : 0); + errno = 0; + return true; +} + +void cocoa_display_paste(struct cocoa_display *d, enum yasl_selection sel, struct cocoa_surface *target) +{ + (void)cocoa_display_request_paste(d, sel, target, 0); } int cocoa_display_modifiers(const struct cocoa_display *d) @@ -1990,6 +2105,7 @@ void cocoa_surface_set_callbacks(struct cocoa_surface *w, { if (!w || w->closed) return; w->base.callbacks = cbs ? *cbs : (struct yasl_window_cbs){0}; + w->base.ud = ud; w->cbs = &w->base.callbacks; w->ud = ud; } @@ -2071,6 +2187,18 @@ static void impl_display_paste(struct yasl_display *d, enum yasl_selection sel, cocoa_display_paste((struct cocoa_display *)d, sel, (struct cocoa_surface *)target); } +static bool impl_display_copy_text(struct yasl_display *d, enum yasl_selection sel, const char *text) +{ + return cocoa_display_copy_text((struct cocoa_display *)d, sel, text); +} + +static bool impl_display_request_paste(struct yasl_display *d, enum yasl_selection sel, + struct yasl_surface *target, uint64_t request_id) +{ + return cocoa_display_request_paste((struct cocoa_display *)d, sel, + (struct cocoa_surface *)target, request_id); +} + 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); @@ -2104,6 +2232,12 @@ static void impl_surface_destroy(struct yasl_surface *s) cocoa_surface_destroy((struct cocoa_surface *)s); } +static struct input_queue *impl_surface_input_queue(struct yasl_surface *s) +{ + struct cocoa_surface *w = (struct cocoa_surface *)s; + return w ? &w->input : NULL; +} + static struct yasl_display *impl_surface_display(const struct yasl_surface *s) { return s ? &darwin_ctx.base : NULL; @@ -2129,11 +2263,6 @@ static bool impl_surface_present(struct yasl_surface *s, const struct yasl_frame return cocoa_surface_present((struct cocoa_surface *)s, fb, damage); } -static bool impl_surface_poll_event(struct yasl_surface *s, struct yasl_event *ev) -{ - return cocoa_surface_poll_event((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); @@ -2254,18 +2383,24 @@ const struct yasl_backend_ops yasl_cocoa_backend = { .display_double_click_ms = impl_display_double_click_ms, .display_copy = impl_display_copy, .display_paste = impl_display_paste, + .display_copy_text = impl_display_copy_text, + .display_request_paste = impl_display_request_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_input_queue = impl_surface_input_queue, + .surface_set_size = impl_surface_set_size, + .surface_set_fullscreen = impl_surface_set_fullscreen, + .surface_set_maximized = impl_surface_set_maximized, + .surface_minimize = impl_surface_minimize, .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_poll_event = impl_surface_poll_event, .surface_geometry = impl_surface_geometry, .surface_timing = impl_surface_timing, .surface_frame_time_ns = impl_surface_frame_time_ns, |
