From 20685a8d2e152b7b6bc3912cdd445bb14873de89 Mon Sep 17 00:00:00 2001 From: hippoz <10706925-hippoz@users.noreply.gitlab.com> Date: Sat, 5 Nov 2022 14:58:45 +0200 Subject: [PATCH] simple music player implementation using mpv --- .gitignore | 3 +++ meson.build | 16 ++++++++++++++ src/AppWidget.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++ src/AppWidget.hpp | 19 ++++++++++++++++ src/MPVClient.cpp | 45 ++++++++++++++++++++++++++++++++++++++ src/MPVClient.hpp | 30 ++++++++++++++++++++++++++ src/main.cpp | 22 +++++++++++++++++++ 7 files changed, 190 insertions(+) create mode 100644 .gitignore create mode 100644 meson.build create mode 100644 src/AppWidget.cpp create mode 100644 src/AppWidget.hpp create mode 100644 src/MPVClient.cpp create mode 100644 src/MPVClient.hpp create mode 100644 src/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d28b126 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.cache/ +builddir/ +compile_commands.json \ No newline at end of file diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..d0f4f75 --- /dev/null +++ b/meson.build @@ -0,0 +1,16 @@ +project( + 'waffle', + 'cpp', + default_options : ['cpp_std=c++17'] +) + +raven_dep = dependency('raven') +mpv_dep = dependency('mpv') + +executable( + 'waffle', + './src/MPVClient.cpp', + './src/AppWidget.cpp', + './src/main.cpp', + dependencies : [raven_dep, mpv_dep] +) \ No newline at end of file diff --git a/src/AppWidget.cpp b/src/AppWidget.cpp new file mode 100644 index 0000000..22344b7 --- /dev/null +++ b/src/AppWidget.cpp @@ -0,0 +1,55 @@ +#include "AppWidget.hpp" +#include "raven/Styles.hpp" +#include "raven/Box.hpp" +#include "raven/TextInput.hpp" +#include "raven/Button.hpp" +#include "raven/ScrollContainer.hpp" +#include "raven/Widget.hpp" +#include "raven/ListLayout.hpp" +#include "raven/BoxLayout.hpp" +#include "raven/Label.hpp" +#include "raven/DocumentLayout.hpp" +#include "MPVClient.hpp" +#include + +static void populate_song_list(std::filesystem::path songs_directory, std::shared_ptr song_list_widget, MPVClient *mpv_client) { + song_list_widget->clear_children(); + for (const auto &entry : std::filesystem::directory_iterator(songs_directory)) { + std::string filename = entry.path().filename(); + std::string path_string = entry.path().string(); + auto button = song_list_widget->add(filename); + button->on_click = [mpv_client, path_string]() { + const char *command[] = { + "loadfile", + path_string.c_str(), + NULL + }; + + mpv_client->send_command_async(command); + }; + } +} + +void AppWidget::on_init() { + /* init */ + { + auto layout = set_layout(Raven::Direction::Vertical); + layout->set_spacing(8); + layout->set_margin(12); + } + + /* song list */ + { + m_song_list = add(); + m_song_list->set_grows(true); + auto target = m_song_list->make_target(); + auto layout = target->set_layout(Raven::Direction::Vertical); + layout->set_spacing(4.0); + layout->set_inherit_secondary_dimension(true); + } + + populate_song_list("/home/hippoz/music", m_song_list->target(), m_mpv_client); + + reflow(); + set_did_init(true); +} \ No newline at end of file diff --git a/src/AppWidget.hpp b/src/AppWidget.hpp new file mode 100644 index 0000000..102d576 --- /dev/null +++ b/src/AppWidget.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "raven/ScrollContainer.hpp" +#include "raven/Widget.hpp" +#include "raven/TextInput.hpp" +#include "src/MPVClient.hpp" + +class AppWidget : public Raven::Widget { +public: + AppWidget(MPVClient *mpv_client) + : Raven::Widget(), + m_mpv_client(mpv_client) {} + +protected: + void on_init() override; +private: + std::shared_ptr m_song_list; + MPVClient *m_mpv_client { nullptr }; +}; diff --git a/src/MPVClient.cpp b/src/MPVClient.cpp new file mode 100644 index 0000000..a43f7a1 --- /dev/null +++ b/src/MPVClient.cpp @@ -0,0 +1,45 @@ +#include "MPVClient.hpp" +#include + +static void handle_mpv_wakeup_callback(void *mpv_client_ptr) { + MPVClient *mpv_client = static_cast(mpv_client_ptr); + mpv_client->raven_application()->queue_microtask([mpv_client]() { + mpv_client->handle_events(); + }); + mpv_client->raven_application()->wake(); +} + +void MPVClient::handle_events() { + while (1) { + mpv_event *event = mpv_wait_event(m_ctx, 0); + std::cout << "[MPV EVENT] " << mpv_event_name(event->event_id) << std::endl; + if (event->event_id == MPV_EVENT_NONE) { + break; + } + } +} + +int MPVClient::initialize() { + m_ctx = mpv_create(); + if (!m_ctx) { + return -1; + } + + mpv_set_option_string(m_ctx, "vo", "null"); + + if (mpv_initialize(m_ctx) < 0) return -1; + + mpv_set_wakeup_callback(m_ctx, handle_mpv_wakeup_callback, this); + + return 0; +} + +void MPVClient::send_command_async(const char **command) { + mpv_command_async(m_ctx, 1, command); +} + +MPVClient::~MPVClient() { + if (m_ctx) { + mpv_terminate_destroy(m_ctx); + } +} diff --git a/src/MPVClient.hpp b/src/MPVClient.hpp new file mode 100644 index 0000000..39764f2 --- /dev/null +++ b/src/MPVClient.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "Application.hpp" +#include "Window.hpp" +#include + + +class MPVClient { +public: + MPVClient( + Raven::Application *raven_application, + Raven::Window *raven_window + ) + : m_raven_application(raven_application) + , m_raven_window(raven_window) {} + + ~MPVClient(); + + int initialize(); + void handle_events(); + + void send_command_async(const char **command); + + Raven::Application* raven_application() { return m_raven_application; } + Raven::Window* raven_window() { return m_raven_window; } +private: + mpv_handle *m_ctx { nullptr }; + Raven::Application* m_raven_application { nullptr }; + Raven::Window* m_raven_window { nullptr }; +}; diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..6ae2e0c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,22 @@ +#include +#include "raven/Window.hpp" +#include "raven/Application.hpp" +#include "raven/Widget.hpp" +#include "raven/Button.hpp" +#include "AppWidget.hpp" +#include "MPVClient.hpp" + +int main() +{ + Raven::Application application(true); + auto window = application.add_window(); + + MPVClient client { &application, window.get() }; + client.initialize(); + + window->spawn_window(); + + window->set_main_widget(&client); + + return application.run(); +}