From 7da4550eea43a5ec83d1afc88022aaa62827febc Mon Sep 17 00:00:00 2001
From: WaluigiWare64 <68647953+WaluigiWare64@users.noreply.github.com>
Date: Sun, 29 Nov 2020 08:11:33 -0800
Subject: Add support for macOS (#771)
* use shm_open() instead of memfd_create() on macOS
malloc.h isn't a header on macOS
* Change OpenGL headers + create ifdef for DO_PROCLIST
macOS seems to already have the OpenGL functions defined, without the ifdef, it gives "ambiguous references" errors.
* macOS doesn't have ->gregs in uc_mcontext
and it doesn't have REG_RIP either
https://github.com/gperftools/gperftools/blob/master/m4/pc_from_ucontext.m4
* use getpid() to make memory file name unique
* #ifndef __APPLE__ for AF_PACKET and linux/if_packet.h
* Add include and link directories for macOS and link the OpenGL framework
* Add macOS CI
* Use newly added libslirp package from Homebrew
https://github.com/Homebrew/homebrew-core/pull/63412
* Use Apple's Clang instead of GNU GCC on macOS
* Add macOS build instructions to README
* Try to fix macOS undefined symbol
* snprintf doesn't take null terminator into account
* Map new memory on macOS for JIT
* Only use gcc-ar if using GNU Compiler
* re-add fastmem code - whoops!
* Fix style issue - use camelCase not snake_case
* Set Minimum macOS version
* Switch Minimum OS X version to 10.9
* Add macOS libpcap library name
* fix memory leak
* Fix binding keys in macOS
* Allow getting MAC address on macOS
melonDS on Linux uses AF_PACKET, which doesn't exist on macOS. Instead, this commit uses AF_LINK on macOS to get the MAC address.
* Remove unneeded macOS CI dependencies
* Build melonDS app bundle on macOS
Now it is no longer required to install the libraries on macOS, they come with the app bundle.
* fix macOS CI not being able to find macdeployqt
* copy melonDS.app with recursive because it's a folder
* Disable fastmem checkbox on macOS
* Disable fastmem by default in config
* forgot a semicolon
* Don't bundle libraries, causes issues on macOS <10.15
* Update README + allow finding version in Finder on macOS
* Make sure fastmem checkbox stays uncheckable
---
.github/workflows/build-macos.yml | 39 ++++++++++++++++++++++++++++++
CMakeLists.txt | 4 ++-
README.md | 15 ++++++++++++
melonDS.icns | Bin 0 -> 401172 bytes
melonDS.plist | 24 ++++++++++++++++++
src/ARMJIT_Memory.cpp | 31 +++++++++++++++++++++---
src/ARMJIT_x64/ARMJIT_Compiler.cpp | 2 ++
src/ARMJIT_x64/ARMJIT_Linkage.s | 10 ++++++++
src/CMakeLists.txt | 9 ++++++-
src/Config.cpp | 6 ++++-
src/OpenGLSupport.h | 15 ++++++++++--
src/frontend/qt_sdl/CMakeLists.txt | 15 +++++++++++-
src/frontend/qt_sdl/EmuSettingsDialog.cpp | 7 +++++-
src/frontend/qt_sdl/InputConfigDialog.cpp | 1 +
src/frontend/qt_sdl/LAN_PCap.cpp | 21 ++++++++++++++--
15 files changed, 186 insertions(+), 13 deletions(-)
create mode 100644 .github/workflows/build-macos.yml
create mode 100644 melonDS.icns
create mode 100644 melonDS.plist
diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml
new file mode 100644
index 0000000..e2b942a
--- /dev/null
+++ b/.github/workflows/build-macos.yml
@@ -0,0 +1,39 @@
+name: CMake Build (macOS x86-64)
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+
+env:
+ BUILD_TYPE: Release
+
+jobs:
+ build:
+
+ runs-on: macos-latest
+
+ steps:
+ - uses: actions/checkout@v1
+ - name: Install dependencies
+ working-directory: ${{runner.workspace}}
+ run: |
+ brew install cmake sdl2 qt5 libslirp
+ - name: Create build environment
+ run: mkdir ${{runner.workspace}}/build
+ - name: Configure
+ working-directory: ${{runner.workspace}}/build
+ run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DQt5_DIR=$(brew --prefix qt5)/lib/cmake/Qt5
+ - name: Make
+ working-directory: ${{runner.workspace}}/build
+ run: |
+ make -j$(sysctl -n hw.ncpu)
+ mkdir dist
+ cp -r melonDS.app dist
+ - uses: actions/upload-artifact@v1
+ with:
+ name: melonDS.app
+ path: ${{runner.workspace}}/build/dist
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 04ad2a5..fcd4741 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,6 +8,8 @@ if (POLICY CMP0076)
cmake_policy(SET CMP0076 NEW)
endif()
+set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version")
+
project(melonDS CXX)
set(CMAKE_C_STANDARD 11)
@@ -100,4 +102,4 @@ add_subdirectory(src)
if (BUILD_QT_SDL)
add_subdirectory(src/frontend/qt_sdl)
-endif()
+endif()
\ No newline at end of file
diff --git a/README.md b/README.md
index 2fb2315..64489db 100644
--- a/README.md
+++ b/README.md
@@ -84,6 +84,21 @@ If everything went well, melonDS and the libraries it needs should now be in the
```
If everything went well, melonDS should now be in the `dist` folder.
+### macOS:
+1. Install the [Homebrew Package Manager](https://brew.sh)
+2. Install dependencies: `brew install git pkg-config cmake sdl2 qt5 libslirp`
+3. Compile:
+ ```zsh
+ git clone https://github.com/Arisotura/melonDS.git
+ cd melonDS
+ mkdir build && cd build
+ cmake .. -DQt5_DIR=$(brew --prefix qt5)/lib/cmake/Qt5
+ make -j$(sysctl -n hw.ncpu)
+ mkdir dist && cp -r melonDS.app dist
+ ```
+If everything went well, melonDS.app should now be in the `dist` folder.
+
+
## TODO LIST
* DSi emulation
diff --git a/melonDS.icns b/melonDS.icns
new file mode 100644
index 0000000..b4f3733
Binary files /dev/null and b/melonDS.icns differ
diff --git a/melonDS.plist b/melonDS.plist
new file mode 100644
index 0000000..1328777
--- /dev/null
+++ b/melonDS.plist
@@ -0,0 +1,24 @@
+
+
+
+
+ CFBundleExecutable
+ melonDS
+ CFBundleIconFile
+ melonDS.icns
+ CFBundleIdentifier
+ net.kuribo64.melonDS
+ CFBundleDevelopmentRegion
+ English
+ CFBundlePackageType
+ APPL
+ CFBundleVersion
+ 0.9
+ CFBundleShortVersionString
+ 0.9
+ NSHumanReadableCopyright
+ Licensed under GPLv3
+ NSHighResolutionCapable
+
+
+
\ No newline at end of file
diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp
index 70d18e6..cc0f149 100644
--- a/src/ARMJIT_Memory.cpp
+++ b/src/ARMJIT_Memory.cpp
@@ -22,7 +22,9 @@
#include "NDSCart.h"
#include "SPU.h"
+#ifndef __APPLE__
#include
+#endif
/*
We're handling fastmem here.
@@ -152,7 +154,12 @@ static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext)
u8* curArea = (u8*)(NDS::CurCPU == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
#ifdef __x86_64__
desc.EmulatedFaultAddr = (u8*)info->si_addr - curArea;
- desc.FaultPC = (u8*)context->uc_mcontext.gregs[REG_RIP];
+ #ifdef __APPLE__
+ desc.FaultPC = (u8*)context->uc_mcontext->__ss.__rip;
+ #else
+ desc.FaultPC = (u8*)context->uc_mcontext.gregs[REG_RIP];
+ #endif
+
#else
desc.EmulatedFaultAddr = (u8*)context->uc_mcontext.fault_address - curArea;
desc.FaultPC = (u8*)context->uc_mcontext.pc;
@@ -161,7 +168,11 @@ static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext)
if (ARMJIT_Memory::FaultHandler(desc))
{
#ifdef __x86_64__
- context->uc_mcontext.gregs[REG_RIP] = (u64)desc.FaultPC;
+ #ifdef __APPLE__
+ context->uc_mcontext->__ss.__rip = (u64)desc.FaultPC;
+ #else
+ context->uc_mcontext.gregs[REG_RIP] = (u64)desc.FaultPC;
+ #endif
#else
context->uc_mcontext.pc = (u64)desc.FaultPC;
#endif
@@ -701,7 +712,14 @@ void Init()
FastMem7Start = MemoryBase + AddrSpaceSize;
MemoryBase = MemoryBase + AddrSpaceSize*2;
- MemoryFile = memfd_create("melondsfastmem", 0);
+ #ifdef __APPLE__
+ char* fastmemPidName = new char[snprintf(NULL, 0, "melondsfastmem%d", getpid()) + 1];
+ sprintf(fastmemPidName, "melondsfastmem%d", getpid());
+ MemoryFile = shm_open(fastmemPidName, O_RDWR|O_CREAT, 0600);
+ delete[] fastmemPidName;
+ #else
+ MemoryFile = memfd_create("melondsfastmem", 0);
+ #endif
ftruncate(MemoryFile, MemoryTotalSize);
struct sigaction sa;
@@ -736,6 +754,11 @@ void DeInit()
svcUnmapProcessCodeMemory(envGetOwnProcessHandle(), (u64)MemoryBaseCodeMem, (u64)MemoryBase, MemoryTotalSize);
virtmemFree(MemoryBaseCodeMem, MemoryTotalSize);
free(MemoryBase);
+#elif defined(__APPLE__)
+ char* fastmemPidName = new char[snprintf(NULL, 0, "melondsfastmem%d", getpid()) + 1];
+ sprintf(fastmemPidName, "melondsfastmem%d", getpid());
+ shm_unlink(fastmemPidName);
+ delete[] fastmemPidName;
#elif defined(_WIN32)
assert(UnmapViewOfFile(MemoryBase));
CloseHandle(MemoryFile);
@@ -1259,4 +1282,4 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size)
return NULL;
}
-}
\ No newline at end of file
+}
diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.cpp b/src/ARMJIT_x64/ARMJIT_Compiler.cpp
index 076f48c..cc4ad80 100644
--- a/src/ARMJIT_x64/ARMJIT_Compiler.cpp
+++ b/src/ARMJIT_x64/ARMJIT_Compiler.cpp
@@ -221,6 +221,8 @@ Compiler::Compiler()
#ifdef _WIN32
DWORD dummy;
VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy);
+ #elif defined(__APPLE__)
+ pageAligned = (u8*)mmap(NULL, 1024*1024*32, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS ,-1, 0);
#else
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
#endif
diff --git a/src/ARMJIT_x64/ARMJIT_Linkage.s b/src/ARMJIT_x64/ARMJIT_Linkage.s
index 0a84df0..8cc0b5f 100644
--- a/src/ARMJIT_x64/ARMJIT_Linkage.s
+++ b/src/ARMJIT_x64/ARMJIT_Linkage.s
@@ -29,8 +29,13 @@
.p2align 4,,15
+#ifdef __APPLE__
+.global _ARM_Dispatch
+_ARM_Dispatch:
+#else
.global ARM_Dispatch
ARM_Dispatch:
+#endif
#ifdef WIN64
push rdi
push rsi
@@ -54,8 +59,13 @@ ARM_Dispatch:
.p2align 4,,15
+#ifdef __APPLE__
+.global _ARM_Ret
+_ARM_Ret:
+#else
.global ARM_Ret
ARM_Ret:
+#endif
mov [RCPU + ARM_CPSR_offset], RCPSR
#ifdef WIN64
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d6c3897..446480d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -100,10 +100,17 @@ if (ENABLE_JIT)
endif()
endif()
+if (APPLE)
+ target_include_directories(core PUBLIC /usr/local/include)
+ target_link_directories(core PUBLIC /usr/local/lib)
+endif()
+
if (ENABLE_OGLRENDERER)
if (WIN32)
target_link_libraries(core ole32 comctl32 ws2_32 opengl32)
- else()
+ elseif (APPLE)
+ target_link_libraries(core "-framework OpenGL")
+ else()
target_link_libraries(core GL EGL)
endif()
else()
diff --git a/src/Config.cpp b/src/Config.cpp
index 341b14c..f7db252 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -73,7 +73,11 @@ ConfigEntry ConfigFile[] =
{"JIT_MaxBlockSize", 0, &JIT_MaxBlockSize, 32, NULL, 0},
{"JIT_BranchOptimisations", 0, &JIT_BranchOptimisations, 1, NULL, 0},
{"JIT_LiteralOptimisations", 0, &JIT_LiteralOptimisations, 1, NULL, 0},
- {"JIT_FastMemory", 0, &JIT_FastMemory, 1, NULL, 0},
+ #ifdef __APPLE__
+ {"JIT_FastMemory", 0, &JIT_FastMemory, 0, NULL, 0},
+ #else
+ {"JIT_FastMemory", 0, &JIT_FastMemory, 1, NULL, 0},
+ #endif
#endif
{"", -1, NULL, 0, NULL, 0}
diff --git a/src/OpenGLSupport.h b/src/OpenGLSupport.h
index 925c0ad..44c511f 100644
--- a/src/OpenGLSupport.h
+++ b/src/OpenGLSupport.h
@@ -23,8 +23,13 @@
#include
// TODO: different includes for each platform
-#include
-#include
+#ifdef __APPLE__
+ #include
+ #include
+#else
+ #include
+ #include
+#endif
#include "Platform.h"
@@ -61,6 +66,11 @@
#endif
+#ifdef __APPLE__
+
+#define DO_PROCLIST(func)
+
+#else
#define DO_PROCLIST(func) \
DO_PROCLIST_1_3(func) \
@@ -128,6 +138,7 @@
\
func(GLGETSTRINGI, glGetStringi); \
+#endif
namespace OpenGL
{
diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt
index 9a0a025..0d695d6 100644
--- a/src/frontend/qt_sdl/CMakeLists.txt
+++ b/src/frontend/qt_sdl/CMakeLists.txt
@@ -95,6 +95,19 @@ if (PORTABLE)
add_definitions(-DPORTABLE)
endif()
+if (APPLE)
+ set_target_properties(melonDS PROPERTIES
+ MACOSX_BUNDLE true
+ MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/melonDS.plist
+ OUTPUT_NAME melonDS
+ )
+
+ # Copy icon into the bundle
+ target_sources(melonDS PRIVATE "${CMAKE_SOURCE_DIR}/melonDS.icns")
+ set_source_files_properties("${CMAKE_SOURCE_DIR}/melonDS.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
+
+endif()
+
install(FILES ../../../net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
install(FILES ../../../icon/melon_16x16.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/16x16/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../../icon/melon_32x32.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/32x32/apps RENAME net.kuribo64.melonDS.png)
@@ -102,4 +115,4 @@ install(FILES ../../../icon/melon_48x48.png DESTINATION ${CMAKE_INSTALL_PREFIX}/
install(FILES ../../../icon/melon_64x64.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/64x64/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../../icon/melon_128x128.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/128x128/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../../icon/melon_256x256.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps RENAME net.kuribo64.melonDS.png)
-install(TARGETS melonDS RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
+install(TARGETS melonDS BUNDLE DESTINATION ${CMAKE_BINARY_DIR} RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp
index 79ce5ed..3183182 100644
--- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp
@@ -65,6 +65,9 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new
ui->chkJITBranchOptimisations->setChecked(Config::JIT_BranchOptimisations != 0);
ui->chkJITLiteralOptimisations->setChecked(Config::JIT_LiteralOptimisations != 0);
ui->chkJITFastMemory->setChecked(Config::JIT_FastMemory != 0);
+ #ifdef __APPLE__
+ ui->chkJITFastMemory->setDisabled(true);
+ #endif
ui->spnJITMaximumBlockSize->setValue(Config::JIT_MaxBlockSize);
#else
ui->chkEnableJIT->setDisabled(true);
@@ -329,6 +332,8 @@ void EmuSettingsDialog::on_chkEnableJIT_toggled()
bool disabled = !ui->chkEnableJIT->isChecked();
ui->chkJITBranchOptimisations->setDisabled(disabled);
ui->chkJITLiteralOptimisations->setDisabled(disabled);
- ui->chkJITFastMemory->setDisabled(disabled);
+ #ifndef __APPLE__
+ ui->chkJITFastMemory->setDisabled(disabled);
+ #endif
ui->spnJITMaximumBlockSize->setDisabled(disabled);
}
diff --git a/src/frontend/qt_sdl/InputConfigDialog.cpp b/src/frontend/qt_sdl/InputConfigDialog.cpp
index 9f08731..eaf1e9b 100644
--- a/src/frontend/qt_sdl/InputConfigDialog.cpp
+++ b/src/frontend/qt_sdl/InputConfigDialog.cpp
@@ -216,6 +216,7 @@ KeyMapButton::KeyMapButton(int* mapping, bool hotkey) : QPushButton()
setCheckable(true);
setText(mappingText());
+ setFocusPolicy(Qt::StrongFocus); //Fixes binding keys in macOS
connect(this, &KeyMapButton::clicked, this, &KeyMapButton::onClick);
}
diff --git a/src/frontend/qt_sdl/LAN_PCap.cpp b/src/frontend/qt_sdl/LAN_PCap.cpp
index ce278bc..8138699 100644
--- a/src/frontend/qt_sdl/LAN_PCap.cpp
+++ b/src/frontend/qt_sdl/LAN_PCap.cpp
@@ -33,7 +33,11 @@
#include
#include
#include
- #include
+ #ifdef __APPLE__
+ #include
+ #else
+ #include
+ #endif
#endif
@@ -66,6 +70,9 @@ const char* PCapLibNames[] =
#ifdef __WIN32__
// TODO: name for npcap in non-WinPCap mode
"wpcap.dll",
+#elif defined(__APPLE__)
+ "libpcap.A.dylib",
+ "libpcap.dylib",
#else
// Linux lib names
"libpcap.so.1",
@@ -276,6 +283,16 @@ bool Init(bool open_adapter)
struct sockaddr_in* sa = (sockaddr_in*)curaddr->ifa_addr;
memcpy(adata->IP_v4, &sa->sin_addr, 4);
}
+ #ifdef __APPLE__
+ else if (af == AF_LINK)
+ {
+ struct sockaddr_dl* sa = (sockaddr_dl*)curaddr->ifa_addr;
+ if (sa->sdl_alen != 6)
+ printf("weird MAC length %d for %s\n", sa->sdl_alen, curaddr->ifa_name);
+ else
+ memcpy(adata->MAC, LLADDR(sa), 6);
+ }
+ #else
else if (af == AF_PACKET)
{
struct sockaddr_ll* sa = (sockaddr_ll*)curaddr->ifa_addr;
@@ -284,7 +301,7 @@ bool Init(bool open_adapter)
else
memcpy(adata->MAC, sa->sll_addr, 6);
}
-
+ #endif
curaddr = curaddr->ifa_next;
}
}
--
cgit v1.2.3