summaryrefslogtreecommitdiff
path: root/src/cocoa
diff options
context:
space:
mode:
Diffstat (limited to 'src/cocoa')
-rw-r--r--src/cocoa/window.m519
1 files changed, 483 insertions, 36 deletions
diff --git a/src/cocoa/window.m b/src/cocoa/window.m
index 7b59865..78ab0bd 100644
--- a/src/cocoa/window.m
+++ b/src/cocoa/window.m
@@ -55,6 +55,10 @@ struct cocoa_surface {
id<MTLCommandQueue> software_queue;
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;
@@ -91,6 +95,7 @@ struct cocoa_surface {
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;
@@ -118,8 +123,14 @@ struct cocoa_display {
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;
@@ -135,9 +146,29 @@ 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);
}
@@ -159,10 +190,26 @@ static void set_state(struct cocoa_surface *w, uint32_t bit, bool on)
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,
@@ -303,6 +350,76 @@ static struct yasl_vector2i event_pos(struct cocoa_surface *w, NSEvent *ev)
};
}
+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)
{
@@ -349,6 +466,11 @@ static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_pointer
}
}
+ 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,
@@ -362,9 +484,22 @@ static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_pointer
},
};
input_queue_push(&w->input, &input_ev);
- if (kind == YASL_PTR_MOTION && darwin_ctx.drag.active && darwin_ctx.drag.carried) {
- cocoa_surface_place_under_pointer(darwin_ctx.drag.carried, darwin_ctx.drag.carry_offset);
+ 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) {
@@ -417,6 +552,18 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor)
}
@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
@@ -434,8 +581,14 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor)
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
{
NSPasteboard *pb = [sender draggingPasteboard];
- if ([pb canReadItemWithDataConformingToTypes:@[NSPasteboardTypeFileURL]])
+ 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;
}
@@ -444,33 +597,49 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor)
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)
- return NO;
-
- 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);
- return 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;
+ }
}
- return NO;
+ if (w->cbs && w->cbs->drop)
+ w->cbs->drop(&w->base, YASL_DROP_LEAVE, pos, w->ud);
+ return ok;
}
- (CALayer *)makeBackingLayer
@@ -487,7 +656,7 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor)
return layer;
}
-- (BOOL)isOpaque { return YES; }
+- (BOOL)isOpaque { return !self.surface || !self.surface->is_popup; }
- (BOOL)acceptsFirstResponder { return YES; }
- (BOOL)becomeFirstResponder { return YES; }
- (BOOL)canBecomeKeyView { return YES; }
@@ -1128,7 +1297,8 @@ void cocoa_display_close(struct cocoa_display *d)
uint32_t cocoa_display_caps(const struct cocoa_display *d)
{
- return d ? (YASL_CAP_DRAG | YASL_CAP_DRAG_CARRY | YASL_CAP_PLACE_WINDOW |
+ 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;
}
@@ -1248,8 +1418,25 @@ void cocoa_surface_destroy(struct cocoa_surface *w)
{
assert([NSThread isMainThread]);
if (!w || w->destroyed) return;
- if (darwin_ctx.drag.active && (darwin_ctx.drag.origin == w || darwin_ctx.drag.carried == w))
- drag_end(&darwin_ctx, YASL_DRAG_CANCELLED);
+ 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};
@@ -1349,22 +1536,37 @@ bool cocoa_surface_ime_preedit_cursor(const struct cocoa_surface *w, int *begin,
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; (void)sel;
+ (void)d;
if (!text) return;
+ NSPasteboard *pb = pasteboard_for_selection(sel);
+ if (!pb) return;
NSString *s = [NSString stringWithUTF8String:text];
if (!s) return;
- NSPasteboard *pb = [NSPasteboard generalPasteboard];
[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; (void)sel;
+ (void)d;
if (!target) return;
- NSString *s = [[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString];
+ 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 = {
@@ -1390,18 +1592,182 @@ struct yasl_geometry cocoa_surface_geometry(const struct cocoa_surface *w)
};
}
+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)
{
- (void)parent; (void)spec;
- return NULL;
+ 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->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)
{
- (void)popup; (void)spec;
- return false;
+ 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)
@@ -1411,18 +1777,83 @@ bool cocoa_surface_start_interactive_move(struct cocoa_surface *s)
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;
+ CGDataProviderRef provider = CGDataProviderCreateWithCopy((const uint8_t *)pixels, bytes);
+ 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)
{
- (void)image;
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;
}
@@ -1435,6 +1866,11 @@ bool cocoa_surface_drag_carry(struct cocoa_surface *w, struct yasl_vector2i offs
{
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);
@@ -1475,7 +1911,18 @@ void cocoa_surface_set_callbacks(struct cocoa_surface *w,
bool cocoa_surface_input_poll(struct cocoa_surface *w, struct yasl_input_event *ev)
{
- return w && ev && input_queue_pop(&w->input, 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)