Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/1833 #2390

Merged
merged 2 commits into from Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
74 changes: 23 additions & 51 deletions app/app_darwin.go
@@ -1,87 +1,59 @@
// +build !ci

// +build !ios

package app

/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation
#cgo LDFLAGS: -framework Foundation -framework UserNotifications

#include <AppKit/AppKit.h>
#include <stdbool.h>
#include <stdlib.h>

bool isBundled();
bool isDarkMode();
void sendNotification(const char *title, const char *content);
void watchTheme();
void sendNotification(char *title, char *content);
*/
import "C"
import (
"fmt"
"net/url"
"os"
"path/filepath"
"strings"
"unsafe"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"

"golang.org/x/sys/execabs"
)

func defaultVariant() fyne.ThemeVariant {
if C.isDarkMode() {
return theme.VariantDark
}
return theme.VariantLight
}

func rootConfigDir() string {
homeDir, _ := os.UserHomeDir()

desktopConfig := filepath.Join(filepath.Join(homeDir, "Library"), "Preferences")
return filepath.Join(desktopConfig, "fyne")
}

func (a *fyneApp) OpenURL(url *url.URL) error {
cmd := a.exec("open", url.String())
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
return cmd.Run()
}

func (a *fyneApp) SendNotification(n *fyne.Notification) {
if C.isBundled() {
title := C.CString(n.Title)
defer C.free(unsafe.Pointer(title))
content := C.CString(n.Content)
defer C.free(unsafe.Pointer(content))
titleStr := C.CString(n.Title)
defer C.free(unsafe.Pointer(titleStr))
contentStr := C.CString(n.Content)
defer C.free(unsafe.Pointer(contentStr))

C.sendNotification(title, content)
C.sendNotification(titleStr, contentStr)
return
}

title := escapeNotificationString(n.Title)
content := escapeNotificationString(n.Content)
template := `display notification "%s" with title "%s"`
script := fmt.Sprintf(template, content, title)

err := execabs.Command("osascript", "-e", script).Start()
if err != nil {
fyne.LogError("Failed to launch darwin notify script", err)
}
fallbackNotification(n.Title, n.Content)
}

func escapeNotificationString(in string) string {
noSlash := strings.ReplaceAll(in, "\\", "\\\\")
return strings.ReplaceAll(noSlash, "\"", "\\\"")
}

//export themeChanged
func themeChanged() {
fyne.CurrentApp().Settings().(*settings).setupTheme()
//export fallbackSend
func fallbackSend(cTitle, cContent *C.char) {
title := C.GoString(cTitle)
content := C.GoString(cContent)
fallbackNotification(title, content)
}

func watchTheme() {
C.watchTheme()
func fallbackNotification(title, content string) {
template := `display notification "%s" with title "%s"`
script := fmt.Sprintf(template, escapeNotificationString(content), escapeNotificationString(title))

err := execabs.Command("osascript", "-e", script).Start()
if err != nil {
fyne.LogError("Failed to launch darwin notify script", err)
}
}
78 changes: 37 additions & 41 deletions app/app_darwin.m
@@ -1,55 +1,51 @@
// +build !ci
// +build !ios

extern void themeChanged();
#import <UserNotifications/UserNotifications.h>

#import <Foundation/Foundation.h>
static int notifyNum = 0;

@interface FyneUserNotificationCenterDelegate : NSObject<NSUserNotificationCenterDelegate>
extern void fallbackSend(char *cTitle, char *cBody);

- (BOOL)userNotificationCenter:(NSUserNotificationCenter*)center
shouldPresentNotification:(NSUserNotification*)notification;
void doSendNotification(UNUserNotificationCenter *center, NSString *title, NSString *body) {
UNMutableNotificationContent *content = [UNMutableNotificationContent new];
[content autorelease];
content.title = title;
content.body = body;

@end
notifyNum++;
NSString *identifier = [NSString stringWithFormat:@"fyne-notify-%d", notifyNum];
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier
content:content trigger:nil];

@implementation FyneUserNotificationCenterDelegate

- (BOOL)userNotificationCenter:(NSUserNotificationCenter*)center
shouldPresentNotification:(NSUserNotification*)notification
{
return YES;
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error != nil) {
NSLog(@"Could not send notification: %@", error);
}
}];
}

@end

void sendNSUserNotification(const char *, const char *);

bool isBundled() {
return [[NSBundle mainBundle] bundleIdentifier] != nil;
}

bool isDarkMode() {
NSString *style = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
return [@"Dark" isEqualToString:style];
}

void sendNotification(const char *title, const char *body) {
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
if (center.delegate == nil) {
center.delegate = [[FyneUserNotificationCenterDelegate new] autorelease];
}

NSString *uuid = [[NSUUID UUID] UUIDString];
NSUserNotification *notification = [[NSUserNotification new] autorelease];
notification.title = [NSString stringWithUTF8String:title];
notification.informativeText = [NSString stringWithUTF8String:body];
notification.identifier = [NSString stringWithFormat:@"%@-fyne-notify-%@", [[NSBundle mainBundle] bundleIdentifier], uuid];
[center scheduleNotification:notification];
}

