diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4523229..b475ea9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,7 +4,7 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(SDL_MIXER_X_STATIC ON CACHE BOOL "")
set(SDL_MIXER_X_SHARED OFF CACHE BOOL "")
-set(DOWNLOAD_AUDIO_CODECS_DEPENDENCY ON CACHE BOOL "")
+set(DOWNLOAD_AUDIO_CODECS_DEPENDENCY OFF CACHE BOOL "")
set(AUDIO_CODECS_BUILD_LOCAL_SDL2 OFF CACHE BOOL "" FORCE)
set(MIXERX_LGPL ON CACHE BOOL "" FORCE)
set(USE_MIDI ON CACHE BOOL "" FORCE)
@@ -30,6 +30,7 @@ find_package(PkgConfig)
add_subdirectory(subprojects/jsoncpp)
find_package(SDL2 REQUIRED)
find_package(sdbus-c++ REQUIRED)
+include(GNUInstallDirs)
add_subdirectory(subprojects/SDL-Mixer-X)
add_subdirectory(subprojects/vgmstream)
@@ -136,18 +137,18 @@ if (NOT DISABLE_GTK_UI)
list(APPEND ENABLED_UIS "gtk")
endif()
execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/gen_ui_backend_inc.py ${ENABLED_UIS})
-prefix_all(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ backend_glue.cpp main.cpp)
+prefix_all(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ backend_glue.cpp main.cpp daemon_backend.cpp proxy_backend.cpp)
add_executable(looper ${SOURCES})
add_dependencies(looper looper_assets ${UI_BACKENDS})
find_program(ASCLI_EXE NAMES "appstreamcli" NO_CACHE)
if(${ASCLI_EXE} STREQUAL "ASCLIEXE-NOTFOUND")
message("Cannot verify Appstream Metadata.")
else()
- add_test(NAME "verify appstream metadata" COMMAND ${ASCLI_EXE} validate --no-net --pedantic "assets/com.experimentalcraft.Looper.metainfo.xml")
+ add_test(NAME "verify appstream metadata" COMMAND ${ASCLI_EXE} validate --no-net --pedantic "assets/com.complecwaft.Looper.metainfo.xml")
endif()
-target_link_libraries(looper PRIVATE liblooper libvgmstream jsoncpp ${UI_BACKENDS})
+target_link_libraries(looper PUBLIC liblooper libvgmstream jsoncpp ${UI_BACKENDS})
install(TARGETS looper ${EXTRA_LIBS})
install(FILES assets/icon.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps/)
-install(FILES assets/looper.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
-install(FILES assets/com.experimentalcraft.Looper.metainfo.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo)
+install(FILES assets/com.complecwaft.Looper.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
+install(FILES assets/com.complecwaft.Looper.metainfo.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo)
install(DIRECTORY assets/translations/ TYPE LOCALE PATTERN "*" EXCLUDE PATTERN "looper.pot")
diff --git a/assets/app.dbus.xml b/assets/app.dbus.xml
new file mode 100644
index 0000000..b42fcbf
--- /dev/null
+++ b/assets/app.dbus.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/com.complecwaft.Looper.desktop b/assets/com.complecwaft.Looper.desktop
new file mode 100755
index 0000000..9328c4f
--- /dev/null
+++ b/assets/com.complecwaft.Looper.desktop
@@ -0,0 +1,28 @@
+[Desktop Entry]
+Version=1.5
+Type=Application
+Name=Looper
+Comment=An audio player that can properly loop audio files
+GenericName=Looping audio player
+Exec=looper -n %f
+Icon=looper
+StartupWMClass=looper;com.complecwaft.Looper;com.complecwaft.Looper.GTK
+MimeType=audio/x-wav;audio/ogg;audio/x-vorbis+ogg;audio/x-opus+ogg;audio/mpeg;audio/flac;audio/xm;audio/x-mod;
+Categories=Audio;AudioVideo;
+Terminal=false
+SingleMainWindow=true
+StartupNotify=false
+Actions=Open;Daemon;Quit;
+Hidden=false
+PrefersNonDefaultGPU=false
+[Desktop Action Open]
+Name=Open Window
+Icon=window-new
+Exec=looper
+[Desktop Action Quit]
+Name=Quit Looper
+Icon=application-exit
+Exec=looper -q
+[Desktop Action Daemon]
+Name=Start Daemon
+Exec=looper -d -n
diff --git a/assets/com.experimentalcraft.Looper.metainfo.xml b/assets/com.complecwaft.Looper.metainfo.xml
similarity index 91%
rename from assets/com.experimentalcraft.Looper.metainfo.xml
rename to assets/com.complecwaft.Looper.metainfo.xml
index c163785..85779f5 100644
--- a/assets/com.experimentalcraft.Looper.metainfo.xml
+++ b/assets/com.complecwaft.Looper.metainfo.xml
@@ -1,13 +1,13 @@
- com.experimentalcraft.NekoPlayer
+ com.complecwaft.Looper
Catmeow72
- Neko Player
+ Looper
An audio player that can play back files with seamless loops using their metadata.
MIT
- MIT AND LGPL-2.1-only AND Zlib
+ MIT OR GPL-3.0-or-later
diff --git a/assets/dbus_stub_adaptor.hpp b/assets/dbus_stub_adaptor.hpp
new file mode 100644
index 0000000..8e9b06f
--- /dev/null
+++ b/assets/dbus_stub_adaptor.hpp
@@ -0,0 +1,224 @@
+
+/*
+ * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
+ */
+
+#ifndef __sdbuscpp__dbus_stub_adaptor_hpp__adaptor__H__
+#define __sdbuscpp__dbus_stub_adaptor_hpp__adaptor__H__
+
+#include
+#include
+#include
+
+namespace org {
+namespace freedesktop {
+
+class Application_adaptor
+{
+public:
+ static constexpr const char* INTERFACE_NAME = "org.freedesktop.Application";
+
+protected:
+ Application_adaptor(sdbus::IObject& object)
+ : object_(&object)
+ {
+ object_->registerMethod("Activate").onInterface(INTERFACE_NAME).withInputParamNames("platform_data").implementedAs([this](const std::map& platform_data){ return this->Activate(platform_data); });
+ object_->registerMethod("Open").onInterface(INTERFACE_NAME).withInputParamNames("uris", "platform_data").implementedAs([this](const std::vector& uris, const std::map& platform_data){ return this->Open(uris, platform_data); });
+ object_->registerMethod("ActivateAction").onInterface(INTERFACE_NAME).withInputParamNames("action_name", "parameter", "platform_data").implementedAs([this](const std::string& action_name, const std::vector& parameter, const std::map& platform_data){ return this->ActivateAction(action_name, parameter, platform_data); });
+ }
+
+ Application_adaptor(const Application_adaptor&) = delete;
+ Application_adaptor& operator=(const Application_adaptor&) = delete;
+ Application_adaptor(Application_adaptor&&) = default;
+ Application_adaptor& operator=(Application_adaptor&&) = default;
+
+ ~Application_adaptor() = default;
+
+private:
+ virtual void Activate(const std::map& platform_data) = 0;
+ virtual void Open(const std::vector& uris, const std::map& platform_data) = 0;
+ virtual void ActivateAction(const std::string& action_name, const std::vector& parameter, const std::map& platform_data) = 0;
+
+private:
+ sdbus::IObject* object_;
+};
+
+}} // namespaces
+
+namespace com {
+namespace complecwaft {
+
+class Looper_adaptor
+{
+public:
+ static constexpr const char* INTERFACE_NAME = "com.complecwaft.Looper";
+
+protected:
+ Looper_adaptor(sdbus::IObject& object)
+ : object_(&object)
+ {
+ object_->registerMethod("CreateHandle").onInterface(INTERFACE_NAME).withOutputParamNames("new_handle").implementedAs([this](){ return this->CreateHandle(); });
+ object_->registerMethod("ClearHandle").onInterface(INTERFACE_NAME).withInputParamNames("handle").implementedAs([this](const std::string& handle){ return this->ClearHandle(handle); });
+ object_->registerMethod("Start").onInterface(INTERFACE_NAME).withInputParamNames("path").implementedAs([this](const std::string& path){ return this->Start(path); });
+ object_->registerMethod("StartByURI").onInterface(INTERFACE_NAME).withInputParamNames("uri").implementedAs([this](const std::string& uri){ return this->StartByURI(uri); });
+ object_->registerMethod("Quit").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->Quit(); });
+ object_->registerMethod("Stop").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->Stop(); });
+ object_->registerMethod("TogglePause").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->TogglePause(); });
+ object_->registerSignal("PlaybackEngineStarted").onInterface(INTERFACE_NAME);
+ object_->registerSignal("SpeedChanged").onInterface(INTERFACE_NAME).withParameters("new_speed");
+ object_->registerSignal("TempoChanged").onInterface(INTERFACE_NAME).withParameters("new_tempo");
+ object_->registerSignal("PitchChanged").onInterface(INTERFACE_NAME).withParameters("new_pitch");
+ object_->registerSignal("PauseChanged").onInterface(INTERFACE_NAME).withParameters("now_paused");
+ object_->registerSignal("Stopped").onInterface(INTERFACE_NAME);
+ object_->registerSignal("ErrorOccurred").onInterface(INTERFACE_NAME).withParameters("error_desc", "error_type");
+ object_->registerSignal("Seeked").onInterface(INTERFACE_NAME).withParameters("to_position");
+ object_->registerSignal("FileChanged").onInterface(INTERFACE_NAME).withParameters("path", "title");
+ object_->registerProperty("FilePath").onInterface(INTERFACE_NAME).withGetter([this](){ return this->FilePath(); });
+ object_->registerProperty("FileTitle").onInterface(INTERFACE_NAME).withGetter([this](){ return this->FileTitle(); });
+ object_->registerProperty("Position").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Position(); }).withSetter([this](const double& value){ this->Position(value); });
+ object_->registerProperty("Length").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Length(); });
+ object_->registerProperty("Speed").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Speed(); }).withSetter([this](const double& value){ this->Speed(value); });
+ object_->registerProperty("Tempo").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Tempo(); }).withSetter([this](const double& value){ this->Tempo(value); });
+ object_->registerProperty("Pitch").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Pitch(); }).withSetter([this](const double& value){ this->Pitch(value); });
+ object_->registerProperty("Volume").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Volume(); }).withSetter([this](const double& value){ this->Volume(value); });
+ object_->registerProperty("Paused").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Paused(); }).withSetter([this](const bool& value){ this->Paused(value); });
+ object_->registerProperty("IsStopped").onInterface(INTERFACE_NAME).withGetter([this](){ return this->IsStopped(); });
+ object_->registerProperty("IsDaemon").onInterface(INTERFACE_NAME).withGetter([this](){ return this->IsDaemon(); });
+ }
+
+ Looper_adaptor(const Looper_adaptor&) = delete;
+ Looper_adaptor& operator=(const Looper_adaptor&) = delete;
+ Looper_adaptor(Looper_adaptor&&) = default;
+ Looper_adaptor& operator=(Looper_adaptor&&) = default;
+
+ ~Looper_adaptor() = default;
+
+public:
+ void emitPlaybackEngineStarted()
+ {
+ object_->emitSignal("PlaybackEngineStarted").onInterface(INTERFACE_NAME);
+ }
+
+ void emitSpeedChanged(const double& new_speed)
+ {
+ object_->emitSignal("SpeedChanged").onInterface(INTERFACE_NAME).withArguments(new_speed);
+ }
+
+ void emitTempoChanged(const double& new_tempo)
+ {
+ object_->emitSignal("TempoChanged").onInterface(INTERFACE_NAME).withArguments(new_tempo);
+ }
+
+ void emitPitchChanged(const double& new_pitch)
+ {
+ object_->emitSignal("PitchChanged").onInterface(INTERFACE_NAME).withArguments(new_pitch);
+ }
+
+ void emitPauseChanged(const bool& now_paused)
+ {
+ object_->emitSignal("PauseChanged").onInterface(INTERFACE_NAME).withArguments(now_paused);
+ }
+
+ void emitStopped()
+ {
+ object_->emitSignal("Stopped").onInterface(INTERFACE_NAME);
+ }
+
+ void emitErrorOccurred(const std::string& error_desc, const std::string& error_type)
+ {
+ object_->emitSignal("ErrorOccurred").onInterface(INTERFACE_NAME).withArguments(error_desc, error_type);
+ }
+
+ void emitSeeked(const double& to_position)
+ {
+ object_->emitSignal("Seeked").onInterface(INTERFACE_NAME).withArguments(to_position);
+ }
+
+ void emitFileChanged(const std::string& path, const std::string& title)
+ {
+ object_->emitSignal("FileChanged").onInterface(INTERFACE_NAME).withArguments(path, title);
+ }
+
+private:
+ virtual std::string CreateHandle() = 0;
+ virtual void ClearHandle(const std::string& handle) = 0;
+ virtual void Start(const std::string& path) = 0;
+ virtual void StartByURI(const std::string& uri) = 0;
+ virtual void Quit() = 0;
+ virtual void Stop() = 0;
+ virtual void TogglePause() = 0;
+
+private:
+ virtual std::string FilePath() = 0;
+ virtual std::string FileTitle() = 0;
+ virtual double Position() = 0;
+ virtual void Position(const double& value) = 0;
+ virtual double Length() = 0;
+ virtual double Speed() = 0;
+ virtual void Speed(const double& value) = 0;
+ virtual double Tempo() = 0;
+ virtual void Tempo(const double& value) = 0;
+ virtual double Pitch() = 0;
+ virtual void Pitch(const double& value) = 0;
+ virtual double Volume() = 0;
+ virtual void Volume(const double& value) = 0;
+ virtual bool Paused() = 0;
+ virtual void Paused(const bool& value) = 0;
+ virtual bool IsStopped() = 0;
+ virtual bool IsDaemon() = 0;
+
+private:
+ sdbus::IObject* object_;
+};
+
+}} // namespaces
+
+namespace com {
+namespace complecwaft {
+namespace Looper {
+
+class Errors_adaptor
+{
+public:
+ static constexpr const char* INTERFACE_NAME = "com.complecwaft.Looper.Errors";
+
+protected:
+ Errors_adaptor(sdbus::IObject& object)
+ : object_(&object)
+ {
+ object_->registerMethod("PopFront").onInterface(INTERFACE_NAME).withInputParamNames("handle").withOutputParamNames("error").implementedAs([this](const std::string& handle){ return this->PopFront(handle); });
+ object_->registerMethod("PopBack").onInterface(INTERFACE_NAME).withInputParamNames("handle").withOutputParamNames("error").implementedAs([this](const std::string& handle){ return this->PopBack(handle); });
+ object_->registerMethod("PeekFront").onInterface(INTERFACE_NAME).withInputParamNames("handle").withOutputParamNames("error").implementedAs([this](const std::string& handle){ return this->PeekFront(handle); });
+ object_->registerMethod("PeekBack").onInterface(INTERFACE_NAME).withInputParamNames("handle").withOutputParamNames("error").implementedAs([this](const std::string& handle){ return this->PeekBack(handle); });
+ object_->registerMethod("GetCount").onInterface(INTERFACE_NAME).withInputParamNames("handle").withOutputParamNames("count").implementedAs([this](const std::string& handle){ return this->GetCount(handle); });
+ object_->registerMethod("IsEmpty").onInterface(INTERFACE_NAME).withInputParamNames("handle").withOutputParamNames("empty").implementedAs([this](const std::string& handle){ return this->IsEmpty(handle); });
+ object_->registerMethod("Clear").onInterface(INTERFACE_NAME).withInputParamNames("handle").implementedAs([this](const std::string& handle){ return this->Clear(handle); });
+ object_->registerMethod("PeekAll").onInterface(INTERFACE_NAME).withInputParamNames("handle").withOutputParamNames("errors").implementedAs([this](const std::string& handle){ return this->PeekAll(handle); });
+ object_->registerMethod("GetAllAndClear").onInterface(INTERFACE_NAME).withInputParamNames("handle").withOutputParamNames("errors").implementedAs([this](const std::string& handle){ return this->GetAllAndClear(handle); });
+ }
+
+ Errors_adaptor(const Errors_adaptor&) = delete;
+ Errors_adaptor& operator=(const Errors_adaptor&) = delete;
+ Errors_adaptor(Errors_adaptor&&) = default;
+ Errors_adaptor& operator=(Errors_adaptor&&) = default;
+
+ ~Errors_adaptor() = default;
+
+private:
+ virtual std::string PopFront(const std::string& handle) = 0;
+ virtual std::string PopBack(const std::string& handle) = 0;
+ virtual std::string PeekFront(const std::string& handle) = 0;
+ virtual std::string PeekBack(const std::string& handle) = 0;
+ virtual uint32_t GetCount(const std::string& handle) = 0;
+ virtual bool IsEmpty(const std::string& handle) = 0;
+ virtual void Clear(const std::string& handle) = 0;
+ virtual std::vector PeekAll(const std::string& handle) = 0;
+ virtual std::vector GetAllAndClear(const std::string& handle) = 0;
+
+private:
+ sdbus::IObject* object_;
+};
+
+}}} // namespaces
+
+#endif
diff --git a/assets/dbus_stub_proxy.hpp b/assets/dbus_stub_proxy.hpp
new file mode 100644
index 0000000..68ece7f
--- /dev/null
+++ b/assets/dbus_stub_proxy.hpp
@@ -0,0 +1,316 @@
+
+/*
+ * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
+ */
+
+#ifndef __sdbuscpp__dbus_stub_proxy_hpp__proxy__H__
+#define __sdbuscpp__dbus_stub_proxy_hpp__proxy__H__
+
+#include
+#include
+#include
+
+namespace org {
+namespace freedesktop {
+
+class Application_proxy
+{
+public:
+ static constexpr const char* INTERFACE_NAME = "org.freedesktop.Application";
+
+protected:
+ Application_proxy(sdbus::IProxy& proxy)
+ : proxy_(&proxy)
+ {
+ }
+
+ Application_proxy(const Application_proxy&) = delete;
+ Application_proxy& operator=(const Application_proxy&) = delete;
+ Application_proxy(Application_proxy&&) = default;
+ Application_proxy& operator=(Application_proxy&&) = default;
+
+ ~Application_proxy() = default;
+
+public:
+ void Activate(const std::map& platform_data)
+ {
+ proxy_->callMethod("Activate").onInterface(INTERFACE_NAME).withArguments(platform_data);
+ }
+
+ void Open(const std::vector& uris, const std::map& platform_data)
+ {
+ proxy_->callMethod("Open").onInterface(INTERFACE_NAME).withArguments(uris, platform_data);
+ }
+
+ void ActivateAction(const std::string& action_name, const std::vector& parameter, const std::map& platform_data)
+ {
+ proxy_->callMethod("ActivateAction").onInterface(INTERFACE_NAME).withArguments(action_name, parameter, platform_data);
+ }
+
+private:
+ sdbus::IProxy* proxy_;
+};
+
+}} // namespaces
+
+namespace com {
+namespace complecwaft {
+
+class Looper_proxy
+{
+public:
+ static constexpr const char* INTERFACE_NAME = "com.complecwaft.Looper";
+
+protected:
+ Looper_proxy(sdbus::IProxy& proxy)
+ : proxy_(&proxy)
+ {
+ proxy_->uponSignal("PlaybackEngineStarted").onInterface(INTERFACE_NAME).call([this](){ this->onPlaybackEngineStarted(); });
+ proxy_->uponSignal("SpeedChanged").onInterface(INTERFACE_NAME).call([this](const double& new_speed){ this->onSpeedChanged(new_speed); });
+ proxy_->uponSignal("TempoChanged").onInterface(INTERFACE_NAME).call([this](const double& new_tempo){ this->onTempoChanged(new_tempo); });
+ proxy_->uponSignal("PitchChanged").onInterface(INTERFACE_NAME).call([this](const double& new_pitch){ this->onPitchChanged(new_pitch); });
+ proxy_->uponSignal("PauseChanged").onInterface(INTERFACE_NAME).call([this](const bool& now_paused){ this->onPauseChanged(now_paused); });
+ proxy_->uponSignal("Stopped").onInterface(INTERFACE_NAME).call([this](){ this->onStopped(); });
+ proxy_->uponSignal("ErrorOccurred").onInterface(INTERFACE_NAME).call([this](const std::string& error_desc, const std::string& error_type){ this->onErrorOccurred(error_desc, error_type); });
+ proxy_->uponSignal("Seeked").onInterface(INTERFACE_NAME).call([this](const double& to_position){ this->onSeeked(to_position); });
+ proxy_->uponSignal("FileChanged").onInterface(INTERFACE_NAME).call([this](const std::string& path, const std::string& title){ this->onFileChanged(path, title); });
+ }
+
+ Looper_proxy(const Looper_proxy&) = delete;
+ Looper_proxy& operator=(const Looper_proxy&) = delete;
+ Looper_proxy(Looper_proxy&&) = default;
+ Looper_proxy& operator=(Looper_proxy&&) = default;
+
+ ~Looper_proxy() = default;
+
+ virtual void onPlaybackEngineStarted() = 0;
+ virtual void onSpeedChanged(const double& new_speed) = 0;
+ virtual void onTempoChanged(const double& new_tempo) = 0;
+ virtual void onPitchChanged(const double& new_pitch) = 0;
+ virtual void onPauseChanged(const bool& now_paused) = 0;
+ virtual void onStopped() = 0;
+ virtual void onErrorOccurred(const std::string& error_desc, const std::string& error_type) = 0;
+ virtual void onSeeked(const double& to_position) = 0;
+ virtual void onFileChanged(const std::string& path, const std::string& title) = 0;
+
+public:
+ std::string CreateHandle()
+ {
+ std::string result;
+ proxy_->callMethod("CreateHandle").onInterface(INTERFACE_NAME).storeResultsTo(result);
+ return result;
+ }
+
+ void ClearHandle(const std::string& handle)
+ {
+ proxy_->callMethod("ClearHandle").onInterface(INTERFACE_NAME).withArguments(handle);
+ }
+
+ void Start(const std::string& path)
+ {
+ proxy_->callMethod("Start").onInterface(INTERFACE_NAME).withArguments(path);
+ }
+
+ void StartByURI(const std::string& uri)
+ {
+ proxy_->callMethod("StartByURI").onInterface(INTERFACE_NAME).withArguments(uri);
+ }
+
+ void Quit()
+ {
+ proxy_->callMethod("Quit").onInterface(INTERFACE_NAME);
+ }
+
+ void Stop()
+ {
+ proxy_->callMethod("Stop").onInterface(INTERFACE_NAME);
+ }
+
+ void TogglePause()
+ {
+ proxy_->callMethod("TogglePause").onInterface(INTERFACE_NAME);
+ }
+
+public:
+ std::string FilePath()
+ {
+ return proxy_->getProperty("FilePath").onInterface(INTERFACE_NAME);
+ }
+
+ std::string FileTitle()
+ {
+ return proxy_->getProperty("FileTitle").onInterface(INTERFACE_NAME);
+ }
+
+ double Position()
+ {
+ return proxy_->getProperty("Position").onInterface(INTERFACE_NAME);
+ }
+
+ void Position(const double& value)
+ {
+ proxy_->setProperty("Position").onInterface(INTERFACE_NAME).toValue(value);
+ }
+
+ double Length()
+ {
+ return proxy_->getProperty("Length").onInterface(INTERFACE_NAME);
+ }
+
+ double Speed()
+ {
+ return proxy_->getProperty("Speed").onInterface(INTERFACE_NAME);
+ }
+
+ void Speed(const double& value)
+ {
+ proxy_->setProperty("Speed").onInterface(INTERFACE_NAME).toValue(value);
+ }
+
+ double Tempo()
+ {
+ return proxy_->getProperty("Tempo").onInterface(INTERFACE_NAME);
+ }
+
+ void Tempo(const double& value)
+ {
+ proxy_->setProperty("Tempo").onInterface(INTERFACE_NAME).toValue(value);
+ }
+
+ double Pitch()
+ {
+ return proxy_->getProperty("Pitch").onInterface(INTERFACE_NAME);
+ }
+
+ void Pitch(const double& value)
+ {
+ proxy_->setProperty("Pitch").onInterface(INTERFACE_NAME).toValue(value);
+ }
+
+ double Volume()
+ {
+ return proxy_->getProperty("Volume").onInterface(INTERFACE_NAME);
+ }
+
+ void Volume(const double& value)
+ {
+ proxy_->setProperty("Volume").onInterface(INTERFACE_NAME).toValue(value);
+ }
+
+ bool Paused()
+ {
+ return proxy_->getProperty("Paused").onInterface(INTERFACE_NAME);
+ }
+
+ void Paused(const bool& value)
+ {
+ proxy_->setProperty("Paused").onInterface(INTERFACE_NAME).toValue(value);
+ }
+
+ bool IsStopped()
+ {
+ return proxy_->getProperty("IsStopped").onInterface(INTERFACE_NAME);
+ }
+
+ bool IsDaemon()
+ {
+ return proxy_->getProperty("IsDaemon").onInterface(INTERFACE_NAME);
+ }
+
+private:
+ sdbus::IProxy* proxy_;
+};
+
+}} // namespaces
+
+namespace com {
+namespace complecwaft {
+namespace Looper {
+
+class Errors_proxy
+{
+public:
+ static constexpr const char* INTERFACE_NAME = "com.complecwaft.Looper.Errors";
+
+protected:
+ Errors_proxy(sdbus::IProxy& proxy)
+ : proxy_(&proxy)
+ {
+ }
+
+ Errors_proxy(const Errors_proxy&) = delete;
+ Errors_proxy& operator=(const Errors_proxy&) = delete;
+ Errors_proxy(Errors_proxy&&) = default;
+ Errors_proxy& operator=(Errors_proxy&&) = default;
+
+ ~Errors_proxy() = default;
+
+public:
+ std::string PopFront(const std::string& handle)
+ {
+ std::string result;
+ proxy_->callMethod("PopFront").onInterface(INTERFACE_NAME).withArguments(handle).storeResultsTo(result);
+ return result;
+ }
+
+ std::string PopBack(const std::string& handle)
+ {
+ std::string result;
+ proxy_->callMethod("PopBack").onInterface(INTERFACE_NAME).withArguments(handle).storeResultsTo(result);
+ return result;
+ }
+
+ std::string PeekFront(const std::string& handle)
+ {
+ std::string result;
+ proxy_->callMethod("PeekFront").onInterface(INTERFACE_NAME).withArguments(handle).storeResultsTo(result);
+ return result;
+ }
+
+ std::string PeekBack(const std::string& handle)
+ {
+ std::string result;
+ proxy_->callMethod("PeekBack").onInterface(INTERFACE_NAME).withArguments(handle).storeResultsTo(result);
+ return result;
+ }
+
+ uint32_t GetCount(const std::string& handle)
+ {
+ uint32_t result;
+ proxy_->callMethod("GetCount").onInterface(INTERFACE_NAME).withArguments(handle).storeResultsTo(result);
+ return result;
+ }
+
+ bool IsEmpty(const std::string& handle)
+ {
+ bool result;
+ proxy_->callMethod("IsEmpty").onInterface(INTERFACE_NAME).withArguments(handle).storeResultsTo(result);
+ return result;
+ }
+
+ void Clear(const std::string& handle)
+ {
+ proxy_->callMethod("Clear").onInterface(INTERFACE_NAME).withArguments(handle);
+ }
+
+ std::vector PeekAll(const std::string& handle)
+ {
+ std::vector result;
+ proxy_->callMethod("PeekAll").onInterface(INTERFACE_NAME).withArguments(handle).storeResultsTo(result);
+ return result;
+ }
+
+ std::vector GetAllAndClear(const std::string& handle)
+ {
+ std::vector result;
+ proxy_->callMethod("GetAllAndClear").onInterface(INTERFACE_NAME).withArguments(handle).storeResultsTo(result);
+ return result;
+ }
+
+private:
+ sdbus::IProxy* proxy_;
+};
+
+}}} // namespaces
+
+#endif
diff --git a/assets/looper.desktop b/assets/looper.desktop
deleted file mode 100755
index 20bc2c8..0000000
--- a/assets/looper.desktop
+++ /dev/null
@@ -1,10 +0,0 @@
-[Desktop Entry]
-Type=Application
-Name=Looper
-Comment=An audio player that can properly loop audio files
-GenericName=Looping audio player
-Exec=looper %f
-Icon=looper
-MimeType=audio/x-wav;audio/ogg;audio/x-vorbis+ogg;audio/x-opus+ogg;audio/mpeg;audio/flac;audio/xm;audio/x-mod;
-StartupWMClass=looper
-Categories=Audio;AudioVideo;
diff --git a/assets/mpris.dbus.xml b/assets/mpris.dbus.xml
new file mode 100644
index 0000000..642582b
--- /dev/null
+++ b/assets/mpris.dbus.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/mpris_stub_adaptor.hpp b/assets/mpris_stub_adaptor.hpp
new file mode 100644
index 0000000..ccf5da5
--- /dev/null
+++ b/assets/mpris_stub_adaptor.hpp
@@ -0,0 +1,214 @@
+
+/*
+ * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
+ */
+
+#ifndef __sdbuscpp__mpris_stub_adaptor_hpp__adaptor__H__
+#define __sdbuscpp__mpris_stub_adaptor_hpp__adaptor__H__
+
+#include
+#include
+#include
+
+namespace org {
+namespace mpris {
+
+class MediaPlayer2_adaptor
+{
+public:
+ static constexpr const char* INTERFACE_NAME = "org.mpris.MediaPlayer2";
+
+protected:
+ MediaPlayer2_adaptor(sdbus::IObject& object)
+ : object_(&object)
+ {
+ object_->registerMethod("Raise").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->Raise(); });
+ object_->registerMethod("Quit").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->Quit(); });
+ object_->registerProperty("CanRaise").onInterface(INTERFACE_NAME).withGetter([this](){ return this->CanRaise(); });
+ object_->registerProperty("CanQuit").onInterface(INTERFACE_NAME).withGetter([this](){ return this->CanQuit(); });
+ object_->registerProperty("HasTrackList").onInterface(INTERFACE_NAME).withGetter([this](){ return this->HasTrackList(); });
+ object_->registerProperty("Identity").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Identity(); });
+ object_->registerProperty("SupportedUriSchemes").onInterface(INTERFACE_NAME).withGetter([this](){ return this->SupportedUriSchemes(); });
+ object_->registerProperty("SupportedMimeTypes").onInterface(INTERFACE_NAME).withGetter([this](){ return this->SupportedMimeTypes(); });
+ }
+
+ MediaPlayer2_adaptor(const MediaPlayer2_adaptor&) = delete;
+ MediaPlayer2_adaptor& operator=(const MediaPlayer2_adaptor&) = delete;
+ MediaPlayer2_adaptor(MediaPlayer2_adaptor&&) = default;
+ MediaPlayer2_adaptor& operator=(MediaPlayer2_adaptor&&) = default;
+
+ ~MediaPlayer2_adaptor() = default;
+
+private:
+ virtual void Raise() = 0;
+ virtual void Quit() = 0;
+
+private:
+ virtual bool CanRaise() = 0;
+ virtual bool CanQuit() = 0;
+ virtual bool HasTrackList() = 0;
+ virtual std::string Identity() = 0;
+ virtual std::vector SupportedUriSchemes() = 0;
+ virtual std::vector SupportedMimeTypes() = 0;
+
+private:
+ sdbus::IObject* object_;
+};
+
+}} // namespaces
+
+namespace org {
+namespace mpris {
+namespace MediaPlayer2 {
+
+class Player_adaptor
+{
+public:
+ static constexpr const char* INTERFACE_NAME = "org.mpris.MediaPlayer2.Player";
+
+protected:
+ Player_adaptor(sdbus::IObject& object)
+ : object_(&object)
+ {
+ object_->registerMethod("Next").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->Next(); });
+ object_->registerMethod("Previous").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->Previous(); });
+ object_->registerMethod("Pause").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->Pause(); });
+ object_->registerMethod("PlayPause").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->PlayPause(); });
+ object_->registerMethod("Stop").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->Stop(); });
+ object_->registerMethod("Play").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->Play(); });
+ object_->registerMethod("Seek").onInterface(INTERFACE_NAME).withInputParamNames("Offset").implementedAs([this](const int64_t& Offset){ return this->Seek(Offset); });
+ object_->registerMethod("SetPosition").onInterface(INTERFACE_NAME).withInputParamNames("TrackId", "Position").implementedAs([this](const sdbus::ObjectPath& TrackId, const int64_t& Position){ return this->SetPosition(TrackId, Position); });
+ object_->registerMethod("OpenUri").onInterface(INTERFACE_NAME).withInputParamNames("Uri").implementedAs([this](const std::string& Uri){ return this->OpenUri(Uri); });
+ object_->registerSignal("Seeked").onInterface(INTERFACE_NAME).withParameters("Position");
+ object_->registerProperty("PlaybackStatus").onInterface(INTERFACE_NAME).withGetter([this](){ return this->PlaybackStatus(); });
+ object_->registerProperty("Rate").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Rate(); }).withSetter([this](const double& value){ this->Rate(value); });
+ object_->registerProperty("Metadata").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Metadata(); });
+ object_->registerProperty("Volume").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Volume(); }).withSetter([this](const double& value){ this->Volume(value); });
+ object_->registerProperty("Position").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Position(); });
+ object_->registerProperty("MinimumRate").onInterface(INTERFACE_NAME).withGetter([this](){ return this->MinimumRate(); });
+ object_->registerProperty("MaximumRate").onInterface(INTERFACE_NAME).withGetter([this](){ return this->MaximumRate(); });
+ object_->registerProperty("CanGoNext").onInterface(INTERFACE_NAME).withGetter([this](){ return this->CanGoNext(); });
+ object_->registerProperty("CanGoPrevious").onInterface(INTERFACE_NAME).withGetter([this](){ return this->CanGoPrevious(); });
+ object_->registerProperty("CanPlay").onInterface(INTERFACE_NAME).withGetter([this](){ return this->CanPlay(); });
+ object_->registerProperty("CanPause").onInterface(INTERFACE_NAME).withGetter([this](){ return this->CanPause(); });
+ object_->registerProperty("CanSeek").onInterface(INTERFACE_NAME).withGetter([this](){ return this->CanSeek(); });
+ object_->registerProperty("CanControl").onInterface(INTERFACE_NAME).withGetter([this](){ return this->CanControl(); });
+ }
+
+ Player_adaptor(const Player_adaptor&) = delete;
+ Player_adaptor& operator=(const Player_adaptor&) = delete;
+ Player_adaptor(Player_adaptor&&) = default;
+ Player_adaptor& operator=(Player_adaptor&&) = default;
+
+ ~Player_adaptor() = default;
+
+public:
+ void emitSeeked(const int64_t& Position)
+ {
+ object_->emitSignal("Seeked").onInterface(INTERFACE_NAME).withArguments(Position);
+ }
+
+private:
+ virtual void Next() = 0;
+ virtual void Previous() = 0;
+ virtual void Pause() = 0;
+ virtual void PlayPause() = 0;
+ virtual void Stop() = 0;
+ virtual void Play() = 0;
+ virtual void Seek(const int64_t& Offset) = 0;
+ virtual void SetPosition(const sdbus::ObjectPath& TrackId, const int64_t& Position) = 0;
+ virtual void OpenUri(const std::string& Uri) = 0;
+
+private:
+ virtual std::string PlaybackStatus() = 0;
+ virtual double Rate() = 0;
+ virtual void Rate(const double& value) = 0;
+ virtual std::map Metadata() = 0;
+ virtual double Volume() = 0;
+ virtual void Volume(const double& value) = 0;
+ virtual int64_t Position() = 0;
+ virtual double MinimumRate() = 0;
+ virtual double MaximumRate() = 0;
+ virtual bool CanGoNext() = 0;
+ virtual bool CanGoPrevious() = 0;
+ virtual bool CanPlay() = 0;
+ virtual bool CanPause() = 0;
+ virtual bool CanSeek() = 0;
+ virtual bool CanControl() = 0;
+
+private:
+ sdbus::IObject* object_;
+};
+
+}}} // namespaces
+
+namespace org {
+namespace mpris {
+namespace MediaPlayer2 {
+
+class TrackList_adaptor
+{
+public:
+ static constexpr const char* INTERFACE_NAME = "org.mpris.MediaPlayer2.TrackList";
+
+protected:
+ TrackList_adaptor(sdbus::IObject& object)
+ : object_(&object)
+ {
+ object_->registerMethod("GetTracksMetadata").onInterface(INTERFACE_NAME).withInputParamNames("TrackIds").withOutputParamNames("Metadata").implementedAs([this](const std::vector& TrackIds){ return this->GetTracksMetadata(TrackIds); });
+ object_->registerMethod("AddTrack").onInterface(INTERFACE_NAME).withInputParamNames("Uri", "AfterTrack", "SetAsCurrent").implementedAs([this](const std::string& Uri, const sdbus::ObjectPath& AfterTrack, const bool& SetAsCurrent){ return this->AddTrack(Uri, AfterTrack, SetAsCurrent); });
+ object_->registerMethod("RemoveTrack").onInterface(INTERFACE_NAME).withInputParamNames("TrackId").implementedAs([this](const sdbus::ObjectPath& TrackId){ return this->RemoveTrack(TrackId); });
+ object_->registerMethod("GoTo").onInterface(INTERFACE_NAME).withInputParamNames("TrackId").implementedAs([this](const sdbus::ObjectPath& TrackId){ return this->GoTo(TrackId); });
+ object_->registerSignal("TrackListReplaced").onInterface(INTERFACE_NAME).withParameters, sdbus::ObjectPath>("Tracks", "CurrentTrack");
+ object_->registerSignal("TrackAdded").onInterface(INTERFACE_NAME).withParameters, sdbus::ObjectPath>("Metadata", "AfterTrack");
+ object_->registerSignal("TrackRemoved").onInterface(INTERFACE_NAME).withParameters("TrackId");
+ object_->registerSignal("TrackMetadataChanged").onInterface(INTERFACE_NAME).withParameters>("TrackId", "Metadata");
+ object_->registerProperty("Tracks").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Tracks(); });
+ object_->registerProperty("CanEditTracks").onInterface(INTERFACE_NAME).withGetter([this](){ return this->CanEditTracks(); });
+ }
+
+ TrackList_adaptor(const TrackList_adaptor&) = delete;
+ TrackList_adaptor& operator=(const TrackList_adaptor&) = delete;
+ TrackList_adaptor(TrackList_adaptor&&) = default;
+ TrackList_adaptor& operator=(TrackList_adaptor&&) = default;
+
+ ~TrackList_adaptor() = default;
+
+public:
+ void emitTrackListReplaced(const std::vector& Tracks, const sdbus::ObjectPath& CurrentTrack)
+ {
+ object_->emitSignal("TrackListReplaced").onInterface(INTERFACE_NAME).withArguments(Tracks, CurrentTrack);
+ }
+
+ void emitTrackAdded(const std::map& Metadata, const sdbus::ObjectPath& AfterTrack)
+ {
+ object_->emitSignal("TrackAdded").onInterface(INTERFACE_NAME).withArguments(Metadata, AfterTrack);
+ }
+
+ void emitTrackRemoved(const sdbus::ObjectPath& TrackId)
+ {
+ object_->emitSignal("TrackRemoved").onInterface(INTERFACE_NAME).withArguments(TrackId);
+ }
+
+ void emitTrackMetadataChanged(const sdbus::ObjectPath& TrackId, const std::map& Metadata)
+ {
+ object_->emitSignal("TrackMetadataChanged").onInterface(INTERFACE_NAME).withArguments(TrackId, Metadata);
+ }
+
+private:
+ virtual std::vector> GetTracksMetadata(const std::vector& TrackIds) = 0;
+ virtual void AddTrack(const std::string& Uri, const sdbus::ObjectPath& AfterTrack, const bool& SetAsCurrent) = 0;
+ virtual void RemoveTrack(const sdbus::ObjectPath& TrackId) = 0;
+ virtual void GoTo(const sdbus::ObjectPath& TrackId) = 0;
+
+private:
+ virtual std::vector Tracks() = 0;
+ virtual bool CanEditTracks() = 0;
+
+private:
+ sdbus::IObject* object_;
+};
+
+}}} // namespaces
+
+#endif
diff --git a/assets/mpris_stub_proxy.hpp b/assets/mpris_stub_proxy.hpp
new file mode 100644
index 0000000..72a660b
--- /dev/null
+++ b/assets/mpris_stub_proxy.hpp
@@ -0,0 +1,306 @@
+
+/*
+ * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
+ */
+
+#ifndef __sdbuscpp__mpris_stub_proxy_hpp__proxy__H__
+#define __sdbuscpp__mpris_stub_proxy_hpp__proxy__H__
+
+#include
+#include
+#include
+
+namespace org {
+namespace mpris {
+
+class MediaPlayer2_proxy
+{
+public:
+ static constexpr const char* INTERFACE_NAME = "org.mpris.MediaPlayer2";
+
+protected:
+ MediaPlayer2_proxy(sdbus::IProxy& proxy)
+ : proxy_(&proxy)
+ {
+ }
+
+ MediaPlayer2_proxy(const MediaPlayer2_proxy&) = delete;
+ MediaPlayer2_proxy& operator=(const MediaPlayer2_proxy&) = delete;
+ MediaPlayer2_proxy(MediaPlayer2_proxy&&) = default;
+ MediaPlayer2_proxy& operator=(MediaPlayer2_proxy&&) = default;
+
+ ~MediaPlayer2_proxy() = default;
+
+public:
+ void Raise()
+ {
+ proxy_->callMethod("Raise").onInterface(INTERFACE_NAME);
+ }
+
+ void Quit()
+ {
+ proxy_->callMethod("Quit").onInterface(INTERFACE_NAME);
+ }
+
+public:
+ bool CanRaise()
+ {
+ return proxy_->getProperty("CanRaise").onInterface(INTERFACE_NAME);
+ }
+
+ bool CanQuit()
+ {
+ return proxy_->getProperty("CanQuit").onInterface(INTERFACE_NAME);
+ }
+
+ bool HasTrackList()
+ {
+ return proxy_->getProperty("HasTrackList").onInterface(INTERFACE_NAME);
+ }
+
+ std::string Identity()
+ {
+ return proxy_->getProperty("Identity").onInterface(INTERFACE_NAME);
+ }
+
+ std::vector SupportedUriSchemes()
+ {
+ return proxy_->getProperty("SupportedUriSchemes").onInterface(INTERFACE_NAME);
+ }
+
+ std::vector SupportedMimeTypes()
+ {
+ return proxy_->getProperty("SupportedMimeTypes").onInterface(INTERFACE_NAME);
+ }
+
+private:
+ sdbus::IProxy* proxy_;
+};
+
+}} // namespaces
+
+namespace org {
+namespace mpris {
+namespace MediaPlayer2 {
+
+class Player_proxy
+{
+public:
+ static constexpr const char* INTERFACE_NAME = "org.mpris.MediaPlayer2.Player";
+
+protected:
+ Player_proxy(sdbus::IProxy& proxy)
+ : proxy_(&proxy)
+ {
+ proxy_->uponSignal("Seeked").onInterface(INTERFACE_NAME).call([this](const int64_t& Position){ this->onSeeked(Position); });
+ }
+
+ Player_proxy(const Player_proxy&) = delete;
+ Player_proxy& operator=(const Player_proxy&) = delete;
+ Player_proxy(Player_proxy&&) = default;
+ Player_proxy& operator=(Player_proxy&&) = default;
+
+ ~Player_proxy() = default;
+
+ virtual void onSeeked(const int64_t& Position) = 0;
+
+public:
+ void Next()
+ {
+ proxy_->callMethod("Next").onInterface(INTERFACE_NAME);
+ }
+
+ void Previous()
+ {
+ proxy_->callMethod("Previous").onInterface(INTERFACE_NAME);
+ }
+
+ void Pause()
+ {
+ proxy_->callMethod("Pause").onInterface(INTERFACE_NAME);
+ }
+
+ void PlayPause()
+ {
+ proxy_->callMethod("PlayPause").onInterface(INTERFACE_NAME);
+ }
+
+ void Stop()
+ {
+ proxy_->callMethod("Stop").onInterface(INTERFACE_NAME);
+ }
+
+ void Play()
+ {
+ proxy_->callMethod("Play").onInterface(INTERFACE_NAME);
+ }
+
+ void Seek(const int64_t& Offset)
+ {
+ proxy_->callMethod("Seek").onInterface(INTERFACE_NAME).withArguments(Offset);
+ }
+
+ void SetPosition(const sdbus::ObjectPath& TrackId, const int64_t& Position)
+ {
+ proxy_->callMethod("SetPosition").onInterface(INTERFACE_NAME).withArguments(TrackId, Position);
+ }
+
+ void OpenUri(const std::string& Uri)
+ {
+ proxy_->callMethod("OpenUri").onInterface(INTERFACE_NAME).withArguments(Uri);
+ }
+
+public:
+ std::string PlaybackStatus()
+ {
+ return proxy_->getProperty("PlaybackStatus").onInterface(INTERFACE_NAME);
+ }
+
+ double Rate()
+ {
+ return proxy_->getProperty("Rate").onInterface(INTERFACE_NAME);
+ }
+
+ void Rate(const double& value)
+ {
+ proxy_->setProperty("Rate").onInterface(INTERFACE_NAME).toValue(value);
+ }
+
+ std::map Metadata()
+ {
+ return proxy_->getProperty("Metadata").onInterface(INTERFACE_NAME);
+ }
+
+ double Volume()
+ {
+ return proxy_->getProperty("Volume").onInterface(INTERFACE_NAME);
+ }
+
+ void Volume(const double& value)
+ {
+ proxy_->setProperty("Volume").onInterface(INTERFACE_NAME).toValue(value);
+ }
+
+ int64_t Position()
+ {
+ return proxy_->getProperty("Position").onInterface(INTERFACE_NAME);
+ }
+
+ double MinimumRate()
+ {
+ return proxy_->getProperty("MinimumRate").onInterface(INTERFACE_NAME);
+ }
+
+ double MaximumRate()
+ {
+ return proxy_->getProperty("MaximumRate").onInterface(INTERFACE_NAME);
+ }
+
+ bool CanGoNext()
+ {
+ return proxy_->getProperty("CanGoNext").onInterface(INTERFACE_NAME);
+ }
+
+ bool CanGoPrevious()
+ {
+ return proxy_->getProperty("CanGoPrevious").onInterface(INTERFACE_NAME);
+ }
+
+ bool CanPlay()
+ {
+ return proxy_->getProperty("CanPlay").onInterface(INTERFACE_NAME);
+ }
+
+ bool CanPause()
+ {
+ return proxy_->getProperty("CanPause").onInterface(INTERFACE_NAME);
+ }
+
+ bool CanSeek()
+ {
+ return proxy_->getProperty("CanSeek").onInterface(INTERFACE_NAME);
+ }
+
+ bool CanControl()
+ {
+ return proxy_->getProperty("CanControl").onInterface(INTERFACE_NAME);
+ }
+
+private:
+ sdbus::IProxy* proxy_;
+};
+
+}}} // namespaces
+
+namespace org {
+namespace mpris {
+namespace MediaPlayer2 {
+
+class TrackList_proxy
+{
+public:
+ static constexpr const char* INTERFACE_NAME = "org.mpris.MediaPlayer2.TrackList";
+
+protected:
+ TrackList_proxy(sdbus::IProxy& proxy)
+ : proxy_(&proxy)
+ {
+ proxy_->uponSignal("TrackListReplaced").onInterface(INTERFACE_NAME).call([this](const std::vector& Tracks, const sdbus::ObjectPath& CurrentTrack){ this->onTrackListReplaced(Tracks, CurrentTrack); });
+ proxy_->uponSignal("TrackAdded").onInterface(INTERFACE_NAME).call([this](const std::map& Metadata, const sdbus::ObjectPath& AfterTrack){ this->onTrackAdded(Metadata, AfterTrack); });
+ proxy_->uponSignal("TrackRemoved").onInterface(INTERFACE_NAME).call([this](const sdbus::ObjectPath& TrackId){ this->onTrackRemoved(TrackId); });
+ proxy_->uponSignal("TrackMetadataChanged").onInterface(INTERFACE_NAME).call([this](const sdbus::ObjectPath& TrackId, const std::map& Metadata){ this->onTrackMetadataChanged(TrackId, Metadata); });
+ }
+
+ TrackList_proxy(const TrackList_proxy&) = delete;
+ TrackList_proxy& operator=(const TrackList_proxy&) = delete;
+ TrackList_proxy(TrackList_proxy&&) = default;
+ TrackList_proxy& operator=(TrackList_proxy&&) = default;
+
+ ~TrackList_proxy() = default;
+
+ virtual void onTrackListReplaced(const std::vector& Tracks, const sdbus::ObjectPath& CurrentTrack) = 0;
+ virtual void onTrackAdded(const std::map& Metadata, const sdbus::ObjectPath& AfterTrack) = 0;
+ virtual void onTrackRemoved(const sdbus::ObjectPath& TrackId) = 0;
+ virtual void onTrackMetadataChanged(const sdbus::ObjectPath& TrackId, const std::map& Metadata) = 0;
+
+public:
+ std::vector> GetTracksMetadata(const std::vector& TrackIds)
+ {
+ std::vector> result;
+ proxy_->callMethod("GetTracksMetadata").onInterface(INTERFACE_NAME).withArguments(TrackIds).storeResultsTo(result);
+ return result;
+ }
+
+ void AddTrack(const std::string& Uri, const sdbus::ObjectPath& AfterTrack, const bool& SetAsCurrent)
+ {
+ proxy_->callMethod("AddTrack").onInterface(INTERFACE_NAME).withArguments(Uri, AfterTrack, SetAsCurrent);
+ }
+
+ void RemoveTrack(const sdbus::ObjectPath& TrackId)
+ {
+ proxy_->callMethod("RemoveTrack").onInterface(INTERFACE_NAME).withArguments(TrackId);
+ }
+
+ void GoTo(const sdbus::ObjectPath& TrackId)
+ {
+ proxy_->callMethod("GoTo").onInterface(INTERFACE_NAME).withArguments(TrackId);
+ }
+
+public:
+ std::vector Tracks()
+ {
+ return proxy_->getProperty("Tracks").onInterface(INTERFACE_NAME);
+ }
+
+ bool CanEditTracks()
+ {
+ return proxy_->getProperty("CanEditTracks").onInterface(INTERFACE_NAME);
+ }
+
+private:
+ sdbus::IProxy* proxy_;
+};
+
+}}} // namespaces
+
+#endif
diff --git a/assets/update_assets.py b/assets/update_assets.py
index b709fae..cff910a 100755
--- a/assets/update_assets.py
+++ b/assets/update_assets.py
@@ -4,11 +4,21 @@ import sys
import subprocess
import os.path as path
import shutil
+from shutil import which
from glob import glob
olddir = os.curdir
print("Entering assets directory to begin asset conversion...")
os.chdir(path.realpath(path.dirname(__file__)))
ASSETS = []
+WARNINGS: dict[str, bool] = {}
+def warn(id: str, message: str, WARNINGS = WARNINGS) -> None:
+ warned_already: bool = False
+ try:
+ warned_already = WARNINGS[id]
+ except KeyError:
+ pass
+ if not warned_already:
+ print("WARNING: %s" % message)
def run_btcc(args: list[str], outpath: str):
with open(outpath + ".h", "wt+") as f:
actual_args = ["./btcc"]
@@ -44,6 +54,15 @@ def add_css(file: str, output: str):
CSS_FILE = output + ".h"
print("Adding CSS file '%s' (C identifier '%s') from file '%s'" % (file, output, CSS_FILE))
add_basic(file, output)
+def add_dbus(file: str, output_basename: str, adaptor: str|None = None, proxy: str|None = None, WARNINGS = WARNINGS):
+ if adaptor == None:
+ adaptor = output_basename + '_adaptor.hpp'
+ if proxy == None:
+ proxy = output_basename + '_proxy.hpp'
+ if which('sdbus-c++-xml2cpp') is not None:
+ subprocess.call(['sdbus-c++-xml2cpp', file, '--adaptor=' + adaptor, '--proxy=' + proxy])
+ else:
+ warn("Not generating DBus API stubs.")
compile_program("../backends/ui/imgui/imgui/misc/fonts/binary_to_compressed_c.cpp", "btcc")
for i in glob("Noto_Sans/*.ttf"):
add_font(i)
@@ -69,6 +88,8 @@ add_license("licenses/cli11.txt", "cli11")
add_license("licenses/TomlPlusPlus.txt", "tomlplusplus")
add_license("../backends/ui/imgui/IconFontCppHeaders/licence.txt", "icnfntcpphdrs")
add_css("gtk-frontend.css", "gtk_frontend_css")
+add_dbus('app.dbus.xml', 'dbus_stub')
+add_dbus('mpris.dbus.xml', 'mpris_stub')
def finalize(output: str, ASSETS = ASSETS):
print("Writing a header including all previous asset headers and writing it to '%s'..." % output)
with open(output, "wt+") as f:
diff --git a/backend.cpp b/backend.cpp
index f042d84..3848f69 100644
--- a/backend.cpp
+++ b/backend.cpp
@@ -8,57 +8,87 @@ std::map UIBackend::backends;
std::string UIBackend::get_id() {
return "";
}
-int UIBackend::run(std::vector realArgs, int argc, char **argv) {
+UIBackend *UIBackend::running_ui_backend = nullptr;
+void UIBackend::init_playback() {
+ if (multi_instance) {
+ playback = new PlaybackInstance();
+ } else {
+ playback = Playback::Create(&daemon_found);
+ if (playback == nullptr) {
+ throw 1;
+ }
+ }
+}
+void UIBackend::init_libportal() {
#ifdef GLIB_ENABLED
- g_set_application_name("Looper")
+ g_set_application_name("Looper");
#endif
+}
+void UIBackend::setup_playback_args() {
+ if (speed_set) {
+ playback->SetSpeed(new_speed);
+ }
+ if (tempo_set) {
+ playback->SetTempo(new_tempo);
+ }
+ if (pitch_set) {
+ playback->SetPitch(new_pitch);
+ }
+ if (args.size() > 0) {
+ playback->Start(args[0]);
+ }
+ if (!daemon_found && playback->is_proxy()) {
+ throw 0;
+ }
+}
+bool UIBackend::parse_args(std::vector realArgs, int argc, char **argv) {
args = realArgs;
- playback = new PlaybackInstance();
CLI::App app{DESCRIPTION};
std::string filename = "";
app.allow_extras();
- double new_speed = 1.0;
- double new_tempo = 1.0;
- double new_pitch = 1.0;
auto speed_opt = app.add_option("-s,--speed", new_speed, "Set the initial speed of the playback.")->default_val(1.0);
auto tempo_opt = app.add_option("-t,--tempo", new_tempo, "Set the initial tempo of the playback.")->default_val(1.0);
auto pitch_opt = app.add_option("-p,--pitch", new_pitch, "Set the initial pitch of the playback.")->default_val(1.0);
- bool multi_instance = false;
- auto multi_instance_opt = app.add_flag("-m,--multi-instance", multi_instance, "Disables the DBus api to allow multiple instances.");
+ std::vector options;
+ options.push_back(speed_opt);
+ options.push_back(tempo_opt);
+ options.push_back(pitch_opt);
+ if (allow_multi_instance()) {
+ auto multi_instance_opt = app.add_flag("-m,--multi-instance", multi_instance, "Disables the DBus api to allow multiple instances.");
+ options.push_back(multi_instance_opt);
+ }
try {
app.parse(args);
} catch (const CLI::ParseError &e) {
throw app.exit(e);
}
- playback->SetSpeed(new_speed);
- playback->SetTempo(new_tempo);
- playback->SetPitch(new_pitch);
args = app.remaining();
- if (!multi_instance) {
- DBusAPISender sender;
- if (!sender.isOnlyInstance()) {
- if (args.size() > 0) {
- sender.playFile(args[0]);
- }
- if (!speed_opt->empty()) {
- sender.setSpeed(new_speed);
- }
- if (!tempo_opt->empty()) {
- sender.setTempo(new_tempo);
- }
- if (!pitch_opt->empty()) {
- sender.setPitch(new_pitch);
- }
- throw 0;
- } else {
- dbus_api = new DBusAPI(playback);
- }
- } else {
- dbus_api = nullptr;
- }
+
+ speed_set = !speed_opt->empty();
+ tempo_set = !tempo_opt->empty();
+ pitch_set = !pitch_opt->empty();
if (args.size() > 0) {
- playback->Start(args[0]);
+ return true;
+ } else {
+ for (auto opt : options) {
+ if (!opt->empty()) {
+ return true;
+ }
+ }
}
+ return false;
+}
+void UIBackend::init_dbus() {
+ if (!multi_instance && !playback->is_proxy()) {
+ dbus_api = DBusAPI::Create(playback);
+ }
+}
+int UIBackend::run(std::vector realArgs, int argc, char **argv) {
+ init_libportal();
+ parse_args(realArgs, argc, argv);
+ init_playback();
+ setup_playback_args();
+ init_dbus();
return 0;
}
void UIBackend::deinit_backends() {
diff --git a/backend.hpp b/backend.hpp
index d37e977..b992d68 100644
--- a/backend.hpp
+++ b/backend.hpp
@@ -3,28 +3,53 @@
#include
#include