Move ArtLoader from mpris_common.h to its own file, add libxrme to 3rdparty, add a working XMPP remote.
This commit is contained in:
parent
304ce97b16
commit
72096bf1c8
51
3rdparty/libxrme/CMakeLists.txt
vendored
Normal file
51
3rdparty/libxrme/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
include_directories(${CMAKE_BINARY_DIR})
|
||||
include_directories(${GLOOX_INCLUDE_DIRS})
|
||||
|
||||
link_directories(${GLOOX_LIBRARY_DIRS})
|
||||
|
||||
set(SOURCES
|
||||
common.cpp
|
||||
connection.cpp
|
||||
handler.cpp
|
||||
mediaplayerhandler.cpp
|
||||
mediaplayerinterface.cpp
|
||||
remotecontrolhandler.cpp
|
||||
remotecontrolinterface.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
connection.h
|
||||
)
|
||||
|
||||
SET(PUBLIC_HEADERS
|
||||
connection.h
|
||||
common.h
|
||||
mediaplayerinterface.h
|
||||
remotecontrolinterface.h
|
||||
)
|
||||
|
||||
qt4_wrap_cpp(MOC ${HEADERS})
|
||||
|
||||
add_library(xrme ${SOURCES} ${MOC})
|
||||
target_link_libraries(xrme
|
||||
${QT_LIBRARIES}
|
||||
${GLOOX_LIBRARIES}
|
||||
)
|
||||
|
||||
# Install library
|
||||
install(TARGETS xrme
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
|
||||
# Install public headers
|
||||
install(FILES ${PUBLIC_HEADERS}
|
||||
DESTINATION include/xrme/
|
||||
)
|
||||
|
||||
# Also install public headers into the cmake binary dir so packages can use
|
||||
# libxrme in their source tree by doing include_directories(${CMAKE_BINARY_DIR})
|
||||
foreach(header ${PUBLIC_HEADERS})
|
||||
configure_file(${header} ${CMAKE_BINARY_DIR}/xrme/${header} COPYONLY)
|
||||
endforeach(header)
|
43
3rdparty/libxrme/common.cpp
vendored
Normal file
43
3rdparty/libxrme/common.cpp
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
/* This file is part of the XMPP Remote Media Extension.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
namespace xrme {
|
||||
|
||||
const char* kXmlnsXrme = "http://purplehatstands.com/xmlns/xrme";
|
||||
const char* kXmlnsXrmeMediaPlayer = "http://purplehatstands.com/xmlns/xrme/mediaplayer";
|
||||
const char* kXmlnsXrmeRemoteControl = "http://purplehatstands.com/xmlns/xrme/remotecontrol";
|
||||
|
||||
Metadata::Metadata()
|
||||
: track(0),
|
||||
disc(0),
|
||||
year(0),
|
||||
length_millisec(0),
|
||||
rating(0.0) {
|
||||
}
|
||||
|
||||
State::State()
|
||||
: playback_state(PlaybackState_Stopped),
|
||||
position_millisec(0),
|
||||
volume(0.0),
|
||||
can_go_next(false),
|
||||
can_go_previous(false),
|
||||
can_seek(false) {
|
||||
}
|
||||
|
||||
} // namespace xrme
|
65
3rdparty/libxrme/common.h
vendored
Normal file
65
3rdparty/libxrme/common.h
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/* This file is part of the XMPP Remote Media Extension.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef XRME_COMMON_H
|
||||
#define XRME_COMMON_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace xrme {
|
||||
|
||||
extern const char* kXmlnsXrme;
|
||||
extern const char* kXmlnsXrmeMediaPlayer;
|
||||
extern const char* kXmlnsXrmeRemoteControl;
|
||||
|
||||
struct Metadata {
|
||||
Metadata();
|
||||
|
||||
QString title;
|
||||
QString artist;
|
||||
QString album;
|
||||
QString albumartist;
|
||||
QString composer;
|
||||
QString genre;
|
||||
int track;
|
||||
int disc;
|
||||
int year;
|
||||
int length_millisec;
|
||||
double rating; // range 0.0 - 1.0
|
||||
};
|
||||
|
||||
struct State {
|
||||
State();
|
||||
|
||||
enum PlaybackState {
|
||||
PlaybackState_Stopped = 0,
|
||||
PlaybackState_Playing = 1,
|
||||
PlaybackState_Paused = 2,
|
||||
};
|
||||
|
||||
PlaybackState playback_state;
|
||||
int position_millisec;
|
||||
double volume; // range 0.0 - 1.0
|
||||
bool can_go_next;
|
||||
bool can_go_previous;
|
||||
bool can_seek;
|
||||
Metadata metadata;
|
||||
};
|
||||
|
||||
} // namespace xrme
|
||||
|
||||
#endif // XRME_COMMON_H
|
498
3rdparty/libxrme/connection.cpp
vendored
Normal file
498
3rdparty/libxrme/connection.cpp
vendored
Normal file
@ -0,0 +1,498 @@
|
||||
/* This file is part of the XMPP Remote Media Extension.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "connection.h"
|
||||
#include "mediaplayerhandler.h"
|
||||
#include "remotecontrolhandler.h"
|
||||
|
||||
#include <QSocketNotifier>
|
||||
#include <QTimer>
|
||||
#include <QtDebug>
|
||||
|
||||
#include <gloox/client.h>
|
||||
#include <gloox/connectionlistener.h>
|
||||
#include <gloox/connectiontcpclient.h>
|
||||
#include <gloox/disco.h>
|
||||
#include <gloox/discohandler.h>
|
||||
#include <gloox/loghandler.h>
|
||||
#include <gloox/rosterlistener.h>
|
||||
#include <gloox/rostermanager.h>
|
||||
|
||||
namespace xrme {
|
||||
|
||||
struct Connection::Private : public gloox::ConnectionListener,
|
||||
public gloox::LogHandler,
|
||||
public gloox::RosterListener,
|
||||
public gloox::DiscoHandler {
|
||||
Private(Connection* parent)
|
||||
: parent_(parent),
|
||||
server_(kDefaultServer),
|
||||
jid_resource_(kDefaultJIDResource),
|
||||
jid_host_(kDefaultJIDHost),
|
||||
verbose_(false),
|
||||
media_player_(NULL),
|
||||
remote_control_(NULL),
|
||||
spontaneous_disconnect_(true) {}
|
||||
|
||||
static const char* kDefaultServer;
|
||||
static const char* kDefaultJIDResource;
|
||||
static const char* kDefaultJIDHost;
|
||||
|
||||
Connection* parent_;
|
||||
|
||||
// Stuff the user sets before Connect()
|
||||
QString username_;
|
||||
QString password_;
|
||||
QString agent_name_;
|
||||
QString server_;
|
||||
QString jid_resource_;
|
||||
QString jid_host_;
|
||||
bool verbose_;
|
||||
|
||||
// Interfaces
|
||||
MediaPlayerInterface* media_player_;
|
||||
RemoteControlInterface* remote_control_;
|
||||
QList<Handler*> handlers_;
|
||||
|
||||
// Stuff that is valid when we're connected.
|
||||
QScopedPointer<gloox::Client> client_;
|
||||
QScopedPointer<QSocketNotifier> socket_notifier_;
|
||||
|
||||
// After discovering a peer we query it to find its capabilities. Only after
|
||||
// it replies to the query do we put it in peers_ and emit PeerFound().
|
||||
QList<Peer> peers_;
|
||||
QList<Peer> querying_peers_;
|
||||
|
||||
bool has_peer(const QString& jid_resource) const;
|
||||
|
||||
// We can't destroy the client_ in the onDisconnect() handler, so we have to
|
||||
// do it with a QTimer if we get a spontaneous disconnect.
|
||||
bool spontaneous_disconnect_;
|
||||
|
||||
// gloox::MessageHandler
|
||||
void handleMessage(gloox::Stanza* stanza, gloox::MessageSession* session = 0);
|
||||
|
||||
// gloox::ConnectionListener
|
||||
void onConnect();
|
||||
void onDisconnect(gloox::ConnectionError e);
|
||||
bool onTLSConnect(const gloox::CertInfo& info);
|
||||
|
||||
// gloox::LogHandler
|
||||
void handleLog(gloox::LogLevel level, gloox::LogArea area,
|
||||
const std::string& message);
|
||||
|
||||
// gloox::IqHandler
|
||||
bool handleIq(gloox::Stanza* stanza);
|
||||
bool handleIqID(gloox::Stanza* stanza, int context);
|
||||
|
||||
// gloox::RosterListener
|
||||
void handleItemAdded(const gloox::JID&) {}
|
||||
void handleItemSubscribed(const gloox::JID&) {}
|
||||
void handleItemRemoved(const gloox::JID&) {}
|
||||
void handleItemUpdated(const gloox::JID&) {}
|
||||
void handleItemUnsubscribed(const gloox::JID&) {}
|
||||
void handleRoster(const gloox::Roster&) {}
|
||||
void handleRosterPresence(const gloox::RosterItem& item, const std::string& resource, gloox::Presence presence, const std::string& msg);
|
||||
void handleSelfPresence(const gloox::RosterItem&, const std::string&, gloox::Presence, const std::string&) {}
|
||||
bool handleSubscriptionRequest(const gloox::JID&, const std::string&) { return false; }
|
||||
bool handleUnsubscriptionRequest(const gloox::JID&, const std::string&) { return false; }
|
||||
void handleNonrosterPresence(gloox::Stanza*) {}
|
||||
void handleRosterError(gloox::Stanza*) {}
|
||||
|
||||
// gloox::DiscoHandler
|
||||
void handleDiscoInfoResult(gloox::Stanza* stanza, int context);
|
||||
void handleDiscoItemsResult(gloox::Stanza*, int) {}
|
||||
void handleDiscoError(gloox::Stanza* stanza, int context);
|
||||
};
|
||||
|
||||
const char* Connection::Private::kDefaultServer = "talk.google.com";
|
||||
const char* Connection::Private::kDefaultJIDResource = "xrmeagent";
|
||||
const char* Connection::Private::kDefaultJIDHost = "gmail.com";
|
||||
|
||||
|
||||
Connection::Connection(QObject* parent)
|
||||
: QObject(parent),
|
||||
d(new Private(this)) {
|
||||
}
|
||||
|
||||
Connection::~Connection() {
|
||||
qDeleteAll(d->handlers_);
|
||||
}
|
||||
|
||||
void Connection::set_username(const QString& username) { d->username_ = username; }
|
||||
void Connection::set_password(const QString& password) { d->password_ = password; }
|
||||
void Connection::set_agent_name(const QString& agent_name) { d->agent_name_ = agent_name; }
|
||||
void Connection::set_server(const QString& server) { d->server_ = server; }
|
||||
void Connection::set_jid_resource(const QString& resource) { d->jid_resource_ = resource; }
|
||||
void Connection::set_jid_host(const QString& host) { d->jid_host_ = host; }
|
||||
void Connection::set_verbose(bool verbose) { d->verbose_ = verbose; }
|
||||
|
||||
QString Connection::username() const { return d->username_; }
|
||||
QString Connection::password() const { return d->password_; }
|
||||
QString Connection::agent_name() const { return d->agent_name_; }
|
||||
QString Connection::server() const { return d->server_; }
|
||||
QString Connection::jid_resource() const { return d->jid_resource_; }
|
||||
QString Connection::jid_host() const { return d->jid_host_; }
|
||||
bool Connection::is_verbose() const { return d->verbose_; }
|
||||
|
||||
void Connection::SetMediaPlayer(MediaPlayerInterface* interface) {
|
||||
if (d->media_player_) {
|
||||
qWarning() << "Connection::SetMediaPlayer: this connection already has a"
|
||||
" MediaPlayerInterface set";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!interface) {
|
||||
qWarning() << "Connection::SetMediaPlayer called with NULL interface";
|
||||
return;
|
||||
}
|
||||
|
||||
d->media_player_ = interface;
|
||||
d->handlers_ << new MediaPlayerHandler(interface);
|
||||
}
|
||||
|
||||
void Connection::SetRemoteControl(RemoteControlInterface* interface) {
|
||||
if (d->media_player_) {
|
||||
qWarning() << "Connection::RemoteControlInterface: this connection already"
|
||||
" has a RemoteControlInterface set";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!interface) {
|
||||
qWarning() << "Connection::SetRemoteControl called with NULL interface";
|
||||
return;
|
||||
}
|
||||
|
||||
d->remote_control_ = interface;
|
||||
d->handlers_ << new RemoteControlHandler(interface);
|
||||
}
|
||||
|
||||
bool Connection::is_connected() const {
|
||||
return (d->client_ && d->client_->state() == gloox::StateConnected);
|
||||
}
|
||||
|
||||
QString Connection::jid() const {
|
||||
if (is_connected()) {
|
||||
return QString::fromUtf8(d->client_->jid().full().c_str());
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
Connection::PeerList Connection::peers() const {
|
||||
if (is_connected()) {
|
||||
return d->peers_;
|
||||
}
|
||||
return PeerList();
|
||||
}
|
||||
|
||||
Connection::PeerList Connection::peers(Peer::Capability cap) const {
|
||||
PeerList ret;
|
||||
foreach (const Peer& peer, peers()) {
|
||||
if (peer.caps_ & cap) {
|
||||
ret << peer;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Connection::Connect() {
|
||||
if (d->username_.isEmpty() || d->password_.isEmpty() || d->agent_name_.isEmpty()) {
|
||||
qWarning() << __PRETTY_FUNCTION__
|
||||
<< ": A required field (username/password/agent_name) was empty";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Construct the JID - append the default host if the user didn't provide one
|
||||
QString jid = d->username_;
|
||||
if (!jid.contains('@')) {
|
||||
jid.append("@" + d->jid_host_);
|
||||
}
|
||||
jid.append("/" + d->jid_resource_);
|
||||
|
||||
// Create a new connection
|
||||
d->client_.reset(new gloox::Client(gloox::JID(jid.toUtf8().constData()),
|
||||
d->password_.toUtf8().constData()));
|
||||
gloox::ConnectionTCPClient* connection = new gloox::ConnectionTCPClient(
|
||||
d->client_.data(), d->client_->logInstance(),
|
||||
d->server_.toUtf8().constData());
|
||||
d->client_->setConnectionImpl(connection);
|
||||
|
||||
// Add listeners
|
||||
d->client_->registerConnectionListener(d.data());
|
||||
d->client_->rosterManager()->registerRosterListener(d.data());
|
||||
d->client_->logInstance().registerLogHandler(
|
||||
gloox::LogLevelDebug, gloox::LogAreaAll, d.data());
|
||||
|
||||
// Setup disco
|
||||
d->client_->disco()->setIdentity("client", "bot");
|
||||
d->client_->disco()->setVersion(d->agent_name_.toUtf8().constData(), std::string());
|
||||
d->client_->disco()->addFeature(kXmlnsXrme);
|
||||
|
||||
// Initialise the handlers
|
||||
foreach (Handler* handler, d->handlers_) {
|
||||
handler->Init(this, d->client_.data());
|
||||
}
|
||||
|
||||
// Set presence
|
||||
d->client_->setPresence(gloox::PresenceAvailable, -128);
|
||||
|
||||
// Connect
|
||||
if (!d->client_->connect(false)) {
|
||||
d->client_.reset();
|
||||
foreach (Handler* handler, d->handlers_) {
|
||||
handler->Reset();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Listen on the connection's socket
|
||||
d->socket_notifier_.reset(new QSocketNotifier(
|
||||
connection->socket(), QSocketNotifier::Read));
|
||||
connect(d->socket_notifier_.data(), SIGNAL(activated(int)),
|
||||
SLOT(SocketReadyReceive()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Connection::Disconnect() {
|
||||
if (is_connected()) {
|
||||
d->spontaneous_disconnect_ = false;
|
||||
d->client_->disconnect();
|
||||
d->client_.reset();
|
||||
d->spontaneous_disconnect_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::SocketReadyReceive() {
|
||||
d->client_->recv();
|
||||
}
|
||||
|
||||
void Connection::Private::onConnect() {
|
||||
parent_->RefreshPeers();
|
||||
emit parent_->Connected();
|
||||
}
|
||||
|
||||
void Connection::Private::onDisconnect(gloox::ConnectionError e) {
|
||||
QString error_text;
|
||||
switch (e) {
|
||||
case gloox::ConnNoError:
|
||||
case gloox::ConnUserDisconnected:
|
||||
break;
|
||||
case gloox::ConnStreamError:
|
||||
error_text = QString::fromUtf8(client_->streamErrorText().c_str());
|
||||
break;
|
||||
case gloox::ConnAuthenticationFailed:
|
||||
error_text = QString("Authentication error (%1)").arg(client_->authError());
|
||||
break;
|
||||
default:
|
||||
error_text = QString("Unknown error (%1)").arg(e);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (Handler* handler, handlers_) {
|
||||
handler->Reset();
|
||||
}
|
||||
|
||||
socket_notifier_->setEnabled(false);
|
||||
socket_notifier_.reset();
|
||||
peers_.clear();
|
||||
querying_peers_.clear();
|
||||
|
||||
emit parent_->Disconnected(error_text);
|
||||
|
||||
if (spontaneous_disconnect_) {
|
||||
QTimer::singleShot(0, parent_, SLOT(CleanupClient()));
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::CleanupClient() {
|
||||
d->client_.reset();
|
||||
}
|
||||
|
||||
bool Connection::Private::onTLSConnect(const gloox::CertInfo& info) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Connection::Private::handleLog(gloox::LogLevel level, gloox::LogArea area,
|
||||
const std::string& message) {
|
||||
if (!verbose_) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString prefix = "---";
|
||||
if (area == gloox::LogAreaXmlIncoming) {
|
||||
prefix = "<<<";
|
||||
} else if (area == gloox::LogAreaXmlOutgoing) {
|
||||
prefix = ">>>";
|
||||
}
|
||||
|
||||
qDebug() << "XMPP" << prefix.toAscii().constData() << message.c_str();
|
||||
}
|
||||
|
||||
void Connection::RefreshPeers() {
|
||||
// Clear the lists
|
||||
d->peers_.clear();
|
||||
d->querying_peers_.clear();
|
||||
|
||||
// Query presence
|
||||
qDebug() << "Sending presence query";
|
||||
d->client_->send(gloox::Stanza::createPresenceStanza(d->client_->jid().bareJID()));
|
||||
}
|
||||
|
||||
Connection::Peer::Peer()
|
||||
: caps_(0) {
|
||||
}
|
||||
|
||||
void Connection::Private::handleRosterPresence(
|
||||
const gloox::RosterItem& item, const std::string& res,
|
||||
gloox::Presence presence, const std::string&) {
|
||||
// Ignore presence from anyone else
|
||||
if (item.jid() != client_->jid().bare()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString resource = QString::fromUtf8(res.c_str());
|
||||
|
||||
switch (presence) {
|
||||
case gloox::PresenceUnknown:
|
||||
case gloox::PresenceUnavailable:
|
||||
// The peer went offline - did we know about him?
|
||||
qDebug() << "Peer unavailable" << resource;
|
||||
|
||||
for (int i=0 ; i<querying_peers_.count() ; ++i) {
|
||||
if (querying_peers_[i].jid_resource_ == resource) {
|
||||
querying_peers_.takeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0 ; i<peers_.count() ; ++i) {
|
||||
if (peers_[i].jid_resource_ == resource) {
|
||||
emit parent_->PeerRemoved(peers_.takeAt(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// The peer came online
|
||||
if (!has_peer(resource)) {
|
||||
qDebug() << "Got presence from" << resource;
|
||||
|
||||
// This is a peer on our own bare JID, and we haven't seen it before
|
||||
gloox::JID full_jid(item.jid());
|
||||
full_jid.setResource(res);
|
||||
|
||||
Peer peer;
|
||||
peer.jid_resource_ = resource;
|
||||
querying_peers_ << peer;
|
||||
|
||||
client_->disco()->getDiscoInfo(full_jid, std::string(), this, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Connection::Private::has_peer(const QString& jid_resource) const {
|
||||
foreach (const Peer& peer, peers_) {
|
||||
if (peer.jid_resource_ == jid_resource) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const Peer& peer, querying_peers_) {
|
||||
if (peer.jid_resource_ == jid_resource) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::Private::handleDiscoInfoResult(gloox::Stanza* stanza, int context) {
|
||||
// Is this from our own bare JID?
|
||||
if (stanza->from().bareJID() != client_->jid().bareJID()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString resource = QString::fromUtf8(stanza->from().resource().c_str());
|
||||
|
||||
qDebug() << "Got disco info from" << resource;
|
||||
|
||||
// Are we currently querying this peer?
|
||||
int querying_peer_index = -1;
|
||||
for (int i=0 ; i<querying_peers_.count() ; ++i) {
|
||||
if (querying_peers_[i].jid_resource_ == resource) {
|
||||
querying_peer_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (querying_peer_index == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove this peer from the querying list and try to fill in his info.
|
||||
Peer peer = querying_peers_.takeAt(querying_peer_index);
|
||||
|
||||
// Check for requried tags in the stanza.
|
||||
gloox::Tag* query = stanza->findChild("query");
|
||||
if (!query) {
|
||||
return;
|
||||
}
|
||||
|
||||
gloox::Tag* identity = query->findChild("identity");
|
||||
gloox::Tag::TagList features = query->findChildren("feature");
|
||||
if (identity == NULL || features.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill in the name.
|
||||
peer.agent_name_ = QString::fromUtf8(identity->findAttribute("name").c_str());
|
||||
|
||||
// Fill in the list of capabilities.
|
||||
foreach (gloox::Tag* feature, features) {
|
||||
const std::string feature_name = feature->findAttribute("var");
|
||||
if (feature_name == kXmlnsXrmeMediaPlayer) {
|
||||
peer.caps_ |= Peer::MediaPlayer;
|
||||
}
|
||||
if (feature_name == kXmlnsXrmeRemoteControl) {
|
||||
peer.caps_ |= Peer::RemoteControl;
|
||||
}
|
||||
}
|
||||
|
||||
// No recognised capabilities? Discard the peer.
|
||||
if (peer.caps_ == Peer::None) {
|
||||
return;
|
||||
}
|
||||
|
||||
peers_ << peer;
|
||||
emit parent_->PeerFound(peer);
|
||||
|
||||
qDebug() << "Peer found:" << peer.agent_name_ << peer.jid_resource_ << peer.caps_;
|
||||
}
|
||||
|
||||
void Connection::Private::handleDiscoError(gloox::Stanza* stanza, int context) {
|
||||
QString resource = QString::fromUtf8(stanza->from().resource().c_str());
|
||||
|
||||
// Remove this peer if we're currently querying it
|
||||
for (int i=0 ; i<querying_peers_.count() ; ++i) {
|
||||
if (querying_peers_[i].jid_resource_ == resource) {
|
||||
querying_peers_.removeAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xrme
|
152
3rdparty/libxrme/connection.h
vendored
Normal file
152
3rdparty/libxrme/connection.h
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
/* This file is part of the XMPP Remote Media Extension.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LIBXRME_CONNECTION_H
|
||||
#define LIBXRME_CONNECTION_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
#include <QScopedPointer>
|
||||
#include <QString>
|
||||
|
||||
namespace xrme {
|
||||
|
||||
class MediaPlayerInterface;
|
||||
class RemoteControlInterface;
|
||||
|
||||
class Connection : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Connection(QObject* parent = NULL);
|
||||
~Connection();
|
||||
|
||||
struct Peer {
|
||||
Peer();
|
||||
|
||||
enum Capability {
|
||||
None = 0x00,
|
||||
|
||||
MediaPlayer = 0x01,
|
||||
RemoteControl = 0x02,
|
||||
MediaStorage = 0x04,
|
||||
};
|
||||
Q_DECLARE_FLAGS(Capabilities, Capability);
|
||||
|
||||
QString agent_name_;
|
||||
QString jid_resource_;
|
||||
Capabilities caps_;
|
||||
};
|
||||
typedef QList<Peer> PeerList;
|
||||
|
||||
// The username and password MUST be set before calling Connect().
|
||||
void set_username(const QString& username);
|
||||
void set_password(const QString& password);
|
||||
|
||||
// The agent name MUST be set before calling Connect(). The agent name is a
|
||||
// friendly user-visible name assigned to this agent to help the user pick it
|
||||
// out amongst other media players or remotes. It might contain the name of
|
||||
// the application or the computer's hostname.
|
||||
void set_agent_name(const QString& agent_name);
|
||||
|
||||
// Sets the hostname of the XMPP server to connect to. Defaults to
|
||||
// "talk.google.com".
|
||||
void set_server(const QString& server);
|
||||
|
||||
// Sets the resource string to use in the JID. Defaults to "xrmeagent".
|
||||
void set_jid_resource(const QString& resource);
|
||||
|
||||
// Sets the host part to append to the JID if the user didn't specify one in
|
||||
// the username. The host part is the part of the JID after the @ and before
|
||||
// the /, eg: "username@host/resource". Defaults to "gmail.com".
|
||||
void set_jid_host(const QString& host);
|
||||
|
||||
// If this is set then detailed XMPP output will be printed. Defaults to off.
|
||||
void set_verbose(bool verbose);
|
||||
|
||||
// The following getters just return the data set above.
|
||||
QString username() const;
|
||||
QString password() const;
|
||||
QString agent_name() const;
|
||||
QString server() const;
|
||||
QString jid_resource() const;
|
||||
QString jid_host() const;
|
||||
bool is_verbose() const;
|
||||
|
||||
// Sets a media player on the connection. Calling this means the XRME agent
|
||||
// will advertise itself as a media player, and will be able to controlled by
|
||||
// remote control agents. Should be called before calling Connect(). The
|
||||
// Connection will NOT take ownership of the MediaPlayerInterface, and the
|
||||
// MediaPlayerInterface MUST stay alive for as long as the connection.
|
||||
void SetMediaPlayer(MediaPlayerInterface* interface);
|
||||
|
||||
// Sets a remote control on the connection. Calling this means the XRME agent
|
||||
// will advertise itself as a remote control, and will receive state changes
|
||||
// from media player agents. Should be called before calling Connect(). The
|
||||
// Connection will NOT take ownership of the RemoteControlInterface, and the
|
||||
// RemoteControlInterface MUST stay alive for as long as the connection.
|
||||
void SetRemoteControl(RemoteControlInterface* interface);
|
||||
|
||||
// Returns true after Connected() is emitted.
|
||||
bool is_connected() const;
|
||||
|
||||
// Returns the user's actual JID. This is only valid if is_connected() is
|
||||
// true. Before the connection is complete it will return a null QString.
|
||||
QString jid() const;
|
||||
|
||||
// Returns the list of all known peers. You can refresh this list by calling
|
||||
// RefreshPeers(). PeerFound() is emitted any time a peer is added to this
|
||||
// list.
|
||||
PeerList peers() const;
|
||||
|
||||
// Returns the list of known peers that support a certain capability.
|
||||
PeerList peers(Peer::Capability cap) const;
|
||||
|
||||
public slots:
|
||||
// Starts connecting and returns immediately. Will emit Connected() or
|
||||
// Disconnected() later. The username and password must already be set.
|
||||
// Returns true on success, false if there was some problem starting the
|
||||
// connection (eg. invalid/unspecified username/password/agent_name).
|
||||
bool Connect();
|
||||
|
||||
// Disconnects immediately and emits Disconnected().
|
||||
void Disconnect();
|
||||
|
||||
// Clears the internal list of peers and sends XMPP queries to discover the
|
||||
// current list. Emits PeerFound().
|
||||
void RefreshPeers();
|
||||
|
||||
signals:
|
||||
void Connected();
|
||||
void Disconnected(const QString& error);
|
||||
|
||||
void PeerFound(const xrme::Connection::Peer& peer);
|
||||
void PeerRemoved(const xrme::Connection::Peer& peer);
|
||||
|
||||
private slots:
|
||||
void SocketReadyReceive();
|
||||
void CleanupClient();
|
||||
|
||||
private:
|
||||
struct Private;
|
||||
friend struct Private;
|
||||
QScopedPointer<Private> d;
|
||||
};
|
||||
|
||||
} // namespace xrme
|
||||
|
||||
#endif
|
39
3rdparty/libxrme/handler.cpp
vendored
Normal file
39
3rdparty/libxrme/handler.cpp
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
/* This file is part of the XMPP Remote Media Extension.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "handler.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
namespace xrme {
|
||||
|
||||
Handler::Handler()
|
||||
: client_(NULL),
|
||||
connection_(NULL) {
|
||||
}
|
||||
|
||||
void Handler::Init(Connection* connection, gloox::Client* client) {
|
||||
connection_ = connection;
|
||||
client_ = client;
|
||||
}
|
||||
|
||||
void Handler::Reset() {
|
||||
connection_ = NULL;
|
||||
client_ = NULL;
|
||||
}
|
||||
|
||||
} // namespace xrme
|
43
3rdparty/libxrme/handler.h
vendored
Normal file
43
3rdparty/libxrme/handler.h
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
/* This file is part of the XMPP Remote Media Extension.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LIBXRME_HANDLER_H
|
||||
#define LIBXRME_HANDLER_H
|
||||
|
||||
namespace gloox {
|
||||
class Client;
|
||||
}
|
||||
|
||||
namespace xrme {
|
||||
class Connection;
|
||||
|
||||
class Handler {
|
||||
public:
|
||||
Handler();
|
||||
virtual ~Handler() {}
|
||||
|
||||
virtual void Init(Connection* connection, gloox::Client* client);
|
||||
virtual void Reset();
|
||||
|
||||
protected:
|
||||
Connection* connection_;
|
||||
gloox::Client* client_;
|
||||
};
|
||||
|
||||
} // namespace xrme
|
||||
|
||||
#endif // LIBXRME_HANDLER_H
|
149
3rdparty/libxrme/mediaplayerhandler.cpp
vendored
Normal file
149
3rdparty/libxrme/mediaplayerhandler.cpp
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
/* This file is part of the XMPP Remote Media Extension.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "connection.h"
|
||||
#include "mediaplayerhandler.h"
|
||||
#include "mediaplayerinterface.h"
|
||||
#include "remotecontrolhandler.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QtDebug>
|
||||
|
||||
#include <gloox/client.h>
|
||||
#include <gloox/disco.h>
|
||||
|
||||
namespace xrme {
|
||||
|
||||
MediaPlayerHandler::MediaPlayerHandler(MediaPlayerInterface* interface)
|
||||
: interface_(interface) {
|
||||
interface_->Attach(this);
|
||||
}
|
||||
|
||||
void MediaPlayerHandler::StateChanged() {
|
||||
if (!connection_) {
|
||||
return;
|
||||
}
|
||||
|
||||
State s = interface_->state();
|
||||
|
||||
foreach (const Connection::Peer& peer, connection_->peers(Connection::Peer::RemoteControl)) {
|
||||
gloox::JID to(client_->jid().bareJID());
|
||||
to.setResource(peer.jid_resource_.toUtf8().constData());
|
||||
|
||||
gloox::Tag* stanza = gloox::Stanza::createIqStanza(
|
||||
to, std::string(), gloox::StanzaIqSet, kXmlnsXrmeRemoteControl);
|
||||
gloox::Tag* state = new gloox::Tag(stanza, "state");
|
||||
state->addAttribute("xmlns", kXmlnsXrmeRemoteControl);
|
||||
state->addAttribute("playback_state", s.playback_state);
|
||||
state->addAttribute("position_millisec", s.position_millisec);
|
||||
state->addAttribute("volume", QString::number(s.volume, 'f').toUtf8().constData());
|
||||
state->addAttribute("can_go_next", s.can_go_next ? 1 : 0);
|
||||
state->addAttribute("can_go_previous", s.can_go_previous ? 1 : 0);
|
||||
state->addAttribute("can_seek", s.can_seek ? 1 : 0);
|
||||
|
||||
gloox::Tag* metadata = new gloox::Tag(state, "metadata");
|
||||
metadata->addAttribute("title", s.metadata.title.toUtf8().constData());
|
||||
metadata->addAttribute("artist", s.metadata.artist.toUtf8().constData());
|
||||
metadata->addAttribute("album", s.metadata.album.toUtf8().constData());
|
||||
metadata->addAttribute("albumartist", s.metadata.albumartist.toUtf8().constData());
|
||||
metadata->addAttribute("composer", s.metadata.composer.toUtf8().constData());
|
||||
metadata->addAttribute("genre", s.metadata.genre.toUtf8().constData());
|
||||
metadata->addAttribute("track", s.metadata.track);
|
||||
metadata->addAttribute("disc", s.metadata.disc);
|
||||
metadata->addAttribute("year", s.metadata.year);
|
||||
metadata->addAttribute("length_millisec", s.metadata.length_millisec);
|
||||
metadata->addAttribute("rating", QString::number(s.metadata.rating, 'f').toUtf8().constData());
|
||||
|
||||
client_->send(stanza);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaPlayerHandler::AlbumArtChanged() {
|
||||
if (!connection_) {
|
||||
return;
|
||||
}
|
||||
|
||||
QImage image = interface_->album_art();
|
||||
|
||||
// Scale the image down if it's too big
|
||||
if (!image.isNull() && (image.width() > kMaxAlbumArtSize ||
|
||||
image.height() > kMaxAlbumArtSize)) {
|
||||
image = image.scaled(kMaxAlbumArtSize,kMaxAlbumArtSize,
|
||||
Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
// Write the image data
|
||||
QByteArray image_data;
|
||||
if (!image.isNull()) {
|
||||
QBuffer buffer(&image_data);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
image.save(&buffer, "JPEG");
|
||||
}
|
||||
|
||||
// Convert to base64
|
||||
QByteArray image_data_base64 = image_data.toBase64();
|
||||
|
||||
// Send the IQs
|
||||
foreach (const Connection::Peer& peer, connection_->peers(Connection::Peer::RemoteControl)) {
|
||||
gloox::JID to(client_->jid().bareJID());
|
||||
to.setResource(peer.jid_resource_.toUtf8().constData());
|
||||
|
||||
gloox::Tag* stanza = gloox::Stanza::createIqStanza(
|
||||
to, std::string(), gloox::StanzaIqSet, kXmlnsXrmeRemoteControl);
|
||||
gloox::Tag* album_art = new gloox::Tag(stanza, "album_art");
|
||||
album_art->addAttribute("xmlns", kXmlnsXrmeRemoteControl);
|
||||
album_art->setCData(image_data_base64.constData());
|
||||
|
||||
client_->send(stanza);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaPlayerHandler::Init(Connection* connection, gloox::Client* client) {
|
||||
Handler::Init(connection, client);
|
||||
|
||||
client->registerIqHandler(this, kXmlnsXrmeMediaPlayer);
|
||||
client->disco()->addFeature(kXmlnsXrmeMediaPlayer);
|
||||
}
|
||||
|
||||
bool MediaPlayerHandler::handleIq(gloox::Stanza* stanza) {
|
||||
// Ignore stanzas from anyone else
|
||||
if (stanza->from().bareJID() != client_->jid().bareJID()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stanza->hasChild("playpause")) {
|
||||
interface_->PlayPause();
|
||||
} else if (stanza->hasChild("stop")) {
|
||||
interface_->Stop();
|
||||
} else if (stanza->hasChild("previous")) {
|
||||
interface_->Previous();
|
||||
} else if (stanza->hasChild("next")) {
|
||||
interface_->Next();
|
||||
} else if (stanza->hasChild("querystate")) {
|
||||
StateChanged();
|
||||
AlbumArtChanged();
|
||||
} else {
|
||||
qWarning() << "Unknown command received from"
|
||||
<< stanza->from().resource().c_str()
|
||||
<< stanza->xml().c_str();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace xrme
|
52
3rdparty/libxrme/mediaplayerhandler.h
vendored
Normal file
52
3rdparty/libxrme/mediaplayerhandler.h
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
/* This file is part of the XMPP Remote Media Extension.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LIBXRME_MEDIAPLAYERHANDLER_H
|
||||
#define LIBXRME_MEDIAPLAYERHANDLER_H
|
||||
|
||||
#include "handler.h"
|
||||
|
||||
#include <gloox/iqhandler.h>
|
||||
|
||||
namespace xrme {
|
||||
|
||||
class MediaPlayerInterface;
|
||||
|
||||
class MediaPlayerHandler : public Handler,
|
||||
public gloox::IqHandler {
|
||||
public:
|
||||
MediaPlayerHandler(MediaPlayerInterface* interface);
|
||||
|
||||
static const int kMaxAlbumArtSize = 300;
|
||||
|
||||
void StateChanged();
|
||||
void AlbumArtChanged();
|
||||
|
||||
// Handler
|
||||
void Init(Connection* connection, gloox::Client* client);
|
||||
|
||||
// gloox::IqHandler
|
||||
bool handleIq(gloox::Stanza* stanza);
|
||||
bool handleIqID(gloox::Stanza*, int) {}
|
||||
|
||||
private:
|
||||
MediaPlayerInterface* interface_;
|
||||
};
|
||||
|
||||
} // namespace xrme
|
||||
|
||||
#endif // LIBXRME_MEDIAPLAYERHANDLER_H
|
60
3rdparty/libxrme/mediaplayerinterface.cpp
vendored
Normal file
60
3rdparty/libxrme/mediaplayerinterface.cpp
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
/* This file is part of the XMPP Remote Media Extension.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "mediaplayerhandler.h"
|
||||
#include "mediaplayerinterface.h"
|
||||
|
||||
#include <QtDebug>
|
||||
|
||||
namespace xrme {
|
||||
|
||||
struct MediaPlayerInterface::Private {
|
||||
Private()
|
||||
: handler_(NULL) {}
|
||||
|
||||
MediaPlayerHandler* handler_;
|
||||
};
|
||||
|
||||
|
||||
MediaPlayerInterface::MediaPlayerInterface()
|
||||
: d(new Private) {
|
||||
}
|
||||
|
||||
MediaPlayerInterface::~MediaPlayerInterface() {
|
||||
}
|
||||
|
||||
void MediaPlayerInterface::StateChanged() {
|
||||
if (!d->handler_) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->handler_->StateChanged();
|
||||
}
|
||||
|
||||
void MediaPlayerInterface::AlbumArtChanged() {
|
||||
if (!d->handler_) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->handler_->AlbumArtChanged();
|
||||
}
|
||||
|
||||
void MediaPlayerInterface::Attach(MediaPlayerHandler* handler) {
|
||||
d->handler_ = handler;
|
||||
}
|
||||
|
||||
} // namespace xrme
|
67
3rdparty/libxrme/mediaplayerinterface.h
vendored
Normal file
67
3rdparty/libxrme/mediaplayerinterface.h
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
/* This file is part of the XMPP Remote Media Extension.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LIBXRME_MEDIAPLAYERINTERFACE_H
|
||||
#define LIBXRME_MEDIAPLAYERINTERFACE_H
|
||||
|
||||
#include <xrme/common.h>
|
||||
|
||||
#include <QImage>
|
||||
#include <QScopedPointer>
|
||||
|
||||
namespace xrme {
|
||||
|
||||
class MediaPlayerHandler;
|
||||
|
||||
class MediaPlayerInterface {
|
||||
public:
|
||||
MediaPlayerInterface();
|
||||
virtual ~MediaPlayerInterface();
|
||||
|
||||
// Control playback
|
||||
virtual void PlayPause() = 0;
|
||||
virtual void Stop() = 0;
|
||||
virtual void Next() = 0;
|
||||
virtual void Previous() = 0;
|
||||
|
||||
// Query the current state of the player. StateChanged() should be called
|
||||
// when any part of the state changes.
|
||||
virtual State state() const = 0;
|
||||
|
||||
// Query the album art of the currently playing song. This is separate from
|
||||
// state() because it is bigger and needs to be sent less often.
|
||||
// AlbumArtChanged() should be called when this changes. Return a null
|
||||
// QImage() if there is no song playing or the song has no album art.
|
||||
virtual QImage album_art() const = 0;
|
||||
|
||||
// Call when the values returned from the above getters have changed.
|
||||
virtual void StateChanged();
|
||||
virtual void AlbumArtChanged();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(MediaPlayerInterface);
|
||||
friend class MediaPlayerHandler;
|
||||
|
||||
void Attach(MediaPlayerHandler* handler);
|
||||
|
||||
struct Private;
|
||||
QScopedPointer<Private> d;
|
||||
};
|
||||
|
||||
} // namespace xrme
|
||||
|
||||
#endif // LIBXRME_MEDIAPLAYERINTERFACE_H
|
145
3rdparty/libxrme/remotecontrolhandler.cpp
vendored
Normal file
145
3rdparty/libxrme/remotecontrolhandler.cpp
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
/* This file is part of the XMPP Remote Media Extension.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "mediaplayerhandler.h"
|
||||
#include "remotecontrolinterface.h"
|
||||
#include "remotecontrolhandler.h"
|
||||
|
||||
#include <QImage>
|
||||
#include <QtDebug>
|
||||
|
||||
#include <gloox/client.h>
|
||||
#include <gloox/disco.h>
|
||||
|
||||
namespace xrme {
|
||||
|
||||
RemoteControlHandler::RemoteControlHandler(RemoteControlInterface* interface)
|
||||
: interface_(interface) {
|
||||
interface_->Attach(this);
|
||||
}
|
||||
|
||||
void RemoteControlHandler::Init(Connection* connection, gloox::Client* client) {
|
||||
Handler::Init(connection, client);
|
||||
|
||||
client->registerIqHandler(this, kXmlnsXrmeRemoteControl);
|
||||
client->disco()->addFeature(kXmlnsXrmeRemoteControl);
|
||||
}
|
||||
|
||||
void RemoteControlHandler::SendIQ(const QString& jid_resource,
|
||||
gloox::StanzaSubType type,
|
||||
const QString& command) {
|
||||
if (!client_) {
|
||||
return;
|
||||
}
|
||||
|
||||
gloox::JID to(client_->jid());
|
||||
to.setResource(jid_resource.toUtf8().constData());
|
||||
|
||||
gloox::Tag* stanza = gloox::Stanza::createIqStanza(
|
||||
to, client_->getID(), type, kXmlnsXrmeMediaPlayer);
|
||||
gloox::Tag* c = new gloox::Tag(stanza, command.toUtf8().constData());
|
||||
c->addAttribute("xmlns", kXmlnsXrmeMediaPlayer);
|
||||
|
||||
client_->send(stanza);
|
||||
}
|
||||
|
||||
void RemoteControlHandler::PlayPause(const QString& jid_resource) {
|
||||
SendIQ(jid_resource, gloox::StanzaIqSet, "playpause");
|
||||
}
|
||||
|
||||
void RemoteControlHandler::Stop(const QString& jid_resource) {
|
||||
SendIQ(jid_resource, gloox::StanzaIqSet, "stop");
|
||||
}
|
||||
|
||||
void RemoteControlHandler::Next(const QString& jid_resource) {
|
||||
SendIQ(jid_resource, gloox::StanzaIqSet, "next");
|
||||
}
|
||||
|
||||
void RemoteControlHandler::Previous(const QString& jid_resource) {
|
||||
SendIQ(jid_resource, gloox::StanzaIqSet, "previous");
|
||||
}
|
||||
|
||||
void RemoteControlHandler::QueryState(const QString& jid_resource) {
|
||||
SendIQ(jid_resource, gloox::StanzaIqGet, "querystate");
|
||||
}
|
||||
|
||||
int RemoteControlHandler::ParseInt(gloox::Tag* tag, const char* attribute_name) {
|
||||
return ParseString(tag, attribute_name).toInt();
|
||||
}
|
||||
|
||||
double RemoteControlHandler::ParseDouble(gloox::Tag* tag, const char* attribute_name) {
|
||||
return ParseString(tag, attribute_name).toFloat();
|
||||
}
|
||||
|
||||
QString RemoteControlHandler::ParseString(gloox::Tag* tag, const char* attribute_name) {
|
||||
return QString::fromUtf8(tag->findAttribute(attribute_name).c_str());
|
||||
}
|
||||
|
||||
bool RemoteControlHandler::handleIq(gloox::Stanza* stanza) {
|
||||
// Ignore stanzas from anyone else
|
||||
if (stanza->from().bareJID() != client_->jid().bareJID()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QString resource = QString::fromUtf8(stanza->from().resource().c_str());
|
||||
|
||||
qDebug() << resource << stanza->xml().c_str();
|
||||
|
||||
gloox::Tag* state = stanza->findChild("state");
|
||||
if (state) {
|
||||
gloox::Tag* metadata = state->findChild("metadata");
|
||||
if (metadata) {
|
||||
State s;
|
||||
s.playback_state = State::PlaybackState(ParseInt(state, "playback_state"));
|
||||
s.position_millisec = ParseInt(state, "position_millisec");
|
||||
s.volume = ParseDouble(state, "volume");
|
||||
s.can_go_next = ParseInt(state, "can_go_next");
|
||||
s.can_go_previous = ParseInt(state, "can_go_previous");
|
||||
s.can_seek = ParseInt(state, "can_seek");
|
||||
|
||||
s.metadata.title = ParseString(metadata, "title");
|
||||
s.metadata.artist = ParseString(metadata, "artist");
|
||||
s.metadata.album = ParseString(metadata, "album");
|
||||
s.metadata.albumartist = ParseString(metadata, "albumartist");
|
||||
s.metadata.composer = ParseString(metadata, "composer");
|
||||
s.metadata.genre = ParseString(metadata, "genre");
|
||||
s.metadata.track = ParseInt(metadata, "track");
|
||||
s.metadata.disc = ParseInt(metadata, "disc");
|
||||
s.metadata.year = ParseInt(metadata, "year");
|
||||
s.metadata.length_millisec = ParseInt(metadata, "length_millisec");
|
||||
s.metadata.rating = ParseDouble(metadata, "rating");
|
||||
|
||||
interface_->StateChanged(resource, s);
|
||||
}
|
||||
}
|
||||
|
||||
gloox::Tag* album_art = stanza->findChild("album_art");
|
||||
if (album_art) {
|
||||
QByteArray data(album_art->cdata().c_str(), album_art->cdata().size());
|
||||
|
||||
QImage image;
|
||||
if (!data.isEmpty()) {
|
||||
image.loadFromData(QByteArray::fromBase64(data), "JPEG");
|
||||
}
|
||||
|
||||
interface_->AlbumArtChanged(resource, image);
|
||||
}
|
||||
|
||||
return state || album_art;
|
||||
}
|
||||
|
||||
} // namespace xrme
|
63
3rdparty/libxrme/remotecontrolhandler.h
vendored
Normal file
63
3rdparty/libxrme/remotecontrolhandler.h
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
/* This file is part of the XMPP Remote Media Extension.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REMOTECONTROLHANDLER_H
|
||||
#define REMOTECONTROLHANDLER_H
|
||||
|
||||
#include "handler.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <gloox/iqhandler.h>
|
||||
|
||||
namespace xrme {
|
||||
|
||||
class RemoteControlInterface;
|
||||
|
||||
class RemoteControlHandler : public Handler,
|
||||
public gloox::IqHandler {
|
||||
public:
|
||||
RemoteControlHandler(RemoteControlInterface* interface);
|
||||
|
||||
void PlayPause(const QString& jid_resource);
|
||||
void Stop(const QString& jid_resource);
|
||||
void Next(const QString& jid_resource);
|
||||
void Previous(const QString& jid_resource);
|
||||
|
||||
void QueryState(const QString& jid_resource);
|
||||
|
||||
// Handler
|
||||
void Init(Connection* connection, gloox::Client* client);
|
||||
|
||||
// gloox::IqHandler
|
||||
bool handleIq(gloox::Stanza* stanza);
|
||||
bool handleIqID(gloox::Stanza*, int) {}
|
||||
|
||||
private:
|
||||
void SendIQ(const QString& jid_resource, gloox::StanzaSubType type,
|
||||
const QString& command);
|
||||
static int ParseInt(gloox::Tag* tag, const char* attribute_name);
|
||||
static double ParseDouble(gloox::Tag* tag, const char* attribute_name);
|
||||
static QString ParseString(gloox::Tag* tag, const char* attribute_name);
|
||||
|
||||
private:
|
||||
RemoteControlInterface* interface_;
|
||||
};
|
||||
|
||||
} // namespace xrme
|
||||
|
||||
#endif // REMOTECONTROLHANDLER_H
|
72
3rdparty/libxrme/remotecontrolinterface.cpp
vendored
Normal file
72
3rdparty/libxrme/remotecontrolinterface.cpp
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
/* This file is part of the XMPP Remote Media Extension.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "remotecontrolhandler.h"
|
||||
#include "remotecontrolinterface.h"
|
||||
|
||||
namespace xrme {
|
||||
|
||||
struct RemoteControlInterface::Private {
|
||||
Private()
|
||||
: handler_(NULL) {}
|
||||
|
||||
RemoteControlHandler* handler_;
|
||||
};
|
||||
|
||||
|
||||
RemoteControlInterface::RemoteControlInterface()
|
||||
: d(new Private) {
|
||||
}
|
||||
|
||||
RemoteControlInterface::~RemoteControlInterface() {
|
||||
}
|
||||
|
||||
void RemoteControlInterface::PlayPause(const QString& peer_jid_resource) {
|
||||
if (d->handler_) {
|
||||
d->handler_->PlayPause(peer_jid_resource);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteControlInterface::Stop(const QString& peer_jid_resource) {
|
||||
if (d->handler_) {
|
||||
d->handler_->Stop(peer_jid_resource);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteControlInterface::Next(const QString& peer_jid_resource) {
|
||||
if (d->handler_) {
|
||||
d->handler_->Next(peer_jid_resource);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteControlInterface::Previous(const QString& peer_jid_resource) {
|
||||
if (d->handler_) {
|
||||
d->handler_->Previous(peer_jid_resource);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteControlInterface::QueryState(const QString& peer_jid_resource) {
|
||||
if (d->handler_) {
|
||||
d->handler_->QueryState(peer_jid_resource);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteControlInterface::Attach(RemoteControlHandler* handler) {
|
||||
d->handler_ = handler;
|
||||
}
|
||||
|
||||
} // namespace xrme
|
66
3rdparty/libxrme/remotecontrolinterface.h
vendored
Normal file
66
3rdparty/libxrme/remotecontrolinterface.h
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
/* This file is part of the XMPP Remote Media Extension.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef XRME_REMOTECONTROLINTERFACE_H
|
||||
#define XRME_REMOTECONTROLINTERFACE_H
|
||||
|
||||
#include "common.h"
|
||||
#include "connection.h"
|
||||
|
||||
class QImage;
|
||||
|
||||
namespace xrme {
|
||||
|
||||
class RemoteControlHandler;
|
||||
|
||||
class RemoteControlInterface {
|
||||
public:
|
||||
RemoteControlInterface();
|
||||
virtual ~RemoteControlInterface();
|
||||
|
||||
// All functions here will work asynchronously and return immediately.
|
||||
|
||||
// Call these to control the playback of a MediaPlayer.
|
||||
void PlayPause(const QString& peer_jid_resource);
|
||||
void Stop(const QString& peer_jid_resource);
|
||||
void Next(const QString& peer_jid_resource);
|
||||
void Previous(const QString& peer_jid_resource);
|
||||
|
||||
// Call this to query the MediaPlayer. StateChanged() will be called later.
|
||||
void QueryState(const QString& peer_jid_resource);
|
||||
|
||||
// Called whenever the MediaPlayer's state changes.
|
||||
virtual void StateChanged(const QString& peer_jid_resource,
|
||||
const State& state) = 0;
|
||||
|
||||
// Called whenever the MediaPlayer's album art changes.
|
||||
virtual void AlbumArtChanged(const QString& peer_jid_resource,
|
||||
const QImage& art) = 0;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(RemoteControlInterface);
|
||||
friend class RemoteControlHandler;
|
||||
|
||||
void Attach(RemoteControlHandler* handler);
|
||||
|
||||
struct Private;
|
||||
QScopedPointer<Private> d;
|
||||
};
|
||||
|
||||
} // namespace xrme
|
||||
|
||||
#endif // XRME_REMOTECONTROLINTERFACE_H
|
@ -237,7 +237,7 @@ option(STATIC_SQLITE "Compile and use a static sqlite3 library" ON)
|
||||
|
||||
if(ENABLE_REMOTE AND GLOOX_LIBRARIES)
|
||||
set(HAVE_REMOTE ON)
|
||||
add_subdirectory(3rdparty/keychain)
|
||||
add_subdirectory(3rdparty/libxrme)
|
||||
endif(ENABLE_REMOTE AND GLOOX_LIBRARIES)
|
||||
|
||||
set(HAVE_STATIC_SQLITE ${STATIC_SQLITE})
|
||||
|
@ -12,6 +12,7 @@ if(WIN32)
|
||||
include_directories(../3rdparty/qtwin)
|
||||
endif(WIN32)
|
||||
|
||||
include_directories(${CMAKE_BINARY_DIR})
|
||||
include_directories(${GLIB_INCLUDE_DIRS})
|
||||
include_directories(${LIBXML_INCLUDE_DIRS})
|
||||
include_directories(${GOBJECT_INCLUDE_DIRS})
|
||||
@ -52,6 +53,7 @@ set(SOURCES
|
||||
analyzers/turbine.cpp
|
||||
|
||||
core/albumcoverloader.cpp
|
||||
core/artloader.cpp
|
||||
core/backgroundstreams.cpp
|
||||
core/backgroundthread.cpp
|
||||
core/commandlineoptions.cpp
|
||||
@ -66,7 +68,6 @@ set(SOURCES
|
||||
core/gnomeglobalshortcutbackend.cpp
|
||||
core/kittenloader.cpp
|
||||
core/mergedproxymodel.cpp
|
||||
core/mpris_common.cpp
|
||||
core/musicstorage.cpp
|
||||
core/network.cpp
|
||||
core/networkproxyfactory.cpp
|
||||
@ -258,6 +259,7 @@ set(HEADERS
|
||||
analyzers/turbine.h
|
||||
|
||||
core/albumcoverloader.h
|
||||
core/artloader.h
|
||||
core/backgroundstreams.h
|
||||
core/backgroundthread.h
|
||||
core/database.h
|
||||
@ -268,7 +270,6 @@ set(HEADERS
|
||||
core/kittenloader.h
|
||||
core/mergedproxymodel.h
|
||||
core/mimedata.h
|
||||
core/mpris_common.h
|
||||
core/network.h
|
||||
core/organise.h
|
||||
core/player.h
|
||||
@ -663,36 +664,6 @@ if(HAVE_DBUS)
|
||||
|
||||
# Gnome Screensaver DBus interface
|
||||
list(APPEND SOURCES ui/dbusscreensaver.cpp)
|
||||
|
||||
if(HAVE_REMOTE)
|
||||
# TODO: Use CMake macro when it supports -i
|
||||
find_program(QDBUSXML2CPP qdbusxml2cpp)
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${CMAKE_CURRENT_BINARY_DIR}/dbus/avahientrygroup.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/dbus/avahientrygroup.h
|
||||
COMMAND ${QDBUSXML2CPP}
|
||||
dbus/org.freedesktop.Avahi.EntryGroup.xml
|
||||
-p ${CMAKE_CURRENT_BINARY_DIR}/dbus/avahientrygroup
|
||||
-i dbus/metatypes.h
|
||||
DEPENDS dbus/org.freedesktop.Avahi.EntryGroup.xml
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
list(APPEND HEADERS ${CMAKE_CURRENT_BINARY_DIR}/dbus/avahientrygroup.h)
|
||||
list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dbus/avahientrygroup.cpp)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${CMAKE_CURRENT_BINARY_DIR}/dbus/avahiserver.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/dbus/avahiserver.h
|
||||
COMMAND ${QDBUSXML2CPP}
|
||||
dbus/org.freedesktop.Avahi.Server.xml
|
||||
-p ${CMAKE_CURRENT_BINARY_DIR}/dbus/avahiserver
|
||||
-i dbus/metatypes.h
|
||||
DEPENDS dbus/org.freedesktop.Avahi.Server.xml
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
list(APPEND HEADERS ${CMAKE_CURRENT_BINARY_DIR}/dbus/avahiserver.h)
|
||||
list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dbus/avahiserver.cpp)
|
||||
endif(HAVE_REMOTE)
|
||||
endif(HAVE_DBUS)
|
||||
|
||||
# Libgpod device backend
|
||||
@ -788,24 +759,15 @@ if(HAVE_SCRIPTING_PYTHON)
|
||||
endif(HAVE_SCRIPTING_PYTHON)
|
||||
|
||||
if(HAVE_REMOTE)
|
||||
include_directories(${GLOOX_INCLUDE_DIRS})
|
||||
link_directories(${GLOOX_LIBRARY_DIRS})
|
||||
include_directories(../3rdparty/keychain)
|
||||
list(APPEND UI remote/remoteconfig.ui)
|
||||
list(APPEND HEADERS remote/remoteconfig.h)
|
||||
list(APPEND SOURCES remote/remoteconfig.cpp)
|
||||
list(APPEND HEADERS remote/xmpp.h)
|
||||
list(APPEND SOURCES remote/xmpp.cpp)
|
||||
list(APPEND SOURCES remote/zeroconf.cpp)
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND SOURCES remote/bonjour.mm)
|
||||
endif(APPLE)
|
||||
|
||||
if(HAVE_DBUS)
|
||||
list(APPEND SOURCES remote/avahi.cpp)
|
||||
list(APPEND HEADERS remote/avahi.h)
|
||||
endif(HAVE_DBUS)
|
||||
list(APPEND HEADERS
|
||||
remote/remote.h
|
||||
remote/remoteconfig.h
|
||||
)
|
||||
list(APPEND SOURCES
|
||||
remote/remote.cpp
|
||||
remote/remoteconfig.cpp
|
||||
)
|
||||
endif(HAVE_REMOTE)
|
||||
|
||||
# OS-specific sources that should be searched for translatable strings even
|
||||
@ -846,6 +808,11 @@ list(APPEND OTHER_SOURCES
|
||||
devices/wmdmlister.h
|
||||
devices/wmdmloader.h
|
||||
devices/wmdmloader.cpp
|
||||
remote/remote.cpp
|
||||
remote/remote.h
|
||||
remote/remoteconfig.cpp
|
||||
remote/remoteconfig.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/ui_remoteconfig.h
|
||||
ui/macsystemtrayicon.h
|
||||
ui/macsystemtrayicon.mm
|
||||
ui/wiimotedevshortcutsconfig.cpp
|
||||
@ -968,7 +935,7 @@ endif(HAVE_SCRIPTING_PYTHON)
|
||||
|
||||
if(HAVE_REMOTE)
|
||||
target_link_libraries(clementine_lib ${GLOOX_LIBRARIES})
|
||||
target_link_libraries(clementine_lib keychain)
|
||||
target_link_libraries(clementine_lib xrme)
|
||||
endif(HAVE_REMOTE)
|
||||
|
||||
if (APPLE)
|
||||
|
@ -16,13 +16,11 @@
|
||||
*/
|
||||
|
||||
#include "albumcoverloader.h"
|
||||
#include "mpris_common.h"
|
||||
#include "artloader.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
namespace mpris {
|
||||
|
||||
ArtLoader::ArtLoader(QObject* parent)
|
||||
: QObject(parent),
|
||||
temp_file_pattern_(QDir::tempPath() + "/clementine-art-XXXXXX.jpg"),
|
||||
@ -56,6 +54,7 @@ void ArtLoader::TempArtLoaded(quint64 id, const QImage& image) {
|
||||
|
||||
QString uri;
|
||||
QString thumbnail_uri;
|
||||
QImage thumbnail;
|
||||
|
||||
if (!image.isNull()) {
|
||||
temp_art_.reset(new QTemporaryFile(temp_file_pattern_));
|
||||
@ -66,16 +65,13 @@ void ArtLoader::TempArtLoaded(quint64 id, const QImage& image) {
|
||||
// since it's the GUI thread, but the alternative is hard.
|
||||
temp_art_thumbnail_.reset(new QTemporaryFile(temp_file_pattern_));
|
||||
temp_art_thumbnail_->open();
|
||||
image.scaledToHeight(120, Qt::SmoothTransformation)
|
||||
.save(temp_art_thumbnail_->fileName(), "JPEG");
|
||||
thumbnail = image.scaledToHeight(120, Qt::SmoothTransformation);
|
||||
thumbnail.save(temp_art_thumbnail_->fileName(), "JPEG");
|
||||
|
||||
uri = "file://" + temp_art_->fileName();
|
||||
thumbnail_uri = "file://" + temp_art_thumbnail_->fileName();
|
||||
}
|
||||
|
||||
emit ArtLoaded(last_song_, uri);
|
||||
emit ThumbnailLoaded(last_song_, thumbnail_uri);
|
||||
emit ArtLoaded(last_song_, uri, image);
|
||||
emit ThumbnailLoaded(last_song_, thumbnail_uri, thumbnail);
|
||||
}
|
||||
|
||||
|
||||
} // namespace mpris
|
62
src/core/artloader.h
Normal file
62
src/core/artloader.h
Normal file
@ -0,0 +1,62 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ARTLOADER_H
|
||||
#define ARTLOADER_H
|
||||
|
||||
#include "backgroundthread.h"
|
||||
#include "song.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
class AlbumCoverLoader;
|
||||
|
||||
class QImage;
|
||||
class QTemporaryFile;
|
||||
|
||||
class ArtLoader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ArtLoader(QObject* parent = 0);
|
||||
~ArtLoader();
|
||||
|
||||
public slots:
|
||||
void LoadArt(const Song& song);
|
||||
|
||||
signals:
|
||||
void ArtLoaded(const Song& song, const QString& uri, const QImage& image);
|
||||
void ThumbnailLoaded(const Song& song, const QString& uri, const QImage& image);
|
||||
|
||||
private slots:
|
||||
void Initialised();
|
||||
void TempArtLoaded(quint64 id, const QImage& image);
|
||||
|
||||
private:
|
||||
QString temp_file_pattern_;
|
||||
|
||||
boost::scoped_ptr<QTemporaryFile> temp_art_;
|
||||
boost::scoped_ptr<QTemporaryFile> temp_art_thumbnail_;
|
||||
BackgroundThread<AlbumCoverLoader>* cover_loader_;
|
||||
quint64 id_;
|
||||
|
||||
Song last_song_;
|
||||
};
|
||||
|
||||
#endif // ARTLOADER_H
|
@ -18,6 +18,7 @@
|
||||
#include "mpris.h"
|
||||
#include "mpris1.h"
|
||||
#include "mpris2.h"
|
||||
#include "core/artloader.h"
|
||||
|
||||
namespace mpris {
|
||||
|
||||
|
@ -20,11 +20,11 @@
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class ArtLoader;
|
||||
class Player;
|
||||
|
||||
namespace mpris {
|
||||
|
||||
class ArtLoader;
|
||||
class Mpris1;
|
||||
class Mpris2;
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "artloader.h"
|
||||
#include "mpris1.h"
|
||||
#include "mpris_common.h"
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <QDBusArgument>
|
||||
#include <QObject>
|
||||
|
||||
class ArtLoader;
|
||||
class PlayerInterface;
|
||||
class Playlist;
|
||||
|
||||
@ -54,7 +55,6 @@ Q_DECLARE_METATYPE(Version);
|
||||
QDBusArgument& operator <<(QDBusArgument& arg, const Version& version);
|
||||
const QDBusArgument& operator >>(const QDBusArgument& arg, Version& version);
|
||||
|
||||
|
||||
namespace mpris {
|
||||
|
||||
enum DBusCaps {
|
||||
@ -68,8 +68,6 @@ enum DBusCaps {
|
||||
CAN_HAS_TRACKLIST = 1 << 6,
|
||||
};
|
||||
|
||||
|
||||
class ArtLoader;
|
||||
class Mpris1Root;
|
||||
class Mpris1Player;
|
||||
class Mpris1TrackList;
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "mpris_common.h"
|
||||
#include "mpris1.h"
|
||||
#include "mpris2.h"
|
||||
#include "core/artloader.h"
|
||||
#include "core/mpris2_player.h"
|
||||
#include "core/mpris2_root.h"
|
||||
#include "core/mpris2_tracklist.h"
|
||||
@ -56,7 +57,7 @@ Mpris2::Mpris2(PlayerInterface* player, ArtLoader* art_loader,
|
||||
QDBusConnection::sessionBus().registerService(kServiceName);
|
||||
QDBusConnection::sessionBus().registerObject(kMprisObjectPath, this);
|
||||
|
||||
connect(art_loader, SIGNAL(ArtLoaded(Song,QString)), SLOT(ArtLoaded(Song,QString)));
|
||||
connect(art_loader, SIGNAL(ArtLoaded(Song,QString,QImage)), SLOT(ArtLoaded(Song,QString)));
|
||||
|
||||
connect(player->engine(), SIGNAL(StateChanged(Engine::State)), SLOT(EngineStateChanged(Engine::State)));
|
||||
connect(player, SIGNAL(VolumeChanged(int)), SLOT(VolumeChanged()));
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
class ArtLoader;
|
||||
class MainWindow;
|
||||
class PlayerInterface;
|
||||
|
||||
@ -35,7 +36,6 @@ Q_DECLARE_METATYPE(TrackMetadata)
|
||||
|
||||
namespace mpris {
|
||||
|
||||
class ArtLoader;
|
||||
class Mpris1;
|
||||
|
||||
class Mpris2 : public QObject {
|
||||
|
@ -18,53 +18,13 @@
|
||||
#ifndef MPRIS_COMMON_H
|
||||
#define MPRIS_COMMON_H
|
||||
|
||||
#include "backgroundthread.h"
|
||||
#include "song.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
class AlbumCoverLoader;
|
||||
|
||||
class QImage;
|
||||
class QTemporaryFile;
|
||||
|
||||
namespace mpris {
|
||||
|
||||
class ArtLoader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ArtLoader(QObject* parent = 0);
|
||||
~ArtLoader();
|
||||
|
||||
public slots:
|
||||
void LoadArt(const Song& song);
|
||||
|
||||
signals:
|
||||
void ArtLoaded(const Song& song, const QString& uri);
|
||||
void ThumbnailLoaded(const Song& song, const QString& uri);
|
||||
|
||||
private slots:
|
||||
void Initialised();
|
||||
void TempArtLoaded(quint64 id, const QImage& image);
|
||||
|
||||
private:
|
||||
QString temp_file_pattern_;
|
||||
|
||||
boost::scoped_ptr<QTemporaryFile> temp_art_;
|
||||
boost::scoped_ptr<QTemporaryFile> temp_art_thumbnail_;
|
||||
BackgroundThread<AlbumCoverLoader>* cover_loader_;
|
||||
quint64 id_;
|
||||
|
||||
Song last_song_;
|
||||
};
|
||||
|
||||
|
||||
inline void AddMetadata(const QString& key, const QString& metadata, QVariantMap* map) {
|
||||
if (!metadata.isEmpty()) (*map)[key] = metadata;
|
||||
}
|
||||
|
20
src/main.cpp
20
src/main.cpp
@ -24,6 +24,7 @@
|
||||
#endif // Q_OS_WIN32
|
||||
|
||||
#include "config.h"
|
||||
#include "core/artloader.h"
|
||||
#include "core/commandlineoptions.h"
|
||||
#include "core/database.h"
|
||||
#include "core/encoding.h"
|
||||
@ -80,7 +81,6 @@ using boost::scoped_ptr;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
#include "core/mpris_common.h"
|
||||
#include "core/mpris.h"
|
||||
#include "core/mpris2.h"
|
||||
#include "dbus/metatypes.h"
|
||||
@ -92,11 +92,6 @@ using boost::scoped_ptr;
|
||||
const QDBusArgument& operator>> (const QDBusArgument& arg, QImage& image);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_REMOTE
|
||||
#include "remote/xmpp.h"
|
||||
#include "remote/zeroconf.h"
|
||||
#endif
|
||||
|
||||
class GstEnginePipeline;
|
||||
|
||||
// Load sqlite plugin on windows and mac.
|
||||
@ -316,25 +311,21 @@ int main(int argc, char *argv[]) {
|
||||
scoped_ptr<SystemTrayIcon> tray_icon(SystemTrayIcon::CreateSystemTrayIcon());
|
||||
OSD osd(tray_icon.get());
|
||||
|
||||
ArtLoader art_loader;
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
qDBusRegisterMetaType<QImage>();
|
||||
qDBusRegisterMetaType<TrackMetadata>();
|
||||
qDBusRegisterMetaType<TrackIds>();
|
||||
qDBusRegisterMetaType<QList<QByteArray> >();
|
||||
|
||||
mpris::ArtLoader art_loader;
|
||||
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)),
|
||||
QObject::connect(&art_loader, SIGNAL(ThumbnailLoaded(Song, QString, QImage)),
|
||||
&osd, SLOT(CoverArtPathReady(Song, QString)));
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_REMOTE
|
||||
XMPP xmpp;
|
||||
xmpp.Connect();
|
||||
#endif
|
||||
|
||||
// Window
|
||||
MainWindow w(
|
||||
database.get(),
|
||||
@ -343,7 +334,8 @@ int main(int argc, char *argv[]) {
|
||||
&radio_model,
|
||||
&player,
|
||||
tray_icon.get(),
|
||||
&osd);
|
||||
&osd,
|
||||
&art_loader);
|
||||
#ifdef HAVE_DBUS
|
||||
QObject::connect(&mpris, SIGNAL(RaiseMainWindow()), &w, SLOT(Raise()));
|
||||
#endif
|
||||
|
@ -1,95 +0,0 @@
|
||||
#include "avahi.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QHostInfo>
|
||||
|
||||
#include <QtDebug>
|
||||
|
||||
#include "dbus/avahientrygroup.h"
|
||||
#include "dbus/avahiserver.h"
|
||||
|
||||
Avahi::Avahi()
|
||||
: server_(NULL),
|
||||
entry_group_(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void Avahi::Publish(const QString& domain,
|
||||
const QString& type,
|
||||
const QString& name,
|
||||
quint16 port) {
|
||||
if (server_) {
|
||||
// Already published
|
||||
return;
|
||||
}
|
||||
|
||||
domain_ = domain;
|
||||
type_ = type;
|
||||
name_ = name;
|
||||
port_ = port;
|
||||
|
||||
server_ = new OrgFreedesktopAvahiServerInterface(
|
||||
"org.freedesktop.Avahi",
|
||||
"/",
|
||||
QDBusConnection::systemBus(),
|
||||
this);
|
||||
|
||||
QDBusPendingReply<QDBusObjectPath> reply = server_->EntryGroupNew();
|
||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply);
|
||||
connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
|
||||
SLOT(EntryGroupNewFinished(QDBusPendingCallWatcher*)));
|
||||
}
|
||||
|
||||
void Avahi::EntryGroupNewFinished(QDBusPendingCallWatcher* call) {
|
||||
call->deleteLater();
|
||||
QDBusPendingReply<QDBusObjectPath> reply = *call;
|
||||
|
||||
if (reply.isError()) {
|
||||
qWarning() << "Failed to create new Avahi entry group:" << call->error().message();
|
||||
return;
|
||||
}
|
||||
|
||||
entry_group_ = new OrgFreedesktopAvahiEntryGroupInterface(
|
||||
"org.freedesktop.Avahi",
|
||||
reply.value().path(),
|
||||
QDBusConnection::systemBus(),
|
||||
this);
|
||||
|
||||
QDBusPendingReply<> add_reply = entry_group_->AddService(
|
||||
-1, // Interface (Unspecified, ie. all interfaces)
|
||||
-1, // Protocol (Unspecified, ie. IPv4 & IPv6)
|
||||
0, // Flags
|
||||
name_, // Service name
|
||||
type_, // Service type
|
||||
domain_, // Domain, ie. local
|
||||
QString::null, // Hostname (Avahi fills it if it's null)
|
||||
port_, // Port
|
||||
QList<QByteArray>()); // TXT record
|
||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(add_reply);
|
||||
connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
|
||||
SLOT(AddServiceFinished(QDBusPendingCallWatcher*)));
|
||||
}
|
||||
|
||||
void Avahi::AddServiceFinished(QDBusPendingCallWatcher* call) {
|
||||
call->deleteLater();
|
||||
|
||||
if (call->isError()) {
|
||||
qWarning() << "Failed to add Avahi service:" << call->error().message();
|
||||
return;
|
||||
}
|
||||
|
||||
QDBusPendingReply<> commit_reply = entry_group_->Commit();
|
||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(commit_reply);
|
||||
connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
|
||||
SLOT(CommitFinished(QDBusPendingCallWatcher*)));
|
||||
}
|
||||
|
||||
void Avahi::CommitFinished(QDBusPendingCallWatcher* call) {
|
||||
call->deleteLater();
|
||||
|
||||
if (call->isError()) {
|
||||
qWarning() << "Failed to commit Avahi changes:" << call->error().message();
|
||||
} else {
|
||||
qDebug() << "Remote interface published on Avahi";
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
#ifndef AVAHI_H
|
||||
#define AVAHI_H
|
||||
|
||||
#include "zeroconf.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class OrgFreedesktopAvahiEntryGroupInterface;
|
||||
class OrgFreedesktopAvahiServerInterface;
|
||||
|
||||
class QDBusPendingCallWatcher;
|
||||
|
||||
class Avahi : public QObject, public Zeroconf {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Avahi();
|
||||
|
||||
virtual void Publish(const QString& domain,
|
||||
const QString& type,
|
||||
const QString& name,
|
||||
quint16 port);
|
||||
|
||||
private slots:
|
||||
void EntryGroupNewFinished(QDBusPendingCallWatcher* call);
|
||||
void AddServiceFinished(QDBusPendingCallWatcher* call);
|
||||
void CommitFinished(QDBusPendingCallWatcher* call);
|
||||
|
||||
private:
|
||||
OrgFreedesktopAvahiServerInterface* server_;
|
||||
OrgFreedesktopAvahiEntryGroupInterface* entry_group_;
|
||||
|
||||
QString domain_;
|
||||
QString type_;
|
||||
QString name_;
|
||||
quint16 port_;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,18 +0,0 @@
|
||||
#ifndef BONJOUR_H
|
||||
#define BONJOUR_H
|
||||
|
||||
#include "zeroconf.h"
|
||||
|
||||
class BonjourWrapper;
|
||||
|
||||
class Bonjour : public Zeroconf {
|
||||
public:
|
||||
Bonjour();
|
||||
virtual ~Bonjour();
|
||||
void Publish(const QString& domain, const QString& type, const QString& name, quint16 port);
|
||||
|
||||
private:
|
||||
BonjourWrapper* wrapper_;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,84 +0,0 @@
|
||||
#include "bonjour.h"
|
||||
|
||||
#include <QtDebug>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NetServicePublicationDelegate : NSObject <NSNetServiceDelegate> {
|
||||
NSMutableArray* services_;
|
||||
}
|
||||
|
||||
- (void)netServiceWillPublish:(NSNetService*)netService;
|
||||
- (void)netService:(NSNetService*)netService
|
||||
didNotPublish:(NSDictionary*)errorDict;
|
||||
- (void)netServiceDidStop:(NSNetService*)netService;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NetServicePublicationDelegate
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
services_ = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[services_ release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)netServiceWillPublish: (NSNetService*)netService {
|
||||
[services_ addObject: netService];
|
||||
qDebug() << Q_FUNC_INFO
|
||||
<< [[netService name] UTF8String];
|
||||
}
|
||||
|
||||
- (void)netService: (NSNetService*)netService didNotPublish: (NSDictionary*)errorDict {
|
||||
[services_ removeObject: netService];
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
NSLog(@"%@", errorDict);
|
||||
}
|
||||
|
||||
- (void)netServiceDidStop: (NSNetService*)netService {
|
||||
[services_ removeObject: netService];
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
class BonjourWrapper {
|
||||
public:
|
||||
BonjourWrapper() {
|
||||
delegate_ = [[NetServicePublicationDelegate alloc] init];
|
||||
}
|
||||
|
||||
NetServicePublicationDelegate* delegate() const { return delegate_; }
|
||||
|
||||
private:
|
||||
NetServicePublicationDelegate* delegate_;
|
||||
};
|
||||
|
||||
#define QSTRING_TO_NSSTRING(x) \
|
||||
[[NSString alloc] initWithUTF8String:x.toUtf8().constData()]
|
||||
|
||||
Bonjour::Bonjour()
|
||||
: wrapper_(new BonjourWrapper) {
|
||||
}
|
||||
|
||||
Bonjour::~Bonjour() {
|
||||
delete wrapper_;
|
||||
}
|
||||
|
||||
void Bonjour::Publish(const QString& domain, const QString& type, const QString& name, quint16 port) {
|
||||
NSNetService* service = [[NSNetService alloc] initWithDomain: QSTRING_TO_NSSTRING(domain)
|
||||
type: QSTRING_TO_NSSTRING(type)
|
||||
name: QSTRING_TO_NSSTRING(name)
|
||||
port: port];
|
||||
if (service) {
|
||||
[service setDelegate: wrapper_->delegate()];
|
||||
[service publish];
|
||||
}
|
||||
}
|
165
src/remote/remote.cpp
Normal file
165
src/remote/remote.cpp
Normal file
@ -0,0 +1,165 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "remote.h"
|
||||
#include "remoteconfig.h"
|
||||
#include "core/player.h"
|
||||
#include "engines/enginebase.h"
|
||||
#include "playlist/playlist.h"
|
||||
#include "playlist/playlistmanager.h"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QTimer>
|
||||
|
||||
Remote::Remote(Player* player, QObject* parent)
|
||||
: QObject(parent),
|
||||
player_(player),
|
||||
connection_(new xrme::Connection(this)),
|
||||
retry_count_(0)
|
||||
{
|
||||
connection_->SetMediaPlayer(this);
|
||||
connection_->set_verbose(true);
|
||||
connect(connection_, SIGNAL(Connected()), SLOT(Connected()));
|
||||
connect(connection_, SIGNAL(Disconnected(QString)), SLOT(Disconnected(QString)));
|
||||
|
||||
connect(player_, SIGNAL(Playing()), SLOT(SetStateChanged()));
|
||||
connect(player_, SIGNAL(Paused()), SLOT(SetStateChanged()));
|
||||
connect(player_, SIGNAL(Stopped()), SLOT(SetStateChanged()));
|
||||
connect(player_, SIGNAL(PlaylistFinished()), SLOT(SetStateChanged()));
|
||||
connect(player_, SIGNAL(VolumeChanged(int)), SLOT(SetStateChanged()));
|
||||
connect(player_, SIGNAL(Seeked(qlonglong)), SLOT(SetStateChanged()));
|
||||
connect(player_->playlists(), SIGNAL(CurrentSongChanged(Song)), SLOT(SetStateChanged()));
|
||||
|
||||
ReloadSettings();
|
||||
}
|
||||
|
||||
void Remote::ReloadSettings() {
|
||||
QSettings s;
|
||||
s.beginGroup(RemoteConfig::kSettingsGroup);
|
||||
|
||||
QString username = s.value("username").toString();
|
||||
QString password = s.value("password").toString();
|
||||
QString agent_name = s.value("agent_name", RemoteConfig::DefaultAgentName()).toString();
|
||||
|
||||
// Have the settings changed?
|
||||
if (username != connection_->username() ||
|
||||
password != connection_->password() ||
|
||||
agent_name != connection_->agent_name()) {
|
||||
connection_->set_username(username);
|
||||
connection_->set_agent_name(agent_name);
|
||||
connection_->set_password(password);
|
||||
|
||||
if (connection_->is_connected()) {
|
||||
// We'll reconnect later
|
||||
connection_->Disconnect();
|
||||
} else if (is_configured()) {
|
||||
connection_->Connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Remote::is_configured() const {
|
||||
return !connection_->username().isEmpty() &&
|
||||
!connection_->password().isEmpty() &&
|
||||
!connection_->agent_name().isEmpty();
|
||||
}
|
||||
|
||||
void Remote::Connected() {
|
||||
retry_count_ = 0;
|
||||
}
|
||||
|
||||
void Remote::Disconnected(const QString& error) {
|
||||
if (retry_count_++ >= kMaxRetries) {
|
||||
// Show an error and give up if we're above the retry count
|
||||
if (!error.isEmpty()) {
|
||||
emit Error("XMPP remote control disconnected: " + error);
|
||||
}
|
||||
} else if (is_configured()) {
|
||||
// Try again
|
||||
QTimer::singleShot(0, connection_, SLOT(Connect()));
|
||||
}
|
||||
}
|
||||
|
||||
void Remote::PlayPause() {
|
||||
player_->PlayPause();
|
||||
}
|
||||
|
||||
void Remote::Stop() {
|
||||
player_->Stop();
|
||||
}
|
||||
|
||||
void Remote::Next() {
|
||||
player_->Next();
|
||||
}
|
||||
|
||||
void Remote::Previous() {
|
||||
player_->Previous();
|
||||
}
|
||||
|
||||
xrme::State Remote::state() const {
|
||||
const Playlist* active = player_->playlists()->active();
|
||||
const Engine::State state = player_->GetState();
|
||||
const PlaylistItemPtr current_item = player_->GetCurrentItem();
|
||||
|
||||
xrme::State ret;
|
||||
ret.can_go_next = active->next_row() != -1 ||
|
||||
active->current_item_options() & PlaylistItem::ContainsMultipleTracks;
|
||||
ret.can_go_previous = active->previous_row() != -1;
|
||||
ret.can_seek = current_item &&
|
||||
current_item->Metadata().filetype() != Song::Type_Stream;
|
||||
|
||||
switch (state) {
|
||||
case Engine::Playing: ret.playback_state = xrme::State::PlaybackState_Playing; break;
|
||||
case Engine::Paused: ret.playback_state = xrme::State::PlaybackState_Paused; break;
|
||||
case Engine::Idle:
|
||||
case Engine::Empty: ret.playback_state = xrme::State::PlaybackState_Stopped; break;
|
||||
}
|
||||
|
||||
ret.position_millisec = player_->engine()->position_nanosec() / kNsecPerMsec;
|
||||
ret.volume = double(player_->GetVolume()) / 100;
|
||||
|
||||
if (current_item) {
|
||||
const Song m = current_item->Metadata();
|
||||
|
||||
ret.metadata.title = m.title();
|
||||
ret.metadata.artist = m.artist();
|
||||
ret.metadata.album = m.album();
|
||||
ret.metadata.albumartist = m.albumartist();
|
||||
ret.metadata.composer = m.composer();
|
||||
ret.metadata.genre = m.genre();
|
||||
ret.metadata.track = m.track();
|
||||
ret.metadata.disc = m.disc();
|
||||
ret.metadata.year = m.year();
|
||||
ret.metadata.length_millisec = m.length_nanosec() / kNsecPerMsec;
|
||||
ret.metadata.rating = m.rating();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QImage Remote::album_art() const {
|
||||
return last_image_;
|
||||
}
|
||||
|
||||
void Remote::SetStateChanged() {
|
||||
StateChanged();
|
||||
}
|
||||
|
||||
void Remote::ArtLoaded(const Song&, const QString&, const QImage& image) {
|
||||
last_image_ = image;
|
||||
AlbumArtChanged();
|
||||
}
|
73
src/remote/remote.h
Normal file
73
src/remote/remote.h
Normal file
@ -0,0 +1,73 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REMOTE_H
|
||||
#define REMOTE_H
|
||||
|
||||
#include "core/song.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <xrme/connection.h>
|
||||
#include <xrme/mediaplayerinterface.h>
|
||||
|
||||
class ArtLoader;
|
||||
class Player;
|
||||
|
||||
class Remote : public QObject,
|
||||
protected xrme::MediaPlayerInterface {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Remote(Player* player, QObject* parent = 0);
|
||||
|
||||
static const int kMaxRetries = 3;
|
||||
|
||||
public slots:
|
||||
void ReloadSettings();
|
||||
void ArtLoaded(const Song&, const QString&, const QImage& image);
|
||||
|
||||
signals:
|
||||
void Error(const QString& message);
|
||||
|
||||
protected:
|
||||
// xrme::MediaPlayerInterface
|
||||
void PlayPause();
|
||||
void Stop();
|
||||
void Next();
|
||||
void Previous();
|
||||
xrme::State state() const;
|
||||
QImage album_art() const;
|
||||
|
||||
private slots:
|
||||
void Connected();
|
||||
void Disconnected(const QString& error);
|
||||
|
||||
void SetStateChanged();
|
||||
|
||||
private:
|
||||
bool is_configured() const;
|
||||
|
||||
private:
|
||||
Player* player_;
|
||||
xrme::Connection* connection_;
|
||||
|
||||
QImage last_image_;
|
||||
int retry_count_;
|
||||
};
|
||||
|
||||
#endif // REMOTE_H
|
@ -19,8 +19,8 @@
|
||||
#include "ui_remoteconfig.h"
|
||||
#include "ui/iconloader.h"
|
||||
|
||||
#include "keychain.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QHostInfo>
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkReply>
|
||||
#include <QSettings>
|
||||
@ -46,6 +46,11 @@ RemoteConfig::RemoteConfig(QWidget *parent)
|
||||
resize(sizeHint());
|
||||
}
|
||||
|
||||
QString RemoteConfig::DefaultAgentName() {
|
||||
return QString("%1 on %2").arg(QCoreApplication::applicationName(),
|
||||
QHostInfo::localHostName());
|
||||
}
|
||||
|
||||
RemoteConfig::~RemoteConfig() {
|
||||
delete ui_;
|
||||
}
|
||||
@ -95,11 +100,13 @@ void RemoteConfig::AuthenticationComplete(bool success) {
|
||||
ui_->busy->hide();
|
||||
waiting_for_auth_ = false;
|
||||
|
||||
if (success) {
|
||||
validated_password_ = ui_->password->text();
|
||||
ui_->password->clear();
|
||||
} else {
|
||||
if (!success) {
|
||||
QMessageBox::warning(this, tr("Authentication failed"), tr("Your Google credentials were incorrect"));
|
||||
} else {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("password", ui_->password->text());
|
||||
ui_->password->clear();
|
||||
}
|
||||
|
||||
emit ValidationComplete(success);
|
||||
@ -108,24 +115,25 @@ void RemoteConfig::AuthenticationComplete(bool success) {
|
||||
void RemoteConfig::Load() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
QVariant username = s.value("username");
|
||||
if (username.isValid()) {
|
||||
ui_->username->setText(username.toString());
|
||||
}
|
||||
|
||||
ui_->username->setText(s.value("username").toString());
|
||||
ui_->agent_name->setText(s.value("agent_name", DefaultAgentName()).toString());
|
||||
}
|
||||
|
||||
void RemoteConfig::Save() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
const QString& username = ui_->username->text();
|
||||
s.setValue("username", username);
|
||||
Keychain* keychain = Keychain::getDefault();
|
||||
keychain->setPassword(username, validated_password_);
|
||||
validated_password_.clear();
|
||||
|
||||
s.setValue("username", ui_->username->text());
|
||||
s.setValue("agent_name", ui_->agent_name->text());
|
||||
}
|
||||
|
||||
void RemoteConfig::SignOut() {
|
||||
ui_->username->clear();
|
||||
ui_->password->clear();
|
||||
ui_->sign_out->setEnabled(false);
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("password", QString());
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ class RemoteConfig : public QWidget {
|
||||
RemoteConfig(QWidget* parent = 0);
|
||||
~RemoteConfig();
|
||||
|
||||
static QString DefaultAgentName();
|
||||
|
||||
bool NeedsValidation() const;
|
||||
|
||||
static const char* kSettingsGroup;
|
||||
@ -54,8 +56,6 @@ class RemoteConfig : public QWidget {
|
||||
Ui_RemoteConfig* ui_;
|
||||
bool waiting_for_auth_;
|
||||
NetworkAccessManager* network_;
|
||||
|
||||
QString validated_password_;
|
||||
};
|
||||
|
||||
#endif // REMOTECONFIG_H
|
||||
|
@ -6,20 +6,27 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>773</width>
|
||||
<height>555</height>
|
||||
<width>507</width>
|
||||
<height>437</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Clementine can be controlled remotely by an Android phone. To enable this feature log in with the same Google account that is configured on your phone.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Account details</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
@ -27,20 +34,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Google password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="password">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
@ -65,6 +58,49 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Google password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="password">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Player name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="agent_name"/>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>If you use the remote on more than one computer, this name will help you choose which one to connect to on your phone.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -1,93 +0,0 @@
|
||||
#include "xmpp.h"
|
||||
|
||||
#include <gloox/connectiontcpclient.h>
|
||||
|
||||
#include <QSettings>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "keychain.h"
|
||||
#include "remoteconfig.h"
|
||||
|
||||
using gloox::Client;
|
||||
using gloox::ConnectionTCPClient;
|
||||
using gloox::JID;
|
||||
using gloox::MessageSession;
|
||||
|
||||
XMPP::XMPP() {
|
||||
|
||||
}
|
||||
|
||||
XMPP::~XMPP() {
|
||||
|
||||
}
|
||||
|
||||
void XMPP::Connect() {
|
||||
QSettings s;
|
||||
s.beginGroup(RemoteConfig::kSettingsGroup);
|
||||
QVariant username = s.value("username");
|
||||
if (username.isValid()) {
|
||||
Keychain* keychain = Keychain::getDefault();
|
||||
QString password = keychain->getPassword(username.toString());
|
||||
Connect(username.toString() + "/clementine", password);
|
||||
} else {
|
||||
qWarning() << "No username or password set.";
|
||||
}
|
||||
}
|
||||
|
||||
void XMPP::Connect(const QString& jid, const QString& password) {
|
||||
// TODO: Generate <256 char resource.
|
||||
JID j(jid.toUtf8().constData());
|
||||
|
||||
client_.reset(new Client(j, password.toUtf8().constData()));
|
||||
ConnectionTCPClient* connection = new ConnectionTCPClient(
|
||||
client_.get(), client_->logInstance(), "talk.google.com");
|
||||
client_->setConnectionImpl(connection);
|
||||
|
||||
client_->registerConnectionListener(this);
|
||||
client_->registerMessageHandler(this);
|
||||
client_->logInstance().registerLogHandler(gloox::LogLevelDebug, gloox::LogAreaAll, this);
|
||||
client_->setPresence(gloox::PresenceAvailable, -128);
|
||||
client_->connect(false);
|
||||
|
||||
notifier_.reset(new QSocketNotifier(connection->socket(), QSocketNotifier::Read));
|
||||
connect(notifier_.get(), SIGNAL(activated(int)), SLOT(Receive()));
|
||||
}
|
||||
|
||||
void XMPP::handleMessage(gloox::Stanza* stanza, MessageSession* session) {
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
qDebug() << stanza->xml().c_str();
|
||||
gloox::Stanza* reply = gloox::Stanza::createMessageStanza(
|
||||
stanza->from(), "Hello world!");
|
||||
client_->send(reply);
|
||||
}
|
||||
|
||||
void XMPP::onConnect() {
|
||||
qDebug() << "Connected with resource:" << client_->resource().c_str()
|
||||
<< client_->jid().full().c_str();
|
||||
client_->login();
|
||||
}
|
||||
|
||||
void XMPP::onDisconnect(gloox::ConnectionError e) {
|
||||
qDebug() << "Disconnected:" << e;
|
||||
notifier_->setEnabled(false);
|
||||
}
|
||||
|
||||
bool XMPP::onTLSConnect(const gloox::CertInfo& info) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void XMPP::Receive() {
|
||||
client_->recv();
|
||||
}
|
||||
|
||||
void XMPP::handleLog(gloox::LogLevel level, gloox::LogArea area,
|
||||
const std::string& message) {
|
||||
QString prefix = "---";
|
||||
if (area == gloox::LogAreaXmlIncoming) {
|
||||
prefix = "<<<";
|
||||
} else if (area == gloox::LogAreaXmlOutgoing) {
|
||||
prefix = ">>>";
|
||||
}
|
||||
|
||||
qDebug() << "XMPP" << prefix.toAscii().constData() << message.c_str();
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
#ifndef XMPP_H
|
||||
#define XMPP_H
|
||||
|
||||
#include <gloox/client.h>
|
||||
#include <gloox/connectionlistener.h>
|
||||
#include <gloox/loghandler.h>
|
||||
#include <gloox/messagehandler.h>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
#include <QSocketNotifier>
|
||||
#include <QString>
|
||||
|
||||
class XMPP : public QObject,
|
||||
public gloox::ConnectionListener,
|
||||
public gloox::MessageHandler,
|
||||
public gloox::LogHandler {
|
||||
Q_OBJECT
|
||||
public:
|
||||
XMPP();
|
||||
virtual ~XMPP();
|
||||
|
||||
void Connect();
|
||||
void Connect(const QString& jid, const QString& password);
|
||||
|
||||
private slots:
|
||||
void Receive();
|
||||
|
||||
private:
|
||||
// gloox::MessageHandler
|
||||
virtual void handleMessage(gloox::Stanza* stanza,
|
||||
gloox::MessageSession* session = 0);
|
||||
|
||||
// gloox::ConnectionListener
|
||||
virtual void onConnect();
|
||||
virtual void onDisconnect(gloox::ConnectionError e);
|
||||
virtual bool onTLSConnect(const gloox::CertInfo& info);
|
||||
|
||||
// gloox::LogHandler
|
||||
virtual void handleLog(gloox::LogLevel level, gloox::LogArea area,
|
||||
const std::string& message);
|
||||
|
||||
boost::scoped_ptr<gloox::Client> client_;
|
||||
boost::scoped_ptr<QSocketNotifier> notifier_;
|
||||
};
|
||||
|
||||
#endif // XMPP_H
|
@ -1,25 +0,0 @@
|
||||
#include "zeroconf.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
#include "bonjour.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
#include "avahi.h"
|
||||
#endif
|
||||
|
||||
Zeroconf* Zeroconf::instance_ = NULL;
|
||||
|
||||
Zeroconf* Zeroconf::GetZeroconf() {
|
||||
if (!instance_) {
|
||||
#if defined(Q_OS_DARWIN)
|
||||
instance_ = new Bonjour();
|
||||
#elif defined(HAVE_DBUS)
|
||||
instance_ = new Avahi();
|
||||
#endif
|
||||
}
|
||||
|
||||
return instance_;
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
#ifndef ZEROCONF_H
|
||||
#define ZEROCONF_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
class Zeroconf {
|
||||
public:
|
||||
virtual ~Zeroconf() {}
|
||||
|
||||
virtual void Publish(const QString& domain,
|
||||
const QString& type,
|
||||
const QString& name,
|
||||
quint16 port) = 0;
|
||||
|
||||
static Zeroconf* GetZeroconf();
|
||||
|
||||
private:
|
||||
static Zeroconf* instance_;
|
||||
};
|
||||
|
||||
#endif
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "core/artloader.h"
|
||||
#include "core/backgroundstreams.h"
|
||||
#include "core/commandlineoptions.h"
|
||||
#include "core/database.h"
|
||||
@ -103,6 +104,10 @@
|
||||
# include "visualisations/visualisationcontainer.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_REMOTE
|
||||
# include "remote/remote.h"
|
||||
#endif
|
||||
|
||||
#include <QCloseEvent>
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
@ -148,6 +153,7 @@ MainWindow::MainWindow(
|
||||
Player* player,
|
||||
SystemTrayIcon* tray_icon,
|
||||
OSD* osd,
|
||||
ArtLoader* art_loader,
|
||||
QWidget* parent)
|
||||
: QMainWindow(parent),
|
||||
ui_(new Ui_MainWindow),
|
||||
@ -162,6 +168,7 @@ MainWindow::MainWindow(
|
||||
player_(player),
|
||||
library_(NULL),
|
||||
global_shortcuts_(new GlobalShortcuts(this)),
|
||||
remote_(NULL),
|
||||
devices_(NULL),
|
||||
library_view_(new LibraryViewContainer(this)),
|
||||
file_view_(new FileView(this)),
|
||||
@ -574,6 +581,14 @@ MainWindow::MainWindow(
|
||||
|
||||
connect(global_shortcuts_, SIGNAL(RateCurrentSong(int)), playlists_, SLOT(RateCurrentSong(int)));
|
||||
|
||||
// XMPP Remote control
|
||||
#ifdef HAVE_REMOTE
|
||||
remote_ = new Remote(player_, this);
|
||||
connect(remote_, SIGNAL(Error(QString)), SLOT(ShowErrorDialog(QString)));
|
||||
connect(art_loader, SIGNAL(ArtLoaded(Song,QString,QImage)),
|
||||
remote_, SLOT(ArtLoaded(Song,QString,QImage)));
|
||||
#endif
|
||||
|
||||
// Fancy tabs
|
||||
connect(ui_->tabs, SIGNAL(ModeChanged(FancyTabWidget::Mode)), SLOT(SaveGeometry()));
|
||||
connect(ui_->tabs, SIGNAL(CurrentChanged(int)), SLOT(SaveGeometry()));
|
||||
@ -733,6 +748,9 @@ void MainWindow::ReloadAllSettings() {
|
||||
#ifdef ENABLE_WIIMOTEDEV
|
||||
wiimotedev_shortcuts_->ReloadSettings();
|
||||
#endif
|
||||
#ifdef HAVE_REMOTE
|
||||
remote_->ReloadSettings();
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::MediaStopped() {
|
||||
|
@ -34,6 +34,7 @@
|
||||
class About;
|
||||
class AddStreamDialog;
|
||||
class ArtistInfoView;
|
||||
class ArtLoader;
|
||||
class BackgroundStreams;
|
||||
class CommandlineOptions;
|
||||
class Database;
|
||||
@ -58,6 +59,7 @@ class QueueManager;
|
||||
class RadioItem;
|
||||
class RadioModel;
|
||||
class RadioViewContainer;
|
||||
class Remote;
|
||||
class ScriptDialog;
|
||||
class ScriptManager;
|
||||
class Song;
|
||||
@ -88,6 +90,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
||||
Player* player,
|
||||
SystemTrayIcon* tray_icon,
|
||||
OSD* osd,
|
||||
ArtLoader* art_loader,
|
||||
QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
@ -255,6 +258,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
||||
Player* player_;
|
||||
Library* library_;
|
||||
GlobalShortcuts* global_shortcuts_;
|
||||
Remote* remote_;
|
||||
|
||||
DeviceManager* devices_;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user