Zachary Hall
9899794b81
Some checks failed
Build / build-gentoo (push) Failing after 1m11s
Build / download-system-deps (push) Successful in 3m44s
Build / get-source-code (push) Successful in 13m23s
Build / build-deb (push) Successful in 11m40s
Build / build-appimage (push) Successful in 4m53s
Build / build-android (push) Failing after 3m19s
Build / build-windows (push) Failing after 8m9s
306 lines
11 KiB
C++
306 lines
11 KiB
C++
#include "options.hpp"
|
|
#include "backend.hpp"
|
|
#include "daemon_backend.hpp"
|
|
#include "proxy_backend.hpp"
|
|
#include "log.hpp"
|
|
#include "thirdparty/CLI11.hpp"
|
|
#include "data.h"
|
|
#include "license.hpp"
|
|
#include <SDL.h>
|
|
#include "assets/assets.h"
|
|
#ifdef __EMSCRIPTEN__
|
|
#include <emscripten.h>
|
|
#endif
|
|
#ifdef __HAIKU__
|
|
#include <os/AppKit.h>
|
|
#include <image.h>
|
|
#endif
|
|
#include "web_functions.hpp"
|
|
#include "cats.hpp"
|
|
using namespace Looper;
|
|
using namespace Looper::Options;
|
|
using namespace Looper::Log;
|
|
#ifdef __EMSCRIPTEN__
|
|
extern "C" {
|
|
void quit();
|
|
}
|
|
#endif
|
|
std::vector<CatData> &get_cat_data() {
|
|
static std::vector<CatData> data;
|
|
return data;
|
|
}
|
|
std::unordered_set<LicenseData> license_data;
|
|
std::unordered_set<LicenseData> &get_license_data() {
|
|
return license_data;
|
|
}
|
|
char *executable_path;
|
|
#ifdef __ANDROID__
|
|
#include <SDL.h>
|
|
#include <jni.h>
|
|
extern jclass MainActivity;
|
|
extern jobject mainActivity;
|
|
extern jmethodID GetUserDir_Method;
|
|
extern jmethodID OpenFilePicker_Method;
|
|
extern jmethodID GetPickedFile_Method;
|
|
extern jmethodID ClearSelected_Method;
|
|
extern jmethodID IsLoading_Method;
|
|
extern JNIEnv *env;
|
|
void initNative() {
|
|
MainActivity = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("com/complecwaft/looper/MainActivity")));
|
|
GetUserDir_Method = env->GetStaticMethodID(MainActivity, "GetUserDir",
|
|
"()Ljava/lang/String;");
|
|
OpenFilePicker_Method = env->GetStaticMethodID(MainActivity, "OpenFilePicker",
|
|
"(Ljava/lang/Object;)V");
|
|
GetPickedFile_Method = env->GetStaticMethodID(MainActivity, "GetPickedFile",
|
|
"()Ljava/lang/String;");
|
|
ClearSelected_Method = env->GetStaticMethodID(MainActivity, "ClearSelected", "()V");
|
|
IsLoading_Method = env->GetStaticMethodID(MainActivity, "IsLoading", "()Z");
|
|
jfieldID singleton = env->GetStaticFieldID(MainActivity, "mSingleton", "Lcom/complecwaft/looper/MainActivity;");
|
|
mainActivity = reinterpret_cast<jobject>(env->NewGlobalRef(env->GetStaticObjectField(MainActivity, singleton)));
|
|
}
|
|
#endif
|
|
extern "C" int looper_run_as_executable(std::vector<std::string> args) {
|
|
#ifdef __ANDROID__
|
|
env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
|
initNative();
|
|
#endif
|
|
#ifdef __EMSCRIPTEN__
|
|
EM_ASM({ Module.wasmTable = wasmTable; });
|
|
#endif
|
|
CLI::App app{DESCRIPTION};
|
|
std::string filename = "";
|
|
app.allow_extras();
|
|
std::string ui_backend_option = "";
|
|
bool full_help = false;
|
|
bool open_window = false;
|
|
app.add_option("-l, --log-level", LogStream::log_level, "Sets the minimum log level to display in the logs.");
|
|
app.add_option("-u, --ui-backend", ui_backend_option, "Specifies which UI backend to use.");
|
|
#ifdef DBUS_ENABLED
|
|
bool daemonize = false;
|
|
bool disable_gui = false;
|
|
bool quit = false;
|
|
app.add_flag("-d, --daemon", daemonize, "Daemonizes the program.");
|
|
app.add_flag("-n, --no-gui", disable_gui, "Don't open the GUI when there is a daemon and there are settings or commands for it. Ignored in daemon mode, or when no changes in state are issued.");
|
|
app.add_flag("-q, --quit", quit, "Quits an existing instance.");
|
|
#endif
|
|
try {
|
|
app.parse(args);
|
|
} catch (const CLI::ParseError &e) {
|
|
if (app.get_help_ptr()->get_callback_run()) {
|
|
full_help = true;
|
|
} else {
|
|
exit(app.exit(e));
|
|
}
|
|
}
|
|
#ifdef DBUS_ENABLED
|
|
if (daemonize) {
|
|
ui_backend_option = "daemon";
|
|
}
|
|
#endif
|
|
args.clear();
|
|
args = app.remaining(false);
|
|
int new_argc = args.size();
|
|
char **new_argv = (char**)malloc(new_argc * sizeof(char*));
|
|
|
|
#ifdef DBUS_ENABLED
|
|
if (quit) {
|
|
DBusAPISender *sender = DBusAPISender::Create();
|
|
if (sender != nullptr) {
|
|
sender->Quit();
|
|
} else {
|
|
ERROR.writeln("Did not find existing instance to quit.");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
{
|
|
auto looper_mit = LicenseData("Looper (MIT)", "MIT");
|
|
auto looper_gpl = LicenseData("Looper (GPL)", "GPL-3.0-or-later");
|
|
auto looper = LicenseData("Looper", "MIT OR GPL-3.0-or-later");
|
|
auto tomlpp = LicenseData("TOML++", "MIT");
|
|
auto cli11 = LicenseData("CLI11", "BSD-3-Clause");
|
|
auto jsoncpp = LicenseData("JsonCpp", "MIT");
|
|
auto soundtouch = LicenseData("SoundTouch", "LGPL-2.1-only");
|
|
LOAD_LICENSE(looper_mit, looper_mit);
|
|
LOAD_LICENSE(looper_gpl, looper_gpl);
|
|
LOAD_LICENSE(looper, looper);
|
|
LOAD_LICENSE(tomlpp, tomlplusplus);
|
|
LOAD_LICENSE(cli11, cli11);
|
|
LOAD_LICENSE(jsoncpp, jsoncpp);
|
|
LOAD_LICENSE(soundtouch, lgpl_2_1);
|
|
license_data.insert(looper);
|
|
license_data.insert(looper_gpl);
|
|
license_data.insert(looper_mit);
|
|
license_data.insert(tomlpp);
|
|
license_data.insert(cli11);
|
|
license_data.insert(jsoncpp);
|
|
license_data.insert(soundtouch);
|
|
}
|
|
DEBUG.writeln("Command line arguments after first parse:");
|
|
for (size_t i = 0; i < new_argc; i++) {
|
|
auto &arg = args[i];
|
|
DEBUG.writefln(" - '%s'", arg.c_str());
|
|
new_argv[i] = strdup(arg.c_str());
|
|
}
|
|
DEBUG.writeln("Initializing frontends...");
|
|
init_backends();
|
|
init_playback_backends();
|
|
init_audio_data();
|
|
#ifdef DBUS_ENABLED
|
|
ProxyGlueBackend *proxy_backend = nullptr;
|
|
if ((disable_gui && !daemonize) || quit) {
|
|
if (!DBusAPISender::isOnlyInstance()) {
|
|
UIBackend::register_backend<ProxyGlueBackend>();
|
|
if (disable_gui) {
|
|
proxy_backend = UIBackend::get_backend<ProxyGlueBackend>().value_or(nullptr);
|
|
}
|
|
}
|
|
if (quit && proxy_backend == nullptr) {
|
|
proxy_backend = UIBackend::get_backend<ProxyGlueBackend>().value_or(nullptr);
|
|
}
|
|
}
|
|
if (daemonize) {
|
|
UIBackend::register_backend<DaemonGlueBackend>();
|
|
}
|
|
#endif
|
|
for (auto kv : UIBackend::backends) {
|
|
kv.second->add_licenses();
|
|
}
|
|
for (auto kv : PlaybackBackendHelper()) {
|
|
kv.second->add_licenses();
|
|
}
|
|
DEBUG.writeln("Loaded frontends: ");
|
|
for (auto kv : UIBackend::backends) {
|
|
DEBUG.writefln(" - '%s'", kv.first.c_str());
|
|
}
|
|
DEBUG.writeln("Loaded playback backends: ");
|
|
for (auto kv : PlaybackBackendHelper()) {
|
|
DEBUG.writefln(" - '%s'", kv.first.c_str());
|
|
}
|
|
DEBUG.writeln("Loading options file...");
|
|
load_options();
|
|
{
|
|
auto &cat_data = get_cat_data();
|
|
cat_data.push_back(CatData(catoc_data, catoc_size, "Cat OC (Built-In)", "catoc.png"));
|
|
#ifndef __EMSCRIPTEN__
|
|
fs::path baseDir = get_prefs_path() / fs::path("looper") / fs::path("themes");
|
|
if (!fs::exists(baseDir)) {
|
|
fs::create_directories(baseDir);
|
|
}
|
|
for (auto const&dir_entry : std::filesystem::directory_iterator(baseDir)) {
|
|
if (dir_entry.is_regular_file()) {
|
|
cat_data.push_back(CatData(dir_entry.path()));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
std::string backend_id = get_option<std::string>("ui.frontend", "imgui");
|
|
UIBackend *backend = UIBackend::get_backend(ui_backend_option).value_or(UIBackend::get_backend(backend_id).value_or(UIBackend::get_first_backend()));
|
|
int output = 0;
|
|
if (backend == nullptr) {
|
|
ERROR.writeln("No UI backend could be found.");
|
|
return -1;
|
|
} else {
|
|
DEBUG.writefln("Using backend: '%s'...", backend->get_id().c_str());
|
|
UIBackend::running_ui_backend = backend;
|
|
if (full_help) {
|
|
args.clear();
|
|
args.push_back("--help");
|
|
}
|
|
try {
|
|
#ifdef DBUS_ENABLED
|
|
if (proxy_backend != nullptr && !quit) {
|
|
if (!proxy_backend->run(args, new_argc, new_argv)) {
|
|
throw 0;
|
|
}
|
|
proxy_backend = nullptr;
|
|
}
|
|
if (!quit) {
|
|
UIBackend::unregister_backend<ProxyGlueBackend>();
|
|
}
|
|
#endif
|
|
output = backend->run(args, new_argc, new_argv);
|
|
#ifdef DBUS_ENABLED
|
|
if (quit && proxy_backend != nullptr) {
|
|
proxy_backend->quitDaemon();
|
|
proxy_backend->unregister_self();
|
|
}
|
|
#endif
|
|
} catch (int return_code) {
|
|
if (full_help) {
|
|
std::string helpstr = app.help();
|
|
helpstr = helpstr.substr(helpstr.find("\n") + 1);
|
|
helpstr = helpstr.substr(helpstr.find("\n") + 1);
|
|
helpstr = helpstr.substr(helpstr.find("\n") + 1);
|
|
helpstr = helpstr.substr(helpstr.find("\n") + 1);
|
|
helpstr = helpstr.substr(helpstr.find("\n") + 1);
|
|
printf("%s", helpstr.c_str());
|
|
}
|
|
output = return_code;
|
|
}
|
|
}
|
|
DEBUG.writeln("Exiting...");
|
|
UIBackend::deinit_backends();
|
|
for (int i = 0; i < new_argc; i++) {
|
|
free(new_argv[i]);
|
|
}
|
|
free(new_argv);
|
|
save_options();
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
quit();
|
|
#endif
|
|
return output;
|
|
}
|
|
std::string current_process_type;
|
|
extern int looper_run_playback_process(std::vector<std::string> args);
|
|
int main(int argc, char **argv) {
|
|
if (argc == 0) {
|
|
throw std::exception();
|
|
}
|
|
#ifdef _WIN32
|
|
size_t size = std::max(10240, PATH_MAX) + 1;
|
|
executable_path = malloc(size);
|
|
size = GetModuleFileNameA(NULL, executable_path, size);
|
|
realloc(executable_path, size);
|
|
#elif defined(__HAIKU__)
|
|
{
|
|
int32 cookie = 0;
|
|
image_info info;
|
|
while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
|
|
if (info.type == B_APP_IMAGE) {
|
|
executable_path = strdup(info.name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
executable_path = strdup(fs::canonical("/proc/self/exe").c_str());
|
|
#endif
|
|
CLI::App app{DESCRIPTION};
|
|
std::string process_type = "normal";
|
|
app.add_option<std::string, std::string>("--process-type", process_type);
|
|
app.add_option("-l, --log-level", LogStream::log_level, "Sets the minimum log level to display in the logs.");
|
|
app.allow_extras();
|
|
init_logging();
|
|
try {
|
|
app.parse(argc, argv);
|
|
executable_path = argv[0];
|
|
current_process_type = "host";
|
|
if (process_type == "playback") {
|
|
current_process_type = "playback";
|
|
return looper_run_playback_process(app.remaining(false));
|
|
} else if (process_type == "daemon") {
|
|
std::vector<std::string> opts = app.remaining(false);
|
|
opts.push_back("-d");
|
|
return looper_run_as_executable(opts);
|
|
} else {
|
|
return looper_run_as_executable(app.remaining(false));
|
|
}
|
|
} catch (CLI::CallForHelp) {
|
|
return looper_run_as_executable(std::vector<std::string>({"--help"}));
|
|
}
|
|
free(executable_path);
|
|
return 255;
|
|
}
|