mirror of
https://github.com/geode-sdk/geode.git
synced 2024-12-13 17:41:15 -05:00
351 lines
No EOL
11 KiB
Text
351 lines
No EOL
11 KiB
Text
#include <Geode/DefaultInclude.hpp>
|
|
|
|
using namespace geode::prelude;
|
|
|
|
#include <Geode/loader/Dirs.hpp>
|
|
#import <AppKit/AppKit.h>
|
|
#include <Geode/Utils.hpp>
|
|
#include <Geode/binding/GameManager.hpp>
|
|
#include <objc/runtime.h>
|
|
#include <Geode/utils/web.hpp>
|
|
|
|
#define CommentType CommentTypeDummy
|
|
#import <Cocoa/Cocoa.h>
|
|
#undef CommentType
|
|
|
|
bool utils::clipboard::write(std::string const& data) {
|
|
[[NSPasteboard generalPasteboard] clearContents];
|
|
[[NSPasteboard generalPasteboard] setString:[NSString stringWithUTF8String:data.c_str()]
|
|
forType:NSPasteboardTypeString];
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string utils::clipboard::read() {
|
|
auto clipboard =
|
|
[[[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString] UTF8String];
|
|
if (!clipboard) return std::string("");
|
|
return std::string(clipboard);
|
|
}
|
|
|
|
bool utils::file::openFolder(ghc::filesystem::path const& path) {
|
|
NSURL* fileURL = [NSURL fileURLWithPath:[NSString stringWithUTF8String:path.string().c_str()]];
|
|
NSURL* folderURL = [fileURL URLByDeletingLastPathComponent];
|
|
[[NSWorkspace sharedWorkspace] openURL:folderURL];
|
|
return true;
|
|
}
|
|
|
|
void utils::web::openLinkInBrowser(std::string const& url) {
|
|
[[NSWorkspace sharedWorkspace]
|
|
openURL:[NSURL URLWithString:[NSString stringWithUTF8String:url.c_str()]]];
|
|
}
|
|
|
|
/*@interface FileDialog : NSObject
|
|
+ (Result<ghc::filesystem::path>)importDocumentWithMode:(file::PickMode)mode options:(file::FilePickOptions const&)options mult:(bool)mult;
|
|
+ (Result<std::vector<ghc::filesystem::path>>)importDocumentsWithOptions:(file::FilePickOptions const&)options;
|
|
@end
|
|
|
|
@implementation FileDialog
|
|
|
|
+ (Result<ghc::filesystem::path>)importDocumentWithMode:(file::PickMode)mode options:(file::FilePickOptions const&)options mult:(bool)mult {
|
|
NSOpenPanel* panel = [NSOpenPanel openPanel];
|
|
// TODO: [panel setAllowedFileTypes:@[]];
|
|
|
|
if (options.defaultPath) {
|
|
auto defaultPath = [NSString stringWithCString:options.defaultPath->c_str() encoding:NSUTF8StringEncoding];
|
|
[panel setDirectoryURL: [NSURL fileURLWithPath: defaultPath]];
|
|
}
|
|
|
|
panel.allowsMultipleSelection = mult;
|
|
|
|
int result = [panel runModal];
|
|
if (result == NSFileHandlingPanelOKButton) {
|
|
auto fileUrl = [[panel URLs] objectAtIndex:0];
|
|
auto path = std::string([[fileUrl path] UTF8String], [[fileUrl path] lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
|
return Ok(ghc::filesystem::path(path));
|
|
} else {
|
|
return Err(result);
|
|
}
|
|
}
|
|
|
|
@end*/
|
|
|
|
namespace {
|
|
using FileResult = Result<std::vector<ghc::filesystem::path>>;
|
|
}
|
|
|
|
@interface FileDialog : NSObject
|
|
+(Result<std::vector<ghc::filesystem::path>>) filePickerWithMode:(file::PickMode)mode options:(file::FilePickOptions const&)options multiple:(bool)mult;
|
|
+(void) dispatchFilePickerWithMode:(file::PickMode)mode options:(file::FilePickOptions const&)options multiple:(bool)mult onCompletion:(void(^)(FileResult))onCompletion;
|
|
@end
|
|
|
|
@implementation FileDialog
|
|
+(Result<std::vector<ghc::filesystem::path>>) filePickerWithMode:(file::PickMode)mode options:(file::FilePickOptions const&)options multiple:(bool)mult {
|
|
NSSavePanel* panel;
|
|
if (mode == file::PickMode::SaveFile)
|
|
panel = [NSSavePanel savePanel];
|
|
else
|
|
panel = [NSOpenPanel openPanel];
|
|
|
|
[panel setCanCreateDirectories: TRUE];
|
|
|
|
|
|
// default path
|
|
if (options.defaultPath) {
|
|
auto defaultPath = [NSString stringWithUTF8String:options.defaultPath->c_str()];
|
|
[panel setDirectoryURL: [NSURL URLWithString: defaultPath]];
|
|
}
|
|
|
|
// other
|
|
if (mode != file::PickMode::SaveFile) {
|
|
auto openPanel = (NSOpenPanel*)panel;
|
|
|
|
if (mode == file::PickMode::OpenFile){
|
|
[openPanel setCanChooseDirectories: NO];
|
|
[openPanel setCanChooseFiles: YES];
|
|
}
|
|
else {
|
|
[openPanel setCanChooseDirectories: YES];
|
|
[openPanel setCanChooseFiles: NO];
|
|
}
|
|
|
|
[openPanel setAllowsMultipleSelection: mult];
|
|
|
|
// allowed files
|
|
// TODO: allowed files using the NSOpenSavePanelDelegate xd
|
|
// NSMutableArray* allowed = [NSMutableArray array];
|
|
|
|
// for (auto& f : options.filters) {
|
|
// for (auto& i : f.files) {
|
|
// auto nsstr = [NSString stringWithUTF8String: i.c_str()];
|
|
|
|
// if (![allowed containsObject: nsstr])
|
|
// [allowed addObject: nsstr];
|
|
// }
|
|
// }
|
|
|
|
// if (options.filters.size())
|
|
// [panel setAllowedFileTypes: allowed];
|
|
}
|
|
|
|
// run thing
|
|
|
|
int result = [panel runModal];
|
|
|
|
if (result == NSModalResponseOK) {
|
|
std::vector<ghc::filesystem::path> fileURLs;
|
|
if (mode == file::PickMode::SaveFile) {
|
|
fileURLs.push_back(std::string([[[panel URL] path] UTF8String]));
|
|
}
|
|
else {
|
|
auto openPanel = (NSOpenPanel*)panel;
|
|
|
|
for (NSURL* i in openPanel.URLs) {
|
|
fileURLs.push_back(std::string(i.path.UTF8String));
|
|
}
|
|
}
|
|
return Ok(fileURLs);
|
|
} else {
|
|
return Err("File picker cancelled");
|
|
}
|
|
}
|
|
|
|
+(void) dispatchFilePickerWithMode:(file::PickMode)mode options:(file::FilePickOptions const&)options multiple:(bool)mult onCompletion:(void(^)(FileResult))onCompletion {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
auto result = [self filePickerWithMode:mode options:options multiple:mult];
|
|
onCompletion(result);
|
|
});
|
|
}
|
|
|
|
@end
|
|
|
|
Result<ghc::filesystem::path> file::pickFile(
|
|
file::PickMode mode, file::FilePickOptions const& options
|
|
) {
|
|
return Err("Use the callback version");
|
|
}
|
|
|
|
GEODE_DLL void file::pickFile(
|
|
PickMode mode, FilePickOptions const& options,
|
|
MiniFunction<void(ghc::filesystem::path)> callback,
|
|
MiniFunction<void()> failed
|
|
) {
|
|
[FileDialog dispatchFilePickerWithMode:mode options:options multiple:false onCompletion: ^(FileResult result) {
|
|
Loader::get()->queueInMainThread([=]() {
|
|
if (result.isOk()) {
|
|
callback(std::move(result.unwrap()[0]));
|
|
} else {
|
|
failed();
|
|
}
|
|
});
|
|
}];
|
|
}
|
|
|
|
Result<std::vector<ghc::filesystem::path>> file::pickFiles(
|
|
file::FilePickOptions const& options
|
|
) {
|
|
return Err("Use the callback version");
|
|
}
|
|
|
|
GEODE_DLL void file::pickFiles(
|
|
FilePickOptions const& options,
|
|
MiniFunction<void(std::vector<ghc::filesystem::path>)> callback,
|
|
MiniFunction<void()> failed
|
|
) {
|
|
[FileDialog dispatchFilePickerWithMode: file::PickMode::OpenFile options:options multiple:true onCompletion: ^(FileResult result) {
|
|
Loader::get()->queueInMainThread([=]() {
|
|
if (result.isOk()) {
|
|
callback(std::move(result.unwrap()));
|
|
} else {
|
|
failed();
|
|
}
|
|
});
|
|
}];
|
|
}
|
|
|
|
CCPoint cocos::getMousePos() {
|
|
auto windowFrame = NSApp.mainWindow.frame;
|
|
auto viewFrame = NSApp.mainWindow.contentView.frame;
|
|
auto scaleFactor = CCPoint(CCDirector::get()->getWinSize()) / ccp(viewFrame.size.width, viewFrame.size.height);
|
|
auto mouse = [NSEvent mouseLocation];
|
|
return ccp(mouse.x - windowFrame.origin.x, mouse.y - windowFrame.origin.y) * scaleFactor;
|
|
}
|
|
|
|
ghc::filesystem::path dirs::getGameDir() {
|
|
static auto path = [] {
|
|
std::array<char, PATH_MAX> gddir;
|
|
|
|
uint32_t out = PATH_MAX;
|
|
_NSGetExecutablePath(gddir.data(), &out);
|
|
|
|
ghc::filesystem::path gdpath = gddir.data();
|
|
auto currentPath = ghc::filesystem::canonical(gdpath.parent_path().parent_path());
|
|
return currentPath;
|
|
}();
|
|
|
|
return path;
|
|
}
|
|
|
|
ghc::filesystem::path dirs::getSaveDir() {
|
|
static auto path = [] {
|
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
|
NSString *applicationSupportDirectory = [paths firstObject];
|
|
|
|
ghc::filesystem::path supportPath = [applicationSupportDirectory UTF8String];
|
|
auto currentPath = supportPath / "GeometryDash";
|
|
return currentPath;
|
|
}();
|
|
|
|
return path;
|
|
}
|
|
|
|
ghc::filesystem::path dirs::getModRuntimeDir() {
|
|
return dirs::getGeodeDir() / "unzipped";
|
|
}
|
|
|
|
void geode::utils::game::exit() {
|
|
if (CCApplication::sharedApplication() &&
|
|
(GameManager::get()->m_playLayer || GameManager::get()->m_levelEditorLayer)) {
|
|
log::error("Cannot exit in PlayLayer or LevelEditorLayer!");
|
|
return;
|
|
}
|
|
|
|
class Exit : public CCObject {
|
|
public:
|
|
void shutdown() {
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wobjc-method-access"
|
|
[[[NSClassFromString(@"AppControllerManager") sharedInstance] controller] shutdownGame];
|
|
#pragma clang diagnostic pop
|
|
}
|
|
};
|
|
|
|
CCDirector::get()->getActionManager()->addAction(CCSequence::create(
|
|
CCDelayTime::create(0.5f),
|
|
CCCallFunc::create(nullptr, callfunc_selector(Exit::shutdown)),
|
|
nullptr
|
|
), CCDirector::get()->getRunningScene(), false);
|
|
}
|
|
|
|
void geode::utils::game::restart() {
|
|
if (CCApplication::sharedApplication() &&
|
|
(GameManager::get()->m_playLayer || GameManager::get()->m_levelEditorLayer)) {
|
|
log::error("Cannot restart in PlayLayer or LevelEditorLayer!");
|
|
return;
|
|
}
|
|
|
|
auto restart = +[] {
|
|
log::info("Restarting game...");
|
|
auto gdExec = dirs::getGameDir() / "MacOS" / "Geometry Dash";
|
|
|
|
NSTask *task = [NSTask new];
|
|
[task setLaunchPath: [NSString stringWithUTF8String: gdExec.string().c_str()]];
|
|
[task launch];
|
|
};
|
|
|
|
std::atexit(restart);
|
|
exit();
|
|
}
|
|
|
|
void geode::utils::game::launchLoaderUninstaller(bool deleteSaveData) {
|
|
log::error("Launching Geode uninstaller is not supported on macOS");
|
|
}
|
|
|
|
Result<> geode::hook::addObjcMethod(std::string const& className, std::string const& selectorName, void* imp) {
|
|
auto cls = objc_getClass(className.c_str());
|
|
if (!cls)
|
|
return Err("Class not found");
|
|
|
|
auto sel = sel_registerName(selectorName.c_str());
|
|
|
|
class_addMethod(cls, sel, (IMP)imp, "v@:");
|
|
|
|
return Ok();
|
|
}
|
|
Result<void*> geode::hook::getObjcMethodImp(std::string const& className, std::string const& selectorName) {
|
|
auto cls = objc_getClass(className.c_str());
|
|
if (!cls)
|
|
return Err("Class not found");
|
|
|
|
auto sel = sel_registerName(selectorName.c_str());
|
|
|
|
auto method = class_getInstanceMethod(cls, sel);
|
|
if (!method)
|
|
return Err("Method not found");
|
|
|
|
return Ok((void*)method_getImplementation(method));
|
|
}
|
|
|
|
bool geode::utils::permission::getPermissionStatus(Permission permission) {
|
|
return true; // unimplemented
|
|
}
|
|
|
|
void geode::utils::permission::requestPermission(Permission permission, utils::MiniFunction<void(bool)> callback) {
|
|
callback(true); // unimplemented
|
|
}
|
|
|
|
#include "../../utils/thread.hpp"
|
|
|
|
std::string geode::utils::thread::getDefaultName() {
|
|
uint64_t tid = 0ul;
|
|
pthread_threadid_np(nullptr, &tid);
|
|
|
|
return fmt::format("Thread #{}", tid);
|
|
}
|
|
|
|
void geode::utils::thread::platformSetName(std::string const& name) {
|
|
pthread_setname_np(name.c_str());
|
|
}
|
|
|
|
float geode::utils::getDisplayFactor() {
|
|
float displayScale = 1.f;
|
|
if ([[NSScreen mainScreen] respondsToSelector:@selector(backingScaleFactor)]) {
|
|
NSArray* screens = [NSScreen screens];
|
|
for (int i = 0; i < screens.count; i++) {
|
|
float s = [screens[i] backingScaleFactor];
|
|
if (s > displayScale)
|
|
displayScale = s;
|
|
}
|
|
}
|
|
return displayScale;
|
|
} |