Skip to content

Commit

Permalink
started work on mac notifications 🚀
Browse files Browse the repository at this point in the history
  • Loading branch information
zbaylin committed Dec 19, 2019
1 parent 7558f7c commit df93af8
Show file tree
Hide file tree
Showing 19 changed files with 262 additions and 10 deletions.
11 changes: 8 additions & 3 deletions examples/Examples.re
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,14 @@ let state: state = {
source: "Stopwatch.re",
},
{
name: "Native",
render: _ => NativeExample.render(),
source: "NativeExample.re",
name: "Native: File(s)/Folders(s)",
render: _ => NativeFileExample.render(),
source: "NativeFileExample.re",
},
{
name: "Native: Notifications",
render: _ => NativeNotificationExample.render(),
source: "NativeNotificationExample.re",
},
{
name: "Input",
Expand Down
8 changes: 8 additions & 0 deletions examples/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>com.revery.examples</string>
</dict>
</plist>
4 changes: 2 additions & 2 deletions examples/NativeExample.re → examples/NativeFileExample.re
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ open Revery.UI.Components;

open Revery.Native;

module NativeExamples = {
module NativeFileExamples = {
let%component make = () => {
let%hook (fileListOpt, setFileListOpt) = Hooks.state(None);
let%hook (allowMultiple, setAllowMultiple) = Hooks.state(false);
Expand Down Expand Up @@ -81,4 +81,4 @@ module NativeExamples = {
};
};

let render = () => <NativeExamples />;
let render = () => <NativeFileExamples />;
30 changes: 30 additions & 0 deletions examples/NativeNotificationExample.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
open Revery;
open Revery.UI;
open Revery.UI.Components;

open Revery.Native;

module Example = {
let%component make = () => {
let%hook (notification, setNotification) =
Hooks.state(
Notification.create(
~title="Revery Test",
~body="This is a test!",
~mute=false,
~onClick=() => Console.log("Notification clicked!"),
(),
),
);
<View>
<Center>
<Button
title="Dispatch!"
onClick={() => Notification.dispatch(notification)}
/>
</Center>
</View>;
};
};

let render = () => <Example />;
2 changes: 1 addition & 1 deletion examples/dune
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
(install
(section bin)
(package Revery)
(files Roboto-Regular.ttf FontAwesome5FreeSolid.otf binary.dat outrun-logo.png outrun-icon.png revery-icon.png))
(files Info.plist Roboto-Regular.ttf FontAwesome5FreeSolid.otf binary.dat outrun-logo.png outrun-icon.png revery-icon.png))

(install
(section bin)
Expand Down
4 changes: 4 additions & 0 deletions src/Core/App.re
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
open Revery_Native;

type delegatedFunc = unit => unit;
type idleFunc = unit => unit;
type canIdleFunc = unit => bool;
Expand Down Expand Up @@ -184,6 +186,8 @@ let start = (~onIdle=noop, initFunc: appInitFunc) => {
};
};

Cocoa.setAppDelegate();

let appLoop = () => {
_flushEvents();

Expand Down
2 changes: 1 addition & 1 deletion src/Core/dune
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
(public_name Revery.Core)
(js_of_ocaml (javascript_files file.js))
(cxx_names file)
(libraries threads console.lib str lwt sdl2 flex fontkit Rench re))
(libraries threads console.lib str lwt sdl2 flex fontkit Rench re Revery_Native))
1 change: 1 addition & 0 deletions src/Native/Cocoa.re
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
external setAppDelegate: unit => unit = "revery_cocoaSetAppDelegate";
16 changes: 16 additions & 0 deletions src/Native/Notification.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
type t = {
title: string,
body: string,
onClick: unit => unit,
mute: bool,
};

let create =
(~title: string, ~body: string, ~onClick=() => (), ~mute=false, ()) => {
title,
body,
onClick,
mute,
};

external dispatch: t => unit = "revery_dispatchNotification";
2 changes: 2 additions & 0 deletions src/Native/ReveryCocoa.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ char** revery_open_files_cocoa(const char* startDir, char* fileTypes[],
int canChooseFiles, int canChooseDirectories,
int showHidden, const char* buttonText,
const char* title);
void revery_dispatchNotification_cocoa(const char* title, const char* body,
long onClickFunc, int mute);
}
2 changes: 2 additions & 0 deletions src/Native/Revery_Native.re
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
module Dialog = Dialog;
module Notification = Notification;
module Cocoa = Cocoa;
47 changes: 47 additions & 0 deletions src/Native/cocoa/ReveryAppDelegate.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#ifdef __APPLE__
#import "ReveryAppDelegate.h"
#import <Cocoa/Cocoa.h>

#import "utilities.h"

// Implementation of ReveryAppDelegate
@implementation ReveryAppDelegate

/* init - initializes the AppDelegate
Assigns a mutable dictionary to notificationActions
*/
- (id)init {
self = [super init];
if (self) {
_notificationActions = [NSMutableDictionary new];
}
return self;
}

/* applicationDidFinishLaunching
Assigns self as the notification center delegate
*/
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
}

/* didActivateNotification
Gets the long from the NSDictionary from the notification's identifier and calls it
*/
- (void)userNotificationCenter:(NSUserNotificationCenter *)center
didActivateNotification:(NSUserNotification *)notification {
NSNumber *num = _notificationActions[[notification identifier]];
long ocamlFunc = [num longValue];
revery_caml_call(ocamlFunc);
}

/* shouldPresentNotification
Always presents the notification
*/
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
shouldPresentNotification:(NSUserNotification *)notification {
return YES;
}
@end

