player_command: move the seek timer to class DelayedSeek
authorMax Kellermann <max@musicpd.org>
Tue, 25 Sep 2018 07:55:25 +0000 (09:55 +0200)
committerMax Kellermann <max@musicpd.org>
Tue, 25 Sep 2018 08:41:33 +0000 (10:41 +0200)
Eliminate global variables.

13 files changed:
meson.build
src/DelayedSeek.cxx [new file with mode: 0644]
src/DelayedSeek.hxx [new file with mode: 0644]
src/Instance.cxx
src/Instance.hxx
src/Main.cxx
src/StatusBar.cxx
src/StatusBar.hxx
src/player_command.cxx
src/player_command.hxx
src/screen.cxx
src/screen.hxx
src/screen_paint.cxx

index 0e91104..2325e89 100644 (file)
@@ -319,6 +319,7 @@ ncmpc = executable('ncmpc',
   'src/Match.cxx',
   'src/ncu.cxx',
   'src/player_command.cxx',
+  'src/DelayedSeek.cxx',
   'src/TabBar.cxx',
   'src/TitleBar.cxx',
   'src/ProgressBar.cxx',
diff --git a/src/DelayedSeek.cxx b/src/DelayedSeek.cxx
new file mode 100644 (file)
index 0000000..307fac4
--- /dev/null
@@ -0,0 +1,109 @@
+/* ncmpc (Ncurses MPD Client)
+ * (c) 2004-2018 The Music Player Daemon Project
+ * Project homepage: http://musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "DelayedSeek.hxx"
+#include "mpdclient.hxx"
+
+#include <glib.h>
+
+void
+DelayedSeek::Commit() noexcept
+{
+       if (id < 0)
+               return;
+
+       struct mpd_connection *connection = c.GetConnection();
+       if (connection == nullptr) {
+               id = -1;
+               return;
+       }
+
+       if (c.song != nullptr && (unsigned)id == mpd_song_get_id(c.song))
+               if (!mpd_run_seek_id(connection, id, time))
+                       c.HandleError();
+
+       id = -1;
+}
+
+void
+DelayedSeek::Cancel() noexcept
+{
+       if (source_id != 0) {
+               g_source_remove(source_id);
+               source_id = 0;
+       }
+}
+
+void
+DelayedSeek::OnTimer() noexcept
+{
+       source_id = 0;
+       Commit();
+}
+
+/**
+ * This timer is invoked after seeking when the user hasn't typed a
+ * key for 500ms.  It is used to do the real seeking.
+ */
+static gboolean
+seek_timer(gpointer data)
+{
+       auto &ds = *(DelayedSeek *)data;
+       ds.OnTimer();
+       return false;
+}
+
+void
+DelayedSeek::ScheduleTimer() noexcept
+{
+       assert(source_id == 0);
+
+       source_id = g_timeout_add(500, seek_timer, this);
+}
+
+bool
+DelayedSeek::Seek(int offset) noexcept
+{
+       if (!c.playing_or_paused)
+               return false;
+
+       int current_id = mpd_status_get_song_id(c.status);
+       if (current_id < 0)
+               return false;
+
+       int new_time;
+       if (current_id == id) {
+               new_time = time;
+       } else {
+               id = current_id;
+               new_time = mpd_status_get_elapsed_time(c.status);
+       }
+
+       new_time += offset;
+       if (new_time < 0)
+               new_time = 0;
+       else if ((unsigned)new_time > mpd_status_get_total_time(c.status))
+               new_time = mpd_status_get_total_time(c.status);
+
+       time = new_time;
+
+       ScheduleTimer();
+
+       return true;
+}
diff --git a/src/DelayedSeek.hxx b/src/DelayedSeek.hxx
new file mode 100644 (file)
index 0000000..aad9d1c
--- /dev/null
@@ -0,0 +1,64 @@
+/* ncmpc (Ncurses MPD Client)
+ * (c) 2004-2018 The Music Player Daemon Project
+ * Project homepage: http://musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef NCMPC_DELAYED_SEEK_HXX
+#define NCMPC_DELAYED_SEEK_HXX
+
+struct mpdclient;
+
+/**
+ * Helper class which handles user seek commands; it will delay
+ * actually sending the seek command to MPD.
+ */
+class DelayedSeek {
+       struct mpdclient &c;
+
+       int id = -1;
+       unsigned time;
+
+       unsigned source_id = 0;
+
+public:
+       DelayedSeek(struct mpdclient &_c) noexcept
+               :c(_c) {}
+
+       ~DelayedSeek() noexcept {
+               Cancel();
+       }
+
+       bool IsSeeking(int _id) const noexcept {
+               return id >= 0 && _id == id;
+       }
+
+       unsigned GetTime() const noexcept {
+               return time;
+       }
+
+       bool Seek(int offset) noexcept;
+
+       void Commit() noexcept;
+       void Cancel() noexcept;
+
+       void OnTimer() noexcept;
+
+private:
+       void ScheduleTimer() noexcept;
+};
+
+#endif
index 7fcfe6b..f56f0ca 100644 (file)
@@ -31,8 +31,8 @@ Instance::Instance()
         client(options.host.empty() ? nullptr : options.host.c_str(),
                options.port,
                options.timeout_ms,
-               options.password.empty() ? nullptr : options.password.c_str())
-
+               options.password.empty() ? nullptr : options.password.c_str()),
+        seek(client)
 {
        screen_manager.Init(&client);
 
@@ -58,7 +58,7 @@ Instance::~Instance()
 void
 Instance::Run()
 {
-       screen_manager.Update(client);
+       screen_manager.Update(client, seek);
 
        g_main_loop_run(main_loop);
 }
index 5839ef3..fb0c202 100644 (file)
@@ -21,6 +21,7 @@
 #define NCMPC_INSTANCE_HXX
 
 #include "mpdclient.hxx"
+#include "DelayedSeek.hxx"
 #include "screen.hxx"
 
 typedef struct _GMainLoop GMainLoop;
@@ -33,6 +34,8 @@ class Instance {
 
        struct mpdclient client;
 
+       DelayedSeek seek;
+
        ScreenManager screen_manager;
 
 public:
@@ -50,6 +53,10 @@ public:
                return client;
        }
 
+       auto &GetSeek() noexcept {
+               return seek;
+       }
+
        auto &GetScreenManager() {
                return screen_manager;
        }
index f9c09c9..735af05 100644 (file)
@@ -63,6 +63,7 @@ static const guint update_interval = 500;
 
 #define BUFSIZE 1024
 
+static Instance *global_instance;
 static struct mpdclient *mpd = nullptr;
 static GMainLoop *main_loop;
 static guint reconnect_source_id, update_source_id;
@@ -148,7 +149,7 @@ do_mpd_update()
                update_xterm_title();
 #endif
 
-       screen->Update(*mpd);
+       screen->Update(*mpd, global_instance->GetSeek());
        mpd->events = (enum mpd_idle)0;
 }
 