void watchTheme() {
[[NSDistributedNotificationCenter defaultCenter] addObserverForName:@"AppleInterfaceThemeChangedNotification" object:nil queue:nil
usingBlock:^(NSNotification *note) {
themeChanged(); // calls back into Go
}];
void sendNotification(char *cTitle, char *cBody) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
NSString *title = [NSString stringWithUTF8String:cTitle];
NSString *body = [NSString stringWithUTF8String:cBody];

UNAuthorizationOptions options = UNAuthorizationOptionAlert;
[center requestAuthorizationWithOptions:options
completionHandler:^(BOOL granted, NSError *_Nullable error) {
if (!granted) {
if (error != NULL) {
NSLog(@"Error asking for permission to send notifications %@", error);
// this happens if our app was not signed, so do it the old way
fallbackSend((char *)[title UTF8String], (char *)[body UTF8String]);
} else {
NSLog(@"Unable to get permission to send notifications");
}
} else {
doSendNotification(center, title, body);
}
}];
}
54 changes: 54 additions & 0 deletions app/app_desktop_darwin.go
@@ -0,0 +1,54 @@
// +build !ci

// +build !ios

package app

/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation

#include <AppKit/AppKit.h>

bool isBundled();
bool isDarkMode();
void watchTheme();
*/
import "C"
import (
"net/url"
"os"
"path/filepath"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
)

func defaultVariant() fyne.ThemeVariant {
if C.isDarkMode() {
return theme.VariantDark
}
return theme.VariantLight
}

func rootConfigDir() string {
homeDir, _ := os.UserHomeDir()

desktopConfig := filepath.Join(filepath.Join(homeDir, "Library"), "Preferences")
return filepath.Join(desktopConfig, "fyne")
}

func (a *fyneApp) OpenURL(url *url.URL) error {
cmd := a.exec("open", url.String())
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
return cmd.Run()
}

//export themeChanged
func themeChanged() {
fyne.CurrentApp().Settings().(*settings).setupTheme()
}

func watchTheme() {
C.watchTheme()
}
18 changes: 18 additions & 0 deletions app/app_desktop_darwin.m
@@ -0,0 +1,18 @@
// +build !ci
// +build !ios

extern void themeChanged();

#import <Foundation/Foundation.h>

bool isDarkMode() {
NSString *style = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
return [@"Dark" isEqualToString:style];
}

void watchTheme() {
[[NSDistributedNotificationCenter defaultCenter] addObserverForName:@"AppleInterfaceThemeChangedNotification" object:nil queue:nil
usingBlock:^(NSNotification *note) {
themeChanged(); // calls back into Go
}];
}
9 changes: 0 additions & 9 deletions app/app_mobile_ios.go
Expand Up @@ -36,15 +36,6 @@ func (a *fyneApp) OpenURL(url *url.URL) error {
return nil
}

func (a *fyneApp) SendNotification(n *fyne.Notification) {
titleStr := C.CString(n.Title)
defer C.free(unsafe.Pointer(titleStr))
contentStr := C.CString(n.Content)
defer C.free(unsafe.Pointer(contentStr))

C.sendNotification(titleStr, contentStr)
}

func defaultVariant() fyne.ThemeVariant {
return systemTheme
}
37 changes: 0 additions & 37 deletions app/app_mobile_ios.m
Expand Up @@ -3,50 +3,13 @@
// +build ios

#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>

void openURL(char *urlStr) {
UIApplication *app = [UIApplication sharedApplication];
NSURL *url = [NSURL URLWithString:[NSString stringWithUTF8String:urlStr]];
[app openURL:url options:@{} completionHandler:nil];
}

static int notifyNum = 0;

void doSendNotification(UNUserNotificationCenter *center, NSString *title, NSString *body) {
UNMutableNotificationContent *content = [UNMutableNotificationContent new];
[content autorelease];
content.title = title;
content.body = body;

notifyNum++;
NSString *identifier = [NSString stringWithFormat:@"fyne-notify-%d", notifyNum];
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier
content:content trigger:nil];

[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error != nil) {
NSLog(@"Could not send notification: %@", error);
}
}];
}

void sendNotification(char *cTitle, char *cBody) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
NSString *title = [NSString stringWithUTF8String:cTitle];
NSString *body = [NSString stringWithUTF8String:cBody];

UNAuthorizationOptions options = UNAuthorizationOptionAlert;
[center requestAuthorizationWithOptions:options
completionHandler:^(BOOL granted, NSError *_Nullable error) {
if (!granted) {
NSLog(@"Unable to get permission to send notifications");
} else {
doSendNotification(center, title, body);
}
}];
}

char *documentsPath() {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = paths.firstObject;
Expand Down
13 changes: 11 additions & 2 deletions internal/driver/glfw/menu_darwin.m
Expand Up @@ -5,6 +5,15 @@

const int menuTagMin = 5000;

#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
NSControlStateValue STATE_ON = NSControlStateValueOn;
NSControlStateValue STATE_OFF = NSControlStateValueOff;
#else
NSControlStateValue STATE_ON = NSOnState;
NSControlStateValue STATE_OFF = NSOffState;
#endif


extern void menuCallback(int);
extern BOOL menuEnabled(int);
extern BOOL menuChecked(int);
Expand All @@ -21,9 +30,9 @@ + (void) tapped:(NSMenuItem*) item {
+ (BOOL) validateMenuItem:(NSMenuItem*) item {
BOOL checked = menuChecked([item tag]-menuTagMin);
if (checked) {
[item setState:NSOnState];
[item setState:STATE_ON];
} else {
[item setState:NSOffState];
[item setState:STATE_OFF];
}

return menuEnabled([item tag]-menuTagMin);
Expand Down