diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-14 14:05:19 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-14 14:06:16 +0300 |
| commit | 0eec911bdabb130415aeb2fc6eb4281b891a5d2f (patch) | |
| tree | 0ab5dfa8ee08d1fc5ecc7092fd43d2cda723d2fa /src/platform/cocoa/desktop.m | |
| parent | 87279f6903343e621c0446b20d1e7d6a495f1125 (diff) | |
Fix Cocoa backend focus, close, live resize, popups and notifications
- Undecorated windows can become key and main; popups never take focus.
- Undecorated windows stay miniaturizable and resizable unless FIXED_SIZE.
- The close button sends a CLOSE request (windowShouldClose:) and does not
close; the app destroys the surface to comply.
- Refresh runs during live resize: the modal flag is set between
windowWillStartLiveResize: and windowDidEndLiveResize:, and CPU surfaces
refresh before the frame is rendered. A destroyed window clears the flag.
- presentsWithTransaction is only changed for CPU presenters.
- Display open and surface creation return EPERM off the main thread.
- Popups are always CPU surfaces with a software queue, also under a Metal
parent.
- Interactive move and drag start use the recorded mouse-down; without a
press they set ESTALE. Interactive resize and window menu return ENOTSUP.
- Notifications: ENOTSUP outside an app bundle; one authorization request,
notifications queued until it is granted, EACCES after a denial.
- Titles use the full string instead of a 159-byte copy that could split a
UTF-8 sequence.
- finishLaunching runs once per process, not once per display open.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src/platform/cocoa/desktop.m')
| -rw-r--r-- | src/platform/cocoa/desktop.m | 57 |
1 files changed, 46 insertions, 11 deletions
diff --git a/src/platform/cocoa/desktop.m b/src/platform/cocoa/desktop.m index 76aa551..25393d5 100644 --- a/src/platform/cocoa/desktop.m +++ b/src/platform/cocoa/desktop.m @@ -4,7 +4,9 @@ #include "common/desktop.h" #include "common/private_desktop.h" +#include <dispatch/dispatch.h> #include <CoreFoundation/CoreFoundation.h> +#include <errno.h> #include <limits.h> #include <mach-o/dyld.h> #include <stdio.h> @@ -44,7 +46,12 @@ bool desktop_open_uri(const char *uri) @end static YaslNotificationDelegate *notification_delegate; +/* Main-thread state. Notifications made before the answer to the one + * authorization request wait in the queue and are posted when it is granted. */ +static NSMutableArray<UNNotificationRequest *> *notification_queue; static bool authorization_requested; +static bool authorization_granted; +static bool authorization_denied; /* UNUserNotificationCenter raises an exception outside an app bundle. */ static bool notifications_available(void) @@ -56,24 +63,26 @@ static bool notifications_available(void) bool desktop_notify(const char *app_id, const char *title, const char *body) { (void)app_id; - if (!notifications_available()) return false; + if (!notifications_available()) { + errno = ENOTSUP; + return false; + } + if (authorization_denied) { + errno = EACCES; + return false; + } @autoreleasepool { NSString *heading = [NSString stringWithUTF8String:title]; NSString *message = [NSString stringWithUTF8String:body]; - if (!heading || !message) return false; + if (!heading || !message) { + errno = EINVAL; + 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; @@ -82,7 +91,33 @@ bool desktop_notify(const char *app_id, const char *title, const char *body) content:content trigger:nil]; [content release]; - [center addNotificationRequest:request withCompletionHandler:nil]; + if (authorization_granted) { + [center addNotificationRequest:request withCompletionHandler:nil]; + return true; + } + if (!notification_queue) + notification_queue = [[NSMutableArray alloc] init]; + [notification_queue addObject:request]; + if (!authorization_requested) { + authorization_requested = true; + [center requestAuthorizationWithOptions:UNAuthorizationOptionAlert + completionHandler:^(BOOL granted, NSError *error) { + (void)error; + /* The handler runs on a background queue; the state is main-thread only. */ + dispatch_async(dispatch_get_main_queue(), ^{ + if (!granted) { + authorization_denied = true; + [notification_queue removeAllObjects]; + return; + } + authorization_granted = true; + UNUserNotificationCenter *granted_center = [UNUserNotificationCenter currentNotificationCenter]; + for (UNNotificationRequest *queued in notification_queue) + [granted_center addNotificationRequest:queued withCompletionHandler:nil]; + [notification_queue removeAllObjects]; + }); + }]; + } return true; } } |