@@ -218,7 +219,7 @@ mpdclient_lost_callback()
 {
        assert(reconnect_source_id == 0);
 
-       screen->Update(*mpd);
+       screen->Update(*mpd, global_instance->GetSeek());
 
        reconnect_source_id = g_timeout_add_seconds(1, timer_reconnect, nullptr);
 }
@@ -235,7 +236,7 @@ mpdclient_idle_callback(gcc_unused unsigned events)
                update_xterm_title();
 #endif
 
-       screen->Update(*mpd);
+       screen->Update(*mpd, global_instance->GetSeek());
        auto_update_timer();
 }
 
@@ -258,7 +259,7 @@ void begin_input_event()
 
 void end_input_event()
 {
-       screen->Update(*mpd);
+       screen->Update(*mpd, global_instance->GetSeek());
        mpd->events = (enum mpd_idle)0;
 
        auto_update_timer();
@@ -272,7 +273,7 @@ do_input_event(Command cmd)
                return false;
        }
 
-       screen->OnCommand(*mpd, cmd);
+       screen->OnCommand(*mpd, global_instance->GetSeek(), cmd);
 
        if (cmd == Command::VOLUME_UP || cmd == Command::VOLUME_DOWN)
                /* make sure we don't update the volume yet */
@@ -286,7 +287,7 @@ do_input_event(Command cmd)
 void
 do_mouse_event(Point p, mmask_t bstate)
 {
-       screen->OnMouse(*mpd, p, bstate);
+       screen->OnMouse(*mpd, global_instance->GetSeek(), p, bstate);
 }
 
 #endif
@@ -366,6 +367,7 @@ main(int argc, const char *argv[])
 
        /* create the global Instance */
        Instance instance;
+       global_instance = &instance;
        main_loop = instance.GetMainLoop();
        mpd = &instance.GetClient();
        screen = &instance.GetScreenManager();
@@ -393,8 +395,6 @@ main(int argc, const char *argv[])
 
        /* cleanup */
 
-       cancel_seek_timer();
-
        disable_update_timer();
 
        if (reconnect_source_id != 0)
index be5c00c..7d1b116 100644 (file)
@@ -23,7 +23,7 @@
 #include "Styles.hxx"
 #include "i18n.h"
 #include "strfsong.hxx"
-#include "player_command.hxx"
+#include "DelayedSeek.hxx"
 #include "time_format.hxx"
 #include "util/StringUTF8.hxx"
 
