Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/hello/src/runner.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ pub const RunOptions = struct {
bridge: ?zero_native.BridgeDispatcher = null,
builtin_bridge: zero_native.BridgePolicy = .{},
security: zero_native.SecurityPolicy = .{},
main_window: zero_native.WindowOptions = .{},

fn appInfo(self: RunOptions) zero_native.AppInfo {
return .{
.app_name = self.app_name,
.window_title = self.window_title,
.bundle_id = self.bundle_id,
.icon_path = self.icon_path,
.main_window = self.main_window,
};
}
};
Expand Down
1 change: 1 addition & 0 deletions examples/webview/src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ const builtin_policies = [_]zero_native.BridgeCommandPolicy{
.{ .name = "zero-native.window.create", .permissions = &window_permission, .origins = &example_origins },
.{ .name = "zero-native.window.focus", .permissions = &window_permission, .origins = &example_origins },
.{ .name = "zero-native.window.close", .permissions = &window_permission, .origins = &example_origins },
.{ .name = "zero-native.window.move", .permissions = &window_permission, .origins = &example_origins },
.{ .name = "zero-native.webview.create", .permissions = &window_permission, .origins = &example_origins },
.{ .name = "zero-native.webview.list", .permissions = &window_permission, .origins = &example_origins },
.{ .name = "zero-native.webview.setFrame", .permissions = &window_permission, .origins = &example_origins },
Expand Down
5 changes: 3 additions & 2 deletions src/platform/macos/appkit_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ typedef struct {
typedef void (*zero_native_appkit_event_callback_t)(void *context, const zero_native_appkit_event_t *event);
typedef void (*zero_native_appkit_bridge_callback_t)(void *context, uint64_t window_id, const char *webview_label, size_t webview_label_len, const char *message, size_t message_len, const char *origin, size_t origin_len);

zero_native_appkit_host_t *zero_native_appkit_create(const char *app_name, size_t app_name_len, const char *window_title, size_t window_title_len, const char *bundle_id, size_t bundle_id_len, const char *icon_path, size_t icon_path_len, const char *window_label, size_t window_label_len, double x, double y, double width, double height, int restore_frame);
zero_native_appkit_host_t *zero_native_appkit_create(const char *app_name, size_t app_name_len, const char *window_title, size_t window_title_len, const char *bundle_id, size_t bundle_id_len, const char *icon_path, size_t icon_path_len, const char *window_label, size_t window_label_len, double x, double y, double width, double height, int restore_frame, int frameless, int transparent, int always_on_top);
void zero_native_appkit_destroy(zero_native_appkit_host_t *host);
void zero_native_appkit_run(zero_native_appkit_host_t *host, zero_native_appkit_event_callback_t callback, void *context);
void zero_native_appkit_stop(zero_native_appkit_host_t *host);
Expand All @@ -47,8 +47,9 @@ void zero_native_appkit_bridge_respond_window(zero_native_appkit_host_t *host, u
void zero_native_appkit_bridge_respond_webview(zero_native_appkit_host_t *host, uint64_t window_id, const char *webview_label, size_t webview_label_len, const char *response, size_t response_len);
void zero_native_appkit_emit_window_event(zero_native_appkit_host_t *host, uint64_t window_id, const char *name, size_t name_len, const char *detail_json, size_t detail_json_len);
void zero_native_appkit_set_security_policy(zero_native_appkit_host_t *host, const char *allowed_origins, size_t allowed_origins_len, const char *external_urls, size_t external_urls_len, int external_action);
int zero_native_appkit_create_window(zero_native_appkit_host_t *host, uint64_t window_id, const char *window_title, size_t window_title_len, const char *window_label, size_t window_label_len, double x, double y, double width, double height, int restore_frame);
int zero_native_appkit_create_window(zero_native_appkit_host_t *host, uint64_t window_id, const char *window_title, size_t window_title_len, const char *window_label, size_t window_label_len, double x, double y, double width, double height, int restore_frame, int frameless, int transparent, int always_on_top);
int zero_native_appkit_focus_window(zero_native_appkit_host_t *host, uint64_t window_id);
int zero_native_appkit_move_window(zero_native_appkit_host_t *host, uint64_t window_id, double dx, double dy, int clamp_to_visible_frame, int *out_hit_x, int *out_hit_y);
int zero_native_appkit_close_window(zero_native_appkit_host_t *host, uint64_t window_id);
size_t zero_native_appkit_clipboard_read(zero_native_appkit_host_t *host, char *buffer, size_t buffer_len);
void zero_native_appkit_clipboard_write(zero_native_appkit_host_t *host, const char *text, size_t text_len);
Expand Down
88 changes: 75 additions & 13 deletions src/platform/macos/appkit_host.m
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ @interface ZeroNativeAppKitHost : NSObject <WKNavigationDelegate>
@property(nonatomic, strong) NSArray<NSString *> *allowedNavigationOrigins;
@property(nonatomic, strong) NSArray<NSString *> *allowedExternalURLs;
@property(nonatomic, assign) NSInteger externalLinkAction;
- (instancetype)initWithAppName:(NSString *)appName windowTitle:(NSString *)windowTitle bundleIdentifier:(NSString *)bundleIdentifier iconPath:(NSString *)iconPath windowLabel:(NSString *)windowLabel x:(double)x y:(double)y width:(double)width height:(double)height restoreFrame:(BOOL)restoreFrame;
- (BOOL)createWindowWithId:(uint64_t)windowId title:(NSString *)title label:(NSString *)label x:(double)x y:(double)y width:(double)width height:(double)height restoreFrame:(BOOL)restoreFrame makeMain:(BOOL)makeMain;
- (instancetype)initWithAppName:(NSString *)appName windowTitle:(NSString *)windowTitle bundleIdentifier:(NSString *)bundleIdentifier iconPath:(NSString *)iconPath windowLabel:(NSString *)windowLabel x:(double)x y:(double)y width:(double)width height:(double)height restoreFrame:(BOOL)restoreFrame frameless:(BOOL)frameless transparent:(BOOL)transparent alwaysOnTop:(BOOL)alwaysOnTop;
- (BOOL)createWindowWithId:(uint64_t)windowId title:(NSString *)title label:(NSString *)label x:(double)x y:(double)y width:(double)width height:(double)height restoreFrame:(BOOL)restoreFrame frameless:(BOOL)frameless transparent:(BOOL)transparent alwaysOnTop:(BOOL)alwaysOnTop makeMain:(BOOL)makeMain;
- (void)focusWindowWithId:(uint64_t)windowId;
- (void)closeWindowWithId:(uint64_t)windowId;
- (WKWebView *)webViewForWindowId:(uint64_t)windowId;
Expand Down Expand Up @@ -263,7 +263,7 @@ - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id<WKURLSchemeTask>)urlS

@implementation ZeroNativeAppKitHost

- (instancetype)initWithAppName:(NSString *)appName windowTitle:(NSString *)windowTitle bundleIdentifier:(NSString *)bundleIdentifier iconPath:(NSString *)iconPath windowLabel:(NSString *)windowLabel x:(double)x y:(double)y width:(double)width height:(double)height restoreFrame:(BOOL)restoreFrame {
- (instancetype)initWithAppName:(NSString *)appName windowTitle:(NSString *)windowTitle bundleIdentifier:(NSString *)bundleIdentifier iconPath:(NSString *)iconPath windowLabel:(NSString *)windowLabel x:(double)x y:(double)y width:(double)width height:(double)height restoreFrame:(BOOL)restoreFrame frameless:(BOOL)frameless transparent:(BOOL)transparent alwaysOnTop:(BOOL)alwaysOnTop {
self = [super init];
if (!self) {
return nil;
Expand All @@ -288,13 +288,13 @@ - (instancetype)initWithAppName:(NSString *)appName windowTitle:(NSString *)wind
self.externalLinkAction = 0;
[self configureApplication];

[self createWindowWithId:1 title:(windowTitle.length > 0 ? windowTitle : self.appName) label:self.windowLabel x:x y:y width:width height:height restoreFrame:restoreFrame makeMain:YES];
[self createWindowWithId:1 title:(windowTitle.length > 0 ? windowTitle : self.appName) label:self.windowLabel x:x y:y width:width height:height restoreFrame:restoreFrame frameless:frameless transparent:transparent alwaysOnTop:alwaysOnTop makeMain:YES];
self.didShutdown = NO;

return self;
}

- (BOOL)createWindowWithId:(uint64_t)windowId title:(NSString *)title label:(NSString *)label x:(double)x y:(double)y width:(double)width height:(double)height restoreFrame:(BOOL)restoreFrame makeMain:(BOOL)makeMain {
- (BOOL)createWindowWithId:(uint64_t)windowId title:(NSString *)title label:(NSString *)label x:(double)x y:(double)y width:(double)width height:(double)height restoreFrame:(BOOL)restoreFrame frameless:(BOOL)frameless transparent:(BOOL)transparent alwaysOnTop:(BOOL)alwaysOnTop makeMain:(BOOL)makeMain {
NSNumber *key = @(windowId);
if (self.windows[key]) {
return NO;
Expand All @@ -304,14 +304,32 @@ - (BOOL)createWindowWithId:(uint64_t)windowId title:(NSString *)title label:(NSS
if (restoreFrame) {
rect = constrainFrame(rect);
}
NSWindowStyleMask styleMask = frameless
? NSWindowStyleMaskBorderless
: (NSWindowStyleMaskTitled |
NSWindowStyleMaskClosable |
NSWindowStyleMaskResizable |
NSWindowStyleMaskMiniaturizable);
NSWindow *window = [[NSWindow alloc] initWithContentRect:rect
styleMask:(NSWindowStyleMaskTitled |
NSWindowStyleMaskClosable |
NSWindowStyleMaskResizable |
NSWindowStyleMaskMiniaturizable)
styleMask:styleMask
backing:NSBackingStoreBuffered
defer:NO];
[window setTitle:(title.length > 0 ? title : self.appName)];
if (frameless) {
[window setMovableByWindowBackground:YES];
}
if (transparent) {
[window setOpaque:NO];
[window setBackgroundColor:[NSColor clearColor]];
[window setHasShadow:NO];
}
if (alwaysOnTop) {
[window setLevel:NSFloatingWindowLevel];
[window setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces |
NSWindowCollectionBehaviorFullScreenAuxiliary |
NSWindowCollectionBehaviorIgnoresCycle];
[window setHidesOnDeactivate:NO];
}
if (!restoreFrame) {
[window center];
}
Expand Down Expand Up @@ -343,6 +361,17 @@ - (BOOL)createWindowWithId:(uint64_t)windowId title:(NSString *)title label:(NSS
if ([webView respondsToSelector:NSSelectorFromString(@"setInspectable:")]) {
[webView setValue:@YES forKey:@"inspectable"];
}
if (transparent) {
if ([webView respondsToSelector:NSSelectorFromString(@"setValue:forKey:")]) {
@try {
[webView setValue:@NO forKey:@"drawsBackground"];
} @catch (NSException *exception) {
(void)exception;
}
}
webView.layer.backgroundColor = [NSColor clearColor].CGColor;
webView.layer.opaque = NO;
}
webView.navigationDelegate = self;
webView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
[container addSubview:webView positioned:NSWindowAbove relativeTo:nil];
Expand All @@ -366,6 +395,10 @@ - (BOOL)createWindowWithId:(uint64_t)windowId title:(NSString *)title label:(NSS
self.bridgeScriptHandler = bridgeScriptHandler;
self.assetSchemeHandler = assetSchemeHandler;
self.windowLabel = label.length > 0 ? label : @"main";
if (frameless) {
[window makeKeyAndOrderFront:nil];
[NSApp activate];
}
} else {
[window makeKeyAndOrderFront:nil];
[NSApp activate];
Expand Down Expand Up @@ -1342,14 +1375,14 @@ static BOOL ZeroNativePolicyListMatches(NSArray<NSString *> *values, NSURL *url)
return NO;
}

zero_native_appkit_host_t *zero_native_appkit_create(const char *app_name, size_t app_name_len, const char *window_title, size_t window_title_len, const char *bundle_id, size_t bundle_id_len, const char *icon_path, size_t icon_path_len, const char *window_label, size_t window_label_len, double x, double y, double width, double height, int restore_frame) {
zero_native_appkit_host_t *zero_native_appkit_create(const char *app_name, size_t app_name_len, const char *window_title, size_t window_title_len, const char *bundle_id, size_t bundle_id_len, const char *icon_path, size_t icon_path_len, const char *window_label, size_t window_label_len, double x, double y, double width, double height, int restore_frame, int frameless, int transparent, int always_on_top) {
@autoreleasepool {
NSString *appNameString = [[NSString alloc] initWithBytes:app_name length:app_name_len encoding:NSUTF8StringEncoding] ?: @"zero-native";
NSString *windowTitleString = [[NSString alloc] initWithBytes:window_title length:window_title_len encoding:NSUTF8StringEncoding] ?: appNameString;
NSString *bundleIdString = [[NSString alloc] initWithBytes:bundle_id length:bundle_id_len encoding:NSUTF8StringEncoding] ?: @"dev.zero_native.app";
NSString *iconPathString = [[NSString alloc] initWithBytes:icon_path length:icon_path_len encoding:NSUTF8StringEncoding] ?: @"";
NSString *windowLabelString = [[NSString alloc] initWithBytes:window_label length:window_label_len encoding:NSUTF8StringEncoding] ?: @"main";
ZeroNativeAppKitHost *host = [[ZeroNativeAppKitHost alloc] initWithAppName:appNameString windowTitle:windowTitleString bundleIdentifier:bundleIdString iconPath:iconPathString windowLabel:windowLabelString x:x y:y width:width height:height restoreFrame:(restore_frame != 0)];
ZeroNativeAppKitHost *host = [[ZeroNativeAppKitHost alloc] initWithAppName:appNameString windowTitle:windowTitleString bundleIdentifier:bundleIdString iconPath:iconPathString windowLabel:windowLabelString x:x y:y width:width height:height restoreFrame:(restore_frame != 0) frameless:(frameless != 0) transparent:(transparent != 0) alwaysOnTop:(always_on_top != 0)];
return (__bridge_retained zero_native_appkit_host_t *)host;
}
}
Expand Down Expand Up @@ -1428,11 +1461,11 @@ void zero_native_appkit_set_security_policy(zero_native_appkit_host_t *host, con
[object setAllowedNavigationOrigins:origins externalURLs:externalURLs externalAction:external_action];
}

int zero_native_appkit_create_window(zero_native_appkit_host_t *host, uint64_t window_id, const char *window_title, size_t window_title_len, const char *window_label, size_t window_label_len, double x, double y, double width, double height, int restore_frame) {
int zero_native_appkit_create_window(zero_native_appkit_host_t *host, uint64_t window_id, const char *window_title, size_t window_title_len, const char *window_label, size_t window_label_len, double x, double y, double width, double height, int restore_frame, int frameless, int transparent, int always_on_top) {
ZeroNativeAppKitHost *object = (__bridge ZeroNativeAppKitHost *)host;
NSString *titleString = window_title ? [[NSString alloc] initWithBytes:window_title length:window_title_len encoding:NSUTF8StringEncoding] : @"";
NSString *labelString = window_label ? [[NSString alloc] initWithBytes:window_label length:window_label_len encoding:NSUTF8StringEncoding] : @"";
return [object createWindowWithId:window_id title:titleString ?: @"" label:labelString ?: @"" x:x y:y width:width height:height restoreFrame:(restore_frame != 0) makeMain:NO] ? 1 : 0;
return [object createWindowWithId:window_id title:titleString ?: @"" label:labelString ?: @"" x:x y:y width:width height:height restoreFrame:(restore_frame != 0) frameless:(frameless != 0) transparent:(transparent != 0) alwaysOnTop:(always_on_top != 0) makeMain:NO] ? 1 : 0;
}

int zero_native_appkit_focus_window(zero_native_appkit_host_t *host, uint64_t window_id) {
Expand All @@ -1449,6 +1482,35 @@ int zero_native_appkit_close_window(zero_native_appkit_host_t *host, uint64_t wi
return 1;
}

int zero_native_appkit_move_window(zero_native_appkit_host_t *host, uint64_t window_id, double dx, double dy, int clamp_to_visible_frame, int *out_hit_x, int *out_hit_y) {
ZeroNativeAppKitHost *object = (__bridge ZeroNativeAppKitHost *)host;
NSWindow *window = object.windows[@(window_id)];
if (!window) {
if (out_hit_x) *out_hit_x = 0;
if (out_hit_y) *out_hit_y = 0;
return 0;
}
NSRect frame = window.frame;
double newX = frame.origin.x + dx;
double newY = frame.origin.y - dy;
int hitX = 0, hitY = 0;
if (clamp_to_visible_frame) {
NSRect visible = (window.screen ?: NSScreen.mainScreen).visibleFrame;
double minX = visible.origin.x;
double maxX = visible.origin.x + visible.size.width - frame.size.width;
double minY = visible.origin.y;
double maxY = visible.origin.y + visible.size.height - frame.size.height;
if (newX < minX) { newX = minX; hitX = 1; }
else if (newX > maxX) { newX = maxX; hitX = 1; }
if (newY < minY) { newY = minY; hitY = 1; }
else if (newY > maxY) { newY = maxY; hitY = 1; }
}
[window setFrameOrigin:NSMakePoint(newX, newY)];
if (out_hit_x) *out_hit_x = hitX;
if (out_hit_y) *out_hit_y = hitY;
return 1;
}

int zero_native_appkit_create_webview(zero_native_appkit_host_t *host, uint64_t window_id, const char *label, size_t label_len, const char *url, size_t url_len, double x, double y, double width, double height, int layer, int transparent, int bridge_enabled) {
ZeroNativeAppKitHost *object = (__bridge ZeroNativeAppKitHost *)host;
NSString *labelString = label ? [[NSString alloc] initWithBytes:label length:label_len encoding:NSUTF8StringEncoding] : @"";
Expand Down
Loading