diff --git a/emulator/resources/Info.plist b/emulator/resources/Info.plist
index 9afd51a71..17ab3507d 100644
--- a/emulator/resources/Info.plist
+++ b/emulator/resources/Info.plist
@@ -31,5 +31,30 @@
DISPLAY
:0
+ CFBundleDocumentTypes
+
+
+ CFBundleTypeExtensions
+
+ midi
+ mid
+ kar
+
+ CFBundleTypeIconFile
+ SwadgeEmulator.icns
+ CFBundleTypeNames
+ MIDI File
+ CFBundleTypeOSTypes
+
+ Midi
+
+ LSItemContentTypes
+
+ public.midi-audio
+
+ CFBundleTypeRole
+ Viewer
+
+
diff --git a/emulator/src/extensions/midi/ext_midi.c b/emulator/src/extensions/midi/ext_midi.c
index ca39ac292..bb6e8a4e7 100644
--- a/emulator/src/extensions/midi/ext_midi.c
+++ b/emulator/src/extensions/midi/ext_midi.c
@@ -1,6 +1,7 @@
#include "ext_midi.h"
#include "emu_ext.h"
#include "emu_main.h"
+#include "emu_utils.h"
#include "hdw-nvs_emu.h"
#include "emu_cnfs.h"
@@ -8,12 +9,50 @@
#include "mode_synth.h"
#include
+#include
+#include
+
+#ifdef EMU_MACOS
+ // Used to handle DocumentOpen event that OSX uses instead of Just Putting It In Argv
+ #include
+#endif
+
+//==============================================================================
+// Types
+//==============================================================================
+
+#ifdef EMU_MACOS
+typedef void (*MacOpenFileCb)(const char* path);
+
+typedef struct
+{
+ EventHandlerUPP globalEventHandler;
+ AEEventHandlerUPP appleEventHandler;
+ EventHandlerRef globalEventHandlerRef;
+ MacOpenFileCb openFileCallback;
+} MacOpenFileHandler;
+#endif
//==============================================================================
// Function Prototypes
//==============================================================================
static bool midiInitCb(emuArgs_t* emuArgs);
+static void midiPreFrameCb(uint64_t frame);
+static bool midiInjectFile(const char* path);
+
+#ifdef EMU_MACOS
+// Exists but isn't declared in the headers
+extern Boolean ConvertEventRefToEventRecord(EventRef, EventRecord*);
+
+bool installMacOpenFileHandler(MacOpenFileHandler* handlerRef, MacOpenFileCb callback);
+void checkForEventsMacOpenFileHandler(MacOpenFileHandler* handlerRef, uint32_t millis);
+void uninstallMacOpenFileHandler(MacOpenFileHandler* handlerRef);
+
+static pascal OSErr handleOpenDocumentEvent(const AppleEvent* event, AppleEvent* reply, SRefCon handlerRef);
+static OSStatus globalEventHandler(EventHandlerCallRef handler, EventRef event, void* data);
+static void doFileOpenCb(const char* path);
+#endif
//==============================================================================
// Variables
@@ -22,7 +61,7 @@ static bool midiInitCb(emuArgs_t* emuArgs);
emuExtension_t midiEmuExtension = {
.name = "midi",
.fnInitCb = midiInitCb,
- .fnPreFrameCb = NULL,
+ .fnPreFrameCb = midiPreFrameCb,
.fnPostFrameCb = NULL,
.fnKeyCb = NULL,
.fnMouseMoveCb = NULL,
@@ -30,6 +69,17 @@ emuExtension_t midiEmuExtension = {
.fnRenderCb = NULL,
};
+static char midiPathBuffer[1024];
+static const char* midiFile = NULL;
+
+#ifdef EMU_MACOS
+static const EventTypeSpec eventTypes[] = {{.eventClass = kEventClassAppleEvent, .eventKind = kEventAppleEvent}};
+
+static bool handlerInstalled = false;
+static bool emulatorStarted = false;
+static MacOpenFileHandler macOpenFileHandler;
+#endif
+
//==============================================================================
// Functions
//==============================================================================
@@ -38,14 +88,20 @@ static bool midiInitCb(emuArgs_t* emuArgs)
{
if (emuArgs->midiFile)
{
- printf("Opening MIDI file: %s\n", emuArgs->midiFile);
- if (emuCnfsInjectFile(emuArgs->midiFile, emuArgs->midiFile))
- {
- emuInjectNvs32("storage", "synth_playmode", 1);
- emuInjectNvsBlob("storage", "synth_lastsong", strlen(emuArgs->midiFile), emuArgs->midiFile);
- emulatorSetSwadgeModeByName(synthMode.modeName);
- }
- else
+ midiFile = emuArgs->midiFile;
+ }
+
+#ifdef EMU_MACOS
+ handlerInstalled = installMacOpenFileHandler(&macOpenFileHandler, doFileOpenCb);
+ // Wait up to 100ms for an event at startup
+ checkForEventsMacOpenFileHandler(&macOpenFileHandler, 100);
+ emulatorStarted = true;
+#endif
+
+ if (midiFile)
+ {
+ printf("Opening MIDI file: %s\n", midiFile);
+ if (!midiInjectFile(midiFile))
{
printf("Could not read MIDI file!\n");
emulatorQuit();
@@ -56,4 +112,243 @@ static bool midiInitCb(emuArgs_t* emuArgs)
}
return false;
-}
\ No newline at end of file
+}
+
+void midiPreFrameCb(uint64_t frame)
+{
+#ifdef EMU_MACOS
+ if (handlerInstalled)
+ {
+ // Wait up to 5ms for an event, is that enough?
+ checkForEventsMacOpenFileHandler(&macOpenFileHandler, 5);
+ }
+#endif
+}
+
+static bool midiInjectFile(const char* path)
+{
+ if (emuCnfsInjectFile(midiFile, midiFile))
+ {
+ emuInjectNvs32("storage", "synth_playmode", 1);
+ emuInjectNvsBlob("storage", "synth_lastsong", strlen(midiFile), midiFile);
+ emulatorSetSwadgeModeByName(synthMode.modeName);
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+#ifdef EMU_MACOS
+static void doFileOpenCb(const char* path)
+{
+ strncpy(midiPathBuffer, path, sizeof(midiPathBuffer));
+ midiFile = midiPathBuffer;
+
+ if (emulatorStarted)
+ {
+ if (!midiInjectFile(path))
+ {
+ printf("Error: could not read MIDI file %s!\n", path);
+ }
+ }
+}
+
+bool installMacOpenFileHandler(MacOpenFileHandler* handlerRef, MacOpenFileCb callback)
+{
+ // Init handler
+ handlerRef->appleEventHandler = NULL;
+ handlerRef->globalEventHandler = NULL;
+ handlerRef->openFileCallback = callback;
+
+ // Install handler
+ handlerRef->appleEventHandler = NewAEEventHandlerUPP(handleOpenDocumentEvent);
+ OSStatus result = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, handlerRef->appleEventHandler,
+ (SRefCon)handlerRef, false);
+
+ if (result != noErr)
+ {
+ printf("Failed to install OpenDocument handler\n");
+ uninstallMacOpenFileHandler(handlerRef);
+ return false;
+ }
+
+ // Install the application-level handler
+ handlerRef->globalEventHandler = NewEventHandlerUPP(globalEventHandler);
+ result = InstallApplicationEventHandler(handlerRef->globalEventHandler, 1, eventTypes, NULL,
+ &handlerRef->globalEventHandlerRef);
+
+ if (result != noErr)
+ {
+ printf("Failed to install global event handler\n");
+ uninstallMacOpenFileHandler(handlerRef);
+ return false;
+ }
+
+ // Handler successfully installed
+ return true;
+}
+
+// Runs the event loop and waits for up to the specified time
+// The callback will be called for any events that are recevied
+void checkForEventsMacOpenFileHandler(MacOpenFileHandler* handlerRef, uint32_t millis)
+{
+ EventTimeout timeout = millis / 1000.0;
+ while (1)
+ {
+ EventRef eventRef;
+
+ OSErr result = ReceiveNextEvent(1, eventTypes, timeout, kEventRemoveFromQueue, &eventRef);
+
+ if (result == eventLoopTimedOutErr)
+ {
+ // printf("No event received after timeout\n");
+ break;
+ }
+ else if (result == noErr)
+ {
+ result = SendEventToEventTarget(eventRef, GetEventDispatcherTarget());
+ ReleaseEvent(eventRef);
+ if (result != noErr)
+ {
+ if (result == eventNotHandledErr)
+ {
+ // printf("Got eventNotHandledErr from SendEventToEventTarget()\n");
+ }
+ else
+ {
+ printf("Error in SendEventToEventTarget(): %d %s\n", result, strerror(result));
+ break;
+ }
+ }
+ }
+ else
+ {
+ printf("Error in ReceiveNextEvent()\n");
+ break;
+ }
+ }
+}
+
+// Uninstalls and deletes
+void uninstallMacOpenFileHandler(MacOpenFileHandler* handlerRef)
+{
+ if (handlerRef != NULL)
+ {
+ if (handlerRef->appleEventHandler != NULL)
+ {
+ DisposeAEEventHandlerUPP(handlerRef->appleEventHandler);
+ handlerRef->appleEventHandler = NULL;
+ }
+
+ if (handlerRef->globalEventHandler != NULL)
+ {
+ DisposeEventHandlerUPP(handlerRef->globalEventHandler);
+ handlerRef->globalEventHandler = NULL;
+ }
+ handlerRef->openFileCallback = NULL;
+ }
+}
+
+static pascal OSErr handleOpenDocumentEvent(const AppleEvent* event, AppleEvent* reply, SRefCon handlerRefArg)
+{
+ MacOpenFileHandler* handlerRef = (MacOpenFileHandler*)handlerRefArg;
+
+ AEDescList docList;
+ OSErr result = AEGetParamDesc(event, keyDirectObject, typeAEList, &docList);
+
+ if (result != noErr)
+ {
+ return result;
+ }
+
+ long docCount = 0;
+ result = AECountItems(&docList, &docCount);
+ if (result != noErr)
+ {
+ return result;
+ }
+
+ char buffer[2048];
+
+ // Yup, it's zero-indexed. Weird.
+ for (long i = 1; i <= docCount; i++)
+ {
+ AEKeyword keyword;
+ DescType docType;
+ Size docSize;
+
+ result = AEGetNthPtr(&docList, i, typeFileURL, &keyword, &docType, &buffer, sizeof(buffer), &docSize);
+
+ if (result != noErr)
+ {
+ return result;
+ }
+
+ CFURLRef docUrlRef = CFURLCreateWithBytes(NULL, (UInt8*)buffer, docSize, kCFStringEncodingUTF8, NULL);
+
+ if (docUrlRef != NULL)
+ {
+ CFStringRef docStringRef = CFURLCopyFileSystemPath(docUrlRef, kCFURLPOSIXPathStyle);
+ if (docStringRef != NULL)
+ {
+ char pathBuffer[1024];
+ if (CFStringGetFileSystemRepresentation(docStringRef, pathBuffer, sizeof(pathBuffer)))
+ {
+ handlerRef->openFileCallback(pathBuffer);
+ }
+ CFRelease(docStringRef);
+ }
+ CFRelease(docUrlRef);
+ }
+ }
+
+ return AEDisposeDesc(&docList);
+}
+
+static OSStatus globalEventHandler(EventHandlerCallRef handler, EventRef event, void* data)
+{
+ bool inQueue = IsEventInQueue(GetMainEventQueue(), event);
+
+ if (inQueue)
+ {
+ RetainEvent(event);
+ RemoveEventFromQueue(GetMainEventQueue(), event);
+ }
+
+ EventRecord record;
+ ConvertEventRefToEventRecord(event, &record);
+ char messageStr[5] = {
+ (char)((record.message >> 24) & 0xff),
+ (char)((record.message >> 16) & 0xff),
+ (char)((record.message >> 8) & 0xff),
+ (char)((record.message) & 0xff),
+ 0,
+ };
+ printf("globalEventHandler() what=%hu, message=%s\n", record.what, messageStr);
+ OSStatus result = AEProcessAppleEvent(&record);
+
+ if (result == errAEEventNotHandled)
+ {
+ printf("errAEEventNotHandled in globalEventHandler()\n");
+ }
+ else if (result != noErr)
+ {
+ printf("globalEventHandler() AEProcessAppleEvent() returned ERROR: %d (%s)\n", result, strerror(result));
+ }
+ else
+ {
+ printf("globalEventHandler() AEProcessAppleEvent() success!\n");
+ }
+
+ if (inQueue)
+ {
+ ReleaseEvent(event);
+ }
+
+ return noErr;
+}
+
+#endif
diff --git a/makefile b/makefile
index 96f04b0be..36c14b9c5 100644
--- a/makefile
+++ b/makefile
@@ -279,6 +279,7 @@ LIBRARY_FLAGS += \
-static-libstdc++
else
LIBRARY_FLAGS += \
+ -framework Carbon \
-framework Foundation \
-framework CoreFoundation \
-framework CoreMIDI \
diff --git a/tools/mac_events_test/.gitignore b/tools/mac_events_test/.gitignore
new file mode 100644
index 000000000..5e53c4401
--- /dev/null
+++ b/tools/mac_events_test/.gitignore
@@ -0,0 +1,4 @@
+test
+test.icns
+test.app
+
diff --git a/tools/mac_events_test/Info.plist b/tools/mac_events_test/Info.plist
new file mode 100644
index 000000000..3484970e6
--- /dev/null
+++ b/tools/mac_events_test/Info.plist
@@ -0,0 +1,55 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ test
+ CFBundleGetInfoString
+ Public Domain
+ CFBundleIconFile
+ test.icns
+ CFBundleIdentifier
+ com.dylwhich.osxargtest
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ test
+ CFBundleVersion
+ 1.0
+ NSHumanReadableCopyright
+ Public Domain
+ LSMinimumSystemVersion
+ 10
+ CFBundleDocumentTypes
+
+
+ CFBundleTypeExtensions
+
+ midi
+ mid
+ kar
+
+ CFBundleTypeIconFile
+ test.icns
+ CFBundleTypeNames
+ MIDI File
+ CFBundleTypeOSTypes
+
+ Midi
+
+ LSItemContentTypes
+
+ public.midi-audio
+
+ CFBundleTypeRole
+ Viewer
+
+
+
+
diff --git a/tools/mac_events_test/main.c b/tools/mac_events_test/main.c
new file mode 100644
index 000000000..4d886ef3f
--- /dev/null
+++ b/tools/mac_events_test/main.c
@@ -0,0 +1,274 @@
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+extern Boolean ConvertEventRefToEventRecord(EventRef, EventRecord*);
+
+#define LOG_FILE "/Users/dylwhich/events.log"
+
+#ifdef LOG_FILE
+#define LOG(...) fprintf(logFile, __VA_ARGS__)
+#else
+#define LOG printf
+#endif
+
+typedef void (*MacOpenFileCb)(const char* path);
+
+typedef struct
+{
+ EventHandlerUPP globalEventHandler;
+ AEEventHandlerUPP appleEventHandler;
+ MacOpenFileCb openFileCallback;
+ EventHandlerRef globalEventHandlerRef;
+} MacOpenFileHandler;
+
+bool installMacOpenFileHandler(MacOpenFileHandler* handlerRef, MacOpenFileCb callback);
+void checkForEventsMacOpenFileHandler(MacOpenFileHandler* handlerRef, uint32_t millis);
+void uninstallMacOpenFileHandler(MacOpenFileHandler* handlerRef);
+
+static pascal OSErr handleOpenDocumentEvent(const AppleEvent* event, AppleEvent* reply, SRefCon handlerRef);
+static OSStatus globalEventHandler(EventHandlerCallRef handler, EventRef event, void* data);
+static void doOpenCb(const char* path);
+
+FILE* logFile;
+
+const EventTypeSpec eventTypes[] = {{.eventClass = kEventClassAppleEvent, .eventKind = kEventAppleEvent}};
+
+static void doOpenCb(const char* path)
+{
+ LOG("Got file: %s\n", path);
+}
+
+int main(int argc, char** argv)
+{
+#ifdef LOG_FILE
+ logFile = fopen(LOG_FILE, "a");
+#endif
+
+ LOG("\nStarting up\n");
+
+ LOG("Installing event handler...\n");
+ MacOpenFileHandler handler;
+
+ bool installed = installMacOpenFileHandler(&handler, doOpenCb);
+
+ if (installed)
+ {
+ LOG("OK!\n");
+ checkForEventsMacOpenFileHandler(&handler, 500);
+ uninstallMacOpenFileHandler(&handler);
+ LOG("Done processing events\n");
+ }
+ else
+ {
+ LOG("FAILED!");
+ return 1;
+ }
+
+ return 0;
+}
+
+bool installMacOpenFileHandler(MacOpenFileHandler* handlerRef, MacOpenFileCb callback)
+{
+ // Init handler
+ handlerRef->appleEventHandler = NULL;
+ handlerRef->globalEventHandler = NULL;
+ handlerRef->openFileCallback = callback;
+
+ // Install handler
+ handlerRef->appleEventHandler = NewAEEventHandlerUPP(handleOpenDocumentEvent);
+ OSStatus result = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, handlerRef->appleEventHandler, (SRefCon)handlerRef, false);
+
+ if (result != noErr)
+ {
+ LOG("Failed to install OpenDocument handler\n");
+ uninstallMacOpenFileHandler(handlerRef);
+ return false;
+ }
+
+ // Install the application-level handler
+ handlerRef->globalEventHandler = NewEventHandlerUPP(globalEventHandler);
+ result = InstallApplicationEventHandler(handlerRef->globalEventHandler, 1, eventTypes, NULL, &handlerRef->globalEventHandlerRef);
+
+ if (result != noErr)
+ {
+ LOG("Failed to install global event handler\n");
+ uninstallMacOpenFileHandler(handlerRef);
+ return false;
+ }
+
+ // Handler successfully installed
+ return true;
+}
+
+// Runs the event loop and waits for up to the specified time
+// The callback will be called for any events that are recevied
+void checkForEventsMacOpenFileHandler(MacOpenFileHandler* handlerRef, uint32_t millis)
+{
+ EventTimeout timeout = millis / 1000.0;
+ while (1)
+ {
+ EventRef eventRef;
+
+ OSErr result = ReceiveNextEvent(1, eventTypes, timeout, kEventRemoveFromQueue, &eventRef);
+
+ if (result == eventLoopTimedOutErr)
+ {
+ LOG("No event received after timeout\n");
+ break;
+ }
+ else if (result == noErr)
+ {
+ LOG("Got an event!\n");
+ result = SendEventToEventTarget(eventRef, GetEventDispatcherTarget());
+ ReleaseEvent(eventRef);
+ if (result != noErr)
+ {
+ if (result == eventNotHandledErr)
+ {
+ LOG("Got eventNotHandledErr from SendEventToEventTarget()\n");
+ }
+ else
+ {
+ LOG("Error in SendEventToEventTarget(): %d %s\n", result, strerror(result));
+ break;
+ }
+ }
+ }
+ else
+ {
+ LOG("Error in ReceiveNextEvent()\n");
+ break;
+ }
+ }
+}
+
+// Uninstalls and deletes
+void uninstallMacOpenFileHandler(MacOpenFileHandler* handlerRef)
+{
+ if (handlerRef != NULL)
+ {
+ if (handlerRef->appleEventHandler != NULL)
+ {
+ DisposeAEEventHandlerUPP(handlerRef->appleEventHandler);
+ handlerRef->appleEventHandler = NULL;
+ }
+
+ if (handlerRef->globalEventHandler != NULL)
+ {
+ DisposeEventHandlerUPP(handlerRef->globalEventHandler);
+ handlerRef->globalEventHandler = NULL;
+ }
+ handlerRef->openFileCallback = NULL;
+ }
+}
+
+static pascal OSErr handleOpenDocumentEvent(const AppleEvent* event, AppleEvent* reply, SRefCon handlerRefArg)
+{
+ LOG("Hey, handleOpenDocumentEvent() got called!\n");
+ MacOpenFileHandler* handlerRef = (MacOpenFileHandler*)handlerRefArg;
+
+ AEDescList docList;
+ OSErr result = AEGetParamDesc(event, keyDirectObject, typeAEList, &docList);
+
+ if (result != noErr)
+ {
+ return result;
+ }
+
+ long docCount = 0;
+ result = AECountItems(&docList, &docCount);
+ if (result != noErr)
+ {
+ return result;
+ }
+
+ char buffer[2048];
+
+ // Yup, it's zero-indexed. Weird.
+ for (long i = 1; i <= docCount; i++)
+ {
+ AEKeyword keyword;
+ DescType docType;
+ Size docSize;
+
+ result = AEGetNthPtr(&docList, i, typeFileURL, &keyword, &docType, &buffer, sizeof(buffer), &docSize);
+
+ if (result != noErr)
+ {
+ LOG("Error getting event doc index %ld\n", i);
+ return result;
+ }
+
+ CFURLRef docUrlRef = CFURLCreateWithBytes(NULL, (UInt8*)buffer, docSize, kCFStringEncodingUTF8, NULL);
+
+ if (docUrlRef != NULL)
+ {
+ CFStringRef docStringRef = CFURLCopyFileSystemPath(docUrlRef, kCFURLPOSIXPathStyle);
+ if (docStringRef != NULL)
+ {
+ char pathBuffer[1024];
+ if (CFStringGetFileSystemRepresentation(docStringRef, pathBuffer, sizeof(pathBuffer)))
+ {
+ handlerRef->openFileCallback(pathBuffer);
+ LOG("Successfully handled event?\n");
+ }
+ CFRelease(docStringRef);
+ }
+ CFRelease(docUrlRef);
+ }
+ }
+
+ return AEDisposeDesc(&docList);
+}
+
+static OSStatus globalEventHandler(EventHandlerCallRef handler, EventRef event, void* data)
+{
+ LOG("globalEventHandler()!!!!!!\n");
+
+ bool inQueue = IsEventInQueue(GetMainEventQueue(), event);
+
+ if (inQueue)
+ {
+ RetainEvent(event);
+ RemoveEventFromQueue(GetMainEventQueue(), event);
+ }
+
+ EventRecord record;
+ ConvertEventRefToEventRecord(event, &record);
+ char messageStr[5] =
+ {
+ (char)((record.message >> 24) & 0xff),
+ (char)((record.message >> 16) & 0xff),
+ (char)((record.message >> 8) & 0xff),
+ (char)((record.message) & 0xff),
+ 0,
+ };
+ LOG("globalEventHandler() what=%hu, message=%s\n", record.what, messageStr);
+ OSStatus result = AEProcessAppleEvent(&record);
+
+ if (result == errAEEventNotHandled)
+ {
+ LOG("errAEEventNotHandled in globalEventHandler()\n");
+ }
+ else if (result != noErr)
+ {
+ LOG("globalEventHandler() AEProcessAppleEvent() returned ERROR: %d (%s)\n", result, strerror(result));
+ }
+ else
+ {
+ LOG("globalEventHandler() AEProcessAppleEvent() success!\n");
+ }
+
+ if (inQueue)
+ {
+ ReleaseEvent(event);
+ }
+
+ return noErr;
+}
+
diff --git a/tools/mac_events_test/makefile b/tools/mac_events_test/makefile
new file mode 100644
index 000000000..00225e3ad
--- /dev/null
+++ b/tools/mac_events_test/makefile
@@ -0,0 +1,218 @@
+# Makefile by Adam, 2022
+
+################################################################################
+# What OS we're compiling on
+################################################################################
+
+IS_WSL := 0
+ifeq ($(OS),Windows_NT)
+ HOST_OS = Windows
+else
+ UNAME_S := $(shell uname -s)
+ ifeq ($(UNAME_S),Linux)
+ HOST_OS = Linux
+ # Check if this is WSL. 0 for not WSL, 1 for WSL
+ IS_WSL := $(shell uname -a | grep -i WSL | wc -l)
+ else ifeq ($(UNAME_S),Darwin)
+ HOST_OS = Darwin
+ endif
+endif
+
+################################################################################
+# Programs to use
+################################################################################
+
+CC = gcc
+
+FIND:=find
+
+
+################################################################################
+# Source Files
+################################################################################
+
+SOURCES := main.c
+
+################################################################################
+# Includes
+################################################################################
+
+################################################################################
+# Compiler Flags
+################################################################################
+
+# These are flags for the compiler, all files
+CFLAGS = \
+ -c \
+ -g \
+ -fdiagnostics-color=always \
+ -ffunction-sections \
+ -fdata-sections \
+ -gdwarf-4 \
+ -ggdb \
+ -O2 \
+ -fno-jump-tables \
+ -finline-functions \
+ -std=gnu17
+
+# Required for OpenGL and some other libraries
+CFLAGS += \
+ -mmacosx-version-min=10.0
+
+# These are warning flags that the IDF uses
+CFLAGS_WARNINGS = \
+ -Wall \
+ -Werror=all \
+ -Wno-error=unused-function \
+ -Wno-error=unused-variable \
+ -Wno-error=deprecated-declarations \
+ -Wextra \
+ -Wno-unused-parameter \
+ -Wno-sign-compare \
+ -Wno-enum-conversion \
+ -Wno-error=unused-but-set-variable
+
+# These are warning flags that I like
+CFLAGS_WARNINGS_EXTRA = \
+ -Wundef \
+ -Wformat=2 \
+ -Winvalid-pch \
+ -Wmissing-format-attribute \
+ -Wmissing-include-dirs \
+ -Wpointer-arith \
+ -Wunused-local-typedefs \
+ -Wuninitialized \
+ -Wshadow \
+ -Wredundant-decls \
+ -Wswitch \
+ -Wcast-align \
+ -Wformat-nonliteral \
+ -Wno-switch-default \
+ -Wunused \
+ -Wunused-macros \
+ -Wmissing-declarations \
+ -Wmissing-prototypes \
+ -Wcast-qual \
+ -Wno-switch \
+ -Wunused-result \
+# -Wstrict-prototypes \
+# -Wpedantic \
+# -Wconversion \
+# -Wsign-conversion \
+# -Wdouble-promotion
+
+################################################################################
+# Defines
+################################################################################
+
+# Create a variable with the git hash and branch name
+GIT_HASH = \"$(shell git rev-parse --short=7 HEAD)\"
+
+# Used by the ESP SDK
+DEFINES_LIST = \
+ _GNU_SOURCE \
+ _POSIX_READER_WRITER_LOCKS
+
+DEFINES = $(patsubst %, -D%, $(DEFINES_LIST))
+
+################################################################################
+# Output Objects
+################################################################################
+
+# This is the directory in which object files will be stored
+OBJ_DIR = obj
+
+# This is a list of objects to build
+OBJECTS = $(patsubst %.c, $(OBJ_DIR)/%.o, $(SOURCES))
+
+################################################################################
+# Linker options
+################################################################################
+
+# This is a list of libraries to include. Order doesn't matter
+
+#LIBS = m X11 GL pthread Xext Xinerama
+LIBS =
+
+# These are directories to look for library files in
+LIB_DIRS =
+
+# On MacOS we need to ensure that X11 is added for OpenGL and some others
+#ifeq ($(HOST_OS),Darwin)
+# LIB_DIRS = /opt/X11/lib
+#endif
+
+# This combines the flags for the linker to find and use libraries
+LIBRARY_FLAGS = $(patsubst %, -L%, $(LIB_DIRS)) $(patsubst %, -l%, $(LIBS)) \
+ -ggdb
+
+LIBRARY_FLAGS += \
+ -framework Carbon
+
+################################################################################
+# Build Filenames
+################################################################################
+
+# These are the files to build
+EXECUTABLE = test
+BUNDLE = test.app
+ICONS = test.icns
+
+################################################################################
+# Targets for Building
+################################################################################
+
+# This list of targets do not build files which match their name
+.PHONY: all bundle clean print-%
+
+# Build the executable
+all: $(EXECUTABLE)
+
+# To build the main file, you have to compile the objects
+$(EXECUTABLE): $(OBJECTS)
+ $(CC) $(OBJECTS) $(LIBRARY_FLAGS) -o $@
+
+# This compiles each c file into an o file
+./$(OBJ_DIR)/%.o: ./%.c
+ @mkdir -p $(@D) # This creates a directory before building an object in it.
+ $(CC) $(CFLAGS) $(CFLAGS_WARNINGS) $(CFLAGS_WARNINGS_EXTRA) $(DEFINES) $(INC) $< -o $@
+
+bundle: $(BUNDLE)
+
+$(BUNDLE): $(EXECUTABLE) $(ICONS) Info.plist
+ rm -rf $(BUNDLE)
+ mkdir -p $(BUNDLE)/Contents/{MacOS,Resources,libs}
+ cp Info.plist $(BUNDLE)/Contents/Info.plist
+ echo "APPLTest" > $(BUNDLE)/Contents/PkgInfo
+ cp $(ICONS) $(BUNDLE)/Contents/Resources/
+ vtool -set-build-version macos 10.0 10.0 -replace -output $(BUNDLE)/Contents/MacOS/test $(EXECUTABLE)
+ dylibbundler -od -b -x ./$(BUNDLE)/Contents/MacOS/test -d ./$(BUNDLE)/Contents/libs/
+
+
+$(ICONS): ../../emulator/resources/icon.png
+ rm -rf test.iconset
+ mkdir -p test.iconset
+ sips -z 16 16 $< --out test.iconset/icon_16x16.png
+ sips -z 32 32 $< --out test.iconset/icon_16x16@2x.png
+ sips -z 32 32 $< --out test.iconset/icon_32x32.png
+ sips -z 64 64 $< --out test.iconset/icon_32x32@2x.png
+ sips -z 128 128 $< --out test.iconset/icon_128x128.png
+ sips -z 256 256 $< --out test.iconset/icon_128x128@2x.png
+ sips -z 256 256 $< --out test.iconset/icon_256x256.png
+ sips -z 512 512 $< --out test.iconset/icon_256x256@2x.png
+ sips -z 512 512 $< --out test.iconset/icon_512x512.png
+ sips -z 1024 1024 $< --out test.iconset/icon_512x512@2x.png
+ iconutil -c icns -o $(ICONS) test.iconset
+ rm -r test.iconset
+
+# This cleans emulator files
+clean:
+ -@rm -rf $(OBJECTS) $(EXECUTABLE) $(ICONS) test.iconset $(BUNDLE)
+
+
+################################################################################
+# Firmware targets
+################################################################################
+
+# Print any value from this makefile
+print-% : ; @echo $* = $($*)