mirror of
				https://github.com/rad4day/Waybar.git
				synced 2025-11-04 09:42:42 +01:00 
			
		
		
		
	Merge pull request #877 from jbenden/jbenden/mpd-module
mpd: revamped to event-driven, single-threaded
This commit is contained in:
		@@ -37,7 +37,7 @@
 | 
				
			|||||||
#include "modules/pulseaudio.hpp"
 | 
					#include "modules/pulseaudio.hpp"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef HAVE_LIBMPDCLIENT
 | 
					#ifdef HAVE_LIBMPDCLIENT
 | 
				
			||||||
#include "modules/mpd.hpp"
 | 
					#include "modules/mpd/mpd.hpp"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef HAVE_LIBSNDIO
 | 
					#ifdef HAVE_LIBSNDIO
 | 
				
			||||||
#include "modules/sndio.hpp"
 | 
					#include "modules/sndio.hpp"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,74 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <fmt/format.h>
 | 
					 | 
				
			||||||
#include <mpd/client.h>
 | 
					 | 
				
			||||||
#include <condition_variable>
 | 
					 | 
				
			||||||
#include <thread>
 | 
					 | 
				
			||||||
#include "ALabel.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace waybar::modules {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class MPD : public ALabel {
 | 
					 | 
				
			||||||
 public:
 | 
					 | 
				
			||||||
  MPD(const std::string&, const Json::Value&);
 | 
					 | 
				
			||||||
  auto update() -> void;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 private:
 | 
					 | 
				
			||||||
  std::thread periodic_updater();
 | 
					 | 
				
			||||||
  std::string getTag(mpd_tag_type type, unsigned idx = 0);
 | 
					 | 
				
			||||||
  void        setLabel();
 | 
					 | 
				
			||||||
  std::string getStateIcon();
 | 
					 | 
				
			||||||
  std::string getOptionIcon(std::string optionName, bool activated);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::thread event_listener();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Assumes `connection_lock_` is locked
 | 
					 | 
				
			||||||
  void tryConnect();
 | 
					 | 
				
			||||||
  // If checking errors on the main connection, make sure to lock it using
 | 
					 | 
				
			||||||
  // `connection_lock_` before calling checkErrors
 | 
					 | 
				
			||||||
  void checkErrors(mpd_connection* conn);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Assumes `connection_lock_` is locked
 | 
					 | 
				
			||||||
  void fetchState();
 | 
					 | 
				
			||||||
  void waitForEvent();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool handlePlayPause(GdkEventButton* const&);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool stopped();
 | 
					 | 
				
			||||||
  bool playing();
 | 
					 | 
				
			||||||
  bool paused();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const std::string module_name_;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  using unique_connection = std::unique_ptr<mpd_connection, decltype(&mpd_connection_free)>;
 | 
					 | 
				
			||||||
  using unique_status = std::unique_ptr<mpd_status, decltype(&mpd_status_free)>;
 | 
					 | 
				
			||||||
  using unique_song = std::unique_ptr<mpd_song, decltype(&mpd_song_free)>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Not using unique_ptr since we don't manage the pointer
 | 
					 | 
				
			||||||
  // (It's either nullptr, or from the config)
 | 
					 | 
				
			||||||
  const char*    server_;
 | 
					 | 
				
			||||||
  const unsigned port_;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  unsigned timeout_;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // We need a mutex here because we can trigger updates from multiple thread:
 | 
					 | 
				
			||||||
  // the event based updates, the periodic updates needed for the elapsed time,
 | 
					 | 
				
			||||||
  // and the click play/pause feature
 | 
					 | 
				
			||||||
  std::mutex        connection_lock_;
 | 
					 | 
				
			||||||
  unique_connection connection_;
 | 
					 | 
				
			||||||
  // The alternate connection will be used to wait for events: since it will
 | 
					 | 
				
			||||||
  // be blocking (idle) we can't send commands via this connection
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // No lock since only used in the event listener thread
 | 
					 | 
				
			||||||
  unique_connection alternate_connection_;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Protect them using the `connection_lock_`
 | 
					 | 
				
			||||||
  unique_status status_;
 | 
					 | 
				
			||||||
  mpd_state     state_;
 | 
					 | 
				
			||||||
  unique_song   song_;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // To make sure the previous periodic_updater stops before creating a new one
 | 
					 | 
				
			||||||
  std::mutex periodic_lock_;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}  // namespace waybar::modules
 | 
					 | 
				
			||||||
							
								
								
									
										66
									
								
								include/modules/mpd/mpd.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								include/modules/mpd/mpd.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <mpd/client.h>
 | 
				
			||||||
 | 
					#include <spdlog/fmt/bundled/format.h>
 | 
				
			||||||
 | 
					#include <spdlog/spdlog.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <condition_variable>
 | 
				
			||||||
 | 
					#include <thread>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ALabel.hpp"
 | 
				
			||||||
 | 
					#include "modules/mpd/state.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace waybar::modules {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MPD : public ALabel {
 | 
				
			||||||
 | 
					  friend class detail::Context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // State machine
 | 
				
			||||||
 | 
					  detail::Context context_{this};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const std::string module_name_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Not using unique_ptr since we don't manage the pointer
 | 
				
			||||||
 | 
					  // (It's either nullptr, or from the config)
 | 
				
			||||||
 | 
					  const char*    server_;
 | 
				
			||||||
 | 
					  const unsigned port_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  unsigned timeout_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  detail::unique_connection connection_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  detail::unique_status status_;
 | 
				
			||||||
 | 
					  mpd_state             state_;
 | 
				
			||||||
 | 
					  detail::unique_song   song_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  MPD(const std::string&, const Json::Value&);
 | 
				
			||||||
 | 
					  virtual ~MPD() noexcept = default;
 | 
				
			||||||
 | 
					  auto update() -> void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  std::string getTag(mpd_tag_type type, unsigned idx = 0) const;
 | 
				
			||||||
 | 
					  void        setLabel();
 | 
				
			||||||
 | 
					  std::string getStateIcon() const;
 | 
				
			||||||
 | 
					  std::string getOptionIcon(std::string optionName, bool activated) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // GUI-side methods
 | 
				
			||||||
 | 
					  bool handlePlayPause(GdkEventButton* const&);
 | 
				
			||||||
 | 
					  void emit() { dp.emit(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // MPD-side, Non-GUI methods.
 | 
				
			||||||
 | 
					  void tryConnect();
 | 
				
			||||||
 | 
					  void checkErrors(mpd_connection* conn);
 | 
				
			||||||
 | 
					  void fetchState();
 | 
				
			||||||
 | 
					  void queryMPD();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  inline bool stopped() const { return connection_ && state_ == MPD_STATE_STOP; }
 | 
				
			||||||
 | 
					  inline bool playing() const { return connection_ && state_ == MPD_STATE_PLAY; }
 | 
				
			||||||
 | 
					  inline bool paused() const { return connection_ && state_ == MPD_STATE_PAUSE; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if !defined(MPD_NOINLINE)
 | 
				
			||||||
 | 
					#include "modules/mpd/state.inl.hpp"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace waybar::modules
 | 
				
			||||||
							
								
								
									
										217
									
								
								include/modules/mpd/state.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								include/modules/mpd/state.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,217 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <mpd/client.h>
 | 
				
			||||||
 | 
					#include <spdlog/fmt/bundled/format.h>
 | 
				
			||||||
 | 
					#include <spdlog/spdlog.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <condition_variable>
 | 
				
			||||||
 | 
					#include <thread>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ALabel.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace waybar::modules {
 | 
				
			||||||
 | 
					class MPD;
 | 
				
			||||||
 | 
					}  // namespace waybar::modules
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace waybar::modules::detail {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using unique_connection = std::unique_ptr<mpd_connection, decltype(&mpd_connection_free)>;
 | 
				
			||||||
 | 
					using unique_status = std::unique_ptr<mpd_status, decltype(&mpd_status_free)>;
 | 
				
			||||||
 | 
					using unique_song = std::unique_ptr<mpd_song, decltype(&mpd_song_free)>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// This state machine loosely follows a non-hierarchical, statechart
 | 
				
			||||||
 | 
					/// pattern, and includes ENTRY and EXIT actions.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// The State class is the base class for all other states. The
 | 
				
			||||||
 | 
					/// entry and exit methods are automatically called when entering
 | 
				
			||||||
 | 
					/// into a new state and exiting from the current state. This
 | 
				
			||||||
 | 
					/// includes initially entering (Disconnected class) and exiting
 | 
				
			||||||
 | 
					/// Waybar.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// The following nested "top-level" states are represented:
 | 
				
			||||||
 | 
					/// 1. Idle - await notification of MPD activity.
 | 
				
			||||||
 | 
					/// 2. All Non-Idle states:
 | 
				
			||||||
 | 
					///    1. Playing - An active song is producing audio output.
 | 
				
			||||||
 | 
					///    2. Paused - The current song is paused.
 | 
				
			||||||
 | 
					///    3. Stopped - No song is actively playing.
 | 
				
			||||||
 | 
					/// 3. Disconnected - periodically attempt MPD (re-)connection.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// NOTE: Since this statechart is non-hierarchical, the above
 | 
				
			||||||
 | 
					/// states are flattened into a set.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class State {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  virtual ~State() noexcept = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  virtual void entry() noexcept { spdlog::debug("mpd: ignore entry action"); }
 | 
				
			||||||
 | 
					  virtual void exit() noexcept { spdlog::debug("mpd: ignore exit action"); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  virtual void play() { spdlog::debug("mpd: ignore play state transition"); }
 | 
				
			||||||
 | 
					  virtual void stop() { spdlog::debug("mpd: ignore stop state transition"); }
 | 
				
			||||||
 | 
					  virtual void pause() { spdlog::debug("mpd: ignore pause state transition"); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Request state update the GUI.
 | 
				
			||||||
 | 
					  virtual void update() noexcept { spdlog::debug("mpd: ignoring update method request"); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Idle : public State {
 | 
				
			||||||
 | 
					  Context* const   ctx_;
 | 
				
			||||||
 | 
					  sigc::connection idle_connection_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  Idle(Context* const ctx) : ctx_{ctx} {}
 | 
				
			||||||
 | 
					  virtual ~Idle() noexcept { this->exit(); };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void entry() noexcept override;
 | 
				
			||||||
 | 
					  void exit() noexcept override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void play() override;
 | 
				
			||||||
 | 
					  void stop() override;
 | 
				
			||||||
 | 
					  void pause() override;
 | 
				
			||||||
 | 
					  void update() noexcept override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  Idle(const Idle&) = delete;
 | 
				
			||||||
 | 
					  Idle& operator=(const Idle&) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool on_io(Glib::IOCondition const&);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Playing : public State {
 | 
				
			||||||
 | 
					  Context* const   ctx_;
 | 
				
			||||||
 | 
					  sigc::connection timer_connection_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  Playing(Context* const ctx) : ctx_{ctx} {}
 | 
				
			||||||
 | 
					  virtual ~Playing() noexcept { this->exit(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void entry() noexcept override;
 | 
				
			||||||
 | 
					  void exit() noexcept override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void pause() override;
 | 
				
			||||||
 | 
					  void stop() override;
 | 
				
			||||||
 | 
					  void update() noexcept override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  Playing(Playing const&) = delete;
 | 
				
			||||||
 | 
					  Playing& operator=(Playing const&) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool on_timer();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Paused : public State {
 | 
				
			||||||
 | 
					  Context* const   ctx_;
 | 
				
			||||||
 | 
					  sigc::connection timer_connection_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  Paused(Context* const ctx) : ctx_{ctx} {}
 | 
				
			||||||
 | 
					  virtual ~Paused() noexcept { this->exit(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void entry() noexcept override;
 | 
				
			||||||
 | 
					  void exit() noexcept override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void play() override;
 | 
				
			||||||
 | 
					  void stop() override;
 | 
				
			||||||
 | 
					  void update() noexcept override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  Paused(Paused const&) = delete;
 | 
				
			||||||
 | 
					  Paused& operator=(Paused const&) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool on_timer();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Stopped : public State {
 | 
				
			||||||
 | 
					  Context* const   ctx_;
 | 
				
			||||||
 | 
					  sigc::connection timer_connection_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  Stopped(Context* const ctx) : ctx_{ctx} {}
 | 
				
			||||||
 | 
					  virtual ~Stopped() noexcept { this->exit(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void entry() noexcept override;
 | 
				
			||||||
 | 
					  void exit() noexcept override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void play() override;
 | 
				
			||||||
 | 
					  void pause() override;
 | 
				
			||||||
 | 
					  void update() noexcept override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  Stopped(Stopped const&) = delete;
 | 
				
			||||||
 | 
					  Stopped& operator=(Stopped const&) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool on_timer();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Disconnected : public State {
 | 
				
			||||||
 | 
					  Context* const   ctx_;
 | 
				
			||||||
 | 
					  sigc::connection timer_connection_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  Disconnected(Context* const ctx) : ctx_{ctx} {}
 | 
				
			||||||
 | 
					  virtual ~Disconnected() noexcept { this->exit(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void entry() noexcept override;
 | 
				
			||||||
 | 
					  void exit() noexcept override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void update() noexcept override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  Disconnected(Disconnected const&) = delete;
 | 
				
			||||||
 | 
					  Disconnected& operator=(Disconnected const&) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void arm_timer(int interval) noexcept;
 | 
				
			||||||
 | 
					  void disarm_timer() noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool on_timer();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Context {
 | 
				
			||||||
 | 
					  std::unique_ptr<State> state_;
 | 
				
			||||||
 | 
					  waybar::modules::MPD*  mpd_module_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  friend class State;
 | 
				
			||||||
 | 
					  friend class Playing;
 | 
				
			||||||
 | 
					  friend class Paused;
 | 
				
			||||||
 | 
					  friend class Stopped;
 | 
				
			||||||
 | 
					  friend class Disconnected;
 | 
				
			||||||
 | 
					  friend class Idle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  void setState(std::unique_ptr<State>&& new_state) noexcept {
 | 
				
			||||||
 | 
					    if (state_.get() != nullptr) {
 | 
				
			||||||
 | 
					      state_->exit();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    state_ = std::move(new_state);
 | 
				
			||||||
 | 
					    state_->entry();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool                             is_connected() const;
 | 
				
			||||||
 | 
					  bool                             is_playing() const;
 | 
				
			||||||
 | 
					  bool                             is_paused() const;
 | 
				
			||||||
 | 
					  bool                             is_stopped() const;
 | 
				
			||||||
 | 
					  constexpr std::size_t            interval() const;
 | 
				
			||||||
 | 
					  void                             tryConnect() const;
 | 
				
			||||||
 | 
					  void                             checkErrors(mpd_connection*) const;
 | 
				
			||||||
 | 
					  void                             do_update();
 | 
				
			||||||
 | 
					  void                             queryMPD() const;
 | 
				
			||||||
 | 
					  void                             fetchState() const;
 | 
				
			||||||
 | 
					  constexpr mpd_state              state() const;
 | 
				
			||||||
 | 
					  void                             emit() const;
 | 
				
			||||||
 | 
					  [[nodiscard]] unique_connection& connection();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  explicit Context(waybar::modules::MPD* const mpd_module)
 | 
				
			||||||
 | 
					      : state_{std::make_unique<Disconnected>(this)}, mpd_module_{mpd_module} {
 | 
				
			||||||
 | 
					    state_->entry();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void play() { state_->play(); }
 | 
				
			||||||
 | 
					  void stop() { state_->stop(); }
 | 
				
			||||||
 | 
					  void pause() { state_->pause(); }
 | 
				
			||||||
 | 
					  void update() noexcept { state_->update(); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace waybar::modules::detail
 | 
				
			||||||
							
								
								
									
										24
									
								
								include/modules/mpd/state.inl.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								include/modules/mpd/state.inl.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace detail {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline bool Context::is_connected() const { return mpd_module_->connection_ != nullptr; }
 | 
				
			||||||
 | 
					inline bool Context::is_playing() const { return mpd_module_->playing(); }
 | 
				
			||||||
 | 
					inline bool Context::is_paused() const { return mpd_module_->paused(); }
 | 
				
			||||||
 | 
					inline bool Context::is_stopped() const { return mpd_module_->stopped(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr inline std::size_t Context::interval() const { return mpd_module_->interval_.count(); }
 | 
				
			||||||
 | 
					inline void                  Context::tryConnect() const { mpd_module_->tryConnect(); }
 | 
				
			||||||
 | 
					inline unique_connection&    Context::connection() { return mpd_module_->connection_; }
 | 
				
			||||||
 | 
					constexpr inline mpd_state   Context::state() const { return mpd_module_->state_; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline void Context::do_update() {
 | 
				
			||||||
 | 
					  mpd_module_->setLabel();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline void Context::checkErrors(mpd_connection* conn) const { mpd_module_->checkErrors(conn); }
 | 
				
			||||||
 | 
					inline void Context::queryMPD() const { mpd_module_->queryMPD(); }
 | 
				
			||||||
 | 
					inline void Context::fetchState() const { mpd_module_->fetchState(); }
 | 
				
			||||||
 | 
					inline void Context::emit() const { mpd_module_->emit(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace detail
 | 
				
			||||||
@@ -213,7 +213,8 @@ endif
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
if libmpdclient.found()
 | 
					if libmpdclient.found()
 | 
				
			||||||
    add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp')
 | 
					    add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp')
 | 
				
			||||||
    src_files += 'src/modules/mpd.cpp'
 | 
					    src_files += 'src/modules/mpd/mpd.cpp'
 | 
				
			||||||
 | 
					    src_files += 'src/modules/mpd/state.cpp'
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if gtk_layer_shell.found()
 | 
					if gtk_layer_shell.found()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,15 @@
 | 
				
			|||||||
#include "modules/mpd.hpp"
 | 
					#include "modules/mpd/mpd.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <fmt/chrono.h>
 | 
					#include <spdlog/fmt/bundled/chrono.h>
 | 
				
			||||||
#include <spdlog/spdlog.h>
 | 
					#include <spdlog/spdlog.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "modules/mpd/state.hpp"
 | 
				
			||||||
 | 
					#if defined(MPD_NOINLINE)
 | 
				
			||||||
 | 
					namespace waybar::modules {
 | 
				
			||||||
 | 
					#include "modules/mpd/state.inl.hpp"
 | 
				
			||||||
 | 
					}  // namespace waybar::modules
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config)
 | 
					waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config)
 | 
				
			||||||
    : ALabel(config, "mpd", id, "{album} - {artist} - {title}", 5),
 | 
					    : ALabel(config, "mpd", id, "{album} - {artist} - {title}", 5),
 | 
				
			||||||
      module_name_(id.empty() ? "mpd" : "mpd#" + id),
 | 
					      module_name_(id.empty() ? "mpd" : "mpd#" + id),
 | 
				
			||||||
@@ -10,7 +17,6 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config)
 | 
				
			|||||||
      port_(config_["port"].isUInt() ? config["port"].asUInt() : 0),
 | 
					      port_(config_["port"].isUInt() ? config["port"].asUInt() : 0),
 | 
				
			||||||
      timeout_(config_["timeout"].isUInt() ? config_["timeout"].asUInt() * 1'000 : 30'000),
 | 
					      timeout_(config_["timeout"].isUInt() ? config_["timeout"].asUInt() * 1'000 : 30'000),
 | 
				
			||||||
      connection_(nullptr, &mpd_connection_free),
 | 
					      connection_(nullptr, &mpd_connection_free),
 | 
				
			||||||
      alternate_connection_(nullptr, &mpd_connection_free),
 | 
					 | 
				
			||||||
      status_(nullptr, &mpd_status_free),
 | 
					      status_(nullptr, &mpd_status_free),
 | 
				
			||||||
      song_(nullptr, &mpd_song_free) {
 | 
					      song_(nullptr, &mpd_song_free) {
 | 
				
			||||||
  if (!config_["port"].isNull() && !config_["port"].isUInt()) {
 | 
					  if (!config_["port"].isNull() && !config_["port"].isUInt()) {
 | 
				
			||||||
@@ -28,73 +34,33 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config)
 | 
				
			|||||||
    server_ = config["server"].asCString();
 | 
					    server_ = config["server"].asCString();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  event_listener().detach();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
 | 
					  event_box_.add_events(Gdk::BUTTON_PRESS_MASK);
 | 
				
			||||||
  event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &MPD::handlePlayPause));
 | 
					  event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &MPD::handlePlayPause));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
auto waybar::modules::MPD::update() -> void {
 | 
					auto waybar::modules::MPD::update() -> void {
 | 
				
			||||||
  std::lock_guard guard(connection_lock_);
 | 
					  context_.update();
 | 
				
			||||||
  tryConnect();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (connection_ != nullptr) {
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      bool wasPlaying = playing();
 | 
					 | 
				
			||||||
      if(!wasPlaying) {
 | 
					 | 
				
			||||||
        // Wait until the periodic_updater has stopped
 | 
					 | 
				
			||||||
        std::lock_guard periodic_guard(periodic_lock_);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      fetchState();
 | 
					 | 
				
			||||||
      if (!wasPlaying && playing()) {
 | 
					 | 
				
			||||||
        periodic_updater().detach();
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } catch (const std::exception& e) {
 | 
					 | 
				
			||||||
      spdlog::error("{}: {}", module_name_, e.what());
 | 
					 | 
				
			||||||
      state_ = MPD_STATE_UNKNOWN;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  setLabel();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Call parent update
 | 
					  // Call parent update
 | 
				
			||||||
  ALabel::update();
 | 
					  ALabel::update();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::thread waybar::modules::MPD::event_listener() {
 | 
					void waybar::modules::MPD::queryMPD() {
 | 
				
			||||||
  return std::thread([this] {
 | 
					  if (connection_ != nullptr) {
 | 
				
			||||||
    while (true) {
 | 
					    spdlog::debug("{}: fetching state information", module_name_);
 | 
				
			||||||
      try {
 | 
					    try {
 | 
				
			||||||
        if (connection_ == nullptr) {
 | 
					      fetchState();
 | 
				
			||||||
          // Retry periodically if no connection
 | 
					      spdlog::debug("{}: fetch complete", module_name_);
 | 
				
			||||||
          dp.emit();
 | 
					    } catch (std::exception const& e) {
 | 
				
			||||||
          std::this_thread::sleep_for(interval_);
 | 
					      spdlog::error("{}: {}", module_name_, e.what());
 | 
				
			||||||
        } else {
 | 
					      state_ = MPD_STATE_UNKNOWN;
 | 
				
			||||||
          waitForEvent();
 | 
					 | 
				
			||||||
          dp.emit();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      } catch (const std::exception& e) {
 | 
					 | 
				
			||||||
        if (strcmp(e.what(), "Connection to MPD closed") == 0) {
 | 
					 | 
				
			||||||
          spdlog::debug("{}: {}", module_name_, e.what());
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          spdlog::warn("{}: {}", module_name_, e.what());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  });
 | 
					
 | 
				
			||||||
 | 
					    dp.emit();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::thread waybar::modules::MPD::periodic_updater() {
 | 
					std::string waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) const {
 | 
				
			||||||
  return std::thread([this] {
 | 
					 | 
				
			||||||
    std::lock_guard guard(periodic_lock_);
 | 
					 | 
				
			||||||
    while (connection_ != nullptr && playing()) {
 | 
					 | 
				
			||||||
      dp.emit();
 | 
					 | 
				
			||||||
      std::this_thread::sleep_for(std::chrono::seconds(1));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) {
 | 
					 | 
				
			||||||
  std::string result =
 | 
					  std::string result =
 | 
				
			||||||
      config_["unknown-tag"].isString() ? config_["unknown-tag"].asString() : "N/A";
 | 
					      config_["unknown-tag"].isString() ? config_["unknown-tag"].asString() : "N/A";
 | 
				
			||||||
  const char* tag = mpd_song_get_tag(song_.get(), type, idx);
 | 
					  const char* tag = mpd_song_get_tag(song_.get(), type, idx);
 | 
				
			||||||
@@ -133,7 +99,7 @@ void waybar::modules::MPD::setLabel() {
 | 
				
			|||||||
  auto format = format_;
 | 
					  auto format = format_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::string          artist, album_artist, album, title, date;
 | 
					  std::string          artist, album_artist, album, title, date;
 | 
				
			||||||
  int song_pos, queue_length;
 | 
					  int                  song_pos, queue_length;
 | 
				
			||||||
  std::chrono::seconds elapsedTime, totalTime;
 | 
					  std::chrono::seconds elapsedTime, totalTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::string stateIcon = "";
 | 
					  std::string stateIcon = "";
 | 
				
			||||||
@@ -149,8 +115,8 @@ void waybar::modules::MPD::setLabel() {
 | 
				
			|||||||
      label_.get_style_context()->add_class("playing");
 | 
					      label_.get_style_context()->add_class("playing");
 | 
				
			||||||
      label_.get_style_context()->remove_class("paused");
 | 
					      label_.get_style_context()->remove_class("paused");
 | 
				
			||||||
    } else if (paused()) {
 | 
					    } else if (paused()) {
 | 
				
			||||||
      format =
 | 
					      format = config_["format-paused"].isString() ? config_["format-paused"].asString()
 | 
				
			||||||
        config_["format-paused"].isString() ? config_["format-paused"].asString() : config_["format"].asString();
 | 
					                                                   : config_["format"].asString();
 | 
				
			||||||
      label_.get_style_context()->add_class("paused");
 | 
					      label_.get_style_context()->add_class("paused");
 | 
				
			||||||
      label_.get_style_context()->remove_class("playing");
 | 
					      label_.get_style_context()->remove_class("playing");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -216,7 +182,7 @@ void waybar::modules::MPD::setLabel() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string waybar::modules::MPD::getStateIcon() {
 | 
					std::string waybar::modules::MPD::getStateIcon() const {
 | 
				
			||||||
  if (!config_["state-icons"].isObject()) {
 | 
					  if (!config_["state-icons"].isObject()) {
 | 
				
			||||||
    return "";
 | 
					    return "";
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -238,7 +204,7 @@ std::string waybar::modules::MPD::getStateIcon() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool activated) {
 | 
					std::string waybar::modules::MPD::getOptionIcon(std::string optionName, bool activated) const {
 | 
				
			||||||
  if (!config_[optionName + "-icons"].isObject()) {
 | 
					  if (!config_[optionName + "-icons"].isObject()) {
 | 
				
			||||||
    return "";
 | 
					    return "";
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -261,15 +227,11 @@ void waybar::modules::MPD::tryConnect() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  connection_ =
 | 
					  connection_ =
 | 
				
			||||||
      unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free);
 | 
					      detail::unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  alternate_connection_ =
 | 
					  if (connection_ == nullptr) {
 | 
				
			||||||
      unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (connection_ == nullptr || alternate_connection_ == nullptr) {
 | 
					 | 
				
			||||||
    spdlog::error("{}: Failed to connect to MPD", module_name_);
 | 
					    spdlog::error("{}: Failed to connect to MPD", module_name_);
 | 
				
			||||||
    connection_.reset();
 | 
					    connection_.reset();
 | 
				
			||||||
    alternate_connection_.reset();
 | 
					 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -279,7 +241,6 @@ void waybar::modules::MPD::tryConnect() {
 | 
				
			|||||||
  } catch (std::runtime_error& e) {
 | 
					  } catch (std::runtime_error& e) {
 | 
				
			||||||
    spdlog::error("{}: Failed to connect to MPD: {}", module_name_, e.what());
 | 
					    spdlog::error("{}: Failed to connect to MPD: {}", module_name_, e.what());
 | 
				
			||||||
    connection_.reset();
 | 
					    connection_.reset();
 | 
				
			||||||
    alternate_connection_.reset();
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -292,7 +253,6 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) {
 | 
				
			|||||||
    case MPD_ERROR_CLOSED:
 | 
					    case MPD_ERROR_CLOSED:
 | 
				
			||||||
      mpd_connection_clear_error(conn);
 | 
					      mpd_connection_clear_error(conn);
 | 
				
			||||||
      connection_.reset();
 | 
					      connection_.reset();
 | 
				
			||||||
      alternate_connection_.reset();
 | 
					 | 
				
			||||||
      state_ = MPD_STATE_UNKNOWN;
 | 
					      state_ = MPD_STATE_UNKNOWN;
 | 
				
			||||||
      throw std::runtime_error("Connection to MPD closed");
 | 
					      throw std::runtime_error("Connection to MPD closed");
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
@@ -306,37 +266,20 @@ void waybar::modules::MPD::checkErrors(mpd_connection* conn) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void waybar::modules::MPD::fetchState() {
 | 
					void waybar::modules::MPD::fetchState() {
 | 
				
			||||||
 | 
					  if (connection_ == nullptr) {
 | 
				
			||||||
 | 
					    spdlog::error("{}: Not connected to MPD", module_name_);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  auto conn = connection_.get();
 | 
					  auto conn = connection_.get();
 | 
				
			||||||
  status_ = unique_status(mpd_run_status(conn), &mpd_status_free);
 | 
					
 | 
				
			||||||
 | 
					  status_ = detail::unique_status(mpd_run_status(conn), &mpd_status_free);
 | 
				
			||||||
  checkErrors(conn);
 | 
					  checkErrors(conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  state_ = mpd_status_get_state(status_.get());
 | 
					  state_ = mpd_status_get_state(status_.get());
 | 
				
			||||||
  checkErrors(conn);
 | 
					  checkErrors(conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  song_ = unique_song(mpd_run_current_song(conn), &mpd_song_free);
 | 
					  song_ = detail::unique_song(mpd_run_current_song(conn), &mpd_song_free);
 | 
				
			||||||
  checkErrors(conn);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void waybar::modules::MPD::waitForEvent() {
 | 
					 | 
				
			||||||
  auto conn = alternate_connection_.get();
 | 
					 | 
				
			||||||
  // Wait for a player (play/pause), option (random, shuffle, etc.), or playlist
 | 
					 | 
				
			||||||
  // change
 | 
					 | 
				
			||||||
  if (!mpd_send_idle_mask(
 | 
					 | 
				
			||||||
          conn, static_cast<mpd_idle>(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_QUEUE))) {
 | 
					 | 
				
			||||||
    checkErrors(conn);
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  // alternate_idle_ = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // See issue #277:
 | 
					 | 
				
			||||||
  // https://github.com/Alexays/Waybar/issues/277
 | 
					 | 
				
			||||||
  mpd_recv_idle(conn, /* disable_timeout = */ false);
 | 
					 | 
				
			||||||
  // See issue #281:
 | 
					 | 
				
			||||||
  // https://github.com/Alexays/Waybar/issues/281
 | 
					 | 
				
			||||||
  std::lock_guard guard(connection_lock_);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  checkErrors(conn);
 | 
					 | 
				
			||||||
  mpd_response_finish(conn);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  checkErrors(conn);
 | 
					  checkErrors(conn);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -346,24 +289,13 @@ bool waybar::modules::MPD::handlePlayPause(GdkEventButton* const& e) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (e->button == 1) {
 | 
					  if (e->button == 1) {
 | 
				
			||||||
    std::lock_guard guard(connection_lock_);
 | 
					    if (state_ == MPD_STATE_PLAY)
 | 
				
			||||||
    if (stopped()) {
 | 
					      context_.pause();
 | 
				
			||||||
      mpd_run_play(connection_.get());
 | 
					    else
 | 
				
			||||||
    } else {
 | 
					      context_.play();
 | 
				
			||||||
      mpd_run_toggle_pause(connection_.get());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  } else if (e->button == 3) {
 | 
					  } else if (e->button == 3) {
 | 
				
			||||||
    std::lock_guard guard(connection_lock_);
 | 
					    context_.stop();
 | 
				
			||||||
    mpd_run_stop(connection_.get());
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
bool waybar::modules::MPD::stopped() {
 | 
					 | 
				
			||||||
  return connection_ == nullptr || state_ == MPD_STATE_UNKNOWN || state_ == MPD_STATE_STOP || status_ == nullptr;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool waybar::modules::MPD::playing() { return connection_ != nullptr && state_ == MPD_STATE_PLAY; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool waybar::modules::MPD::paused() { return connection_ != nullptr && state_ == MPD_STATE_PAUSE; }
 | 
					 | 
				
			||||||
							
								
								
									
										382
									
								
								src/modules/mpd/state.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										382
									
								
								src/modules/mpd/state.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,382 @@
 | 
				
			|||||||
 | 
					#include "modules/mpd/state.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <spdlog/fmt/bundled/chrono.h>
 | 
				
			||||||
 | 
					#include <spdlog/spdlog.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "modules/mpd/mpd.hpp"
 | 
				
			||||||
 | 
					#if defined(MPD_NOINLINE)
 | 
				
			||||||
 | 
					namespace waybar::modules {
 | 
				
			||||||
 | 
					#include "modules/mpd/state.inl.hpp"
 | 
				
			||||||
 | 
					}  // namespace waybar::modules
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace waybar::modules::detail {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define IDLE_RUN_NOIDLE_AND_CMD(...)                                      \
 | 
				
			||||||
 | 
					  if (idle_connection_.connected()) {                                     \
 | 
				
			||||||
 | 
					    idle_connection_.disconnect();                                        \
 | 
				
			||||||
 | 
					    auto conn = ctx_->connection().get();                                 \
 | 
				
			||||||
 | 
					    if (!mpd_run_noidle(conn)) {                                          \
 | 
				
			||||||
 | 
					      if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) {          \
 | 
				
			||||||
 | 
					        spdlog::error("mpd: Idle: failed to unregister for IDLE events"); \
 | 
				
			||||||
 | 
					        ctx_->checkErrors(conn);                                          \
 | 
				
			||||||
 | 
					      }                                                                   \
 | 
				
			||||||
 | 
					    }                                                                     \
 | 
				
			||||||
 | 
					    __VA_ARGS__;                                                          \
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Idle::play() {
 | 
				
			||||||
 | 
					  IDLE_RUN_NOIDLE_AND_CMD(mpd_run_play(conn));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ctx_->setState(std::make_unique<Playing>(ctx_));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Idle::pause() {
 | 
				
			||||||
 | 
					  IDLE_RUN_NOIDLE_AND_CMD(mpd_run_pause(conn, true));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ctx_->setState(std::make_unique<Paused>(ctx_));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Idle::stop() {
 | 
				
			||||||
 | 
					  IDLE_RUN_NOIDLE_AND_CMD(mpd_run_stop(conn));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ctx_->setState(std::make_unique<Stopped>(ctx_));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#undef IDLE_RUN_NOIDLE_AND_CMD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Idle::update() noexcept {
 | 
				
			||||||
 | 
					  // This is intentionally blank.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Idle::entry() noexcept {
 | 
				
			||||||
 | 
					  auto            conn = ctx_->connection().get();
 | 
				
			||||||
 | 
					  assert(conn != nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!mpd_send_idle_mask(
 | 
				
			||||||
 | 
					          conn, static_cast<mpd_idle>(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_QUEUE))) {
 | 
				
			||||||
 | 
					    ctx_->checkErrors(conn);
 | 
				
			||||||
 | 
					    spdlog::error("mpd: Idle: failed to register for IDLE events");
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    spdlog::debug("mpd: Idle: watching FD");
 | 
				
			||||||
 | 
					    sigc::slot<bool, Glib::IOCondition const&> idle_slot = sigc::mem_fun(*this, &Idle::on_io);
 | 
				
			||||||
 | 
					    idle_connection_ =
 | 
				
			||||||
 | 
					        Glib::signal_io().connect(idle_slot,
 | 
				
			||||||
 | 
					                                  mpd_connection_get_fd(conn),
 | 
				
			||||||
 | 
					                                  Glib::IO_IN | Glib::IO_PRI | Glib::IO_ERR | Glib::IO_HUP);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Idle::exit() noexcept {
 | 
				
			||||||
 | 
					  if (idle_connection_.connected()) {
 | 
				
			||||||
 | 
					    idle_connection_.disconnect();
 | 
				
			||||||
 | 
					    spdlog::debug("mpd: Idle: unwatching FD");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Idle::on_io(Glib::IOCondition const&) {
 | 
				
			||||||
 | 
					  auto                         conn = ctx_->connection().get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // callback should do this:
 | 
				
			||||||
 | 
					  enum mpd_idle events = mpd_recv_idle(conn, /* ignore_timeout?= */ false);
 | 
				
			||||||
 | 
					  spdlog::debug("mpd: Idle: recv_idle events -> {}", events);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mpd_response_finish(conn);
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    ctx_->checkErrors(conn);
 | 
				
			||||||
 | 
					  } catch (std::exception const& e) {
 | 
				
			||||||
 | 
					    spdlog::warn("mpd: Idle: error: {}", e.what());
 | 
				
			||||||
 | 
					    ctx_->setState(std::make_unique<Disconnected>(ctx_));
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ctx_->fetchState();
 | 
				
			||||||
 | 
					  mpd_state state = ctx_->state();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (state == MPD_STATE_STOP) {
 | 
				
			||||||
 | 
					    ctx_->emit();
 | 
				
			||||||
 | 
					    ctx_->setState(std::make_unique<Stopped>(ctx_));
 | 
				
			||||||
 | 
					  } else if (state == MPD_STATE_PLAY) {
 | 
				
			||||||
 | 
					    ctx_->emit();
 | 
				
			||||||
 | 
					    ctx_->setState(std::make_unique<Playing>(ctx_));
 | 
				
			||||||
 | 
					  } else if (state == MPD_STATE_PAUSE) {
 | 
				
			||||||
 | 
					    ctx_->emit();
 | 
				
			||||||
 | 
					    ctx_->setState(std::make_unique<Paused>(ctx_));
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    ctx_->emit();
 | 
				
			||||||
 | 
					    // self transition
 | 
				
			||||||
 | 
					    ctx_->setState(std::make_unique<Idle>(ctx_));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Playing::entry() noexcept {
 | 
				
			||||||
 | 
					  sigc::slot<bool> timer_slot = sigc::mem_fun(*this, &Playing::on_timer);
 | 
				
			||||||
 | 
					  timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 1'000);
 | 
				
			||||||
 | 
					  spdlog::debug("mpd: Playing: enabled 1 second periodic timer.");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Playing::exit() noexcept {
 | 
				
			||||||
 | 
					  if (timer_connection_.connected()) {
 | 
				
			||||||
 | 
					    timer_connection_.disconnect();
 | 
				
			||||||
 | 
					    spdlog::debug("mpd: Playing: disabled 1 second periodic timer.");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Playing::on_timer() {
 | 
				
			||||||
 | 
					  // Attempt to connect with MPD.
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    ctx_->tryConnect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Success?
 | 
				
			||||||
 | 
					    if (!ctx_->is_connected()) {
 | 
				
			||||||
 | 
					      ctx_->setState(std::make_unique<Disconnected>(ctx_));
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ctx_->fetchState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!ctx_->is_playing()) {
 | 
				
			||||||
 | 
					      if (ctx_->is_paused()) {
 | 
				
			||||||
 | 
					        ctx_->setState(std::make_unique<Paused>(ctx_));
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        ctx_->setState(std::make_unique<Stopped>(ctx_));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ctx_->queryMPD();
 | 
				
			||||||
 | 
					    ctx_->emit();
 | 
				
			||||||
 | 
					  } catch (std::exception const& e) {
 | 
				
			||||||
 | 
					    spdlog::warn("mpd: Playing: error: {}", e.what());
 | 
				
			||||||
 | 
					    ctx_->setState(std::make_unique<Disconnected>(ctx_));
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Playing::stop() {
 | 
				
			||||||
 | 
					  if (timer_connection_.connected()) {
 | 
				
			||||||
 | 
					    timer_connection_.disconnect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mpd_run_stop(ctx_->connection().get());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ctx_->setState(std::make_unique<Stopped>(ctx_));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Playing::pause() {
 | 
				
			||||||
 | 
					  if (timer_connection_.connected()) {
 | 
				
			||||||
 | 
					    timer_connection_.disconnect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mpd_run_pause(ctx_->connection().get(), true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ctx_->setState(std::make_unique<Paused>(ctx_));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Playing::update() noexcept { ctx_->do_update(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Paused::entry() noexcept {
 | 
				
			||||||
 | 
					  sigc::slot<bool> timer_slot = sigc::mem_fun(*this, &Paused::on_timer);
 | 
				
			||||||
 | 
					  timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 200);
 | 
				
			||||||
 | 
					  spdlog::debug("mpd: Paused: enabled 200 ms periodic timer.");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Paused::exit() noexcept {
 | 
				
			||||||
 | 
					  if (timer_connection_.connected()) {
 | 
				
			||||||
 | 
					    timer_connection_.disconnect();
 | 
				
			||||||
 | 
					    spdlog::debug("mpd: Paused: disabled 200 ms periodic timer.");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Paused::on_timer() {
 | 
				
			||||||
 | 
					  bool                         rc = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Attempt to connect with MPD.
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    ctx_->tryConnect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Success?
 | 
				
			||||||
 | 
					    if (!ctx_->is_connected()) {
 | 
				
			||||||
 | 
					      ctx_->setState(std::make_unique<Disconnected>(ctx_));
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ctx_->fetchState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ctx_->emit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (ctx_->is_paused()) {
 | 
				
			||||||
 | 
					      ctx_->setState(std::make_unique<Idle>(ctx_));
 | 
				
			||||||
 | 
					      rc = false;
 | 
				
			||||||
 | 
					    } else if (ctx_->is_playing()) {
 | 
				
			||||||
 | 
					      ctx_->setState(std::make_unique<Playing>(ctx_));
 | 
				
			||||||
 | 
					      rc = false;
 | 
				
			||||||
 | 
					    } else if (ctx_->is_stopped()) {
 | 
				
			||||||
 | 
					      ctx_->setState(std::make_unique<Stopped>(ctx_));
 | 
				
			||||||
 | 
					      rc = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } catch (std::exception const& e) {
 | 
				
			||||||
 | 
					    spdlog::warn("mpd: Paused: error: {}", e.what());
 | 
				
			||||||
 | 
					    ctx_->setState(std::make_unique<Disconnected>(ctx_));
 | 
				
			||||||
 | 
					    rc = false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Paused::play() {
 | 
				
			||||||
 | 
					  if (timer_connection_.connected()) {
 | 
				
			||||||
 | 
					    timer_connection_.disconnect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mpd_run_play(ctx_->connection().get());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ctx_->setState(std::make_unique<Playing>(ctx_));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Paused::stop() {
 | 
				
			||||||
 | 
					  if (timer_connection_.connected()) {
 | 
				
			||||||
 | 
					    timer_connection_.disconnect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mpd_run_stop(ctx_->connection().get());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ctx_->setState(std::make_unique<Stopped>(ctx_));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Paused::update() noexcept { ctx_->do_update(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Stopped::entry() noexcept {
 | 
				
			||||||
 | 
					  sigc::slot<bool> timer_slot = sigc::mem_fun(*this, &Stopped::on_timer);
 | 
				
			||||||
 | 
					  timer_connection_ = Glib::signal_timeout().connect(timer_slot, /* milliseconds */ 200);
 | 
				
			||||||
 | 
					  spdlog::debug("mpd: Stopped: enabled 200 ms periodic timer.");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Stopped::exit() noexcept {
 | 
				
			||||||
 | 
					  if (timer_connection_.connected()) {
 | 
				
			||||||
 | 
					    timer_connection_.disconnect();
 | 
				
			||||||
 | 
					    spdlog::debug("mpd: Stopped: disabled 200 ms periodic timer.");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Stopped::on_timer() {
 | 
				
			||||||
 | 
					  bool                         rc = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Attempt to connect with MPD.
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    ctx_->tryConnect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Success?
 | 
				
			||||||
 | 
					    if (!ctx_->is_connected()) {
 | 
				
			||||||
 | 
					      ctx_->setState(std::make_unique<Disconnected>(ctx_));
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ctx_->fetchState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ctx_->emit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (ctx_->is_stopped()) {
 | 
				
			||||||
 | 
					      ctx_->setState(std::make_unique<Idle>(ctx_));
 | 
				
			||||||
 | 
					      rc = false;
 | 
				
			||||||
 | 
					    } else if (ctx_->is_playing()) {
 | 
				
			||||||
 | 
					      ctx_->setState(std::make_unique<Playing>(ctx_));
 | 
				
			||||||
 | 
					      rc = false;
 | 
				
			||||||
 | 
					    } else if (ctx_->is_paused()) {
 | 
				
			||||||
 | 
					      ctx_->setState(std::make_unique<Paused>(ctx_));
 | 
				
			||||||
 | 
					      rc = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } catch (std::exception const& e) {
 | 
				
			||||||
 | 
					    spdlog::warn("mpd: Stopped: error: {}", e.what());
 | 
				
			||||||
 | 
					    ctx_->setState(std::make_unique<Disconnected>(ctx_));
 | 
				
			||||||
 | 
					    rc = false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Stopped::play() {
 | 
				
			||||||
 | 
					  if (timer_connection_.connected()) {
 | 
				
			||||||
 | 
					    timer_connection_.disconnect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mpd_run_play(ctx_->connection().get());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ctx_->setState(std::make_unique<Playing>(ctx_));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Stopped::pause() {
 | 
				
			||||||
 | 
					  if (timer_connection_.connected()) {
 | 
				
			||||||
 | 
					    timer_connection_.disconnect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mpd_run_pause(ctx_->connection().get(), true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ctx_->setState(std::make_unique<Paused>(ctx_));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Stopped::update() noexcept { ctx_->do_update(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Disconnected::arm_timer(int interval) noexcept {
 | 
				
			||||||
 | 
					  // unregister timer, if present
 | 
				
			||||||
 | 
					  disarm_timer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // register timer
 | 
				
			||||||
 | 
					  sigc::slot<bool> timer_slot = sigc::mem_fun(*this, &Disconnected::on_timer);
 | 
				
			||||||
 | 
					  timer_connection_ =
 | 
				
			||||||
 | 
					      Glib::signal_timeout().connect(timer_slot, interval);
 | 
				
			||||||
 | 
					  spdlog::debug("mpd: Disconnected: enabled interval timer.");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Disconnected::disarm_timer() noexcept {
 | 
				
			||||||
 | 
					  // unregister timer, if present
 | 
				
			||||||
 | 
					  if (timer_connection_.connected()) {
 | 
				
			||||||
 | 
					    timer_connection_.disconnect();
 | 
				
			||||||
 | 
					    spdlog::debug("mpd: Disconnected: disabled interval timer.");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Disconnected::entry() noexcept {
 | 
				
			||||||
 | 
					  arm_timer(1'000);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Disconnected::exit() noexcept {
 | 
				
			||||||
 | 
					  disarm_timer();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Disconnected::on_timer() {
 | 
				
			||||||
 | 
					  // Attempt to connect with MPD.
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    ctx_->tryConnect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Success?
 | 
				
			||||||
 | 
					    if (ctx_->is_connected()) {
 | 
				
			||||||
 | 
					      ctx_->fetchState();
 | 
				
			||||||
 | 
					      ctx_->emit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (ctx_->is_playing()) {
 | 
				
			||||||
 | 
					        ctx_->setState(std::make_unique<Playing>(ctx_));
 | 
				
			||||||
 | 
					      } else if (ctx_->is_paused()) {
 | 
				
			||||||
 | 
					        ctx_->setState(std::make_unique<Paused>(ctx_));
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        ctx_->setState(std::make_unique<Stopped>(ctx_));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return false;  // do not rearm timer
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } catch (std::exception const& e) {
 | 
				
			||||||
 | 
					    spdlog::warn("mpd: Disconnected: error: {}", e.what());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  arm_timer(ctx_->interval() * 1'000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Disconnected::update() noexcept { ctx_->do_update(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace waybar::modules::detail
 | 
				
			||||||
		Reference in New Issue
	
	Block a user