1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
#import <AppKit/AppKit.h>
#import <UserNotifications/UserNotifications.h>
#include "common/desktop.h"
#include "common/private_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_PRIVATE_APPEARANCE_DARK;
else if (name)
flags |= YASL_PRIVATE_APPEARANCE_LIGHT;
if ([NSWorkspace sharedWorkspace].accessibilityDisplayShouldIncreaseContrast)
flags |= YASL_PRIVATE_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);
}
|