2024-03-23 18:41:26 -07:00
# include "options.hpp"
# include "backend.hpp"
2024-04-09 10:15:05 -07:00
# include "daemon_backend.hpp"
# include "proxy_backend.hpp"
2024-03-23 18:41:26 -07:00
# include "log.hpp"
2023-10-16 10:44:25 -07:00
# include "thirdparty/CLI11.hpp"
2024-03-23 18:41:26 -07:00
# include "data.h"
2024-03-26 18:39:02 -07:00
# include "license.hpp"
2024-12-08 09:55:27 -08:00
# include <SDL.h>
2024-03-26 18:39:02 -07:00
# include "assets/assets.h"
2024-05-02 14:52:11 -07:00
# ifdef __EMSCRIPTEN__
# include <emscripten.h>
# endif
2024-11-12 14:53:44 -08:00
# ifdef __HAIKU__
# include <os/AppKit.h>
2024-12-08 09:55:27 -08:00
# include <image.h>
2024-11-12 14:53:44 -08:00
# endif
2024-05-02 14:52:11 -07:00
# include "web_functions.hpp"
2024-12-21 14:23:00 -08:00
# include "cats.hpp"
2024-03-23 18:41:26 -07:00
using namespace Looper ;
using namespace Looper : : Options ;
using namespace Looper : : Log ;
2024-04-24 09:59:51 -07:00
# ifdef __EMSCRIPTEN__
extern " C " {
void quit ( ) ;
}
# endif
2024-12-21 14:23:00 -08:00
std : : vector < CatData > & get_cat_data ( ) {
static std : : vector < CatData > data ;
return data ;
}
2024-03-26 18:39:02 -07:00
std : : unordered_set < LicenseData > license_data ;
std : : unordered_set < LicenseData > & get_license_data ( ) {
return license_data ;
}
2024-08-08 13:12:37 -07:00
char * executable_path ;
2024-04-28 12:31:40 -07:00
# 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
2024-08-08 13:12:37 -07:00
extern " C " int looper_run_as_executable ( std : : vector < std : : string > args ) {
2024-04-28 12:31:40 -07:00
# ifdef __ANDROID__
env = ( JNIEnv * ) SDL_AndroidGetJNIEnv ( ) ;
initNative ( ) ;
2024-05-02 14:52:11 -07:00
# endif
# ifdef __EMSCRIPTEN__
EM_ASM ( { Module . wasmTable = wasmTable ; } ) ;
2024-04-28 12:31:40 -07:00
# endif
2024-03-23 18:41:26 -07:00
CLI : : App app { DESCRIPTION } ;
2023-10-16 10:44:25 -07:00
std : : string filename = " " ;
app . allow_extras ( ) ;
2024-03-23 18:41:26 -07:00
std : : string ui_backend_option = " " ;
bool full_help = false ;
2024-04-09 10:15:05 -07:00
bool open_window = false ;
2024-03-23 18:41:26 -07:00
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. " ) ;
2024-04-24 09:59:51 -07:00
# ifdef DBUS_ENABLED
bool daemonize = false ;
bool disable_gui = false ;
bool quit = false ;
2024-04-09 10:15:05 -07:00
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. " ) ;
2024-04-24 09:59:51 -07:00
# endif
2023-10-16 10:44:25 -07:00
try {
app . parse ( args ) ;
} catch ( const CLI : : ParseError & e ) {
2024-03-23 18:41:26 -07:00
if ( app . get_help_ptr ( ) - > get_callback_run ( ) ) {
full_help = true ;
2023-07-25 14:33:29 -07:00
} else {
2024-03-23 18:41:26 -07:00
exit ( app . exit ( e ) ) ;
}
}
2024-04-24 09:59:51 -07:00
# ifdef DBUS_ENABLED
2024-04-09 10:15:05 -07:00
if ( daemonize ) {
ui_backend_option = " daemon " ;
}
2024-04-24 09:59:51 -07:00
# endif
2024-03-23 18:41:26 -07:00
args . clear ( ) ;
args = app . remaining ( false ) ;
int new_argc = args . size ( ) ;
char * * new_argv = ( char * * ) malloc ( new_argc * sizeof ( char * ) ) ;
2024-04-09 10:15:05 -07:00
2024-04-24 09:59:51 -07:00
# ifdef DBUS_ENABLED
2024-04-09 10:15:05 -07:00
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 ;
}
2024-04-24 09:59:51 -07:00
# endif
2024-03-26 18:39:02 -07:00
{
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 ) ;
}
2024-03-23 18:41:26 -07:00
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 ( ) ;
2024-09-28 10:31:06 -07:00
init_playback_backends ( ) ;
init_audio_data ( ) ;
2024-04-24 09:59:51 -07:00
# ifdef DBUS_ENABLED
2024-04-09 10:15:05 -07:00
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 > ( ) ;
}
2024-04-24 09:59:51 -07:00
# endif
2024-03-23 18:41:26 -07:00
for ( auto kv : UIBackend : : backends ) {
2024-03-26 18:39:02 -07:00
kv . second - > add_licenses ( ) ;
2024-04-09 10:15:05 -07:00
}
2024-11-12 14:53:44 -08:00
for ( auto kv : PlaybackBackendHelper ( ) ) {
kv . second - > add_licenses ( ) ;
}
2024-04-09 10:15:05 -07:00
DEBUG . writeln ( " Loaded frontends: " ) ;
for ( auto kv : UIBackend : : backends ) {
2024-03-23 18:41:26 -07:00
DEBUG . writefln ( " - '%s' " , kv . first . c_str ( ) ) ;
}
2024-08-08 13:12:37 -07:00
DEBUG . writeln ( " Loaded playback backends: " ) ;
for ( auto kv : PlaybackBackendHelper ( ) ) {
DEBUG . writefln ( " - '%s' " , kv . first . c_str ( ) ) ;
}
2024-03-23 18:41:26 -07:00
DEBUG . writeln ( " Loading options file... " ) ;
load_options ( ) ;
2024-12-21 14:23:00 -08:00
{
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
}
2024-03-23 18:41:26 -07:00
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 ( ) ) ;
2024-04-09 10:15:05 -07:00
UIBackend : : running_ui_backend = backend ;
2024-03-23 18:41:26 -07:00
if ( full_help ) {
args . clear ( ) ;
args . push_back ( " --help " ) ;
}
try {
2024-04-24 09:59:51 -07:00
# ifdef DBUS_ENABLED
2024-04-09 10:15:05 -07:00
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 > ( ) ;
}
2024-04-24 09:59:51 -07:00
# endif
2024-03-23 18:41:26 -07:00
output = backend - > run ( args , new_argc , new_argv ) ;
2024-04-24 09:59:51 -07:00
# ifdef DBUS_ENABLED
2024-04-09 10:15:05 -07:00
if ( quit & & proxy_backend ! = nullptr ) {
proxy_backend - > quitDaemon ( ) ;
proxy_backend - > unregister_self ( ) ;
}
2024-04-24 09:59:51 -07:00
# endif
2024-03-23 18:41:26 -07:00
} 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 ) ;
2024-04-10 13:28:06 -07:00
printf ( " %s " , helpstr . c_str ( ) ) ;
2024-03-23 18:41:26 -07:00
}
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 ( ) ;
2024-04-24 09:59:51 -07:00
# ifdef __EMSCRIPTEN__
quit ( ) ;
# endif
2024-03-23 18:41:26 -07:00
return output ;
2024-04-10 13:28:06 -07:00
}
2024-09-16 15:05:53 -07:00
std : : string current_process_type ;
2024-08-08 13:12:37 -07:00
extern int looper_run_playback_process ( std : : vector < std : : string > args ) ;
int main ( int argc , char * * argv ) {
2024-10-14 21:27:16 -07:00
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 ) ;
2024-11-12 14:53:44 -08:00
# elif defined(__HAIKU__)
2024-12-08 09:55:27 -08:00
{
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 ;
}
}
}
2024-10-14 21:27:16 -07:00
# else
executable_path = strdup ( fs : : canonical ( " /proc/self/exe " ) . c_str ( ) ) ;
# endif
2024-08-08 13:12:37 -07:00
CLI : : App app { DESCRIPTION } ;
std : : string process_type = " normal " ;
app . add_option < std : : string , std : : string > ( " --process-type " , process_type ) ;
2024-10-14 21:27:16 -07:00
app . add_option ( " -l, --log-level " , LogStream : : log_level , " Sets the minimum log level to display in the logs. " ) ;
2024-08-08 13:12:37 -07:00
app . allow_extras ( ) ;
2024-10-14 21:27:16 -07:00
init_logging ( ) ;
2024-09-16 15:05:53 -07:00
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 ) {
2024-10-19 16:37:43 -07:00
return looper_run_as_executable ( std : : vector < std : : string > ( { " --help " } ) ) ;
2024-08-08 13:12:37 -07:00
}
2024-10-14 21:27:16 -07:00
free ( executable_path ) ;
2024-10-19 16:37:43 -07:00
return 255 ;
2024-09-16 15:05:53 -07:00
}