File: state.hpp

package info (click to toggle)
waybar 0.14.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,364 kB
  • sloc: cpp: 24,698; xml: 742; python: 146; ansic: 77; makefile: 26
file content (218 lines) | stat: -rw-r--r-- 5,610 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#pragma once

#include <fmt/format.h>
#include <mpd/client.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_;
  int last_interval_;

 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;

  bool 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