#endif
15 changes: 15 additions & 0 deletions src/Native/cocoa/ReveryAppDelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#import <Cocoa/Cocoa.h>

/* ReveryAppDelegate
Contains all of the delegation code for the NSApplication, as well as the
delegation for the NSUserNotificationCenter
*/
@interface ReveryAppDelegate
: NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate>
/* [notificationActions] - a mutable dictionary
maps NSStrings (notification identifiers) -> NSNumbers
(longs which represent OCaml callbacks)
*/
@property(nonatomic, strong) NSMutableDictionary *notificationActions;
@end
17 changes: 17 additions & 0 deletions src/Native/cocoa/revery_cocoa.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifdef __APPLE__
#include <caml/alloc.h>
#include <caml/callback.h>
#include <caml/memory.h>
#include <caml/mlvalues.h>

#import "ReveryAppDelegate.h"

extern "C" {
CAMLprim value revery_cocoaSetAppDelegate() {
ReveryAppDelegate *delegate = [ReveryAppDelegate new];
[NSApp setDelegate:delegate];
return Val_unit;
}
}

#endif
9 changes: 6 additions & 3 deletions src/Native/dune
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
(preprocess (pps lwt_ppx))
(library_flags (:include flags.sexp))
(js_of_ocaml (javascript_files dialog.js))
(c_names dialog_cocoa dialog_win32 dialog_gtk)
(cxx_names dialog)
(c_names dialog_cocoa dialog_win32 dialog_gtk notification_cocoa utilities ReveryAppDelegate)
(cxx_names dialog notification revery_cocoa)
(c_flags (:include c_flags.sexp))
(cxx_flags (:include cxx_flags.sexp))
(c_library_flags (:include c_library_flags.sexp))
(libraries sdl2))

(copy_files cocoa/*)

(rule
(targets flags.sexp c_flags.sexp c_library_flags.sexp)
(targets flags.sexp c_flags.sexp cxx_flags.sexp c_library_flags.sexp)
(deps (:discover config/discover.exe))
(action (run %{discover})))
47 changes: 47 additions & 0 deletions src/Native/notification.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <stdio.h>

#include <caml/alloc.h>
#include <caml/callback.h>
#include <caml/memory.h>
#include <caml/mlvalues.h>
#include <string.h>


#ifdef WIN32
#include "ReveryWin32.h"
#elif __APPLE__
#include "ReveryCocoa.h"
#else
#include "ReveryGtk.h"
#endif

#define Val_none Val_int(0)
static value Val_some(value v) {
CAMLparam1(v);
CAMLlocal1(some);
some = caml_alloc(1, 0);
Store_field(some, 0, v);
CAMLreturn(some);
}

#define Some_val(v) Field(v, 0)

extern "C" {
CAMLprim value revery_dispatchNotification(value vNotificationT) {
CAMLparam1(vNotificationT);

const char *title;
const char *body;
int mute;

title = String_val(Field(vNotificationT, 0));
body = String_val(Field(vNotificationT, 1));
value onClickCaml = Field(vNotificationT, 2);
mute = Int_val(Field(vNotificationT, 3));
#ifdef __APPLE__
revery_dispatchNotification_cocoa(title, body, onClickCaml, mute);
#endif

CAMLreturn(Val_unit);
}
}
33 changes: 33 additions & 0 deletions src/Native/notification_cocoa.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifdef __APPLE__
#import "cocoa/ReveryAppDelegate.h"
#import <Cocoa/Cocoa.h>

#include "utilities.h"

void revery_dispatchNotification_cocoa(const char *title, const char *body,
long onClickFunc, int mute) {
NSUserNotification *notification = [[NSUserNotification alloc] autorelease];
NSUserNotificationCenter *notificationCenter =
[NSUserNotificationCenter defaultUserNotificationCenter];
ReveryAppDelegate *delegate = (ReveryAppDelegate *)[NSApp delegate];
NSString *nsTitle =
[NSString stringWithCString:title encoding:NSUTF8StringEncoding];
NSString *nsBody =
[NSString stringWithCString:body encoding:NSUTF8StringEncoding];

NSString *identifier =
[NSString stringWithFormat:@"%@:notification:%@",
[[NSBundle mainBundle] bundleIdentifier],
[[[NSUUID alloc] init] UUIDString]];
[notification setIdentifier:identifier];
[notification setTitle:nsTitle];
[notification setInformativeText:nsBody];
[notification setSoundName:mute ? NULL : NSUserNotificationDefaultSoundName];

delegate.notificationActions[identifier] =
[NSNumber numberWithLong:onClickFunc];

[notificationCenter deliverNotification:notification];
}

#endif
20 changes: 20 additions & 0 deletions src/Native/utilities.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include <caml/alloc.h>
#include <caml/callback.h>
#include <caml/memory.h>
#include <caml/mlvalues.h>
#import <caml/threads.h>

/* Sourced from Brisk's `BriskCocoa`
Thank you @wokalski!
*/
void revery_caml_call_n(value f, int argCount, value *args) {
caml_c_thread_register();
caml_acquire_runtime_system();
caml_callbackN(f, argCount, args);
caml_release_runtime_system();
}

void revery_caml_call(value f) {
value args[] = {Val_unit};
revery_caml_call_n(f, 1, args);
}
2 changes: 2 additions & 0 deletions src/Native/utilities.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
void revery_caml_call_n(long f, int numArgs, int* args);
void revery_caml_call(long f);

0 comments on commit df93af8

Please sign in to comment.