1 /*
2    Vimpc
3    Copyright (C) 2010 - 2012 Nathan Sweetman
4 
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18    mpdclient.cpp - provides interaction with the music player daemon
19    */
20 
21 #include "mpdclient.hpp"
22 
23 #include "assert.hpp"
24 #include "events.hpp"
25 #include "screen.hpp"
26 #include "settings.hpp"
27 #include "vimpc.hpp"
28 
29 #include "buffer/playlist.hpp"
30 #include "buffer/list.hpp"
31 #include "mode/mode.hpp"
32 #include "window/error.hpp"
33 
34 #include <mpd/client.h>
35 #include <sys/time.h>
36 #include <poll.h>
37 #include <unistd.h>
38 
39 #include <list>
40 #include <signal.h>
41 #include <sys/types.h>
42 
43 using namespace Mpc;
44 
45 #define MPDCOMMAND
46 //#define _DEBUG_ASSERT_ON_ERROR
47 //#define _DEBUG_BREAK_ON_ERROR
48 
49 static std::list<FUNCTION<void()> >       Queue;
50 static Mutex                              QueueMutex;
51 static Atomic(bool)                       Running(true);
52 static ConditionVariable                  Condition;
53 
54 
55 // Helper functions
SecondsToMinutes(uint32_t duration)56 uint32_t Mpc::SecondsToMinutes(uint32_t duration)
57 {
58    return static_cast<uint32_t>(duration / 60);
59 }
60 
RemainingSeconds(uint32_t duration)61 uint32_t Mpc::RemainingSeconds(uint32_t duration)
62 {
63    return (duration - (SecondsToMinutes(duration) * 60));
64 }
65 
66 
CommandList(Mpc::Client & client,bool condition)67 CommandList::CommandList(Mpc::Client & client, bool condition) :
68    condition_(condition),
69    client_   (client)
70 {
71    Debug("Commandlist object construct");
72 
73    if (condition_)
74    {
75       client_.StartCommandList();
76    }
77 }
78 
~CommandList()79 CommandList::~CommandList()
80 {
81    Debug("Commandlist object destruct");
82 
83    if (condition_)
84    {
85       client_.SendCommandList();
86    }
87 }
88 
89 
90 // Mpc::Client Implementation
Client(Main::Vimpc * vimpc,Main::Settings & settings,Mpc::Lists & lists,Ui::Screen & screen)91 Client::Client(Main::Vimpc * vimpc, Main::Settings & settings, Mpc::Lists & lists, Ui::Screen & screen) :
92    vimpc_                (vimpc),
93    settings_             (settings),
94    connection_           (NULL),
95    fd_                   (-1),
96 
97    hostname_             (""),
98    port_                 (0),
99    versionMajor_         (-1),
100    versionMinor_         (-1),
101    versionPatch_         (-1),
102    timeSinceUpdate_      (0),
103    retried_              (true),
104    ready_                (false),
105 
106    volume_               (100),
107    mVolume_              (100),
108    mute_                 (false),
109    updating_             (false),
110    random_               (false),
111    repeat_               (false),
112    single_               (false),
113    consume_              (false),
114    crossfade_            (false),
115    crossfadeTime_        (0),
116    elapsed_              (0),
117    state_                (MPD_STATE_UNKNOWN),
118    mpdstate_             (MPD_STATE_UNKNOWN),
119 
120    currentSong_          (NULL),
121    currentStatus_        (NULL),
122    currentSongId_        (-1),
123    totalNumberOfSongs_   (0),
124    currentSongURI_       (""),
125    currentState_         ("Disconnected"),
126 
127    lists_                (&lists),
128    loadedList_           (""),
129 
130    screen_               (screen),
131    queueVersion_         (-1),
132    oldVersion_           (-1),
133    forceUpdate_          (true),
134    listMode_             (false),
135    idleMode_             (false),
136    queueUpdate_          (false),
137    autoscroll_           (false),
138    clientThread_         (Thread(&Client::ClientQueueExecutor, this, this))
139 {
140    screen_.RegisterProgressCallback([this] (double Value) { SeekToPercent(Value); });
141 
142    Main::Vimpc::EventHandler(Event::PlaylistContentsForRemove, [this] (EventData const & Data)
143    {
144       Mpc::CommandList list(*this);
145 
146       for (auto uri : Data.uris)
147       {
148          Mpc::Song * song = Main::Library().Song(uri);
149 
150          int const PlaylistIndex = Main::Playlist().Index(song);
151 
152          if (PlaylistIndex >= 0)
153          {
154             Delete(PlaylistIndex);
155          }
156       }
157    });
158 }
159 
~Client()160 Client::~Client()
161 {
162    Running = false;
163 
164    clientThread_.join();
165 
166    if (currentStatus_ != NULL)
167    {
168       mpd_status_free(currentStatus_);
169       currentStatus_ = NULL;
170    }
171 
172    if (currentSong_ != NULL)
173    {
174       mpd_song_free(currentSong_);
175       currentSong_ = NULL;
176    }
177 
178    DeleteConnection();
179 }
180 
QueueCommand(FUNCTION<void ()> const & function)181 void Client::QueueCommand(FUNCTION<void()> const & function)
182 {
183    UniqueLock<Mutex> Lock(QueueMutex);
184    Queue.push_back(function);
185    Condition.notify_all();
186 }
187 
WaitForCompletion()188 void Client::WaitForCompletion()
189 {
190    QueueMutex.lock();
191    int Count = Queue.size();
192    QueueMutex.unlock();
193 
194    while (Count != 0)
195    {
196       usleep(20 * 1000);
197       UniqueLock<Mutex> Lock(QueueMutex);
198       Count = Queue.size();
199    }
200 }
201 
202 
Connect(std::string const & hostname,uint16_t port,uint32_t timeout_ms)203 void Client::Connect(std::string const & hostname, uint16_t port, uint32_t timeout_ms)
204 {
205    QueueCommand([this, hostname, port, timeout_ms] () { ConnectImpl(hostname, port, timeout_ms); });
206    //ConnectImpl(hostname, port, timeout_ms);
207 }
208 
ConnectImpl(std::string const & hostname,uint16_t port,uint32_t timeout_ms)209 void Client::ConnectImpl(std::string const & hostname, uint16_t port, uint32_t timeout_ms)
210 {
211    std::string connect_hostname = hostname;
212    uint16_t    connect_port     = port;
213    uint32_t    connect_timeout  = timeout_ms;
214    std::string connect_password = "";
215 
216    DeleteConnection();
217 
218    if (connect_hostname.empty() == true)
219    {
220       char * const host_env = getenv("MPD_HOST");
221 
222       if (host_env != NULL)
223       {
224          connect_hostname = host_env;
225 
226          size_t const pos = connect_hostname.find_last_of("@");
227 
228          if (pos != connect_hostname.npos)
229          {
230             connect_password = connect_hostname.substr(0, pos);
231             connect_hostname = connect_hostname.substr(pos + 1);
232          }
233       }
234       else
235       {
236          connect_hostname = "localhost";
237       }
238    }
239 
240    if (port == 0)
241    {
242       char * const port_env = getenv("MPD_PORT");
243 
244       if (port_env != NULL)
245       {
246          connect_port = atoi(port_env);
247       }
248       else
249       {
250          connect_port = 0;
251       }
252    }
253 
254    if (timeout_ms == 0)
255    {
256       char * const timeout_env = getenv("MPD_TIMEOUT");
257 
258       if (settings_.Get(Setting::Timeout) != "0")
259       {
260          Debug("Client::Connect timeout " + settings_.Get(Setting::Timeout));
261          connect_timeout = atoi(settings_.Get(Setting::Timeout).c_str());
262       }
263       else if (timeout_env != NULL)
264       {
265          connect_timeout = atoi(timeout_env);
266       }
267 
268       connect_timeout *= 1000;
269    }
270 
271    // Connecting may take a long time as this is a single threaded application
272    // and the mpd connect is a blocking call, so be sure to update the screen
273    // first to let the user know that something is happening
274    currentState_ = "Connecting";
275    StateEvent();
276 
277    hostname_   = connect_hostname;
278    port_       = connect_port;
279    connection_ = NULL;
280 
281    EventData HostData;
282    HostData.hostname = hostname_;
283    HostData.port     = port_;
284    Main::Vimpc::CreateEvent(Event::ChangeHost, HostData);
285 
286    //! \TODO make the connection async
287    Debug("Client::Connecting to %s:%u - timeout %u", connect_hostname.c_str(), connect_port, connect_timeout);
288    connection_ = mpd_connection_new(connect_hostname.c_str(), connect_port, connect_timeout);
289 
290    CheckError();
291 
292    if (Connected() == true)
293    {
294       fd_ = mpd_connection_get_fd(connection_);
295 
296       Debug("Client::Connected.");
297 
298       EventData Data;
299       Main::Vimpc::CreateEvent(Event::Connected, Data);
300       Main::Vimpc::CreateEvent(Event::Repaint,   Data);
301 
302       GetVersion();
303 
304       if (connect_password != "")
305       {
306          Password(connect_password);
307       }
308 
309       elapsed_ = 0;
310 
311       if (IsPasswordRequired() == false)
312       {
313          Initialise();
314       }
315       else
316       {
317          StateEvent();
318          EventData Data;
319          Main::Vimpc::CreateEvent(Event::RequirePassword, Data);
320       }
321    }
322    else
323    {
324       Error(ErrorNumber::ClientNoConnection, "Failed to connect to server, please ensure it is running and type :connect <server> [port]");
325    }
326 }
327 
Initialise()328 void Client::Initialise()
329 {
330    UpdateStatus();
331    StateEvent();
332 
333    GetAllMetaInformation();
334    UpdateCurrentSong();
335 
336    if (Connected() == true)
337    {
338       ready_   = true;
339       retried_ = false;
340    }
341 }
342 
343 
Disconnect()344 void Client::Disconnect()
345 {
346    QueueCommand([this] ()
347    {
348       if (Connected() == true)
349       {
350          Debug("Client::Disconnect");
351          DeleteConnection();
352       }
353    });
354 }
355 
Reconnect()356 void Client::Reconnect()
357 {
358    QueueCommand([this] ()
359    {
360       Debug("Client::Reconnect");
361       Disconnect();
362       Connect(hostname_, port_);
363    });
364 }
365 
Password(std::string const & password)366 void Client::Password(std::string const & password)
367 {
368    QueueCommand([this, password] ()
369    {
370       ClearCommand();
371 
372       if (Connected() == true)
373       {
374          Debug("Client::Sending password");
375          mpd_send_password(connection_, password.c_str());
376       }
377       else
378       {
379          ErrorString(ErrorNumber::ClientNoConnection);
380       }
381    });
382 }
383 
Connected() const384 bool Client::Connected() const
385 {
386    return (connection_ != NULL);
387 }
388 
389 
Play(uint32_t const playId)390 void Client::Play(uint32_t const playId)
391 {
392    QueueCommand([this, playId] ()
393    {
394       ClearCommand();
395 
396       if (Connected() == true)
397       {
398          Debug("Client::Play position %u", playId);
399 
400          if (mpd_run_play_pos(connection_, playId) == true)
401          {
402             currentSongId_ = playId;
403             EventData IdData; IdData.id = currentSongId_;
404             Main::Vimpc::CreateEvent(Event::CurrentSongId, IdData);
405 
406             autoscroll_ = false;
407             Main::Vimpc::CreateEvent(Event::Autoscroll,    IdData);
408 
409             state_ = MPD_STATE_PLAY;
410             elapsed_ = 0;
411             timeSinceUpdate_ = 0;
412             StateEvent();
413          }
414       }
415       else
416       {
417          ErrorString(ErrorNumber::ClientNoConnection);
418       }
419    });
420 }
421 
422 
AddComplete()423 void Client::AddComplete()
424 {
425    QueueCommand([this] ()
426    {
427       if ((state_ == MPD_STATE_STOP) && (settings_.Get(Setting::PlayOnAdd) == true))
428       {
429          Debug("Client::Playing start of playlist");
430          Play(0);
431       }
432    });
433 }
434 
Pause()435 void Client::Pause()
436 {
437    QueueCommand([this] ()
438    {
439       ClearCommand();
440 
441       if (Connected() == true)
442       {
443          Debug("Client::Toggling pause state");
444 
445          if (mpd_run_toggle_pause(connection_) == true)
446          {
447             if (state_ == MPD_STATE_PLAY)
448             {
449                state_ = MPD_STATE_PAUSE;
450             }
451             else if (state_ == MPD_STATE_PAUSE)
452             {
453                state_ = MPD_STATE_PLAY;
454             }
455 
456             timeSinceUpdate_ = 0;
457             elapsed_ = mpdelapsed_;
458             StateEvent();
459          }
460       }
461       else
462       {
463          ErrorString(ErrorNumber::ClientNoConnection);
464       }
465    });
466 }
467 
Stop()468 void Client::Stop()
469 {
470    QueueCommand([this] ()
471    {
472       ClearCommand();
473 
474       if (Connected() == true)
475       {
476          Debug("Client::Stopping playback");
477 
478          if (mpd_run_stop(connection_) == true)
479          {
480             state_ = MPD_STATE_STOP;
481             StateEvent();
482          }
483       }
484       else
485       {
486          ErrorString(ErrorNumber::ClientNoConnection);
487       }
488    });
489 }
490 
Next()491 void Client::Next()
492 {
493    QueueCommand([this] ()
494    {
495       ClearCommand();
496 
497       if (Connected() == true)
498       {
499          Debug("Client::Next song");
500          mpd_send_next(connection_);
501 
502          autoscroll_ = true;
503       }
504       else
505       {
506          ErrorString(ErrorNumber::ClientNoConnection);
507       }
508    });
509 }
510 
Previous()511 void Client::Previous()
512 {
513    QueueCommand([this] ()
514    {
515       ClearCommand();
516 
517       if (Connected() == true)
518       {
519          Debug("Client::Previous song");
520          mpd_send_previous(connection_);
521 
522          autoscroll_ = true;
523       }
524       else
525       {
526          ErrorString(ErrorNumber::ClientNoConnection);
527       }
528    });
529 }
530 
Seek(int32_t Offset)531 void Client::Seek(int32_t Offset)
532 {
533    QueueCommand([this, Offset] ()
534    {
535       ClearCommand();
536 
537       if (Connected() == true)
538       {
539          if (currentSongId_ >= 0)
540          {
541             Debug("Client::Seek to time %d", elapsed_ + Offset);
542             mpd_send_seek_pos(connection_, currentSongId_, elapsed_ + Offset);
543          }
544       }
545       else
546       {
547          ErrorString(ErrorNumber::ClientNoConnection);
548       }
549    });
550 }
551 
SeekTo(uint32_t Time)552 void Client::SeekTo(uint32_t Time)
553 {
554    QueueCommand([this, Time] ()
555    {
556       ClearCommand();
557 
558       if (Connected() == true)
559       {
560          if (currentSongId_ >= 0)
561          {
562             Debug("Client::Seek to time %u", Time);
563             mpd_send_seek_pos(connection_, currentSongId_, Time);
564          }
565       }
566       else
567       {
568          ErrorString(ErrorNumber::ClientNoConnection);
569       }
570    });
571 }
572 
SeekToPercent(double Percent)573 void Client::SeekToPercent(double Percent)
574 {
575    QueueCommand([this, Percent] ()
576    {
577       if (currentSong_)
578       {
579          Debug("Client::Seek to percent %d%%", static_cast<int32_t>(Percent * 100));
580 
581          uint32_t const duration = mpd_song_get_duration(currentSong_);
582          SeekTo(static_cast<uint32_t>(Percent * duration));
583       }
584    });
585 }
586 
587 
SetRandom(bool const random)588 void Client::SetRandom(bool const random)
589 {
590    QueueCommand([this, random] ()
591    {
592       ClearCommand();
593 
594       if (Connected() == true)
595       {
596          Debug("Client::Set random state %d", static_cast<int32_t>(random));
597 
598          if (mpd_run_random(connection_, random) == true)
599          {
600             SetStateAndEvent(Event::Random, random_, random);
601          }
602       }
603       else
604       {
605          ErrorString(ErrorNumber::ClientNoConnection);
606       }
607    });
608 }
609 
SetSingle(bool const single)610 void Client::SetSingle(bool const single)
611 {
612    QueueCommand([this, single] ()
613    {
614       ClearCommand();
615 
616       if (Connected() == true)
617       {
618          Debug("Client::Set single state %d", static_cast<int32_t>(single));
619 
620          if (mpd_run_single(connection_, single) == true)
621          {
622             SetStateAndEvent(Event::Single, single_, single);
623          }
624       }
625       else
626       {
627          ErrorString(ErrorNumber::ClientNoConnection);
628       }
629    });
630 }
631 
SetConsume(bool const consume)632 void Client::SetConsume(bool const consume)
633 {
634    QueueCommand([this, consume] ()
635    {
636       ClearCommand();
637 
638       if (Connected() == true)
639       {
640          Debug("Client::Set consume state %d", static_cast<int32_t>(consume));
641 
642          if (mpd_run_consume(connection_, consume) == true)
643          {
644             SetStateAndEvent(Event::Consume, consume_, consume);
645          }
646       }
647       else
648       {
649          ErrorString(ErrorNumber::ClientNoConnection);
650       }
651    });
652 }
653 
SetRepeat(bool const repeat)654 void Client::SetRepeat(bool const repeat)
655 {
656    QueueCommand([this, repeat] ()
657    {
658       ClearCommand();
659 
660       if (Connected() == true)
661       {
662          Debug("Client::Set repeat state %d", static_cast<int32_t>(repeat));
663 
664          if (mpd_run_repeat(connection_, repeat) == true)
665          {
666             SetStateAndEvent(Event::Repeat, repeat_, repeat);
667          }
668       }
669       else
670       {
671          ErrorString(ErrorNumber::ClientNoConnection);
672       }
673    });
674 }
675 
SetCrossfade(bool crossfade)676 void Client::SetCrossfade(bool crossfade)
677 {
678    QueueCommand([this, crossfade] ()
679    {
680       if (crossfade == true)
681       {
682          SetCrossfade(crossfadeTime_);
683       }
684       else
685       {
686          SetCrossfade(static_cast<uint32_t>(0));
687       }
688    });
689 }
690 
SetCrossfade(uint32_t crossfade)691 void Client::SetCrossfade(uint32_t crossfade)
692 {
693    QueueCommand([this, crossfade] ()
694    {
695       ClearCommand();
696 
697       if (Connected() == true)
698       {
699          Debug("Client::Set crossfade time %u", crossfade);
700 
701          if (mpd_run_crossfade(connection_, crossfade) == true)
702          {
703             crossfade_ = (crossfade != 0);
704 
705             if (crossfade_ == true)
706             {
707                crossfadeTime_ = crossfade;
708                EventData Data; Data.value = crossfade;
709                Main::Vimpc::CreateEvent(Event::CrossfadeTime, Data);
710             }
711 
712             EventData Data; Data.state = crossfade_;
713             Main::Vimpc::CreateEvent(Event::Crossfade, Data);
714          }
715       }
716       else
717       {
718          ErrorString(ErrorNumber::ClientNoConnection);
719       }
720    });
721 }
722 
SetVolume(uint32_t volume)723 void Client::SetVolume(uint32_t volume)
724 {
725    QueueCommand([this, volume] ()
726    {
727       ClearCommand();
728 
729       if (Connected() == true)
730       {
731          Debug("Client::Set volume %u", volume);
732 
733          if (mpd_run_set_volume(connection_, volume) == true)
734          {
735             volume_ = volume;
736 
737             EventData Data; Data.value = volume;
738             Main::Vimpc::CreateEvent(Event::Volume, Data);
739          }
740       }
741       else
742       {
743          ErrorString(ErrorNumber::ClientNoConnection);
744       }
745    });
746 }
747 
SetMute(bool mute)748 void Client::SetMute(bool mute)
749 {
750    QueueCommand([this, mute] ()
751    {
752       if ((mute == true) && (mute_ == false))
753       {
754          mVolume_ = volume_;
755          SetVolume(0);
756       }
757       else if ((mute == false) && (mute_ == true))
758       {
759          SetVolume(mVolume_);
760       }
761 
762       mute_ = mute;
763       EventData Data; Data.state = mute_;
764       Main::Vimpc::CreateEvent(Event::Mute, Data);
765    });
766 }
767 
DeltaVolume(int32_t Delta)768 void Client::DeltaVolume(int32_t Delta)
769 {
770    QueueCommand([this, Delta] ()
771    {
772       ClearCommand();
773 
774       if (Connected() == true)
775       {
776          int CurrentVolume = volume_ + Delta;
777 
778          if (CurrentVolume < 0)
779          {
780             CurrentVolume = 0;
781          }
782          else if (CurrentVolume > 100)
783          {
784             CurrentVolume = 100;
785          }
786 
787          Debug("Client::Delta volume %d %d", Delta, CurrentVolume);
788 
789          if (mpd_run_set_volume(connection_, CurrentVolume) == true)
790          {
791             volume_ = CurrentVolume;
792             EventData Data; Data.value = CurrentVolume;
793             Main::Vimpc::CreateEvent(Event::Volume, Data);
794          }
795       }
796    });
797 }
798 
ToggleRandom()799 void Client::ToggleRandom()
800 {
801    QueueCommand([this] ()
802    {
803       ClearCommand();
804 
805       if (Connected() == true)
806       {
807          Debug("Client::Toggle random state %d", static_cast<int32_t>(!random_));
808 
809          if (mpd_run_random(connection_, !random_) == true)
810          {
811             SetStateAndEvent(Event::Random, random_, !random_);
812          }
813       }
814       else
815       {
816          ErrorString(ErrorNumber::ClientNoConnection);
817       }
818    });
819 }
820 
ToggleSingle()821 void Client::ToggleSingle()
822 {
823    QueueCommand([this] ()
824    {
825       ClearCommand();
826 
827       if (Connected() == true)
828       {
829          Debug("Client::Toggle single state %d", static_cast<int32_t>(!single_));
830 
831          if (mpd_run_single(connection_, !single_) == true)
832          {
833             SetStateAndEvent(Event::Single, single_, !single_);
834          }
835       }
836       else
837       {
838          ErrorString(ErrorNumber::ClientNoConnection);
839       }
840    });
841 }
842 
ToggleConsume()843 void Client::ToggleConsume()
844 {
845    QueueCommand([this] ()
846    {
847       ClearCommand();
848 
849       if (Connected() == true)
850       {
851          Debug("Client::Toggle consume state %d", static_cast<int32_t>(!consume_));
852 
853          if (mpd_run_consume(connection_, !consume_) == true)
854          {
855             SetStateAndEvent(Event::Consume, consume_, !consume_);
856          }
857       }
858       else
859       {
860          ErrorString(ErrorNumber::ClientNoConnection);
861       }
862    });
863 }
864 
865 
ToggleRepeat()866 void Client::ToggleRepeat()
867 {
868    QueueCommand([this] ()
869    {
870       ClearCommand();
871 
872       if (Connected() == true)
873       {
874          Debug("Client::Toggle repeat state %d", static_cast<int32_t>(!repeat_));
875 
876          if (mpd_run_repeat(connection_, !repeat_) == true)
877          {
878             SetStateAndEvent(Event::Repeat, repeat_, !repeat_);
879          }
880       }
881       else
882       {
883          ErrorString(ErrorNumber::ClientNoConnection);
884       }
885    });
886 }
887 
ToggleCrossfade()888 void Client::ToggleCrossfade()
889 {
890    QueueCommand([this] ()
891    {
892       if (crossfade_ == false)
893       {
894          SetCrossfade(crossfadeTime_);
895       }
896       else
897       {
898          SetCrossfade(static_cast<uint32_t>(0));
899       }
900    });
901 }
902 
Shuffle()903 void Client::Shuffle()
904 {
905    QueueCommand([this] ()
906    {
907       ClearCommand();
908 
909       if (Connected() == true)
910       {
911          Debug("Client::Send shuffle");
912          mpd_send_shuffle(connection_);
913       }
914       else
915       {
916          ErrorString(ErrorNumber::ClientNoConnection);
917       }
918    });
919 }
920 
Move(uint32_t position1,uint32_t position2)921 void Client::Move(uint32_t position1, uint32_t position2)
922 {
923    QueueCommand([this, position1, position2] ()
924    {
925       ClearCommand();
926 
927       if (Connected() == true)
928       {
929          Debug("Client::Send move %u %u", position1, position2);
930          mpd_send_move(connection_, position1, position2);
931       }
932       else
933       {
934          ErrorString(ErrorNumber::ClientNoConnection);
935       }
936    });
937 }
938 
Swap(uint32_t position1,uint32_t position2)939 void Client::Swap(uint32_t position1, uint32_t position2)
940 {
941    QueueCommand([this, position1, position2] ()
942    {
943       ClearCommand();
944 
945       if (Connected() == true)
946       {
947          Debug("Client::Send swap %u %u", position1, position2);
948          mpd_send_swap(connection_, position1, position2);
949       }
950       else
951       {
952          ErrorString(ErrorNumber::ClientNoConnection);
953       }
954    });
955 }
956 
957 
CreatePlaylist(std::string const & name)958 void Client::CreatePlaylist(std::string const & name)
959 {
960    QueueCommand([this, name] ()
961    {
962       ClearCommand();
963 
964       if (Connected() == true)
965       {
966          Debug("Client::Send save %s", name.c_str());
967 
968          if (mpd_run_save(connection_, name.c_str()) == true)
969          {
970             EventData Data; Data.name = name;
971             Main::Vimpc::CreateEvent(Event::NewPlaylist, Data);
972             Main::Vimpc::CreateEvent(Event::Repaint,   Data);
973          }
974 
975          Debug("Client::Send clear playlist %s", name.c_str());
976          mpd_run_playlist_clear(connection_, name.c_str());
977       }
978       else
979       {
980          ErrorString(ErrorNumber::ClientNoConnection);
981       }
982    });
983 }
984 
HasPlaylist(std::string const & name)985 bool Client::HasPlaylist(std::string const & name)
986 {
987    for (int i=0; i < lists_->Size(); i++) {
988       if (lists_->Get(i).name_.c_str() == name)
989       {
990          return true;
991       }
992    }
993    return false;
994 }
995 
HasLoadedPlaylist()996 bool Client::HasLoadedPlaylist()
997 {
998    return loadedList_ != "";
999 }
1000 
SaveLoadedPlaylist()1001 void Client::SaveLoadedPlaylist()
1002 {
1003    if (Client::HasLoadedPlaylist())
1004    {
1005       Client::SavePlaylist(loadedList_);
1006    }
1007    else
1008    {
1009       ErrorString(ErrorNumber::NoPlaylistLoaded);
1010    }
1011 }
1012 
SavePlaylist(std::string const & name)1013 void Client::SavePlaylist(std::string const & name)
1014 {
1015    QueueCommand([this, name] ()
1016    {
1017       ClearCommand();
1018 
1019       if (Connected() == true)
1020       {
1021 
1022          if (HasPlaylist(name))
1023          {
1024             Debug("Client::Send remove %s", name.c_str());
1025             mpd_run_rm(connection_, name.c_str());
1026          }
1027 
1028          Debug("Client::Send save %s", name.c_str());
1029          if (mpd_run_save(connection_, name.c_str()) == true)
1030          {
1031             EventData Data; Data.name = name;
1032             Main::Vimpc::CreateEvent(Event::NewPlaylist, Data);
1033          }
1034       }
1035       else
1036       {
1037          ErrorString(ErrorNumber::ClientNoConnection);
1038       }
1039    });
1040 }
1041 
LoadPlaylist(std::string const & name)1042 void Client::LoadPlaylist(std::string const & name)
1043 {
1044    QueueCommand([this, name] ()
1045    {
1046       ClearCommand();
1047 
1048       if (Connected() == true)
1049       {
1050          mpd_run_clear(connection_);
1051          Debug("Client::Send load %s", name.c_str());
1052          mpd_run_load(connection_, name.c_str());
1053          loadedList_ = name;
1054       }
1055       else
1056       {
1057          ErrorString(ErrorNumber::ClientNoConnection);
1058       }
1059    });
1060 }
1061 
AppendPlaylist(std::string const & name)1062 void Client::AppendPlaylist(std::string const & name)
1063 {
1064    QueueCommand([this, name] ()
1065    {
1066       ClearCommand();
1067 
1068       if (Connected() == true)
1069       {
1070          Debug("Client::Send load %s", name.c_str());
1071          mpd_run_load(connection_, name.c_str());
1072          loadedList_ = name;
1073       }
1074       else
1075       {
1076          ErrorString(ErrorNumber::ClientNoConnection);
1077       }
1078    });
1079 }
1080 
RemovePlaylist(std::string const & name)1081 void Client::RemovePlaylist(std::string const & name)
1082 {
1083    QueueCommand([this, name] ()
1084    {
1085       ClearCommand();
1086 
1087       if (Connected() == true)
1088       {
1089          Debug("Client::Send remove %s", name.c_str());
1090          mpd_run_rm(connection_, name.c_str());
1091       }
1092       else
1093       {
1094          ErrorString(ErrorNumber::ClientNoConnection);
1095       }
1096    });
1097 }
1098 
AddToNamedPlaylist(std::string const & name,Mpc::Song * song)1099 void Client::AddToNamedPlaylist(std::string const & name, Mpc::Song * song)
1100 {
1101    std::string URI = song->URI();
1102 
1103    QueueCommand([this, name, URI] ()
1104    {
1105       ClearCommand();
1106 
1107       if (Connected() == true)
1108       {
1109          Debug("Client::Playlist add %s to %s", URI.c_str(), name.c_str());
1110          mpd_send_playlist_add(connection_, name.c_str(), URI.c_str());
1111       }
1112       else
1113       {
1114          ErrorString(ErrorNumber::ClientNoConnection);
1115       }
1116    });
1117 }
1118 
1119 
PlaylistContents(std::string const & name)1120 void Client::PlaylistContents(std::string const & name)
1121 {
1122    QueueCommand([this, name] ()
1123    {
1124       std::string const SongFormat = settings_.Get(Setting::SongFormat);
1125 
1126       ClearCommand();
1127 
1128       if (Connected() == true)
1129       {
1130          Debug("Client::Add songs from playlist %s", name.c_str());
1131 
1132          mpd_send_list_playlist(connection_, name.c_str());
1133 
1134          mpd_song * nextSong = mpd_recv_song(connection_);
1135 
1136          std::vector<std::string> URIs;
1137 
1138          if (nextSong == NULL)
1139          {
1140             ErrorString(ErrorNumber::PlaylistEmpty);
1141          }
1142          else
1143          {
1144             for (; nextSong != NULL; nextSong = mpd_recv_song(connection_))
1145             {
1146                if ((settings_.Get(Setting::ListAllMeta) == false))
1147                {
1148                    Mpc::Song * song = Main::Library().Song(mpd_song_get_uri(nextSong));
1149 
1150                    if (song == NULL)
1151                    {
1152                        song = CreateSong(nextSong);
1153                        // Pre cache the print of the song
1154                        (void) song->FormatString(SongFormat);
1155                        EventData Data; Data.song = song;
1156                        Main::Vimpc::CreateEvent(Event::DatabaseSong, Data);
1157                    }
1158                }
1159 
1160                URIs.push_back(mpd_song_get_uri(nextSong));
1161                mpd_song_free(nextSong);
1162             }
1163 
1164             EventData Data; Data.name = name; Data.uris = URIs;
1165             Main::Vimpc::CreateEvent(Event::PlaylistContents, Data);
1166          }
1167       }
1168    });
1169 }
1170 
PlaylistContentsForRemove(std::string const & name)1171 void Client::PlaylistContentsForRemove(std::string const & name)
1172 {
1173    QueueCommand([this, name] ()
1174    {
1175       ClearCommand();
1176 
1177       if (Connected() == true)
1178       {
1179          Debug("Client::Add songs from playlist %s", name.c_str());
1180 
1181          mpd_send_list_playlist(connection_, name.c_str());
1182 
1183          mpd_song * nextSong = mpd_recv_song(connection_);
1184 
1185          std::vector<std::string> URIs;
1186 
1187          if (nextSong != NULL)
1188          {
1189             for (; nextSong != NULL; nextSong = mpd_recv_song(connection_))
1190             {
1191                URIs.push_back(mpd_song_get_uri(nextSong));
1192                mpd_song_free(nextSong);
1193             }
1194 
1195             EventData Data; Data.name = name; Data.uris = URIs;
1196             Main::Vimpc::CreateEvent(Event::PlaylistContentsForRemove, Data);
1197          }
1198       }
1199    });
1200 }
1201 
SetOutput(Mpc::Output * output,bool enable)1202 void Client::SetOutput(Mpc::Output * output, bool enable)
1203 {
1204    if (enable == true)
1205    {
1206       EnableOutput(output);
1207    }
1208    else
1209    {
1210       DisableOutput(output);
1211    }
1212 }
1213 
EnableOutput(Mpc::Output * output)1214 void Client::EnableOutput(Mpc::Output * output)
1215 {
1216    uint32_t Id = output->Id();
1217 
1218    QueueCommand([this, Id] ()
1219    {
1220       ClearCommand();
1221 
1222       if (Connected() == true)
1223       {
1224          Debug("Client::Enable output %d", Id);
1225 
1226          if (mpd_run_enable_output(connection_, Id) == true)
1227          {
1228             EventData Data; Data.id = Id;
1229             Main::Vimpc::CreateEvent(Event::OutputEnabled, Data);
1230             Main::Vimpc::CreateEvent(Event::Repaint,   Data);
1231          }
1232       }
1233       else
1234       {
1235          ErrorString(ErrorNumber::ClientNoConnection);
1236       }
1237    });
1238 }
1239 
DisableOutput(Mpc::Output * output)1240 void Client::DisableOutput(Mpc::Output * output)
1241 {
1242    uint32_t Id = output->Id();
1243 
1244    QueueCommand([this, Id] ()
1245    {
1246       ClearCommand();
1247 
1248       if (Connected() == true)
1249       {
1250          Debug("Client::Disable output %d", Id);
1251 
1252          if (mpd_run_disable_output(connection_, Id) == true)
1253          {
1254             EventData Data; Data.id = Id;
1255             Main::Vimpc::CreateEvent(Event::OutputDisabled, Data);
1256             Main::Vimpc::CreateEvent(Event::Repaint,   Data);
1257          }
1258       }
1259       else
1260       {
1261          ErrorString(ErrorNumber::ClientNoConnection);
1262       }
1263    });
1264 }
1265 
1266 
Add(Mpc::Song * song)1267 void Client::Add(Mpc::Song * song)
1268 {
1269    if (song != NULL)
1270    {
1271       (void) Add(*song);
1272    }
1273 }
1274 
Add(std::vector<Mpc::Song * > songs)1275 void Client::Add(std::vector<Mpc::Song *> songs)
1276 {
1277    std::vector<std::string> URIs;
1278 
1279    for (auto song : songs)
1280    {
1281       URIs.push_back(song->URI());
1282    }
1283 
1284    QueueCommand([this, URIs] ()
1285    {
1286       ClearCommand();
1287 
1288       if (Connected() == true)
1289       {
1290          if (mpd_command_list_begin(connection_, false) == true)
1291          {
1292             listMode_ = true;
1293 
1294             Debug("Client::List add started");
1295 
1296             for (auto URI : URIs)
1297             {
1298                mpd_send_add(connection_, URI.c_str());
1299             }
1300 
1301             if (mpd_command_list_end(connection_) == true)
1302             {
1303                Debug("Client::List add success");
1304                listMode_ = false;
1305 
1306                for (auto URI : URIs)
1307                {
1308                   EventData Data; Data.uri = URI; Data.pos1 = -1;
1309                   Main::Vimpc::CreateEvent(Event::PlaylistAdd, Data);
1310                }
1311 
1312                EventData Data;
1313                Main::Vimpc::CreateEvent(Event::CommandListSend, Data);
1314                Main::Vimpc::CreateEvent(Event::Repaint,   Data);
1315             }
1316             else
1317             {
1318                CheckError();
1319             }
1320          }
1321          else
1322          {
1323             CheckError();
1324          }
1325       }
1326       else
1327       {
1328          ErrorString(ErrorNumber::ClientNoConnection);
1329       }
1330    });
1331 }
1332 
Add(Mpc::Song & song)1333 void Client::Add(Mpc::Song & song)
1334 {
1335    std::string URI = song.URI();
1336 
1337    QueueCommand([this, URI] ()
1338    {
1339       ClearCommand();
1340 
1341       if (Connected() == true)
1342       {
1343          //Debug("Client::Add song %s", URI.c_str());
1344          mpd_send_add(connection_, URI.c_str());
1345 
1346          EventData Data; Data.uri = URI; Data.pos1 = -1;
1347          Main::Vimpc::CreateEvent(Event::PlaylistAdd, Data);
1348       }
1349       else
1350       {
1351          ErrorString(ErrorNumber::ClientNoConnection);
1352       }
1353    });
1354 }
1355 
Add(Mpc::Song & song,uint32_t position)1356 void Client::Add(Mpc::Song & song, uint32_t position)
1357 {
1358    std::string URI = song.URI();
1359 
1360    QueueCommand([this, URI, position] ()
1361    {
1362       ClearCommand();
1363 
1364       if (Connected() == true)
1365       {
1366          Debug("Client::Add song %s at %u", URI.c_str(), position);
1367          mpd_send_add_id_to(connection_, URI.c_str(), position);
1368 
1369          EventData Data; Data.uri = URI; Data.pos1 = position;
1370          Main::Vimpc::CreateEvent(Event::PlaylistAdd, Data);
1371 
1372          if ((currentSongId_ > -1) && (position <= static_cast<uint32_t>(currentSongId_)))
1373          {
1374             ++currentSongId_;
1375             EventData IdData; IdData.id = currentSongId_;
1376             Main::Vimpc::CreateEvent(Event::CurrentSongId, IdData);
1377          }
1378       }
1379       else
1380       {
1381          ErrorString(ErrorNumber::ClientNoConnection);
1382       }
1383    });
1384 }
1385 
AddAllSongs()1386 void Client::AddAllSongs()
1387 {
1388    QueueCommand([this] ()
1389    {
1390       ClearCommand();
1391 
1392       if (Connected() == true)
1393       {
1394          Debug("Client::Add all songs");
1395          mpd_send_add(connection_, "");
1396       }
1397       else
1398       {
1399          ErrorString(ErrorNumber::ClientNoConnection);
1400       }
1401    });
1402 }
1403 
Add(std::string const & URI)1404 void Client::Add(std::string const & URI)
1405 {
1406    QueueCommand([this, URI] ()
1407    {
1408       ClearCommand();
1409 
1410       if (Connected() == true)
1411       {
1412          Debug("Client::Add uri %s", URI.c_str());
1413          mpd_send_add(connection_, URI.c_str());
1414       }
1415       else
1416       {
1417          ErrorString(ErrorNumber::ClientNoConnection);
1418       }
1419    });
1420 }
1421 
1422 
Delete(uint32_t position)1423 void Client::Delete(uint32_t position)
1424 {
1425    QueueCommand([this, position] ()
1426    {
1427       ClearCommand();
1428 
1429       // There might be an add in the queue, so we can't use the totalNumberOfSongs_ to determine
1430       // whether or not to do a delete
1431       if (Connected() == true) // && (totalNumberOfSongs_ > 0))
1432       {
1433          Debug("Client::Delete position %u", position);
1434          mpd_send_delete(connection_, position);
1435 
1436          if ((currentSongId_ > -1) && (position < static_cast<uint32_t>(currentSongId_)))
1437          {
1438             --currentSongId_;
1439             EventData IdData; IdData.id = currentSongId_;
1440             Main::Vimpc::CreateEvent(Event::CurrentSongId, IdData);
1441          }
1442       }
1443       else if (Connected() == false)
1444       {
1445          ErrorString(ErrorNumber::ClientNoConnection);
1446       }
1447    });
1448 
1449    Main::Playlist().Remove(position, 1);
1450 }
1451 
Delete(uint32_t position1,uint32_t position2)1452 void Client::Delete(uint32_t position1, uint32_t position2)
1453 {
1454    QueueCommand([this, position1, position2] ()
1455    {
1456       // There might be an add in the queue, so we can't use the totalNumberOfSongs_ to determine
1457       // whether or not to do a delete
1458       if (Connected() == true) // && (totalNumberOfSongs_ > 0))
1459       {
1460          // Only use range if MPD is >= 0.16
1461          if ((versionMajor_ == 0) && (versionMinor_ < 16))
1462          {
1463             CommandList list(*this);
1464 
1465             for (uint32_t i = 0; i < (position2 - position1); ++i)
1466             {
1467                Delete(position1);
1468             }
1469          }
1470          else
1471          {
1472             ClearCommand();
1473 
1474             if (Connected() == true)
1475             {
1476                Debug("Client::Delete range %u:%u", position1, position2);
1477                mpd_send_delete_range(connection_, position1, position2);
1478 
1479                if (currentSongId_ > -1)
1480                {
1481                   uint32_t const songId = static_cast<uint32_t>(currentSongId_);
1482 
1483                   if ((position1 < songId) && (position2 < songId))
1484                   {
1485                      currentSongId_ -= position2 - position1;
1486                   }
1487                   else if ((position1 <= songId) && (position2 >= songId))
1488                   {
1489                      currentSongId_ -= (currentSongId_ - position1);
1490                   }
1491 
1492                   EventData IdData; IdData.id = currentSongId_;
1493                   Main::Vimpc::CreateEvent(Event::CurrentSongId, IdData);
1494                }
1495             }
1496          }
1497       }
1498 
1499       if (Connected() == false)
1500       {
1501          ErrorString(ErrorNumber::ClientNoConnection);
1502       }
1503    });
1504 
1505    Main::Playlist().Remove(position1, position2 - position1);
1506 }
1507 
Clear()1508 void Client::Clear()
1509 {
1510    QueueCommand([this] ()
1511    {
1512       ClearCommand();
1513 
1514       if (Connected() == true)
1515       {
1516          Debug("Client::Clear");
1517          mpd_send_clear(connection_);
1518       }
1519       else if (Connected() == false)
1520       {
1521          ErrorString(ErrorNumber::ClientNoConnection);
1522       }
1523    });
1524 }
1525 
1526 
SearchAny(std::string const & search,bool exact)1527 void Client::SearchAny(std::string const & search, bool exact)
1528 {
1529    QueueCommand([this, search, exact] ()
1530    {
1531       ClearCommand();
1532 
1533       if (Connected() == true)
1534       {
1535          Debug("Client::Search any %s - exact %d", search.c_str(), static_cast<int32_t>(exact));
1536          mpd_search_db_songs(connection_, exact);
1537          mpd_search_add_any_tag_constraint(connection_, MPD_OPERATOR_DEFAULT, search.c_str());
1538       }
1539    });
1540 }
1541 
SearchArtist(std::string const & search,bool exact)1542 void Client::SearchArtist(std::string const & search, bool exact)
1543 {
1544    QueueCommand([this, search, exact] ()
1545    {
1546       ClearCommand();
1547 
1548       if (Connected() == true)
1549       {
1550          Debug("Client::Search artist %s - exact %d", search.c_str(), static_cast<int32_t>(exact));
1551          mpd_search_db_songs(connection_, exact);
1552          mpd_search_add_tag_constraint(connection_, MPD_OPERATOR_DEFAULT, MPD_TAG_ARTIST, search.c_str());
1553       }
1554    });
1555 }
1556 
SearchGenre(std::string const & search,bool exact)1557 void Client::SearchGenre(std::string const & search, bool exact)
1558 {
1559    QueueCommand([this, search, exact] ()
1560    {
1561       ClearCommand();
1562 
1563       if (Connected() == true)
1564       {
1565          Debug("Client::Search genre %s - exact %d", search.c_str(), static_cast<int32_t>(exact));
1566          mpd_search_db_songs(connection_, exact);
1567          mpd_search_add_tag_constraint(connection_, MPD_OPERATOR_DEFAULT, MPD_TAG_GENRE, search.c_str());
1568       }
1569    });
1570 }
1571 
1572 
SearchAlbum(std::string const & search,bool exact)1573 void Client::SearchAlbum(std::string const & search, bool exact)
1574 {
1575    QueueCommand([this, search, exact] ()
1576    {
1577       ClearCommand();
1578 
1579       if (Connected() == true)
1580       {
1581          Debug("Client::Search album %s - exact %d", search.c_str(), static_cast<int32_t>(exact));
1582          mpd_search_db_songs(connection_, exact);
1583          mpd_search_add_tag_constraint(connection_, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, search.c_str());
1584       }
1585    });
1586 }
1587 
SearchSong(std::string const & search,bool exact)1588 void Client::SearchSong(std::string const & search, bool exact)
1589 {
1590    QueueCommand([this, search, exact] ()
1591    {
1592       ClearCommand();
1593 
1594       if (Connected() == true)
1595       {
1596          Debug("Client::Search title %s - exact %d", search.c_str(), static_cast<int32_t>(exact));
1597          mpd_search_db_songs(connection_, exact);
1598          mpd_search_add_tag_constraint(connection_, MPD_OPERATOR_DEFAULT, MPD_TAG_TITLE, search.c_str());
1599       }
1600    });
1601 }
1602 
1603 
AddAllSearchResults()1604 void Client::AddAllSearchResults()
1605 {
1606    QueueCommand([this] ()
1607    {
1608       if (Connected())
1609       {
1610          Mpc::Song Song;
1611 
1612          // Start the search
1613          Debug("Client::Add all search results");
1614          mpd_search_commit(connection_);
1615 
1616          // Recv the songs and do some callbacks
1617          mpd_song * nextSong = mpd_recv_song(connection_);
1618 
1619          if (nextSong == NULL)
1620          {
1621             ErrorString(ErrorNumber::FindNoResults);
1622          }
1623          else
1624          {
1625             for (; nextSong != NULL; nextSong = mpd_recv_song(connection_))
1626             {
1627                Song.SetURI(mpd_song_get_uri(nextSong));
1628                Add(Song);
1629                mpd_song_free(nextSong);
1630             }
1631          }
1632       }
1633    });
1634 }
1635 
SearchResults(std::string const & name)1636 void Client::SearchResults(std::string const & name)
1637 {
1638    QueueCommand([this, name] ()
1639    {
1640       std::string const SongFormat = settings_.Get(Setting::SongFormat);
1641 
1642       if (Connected())
1643       {
1644          Mpc::Song Song;
1645 
1646          // Start the search
1647          Debug("Client::Search results via events");
1648          mpd_search_commit(connection_);
1649 
1650          // Recv the songs and do some callbacks
1651          mpd_song * nextSong = mpd_recv_song(connection_);
1652 
1653          if (nextSong == NULL)
1654          {
1655             ErrorString(ErrorNumber::FindNoResults);
1656          }
1657          else
1658          {
1659             EventData Data; Data.name = name;
1660 
1661             for (; nextSong != NULL; nextSong = mpd_recv_song(connection_))
1662             {
1663                if ((settings_.Get(Setting::ListAllMeta) == false))
1664                {
1665                    Mpc::Song * song = Main::Library().Song(mpd_song_get_uri(nextSong));
1666 
1667                    if (song == NULL)
1668                    {
1669                        song = CreateSong(nextSong);
1670                        // Pre cache the print of the song
1671                        (void) song->FormatString(SongFormat);
1672                        EventData Data; Data.song = song;
1673                        Main::Vimpc::CreateEvent(Event::DatabaseSong, Data);
1674                    }
1675                }
1676 
1677                Data.uris.push_back(mpd_song_get_uri(nextSong));
1678                mpd_song_free(nextSong);
1679             }
1680 
1681             Main::Vimpc::CreateEvent(Event::SearchResults, Data);
1682          }
1683       }
1684    });
1685 }
1686 
1687 
StateEvent()1688 void Client::StateEvent()
1689 {
1690    if (Connected() == true)
1691    {
1692       switch (state_)
1693       {
1694          case MPD_STATE_STOP:
1695             currentState_ = "Stopped";
1696             break;
1697          case MPD_STATE_PLAY:
1698             currentState_ = "Playing";
1699             break;
1700          case MPD_STATE_PAUSE:
1701             currentState_ = "Paused";
1702             break;
1703          case MPD_STATE_UNKNOWN:
1704          default:
1705             currentState_ = "Unknown";
1706             break;
1707       }
1708    }
1709 
1710    EventData Data; Data.clientstate = currentState_;
1711    Main::Vimpc::CreateEvent(Event::CurrentState, Data);
1712 }
1713 
1714 
Rescan(std::string const & Path)1715 void Client::Rescan(std::string const & Path)
1716 {
1717    QueueCommand([this, Path] ()
1718    {
1719       ClearCommand();
1720 
1721       if (Connected() == true)
1722       {
1723          Debug("Client::Rescan %s", (Path != "") ? Path.c_str() : "all");
1724 
1725          if (mpd_run_rescan(connection_, (Path != "") ? Path.c_str() : NULL) == true)
1726          {
1727             updating_ = true;
1728 
1729             EventData Data;
1730             Main::Vimpc::CreateEvent(Event::Update, Data);
1731          }
1732       }
1733       else
1734       {
1735          ErrorString(ErrorNumber::ClientNoConnection);
1736       }
1737    });
1738 }
1739 
Update(std::string const & Path)1740 void Client::Update(std::string const & Path)
1741 {
1742    QueueCommand([this, Path] ()
1743    {
1744       ClearCommand();
1745 
1746       if (Connected() == true)
1747       {
1748          Debug("Client::Update %s", (Path != "") ? Path.c_str() : "all");
1749 
1750          if (mpd_run_update(connection_, (Path != "") ? Path.c_str() : NULL) == true)
1751          {
1752             updating_ = true;
1753 
1754             EventData Data;
1755             Main::Vimpc::CreateEvent(Event::Update, Data);
1756          }
1757       }
1758       else
1759       {
1760          ErrorString(ErrorNumber::ClientNoConnection);
1761       }
1762    });
1763 }
1764 
IncrementTime(long time)1765 void Client::IncrementTime(long time)
1766 {
1767    bool const poll = settings_.Get(::Setting::Polling);
1768 
1769    if (time >= 0)
1770    {
1771       timeSinceUpdate_ += time;
1772 
1773       if (state_ == MPD_STATE_PLAY)
1774       {
1775          if (elapsed_ != (mpdelapsed_ + (timeSinceUpdate_ / 1000)))
1776          {
1777             elapsed_ = mpdelapsed_ + (timeSinceUpdate_ / 1000);
1778 
1779             EventData EData; EData.value = elapsed_;
1780             Main::Vimpc::CreateEvent(Event::Elapsed, EData);
1781          }
1782       }
1783 
1784       if ((poll == true) && (timeSinceUpdate_ > 900))
1785       {
1786          UpdateStatus();
1787       }
1788    }
1789    else
1790    {
1791       UpdateStatus();
1792    }
1793 }
1794 
IdleMode()1795 void Client::IdleMode()
1796 {
1797    if ((Connected() == true) &&
1798        (idleMode_ == false) &&
1799        (ready_ == true) &&
1800        (settings_.Get(Setting::Polling) == false))
1801    {
1802       ClearCommand();
1803 
1804       if ((Connected() == true))
1805       {
1806          if (mpd_send_idle(connection_) == true)
1807          {
1808             Debug("Client::Enter idle mode");
1809 
1810             idleMode_ = true;
1811 
1812             EventData Data;
1813             Main::Vimpc::CreateEvent(Event::IdleMode, Data);
1814          }
1815       }
1816    }
1817 }
1818 
ExitIdleMode()1819 void Client::ExitIdleMode()
1820 {
1821    if ((idleMode_ == true) && (Connected() == true))
1822    {
1823       mpd_send_noidle(connection_);
1824 
1825       if (mpd_recv_idle(connection_, false) != 0)
1826       {
1827          UpdateStatus();
1828       }
1829 
1830       Debug("Client::Cancelled idle mode");
1831       CheckError();
1832 
1833       idleMode_ = false;
1834 
1835       EventData Data;
1836       Main::Vimpc::CreateEvent(Event::StopIdleMode, Data);
1837    }
1838 }
1839 
CheckForEvents()1840 void Client::CheckForEvents()
1841 {
1842    if ((idleMode_ == true) && (Connected() == true) && (fd_ != -1))
1843    {
1844       pollfd fds;
1845 
1846       fds.fd = fd_;
1847       fds.events = POLLIN;
1848 
1849       if (poll(&fds, 1, 0) > 0)
1850       {
1851          idleMode_ = false;
1852 
1853          EventData Data;
1854          Main::Vimpc::CreateEvent(Event::StopIdleMode, Data);
1855 
1856          if (mpd_recv_idle(connection_, false) != 0)
1857          {
1858             UpdateStatus();
1859             Debug("Client::Event occurred");
1860          }
1861 
1862          Debug("Client::Left idle mode");
1863 
1864          CheckError();
1865       }
1866    }
1867 }
1868 
UpdateCurrentSong()1869 void Client::UpdateCurrentSong()
1870 {
1871    ClearCommand();
1872 
1873    if ((Connected() == true))
1874    {
1875       if (listMode_ == false)
1876       {
1877          if (currentSong_ != NULL)
1878          {
1879             mpd_song_free(currentSong_);
1880             currentSong_ = NULL;
1881             currentSongId_ = -1;
1882             currentSongURI_ = "";
1883          }
1884 
1885          Debug("Client::Send get current song");
1886          currentSong_ = mpd_run_current_song(connection_);
1887          CheckError();
1888 
1889          if (currentSong_ != NULL)
1890          {
1891             currentSongId_  = mpd_song_get_pos(currentSong_);
1892             currentSongURI_ = mpd_song_get_uri(currentSong_);
1893 
1894             Debug("Client::Get current song %d:%s", currentSongId_, currentSongURI_.c_str());
1895          }
1896       }
1897    }
1898    else
1899    {
1900       currentSongId_ = -1;
1901       currentSongURI_ = "";
1902    }
1903 
1904    EventData IdData; IdData.id = currentSongId_;
1905    Main::Vimpc::CreateEvent(Event::CurrentSongId, IdData);
1906 
1907    if (autoscroll_ == true)
1908    {
1909       Main::Vimpc::CreateEvent(Event::Autoscroll, IdData);
1910       autoscroll_ = false;
1911    }
1912 
1913    if (currentSong_ != NULL)
1914    {
1915       EventData SongData; SongData.currentSong = mpd_song_dup(currentSong_);
1916       Main::Vimpc::CreateEvent(Event::CurrentSong, SongData);
1917    }
1918 }
1919 
ClientQueueExecutor(Mpc::Client * client)1920 void Client::ClientQueueExecutor(Mpc::Client * client)
1921 {
1922    struct timeval start, end;
1923    gettimeofday(&start, NULL);
1924 
1925    while (Running == true)
1926    {
1927       gettimeofday(&end,   NULL);
1928       long const seconds  = end.tv_sec  - start.tv_sec;
1929       long const useconds = end.tv_usec - start.tv_usec;
1930       long const mtime    = (seconds * 1000 + (useconds/1000.0)) + 0.5;
1931       IncrementTime(mtime);
1932       gettimeofday(&start, NULL);
1933 
1934       {
1935          UniqueLock<Mutex> Lock(QueueMutex);
1936 
1937          if ((Queue.empty() == false) ||
1938              (ConditionWait(Condition, Lock, 250) != false))
1939          {
1940             if (Queue.empty() == false)
1941             {
1942                FUNCTION<void()> function = Queue.front();
1943                Queue.pop_front();
1944                Lock.unlock();
1945 
1946                ExitIdleMode();
1947                function();
1948                continue;
1949             }
1950          }
1951       }
1952 
1953       {
1954          UniqueLock<Mutex> Lock(QueueMutex);
1955 
1956          if ((Queue.empty() == true) && (listMode_ == false))
1957          {
1958             if (queueUpdate_ == true)
1959             {
1960                Lock.unlock();
1961                QueueMetaChanges();
1962             }
1963             else if (idleMode_ == false)
1964             {
1965                Lock.unlock();
1966                IdleMode();
1967             }
1968             else if (idleMode_ == true)
1969             {
1970                Lock.unlock();
1971                CheckForEvents();
1972             }
1973          }
1974       }
1975    }
1976 }
1977 
1978 
ClearCommand()1979 void Client::ClearCommand()
1980 {
1981 	error_ = false;
1982 
1983    if ((listMode_ == false) && (idleMode_ == false) && (Connected() == true))
1984    {
1985       Debug("Client::Finish the response");
1986       mpd_response_finish(connection_);
1987       CheckError();
1988    }
1989 }
1990 
IsPasswordRequired()1991 bool Client::IsPasswordRequired()
1992 {
1993    Debug("Client::Trying status update to determine if password required");
1994 
1995    bool Result = false;
1996 
1997    if (Connected() == true)
1998    {
1999       struct mpd_status * status = mpd_run_status(connection_);
2000 
2001       mpd_error error = mpd_connection_get_error(connection_);
2002 
2003       if (error != MPD_ERROR_SUCCESS)
2004       {
2005          if ((error == MPD_ERROR_SERVER) &&
2006             (mpd_connection_get_server_error(connection_) == MPD_SERVER_ERROR_PERMISSION))
2007          {
2008             Result = true;
2009          }
2010 
2011          mpd_connection_clear_error(connection_);
2012       }
2013 
2014       if (status != NULL)
2015       {
2016          mpd_status_free(status);
2017       }
2018    }
2019 
2020    return Result;
2021 }
2022 
2023 
GetAllMetaInformation()2024 void Client::GetAllMetaInformation()
2025 {
2026    ClearCommand();
2027 
2028    std::string const SongFormat = settings_.Get(Setting::SongFormat);
2029 
2030    std::vector<Mpc::Song *> songs;
2031    std::vector<std::string> paths;
2032    std::vector<std::pair<std::string, std::string> > lists;
2033 
2034    if (Connected() == true)
2035    {
2036       EventData DBData;
2037       Main::Vimpc::CreateEvent(Event::ClearDatabase, DBData);
2038 
2039       Debug("Client::Get all meta information");
2040 
2041       if ((settings_.Get(Setting::ListAllMeta) == true))
2042       {
2043           mpd_send_list_all_meta(connection_, NULL);
2044 
2045           mpd_entity * nextEntity = mpd_recv_entity(connection_);
2046 
2047           for(; nextEntity != NULL; nextEntity = mpd_recv_entity(connection_))
2048           {
2049              if (mpd_entity_get_type(nextEntity) == MPD_ENTITY_TYPE_SONG)
2050              {
2051                 mpd_song const * const nextSong = mpd_entity_get_song(nextEntity);
2052 
2053                 if (nextSong != NULL)
2054                 {
2055                    Song * const newSong = CreateSong(nextSong);
2056                    songs.push_back(newSong);
2057                 }
2058              }
2059              else if (mpd_entity_get_type(nextEntity) == MPD_ENTITY_TYPE_DIRECTORY)
2060              {
2061                 mpd_directory const * const nextDirectory = mpd_entity_get_directory(nextEntity);
2062                 paths.push_back(std::string(mpd_directory_get_path(nextDirectory)));
2063              }
2064              else if (mpd_entity_get_type(nextEntity) == MPD_ENTITY_TYPE_PLAYLIST)
2065              {
2066                 mpd_playlist const * const nextPlaylist = mpd_entity_get_playlist(nextEntity);
2067 
2068                 if (nextPlaylist != NULL)
2069                 {
2070                    std::string const path = mpd_playlist_get_path(nextPlaylist);
2071                    std::string name = path;
2072 
2073                    if (name.find("/") != std::string::npos)
2074                    {
2075                       name = name.substr(name.find_last_of("/") + 1);
2076                    }
2077 
2078                    lists.push_back(std::make_pair(name, path));
2079                 }
2080              }
2081 
2082              mpd_entity_free(nextEntity);
2083           }
2084        }
2085    }
2086 
2087    ClearCommand();
2088 
2089    if (error_)
2090    {
2091        Debug("List all failed, disabling\n");
2092        Error(ErrorNumber::ErrorClear, "");
2093        ErrorString(ErrorNumber::ClientNoMeta, "not supported on server");
2094        settings_.Set(Setting::ListAllMeta, false);
2095    }
2096 
2097    EventData DatabaseEvent;
2098    DatabaseEvent.state = (settings_.Get(Setting::ListAllMeta));
2099    Main::Vimpc::CreateEvent(Event::DatabaseEnabled, DatabaseEvent);
2100 
2101    if (Connected() == true)
2102    {
2103       // Songs, paths, lists, etc are collated and the events created this way
2104       // because mpd seems to disconnect you if you take to long to recv entities
2105       for (auto song : songs)
2106       {
2107          // Pre cache the print of the song
2108          (void) song->FormatString(SongFormat);
2109          EventData Data; Data.song = song;
2110          Main::Vimpc::CreateEvent(Event::DatabaseSong, Data);
2111       }
2112 
2113       for (auto path : paths)
2114       {
2115          EventData Data; Data.uri = path;
2116          Main::Vimpc::CreateEvent(Event::DatabasePath, Data);
2117       }
2118 
2119       for (auto list : lists)
2120       {
2121          EventData Data; Data.name = list.first; Data.uri = list.second;
2122          Main::Vimpc::CreateEvent(Event::DatabaseListFile, Data);
2123       }
2124    }
2125 
2126    songs.clear();
2127 
2128    if (Connected() == true)
2129    {
2130       Debug("Client::List queue meta data");
2131       mpd_send_list_queue_meta(connection_);
2132 
2133       mpd_song * nextSong = mpd_recv_song(connection_);
2134 
2135       for (; nextSong != NULL; nextSong = mpd_recv_song(connection_))
2136       {
2137          EventData Data; Data.song = NULL; Data.uri = mpd_song_get_uri(nextSong); Data.pos1 = -1;
2138 
2139          if (((settings_.Get(Setting::ListAllMeta) == false) &&
2140               (Main::Library().Song(Data.uri) == NULL)) ||
2141              // Handle "virtual" songs embedded within files
2142              (mpd_song_get_end(nextSong) != 0))
2143          {
2144             Song * const newSong = CreateSong(nextSong);
2145             Data.song = newSong;
2146 
2147             if (settings_.Get(Setting::ListAllMeta) == false) {
2148                songs.push_back(newSong);
2149             }
2150          }
2151 
2152          Main::Vimpc::CreateEvent(Event::PlaylistAdd, Data);
2153 
2154          mpd_song_free(nextSong);
2155       }
2156    }
2157 
2158    if (settings_.Get(Setting::ListAllMeta) == false) {
2159       if (Connected() == true)
2160       {
2161          // Songs, paths, lists, etc are collated and the events created this way
2162          // because mpd seems to disconnect you if you take to long to recv entities
2163          for (auto song : songs)
2164          {
2165             // Pre cache the print of the song
2166             (void) song->FormatString(SongFormat);
2167             EventData Data; Data.song = song;
2168             Main::Vimpc::CreateEvent(Event::DatabaseSong, Data);
2169          }
2170       }
2171    }
2172 
2173    if (Connected() == true)
2174    {
2175 #if LIBMPDCLIENT_CHECK_VERSION(2,5,0)
2176       ClearCommand();
2177 
2178       if (Connected() == true)
2179       {
2180          Debug("Client::Request playlists");
2181 
2182          if (mpd_send_list_playlists(connection_))
2183          {
2184             mpd_playlist * nextPlaylist = mpd_recv_playlist(connection_);
2185 
2186             for(; nextPlaylist != NULL; nextPlaylist = mpd_recv_playlist(connection_))
2187             {
2188                std::string const playlist = mpd_playlist_get_path(nextPlaylist);
2189 
2190                EventData Data; Data.uri = playlist; Data.name = playlist;
2191                Main::Vimpc::CreateEvent(Event::DatabaseList, Data);
2192 
2193                mpd_playlist_free(nextPlaylist);
2194             }
2195          }
2196 
2197          mpd_connection_clear_error(connection_);
2198       }
2199 #endif
2200    }
2201 
2202    if (Connected() == true)
2203    {
2204       EventData Data;
2205       Main::Vimpc::CreateEvent(Event::AllMetaDataReady, Data);
2206       Main::Vimpc::CreateEvent(Event::Repaint,   Data);
2207    }
2208 
2209 #if !LIBMPDCLIENT_CHECK_VERSION(2,5,0)
2210    GetAllMetaFromRoot();
2211 #endif
2212 }
2213 
GetAllOutputs()2214 void Client::GetAllOutputs()
2215 {
2216    QueueCommand([this] ()
2217    {
2218       ClearCommand();
2219 
2220       if (Connected() == true)
2221       {
2222          Debug("Client::Get outputs");
2223          mpd_send_outputs(connection_);
2224 
2225          mpd_output * next = mpd_recv_output(connection_);
2226 
2227          for (; next != NULL; next = mpd_recv_output(connection_))
2228          {
2229             Mpc::Output * output = new Mpc::Output(mpd_output_get_id(next));
2230 
2231             output->SetEnabled(mpd_output_get_enabled(next));
2232             output->SetName(mpd_output_get_name(next));
2233 
2234             EventData Data; Data.output = output;
2235             Main::Vimpc::CreateEvent(Event::Output, Data);
2236 
2237             mpd_output_free(next);
2238          }
2239 
2240          Debug("Client::Get outputs complete");
2241          EventData Data;
2242          Main::Vimpc::CreateEvent(Event::Repaint,Data);
2243       }
2244    });
2245 }
2246 
GetAllMetaFromRoot()2247 void Client::GetAllMetaFromRoot()
2248 {
2249    // This is a hack to get playlists when using older libmpdclients, it should
2250    // not be used unless absolutely necessary
2251    ClearCommand();
2252 
2253    if (Connected() == true)
2254    {
2255       Debug("Client::Get all root meta");
2256       mpd_send_list_meta(connection_, "/");
2257 
2258       mpd_entity * nextEntity = mpd_recv_entity(connection_);
2259 
2260       for(; nextEntity != NULL; nextEntity = mpd_recv_entity(connection_))
2261       {
2262          if (mpd_entity_get_type(nextEntity) == MPD_ENTITY_TYPE_PLAYLIST)
2263          {
2264             mpd_playlist const * const nextPlaylist = mpd_entity_get_playlist(nextEntity);
2265 
2266             if (nextPlaylist != NULL)
2267             {
2268                std::string const path = mpd_playlist_get_path(nextPlaylist);
2269                std::string name = path;
2270 
2271                if (name.find("/") != std::string::npos)
2272                {
2273                   name = name.substr(name.find_last_of("/") + 1);
2274                }
2275 
2276                EventData Data; Data.uri = path; Data.name = name;
2277                Main::Vimpc::CreateEvent(Event::DatabaseList, Data);
2278             }
2279          }
2280 
2281          mpd_entity_free(nextEntity);
2282       }
2283 
2284       screen_.Invalidate(Ui::Screen::Lists);
2285    }
2286 }
2287 
2288 
StartCommandList()2289 void Client::StartCommandList()
2290 {
2291    QueueCommand([this] ()
2292    {
2293       ClearCommand();
2294 
2295       if ((Connected() == true) && (listMode_ == false))
2296       {
2297          Debug("Client::Start command list");
2298 
2299          if (mpd_command_list_begin(connection_, false) == true)
2300          {
2301             listMode_ = true;
2302          }
2303          else
2304          {
2305             Debug("Client::Start command list failed");
2306             CheckError();
2307          }
2308       }
2309    });
2310 }
2311 
SendCommandList()2312 void Client::SendCommandList()
2313 {
2314    QueueCommand([this] ()
2315    {
2316       if ((Connected() == true) && (listMode_ == true))
2317       {
2318          Debug("Client::End command list");
2319 
2320          if (mpd_command_list_end(connection_) == true)
2321          {
2322             listMode_ = false;
2323             EventData Data;
2324             Main::Vimpc::CreateEvent(Event::CommandListSend, Data);
2325             Main::Vimpc::CreateEvent(Event::Repaint,   Data);
2326          }
2327          else
2328          {
2329             Debug("Client::End command list failed");
2330             CheckError();
2331          }
2332       }
2333    });
2334 }
2335 
2336 
QueueVersion()2337 unsigned int Client::QueueVersion()
2338 {
2339    return queueVersion_;
2340 }
2341 
2342 
SetStateAndEvent(int event,bool & state,bool value)2343 void Client::SetStateAndEvent(int event, bool & state, bool value)
2344 {
2345    state = value;
2346    EventData Data; Data.state = value;
2347    Main::Vimpc::CreateEvent(event, Data);
2348 }
2349 
UpdateStatus(bool ExpectUpdate)2350 void Client::UpdateStatus(bool ExpectUpdate)
2351 {
2352    QueueCommand([this, ExpectUpdate] ()
2353    {
2354       ClearCommand();
2355 
2356       if ((Connected() == true) && (listMode_ == false))
2357       {
2358          if (currentStatus_ != NULL)
2359          {
2360             mpd_status_free(currentStatus_);
2361             currentStatus_ = NULL;
2362          }
2363 
2364          Debug("Client::Get current status");
2365          struct mpd_status * status = mpd_run_status(connection_);
2366          CheckError();
2367 
2368          if (status != NULL)
2369          {
2370             currentStatus_   = status;
2371             timeSinceUpdate_ = 0;
2372 
2373             unsigned int version     = mpd_status_get_queue_version(currentStatus_);
2374             unsigned int qVersion    = static_cast<uint32_t>(queueVersion_);
2375             bool const   wasUpdating = updating_;
2376 
2377             if (static_cast<int32_t>(volume_) != mpd_status_get_volume(currentStatus_))
2378             {
2379                volume_ = mpd_status_get_volume(currentStatus_);
2380 
2381                EventData Data; Data.value = volume_;
2382                Main::Vimpc::CreateEvent(Event::Volume, Data);
2383             }
2384 
2385             if (updating_ != (mpd_status_get_update_id(currentStatus_) >= 1))
2386             {
2387                updating_ = (mpd_status_get_update_id(currentStatus_) >= 1);
2388 
2389                if (updating_ == true)
2390                {
2391                   EventData Data;
2392                   Main::Vimpc::CreateEvent(Event::Update, Data);
2393                }
2394             }
2395 
2396             if (random_ != mpd_status_get_random(currentStatus_))
2397             {
2398                SetStateAndEvent(Event::Random, random_, mpd_status_get_random(currentStatus_));
2399             }
2400 
2401             if (repeat_ != mpd_status_get_repeat(currentStatus_))
2402             {
2403                SetStateAndEvent(Event::Repeat, repeat_, mpd_status_get_repeat(currentStatus_));
2404             }
2405 
2406             if (single_ != mpd_status_get_single(currentStatus_))
2407             {
2408                SetStateAndEvent(Event::Single, single_, mpd_status_get_single(currentStatus_));
2409             }
2410 
2411             if (consume_ != mpd_status_get_consume(currentStatus_))
2412             {
2413                SetStateAndEvent(Event::Consume, consume_, mpd_status_get_consume(currentStatus_));
2414             }
2415 
2416             if (totalNumberOfSongs_ != mpd_status_get_queue_length(currentStatus_))
2417             {
2418                totalNumberOfSongs_ = mpd_status_get_queue_length(currentStatus_);
2419 
2420                EventData Data; Data.count = totalNumberOfSongs_;
2421                Main::Vimpc::CreateEvent(Event::TotalSongCount, Data);
2422             }
2423 
2424             if (crossfade_ != (mpd_status_get_crossfade(currentStatus_) > 0))
2425             {
2426                crossfade_ = (mpd_status_get_crossfade(currentStatus_) > 0);
2427                EventData Data; Data.state = crossfade_;
2428                Main::Vimpc::CreateEvent(Event::Crossfade, Data);
2429             }
2430 
2431             if (crossfade_ == true)
2432             {
2433                if (crossfadeTime_ != mpd_status_get_crossfade(currentStatus_))
2434                {
2435                   crossfadeTime_ = mpd_status_get_crossfade(currentStatus_);
2436                   EventData Data; Data.value = crossfadeTime_;
2437                   Main::Vimpc::CreateEvent(Event::CrossfadeTime, Data);
2438                }
2439             }
2440 
2441             // Check if we need to update the current song
2442             if ((mpdstate_ != mpd_status_get_state(currentStatus_)) ||
2443                ((mpdstate_ != MPD_STATE_STOP) && (currentSong_ == NULL)) ||
2444                ((mpdstate_ == MPD_STATE_STOP) && (currentSong_ != NULL)) ||
2445                ((currentSong_ != NULL) &&
2446                 (mpd_song_get_duration(currentSong_) > 0) &&
2447                 ((elapsed_ >= mpd_song_get_duration(currentSong_) - 3) ||
2448                   (mpd_status_get_elapsed_time(currentStatus_) < mpdelapsed_) ||
2449                   (mpd_status_get_elapsed_time(currentStatus_) <= 3))))
2450             {
2451                autoscroll_ = true;
2452                UpdateCurrentSong();
2453             }
2454 
2455             mpdstate_   = mpd_status_get_state(currentStatus_);
2456             mpdelapsed_ = mpd_status_get_elapsed_time(currentStatus_);
2457 
2458             if (state_ != mpdstate_)
2459             {
2460                state_ = mpdstate_;
2461                StateEvent();
2462             }
2463 
2464             if (mpdstate_ == MPD_STATE_STOP)
2465             {
2466                currentSongId_  = -1;
2467                currentSongURI_ = "";
2468 
2469                EventData IdData; IdData.id = currentSongId_;
2470                Main::Vimpc::CreateEvent(Event::CurrentSongId, IdData);
2471                EventData Data; Data.currentSong = NULL;
2472                Main::Vimpc::CreateEvent(Event::CurrentSong, Data);
2473             }
2474 
2475             if (mpdstate_ != MPD_STATE_PLAY)
2476             {
2477                elapsed_ = mpdelapsed_;
2478             }
2479 
2480             EventData EData; EData.value = elapsed_;
2481             Main::Vimpc::CreateEvent(Event::Elapsed, EData);
2482 
2483             if ((queueVersion_ > -1) && (version > qVersion) && (queueUpdate_ == false))
2484             {
2485                oldVersion_  = queueVersion_;
2486                queueUpdate_ = true;
2487             }
2488 
2489             if ((wasUpdating == true) && (updating_ == false))
2490             {
2491                GetAllMetaInformation();
2492                UpdateCurrentSong();
2493 
2494                EventData Data;
2495                Main::Vimpc::CreateEvent(Event::UpdateComplete, Data);
2496                Main::Vimpc::CreateEvent(Event::Repaint,   Data);
2497             }
2498 
2499             queueVersion_ = version;
2500          }
2501       }
2502    });
2503 }
2504 
CreateSong(mpd_song const * const song) const2505 Song * Client::CreateSong(mpd_song const * const song) const
2506 {
2507    static int count = 0;
2508 
2509    Song * const newSong = new Song();
2510 
2511    //Debug("Alloc a song %d %d", count, sizeof(Song));
2512 
2513    newSong->SetArtist     (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0));
2514    newSong->SetAlbumArtist(mpd_song_get_tag(song, MPD_TAG_ALBUM_ARTIST, 0));
2515    newSong->SetAlbum      (mpd_song_get_tag(song, MPD_TAG_ALBUM,  0));
2516    newSong->SetTitle      (mpd_song_get_tag(song, MPD_TAG_TITLE,  0));
2517    newSong->SetTrack      (mpd_song_get_tag(song, MPD_TAG_TRACK,  0));
2518    newSong->SetURI        (mpd_song_get_uri(song));
2519    newSong->SetGenre      (mpd_song_get_tag(song, MPD_TAG_GENRE, 0));
2520    newSong->SetDate       (mpd_song_get_tag(song, MPD_TAG_DATE, 0));
2521    newSong->SetDisc       (mpd_song_get_tag(song, MPD_TAG_DISC, 0));
2522    newSong->SetDuration   (mpd_song_get_duration(song));
2523    newSong->SetVirtualEnd (mpd_song_get_end(song));
2524 
2525    return newSong;
2526 }
2527 
QueueMetaChanges()2528 void Client::QueueMetaChanges()
2529 {
2530    ClearCommand();
2531 
2532    if (Connected() == true)
2533    {
2534       struct mpd_status * status = mpd_run_status(connection_);
2535       queueVersion_ = mpd_status_get_queue_version(status);
2536 
2537       if (totalNumberOfSongs_ != mpd_status_get_queue_length(status))
2538       {
2539          totalNumberOfSongs_ = mpd_status_get_queue_length(status);
2540 
2541          EventData Data; Data.count = totalNumberOfSongs_;
2542          Main::Vimpc::CreateEvent(Event::TotalSongCount, Data);
2543       }
2544 
2545       if (oldVersion_ != queueVersion_)
2546       {
2547          Debug("Client::List queue meta data changes %d %d", oldVersion_, queueVersion_);
2548          mpd_send_queue_changes_meta(connection_, oldVersion_);
2549 
2550          mpd_song * nextSong = mpd_recv_song(connection_);
2551          EventData Data;
2552          Data.count = totalNumberOfSongs_;
2553 
2554          for (; nextSong != NULL; nextSong = mpd_recv_song(connection_))
2555          {
2556             Song * newSong = NULL;
2557 
2558             if (((settings_.Get(Setting::ListAllMeta) == false) &&
2559                  (Main::Library().Song(Data.uri) == NULL)) ||
2560                 // Handle "virtual" songs embedded within files
2561                 (mpd_song_get_end(nextSong) != 0))
2562             {
2563                newSong = CreateSong(nextSong);
2564             }
2565 
2566             //Debug("Change: %d %s", mpd_song_get_pos(nextSong), mpd_song_get_uri(nextSong));
2567             Data.posuri.push_back(std::make_pair(mpd_song_get_pos(nextSong), std::make_pair(newSong, mpd_song_get_uri(nextSong))));
2568             mpd_song_free(nextSong);
2569          }
2570 
2571          QueueMutex.lock();
2572 
2573          if (Queue.size() == 0)
2574          {
2575             QueueMutex.unlock();
2576 
2577             oldVersion_  = queueVersion_;
2578             queueUpdate_ = false;
2579             Main::Vimpc::CreateEvent(Event::PlaylistQueueReplace, Data);
2580 
2581             EventData QueueData;
2582             Main::Vimpc::CreateEvent(Event::QueueUpdate, QueueData);
2583 
2584             UpdateCurrentSong();
2585          }
2586          else
2587          {
2588             QueueMutex.unlock();
2589          }
2590       }
2591       else
2592       {
2593          queueUpdate_ = false;
2594       }
2595    }
2596 }
2597 
2598 
GetVersion()2599 void Client::GetVersion()
2600 {
2601    if (Connected() == true)
2602    {
2603       Debug("Client::Sending version request");
2604       unsigned const * version = mpd_connection_get_server_version(connection_);
2605       CheckError();
2606 
2607       if (version != NULL)
2608       {
2609          versionMajor_ = version[0];
2610          versionMinor_ = version[1];
2611          versionPatch_ = version[2];
2612 
2613          Debug("libmpdclient: %d.%d.%d", LIBMPDCLIENT_MAJOR_VERSION, LIBMPDCLIENT_MINOR_VERSION, LIBMPDCLIENT_PATCH_VERSION);
2614          Debug("MPD Server  : %d.%d.%d", versionMajor_, versionMinor_, versionPatch_);
2615       }
2616    }
2617 }
2618 
CheckError()2619 bool Client::CheckError()
2620 {
2621    if (connection_ != NULL)
2622    {
2623       if (mpd_connection_get_error(connection_) != MPD_ERROR_SUCCESS)
2624       {
2625          char error[255];
2626          snprintf(error, 255, "MPD Error: %s",  mpd_connection_get_error_message(connection_));
2627          Error(ErrorNumber::ClientError, error);
2628 
2629          Debug("Client::%s", error);
2630 
2631 #ifdef _DEBUG_ASSERT_ON_ERROR
2632          ASSERT(false);
2633 #elif defined(_DEBUG_BREAK_ON_ERROR)
2634          BREAKPOINT
2635 #endif
2636          error_ = true;
2637 
2638          bool const ClearError = mpd_connection_clear_error(connection_);
2639 
2640          if (ClearError == false)
2641          {
2642             Debug("Client::Unable to clear error");
2643             DeleteConnection();
2644 
2645             if ((settings_.Get(Setting::Reconnect) == true) && (retried_ == false))
2646             {
2647                retried_ = true;
2648                Connect(hostname_, port_);
2649             }
2650          }
2651          else
2652          {
2653             UpdateStatus();
2654          }
2655 
2656          return true;
2657       }
2658    }
2659 
2660    return false;
2661 }
2662 
DeleteConnection()2663 void Client::DeleteConnection()
2664 {
2665    ready_        = false;
2666    listMode_     = false;
2667    currentState_ = "Disconnected";
2668    volume_       = -1;
2669    updating_     = false;
2670    random_       = false;
2671    single_       = false;
2672    consume_      = false;
2673    repeat_       = false;
2674    idleMode_     = false;
2675    crossfade_    = false;
2676    state_        = MPD_STATE_UNKNOWN;
2677 
2678    totalNumberOfSongs_ = 0;
2679 
2680    versionMajor_ = -1;
2681    versionMinor_ = -1;
2682    versionPatch_ = -1;
2683    queueVersion_ = -1;
2684 
2685    StateEvent();
2686 
2687    // \todo it would be nice to clear the queue here
2688    // but currently it also removes queued up connect attempts
2689    // to different hosts, etc, figure this out
2690    //Queue.clear();
2691 
2692    if (connection_ != NULL)
2693    {
2694       mpd_connection_free(connection_);
2695       connection_ = NULL;
2696       fd_         = -1;
2697    }
2698 
2699    EventData Data;
2700    Main::Vimpc::CreateEvent(Event::Disconnected, Data);
2701 
2702    ENSURE(connection_ == NULL);
2703 }
2704 
2705 
2706 /* vim: set sw=3 ts=3: */
2707