Update DBus API
This commit is contained in:
parent
57cb111358
commit
b84fec3175
27 changed files with 2502 additions and 329 deletions
|
@ -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")
|
||||
|
|
106
assets/app.dbus.xml
Normal file
106
assets/app.dbus.xml
Normal file
|
@ -0,0 +1,106 @@
|
|||
<node name="/com/complecwaft/Looper">
|
||||
<interface name='org.freedesktop.Application'>
|
||||
<method name='Activate'>
|
||||
<arg type='a{sv}' name='platform_data' direction='in'/>
|
||||
</method>
|
||||
<method name='Open'>
|
||||
<arg type='as' name='uris' direction='in'/>
|
||||
<arg type='a{sv}' name='platform_data' direction='in'/>
|
||||
</method>
|
||||
<method name='ActivateAction'>
|
||||
<arg type='s' name='action_name' direction='in'/>
|
||||
<arg type='av' name='parameter' direction='in'/>
|
||||
<arg type='a{sv}' name='platform_data' direction='in'/>
|
||||
</method>
|
||||
</interface>
|
||||
<interface name="com.complecwaft.Looper">
|
||||
<method name="CreateHandle">
|
||||
<arg type='s' name='new_handle' direction='out' />
|
||||
</method>
|
||||
<method name="ClearHandle">
|
||||
<arg type='s' name='handle' direction='in' />
|
||||
</method>
|
||||
<method name="Start">
|
||||
<arg type='s' name='path' direction='in' />
|
||||
</method>
|
||||
<method name="StartByURI">
|
||||
<arg type='s' name='uri' direction='in' />
|
||||
</method>
|
||||
<method name="Quit" />
|
||||
<method name="Stop" />
|
||||
<method name="TogglePause" />
|
||||
<signal name="PlaybackEngineStarted" />
|
||||
<signal name="SpeedChanged">
|
||||
<arg name="new_speed" type="d" />
|
||||
</signal>
|
||||
<signal name="TempoChanged">
|
||||
<arg name="new_tempo" type="d" />
|
||||
</signal>
|
||||
<signal name="PitchChanged">
|
||||
<arg name="new_pitch" type="d" />
|
||||
</signal>
|
||||
<signal name="PauseChanged">
|
||||
<arg name="now_paused" type="b" />
|
||||
</signal>
|
||||
<signal name="Stopped" />
|
||||
<signal name="ErrorOccurred">
|
||||
<arg name="error_desc" type="s" />
|
||||
<arg name="error_type" type="s" />
|
||||
</signal>
|
||||
<signal name="Seeked">
|
||||
<arg name="to_position" type="d" />
|
||||
</signal>
|
||||
<signal name="FileChanged">
|
||||
<arg name="path" type="s" />
|
||||
<arg name="title" type="s" />
|
||||
</signal>
|
||||
<property name="FilePath" type="s" access="read" />
|
||||
<property name="FileTitle" type="s" access="read" />
|
||||
<property name="Position" type="d" access="readwrite" />
|
||||
<property name="Length" type="d" access="read" />
|
||||
<property name="Speed" type="d" access="readwrite" />
|
||||
<property name="Tempo" type="d" access="readwrite" />
|
||||
<property name="Pitch" type="d" access="readwrite" />
|
||||
<property name="Volume" type="d" access="readwrite" />
|
||||
<property name="Paused" type="b" access="readwrite" />
|
||||
<property name="IsStopped" type="b" access="read" />
|
||||
<property name="IsDaemon" type="b" access="read" />
|
||||
</interface>
|
||||
<interface name="com.complecwaft.Looper.Errors" >
|
||||
<method name="PopFront">
|
||||
<arg name="handle" direction="in" type="s" />
|
||||
<arg name="error" direction="out" type="s" />
|
||||
</method>
|
||||
<method name="PopBack">
|
||||
<arg name="handle" direction="in" type="s" />
|
||||
<arg name="error" direction="out" type="s" />
|
||||
</method>
|
||||
<method name="PeekFront">
|
||||
<arg name="handle" direction="in" type="s" />
|
||||
<arg name="error" direction="out" type="s" />
|
||||
</method>
|
||||
<method name="PeekBack">
|
||||
<arg name="handle" direction="in" type="s" />
|
||||
<arg name="error" direction="out" type="s" />
|
||||
</method>
|
||||
<method name="GetCount">
|
||||
<arg name="handle" direction="in" type="s" />
|
||||
<arg name="count" direction="out" type="u" />
|
||||
</method>
|
||||
<method name="IsEmpty">
|
||||
<arg name="handle" direction="in" type="s" />
|
||||
<arg name="empty" direction="out" type="b" />
|
||||
</method>
|
||||
<method name="Clear">
|
||||
<arg name="handle" direction="in" type="s" />
|
||||
</method>
|
||||
<method name="PeekAll">
|
||||
<arg name="handle" direction="in" type="s" />
|
||||
<arg name="errors" direction="out" type="as" />
|
||||
</method>
|
||||
<method name="GetAllAndClear">
|
||||
<arg name="handle" direction="in" type="s" />
|
||||
<arg name="errors" direction="out" type="as" />
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
28
assets/com.complecwaft.Looper.desktop
Executable file
28
assets/com.complecwaft.Looper.desktop
Executable file
|
@ -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
|
|
@ -1,13 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>com.experimentalcraft.NekoPlayer</id>
|
||||
<id>com.complecwaft.Looper</id>
|
||||
<developer_name>Catmeow72</developer_name>
|
||||
|
||||
<name>Neko Player</name>
|
||||
<name>Looper</name>
|
||||
<summary>An audio player that can play back files with seamless loops using their metadata.</summary>
|
||||
|
||||
<metadata_license>MIT</metadata_license>
|
||||
<project_license>MIT AND LGPL-2.1-only AND Zlib</project_license>
|
||||
<project_license>MIT OR GPL-3.0-or-later</project_license>
|
||||
<content_rating type="oars-1.1" />
|
||||
|
||||
<description>
|
224
assets/dbus_stub_adaptor.hpp
Normal file
224
assets/dbus_stub_adaptor.hpp
Normal file
|
@ -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 <sdbus-c++/sdbus-c++.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
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<std::string, sdbus::Variant>& platform_data){ return this->Activate(platform_data); });
|
||||
object_->registerMethod("Open").onInterface(INTERFACE_NAME).withInputParamNames("uris", "platform_data").implementedAs([this](const std::vector<std::string>& uris, const std::map<std::string, sdbus::Variant>& 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<sdbus::Variant>& parameter, const std::map<std::string, sdbus::Variant>& 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<std::string, sdbus::Variant>& platform_data) = 0;
|
||||
virtual void Open(const std::vector<std::string>& uris, const std::map<std::string, sdbus::Variant>& platform_data) = 0;
|
||||
virtual void ActivateAction(const std::string& action_name, const std::vector<sdbus::Variant>& parameter, const std::map<std::string, sdbus::Variant>& 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<double>("new_speed");
|
||||
object_->registerSignal("TempoChanged").onInterface(INTERFACE_NAME).withParameters<double>("new_tempo");
|
||||
object_->registerSignal("PitchChanged").onInterface(INTERFACE_NAME).withParameters<double>("new_pitch");
|
||||
object_->registerSignal("PauseChanged").onInterface(INTERFACE_NAME).withParameters<bool>("now_paused");
|
||||
object_->registerSignal("Stopped").onInterface(INTERFACE_NAME);
|
||||
object_->registerSignal("ErrorOccurred").onInterface(INTERFACE_NAME).withParameters<std::string, std::string>("error_desc", "error_type");
|
||||
object_->registerSignal("Seeked").onInterface(INTERFACE_NAME).withParameters<double>("to_position");
|
||||
object_->registerSignal("FileChanged").onInterface(INTERFACE_NAME).withParameters<std::string, std::string>("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<std::string> PeekAll(const std::string& handle) = 0;
|
||||
virtual std::vector<std::string> GetAllAndClear(const std::string& handle) = 0;
|
||||
|
||||
private:
|
||||
sdbus::IObject* object_;
|
||||
};
|
||||
|
||||
}}} // namespaces
|
||||
|
||||
#endif
|
316
assets/dbus_stub_proxy.hpp
Normal file
316
assets/dbus_stub_proxy.hpp
Normal file
|
@ -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 <sdbus-c++/sdbus-c++.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
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<std::string, sdbus::Variant>& platform_data)
|
||||
{
|
||||
proxy_->callMethod("Activate").onInterface(INTERFACE_NAME).withArguments(platform_data);
|
||||
}
|
||||
|
||||
void Open(const std::vector<std::string>& uris, const std::map<std::string, sdbus::Variant>& platform_data)
|
||||
{
|
||||
proxy_->callMethod("Open").onInterface(INTERFACE_NAME).withArguments(uris, platform_data);
|
||||
}
|
||||
|
||||
void ActivateAction(const std::string& action_name, const std::vector<sdbus::Variant>& parameter, const std::map<std::string, sdbus::Variant>& 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<std::string> PeekAll(const std::string& handle)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
proxy_->callMethod("PeekAll").onInterface(INTERFACE_NAME).withArguments(handle).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetAllAndClear(const std::string& handle)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
proxy_->callMethod("GetAllAndClear").onInterface(INTERFACE_NAME).withArguments(handle).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy* proxy_;
|
||||
};
|
||||
|
||||
}}} // namespaces
|
||||
|
||||
#endif
|
|
@ -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;
|
81
assets/mpris.dbus.xml
Normal file
81
assets/mpris.dbus.xml
Normal file
|
@ -0,0 +1,81 @@
|
|||
<node name="/org/mpris/MediaPlayer2">
|
||||
|
||||
<interface name="org.mpris.MediaPlayer2">
|
||||
<method name="Raise" />
|
||||
<method name="Quit" />
|
||||
<property name="CanRaise" type="b" access="read" />
|
||||
<property name="CanQuit" type="b" access="read" />
|
||||
<property name="HasTrackList" type="b" access="read" />
|
||||
<property name="Identity" type="s" access="read" />
|
||||
<property name="SupportedUriSchemes" type="as" access="read" />
|
||||
<property name="SupportedMimeTypes" type="as" access="read" />
|
||||
</interface>
|
||||
<interface name="org.mpris.MediaPlayer2.Player">
|
||||
<method name="Next" />
|
||||
<method name="Previous" />
|
||||
<method name="Pause" />
|
||||
<method name="PlayPause" />
|
||||
<method name="Stop" />
|
||||
<method name="Play" />
|
||||
<method name="Seek">
|
||||
<arg name="Offset" type="x" direction="in" />
|
||||
</method>
|
||||
<method name="SetPosition">
|
||||
<arg name="TrackId" type="o" direction="in" />
|
||||
<arg name="Position" type="x" direction="in" />
|
||||
</method>
|
||||
<method name="OpenUri">
|
||||
<arg name="Uri" type="s" direction="in" />
|
||||
</method>
|
||||
<signal name="Seeked">
|
||||
<arg name="Position" type="x" />
|
||||
</signal>
|
||||
<property name="PlaybackStatus" type="s" access="read" />
|
||||
<property name="Rate" type="d" access="readwrite" />
|
||||
<property name="Metadata" type="a{sv}" access="read" />
|
||||
<property name="Volume" type="d" access="readwrite" />
|
||||
<property name="Position" type="x" access="read" />
|
||||
<property name="MinimumRate" type="d" access="read" />
|
||||
<property name="MaximumRate" type="d" access="read" />
|
||||
<property name="CanGoNext" type="b" access="read" />
|
||||
<property name="CanGoPrevious" type="b" access="read" />
|
||||
<property name="CanPlay" type="b" access="read" />
|
||||
<property name="CanPause" type="b" access="read" />
|
||||
<property name="CanSeek" type="b" access="read" />
|
||||
<property name="CanControl" type="b" access="read" />
|
||||
</interface>
|
||||
<interface name="org.mpris.MediaPlayer2.TrackList">
|
||||
<method name="GetTracksMetadata">
|
||||
<arg name="TrackIds" direction="in" type="ao" />
|
||||
<arg name="Metadata" direction="out" type="aa{sv}" />
|
||||
</method>
|
||||
<method name="AddTrack">
|
||||
<arg name="Uri" type="s" direction="in" />
|
||||
<arg name="AfterTrack" type="o" direction="in" />
|
||||
<arg name="SetAsCurrent" type="b" direction="in" />
|
||||
</method>
|
||||
<method name="RemoveTrack">
|
||||
<arg name="TrackId" type="o" direction="in" />
|
||||
</method>
|
||||
<method name="GoTo">
|
||||
<arg name="TrackId" type="o" direction="in" />
|
||||
</method>
|
||||
<signal name="TrackListReplaced">
|
||||
<arg name="Tracks" type="ao" />
|
||||
<arg name="CurrentTrack" type="o" />
|
||||
</signal>
|
||||
<signal name="TrackAdded">
|
||||
<arg name="Metadata" type="a{sv}" />
|
||||
<arg name="AfterTrack" type="o" />
|
||||
</signal>
|
||||
<signal name="TrackRemoved">
|
||||
<arg name="TrackId" type="o" />
|
||||
</signal>
|
||||
<signal name="TrackMetadataChanged">
|
||||
<arg name="TrackId" type="o" />
|
||||
<arg name="Metadata" type="a{sv}" />
|
||||
</signal>
|
||||
<property name="Tracks" type="ao" access="read" />
|
||||
<property name="CanEditTracks" type="b" access="read" />
|
||||
</interface>
|
||||
</node>
|
214
assets/mpris_stub_adaptor.hpp
Normal file
214
assets/mpris_stub_adaptor.hpp
Normal file
|
@ -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 <sdbus-c++/sdbus-c++.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
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<std::string> SupportedUriSchemes() = 0;
|
||||
virtual std::vector<std::string> 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<int64_t>("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<std::string, sdbus::Variant> 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<sdbus::ObjectPath>& 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<std::vector<sdbus::ObjectPath>, sdbus::ObjectPath>("Tracks", "CurrentTrack");
|
||||
object_->registerSignal("TrackAdded").onInterface(INTERFACE_NAME).withParameters<std::map<std::string, sdbus::Variant>, sdbus::ObjectPath>("Metadata", "AfterTrack");
|
||||
object_->registerSignal("TrackRemoved").onInterface(INTERFACE_NAME).withParameters<sdbus::ObjectPath>("TrackId");
|
||||
object_->registerSignal("TrackMetadataChanged").onInterface(INTERFACE_NAME).withParameters<sdbus::ObjectPath, std::map<std::string, sdbus::Variant>>("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<sdbus::ObjectPath>& Tracks, const sdbus::ObjectPath& CurrentTrack)
|
||||
{
|
||||
object_->emitSignal("TrackListReplaced").onInterface(INTERFACE_NAME).withArguments(Tracks, CurrentTrack);
|
||||
}
|
||||
|
||||
void emitTrackAdded(const std::map<std::string, sdbus::Variant>& 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<std::string, sdbus::Variant>& Metadata)
|
||||
{
|
||||
object_->emitSignal("TrackMetadataChanged").onInterface(INTERFACE_NAME).withArguments(TrackId, Metadata);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual std::vector<std::map<std::string, sdbus::Variant>> GetTracksMetadata(const std::vector<sdbus::ObjectPath>& 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<sdbus::ObjectPath> Tracks() = 0;
|
||||
virtual bool CanEditTracks() = 0;
|
||||
|
||||
private:
|
||||
sdbus::IObject* object_;
|
||||
};
|
||||
|
||||
}}} // namespaces
|
||||
|
||||
#endif
|
306
assets/mpris_stub_proxy.hpp
Normal file
306
assets/mpris_stub_proxy.hpp
Normal file
|
@ -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 <sdbus-c++/sdbus-c++.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
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<std::string> SupportedUriSchemes()
|
||||
{
|
||||
return proxy_->getProperty("SupportedUriSchemes").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
std::vector<std::string> 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<std::string, sdbus::Variant> 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<sdbus::ObjectPath>& Tracks, const sdbus::ObjectPath& CurrentTrack){ this->onTrackListReplaced(Tracks, CurrentTrack); });
|
||||
proxy_->uponSignal("TrackAdded").onInterface(INTERFACE_NAME).call([this](const std::map<std::string, sdbus::Variant>& 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<std::string, sdbus::Variant>& 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<sdbus::ObjectPath>& Tracks, const sdbus::ObjectPath& CurrentTrack) = 0;
|
||||
virtual void onTrackAdded(const std::map<std::string, sdbus::Variant>& Metadata, const sdbus::ObjectPath& AfterTrack) = 0;
|
||||
virtual void onTrackRemoved(const sdbus::ObjectPath& TrackId) = 0;
|
||||
virtual void onTrackMetadataChanged(const sdbus::ObjectPath& TrackId, const std::map<std::string, sdbus::Variant>& Metadata) = 0;
|
||||
|
||||
public:
|
||||
std::vector<std::map<std::string, sdbus::Variant>> GetTracksMetadata(const std::vector<sdbus::ObjectPath>& TrackIds)
|
||||
{
|
||||
std::vector<std::map<std::string, sdbus::Variant>> 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<sdbus::ObjectPath> Tracks()
|
||||
{
|
||||
return proxy_->getProperty("Tracks").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
bool CanEditTracks()
|
||||
{
|
||||
return proxy_->getProperty("CanEditTracks").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy* proxy_;
|
||||
};
|
||||
|
||||
}}} // namespaces
|
||||
|
||||
#endif
|
|
@ -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:
|
||||
|
|
98
backend.cpp
98
backend.cpp
|
@ -8,57 +8,87 @@ std::map<std::string, UIBackend*> UIBackend::backends;
|
|||
std::string UIBackend::get_id() {
|
||||
return "";
|
||||
}
|
||||
int UIBackend::run(std::vector<std::string> 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<std::string> 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<bool>("-m,--multi-instance", multi_instance, "Disables the DBus api to allow multiple instances.");
|
||||
std::vector<CLI::Option*> 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<bool>("-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<std::string> 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() {
|
||||
|
|
83
backend.hpp
83
backend.hpp
|
@ -3,28 +3,53 @@
|
|||
#include <string>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <atomic>
|
||||
#include "playback.h"
|
||||
#include "dbus.hpp"
|
||||
class UIBackend {
|
||||
protected:
|
||||
std::vector<std::string> args;
|
||||
Playback *playback;
|
||||
std::atomic_bool exit_flag = false;
|
||||
double new_speed = 1.0;
|
||||
double new_tempo = 1.0;
|
||||
double new_pitch = 1.0;
|
||||
bool speed_set = false;
|
||||
bool tempo_set = false;
|
||||
bool pitch_set = false;
|
||||
bool multi_instance = false;
|
||||
bool daemon_found = false;
|
||||
public:
|
||||
DBusAPI *dbus_api;
|
||||
inline virtual bool allow_multi_instance() {
|
||||
return true;
|
||||
}
|
||||
virtual std::string get_id();
|
||||
virtual std::string get_name();
|
||||
UIBackend() = default;
|
||||
virtual void add_licenses() {
|
||||
/// @brief A hook to add any licenses of software packages used by the UI backend.
|
||||
inline virtual void add_licenses() {
|
||||
// Don't add any here, but leave this specified. That way, licenses specific to UI frontends are only added per UI frontend.
|
||||
}
|
||||
void init_libportal();
|
||||
void init_playback();
|
||||
void setup_playback_args();
|
||||
void init_dbus();
|
||||
bool parse_args(std::vector<std::string> realArgs, int argc, char **argv);
|
||||
/// @brief The main loop of the UI. Be sure to call @ref UIBackend::run and be prepared for it to throw an integer exit code that needs to escape your implementation.
|
||||
virtual int run(std::vector<std::string> realArgs, int argc, char **argv);
|
||||
/// @brief A hook that is called when the D-Bus API receives a request to exit the program.
|
||||
inline virtual void QuitHandler() {
|
||||
// Set a flag for loops managed by the UI backend.
|
||||
exit_flag.store(true);
|
||||
}
|
||||
static std::map<std::string, UIBackend*> backends;
|
||||
static inline std::optional<UIBackend*> get_backend(std::string id) {
|
||||
if (backends.contains(id)) {
|
||||
return backends[id];
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
template<class T>
|
||||
static inline std::string get_backend_id() {
|
||||
UIBackend *backend = new T();
|
||||
auto output = backend->get_id();
|
||||
delete backend;
|
||||
return output;
|
||||
}
|
||||
static inline UIBackend* get_first_backend() {
|
||||
if (backends.empty()) {
|
||||
|
@ -32,9 +57,7 @@ class UIBackend {
|
|||
}
|
||||
return (*backends.begin()).second;
|
||||
}
|
||||
template<class T>
|
||||
static void register_backend() {
|
||||
UIBackend *backend = new T();
|
||||
static void register_backend(UIBackend *backend) {
|
||||
std::string backend_id = backend->get_id();
|
||||
if (backends.contains(backend_id)) { // Guard against potential memory leak due to reassigning a new pointer without deallocating the previous one
|
||||
delete backend;
|
||||
|
@ -42,7 +65,47 @@ class UIBackend {
|
|||
}
|
||||
backends[backend_id] = backend;
|
||||
}
|
||||
template<class T>
|
||||
static void register_backend() {
|
||||
UIBackend *backend = new T();
|
||||
backend->register_self();
|
||||
}
|
||||
static inline void unregister_backend(std::string id) {
|
||||
if (backends.contains(id)) {
|
||||
auto backend = backends[id];
|
||||
delete backend;
|
||||
backends.erase(id);
|
||||
}
|
||||
}
|
||||
inline void unregister_self() {
|
||||
UIBackend::unregister_backend(get_id());
|
||||
}
|
||||
inline void register_self() {
|
||||
UIBackend::register_backend(this);
|
||||
}
|
||||
template<class T>
|
||||
static void unregister_backend() {
|
||||
unregister_backend(get_backend_id<T>());
|
||||
}
|
||||
static void deinit_backends();
|
||||
static inline std::optional<UIBackend*> get_backend(std::string id) {
|
||||
if (backends.contains(id)) {
|
||||
return backends[id];
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
template<class T>
|
||||
static inline std::optional<T*> get_backend() {
|
||||
auto id = get_backend_id<T>();
|
||||
auto output = get_backend(id);
|
||||
if (output.has_value()) {
|
||||
return (T*)output.value();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
static UIBackend *running_ui_backend;
|
||||
virtual ~UIBackend();
|
||||
};
|
||||
void init_backends();
|
|
@ -14,7 +14,7 @@ int GtkBackend::run(std::vector<std::string> realArgs, int argc, char **argv) {
|
|||
return ret;
|
||||
}
|
||||
Glib::set_application_name("Looper");
|
||||
auto app = Gtk::Application::create("com.experimentalcraft.Looper.GTK", Gio::Application::Flags::NON_UNIQUE);
|
||||
auto app = Gtk::Application::create("com.complecwaft.Looper.GTK", Gio::Application::Flags::NON_UNIQUE);
|
||||
char *gtk_frontend_css = (char*)malloc(gtk_frontend_css_size + 1);
|
||||
memcpy(gtk_frontend_css, gtk_frontend_css_data, gtk_frontend_css_size);
|
||||
gtk_frontend_css[gtk_frontend_css_size] = '\0';
|
||||
|
@ -32,10 +32,16 @@ int GtkBackend::run(std::vector<std::string> realArgs, int argc, char **argv) {
|
|||
auto *win = new MainWindow(playback, app);
|
||||
win->present();
|
||||
});
|
||||
app_ptr = app.get();
|
||||
ret = app->run(argc, argv);
|
||||
|
||||
app_ptr = nullptr;
|
||||
return ret;
|
||||
}
|
||||
void GtkBackend::QuitHandler() {
|
||||
if (app_ptr != nullptr) {
|
||||
((Gtk::Application*)app_ptr)->quit();
|
||||
}
|
||||
}
|
||||
void GtkBackend::add_licenses() {
|
||||
auto &license_data = get_license_data();
|
||||
auto gtkmm = LicenseData("GtkMM", "lgpl-2.1");
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
#include <vector>
|
||||
#define GTK_FRONTEND
|
||||
class GtkBackend : public UIBackend {
|
||||
void *app_ptr;
|
||||
public:
|
||||
std::string get_id() override;
|
||||
std::string get_name() override;
|
||||
void add_licenses() override;
|
||||
void QuitHandler() override;
|
||||
int run(std::vector<std::string> realArgs, int argc, char **argv) override;
|
||||
};
|
|
@ -361,6 +361,9 @@ void MainLoop::GuiFunction() {
|
|||
// Make sure to not load the file unnecessarily.
|
||||
fileDialog.ClearSelected();
|
||||
}
|
||||
if (exit_flag.load()) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
void MainLoop::LoadFile(std::string file) {
|
||||
playback->Start(file);
|
||||
|
@ -408,6 +411,12 @@ std::string ImGuiUIBackend::get_id() {
|
|||
std::string ImGuiUIBackend::get_name() {
|
||||
return "Dear ImGui frontend";
|
||||
}
|
||||
void ImGuiUIBackend::QuitHandler() {
|
||||
if (main_loop == nullptr) {
|
||||
return;
|
||||
}
|
||||
((MainLoop*)main_loop)->exit_flag.store(true);
|
||||
}
|
||||
// Main code
|
||||
int ImGuiUIBackend::run(std::vector<std::string> realArgs, int argc, char** argv)
|
||||
{
|
||||
|
@ -418,6 +427,7 @@ int ImGuiUIBackend::run(std::vector<std::string> realArgs, int argc, char** argv
|
|||
MainLoop loop;
|
||||
loop.playback = playback;
|
||||
loop.args = args;
|
||||
main_loop = &loop;
|
||||
return loop.Run();
|
||||
}
|
||||
void ImGuiUIBackend::add_licenses() {
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "../libs/emscripten/emscripten_mainloop_stub.h"
|
||||
#endif
|
||||
#include "../../../backend.hpp"
|
||||
#include "ui_backend.hpp"
|
||||
using namespace std::filesystem;
|
||||
using namespace std::numbers;
|
||||
using std::string;
|
||||
|
@ -50,6 +51,8 @@ class MainLoop : public RendererBackend {
|
|||
bool stopped = true;
|
||||
std::vector<UIBackend*> backends;
|
||||
UIBackend *cur_backend;
|
||||
friend class ImGuiUIBackend;
|
||||
std::atomic_bool exit_flag;
|
||||
public:
|
||||
Playback *playback;
|
||||
vector<std::string> args;
|
||||
|
|
|
@ -6,6 +6,8 @@ class ImGuiUIBackend : public UIBackend {
|
|||
public:
|
||||
std::string get_id() override;
|
||||
std::string get_name() override;
|
||||
void QuitHandler() override;
|
||||
void *main_loop;
|
||||
int run(std::vector<std::string> realArgs, int argc, char **argv) override;
|
||||
void add_licenses() override;
|
||||
ImGuiUIBackend() = default;
|
||||
|
|
29
daemon_backend.cpp
Normal file
29
daemon_backend.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include "daemon_backend.hpp"
|
||||
#include "log.hpp"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
using namespace std::literals::chrono_literals;
|
||||
std::string DaemonGlueBackend::get_id() {
|
||||
return "daemon";
|
||||
}
|
||||
std::string DaemonGlueBackend::get_name() {
|
||||
return "Daemon Backend";
|
||||
}
|
||||
bool DaemonGlueBackend::allow_multi_instance() {
|
||||
return false;
|
||||
}
|
||||
int DaemonGlueBackend::run(std::vector<std::string> realArgs, int argc, char **argv) {
|
||||
parse_args(realArgs, argc, argv);
|
||||
playback = Playback::Create(nullptr, true);
|
||||
if (playback == nullptr) {
|
||||
ERROR.writeln("Cannot start daemon with existing instance running.");
|
||||
return 1;
|
||||
}
|
||||
setup_playback_args();
|
||||
dbus_api = DBusAPI::Create(playback, true);
|
||||
exit_flag.store(false);
|
||||
while (!exit_flag.load()) {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
return 0;
|
||||
}
|
9
daemon_backend.hpp
Normal file
9
daemon_backend.hpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
#include "backend.hpp"
|
||||
class DaemonGlueBackend : public UIBackend {
|
||||
public:
|
||||
bool allow_multi_instance() override;
|
||||
std::string get_id() override;
|
||||
std::string get_name() override;
|
||||
int run(std::vector<std::string> realArgs, int argc, char **argv) override;
|
||||
};
|
667
dbus.cpp
667
dbus.cpp
|
@ -1,173 +1,518 @@
|
|||
#include "dbus.hpp"
|
||||
#include "log.hpp"
|
||||
const char *DBusAPI::serviceName = "com.experimentalcraft.looper";
|
||||
const char *DBusAPI::objectPath = "/com/experimentalcraft/looper";
|
||||
const char *DBusAPI::interfaceName = "com.experimentalcraft.Looper";
|
||||
DBusAPI::DBusAPI(Playback *playback) {
|
||||
this->playback = playback;
|
||||
connection = sdbus::createSessionBusConnection(serviceName);
|
||||
api = sdbus::createObject(*connection, objectPath);
|
||||
api->registerMethod(interfaceName, "open", "s", "", [this](sdbus::MethodCall call) {
|
||||
std::string filePath;
|
||||
call >> filePath;
|
||||
this->playback->Start(filePath);
|
||||
});
|
||||
api->registerSignal(interfaceName, "playbackEngineStarted", "");
|
||||
api->registerSignal(interfaceName, "speedChanged", "d");
|
||||
api->registerSignal(interfaceName, "tempoChanged", "d");
|
||||
api->registerSignal(interfaceName, "pitchChanged", "d");
|
||||
api->registerSignal(interfaceName, "pauseChanged", "b");
|
||||
api->registerSignal(interfaceName, "stopped", "");
|
||||
api->registerSignal(interfaceName, "errorOccurred", "s");
|
||||
api->registerSignal(interfaceName, "seeked", "d");
|
||||
api->registerSignal(interfaceName, "fileChanged", "s");
|
||||
api->registerProperty(interfaceName, "speed", "d", [this](sdbus::PropertyGetReply reply) {
|
||||
reply << this->playback->GetSpeed();
|
||||
}, [this](sdbus::PropertySetCall set_call) {
|
||||
double newValue;
|
||||
set_call >> newValue;
|
||||
this->playback->SetSpeed(newValue);
|
||||
});
|
||||
api->registerProperty(interfaceName, "tempo", "d", [this](sdbus::PropertyGetReply reply) {
|
||||
reply << this->playback->GetTempo();
|
||||
}, [this](sdbus::PropertySetCall set_call) {
|
||||
double newValue;
|
||||
set_call >> newValue;
|
||||
this->playback->SetTempo(newValue);
|
||||
});
|
||||
api->registerProperty(interfaceName, "pitch", "d", [this](sdbus::PropertyGetReply reply) {
|
||||
reply << this->playback->GetPitch();
|
||||
}, [this](sdbus::PropertySetCall set_call) {
|
||||
double newValue;
|
||||
set_call >> newValue;
|
||||
this->playback->SetPitch(newValue);
|
||||
});
|
||||
api->registerProperty(interfaceName, "volume", "d", [this](sdbus::PropertyGetReply reply) {
|
||||
reply << this->playback->GetVolume();
|
||||
}, [this](sdbus::PropertySetCall set_call) {
|
||||
double newValue;
|
||||
set_call >> newValue;
|
||||
this->playback->SetVolume(newValue);
|
||||
});
|
||||
api->registerMethod(interfaceName, "stop", "", "", [this](sdbus::MethodCall call) {
|
||||
this->playback->Stop();
|
||||
});
|
||||
api->registerMethod(interfaceName, "togglePause", "", "", [this](sdbus::MethodCall call) {
|
||||
this->playback->Pause();
|
||||
});
|
||||
api->registerProperty(interfaceName, "paused", "b", [this](sdbus::PropertyGetReply reply) {
|
||||
reply << this->playback->IsPaused();
|
||||
}, [this](sdbus::PropertySetCall call) {
|
||||
bool shouldBePaused;
|
||||
call >> shouldBePaused;
|
||||
this->playback->SetPaused(shouldBePaused);
|
||||
});
|
||||
api->registerProperty(interfaceName, "stopped", "b", [this](sdbus::PropertyGetReply reply) {
|
||||
reply << this->playback->IsStopped();
|
||||
});
|
||||
api->registerMethod(interfaceName, "errorExists", "", "b", [this](sdbus::MethodCall call) {
|
||||
auto reply = call.createReply();
|
||||
reply << this->playback->ErrorExists();
|
||||
});
|
||||
api->registerMethod(interfaceName, "getLastError", "", "s", [this](sdbus::MethodCall call) {
|
||||
auto error = this->playback->GetError();
|
||||
if (error.has_value()) {
|
||||
auto reply = call.createReply();
|
||||
reply << error.value();
|
||||
} else {
|
||||
auto errorReply = call.createErrorReply(sdbus::createError(0, "No error found."));
|
||||
#include "backend.hpp"
|
||||
#include <random>
|
||||
MprisAPI::MprisAPI(sdbus::IConnection &connection, std::string objectPath, DBusAPI *dbus_api)
|
||||
: AdaptorInterfaces(connection, std::move(objectPath))
|
||||
, dbus_api(dbus_api)
|
||||
, connection(connection)
|
||||
{
|
||||
registerAdaptor();
|
||||
connection.enterEventLoopAsync();
|
||||
}
|
||||
void MprisAPI::Quit() {
|
||||
dbus_api->Quit();
|
||||
}
|
||||
void MprisAPI::Pause() {
|
||||
dbus_api->Paused(true);
|
||||
}
|
||||
void MprisAPI::PlayPause() {
|
||||
dbus_api->TogglePause();
|
||||
}
|
||||
void MprisAPI::Stop() {
|
||||
dbus_api->Stop();
|
||||
}
|
||||
void MprisAPI::Play() {
|
||||
dbus_api->Paused(false);
|
||||
}
|
||||
void MprisAPI::Seek(const int64_t &offset) {
|
||||
double value = offset;
|
||||
value /= 1000000;
|
||||
dbus_api->Position(value);
|
||||
}
|
||||
void MprisAPI::SetPosition(const sdbus::ObjectPath &TrackId, const int64_t &offset) {
|
||||
Seek(offset);
|
||||
}
|
||||
void MprisAPI::OpenUri(const std::string &Uri) {
|
||||
dbus_api->StartByURI(Uri);
|
||||
}
|
||||
std::string MprisAPI::PlaybackStatus() {
|
||||
if (dbus_api->IsStopped()) {
|
||||
return "Stopped";
|
||||
} else {
|
||||
return dbus_api->Paused() ? "Paused" : "Playing";
|
||||
}
|
||||
}
|
||||
double MprisAPI::Rate() {
|
||||
return dbus_api->Speed() * dbus_api->Tempo();
|
||||
}
|
||||
void MprisAPI::Rate(const double &value) {
|
||||
dbus_api->Speed(value / dbus_api->Tempo());
|
||||
}
|
||||
std::map<std::string, sdbus::Variant> MprisAPI::Metadata() {
|
||||
std::map<std::string, sdbus::Variant> output;
|
||||
if (!dbus_api->IsStopped()) {
|
||||
output["mpris:length"] = (int64_t)(dbus_api->Length() * 1000000);
|
||||
output["mpris:trackid"] = playing_track_id;
|
||||
output["xesam:title"] = dbus_api->FileTitle();
|
||||
output["xesam:url"] = dbus_api->FilePath();
|
||||
} else {
|
||||
output["mpris:trackid"] = empty_track_id;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
double MprisAPI::Volume() {
|
||||
return dbus_api->Volume();
|
||||
}
|
||||
void MprisAPI::Volume(const double &value) {
|
||||
dbus_api->Volume(value);
|
||||
}
|
||||
int64_t MprisAPI::Position() {
|
||||
return dbus_api->Position() * 1000000;
|
||||
}
|
||||
bool MprisAPI::CanPlay() {
|
||||
return !dbus_api->IsStopped();
|
||||
}
|
||||
bool MprisAPI::CanPause() {
|
||||
return !dbus_api->IsStopped();
|
||||
}
|
||||
bool MprisAPI::CanSeek() {
|
||||
return !dbus_api->IsStopped() && dbus_api->Length() > 0;
|
||||
}
|
||||
|
||||
std::vector<meta_t> MprisAPI::GetTracksMetadata(const std::vector<track_id_t> &TrackIds) {
|
||||
std::vector<meta_t> output;
|
||||
if (!dbus_api->IsStopped()) {
|
||||
output.push_back(Metadata());
|
||||
}
|
||||
return output;
|
||||
}
|
||||
void MprisAPI::AddTrack(const std::string &Uri, const track_id_t &AfterTrack, const bool &SetAsCurrent) {
|
||||
return;
|
||||
}
|
||||
void MprisAPI::RemoveTrack(const track_id_t &TrackId) {
|
||||
return;
|
||||
}
|
||||
void MprisAPI::GoTo(const track_id_t &TrackId) {
|
||||
return;
|
||||
}
|
||||
std::vector<track_id_t> MprisAPI::Tracks() {
|
||||
std::vector<track_id_t> output;
|
||||
if (dbus_api->IsStopped()) {
|
||||
return output;
|
||||
}
|
||||
output.push_back(playing_track_id);
|
||||
return output;
|
||||
}
|
||||
bool MprisAPI::CanEditTracks() {
|
||||
return false;
|
||||
}
|
||||
MprisAPI::~MprisAPI() {
|
||||
unregisterAdaptor();
|
||||
}
|
||||
DBusAPI::DBusAPI(Playback *playback, sdbus::IConnection &connection, std::string objectPath, bool daemon)
|
||||
: AdaptorInterfaces(connection, std::move(objectPath))
|
||||
, daemon(daemon)
|
||||
, playback(playback)
|
||||
, connection(connection)
|
||||
{
|
||||
registerAdaptor();
|
||||
playback->register_handle(this);
|
||||
auto mprisConnection = sdbus::createSessionBusConnection("org.mpris.MediaPlayer2.Looper");
|
||||
auto &mprisConRef = *mprisConnection.release();
|
||||
mpris = new MprisAPI(mprisConRef, "/org/mpris/MediaPlayer2", this);
|
||||
threadFunc = std::thread([this]() {
|
||||
while (!threadExitFlag.load()) {
|
||||
Update();
|
||||
std::this_thread::yield();
|
||||
}
|
||||
});
|
||||
api->registerProperty(interfaceName, "position", "d", [this](sdbus::PropertyGetReply reply) {
|
||||
reply << this->playback->GetPosition();
|
||||
}, [this](sdbus::PropertySetCall call) {
|
||||
double value;
|
||||
call >> value;
|
||||
this->playback->Seek(value);
|
||||
});
|
||||
api->registerProperty(interfaceName, "length", "d", [this](sdbus::PropertyGetReply reply) {
|
||||
reply << this->playback->GetLength();
|
||||
});
|
||||
api->registerProperty(interfaceName, "current_file_path", "s", [this](sdbus::PropertyGetReply reply) {
|
||||
auto file_maybe = this->playback->get_current_file();
|
||||
if (file_maybe.has_value()) {
|
||||
reply << file_maybe.value();
|
||||
} else {
|
||||
reply << "";
|
||||
connection.enterEventLoopAsync();
|
||||
}
|
||||
const char *DBusAPI::objectPath = "/com/complecwaft/Looper";
|
||||
const char *DBusAPI::busName = "com.complecwaft.Looper";
|
||||
DBusAPI *DBusAPI::Create(Playback *playback, bool daemon) {
|
||||
auto connection = sdbus::createSessionBusConnection(busName);
|
||||
auto &con_ref = *connection.release();
|
||||
return new DBusAPI(playback, con_ref, objectPath, daemon);
|
||||
}
|
||||
double DBusAPI::Position() {
|
||||
return playback->GetPosition();
|
||||
}
|
||||
void DBusAPI::Position(const double &value) {
|
||||
playback->Seek(value);
|
||||
}
|
||||
double DBusAPI::Length() {
|
||||
return playback->GetLength();
|
||||
}
|
||||
std::string DBusAPI::FilePath() {
|
||||
return playback->get_current_file().value_or("");
|
||||
}
|
||||
std::string DBusAPI::FileTitle() {
|
||||
return playback->get_current_title().value_or("");
|
||||
}
|
||||
void DBusAPI::Update() {
|
||||
uint16_t signals = playback->handle_signals(PlaybackSignalErrorOccurred|PlaybackSignalFileChanged|PlaybackSignalPaused|PlaybackSignalPitchChanged|PlaybackSignalResumed|PlaybackSignalSeeked|PlaybackSignalSpeedChanged|PlaybackSignalStarted|PlaybackSignalStopped|PlaybackSignalTempoChanged, this);
|
||||
if (signals & PlaybackSignalErrorOccurred) {
|
||||
auto error = playback->GetError(this).value_or("Unknown error.");
|
||||
emitErrorOccurred(error, error);
|
||||
mpris->sendPropertiesChanged(mpris->playerInterface, {"PlaybackStatus", "CanSeek", "CanPlay", "CanPause", "Metadata"});
|
||||
}
|
||||
if (signals & PlaybackSignalFileChanged) {
|
||||
emitFileChanged(playback->get_current_file().value_or(""), playback->get_current_title().value_or(""));
|
||||
mpris->emitTrackMetadataChanged(mpris->playing_track_id, mpris->Metadata());
|
||||
mpris->sendPropertiesChanged(mpris->playerInterface, {"Position", "Metadata"});
|
||||
}
|
||||
if (signals & (PlaybackSignalPaused|PlaybackSignalResumed)) {
|
||||
emitPauseChanged(playback->IsPaused());
|
||||
mpris->sendPropertiesChanged(mpris->playerInterface, {"PlaybackStatus"});
|
||||
}
|
||||
if (signals & PlaybackSignalStopped) {
|
||||
emitStopped();
|
||||
emitPauseChanged(playback->IsPaused());
|
||||
mpris->emitTrackRemoved(mpris->playing_track_id);
|
||||
mpris->sendPropertiesChanged(mpris->playerInterface, {"PlaybackStatus", "CanSeek", "CanPlay", "CanPause", "Metadata"});
|
||||
}
|
||||
if (signals & PlaybackSignalPitchChanged) {
|
||||
emitPitchChanged(playback->GetPitch());
|
||||
}
|
||||
if (signals & PlaybackSignalTempoChanged) {
|
||||
emitTempoChanged(playback->GetTempo());
|
||||
mpris->sendPropertiesChanged(mpris->playerInterface, {"Rate"});
|
||||
}
|
||||
if (signals & PlaybackSignalSpeedChanged) {
|
||||
emitSpeedChanged(playback->GetSpeed());
|
||||
mpris->sendPropertiesChanged(mpris->playerInterface, {"Rate"});
|
||||
}
|
||||
if (signals & PlaybackSignalSeeked) {
|
||||
emitSeeked(playback->GetPosition());
|
||||
mpris->emitSeeked(playback->GetPosition() * 1000000);
|
||||
mpris->sendPropertiesChanged(mpris->playerInterface, {"Position"});
|
||||
}
|
||||
if (signals & PlaybackSignalStarted) {
|
||||
emitPlaybackEngineStarted();
|
||||
mpris->emitTrackAdded(mpris->Metadata(), mpris->empty_track_id);
|
||||
mpris->sendPropertiesChanged(mpris->playerInterface, {"CanSeek", "CanPlay", "CanPause", "Position", "Metadata", "PlaybackStatus"});
|
||||
}
|
||||
}
|
||||
void DBusAPI::Activate(const std::map<std::string, sdbus::Variant> &platform_data) {
|
||||
// TODO: Raise window?
|
||||
}
|
||||
void DBusAPI::Open(const std::vector<std::string>& uris, const std::map<std::string, sdbus::Variant> &platform_data) {
|
||||
if (uris.size() > 0) {
|
||||
StartByURI(uris[0]);
|
||||
}
|
||||
}
|
||||
void DBusAPI::ActivateAction(const std::string &action_name, const std::vector<sdbus::Variant>& parameter, const std::map<std::string, sdbus::Variant> &platform_data) {
|
||||
// Nothing yet.
|
||||
}
|
||||
void DBusAPI::Start(const std::string &path) {
|
||||
playback->Start(path);
|
||||
}
|
||||
void DBusAPI::StartByURI(const std::string &uri) {
|
||||
const static std::string file_prefix = "file:";
|
||||
std::string tmp = uri;
|
||||
if (tmp.starts_with(file_prefix)) {
|
||||
tmp = tmp.substr(file_prefix.length());
|
||||
Start(tmp);
|
||||
}
|
||||
}
|
||||
void DBusAPI::Stop() {
|
||||
playback->Stop();
|
||||
}
|
||||
void DBusAPI::TogglePause() {
|
||||
playback->Pause();
|
||||
}
|
||||
double DBusAPI::Speed() {
|
||||
return playback->GetSpeed();
|
||||
}
|
||||
void DBusAPI::Speed(const double &value) {
|
||||
playback->SetSpeed(value);
|
||||
}
|
||||
double DBusAPI::Tempo() {
|
||||
return playback->GetTempo();
|
||||
}
|
||||
void DBusAPI::Tempo(const double &value) {
|
||||
playback->SetTempo(value);
|
||||
}
|
||||
double DBusAPI::Pitch() {
|
||||
return playback->GetPitch();
|
||||
}
|
||||
void DBusAPI::Pitch(const double &value) {
|
||||
playback->SetPitch(value);
|
||||
}
|
||||
double DBusAPI::Volume() {
|
||||
return playback->GetVolume();
|
||||
}
|
||||
void DBusAPI::Volume(const double &value) {
|
||||
playback->SetVolume(value);
|
||||
}
|
||||
bool DBusAPI::Paused() {
|
||||
return playback->IsPaused();
|
||||
}
|
||||
void DBusAPI::Paused(const bool &value) {
|
||||
if (value != playback->IsPaused()) {
|
||||
playback->Pause();
|
||||
}
|
||||
}
|
||||
bool DBusAPI::IsStopped() {
|
||||
return playback->IsStopped();
|
||||
}
|
||||
bool DBusAPI::IsDaemon() {
|
||||
return daemon;
|
||||
}
|
||||
std::string DBusAPI::CreateHandle() {
|
||||
size_t idx = handle_idx++;
|
||||
size_t hash = std::hash<size_t>()(idx);
|
||||
size_t rand = (size_t)rand_engine();
|
||||
size_t rand_hash = std::hash<size_t>()(rand);
|
||||
std::string value = std::format("{}:{}:{}", idx, hash, rand_hash);
|
||||
handles[value] = (void*)&value;
|
||||
playback->register_handle(handles[value]);
|
||||
return value;
|
||||
}
|
||||
void DBusAPI::ClearHandle(const std::string &handle) {
|
||||
if (handles.contains(handle)) {
|
||||
playback->unregister_handle(handles[handle]);
|
||||
handles.erase(handle);
|
||||
}
|
||||
}
|
||||
std::deque<std::string> *DBusAPI::get_errors_by_handle(const std::string &handle) {
|
||||
if (handles.contains(handle)) {
|
||||
auto *v_handle = handles[handle];
|
||||
if (playback->errors_occurred.contains(v_handle)) {
|
||||
return &playback->errors_occurred[v_handle];
|
||||
}
|
||||
});
|
||||
api->registerProperty(interfaceName, "current_file_title", "s", [this](sdbus::PropertyGetReply reply) {
|
||||
auto title_maybe = this->playback->get_current_title();
|
||||
if (title_maybe.has_value()) {
|
||||
reply << title_maybe.value();
|
||||
} else {
|
||||
reply << "";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
std::string DBusAPI::PopFront(const std::string &handle) {
|
||||
auto *errors_list = get_errors_by_handle(handle);
|
||||
if (errors_list != nullptr) {
|
||||
if (errors_list->empty()) {
|
||||
return "";
|
||||
}
|
||||
});
|
||||
api->finishRegistration();
|
||||
connection->enterEventLoopAsync();
|
||||
std::string output = errors_list->front();
|
||||
errors_list->pop_front();
|
||||
return output;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
std::string DBusAPI::PopBack(const std::string &handle) {
|
||||
auto *errors_list = get_errors_by_handle(handle);
|
||||
if (errors_list != nullptr) {
|
||||
if (errors_list->empty()) {
|
||||
return "";
|
||||
}
|
||||
std::string output = errors_list->back();
|
||||
errors_list->pop_back();
|
||||
return output;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
std::string DBusAPI::PeekFront(const std::string &handle) {
|
||||
auto *errors_list = get_errors_by_handle(handle);
|
||||
if (errors_list != nullptr) {
|
||||
if (errors_list->empty()) {
|
||||
return "";
|
||||
}
|
||||
std::string output = errors_list->front();
|
||||
return output;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
void DBusAPI::Quit() {
|
||||
UIBackend::running_ui_backend->QuitHandler();
|
||||
}
|
||||
std::string DBusAPI::PeekBack(const std::string &handle) {
|
||||
auto *errors_list = get_errors_by_handle(handle);
|
||||
if (errors_list != nullptr) {
|
||||
if (errors_list->empty()) {
|
||||
return "";
|
||||
}
|
||||
std::string output = errors_list->back();
|
||||
return output;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
uint32_t DBusAPI::GetCount(const std::string &handle) {
|
||||
auto *errors_list = get_errors_by_handle(handle);
|
||||
if (errors_list != nullptr) {
|
||||
return errors_list->size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
bool DBusAPI::IsEmpty(const std::string& handle) {
|
||||
return GetCount(handle) == 0;
|
||||
}
|
||||
void DBusAPI::Clear(const std::string &handle) {
|
||||
auto *errors_list = get_errors_by_handle(handle);
|
||||
if (errors_list != nullptr) {
|
||||
errors_list->clear();
|
||||
}
|
||||
}
|
||||
std::vector<std::string> DBusAPI::PeekAll(const std::string &handle) {
|
||||
auto *errors_list = get_errors_by_handle(handle);
|
||||
if (errors_list != nullptr) {
|
||||
std::vector<std::string> output;
|
||||
for (auto error : (*errors_list)) {
|
||||
output.push_back(error);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
std::vector<std::string> DBusAPI::GetAllAndClear(const std::string &handle) {
|
||||
auto output = PeekAll(handle);
|
||||
Clear(handle);
|
||||
return output;
|
||||
}
|
||||
DBusAPI::~DBusAPI() {
|
||||
threadExitFlag.store(true);
|
||||
threadFunc.join();
|
||||
unregisterAdaptor();
|
||||
}
|
||||
bool DBusAPISender::isOnlyInstance() {
|
||||
return !connected;
|
||||
}
|
||||
void DBusAPISender::playFile(std::string file) {
|
||||
if (connected) {
|
||||
auto method = proxy->createMethodCall(DBusAPI::interfaceName, "open");
|
||||
method.dontExpectReply();
|
||||
method << file;
|
||||
proxy->callMethod(method);
|
||||
}
|
||||
}
|
||||
void DBusAPISender::setPitch(double value) {
|
||||
if (connected) {
|
||||
proxy->setProperty("pitch").onInterface(DBusAPI::interfaceName).toValue(value);
|
||||
}
|
||||
}
|
||||
void DBusAPISender::setSpeed(double value) {
|
||||
if (connected) {
|
||||
proxy->setProperty("speed").onInterface(DBusAPI::interfaceName).toValue(value);
|
||||
}
|
||||
}
|
||||
void DBusAPISender::setTempo(double value) {
|
||||
if (connected) {
|
||||
proxy->setProperty("tempo").onInterface(DBusAPI::interfaceName).toValue(value);
|
||||
}
|
||||
}
|
||||
double DBusAPISender::getPitch() {
|
||||
if (connected) {
|
||||
return proxy->getProperty("pitch").onInterface(DBusAPI::interfaceName);
|
||||
} else {
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
double DBusAPISender::getTempo() {
|
||||
if (connected) {
|
||||
return proxy->getProperty("tempo").onInterface(DBusAPI::interfaceName);
|
||||
} else {
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
double DBusAPISender::getSpeed() {
|
||||
if (connected) {
|
||||
return proxy->getProperty("speed").onInterface(DBusAPI::interfaceName);
|
||||
} else {
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
DBusAPISender::DBusAPISender() {
|
||||
DEBUG.writeln("Checking and connecting to main instance...");
|
||||
bool output;
|
||||
try {
|
||||
connection = sdbus::createSessionBusConnection();
|
||||
proxy = sdbus::createProxy(*connection, DBusAPI::serviceName, DBusAPI::objectPath);
|
||||
|
||||
proxy->finishRegistration();
|
||||
{
|
||||
auto method = proxy->createMethodCall("org.freedesktop.DBus.Peer", "Ping");
|
||||
proxy->callMethod(method);
|
||||
}
|
||||
DEBUG.writeln("Main instance found.");
|
||||
auto *tmp = Create();
|
||||
output = tmp == nullptr;
|
||||
delete tmp;
|
||||
return output;
|
||||
} catch (sdbus::Error) {
|
||||
connected = false;
|
||||
DEBUG.writeln("Main instance not found.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> DBusAPISender::get_current_file() {
|
||||
if (IsStopped()) {
|
||||
return {};
|
||||
} else {
|
||||
return FilePath();
|
||||
}
|
||||
}
|
||||
std::optional<std::string> DBusAPISender::get_current_title() {
|
||||
if (IsStopped()) {
|
||||
return {};
|
||||
} else {
|
||||
return FileTitle();
|
||||
}
|
||||
}
|
||||
void DBusAPISender::onPlaybackEngineStarted() {
|
||||
set_signal(PlaybackSignalStarted);
|
||||
}
|
||||
void DBusAPISender::onFileChanged(const std::string &FilePath, const std::string &Title) {
|
||||
set_signal(PlaybackSignalFileChanged);
|
||||
}
|
||||
void DBusAPISender::onSpeedChanged(const double &new_speed) {
|
||||
set_signal(PlaybackSignalSpeedChanged);
|
||||
}
|
||||
void DBusAPISender::onTempoChanged(const double &new_tempo) {
|
||||
set_signal(PlaybackSignalTempoChanged);
|
||||
}
|
||||
void DBusAPISender::onPitchChanged(const double &new_pitch) {
|
||||
set_signal(PlaybackSignalPitchChanged);
|
||||
}
|
||||
void DBusAPISender::onPauseChanged(const bool &now_paused) {
|
||||
set_signal(now_paused ? PlaybackSignalPaused : PlaybackSignalResumed);
|
||||
}
|
||||
void DBusAPISender::onStopped() {
|
||||
set_signal(PlaybackSignalStopped);
|
||||
}
|
||||
void DBusAPISender::onErrorOccurred(const std::string &error_desc, const std::string &error_type) {
|
||||
set_signal(PlaybackSignalErrorOccurred);
|
||||
}
|
||||
void DBusAPISender::onSeeked(const double &to_position) {
|
||||
set_signal(PlaybackSignalSeeked);
|
||||
}
|
||||
double DBusAPISender::GetPosition() {
|
||||
return ProxyInterfaces::Position();
|
||||
}
|
||||
double DBusAPISender::GetLength() {
|
||||
return ProxyInterfaces::Length();
|
||||
}
|
||||
void DBusAPISender::Seek(double position) {
|
||||
ProxyInterfaces::Position(position);
|
||||
}
|
||||
void DBusAPISender::Start(std::string filePath) {
|
||||
ProxyInterfaces::Start(filePath);
|
||||
}
|
||||
bool DBusAPISender::IsPaused() {
|
||||
return ProxyInterfaces::Paused();
|
||||
}
|
||||
void DBusAPISender::Pause() {
|
||||
ProxyInterfaces::TogglePause();
|
||||
}
|
||||
void DBusAPISender::Stop() {
|
||||
ProxyInterfaces::Stop();
|
||||
}
|
||||
bool DBusAPISender::IsStopped() {
|
||||
return ProxyInterfaces::IsStopped();
|
||||
}
|
||||
void DBusAPISender::SetTempo(float tempo) {
|
||||
ProxyInterfaces::Tempo(tempo);
|
||||
}
|
||||
void DBusAPISender::SetPitch(float pitch) {
|
||||
ProxyInterfaces::Pitch(pitch);
|
||||
}
|
||||
void DBusAPISender::SetSpeed(float speed) {
|
||||
ProxyInterfaces::Speed(speed);
|
||||
}
|
||||
void DBusAPISender::SetVolume(float volume) {
|
||||
ProxyInterfaces::Volume(volume);
|
||||
}
|
||||
float DBusAPISender::GetTempo() {
|
||||
return (float)ProxyInterfaces::Tempo();
|
||||
}
|
||||
float DBusAPISender::GetPitch() {
|
||||
return (float)ProxyInterfaces::Pitch();
|
||||
}
|
||||
float DBusAPISender::GetSpeed() {
|
||||
return (float)ProxyInterfaces::Speed();
|
||||
}
|
||||
float DBusAPISender::GetVolume() {
|
||||
return (float)ProxyInterfaces::Volume();
|
||||
}
|
||||
void DBusAPISender::Update() {
|
||||
|
||||
}
|
||||
DBusAPISender *DBusAPISender::Create() {
|
||||
try {
|
||||
auto connection = sdbus::createSessionBusConnection();
|
||||
auto &con_ref = *connection.release();
|
||||
auto *output = new DBusAPISender(con_ref, DBusAPI::busName, DBusAPI::objectPath);
|
||||
return output;
|
||||
} catch (sdbus::Error error) {
|
||||
DEBUG.writefln("sdbus::Error: %s: %s", error.getName().c_str(), error.getMessage().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
DBusAPISender::DBusAPISender(sdbus::IConnection &connection, std::string busName, std::string objectPath)
|
||||
: ProxyInterfaces(connection, std::move(busName), std::move(objectPath)) {
|
||||
registerProxy();
|
||||
DEBUG.writeln("Pinging DBus API to check for its existance.");
|
||||
Ping();
|
||||
DEBUG.writeln("Requesting handle...");
|
||||
handle = CreateHandle();
|
||||
DEBUG.writeln("Preparing updates for UI frontend...");
|
||||
if (IsStopped()) {
|
||||
set_signal(PlaybackSignalStopped);
|
||||
} else {
|
||||
filePath = ProxyInterfaces::FilePath();
|
||||
title = ProxyInterfaces::FileTitle();
|
||||
length = ProxyInterfaces::Length();
|
||||
set_signal(PlaybackSignalStarted|PlaybackSignalSeeked|PlaybackSignalFileChanged);
|
||||
}
|
||||
if (IsPaused()) {
|
||||
set_signal(PlaybackSignalPaused);
|
||||
} else {
|
||||
set_signal(PlaybackSignalResumed);
|
||||
}
|
||||
set_signal(PlaybackSignalSpeedChanged|PlaybackSignalPitchChanged|PlaybackSignalTempoChanged);
|
||||
connection.enterEventLoopAsync();
|
||||
}
|
||||
DBusAPISender::~DBusAPISender() {
|
||||
DEBUG.writeln("Requesting handle deletion...");
|
||||
ClearHandle(handle);
|
||||
unregisterProxy();
|
||||
}
|
248
dbus.hpp
248
dbus.hpp
|
@ -1,31 +1,231 @@
|
|||
#pragma once
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <sdbus-c++/StandardInterfaces.h>
|
||||
#include "assets/dbus_stub_adaptor.hpp"
|
||||
#include "assets/dbus_stub_proxy.hpp"
|
||||
#include "assets/mpris_stub_adaptor.hpp"
|
||||
#include "playback.h"
|
||||
class DBusAPI {
|
||||
friend class DBusAPISender;
|
||||
static const char *serviceName;
|
||||
static const char *objectPath;
|
||||
static const char *interfaceName;
|
||||
std::unique_ptr<sdbus::IConnection> connection;
|
||||
std::unique_ptr<sdbus::IObject> api;
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
class DBusAPI;
|
||||
class MprisAPI : public sdbus::AdaptorInterfaces<org::mpris::MediaPlayer2_adaptor, org::mpris::MediaPlayer2::Player_adaptor, org::mpris::MediaPlayer2::TrackList_adaptor, sdbus::Properties_adaptor> {
|
||||
friend class DBusAPI;
|
||||
DBusAPI *dbus_api;
|
||||
std::string curPlayingObjectPath;
|
||||
sdbus::IConnection &connection;
|
||||
const std::string mainInterface = "org.mpris.MediaPlayer2";
|
||||
const std::string playerInterface = "org.mpris.MediaPlayer2.Player";
|
||||
const std::string trackInterface = "org.mpris.MediaPlayer2.TrackList";
|
||||
inline void sendPropertiesChanged(const std::string interface, const std::initializer_list<std::string> properties) {
|
||||
std::vector<std::string> property_vec;
|
||||
for (auto property : properties) {
|
||||
property_vec.push_back(property);
|
||||
}
|
||||
emitPropertiesChangedSignal(interface, property_vec);
|
||||
}
|
||||
public:
|
||||
Playback *playback;
|
||||
DBusAPI(Playback *playback);
|
||||
#define meta_t std::map<std::string, sdbus::Variant>
|
||||
#define track_id_t sdbus::ObjectPath
|
||||
const sdbus::ObjectPath playing_track_id = "/com/complecwaft/Looper/PlayingTrack";
|
||||
const sdbus::ObjectPath empty_track_id = "/org/mpris/MediaPlayer2/TrackList/NoTrack";
|
||||
|
||||
inline void Raise() override { }
|
||||
void Quit() override;
|
||||
inline std::string Identity() override {
|
||||
return "Looper";
|
||||
}
|
||||
inline std::vector<std::string> SupportedUriSchemes() override {
|
||||
std::vector<std::string> output;
|
||||
output.push_back("file:");
|
||||
return output;
|
||||
}
|
||||
inline std::vector<std::string> SupportedMimeTypes() override {
|
||||
std::vector<std::string> output;
|
||||
output.push_back("audio/*");
|
||||
return output;
|
||||
}
|
||||
inline void Next() override { }
|
||||
inline void Previous() override { }
|
||||
void Pause() override;
|
||||
void PlayPause() override;
|
||||
void Stop() override;
|
||||
void Play() override;
|
||||
void Seek(const int64_t &offset) override;
|
||||
void SetPosition(const track_id_t &TrackId, const int64_t &Position) override;
|
||||
void OpenUri(const std::string &Uri) override;
|
||||
std::string PlaybackStatus() override;
|
||||
double Rate() override;
|
||||
void Rate(const double &value) override;
|
||||
meta_t Metadata() override;
|
||||
double Volume() override;
|
||||
void Volume(const double& value) override;
|
||||
int64_t Position() override;
|
||||
inline double MinimumRate() override {
|
||||
return 0.25;
|
||||
}
|
||||
inline double MaximumRate() override {
|
||||
return 4.0;
|
||||
}
|
||||
inline bool CanGoNext() override {
|
||||
return false;
|
||||
}
|
||||
inline bool CanGoPrevious() override {
|
||||
return false;
|
||||
}
|
||||
bool CanPlay() override;
|
||||
bool CanPause() override;
|
||||
bool CanSeek() override;
|
||||
inline bool CanControl() override {
|
||||
return true;
|
||||
}
|
||||
inline bool CanRaise() override {
|
||||
return false;
|
||||
}
|
||||
inline bool CanQuit() override {
|
||||
return true;
|
||||
}
|
||||
inline bool HasTrackList() override {
|
||||
return true;
|
||||
}
|
||||
std::vector<meta_t> GetTracksMetadata(const std::vector<track_id_t> &TrackIds) override;
|
||||
void AddTrack(const std::string &Uri, const track_id_t &AfterTrack, const bool &SetAsCurrent) override;
|
||||
void RemoveTrack(const track_id_t &TrackId) override;
|
||||
void GoTo(const track_id_t &TrackId) override;
|
||||
std::vector<track_id_t> Tracks() override;
|
||||
bool CanEditTracks() override;
|
||||
MprisAPI(sdbus::IConnection &connection, std::string objectPath, DBusAPI *dbus_api);
|
||||
~MprisAPI();
|
||||
};
|
||||
class DBusAPISender {
|
||||
std::unique_ptr<sdbus::IProxy> proxy;
|
||||
std::unique_ptr<sdbus::IConnection> connection;
|
||||
bool connected = true;
|
||||
class DBusAPI : public sdbus::AdaptorInterfaces<com::complecwaft::Looper_adaptor, com::complecwaft::Looper::Errors_adaptor, org::freedesktop::Application_adaptor> {
|
||||
std::map<std::string, void*> handles;
|
||||
size_t handle_idx = 0;
|
||||
MprisAPI *mpris;
|
||||
std::minstd_rand rand_engine;
|
||||
std::deque<std::string> *get_errors_by_handle(const std::string &handle);
|
||||
bool daemon;
|
||||
std::atomic_bool threadExitFlag = false;
|
||||
std::thread threadFunc;
|
||||
sdbus::IConnection &connection;
|
||||
public:
|
||||
void playFile(std::string file);
|
||||
void setPitch(double pitch);
|
||||
void setTempo(double tempo);
|
||||
void setSpeed(double speed);
|
||||
void setVolume(double volume);
|
||||
double getPitch();
|
||||
double getTempo();
|
||||
double getSpeed();
|
||||
double getVolume();
|
||||
bool isOnlyInstance();
|
||||
DBusAPISender();
|
||||
static const char *objectPath;
|
||||
static const char *busName;
|
||||
void Activate(const std::map<std::string, sdbus::Variant>& platform_data) override;
|
||||
void Open(const std::vector<std::string>& uris, const std::map<std::string, sdbus::Variant>& platform_data) override;
|
||||
void ActivateAction(const std::string& action_name, const std::vector<sdbus::Variant>& parameter, const std::map<std::string, sdbus::Variant>& platform_data) override;
|
||||
// com.experimentalcraft.Looper
|
||||
// Properties
|
||||
std::string FilePath() override;
|
||||
std::string FileTitle() override;
|
||||
double Speed() override;
|
||||
void Speed(const double &value) override;
|
||||
double Tempo() override;
|
||||
void Tempo(const double& value) override;
|
||||
double Pitch() override;
|
||||
void Pitch(const double& value) override;
|
||||
double Volume() override;
|
||||
void Volume(const double& value) override;
|
||||
double Position() override;
|
||||
void Position(const double &value) override;
|
||||
double Length() override;
|
||||
// Pausing and Resuming
|
||||
bool Paused() override;
|
||||
void Paused(const bool& value) override;
|
||||
void TogglePause() override;
|
||||
// Starting new Files
|
||||
void Start(const std::string& path) override;
|
||||
void StartByURI(const std::string& uri) override;
|
||||
// Playback stopping
|
||||
void Stop() override;
|
||||
bool IsStopped() override;
|
||||
// Handles
|
||||
std::string CreateHandle() override;
|
||||
void ClearHandle(const std::string& handle) override;
|
||||
// com.experimentalcraft.Looper.Errors
|
||||
// Popping from the queue
|
||||
std::string PopFront(const std::string &handle) override;
|
||||
std::string PopBack(const std::string &handle) override;
|
||||
// Peeking at the values
|
||||
std::string PeekFront(const std::string &handle) override;
|
||||
std::string PeekBack(const std::string &handle) override;
|
||||
// Getting the amount of unread errors.
|
||||
uint32_t GetCount(const std::string &handle) override;
|
||||
bool IsEmpty(const std::string& handle) override;
|
||||
// Getting and Clearing Errors.
|
||||
void Clear(const std::string &handle) override;
|
||||
std::vector<std::string> PeekAll(const std::string &handle) override;
|
||||
std::vector<std::string> GetAllAndClear(const std::string &handle) override;
|
||||
bool IsDaemon() override;
|
||||
void Quit() override;
|
||||
|
||||
// API
|
||||
Playback *playback;
|
||||
void Update();
|
||||
DBusAPI(Playback *playback, sdbus::IConnection &connection, std::string objectPath, bool daemon);
|
||||
~DBusAPI();
|
||||
static DBusAPI *Create(Playback *playback, bool daemon = false);
|
||||
};
|
||||
class DBusAPISender : public Playback, public sdbus::ProxyInterfaces<com::complecwaft::Looper_proxy, com::complecwaft::Looper::Errors_proxy, org::freedesktop::Application_proxy, sdbus::Peer_proxy> {
|
||||
// Cache
|
||||
double length, pitch, speed, tempo, volume;
|
||||
bool stopped, paused;
|
||||
std::string filePath;
|
||||
std::string title;
|
||||
std::mutex cacheMutex;
|
||||
optional<std::string> last_error;
|
||||
// Handle for error handling.
|
||||
std::string handle;
|
||||
|
||||
public:
|
||||
// Public API for creating this object, and checking if it is needed.
|
||||
/// @brief Checks if this is the only instance, by attempting creation and immediately deleting the created object.
|
||||
/// @returns true, if this is the only instance, false otherwise.
|
||||
static bool isOnlyInstance();
|
||||
/// @brief Creates a proxy playback engine, if possible.
|
||||
/// @returns A proxy to the main instance of the playback engine, or nullptr if there is none.
|
||||
static DBusAPISender *Create();
|
||||
|
||||
// Signals. Protected so that they aren't seen as a proper API
|
||||
protected:
|
||||
void onPlaybackEngineStarted() override;
|
||||
void onSpeedChanged(const double &new_speed) override;
|
||||
void onTempoChanged(const double &new_tempo) override;
|
||||
void onPitchChanged(const double &new_pitch) override;
|
||||
void onPauseChanged(const bool &now_paused) override;
|
||||
void onStopped() override;
|
||||
void onErrorOccurred(const std::string &error_desc, const std::string &error_type) override;
|
||||
void onSeeked(const double &to_position) override;
|
||||
void onFileChanged(const std::string &path, const std::string &title) override;
|
||||
|
||||
// Playback API. This is the API to be used by UI frontends.
|
||||
public:
|
||||
std::optional<std::string> get_current_file() override;
|
||||
std::optional<std::string> get_current_title() override;
|
||||
inline bool is_proxy() override {
|
||||
return true;
|
||||
}
|
||||
double GetPosition() override;
|
||||
double GetLength() override;
|
||||
void Seek(double position) override;
|
||||
void Start(std::string filePath) override;
|
||||
bool IsPaused() override;
|
||||
void Pause() override;
|
||||
void Stop() override;
|
||||
void Update();
|
||||
bool IsStopped() override;
|
||||
void SetTempo(float tempo) override;
|
||||
void SetPitch(float pitch) override;
|
||||
void SetSpeed(float speed) override;
|
||||
void SetVolume(float volume) override;
|
||||
float GetTempo() override;
|
||||
float GetPitch() override;
|
||||
float GetSpeed() override;
|
||||
float GetVolume() override;
|
||||
// Constructors and destructors.
|
||||
// The constructor is protected because there is a different API that should be used for creation.
|
||||
protected:
|
||||
DBusAPISender(sdbus::IConnection &connection, std::string busName, std::string objectPath);
|
||||
public:
|
||||
~DBusAPISender();
|
||||
};
|
56
main.cpp
56
main.cpp
|
@ -1,5 +1,7 @@
|
|||
#include "options.hpp"
|
||||
#include "backend.hpp"
|
||||
#include "daemon_backend.hpp"
|
||||
#include "proxy_backend.hpp"
|
||||
#include "log.hpp"
|
||||
#include "thirdparty/CLI11.hpp"
|
||||
#include "data.h"
|
||||
|
@ -23,8 +25,15 @@ int main(int argc, char **argv) {
|
|||
int log_level;
|
||||
std::string ui_backend_option = "";
|
||||
bool full_help = false;
|
||||
bool daemonize = false;
|
||||
bool disable_gui = false;
|
||||
bool quit = 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.");
|
||||
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.");
|
||||
try {
|
||||
app.parse(args);
|
||||
} catch (const CLI::ParseError &e) {
|
||||
|
@ -34,11 +43,25 @@ int main(int argc, char **argv) {
|
|||
exit(app.exit(e));
|
||||
}
|
||||
}
|
||||
if (daemonize) {
|
||||
ui_backend_option = "daemon";
|
||||
}
|
||||
args.clear();
|
||||
args = app.remaining(false);
|
||||
int new_argc = args.size();
|
||||
char **new_argv = (char**)malloc(new_argc * sizeof(char*));
|
||||
init_logging();
|
||||
|
||||
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;
|
||||
}
|
||||
{
|
||||
auto looper_mit = LicenseData("Looper (MIT)", "MIT");
|
||||
auto looper_gpl = LicenseData("Looper (GPL)", "GPL-3.0-or-later");
|
||||
|
@ -73,9 +96,26 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
DEBUG.writeln("Initializing frontends...");
|
||||
init_backends();
|
||||
DEBUG.writeln("Loaded frontends: ");
|
||||
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>();
|
||||
}
|
||||
for (auto kv : UIBackend::backends) {
|
||||
kv.second->add_licenses();
|
||||
}
|
||||
DEBUG.writeln("Loaded frontends: ");
|
||||
for (auto kv : UIBackend::backends) {
|
||||
DEBUG.writefln(" - '%s'", kv.first.c_str());
|
||||
}
|
||||
DEBUG.writeln("Loading options file...");
|
||||
|
@ -88,12 +128,26 @@ int main(int argc, char **argv) {
|
|||
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 {
|
||||
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>();
|
||||
}
|
||||
output = backend->run(args, new_argc, new_argv);
|
||||
if (quit && proxy_backend != nullptr) {
|
||||
proxy_backend->quitDaemon();
|
||||
proxy_backend->unregister_self();
|
||||
}
|
||||
} catch (int return_code) {
|
||||
if (full_help) {
|
||||
std::string helpstr = app.help();
|
||||
|
|
111
playback.cpp
111
playback.cpp
|
@ -10,6 +10,7 @@
|
|||
#endif
|
||||
#include "log.hpp"
|
||||
#include <filesystem>
|
||||
#include "dbus.hpp"
|
||||
using namespace std::chrono;
|
||||
size_t CalculateBufSize(SDL_AudioSpec *obtained, double seconds, double max_seconds, size_t samples_override = 0) {
|
||||
return ((((samples_override == 0) ? obtained->samples : samples_override) * std::min(seconds, max_seconds)) + 1) * sizeof(SAMPLETYPE) * obtained->channels;
|
||||
|
@ -69,9 +70,7 @@ Mix_Music *PlaybackInstance::Load(const char *file) {
|
|||
Mix_Music *output = Mix_LoadMUS(file);
|
||||
if (output == nullptr) {
|
||||
ERROR.writefln("Error loading music '%s': %s", file, Mix_GetError());
|
||||
error_mutex.lock();
|
||||
errors.emplace("Error loading music!");
|
||||
error_mutex.unlock();
|
||||
set_error("Error loading music!");
|
||||
return nullptr;
|
||||
}
|
||||
Mix_PlayMusicStream(output, -1);
|
||||
|
@ -129,9 +128,7 @@ void PlaybackInstance::ThreadFunc() {
|
|||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
ERROR.writefln("Error initializing SDL: '%s'", SDL_GetError());
|
||||
error_mutex.lock();
|
||||
errors.emplace("Failed to initialize SDL!");
|
||||
error_mutex.unlock();
|
||||
set_error("Failed to initialize SDL!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -152,9 +149,7 @@ void PlaybackInstance::ThreadFunc() {
|
|||
Mix_Init(MIX_INIT_FLAC|MIX_INIT_MID|MIX_INIT_MOD|MIX_INIT_MP3|MIX_INIT_OGG|MIX_INIT_OPUS|MIX_INIT_WAVPACK);
|
||||
if ((device = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE|SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE)) == 0) {
|
||||
ERROR.writefln("Error opening audio device: '%s'", SDL_GetError());
|
||||
error_mutex.lock();
|
||||
errors.emplace("Failed to open audio device!");
|
||||
error_mutex.unlock();
|
||||
set_error("Failed to open audio device!");
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
|
@ -171,9 +166,7 @@ void PlaybackInstance::ThreadFunc() {
|
|||
buf = (Uint8*)malloc(new_bufsize);
|
||||
if (buf == nullptr) {
|
||||
ERROR.writeln("Failed to allocate memory for playback!");
|
||||
error_mutex.lock();
|
||||
errors.emplace("Failed to allocate memory for playback!");
|
||||
error_mutex.unlock();
|
||||
set_error("Failed to allocate memory for playback!");
|
||||
set_signal(PlaybackSignalErrorOccurred);
|
||||
running = false;
|
||||
break;
|
||||
|
@ -234,9 +227,7 @@ void PlaybackInstance::ThreadFunc() {
|
|||
buf = (Uint8*)realloc((void*)buf, correct_buf_size);
|
||||
if (buf == nullptr) {
|
||||
ERROR.writes("Failed to allocate memory for playback!");
|
||||
error_mutex.lock();
|
||||
errors.emplace("Failed to allocate memory for playback!");
|
||||
error_mutex.unlock();
|
||||
set_error("Failed to allocate memory for playback!");
|
||||
set_signal(PlaybackSignalErrorOccurred);
|
||||
running = false;
|
||||
break;
|
||||
|
@ -359,38 +350,55 @@ void PlaybackInstance::Update() {
|
|||
bool PlaybackInstance::IsStopped() {
|
||||
return !running;
|
||||
}
|
||||
optional<std::string> PlaybackInstance::GetError() {
|
||||
if (ErrorExists()) {
|
||||
error_mutex.lock();
|
||||
std::string error = errors.back();
|
||||
errors.pop();
|
||||
error_mutex.unlock();
|
||||
return error;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
bool PlaybackInstance::ErrorExists() {
|
||||
error_mutex.lock();
|
||||
bool output = !errors.empty();
|
||||
error_mutex.unlock();
|
||||
return output;
|
||||
}
|
||||
void PlaybackInstance::set_signal(uint16_t signal) {
|
||||
void Playback::set_signal(uint16_t signal) {
|
||||
signal_mutex.lock();
|
||||
signals_occurred |= signal;
|
||||
for (auto &kv : signals_occurred) {
|
||||
kv.second |= signal;
|
||||
}
|
||||
signal_mutex.unlock();
|
||||
}
|
||||
uint16_t PlaybackInstance::handle_signals(uint16_t signals) {
|
||||
uint16_t Playback::handle_signals(uint16_t signals, void *handle) {
|
||||
if (signal_mutex.try_lock()) {
|
||||
uint16_t output = signals_occurred & signals;
|
||||
signals_occurred &= ~output;
|
||||
if (!signals_occurred.contains(handle)) {
|
||||
signals_occurred[handle] = PlaybackSignalNone;
|
||||
}
|
||||
uint16_t output = signals_occurred[handle];
|
||||
signals_occurred[handle] &= ~output;
|
||||
signal_mutex.unlock();
|
||||
return output;
|
||||
} else {
|
||||
return PlaybackSignalNone;
|
||||
}
|
||||
}
|
||||
void Playback::register_handle(void *handle) {
|
||||
signal_mutex.lock();
|
||||
if (!signals_occurred.contains(handle)) {
|
||||
signals_occurred[handle] = PlaybackSignalNone;
|
||||
}
|
||||
signal_mutex.unlock();
|
||||
error_mutex.lock();
|
||||
if (!errors_occurred.contains(handle)) {
|
||||
std::deque<std::string> new_value;
|
||||
for (size_t i = 0; i < errors.size(); i++) {
|
||||
new_value.push_back(errors[i]);
|
||||
}
|
||||
errors_occurred[handle] = new_value;
|
||||
}
|
||||
error_mutex.unlock();
|
||||
}
|
||||
|
||||
void Playback::unregister_handle(void *handle) {
|
||||
signal_mutex.lock();
|
||||
if (signals_occurred.contains(handle)) {
|
||||
signals_occurred.erase(handle);
|
||||
}
|
||||
signal_mutex.unlock();
|
||||
error_mutex.lock();
|
||||
if (errors_occurred.contains(handle)) {
|
||||
errors_occurred.erase(handle);
|
||||
}
|
||||
error_mutex.unlock();
|
||||
}
|
||||
void PlaybackInstance::SetTempo(float tempo) {
|
||||
this->tempo = tempo;
|
||||
Update();
|
||||
|
@ -418,4 +426,33 @@ float PlaybackInstance::GetSpeed() {
|
|||
}
|
||||
float PlaybackInstance::GetVolume() {
|
||||
return volume;
|
||||
}
|
||||
void Playback::set_error(std::string desc) {
|
||||
error_mutex.lock();
|
||||
errors.push_back(desc);
|
||||
for (auto &kv : errors_occurred) {
|
||||
kv.second.push_front(desc);
|
||||
}
|
||||
error_mutex.unlock();
|
||||
set_signal(PlaybackSignalErrorOccurred);
|
||||
}
|
||||
Playback *Playback::Create(bool *daemon_found, bool daemon) {
|
||||
auto *dbus_proxy = DBusAPISender::Create();
|
||||
if (dbus_proxy != nullptr) {
|
||||
if (daemon_found != nullptr) {
|
||||
*daemon_found = dbus_proxy->IsDaemon();
|
||||
}
|
||||
DEBUG.writefln("DBus proxy daemon found: %s", *daemon_found ? "true" : "false");
|
||||
if (daemon) {
|
||||
delete dbus_proxy;
|
||||
return nullptr;
|
||||
} else {
|
||||
return dbus_proxy;
|
||||
}
|
||||
}
|
||||
if (daemon_found != nullptr) {
|
||||
*daemon_found = false;
|
||||
}
|
||||
DEBUG.writeln("Creating new playback instance.");
|
||||
return new PlaybackInstance();
|
||||
}
|
135
playback.h
135
playback.h
|
@ -11,109 +11,166 @@
|
|||
#include <optional>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
using namespace soundtouch;
|
||||
using std::span;
|
||||
using std::optional;
|
||||
using std::vector;
|
||||
using std::queue;
|
||||
using std::deque;
|
||||
enum {
|
||||
/// @brief No signals have occurred.
|
||||
PlaybackSignalNone = 0,
|
||||
/// @brief The file was changed. Recheck the properties of the file because they are likely different.
|
||||
PlaybackSignalFileChanged = 1 << 0,
|
||||
/// @brief The speed was changed.
|
||||
PlaybackSignalSpeedChanged = 1 << 1,
|
||||
/// @brief The speed was changed.
|
||||
PlaybackSignalTempoChanged = 1 << 2,
|
||||
/// @brief The speed was changed.
|
||||
PlaybackSignalPitchChanged = 1 << 3,
|
||||
/// @brief Playback was paused. If @ref PlaybackSignalResumed has also been sent, you must use @ref Playback::IsPaused to check if playback was paused or resumed.
|
||||
PlaybackSignalPaused = 1 << 4,
|
||||
/// @brief Playback was resumed. If @ref PlaybackSignalPaused has also been sent, you must use @ref Playback::IsPaused to check if playback was paused or resumed.
|
||||
PlaybackSignalResumed = 1 << 5,
|
||||
/// @brief Playback was stopped entirely. If @ref PlaybackSignalStarted has also been signalled, call @ref Playback::IsStopped to find out if playback is currently playing.
|
||||
PlaybackSignalStopped = 1 << 6,
|
||||
/// @brief An error occurred and playback has likely (but not necessarily) stopped. Call @ref Playback::GetError for details.
|
||||
PlaybackSignalErrorOccurred = 1 << 7,
|
||||
/// @brief Playback was seeked by the @ref Playback::Seek function
|
||||
PlaybackSignalSeeked = 1 << 8,
|
||||
/// @brief Playback has started. If @ref PlaybackSignalStopped has also been signalled, call @ref Playback::IsStopped to find out if playback is currently playing.
|
||||
PlaybackSignalStarted = 1 << 9
|
||||
};
|
||||
/// @brief Playback handler base class.
|
||||
class Playback {
|
||||
protected:
|
||||
friend class DBusAPI;
|
||||
/// @brief The handle-specific signal state.
|
||||
std::map<void*, uint16_t> signals_occurred;
|
||||
/// @brief The global error state for initializing handle-specific state.
|
||||
std::vector<std::string> errors;
|
||||
/// @brief The handle-specific error state.
|
||||
std::map<void*, std::deque<std::string>> errors_occurred;
|
||||
/// @brief The mutex for signals
|
||||
std::mutex signal_mutex;
|
||||
/// @brief The mutex for errors
|
||||
std::mutex error_mutex;
|
||||
/// @brief Signals other code with the specified signal(s). Use with PlaybackSignal* enum variants.
|
||||
void set_signal(uint16_t signal);
|
||||
/// @brief Signals an error, and pushes details to other code.
|
||||
void set_error(std::string desc);
|
||||
|
||||
public:
|
||||
inline virtual bool is_proxy() {
|
||||
return false;
|
||||
}
|
||||
inline Playback() {};
|
||||
inline virtual ~Playback() {}
|
||||
/// @brief Gets the current file, if any.
|
||||
/// @returns No value if there is no file playing, or the path to the file if there is a file playing.
|
||||
inline virtual std::optional<std::string> get_current_file() {
|
||||
return {};
|
||||
}
|
||||
/// @brief Gets the current file's title, if any, for display to the user.
|
||||
/// @returns No value if there is no file playing. If there is a file playing, a string is returned. The contents of the string depend on whether or not the file has a title tag. If so, the string contains the contents of that tag. Otherwise, the string contains the stem of the file's path.
|
||||
inline virtual std::optional<std::string> get_current_title() {
|
||||
return {};
|
||||
}
|
||||
/// @brief Gets the position of the playing file.
|
||||
inline virtual double GetPosition() {
|
||||
return 0.0;
|
||||
}
|
||||
/// @brief Gets the length of the file. If the length is less than or equal to zero, the file can be considered to have an unknown length.
|
||||
inline virtual double GetLength() {
|
||||
return 0.0;
|
||||
}
|
||||
/// @brief Sets the playback position into the current file.
|
||||
inline virtual void Seek(double position) {}
|
||||
/// @brief Plays a new file.
|
||||
inline virtual void Start(std::string filePath) {}
|
||||
/// @brief Checks whether or not the file is paused.
|
||||
/// @returns true if playback is paused, false otherwise
|
||||
inline virtual bool IsPaused() {
|
||||
return true;
|
||||
}
|
||||
/// @brief Toggles the pause state of the playback.
|
||||
inline virtual void Pause() {}
|
||||
/// @brief Stops all playback.
|
||||
inline virtual void Stop() {}
|
||||
/// @brief Checks whether or not playback is stopped.
|
||||
/// @returns true if playback is stopped, false otherwise.
|
||||
inline virtual bool IsStopped() {
|
||||
return true;
|
||||
}
|
||||
/// @brief Sets the tempo multiplier of the playback.
|
||||
inline virtual void SetTempo(float tempo) {}
|
||||
/// @brief Sets the speed multiplier (Both speed and pitch, but this is a separate property) of the playback.
|
||||
inline virtual void SetSpeed(float speed) {}
|
||||
/// @brief Sets the pitch multiplier of the playback
|
||||
inline virtual void SetPitch(float pitch) {}
|
||||
/// @brief Sets the volume of the playback, as a percentage
|
||||
inline virtual void SetVolume(float volume) {}
|
||||
/// @brief Gets the tempo multiplier of the playback.
|
||||
inline virtual float GetTempo() {
|
||||
return 1.0;
|
||||
}
|
||||
/// @brief Gets the speed multiplier (Both speed and pitch, but this is a separate property) of the playback.
|
||||
inline virtual float GetSpeed() {
|
||||
return 1.0;
|
||||
}
|
||||
/// @brief Gets the pitch multiplier of the playback.
|
||||
inline virtual float GetPitch() {
|
||||
return 1.0;
|
||||
}
|
||||
/// @brief Gets the volume of the playback, as a percentage
|
||||
inline virtual float GetVolume() {
|
||||
return 1.0;
|
||||
}
|
||||
inline virtual uint16_t handle_signals(uint16_t signal) {
|
||||
return 0;
|
||||
/// @brief Returns and clears signals, using a registered handle if specified.
|
||||
/// @param signal The signal(s) to check for and clear.
|
||||
/// @param handle The handle, which was specified to @ref Playback::register_handle , or nullptr.
|
||||
uint16_t handle_signals(uint16_t signal, void *handle = nullptr);
|
||||
/// @brief Registers a handle for usage in handle_signals and GetError.
|
||||
void register_handle(void *handle);
|
||||
/// @brief Unregisters a handle that is no longer needed.
|
||||
void unregister_handle(void *handle);
|
||||
/// @brief Gets the last error from the playback engine, or no value if there was none.
|
||||
/// @param handle The handle as registered with @ref Playback::register_handle
|
||||
inline virtual optional<std::string> GetError(void *handle) {
|
||||
std::optional<std::string> output = {};
|
||||
if (ErrorExists(handle)) {
|
||||
error_mutex.lock();
|
||||
if (errors_occurred.contains(handle)) {
|
||||
output = errors_occurred[handle].back();
|
||||
errors_occurred[handle].pop_back();
|
||||
}
|
||||
error_mutex.unlock();
|
||||
}
|
||||
return output;
|
||||
}
|
||||
inline virtual optional<std::string> GetError() {
|
||||
return {};
|
||||
}
|
||||
inline virtual bool ErrorExists() {
|
||||
return false;
|
||||
/// @brief Checks if an unread error has occurred.
|
||||
/// @param handle The handle as registered with @ref Playback::register_handle
|
||||
inline virtual bool ErrorExists(void *handle) {
|
||||
bool output = false;
|
||||
error_mutex.lock();
|
||||
if (errors_occurred.contains(handle)) {
|
||||
output = !errors_occurred[handle].empty();
|
||||
}
|
||||
error_mutex.unlock();
|
||||
return output;
|
||||
}
|
||||
/// @brief Helper function to set the paused status via the @ref Playback::IsPaused and @ref Playback::Pause APIs.
|
||||
/// @param paused The new pause state.
|
||||
inline virtual void SetPaused(bool paused) {
|
||||
if (IsPaused() != paused) {
|
||||
Pause();
|
||||
}
|
||||
}
|
||||
static Playback *Create(bool *daemon_found, bool daemon = false);
|
||||
};
|
||||
class DBusAPISender;
|
||||
class PlaybackRemote : public Playback {
|
||||
private:
|
||||
DBusAPISender *sender;
|
||||
public:
|
||||
|
||||
double GetPosition() override;
|
||||
double GetLength() override;
|
||||
void Seek(double position) override;
|
||||
void Start(std::string filePath) override;
|
||||
bool IsPaused() override;
|
||||
void Pause() override;
|
||||
void Stop() override;
|
||||
void Update();
|
||||
bool IsStopped() override;
|
||||
void SetTempo(float tempo) override;
|
||||
void SetPitch(float pitch) override;
|
||||
void SetSpeed(float speed) override;
|
||||
void SetVolume(float volume) override;
|
||||
float GetTempo() override;
|
||||
float GetPitch() override;
|
||||
float GetSpeed() override;
|
||||
float GetVolume() override;
|
||||
uint16_t handle_signals(uint16_t signal) override;
|
||||
optional<std::string> GetError() override;
|
||||
bool ErrorExists() override;
|
||||
PlaybackRemote();
|
||||
};
|
||||
class PlaybackInstance : public Playback {
|
||||
private:
|
||||
std::string filePath;
|
||||
|
@ -152,9 +209,6 @@ private:
|
|||
std::mutex current_file_mutex;
|
||||
std::optional<std::string> current_file;
|
||||
std::optional<std::string> current_title;
|
||||
uint16_t signals_occurred = PlaybackSignalNone;
|
||||
std::mutex signal_mutex;
|
||||
void set_signal(uint16_t signal);
|
||||
float prev_pitch, prev_speed, prev_tempo;
|
||||
|
||||
public:
|
||||
|
@ -162,7 +216,9 @@ public:
|
|||
~PlaybackInstance() override;
|
||||
std::optional<std::string> get_current_file() override;
|
||||
std::optional<std::string> get_current_title() override;
|
||||
|
||||
inline bool is_proxy() override {
|
||||
return false;
|
||||
}
|
||||
double GetPosition() override;
|
||||
double GetLength() override;
|
||||
void Seek(double position) override;
|
||||
|
@ -191,7 +247,4 @@ public:
|
|||
float MinSpeed = 0.25;
|
||||
float MinPitch = 0.25;
|
||||
float MinTempo = 0.25;
|
||||
uint16_t handle_signals(uint16_t signal) override;
|
||||
optional<std::string> GetError() override;
|
||||
bool ErrorExists() override;
|
||||
};
|
32
proxy_backend.cpp
Normal file
32
proxy_backend.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include "proxy_backend.hpp"
|
||||
#include "log.hpp"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
using namespace std::literals::chrono_literals;
|
||||
std::string ProxyGlueBackend::get_id() {
|
||||
return "proxy";
|
||||
}
|
||||
std::string ProxyGlueBackend::get_name() {
|
||||
return "Proxy Backend";
|
||||
}
|
||||
bool ProxyGlueBackend::allow_multi_instance() {
|
||||
return false;
|
||||
}
|
||||
int ProxyGlueBackend::run(std::vector<std::string> realArgs, int argc, char **argv) {
|
||||
bool commands_specified = parse_args(realArgs, argc, argv);
|
||||
if (!commands_specified) {
|
||||
DEBUG.writeln("No commands specified.");
|
||||
return 1;
|
||||
}
|
||||
sender = DBusAPISender::Create();
|
||||
if (sender == nullptr) {
|
||||
ERROR.writeln("Existing instance required to send commands.");
|
||||
return 1;
|
||||
}
|
||||
playback = (Playback*)sender;
|
||||
setup_playback_args();
|
||||
return 0;
|
||||
}
|
||||
void ProxyGlueBackend::quitDaemon() {
|
||||
((DBusAPISender*)playback)->Quit();
|
||||
}
|
11
proxy_backend.hpp
Normal file
11
proxy_backend.hpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
#include "backend.hpp"
|
||||
class ProxyGlueBackend : public UIBackend {
|
||||
DBusAPISender *sender;
|
||||
public:
|
||||
bool allow_multi_instance() override;
|
||||
std::string get_id() override;
|
||||
std::string get_name() override;
|
||||
void quitDaemon();
|
||||
int run(std::vector<std::string> realArgs, int argc, char **argv) override;
|
||||
};
|
Loading…
Reference in a new issue