@@ -87,7 +87,8 @@ format_bitrate(char *p, size_t max_length, const struct mpd_status *status)
 
 void
 StatusBar::Update(const struct mpd_status *status,
-                 const struct mpd_song *song)
+                 const struct mpd_song *song,
+                 const DelayedSeek &seek) noexcept
 {
        const auto state = status == nullptr
                ? MPD_STATE_UNKNOWN
@@ -113,9 +114,8 @@ StatusBar::Update(const struct mpd_status *status,
                : 0;
 
        if (state == MPD_STATE_PLAY || state == MPD_STATE_PAUSE) {
-               unsigned elapsed_time = seek_id >= 0 &&
-                       seek_id == mpd_status_get_song_id(status)
-                       ? (unsigned)seek_target_time
+               unsigned elapsed_time = seek.IsSeeking(mpd_status_get_song_id(status))
+                       ? seek.GetTime()
                        : mpd_status_get_elapsed_time(status);
                const unsigned total_time = mpd_status_get_total_time(status);
 
index 196df9b..32fc3d7 100644 (file)
@@ -33,6 +33,7 @@
 
 struct mpd_status;
 struct mpd_song;
+class DelayedSeek;
 
 class StatusBar {
        Window window;
@@ -67,7 +68,8 @@ public:
 
        void OnResize(Point p, unsigned width);
        void Update(const struct mpd_status *status,
-                   const struct mpd_song *song);
+                   const struct mpd_song *song,
+                   const DelayedSeek &seek) noexcept;
        void Paint() const;
 
 private:
index 83476bd..d28cd14 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include "player_command.hxx"
+#include "DelayedSeek.hxx"
 #include "Command.hxx"
 #include "mpdclient.hxx"
 #include "Options.hxx"
 
 #include <glib.h>
 
-int seek_id = -1;
-int seek_target_time;
-
-static guint seek_source_id;
-
-static void
-commit_seek(struct mpdclient &c)
-{
-       if (seek_id < 0)
-               return;
-
-       struct mpd_connection *connection = c.GetConnection();
-       if (connection == nullptr) {
-               seek_id = -1;
-               return;
-       }
-
-       if (c.song != nullptr && (unsigned)seek_id == mpd_song_get_id(c.song))
-               if (!mpd_run_seek_id(connection, seek_id, seek_target_time))
-                       c.HandleError();
-
-       seek_id = -1;
-}
-
-/**
- * This timer is invoked after seeking when the user hasn't typed a
- * key for 500ms.  It is used to do the real seeking.
- */
-static gboolean
-seek_timer(gpointer data)
-{
-       auto &c = *(struct mpdclient *)data;
-
-       seek_source_id = 0;
-       commit_seek(c);
-       return false;
-}
-
-static void
-schedule_seek_timer(struct mpdclient &c)
-{
-       assert(seek_source_id == 0);
-
-       seek_source_id = g_timeout_add(500, seek_timer, &c);
-}
-
-void
-cancel_seek_timer()
-{
-       if (seek_source_id != 0) {
-               g_source_remove(seek_source_id);
-               seek_source_id = 0;
-       }
-}
-
-static bool
-setup_seek(struct mpdclient &c)
-{
-       if (!c.playing_or_paused)
-               return false;
-
-       if (seek_id != (int)mpd_status_get_song_id(c.status)) {
-               seek_id = mpd_status_get_song_id(c.status);
-               seek_target_time = mpd_status_get_elapsed_time(c.status);
-       }
-
-       schedule_seek_timer(c);
-
-       return true;
-}
-
 bool
-handle_player_command(struct mpdclient &c, Command cmd)
+handle_player_command(struct mpdclient &c, DelayedSeek &seek, Command cmd)
 {
        if (!c.IsConnected() || c.status == nullptr)
                return false;
 
-       cancel_seek_timer();
+       seek.Cancel();
 
        switch(cmd) {
                struct mpd_connection *connection;
@@ -134,12 +64,7 @@ handle_player_command(struct mpdclient &c, Command cmd)
                mpdclient_cmd_crop(&c);
                break;
        case Command::SEEK_FORWARD:
-               if (!setup_seek(c))
-                       break;
-
-               seek_target_time += options.seek_time;
-               if (seek_target_time > (int)mpd_status_get_total_time(c.status))
-                       seek_target_time = mpd_status_get_total_time(c.status);
+               seek.Seek(options.seek_time);
                break;
 
        case Command::TRACK_NEXT:
@@ -151,12 +76,7 @@ handle_player_command(struct mpdclient &c, Command cmd)
                        c.HandleError();
                break;
        case Command::SEEK_BACKWARD:
-               if (!setup_seek(c))
-                       break;
-
-               seek_target_time -= options.seek_time;
-               if (seek_target_time < 0)
-                       seek_target_time = 0;
+               seek.Seek(-int(options.seek_time));
                break;
 
        case Command::TRACK_PREVIOUS:
index fcd6b61..0090226 100644 (file)
 
 enum class Command : unsigned;
 struct mpdclient;
-
-extern int seek_id;
-extern int seek_target_time;
-
-/**
- * Call this before exiting; it will unschedule the timer for delayed
- * seeking.
- */
-void
-cancel_seek_timer();
+class DelayedSeek;
 
 bool
-handle_player_command(struct mpdclient &c, Command cmd);
+handle_player_command(struct mpdclient &c, DelayedSeek &seek, Command cmd);
 
 #endif
index 5a4626d..e86d65a 100644 (file)
@@ -28,6 +28,7 @@
 #include "charset.hxx"
 #include "mpdclient.hxx"
 #include "Options.hxx"
+#include "DelayedSeek.hxx"
 #include "player_command.hxx"
 #include "SongPage.hxx"
 #include "LyricsPage.hxx"
@@ -131,7 +132,7 @@ ScreenManager::NextMode(struct mpdclient &c, int offset)
 }
 
 void
-ScreenManager::Update(struct mpdclient &c)
+ScreenManager::Update(struct mpdclient &c, const DelayedSeek &seek) noexcept
 {
        const unsigned events = c.events;
 
@@ -206,8 +207,8 @@ ScreenManager::Update(struct mpdclient &c)
        unsigned elapsed;
        if (c.status == nullptr)
                elapsed = 0;
-       else if (seek_id >= 0 && seek_id == mpd_status_get_song_id(c.status))
-               elapsed = seek_target_time;
+       else if (seek.IsSeeking(mpd_status_get_song_id(c.status)))
+               elapsed = seek.GetTime();
        else
                elapsed = mpd_status_get_elapsed_time(c.status);
 
@@ -217,7 +218,7 @@ ScreenManager::Update(struct mpdclient &c)
 
        progress_bar.Set(elapsed, duration);
 
-       status_bar.Update(c.status, c.song);
+       status_bar.Update(c.status, c.song, seek);
 
        for (auto &i : pages)
                i.second->AddPendingEvents(events);
@@ -229,12 +230,12 @@ ScreenManager::Update(struct mpdclient &c)
 }
 
 void
-ScreenManager::OnCommand(struct mpdclient &c, Command cmd)
+ScreenManager::OnCommand(struct mpdclient &c, DelayedSeek &seek, Command cmd)
 {
        if (current_page->second->OnCommand(c, cmd))
                return;
 
-       if (handle_player_command(c, cmd))
+       if (handle_player_command(c, seek, cmd))
                return;
 
        const auto *new_page = PageByCommand(cmd);
@@ -277,7 +278,8 @@ ScreenManager::OnCommand(struct mpdclient &c, Command cmd)
 #ifdef HAVE_GETMOUSE
 
 bool
-ScreenManager::OnMouse(struct mpdclient &c, Point p, mmask_t bstate)
+ScreenManager::OnMouse(struct mpdclient &c, DelayedSeek &seek,
+                      Point p, mmask_t bstate)
 {
        if (current_page->second->OnMouse(c, p - GetMainPosition(),
                                          bstate))
@@ -285,7 +287,7 @@ ScreenManager::OnMouse(struct mpdclient &c, Point p, mmask_t bstate)
 
        /* if button 2 was pressed switch screen */
        if (bstate & BUTTON2_CLICKED) {
-               OnCommand(c, Command::SCREEN_NEXT);
+               OnCommand(c, seek, Command::SCREEN_NEXT);
                return true;
        }
 
index b0ed5ae..52afbc0 100644 (file)
@@ -41,6 +41,7 @@ enum class Command : unsigned;
 struct mpdclient;
 struct PageMeta;
 class Page;
+class DelayedSeek;
 
 class ScreenManager {
        struct Layout {
@@ -128,11 +129,12 @@ public:
        void PaintTopWindow();
        void Paint(bool main_dirty);
 
-       void Update(struct mpdclient &c);
-       void OnCommand(struct mpdclient &c, Command cmd);
+       void Update(struct mpdclient &c, const DelayedSeek &seek) noexcept;
+       void OnCommand(struct mpdclient &c, DelayedSeek &seek, Command cmd);
 
 #ifdef HAVE_GETMOUSE
-       bool OnMouse(struct mpdclient &c, Point p, mmask_t bstate);
+       bool OnMouse(struct mpdclient &c, DelayedSeek &seek,
+                    Point p, mmask_t bstate);
 #endif
 
 private:
index 3a9a537..acc053d 100644 (file)
@@ -22,7 +22,6 @@
 #include "config.h"
 #include "mpdclient.hxx"
 #include "Options.hxx"
-#include "player_command.hxx"
 
 #include <mpd/client.h>