summaryrefslogtreecommitdiff
path: root/src/platform/cocoa
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/cocoa')
-rw-r--r--src/platform/cocoa/desktop.m145
-rw-r--r--src/platform/cocoa/meson.build2
-rw-r--r--src/platform/cocoa/window.m10
3 files changed, 156 insertions, 1 deletions
diff --git a/src/platform/cocoa/desktop.m b/src/platform/cocoa/desktop.m
new file mode 100644
index 0000000..526cbb9
--- /dev/null
+++ b/src/platform/cocoa/desktop.m
@@ -0,0 +1,145 @@
+#import <AppKit/AppKit.h>
+#import <UserNotifications/UserNotifications.h>
+
+#include "common/desktop.h"
+#include "yasl/desktop.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <limits.h>
+#include <mach-o/dyld.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static size_t copy_str(char *buf, size_t cap, const char *s)
+{
+ size_t len = s ? strlen(s) : 0;
+ if (!len || len >= cap) return 0;
+ memcpy(buf, s, len + 1);
+ return len;
+}
+
+bool desktop_open_uri(const char *uri)
+{
+ @autoreleasepool {
+ NSString *str = [NSString stringWithUTF8String:uri];
+ NSURL *url = str ? [NSURL URLWithString:str] : nil;
+ return url && [[NSWorkspace sharedWorkspace] openURL:url];
+ }
+}
+
+/* Show banners while the application is in front, too. */
+@interface YaslNotificationDelegate : NSObject <UNUserNotificationCenterDelegate>
+@end
+
+@implementation YaslNotificationDelegate
+- (void)userNotificationCenter:(UNUserNotificationCenter *)center
+ willPresentNotification:(UNNotification *)notification
+ withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
+{
+ (void)center;
+ (void)notification;
+ completionHandler(UNNotificationPresentationOptionBanner);
+}
+@end
+
+static YaslNotificationDelegate *notification_delegate;
+static bool authorization_requested;
+
+/* UNUserNotificationCenter raises an exception outside an app bundle. */
+static bool notifications_available(void)
+{
+ NSBundle *bundle = [NSBundle mainBundle];
+ return bundle.bundleIdentifier && [bundle.bundlePath.pathExtension isEqualToString:@"app"];
+}
+
+bool desktop_notify(const char *app_id, const char *title, const char *body)
+{
+ (void)app_id;
+ if (!notifications_available()) return false;
+ @autoreleasepool {
+ NSString *heading = [NSString stringWithUTF8String:title];
+ NSString *message = [NSString stringWithUTF8String:body];
+ if (!heading || !message) return false;
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
+ if (!notification_delegate) {
+ notification_delegate = [[YaslNotificationDelegate alloc] init];
+ center.delegate = notification_delegate;
+ }
+ if (!authorization_requested) {
+ authorization_requested = true;
+ [center requestAuthorizationWithOptions:UNAuthorizationOptionAlert
+ completionHandler:^(BOOL granted, NSError *error) {
+ (void)granted;
+ (void)error;
+ }];
+ }
+ UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
+ content.title = heading;
+ content.body = message;
+ UNNotificationRequest *request =
+ [UNNotificationRequest requestWithIdentifier:[[NSUUID UUID] UUIDString]
+ content:content
+ trigger:nil];
+ [content release];
+ [center addNotificationRequest:request withCompletionHandler:nil];
+ return true;
+ }
+}
+
+uint32_t desktop_appearance(void)
+{
+ @autoreleasepool {
+ uint32_t flags = 0;
+ NSAppearanceName name = [NSApp.effectiveAppearance
+ bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]];
+ if ([name isEqualToString:NSAppearanceNameDarkAqua])
+ flags |= YASL_APPEARANCE_DARK;
+ else if (name)
+ flags |= YASL_APPEARANCE_LIGHT;
+ if ([NSWorkspace sharedWorkspace].accessibilityDisplayShouldIncreaseContrast)
+ flags |= YASL_APPEARANCE_HIGH_CONTRAST;
+ return flags;
+ }
+}
+
+size_t desktop_executable_path(char *buf, size_t cap)
+{
+ char raw[PATH_MAX];
+ uint32_t size = sizeof(raw);
+ char resolved[PATH_MAX];
+ if (_NSGetExecutablePath(raw, &size) != 0 || !realpath(raw, resolved)) return 0;
+ return copy_str(buf, cap, resolved);
+}
+
+size_t desktop_resource_dir(char *buf, size_t cap)
+{
+ @autoreleasepool {
+ NSBundle *bundle = [NSBundle mainBundle];
+ if (!bundle.bundleIdentifier || ![bundle.bundlePath.pathExtension isEqualToString:@"app"])
+ return 0;
+ NSString *path = bundle.resourcePath;
+ return path ? copy_str(buf, cap, path.fileSystemRepresentation) : 0;
+ }
+}
+
+size_t desktop_default_locale(char *buf, size_t cap)
+{
+ /* Identifiers such as "zh-Hans_CN" are not POSIX names, so build the name
+ * from the language and region codes alone. */
+ char name[64] = "en_US.UTF-8";
+ CFLocaleRef locale = CFLocaleCopyCurrent();
+ if (locale) {
+ CFStringRef language = CFLocaleGetValue(locale, kCFLocaleLanguageCode);
+ CFStringRef region = CFLocaleGetValue(locale, kCFLocaleCountryCode);
+ char lang[16], reg[16];
+ if (language && CFStringGetCString(language, lang, sizeof(lang), kCFStringEncodingUTF8) && lang[0]) {
+ if (region && CFStringGetCString(region, reg, sizeof(reg), kCFStringEncodingUTF8) && reg[0])
+ snprintf(name, sizeof(name), "%s_%s.UTF-8", lang, reg);
+ else
+ snprintf(name, sizeof(name), "%s.UTF-8", lang);
+ }
+ CFRelease(locale);
+ }
+ return copy_str(buf, cap, name);
+}
diff --git a/src/platform/cocoa/meson.build b/src/platform/cocoa/meson.build
index 8230376..5fc5f5e 100644
--- a/src/platform/cocoa/meson.build
+++ b/src/platform/cocoa/meson.build
@@ -1 +1 @@
-sources += files('window.m', 'keymap.c')
+sources += files('window.m', 'desktop.m', 'keymap.c')
diff --git a/src/platform/cocoa/window.m b/src/platform/cocoa/window.m
index 811a7c6..75dce54 100644
--- a/src/platform/cocoa/window.m
+++ b/src/platform/cocoa/window.m
@@ -1366,6 +1366,7 @@ struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct ya
darwin_ctx.cbs = &darwin_ctx.base.callbacks;
darwin_ctx.ud = spec->ud;
if (spec->app_id) darwin_ctx.app_id = strdup(spec->app_id);
+ darwin_ctx.base.app_id = darwin_ctx.app_id;
}
return &darwin_ctx;
}
@@ -2191,6 +2192,14 @@ static bool impl_surface_place_under_pointer(struct yasl_surface *s, struct yasl
return cocoa_surface_place_under_pointer((struct cocoa_surface *)s, at);
}
+static bool impl_surface_bell(struct yasl_surface *s)
+{
+ assert([NSThread isMainThread]);
+ (void)s;
+ NSBeep();
+ return true;
+}
+
static void *impl_display_get_native_handle(struct yasl_display *d)
{
return d ? (void *)NSApp : NULL;
@@ -2249,6 +2258,7 @@ const struct yasl_backend_ops yasl_cocoa_backend = {
.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,
+ .surface_bell = impl_surface_bell,
.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,