diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 24ebe8502..3517bd6d5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -565,13 +565,13 @@ if(HAVE_DBUS) # MPRIS DBUS interfaces qt4_add_dbus_adaptor(SOURCES dbus/org.freedesktop.MediaPlayer.player.xml - core/mpris.h mpris::Mpris1Player core/mpris_player MprisPlayer) + core/mpris1.h mpris::Mpris1Player core/mpris_player MprisPlayer) qt4_add_dbus_adaptor(SOURCES dbus/org.freedesktop.MediaPlayer.root.xml - core/mpris.h mpris::Mpris1Root core/mpris_root MprisRoot) + core/mpris1.h mpris::Mpris1Root core/mpris_root MprisRoot) qt4_add_dbus_adaptor(SOURCES dbus/org.freedesktop.MediaPlayer.tracklist.xml - core/mpris.h mpris::Mpris1TrackList core/mpris_tracklist MprisTrackList) + core/mpris1.h mpris::Mpris1TrackList core/mpris_tracklist MprisTrackList) # MPRIS 2.0 DBUS interfaces qt4_add_dbus_adaptor(SOURCES @@ -600,8 +600,8 @@ if(HAVE_DBUS) endif(HAVE_DEVICEKIT) # MPRIS source - list(APPEND SOURCES core/mpris.cpp core/mpris2.cpp) - list(APPEND HEADERS core/mpris.h core/mpris2.h) + list(APPEND SOURCES core/mpris.cpp core/mpris1.cpp core/mpris2.cpp) + list(APPEND HEADERS core/mpris.h core/mpris1.h core/mpris2.h) # Wiimotedev interface classes if(ENABLE_WIIMOTEDEV) @@ -735,6 +735,8 @@ list(APPEND OTHER_SOURCES core/modelfuturewatcher.h core/mpris.cpp core/mpris.h + core/mpris1.cpp + core/mpris1.h core/mpris2.cpp core/mpris2.h devices/afcdevice.cpp diff --git a/src/core/mpris.cpp b/src/core/mpris.cpp index e690fa0c6..bf6c6cb1e 100644 --- a/src/core/mpris.cpp +++ b/src/core/mpris.cpp @@ -16,342 +16,45 @@ */ #include "mpris.h" -#include "mpris_common.h" +#include "mpris1.h" +#include "mpris2.h" -#include -#include - -#include "core/mpris_player.h" -#include "core/mpris_root.h" -#include "core/mpris_tracklist.h" -#include "engines/enginebase.h" -#include "playlist/playlist.h" -#include "playlist/playlistmanager.h" -#include "playlist/playlistsequence.h" +#include +#include +#include namespace mpris { -Mpris1::Mpris1(Player* player, ArtLoader* art_loader, QObject* parent) - : QObject(parent) +Mpris::Mpris(Player* player, ArtLoader* art_loader, QObject* parent) + : QObject(parent), + player_(player), + art_loader_(art_loader), + mpris1_(NULL), + mpris2_(NULL) { - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); + QFuture future = QtConcurrent::run(this, &Mpris::Init); - QDBusConnection::sessionBus().registerService("org.mpris.clementine"); - root_ = new Mpris1Root(player, this); - player_ = new Mpris1Player(player, this); - tracklist_ = new Mpris1TrackList(player, this); + QFutureWatcher* watcher = new QFutureWatcher(this); + watcher->setFuture(future); - connect(art_loader, SIGNAL(ArtLoaded(Song,QString)), - player_, SLOT(CurrentSongChanged(Song,QString))); + connect(watcher, SIGNAL(finished()), SLOT(Initialised())); } -Mpris1Root::Mpris1Root(Player* player, QObject* parent) - : QObject(parent), player_(player) { - new MprisRoot(this); - QDBusConnection::sessionBus().registerObject("/", this); +void Mpris::Init() { + mpris1_ = new mpris::Mpris1(player_, art_loader_); + mpris2_ = new mpris::Mpris2(player_, art_loader_, mpris1_); + + mpris1_->moveToThread(thread()); + mpris2_->moveToThread(thread()); + + mpris1_->setParent(this); + mpris2_->setParent(this); + + connect(mpris2_, SIGNAL(RaiseMainWindow()), SIGNAL(RaiseMainWindow())); } -Mpris1Player::Mpris1Player(Player* player, QObject* parent) - : QObject(parent), player_(player) { - new MprisPlayer(this); - QDBusConnection::sessionBus().registerObject("/Player", this); - - connect(player->engine(), SIGNAL(StateChanged(Engine::State)), SLOT(EngineStateChanged(Engine::State))); - connect(player_->playlists(), SIGNAL(PlaylistManagerInitialized()), SLOT(PlaylistManagerInitialized())); -} - -// when PlaylistManager gets it ready, we connect PlaylistSequence with this -void Mpris1Player::PlaylistManagerInitialized() { - connect(player_->playlists()->sequence(), SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)), - SLOT(ShuffleModeChanged())); - connect(player_->playlists()->sequence(), SIGNAL(RepeatModeChanged(PlaylistSequence::RepeatMode)), - SLOT(RepeatModeChanged())); -} - -Mpris1TrackList::Mpris1TrackList(Player* player, QObject* parent) - : QObject(parent), player_(player) { - new MprisTrackList(this); - QDBusConnection::sessionBus().registerObject("/TrackList", this); - - connect(player->playlists(), SIGNAL(PlaylistChanged(Playlist*)), SLOT(PlaylistChanged(Playlist*))); -} - -void Mpris1TrackList::PlaylistChanged(Playlist* playlist) { - emit TrackListChange(playlist->rowCount()); -} - -// we use the state from event and don't try to obtain it from Player -// later because only the event's version is really the current one -void Mpris1Player::EngineStateChanged(Engine::State state) { - emit StatusChange(GetStatus(state)); - emit CapsChange(GetCaps(state)); -} - -void Mpris1Player::CurrentSongChanged(const Song& song, const QString& art_uri) { - last_metadata_ = Mpris1::GetMetadata(song); - - if (!art_uri.isEmpty()) { - AddMetadata("arturl", art_uri, &last_metadata_); - } - - emit TrackChange(last_metadata_); - emit StatusChange(GetStatus()); - emit CapsChange(GetCaps()); -} - - -QString Mpris1Root::Identity() { - return QString("%1 %2").arg( - QCoreApplication::applicationName(), - QCoreApplication::applicationVersion()); -} - -Version Mpris1Root::MprisVersion() { - Version version; - version.major = 1; - version.minor = 0; - return version; -} - -void Mpris1Root::Quit() { - qApp->quit(); -} - -void Mpris1Player::Pause() { - player_->Pause(); -} - -void Mpris1Player::Stop() { - player_->Stop(); -} - -void Mpris1Player::Prev() { - player_->Previous(); -} - -void Mpris1Player::Play() { - player_->Play(); -} - -void Mpris1Player::Next() { - player_->Next(); -} - -void Mpris1Player::Repeat(bool repeat) { - player_->playlists()->sequence()->SetRepeatMode( - repeat ? PlaylistSequence::Repeat_Track : PlaylistSequence::Repeat_Off); -} - -void Mpris1Player::ShuffleModeChanged() { - emit StatusChange(GetStatus()); -} - -void Mpris1Player::RepeatModeChanged() { - emit StatusChange(GetStatus()); -} - -DBusStatus Mpris1Player::GetStatus() const { - return GetStatus(player_->GetState()); -} - -DBusStatus Mpris1Player::GetStatus(Engine::State state) const { - DBusStatus status; - switch (state) { - case Engine::Empty: - case Engine::Idle: - status.play = DBusStatus::Mpris_Stopped; - break; - case Engine::Playing: - status.play = DBusStatus::Mpris_Playing; - break; - case Engine::Paused: - status.play = DBusStatus::Mpris_Paused; - break; - } - - PlaylistManager* playlists_ = player_->playlists(); - PlaylistSequence::RepeatMode repeat_mode = playlists_->sequence()->repeat_mode(); - - status.random = playlists_->sequence()->shuffle_mode() == PlaylistSequence::Shuffle_Off ? 0 : 1; - status.repeat = repeat_mode == PlaylistSequence::Repeat_Track ? 1 : 0; - status.repeat_playlist = (repeat_mode == PlaylistSequence::Repeat_Album || - repeat_mode == PlaylistSequence::Repeat_Playlist || - repeat_mode == PlaylistSequence::Repeat_Track) ? 1 : 0; - return status; - -} - -void Mpris1Player::VolumeSet(int volume) { - player_->SetVolume(volume); -} - -int Mpris1Player::VolumeGet() const { - return player_->GetVolume(); -} - -void Mpris1Player::PositionSet(int pos) { - player_->Seek(pos/1000); -} - -int Mpris1Player::PositionGet() const { - return player_->engine()->position(); -} - -QVariantMap Mpris1Player::GetMetadata() const { - return last_metadata_; -} - -int Mpris1Player::GetCaps() const { - return GetCaps(player_->GetState()); -} - -int Mpris1Player::GetCaps(Engine::State state) const { - int caps = CAN_HAS_TRACKLIST; - PlaylistItemPtr current_item = player_->GetCurrentItem(); - PlaylistManager* playlists = player_->playlists(); - - // play is disabled when playlist is empty or when last.fm stream is already playing - if (playlists->active()->rowCount() != 0 - && !(state == Engine::Playing && (player_->GetCurrentItem()->options() & PlaylistItem::LastFMControls))) { - caps |= CAN_PLAY; - } - - if (current_item) { - caps |= CAN_PROVIDE_METADATA; - if (state == Engine::Playing && !(current_item->options() & PlaylistItem::PauseDisabled)) { - caps |= CAN_PAUSE; - } - if (state != Engine::Empty && current_item->Metadata().filetype() != Song::Type_Stream) { - caps |= CAN_SEEK; - } - } - - if (playlists->active()->next_row() != -1 || - playlists->active()->current_item_options() & PlaylistItem::ContainsMultipleTracks) { - caps |= CAN_GO_NEXT; - } - if (playlists->active()->previous_row() != -1) { - caps |= CAN_GO_PREV; - } - - return caps; -} - -void Mpris1Player::VolumeUp(int change) { - VolumeSet(VolumeGet() + change); -} - -void Mpris1Player::VolumeDown(int change) { - VolumeSet(VolumeGet() - change); -} - -void Mpris1Player::Mute() { - player_->Mute(); -} - -void Mpris1Player::ShowOSD() { - player_->ShowOSD(); -} - -int Mpris1TrackList::AddTrack(const QString& track, bool play) { - player_->playlists()->active()->InsertUrls( - QList() << QUrl(track), play); - return 0; -} - -void Mpris1TrackList::DelTrack(int index) { - player_->playlists()->active()->removeRows(index, 1); -} - -int Mpris1TrackList::GetCurrentTrack() const { - return player_->playlists()->active()->current_row(); -} - -int Mpris1TrackList::GetLength() const { - return player_->playlists()->active()->rowCount(); -} - -QVariantMap Mpris1TrackList::GetMetadata(int pos) const { - PlaylistItemPtr item = player_->GetItemAt(pos); - if (!item) - return QVariantMap(); - - return Mpris1::GetMetadata(item->Metadata()); -} - -void Mpris1TrackList::SetLoop(bool enable) { - player_->playlists()->active()->sequence()->SetRepeatMode( - enable ? PlaylistSequence::Repeat_Playlist : PlaylistSequence::Repeat_Off); -} - -void Mpris1TrackList::SetRandom(bool enable) { - player_->playlists()->active()->sequence()->SetShuffleMode( - enable ? PlaylistSequence::Shuffle_All : PlaylistSequence::Shuffle_Off); -} - -void Mpris1TrackList::PlayTrack(int index) { - player_->PlayAt(index, Engine::Manual, true); -} - -QVariantMap Mpris1::GetMetadata(const Song& song) { - QVariantMap ret; - - AddMetadata("location", song.filename(), &ret); - AddMetadata("title", song.PrettyTitle(), &ret); - AddMetadata("artist", song.artist(), &ret); - AddMetadata("album", song.album(), &ret); - AddMetadata("time", song.length(), &ret); - AddMetadata("mtime", song.length() * 1000, &ret); - AddMetadata("tracknumber", song.track(), &ret); - AddMetadata("year", song.year(), &ret); - AddMetadata("genre", song.genre(), &ret); - AddMetadata("disc", song.disc(), &ret); - AddMetadata("comment", song.comment(), &ret); - AddMetadata("audio-bitrate", song.bitrate(), &ret); - AddMetadata("audio-samplerate", song.samplerate(), &ret); - AddMetadata("bpm", song.bpm(), &ret); - AddMetadata("composer", song.composer(), &ret); - if (song.rating() != -1.0) { - AddMetadata("rating", song.rating() * 5, &ret); - } - - return ret; +void Mpris::Initialised() { + mpris2_->InitLibIndicate(); } } // namespace mpris - - -QDBusArgument& operator<< (QDBusArgument& arg, const Version& version) { - arg.beginStructure(); - arg << version.major << version.minor; - arg.endStructure(); - return arg; -} - -const QDBusArgument& operator>> (const QDBusArgument& arg, Version& version) { - arg.beginStructure(); - arg >> version.major >> version.minor; - arg.endStructure(); - return arg; -} - -QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status) { - arg.beginStructure(); - arg << status.play; - arg << status.random; - arg << status.repeat; - arg << status.repeat_playlist; - arg.endStructure(); - return arg; -} - -const QDBusArgument& operator>> (const QDBusArgument& arg, DBusStatus& status) { - arg.beginStructure(); - arg >> status.play; - arg >> status.random; - arg >> status.repeat; - arg >> status.repeat_playlist; - arg.endStructure(); - return arg; -} diff --git a/src/core/mpris.h b/src/core/mpris.h index ea88274fb..a33091fe8 100644 --- a/src/core/mpris.h +++ b/src/core/mpris.h @@ -18,176 +18,37 @@ #ifndef MPRIS_H #define MPRIS_H -#include "core/player.h" - -#include -#include #include class Player; -class Playlist; - -struct DBusStatus { // From Amarok. - int play; // Playing = 0, Paused = 1, Stopped = 2 - int random; // Linearly = 0, Randomly = 1 - int repeat; // Go_To_Next = 0, Repeat_Current = 1 - int repeat_playlist; // Stop_When_Finished = 0, Never_Give_Up_Playing = 1, Never_Let_You_Down = 42 - - enum MprisPlayState { - Mpris_Playing = 0, - Mpris_Paused = 1, - Mpris_Stopped = 2, - }; -}; -Q_DECLARE_METATYPE(DBusStatus); - -QDBusArgument& operator <<(QDBusArgument& arg, const DBusStatus& status); -const QDBusArgument& operator >>(const QDBusArgument& arg, DBusStatus& status); - - -struct Version { - quint16 minor; - quint16 major; -}; -Q_DECLARE_METATYPE(Version); - -QDBusArgument& operator <<(QDBusArgument& arg, const Version& version); -const QDBusArgument& operator >>(const QDBusArgument& arg, Version& version); - namespace mpris { -enum DBusCaps { - NONE = 0, - CAN_GO_NEXT = 1 << 0, - CAN_GO_PREV = 1 << 1, - CAN_PAUSE = 1 << 2, - CAN_PLAY = 1 << 3, - CAN_SEEK = 1 << 4, - CAN_PROVIDE_METADATA = 1 << 5, - CAN_HAS_TRACKLIST = 1 << 6, -}; - - class ArtLoader; -class Mpris1Root; -class Mpris1Player; -class Mpris1TrackList; +class Mpris1; +class Mpris2; - -class Mpris1 : public QObject { +class Mpris : public QObject { Q_OBJECT public: - Mpris1(Player* player, ArtLoader* art_loader, QObject* parent = 0); - - static QVariantMap GetMetadata(const Song& song); - - Mpris1Root* root() const { return root_; } - Mpris1Player* player() const { return player_; } - Mpris1TrackList* tracklist() const { return tracklist_; } - -private: - Mpris1Root* root_; - Mpris1Player* player_; - Mpris1TrackList* tracklist_; -}; - - -class Mpris1Root : public QObject { - Q_OBJECT - -public: - Mpris1Root(Player* player, QObject* parent); - - QString Identity(); - void Quit(); - Version MprisVersion(); - -private: - Player* player_; -}; - - -class Mpris1Player : public QObject { - Q_OBJECT - -public: - Mpris1Player(Player* player, QObject* parent); - - void Pause(); - void Stop(); - void Prev(); - void Play(); - void Next(); - void Repeat(bool); - - // those methods will use engine's state obtained with player->GetState() method - DBusStatus GetStatus() const; - int GetCaps() const; - // those methods will use engine's state provided as an argument - DBusStatus GetStatus(Engine::State state) const; - int GetCaps(Engine::State state) const; - - void VolumeSet(int volume); - int VolumeGet() const; - void PositionSet(int pos); - int PositionGet() const; - QVariantMap GetMetadata() const; - - // Amarok extensions - void VolumeUp(int vol); - void VolumeDown(int vol); - void Mute(); - void ShowOSD(); - -public slots: - void CurrentSongChanged(const Song& song, const QString& art_uri); + Mpris(Player* player, ArtLoader* art_loader, QObject* parent = 0); signals: - void CapsChange(int); - void TrackChange(const QVariantMap&); - void StatusChange(DBusStatus); + void RaiseMainWindow(); private slots: - void PlaylistManagerInitialized(); + void Initialised(); - void EngineStateChanged(Engine::State state); - void ShuffleModeChanged(); - void RepeatModeChanged(); +private: + void Init(); private: Player* player_; + ArtLoader* art_loader_; - QVariantMap last_metadata_; -}; - - -class Mpris1TrackList : public QObject { - Q_OBJECT - -public: - Mpris1TrackList(Player* player, QObject* parent); - - int AddTrack(const QString&, bool); - void DelTrack(int index); - int GetCurrentTrack() const; - int GetLength() const; - QVariantMap GetMetadata(int) const; - void SetLoop(bool enable); - void SetRandom(bool enable); - - // Amarok extension - void PlayTrack(int index); - -signals: - void TrackListChange(int i); - -private slots: - void PlaylistChanged(Playlist* playlist); - -private: - Player* player_; + Mpris1* mpris1_; + Mpris2* mpris2_; }; } // namespace mpris diff --git a/src/core/mpris1.cpp b/src/core/mpris1.cpp new file mode 100644 index 000000000..20e7077a3 --- /dev/null +++ b/src/core/mpris1.cpp @@ -0,0 +1,358 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine 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 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#include "mpris1.h" +#include "mpris_common.h" + +#include +#include + +#include "core/mpris_player.h" +#include "core/mpris_root.h" +#include "core/mpris_tracklist.h" +#include "engines/enginebase.h" +#include "playlist/playlist.h" +#include "playlist/playlistmanager.h" +#include "playlist/playlistsequence.h" + +namespace mpris { + +Mpris1::Mpris1(Player* player, ArtLoader* art_loader, QObject* parent) + : QObject(parent) +{ + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + + QDBusConnection::sessionBus().registerService("org.mpris.clementine"); + + root_ = new Mpris1Root(player, this); + player_ = new Mpris1Player(player, this); + tracklist_ = new Mpris1TrackList(player, this); + + connect(art_loader, SIGNAL(ArtLoaded(Song,QString)), + player_, SLOT(CurrentSongChanged(Song,QString))); +} + +Mpris1Root::Mpris1Root(Player* player, QObject* parent) + : QObject(parent), player_(player) { + new MprisRoot(this); + QDBusConnection::sessionBus().registerObject("/", this); +} + +Mpris1Player::Mpris1Player(Player* player, QObject* parent) + : QObject(parent), player_(player) { + new MprisPlayer(this); + QDBusConnection::sessionBus().registerObject("/Player", this); + + connect(player->engine(), SIGNAL(StateChanged(Engine::State)), SLOT(EngineStateChanged(Engine::State))); + connect(player_->playlists(), SIGNAL(PlaylistManagerInitialized()), SLOT(PlaylistManagerInitialized())); +} + +// when PlaylistManager gets it ready, we connect PlaylistSequence with this +void Mpris1Player::PlaylistManagerInitialized() { + connect(player_->playlists()->sequence(), SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)), + SLOT(ShuffleModeChanged())); + connect(player_->playlists()->sequence(), SIGNAL(RepeatModeChanged(PlaylistSequence::RepeatMode)), + SLOT(RepeatModeChanged())); +} + +Mpris1TrackList::Mpris1TrackList(Player* player, QObject* parent) + : QObject(parent), player_(player) { + new MprisTrackList(this); + QDBusConnection::sessionBus().registerObject("/TrackList", this); + + connect(player->playlists(), SIGNAL(PlaylistChanged(Playlist*)), SLOT(PlaylistChanged(Playlist*))); +} + +void Mpris1TrackList::PlaylistChanged(Playlist* playlist) { + emit TrackListChange(playlist->rowCount()); +} + +// we use the state from event and don't try to obtain it from Player +// later because only the event's version is really the current one +void Mpris1Player::EngineStateChanged(Engine::State state) { + emit StatusChange(GetStatus(state)); + emit CapsChange(GetCaps(state)); +} + +void Mpris1Player::CurrentSongChanged(const Song& song, const QString& art_uri) { + last_metadata_ = Mpris1::GetMetadata(song); + + if (!art_uri.isEmpty()) { + AddMetadata("arturl", art_uri, &last_metadata_); + } + + emit TrackChange(last_metadata_); + emit StatusChange(GetStatus()); + emit CapsChange(GetCaps()); +} + + +QString Mpris1Root::Identity() { + return QString("%1 %2").arg( + QCoreApplication::applicationName(), + QCoreApplication::applicationVersion()); +} + +Version Mpris1Root::MprisVersion() { + Version version; + version.major = 1; + version.minor = 0; + return version; +} + +void Mpris1Root::Quit() { + qApp->quit(); +} + +void Mpris1Player::Pause() { + player_->Pause(); +} + +void Mpris1Player::Stop() { + player_->Stop(); +} + +void Mpris1Player::Prev() { + player_->Previous(); +} + +void Mpris1Player::Play() { + player_->Play(); +} + +void Mpris1Player::Next() { + player_->Next(); +} + +void Mpris1Player::Repeat(bool repeat) { + player_->playlists()->sequence()->SetRepeatMode( + repeat ? PlaylistSequence::Repeat_Track : PlaylistSequence::Repeat_Off); +} + +void Mpris1Player::ShuffleModeChanged() { + emit StatusChange(GetStatus()); +} + +void Mpris1Player::RepeatModeChanged() { + emit StatusChange(GetStatus()); +} + +DBusStatus Mpris1Player::GetStatus() const { + return GetStatus(player_->GetState()); +} + +DBusStatus Mpris1Player::GetStatus(Engine::State state) const { + DBusStatus status; + switch (state) { + case Engine::Empty: + case Engine::Idle: + status.play = DBusStatus::Mpris_Stopped; + break; + case Engine::Playing: + status.play = DBusStatus::Mpris_Playing; + break; + case Engine::Paused: + status.play = DBusStatus::Mpris_Paused; + break; + } + + PlaylistManager* playlists_ = player_->playlists(); + PlaylistSequence::RepeatMode repeat_mode = playlists_->sequence()->repeat_mode(); + + status.random = playlists_->sequence()->shuffle_mode() == PlaylistSequence::Shuffle_Off ? 0 : 1; + status.repeat = repeat_mode == PlaylistSequence::Repeat_Track ? 1 : 0; + status.repeat_playlist = (repeat_mode == PlaylistSequence::Repeat_Album || + repeat_mode == PlaylistSequence::Repeat_Playlist || + repeat_mode == PlaylistSequence::Repeat_Track) ? 1 : 0; + return status; + +} + +void Mpris1Player::VolumeSet(int volume) { + player_->SetVolume(volume); +} + +int Mpris1Player::VolumeGet() const { + return player_->GetVolume(); +} + +void Mpris1Player::PositionSet(int pos) { + player_->Seek(pos/1000); +} + +int Mpris1Player::PositionGet() const { + return player_->engine()->position(); +} + +QVariantMap Mpris1Player::GetMetadata() const { + return last_metadata_; +} + +int Mpris1Player::GetCaps() const { + return GetCaps(player_->GetState()); +} + +int Mpris1Player::GetCaps(Engine::State state) const { + int caps = CAN_HAS_TRACKLIST; + PlaylistItemPtr current_item = player_->GetCurrentItem(); + PlaylistManager* playlists = player_->playlists(); + + // play is disabled when playlist is empty or when last.fm stream is already playing + if (playlists->active()->rowCount() != 0 + && !(state == Engine::Playing && (player_->GetCurrentItem()->options() & PlaylistItem::LastFMControls))) { + caps |= CAN_PLAY; + } + + if (current_item) { + caps |= CAN_PROVIDE_METADATA; + if (state == Engine::Playing && !(current_item->options() & PlaylistItem::PauseDisabled)) { + caps |= CAN_PAUSE; + } + if (state != Engine::Empty && current_item->Metadata().filetype() != Song::Type_Stream) { + caps |= CAN_SEEK; + } + } + + if (playlists->active()->next_row() != -1 || + playlists->active()->current_item_options() & PlaylistItem::ContainsMultipleTracks) { + caps |= CAN_GO_NEXT; + } + if (playlists->active()->previous_row() != -1) { + caps |= CAN_GO_PREV; + } + + return caps; +} + +void Mpris1Player::VolumeUp(int change) { + VolumeSet(VolumeGet() + change); +} + +void Mpris1Player::VolumeDown(int change) { + VolumeSet(VolumeGet() - change); +} + +void Mpris1Player::Mute() { + player_->Mute(); +} + +void Mpris1Player::ShowOSD() { + player_->ShowOSD(); +} + +int Mpris1TrackList::AddTrack(const QString& track, bool play) { + player_->playlists()->active()->InsertUrls( + QList() << QUrl(track), play); + return 0; +} + +void Mpris1TrackList::DelTrack(int index) { + player_->playlists()->active()->removeRows(index, 1); +} + +int Mpris1TrackList::GetCurrentTrack() const { + return player_->playlists()->active()->current_row(); +} + +int Mpris1TrackList::GetLength() const { + return player_->playlists()->active()->rowCount(); +} + +QVariantMap Mpris1TrackList::GetMetadata(int pos) const { + PlaylistItemPtr item = player_->GetItemAt(pos); + if (!item) + return QVariantMap(); + + return Mpris1::GetMetadata(item->Metadata()); +} + +void Mpris1TrackList::SetLoop(bool enable) { + player_->playlists()->active()->sequence()->SetRepeatMode( + enable ? PlaylistSequence::Repeat_Playlist : PlaylistSequence::Repeat_Off); +} + +void Mpris1TrackList::SetRandom(bool enable) { + player_->playlists()->active()->sequence()->SetShuffleMode( + enable ? PlaylistSequence::Shuffle_All : PlaylistSequence::Shuffle_Off); +} + +void Mpris1TrackList::PlayTrack(int index) { + player_->PlayAt(index, Engine::Manual, true); +} + +QVariantMap Mpris1::GetMetadata(const Song& song) { + QVariantMap ret; + + AddMetadata("location", song.filename(), &ret); + AddMetadata("title", song.PrettyTitle(), &ret); + AddMetadata("artist", song.artist(), &ret); + AddMetadata("album", song.album(), &ret); + AddMetadata("time", song.length(), &ret); + AddMetadata("mtime", song.length() * 1000, &ret); + AddMetadata("tracknumber", song.track(), &ret); + AddMetadata("year", song.year(), &ret); + AddMetadata("genre", song.genre(), &ret); + AddMetadata("disc", song.disc(), &ret); + AddMetadata("comment", song.comment(), &ret); + AddMetadata("audio-bitrate", song.bitrate(), &ret); + AddMetadata("audio-samplerate", song.samplerate(), &ret); + AddMetadata("bpm", song.bpm(), &ret); + AddMetadata("composer", song.composer(), &ret); + if (song.rating() != -1.0) { + AddMetadata("rating", song.rating() * 5, &ret); + } + + return ret; +} + +} // namespace mpris + + +QDBusArgument& operator<< (QDBusArgument& arg, const Version& version) { + arg.beginStructure(); + arg << version.major << version.minor; + arg.endStructure(); + return arg; +} + +const QDBusArgument& operator>> (const QDBusArgument& arg, Version& version) { + arg.beginStructure(); + arg >> version.major >> version.minor; + arg.endStructure(); + return arg; +} + +QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status) { + arg.beginStructure(); + arg << status.play; + arg << status.random; + arg << status.repeat; + arg << status.repeat_playlist; + arg.endStructure(); + return arg; +} + +const QDBusArgument& operator>> (const QDBusArgument& arg, DBusStatus& status) { + arg.beginStructure(); + arg >> status.play; + arg >> status.random; + arg >> status.repeat; + arg >> status.repeat_playlist; + arg.endStructure(); + return arg; +} diff --git a/src/core/mpris1.h b/src/core/mpris1.h new file mode 100644 index 000000000..c4e92e965 --- /dev/null +++ b/src/core/mpris1.h @@ -0,0 +1,195 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine 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 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#ifndef MPRIS1_H +#define MPRIS1_H + +#include "core/player.h" + +#include +#include +#include + +class Player; +class Playlist; + +struct DBusStatus { // From Amarok. + int play; // Playing = 0, Paused = 1, Stopped = 2 + int random; // Linearly = 0, Randomly = 1 + int repeat; // Go_To_Next = 0, Repeat_Current = 1 + int repeat_playlist; // Stop_When_Finished = 0, Never_Give_Up_Playing = 1, Never_Let_You_Down = 42 + + enum MprisPlayState { + Mpris_Playing = 0, + Mpris_Paused = 1, + Mpris_Stopped = 2, + }; +}; +Q_DECLARE_METATYPE(DBusStatus); + +QDBusArgument& operator <<(QDBusArgument& arg, const DBusStatus& status); +const QDBusArgument& operator >>(const QDBusArgument& arg, DBusStatus& status); + + +struct Version { + quint16 minor; + quint16 major; +}; +Q_DECLARE_METATYPE(Version); + +QDBusArgument& operator <<(QDBusArgument& arg, const Version& version); +const QDBusArgument& operator >>(const QDBusArgument& arg, Version& version); + + +namespace mpris { + +enum DBusCaps { + NONE = 0, + CAN_GO_NEXT = 1 << 0, + CAN_GO_PREV = 1 << 1, + CAN_PAUSE = 1 << 2, + CAN_PLAY = 1 << 3, + CAN_SEEK = 1 << 4, + CAN_PROVIDE_METADATA = 1 << 5, + CAN_HAS_TRACKLIST = 1 << 6, +}; + + +class ArtLoader; +class Mpris1Root; +class Mpris1Player; +class Mpris1TrackList; + + +class Mpris1 : public QObject { + Q_OBJECT + +public: + Mpris1(Player* player, ArtLoader* art_loader, QObject* parent = 0); + + static QVariantMap GetMetadata(const Song& song); + + Mpris1Root* root() const { return root_; } + Mpris1Player* player() const { return player_; } + Mpris1TrackList* tracklist() const { return tracklist_; } + +private: + Mpris1Root* root_; + Mpris1Player* player_; + Mpris1TrackList* tracklist_; +}; + + +class Mpris1Root : public QObject { + Q_OBJECT + +public: + Mpris1Root(Player* player, QObject* parent = 0); + + QString Identity(); + void Quit(); + Version MprisVersion(); + +private: + Player* player_; +}; + + +class Mpris1Player : public QObject { + Q_OBJECT + +public: + Mpris1Player(Player* player, QObject* parent = 0); + + void Pause(); + void Stop(); + void Prev(); + void Play(); + void Next(); + void Repeat(bool); + + // those methods will use engine's state obtained with player->GetState() method + DBusStatus GetStatus() const; + int GetCaps() const; + // those methods will use engine's state provided as an argument + DBusStatus GetStatus(Engine::State state) const; + int GetCaps(Engine::State state) const; + + void VolumeSet(int volume); + int VolumeGet() const; + void PositionSet(int pos); + int PositionGet() const; + QVariantMap GetMetadata() const; + + // Amarok extensions + void VolumeUp(int vol); + void VolumeDown(int vol); + void Mute(); + void ShowOSD(); + +public slots: + void CurrentSongChanged(const Song& song, const QString& art_uri); + +signals: + void CapsChange(int); + void TrackChange(const QVariantMap&); + void StatusChange(DBusStatus); + +private slots: + void PlaylistManagerInitialized(); + + void EngineStateChanged(Engine::State state); + void ShuffleModeChanged(); + void RepeatModeChanged(); + +private: + Player* player_; + + QVariantMap last_metadata_; +}; + + +class Mpris1TrackList : public QObject { + Q_OBJECT + +public: + Mpris1TrackList(Player* player, QObject* parent = 0); + + int AddTrack(const QString&, bool); + void DelTrack(int index); + int GetCurrentTrack() const; + int GetLength() const; + QVariantMap GetMetadata(int) const; + void SetLoop(bool enable); + void SetRandom(bool enable); + + // Amarok extension + void PlayTrack(int index); + +signals: + void TrackListChange(int i); + +private slots: + void PlaylistChanged(Playlist* playlist); + +private: + Player* player_; +}; + +} // namespace mpris + +#endif // MPRIS1_H diff --git a/src/core/mpris2.cpp b/src/core/mpris2.cpp index dba29e59b..03829ef0a 100644 --- a/src/core/mpris2.cpp +++ b/src/core/mpris2.cpp @@ -17,7 +17,7 @@ #include "config.h" #include "mpris_common.h" -#include "mpris.h" +#include "mpris1.h" #include "mpris2.h" #include "core/mpris2_player.h" #include "core/mpris2_root.h" @@ -31,6 +31,7 @@ #include #include +#include #ifdef HAVE_LIBINDICATE # include @@ -55,21 +56,23 @@ Mpris2::Mpris2(Player* player, ArtLoader* art_loader, QDBusConnection::sessionBus().registerService(kServiceName); QDBusConnection::sessionBus().registerObject(kMprisObjectPath, this); -#ifdef HAVE_LIBINDICATE - QIndicate::Server* indicate_server = QIndicate::Server::defaultInstance(); - indicate_server->setType("music.clementine"); - indicate_server->setDesktopFile(DesktopEntryAbsolutePath()); - indicate_server->show(); -#endif - connect(art_loader, SIGNAL(ArtLoaded(Song,QString)), SLOT(ArtLoaded(Song,QString))); connect(player->engine(), SIGNAL(StateChanged(Engine::State)), SLOT(EngineStateChanged(Engine::State))); connect(player, SIGNAL(VolumeChanged(int)), SLOT(VolumeChanged())); connect(player, SIGNAL(Seeked(qlonglong)), SIGNAL(Seeked(qlonglong))); - connect(player_->playlists(), SIGNAL(PlaylistManagerInitialized()), SLOT(PlaylistManagerInitialized())); - connect(player_->playlists(), SIGNAL(CurrentSongChanged(Song)), SLOT(CurrentSongChanged(Song))); + connect(player->playlists(), SIGNAL(PlaylistManagerInitialized()), SLOT(PlaylistManagerInitialized())); + connect(player->playlists(), SIGNAL(CurrentSongChanged(Song)), SLOT(CurrentSongChanged(Song))); +} + +void Mpris2::InitLibIndicate() { +#ifdef HAVE_LIBINDICATE + QIndicate::Server* indicate_server = QIndicate::Server::defaultInstance(); + indicate_server->setType("music.clementine"); + indicate_server->setDesktopFile(DesktopEntryAbsolutePath()); + indicate_server->show(); +#endif } // when PlaylistManager gets it ready, we connect PlaylistSequence with this diff --git a/src/core/mpris2.h b/src/core/mpris2.h index f794de148..760f6d663 100644 --- a/src/core/mpris2.h +++ b/src/core/mpris2.h @@ -73,6 +73,9 @@ class Mpris2 : public QObject { public: Mpris2(Player* player, ArtLoader* art_loader, Mpris1* mpris1, QObject* parent = 0); + + void InitLibIndicate(); + // Root Properties bool CanQuit() const; bool CanRaise() const; diff --git a/src/main.cpp b/src/main.cpp index 47f547251..d50225580 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -312,8 +312,7 @@ int main(int argc, char *argv[]) { qDBusRegisterMetaType(); mpris::ArtLoader art_loader; - mpris::Mpris1 mpris1(&player, &art_loader); - mpris::Mpris2 mpris2(&player, &art_loader, &mpris1); + mpris::Mpris mpris(&player, &art_loader); QObject::connect(&playlists, SIGNAL(CurrentSongChanged(Song)), &art_loader, SLOT(LoadArt(Song))); QObject::connect(&art_loader, SIGNAL(ThumbnailLoaded(Song, QString)), @@ -341,7 +340,7 @@ int main(int argc, char *argv[]) { tray_icon.get(), &osd); #ifdef HAVE_DBUS - QObject::connect(&mpris2, SIGNAL(RaiseMainWindow()), &w, SLOT(Raise())); + QObject::connect(&mpris, SIGNAL(RaiseMainWindow()), &w, SLOT(Raise())); #endif QObject::connect(&a, SIGNAL(messageReceived(QByteArray)), &w, SLOT(CommandlineOptionsReceived(QByteArray))); w.CommandlineOptionsReceived(options);