Beginnings of remote control support.
This commit is contained in:
parent
45a7780f95
commit
ce65c95580
|
@ -147,6 +147,10 @@ set(SOURCES
|
||||||
radio/savedradio.cpp
|
radio/savedradio.cpp
|
||||||
radio/somafmservice.cpp
|
radio/somafmservice.cpp
|
||||||
|
|
||||||
|
remote/httpserver.cpp
|
||||||
|
remote/httpconnection.cpp
|
||||||
|
remote/zeroconf.cpp
|
||||||
|
|
||||||
smartplaylists/generator.cpp
|
smartplaylists/generator.cpp
|
||||||
smartplaylists/generatorinserter.cpp
|
smartplaylists/generatorinserter.cpp
|
||||||
smartplaylists/querygenerator.cpp
|
smartplaylists/querygenerator.cpp
|
||||||
|
@ -314,6 +318,9 @@ set(HEADERS
|
||||||
radio/savedradio.h
|
radio/savedradio.h
|
||||||
radio/somafmservice.h
|
radio/somafmservice.h
|
||||||
|
|
||||||
|
remote/httpserver.h
|
||||||
|
remote/httpconnection.h
|
||||||
|
|
||||||
smartplaylists/generator.h
|
smartplaylists/generator.h
|
||||||
smartplaylists/generatorinserter.h
|
smartplaylists/generatorinserter.h
|
||||||
smartplaylists/generatormimedata.h
|
smartplaylists/generatormimedata.h
|
||||||
|
@ -513,19 +520,19 @@ if(HAVE_LIBLASTFM)
|
||||||
)
|
)
|
||||||
endif(HAVE_LIBLASTFM)
|
endif(HAVE_LIBLASTFM)
|
||||||
|
|
||||||
# OSDs
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
list(APPEND SOURCES widgets/osd_mac.mm)
|
|
||||||
list(APPEND SOURCES core/macglobalshortcutbackend.mm)
|
|
||||||
list(APPEND SOURCES devices/macdevicelister.mm)
|
|
||||||
list(APPEND HEADERS devices/macdevicelister.h)
|
|
||||||
list(APPEND SOURCES ui/macsystemtrayicon.mm)
|
|
||||||
list(APPEND HEADERS core/macglobalshortcutbackend.h)
|
list(APPEND HEADERS core/macglobalshortcutbackend.h)
|
||||||
|
list(APPEND HEADERS devices/macdevicelister.h)
|
||||||
|
list(APPEND HEADERS ui/macscreensaver.h)
|
||||||
list(APPEND HEADERS ui/macsystemtrayicon.h)
|
list(APPEND HEADERS ui/macsystemtrayicon.h)
|
||||||
list(APPEND HEADERS widgets/maclineedit.h)
|
list(APPEND HEADERS widgets/maclineedit.h)
|
||||||
list(APPEND SOURCES widgets/maclineedit.mm)
|
list(APPEND SOURCES core/macglobalshortcutbackend.mm)
|
||||||
|
list(APPEND SOURCES devices/macdevicelister.mm)
|
||||||
|
list(APPEND SOURCES remote/bonjour.mm)
|
||||||
list(APPEND SOURCES ui/macscreensaver.cpp)
|
list(APPEND SOURCES ui/macscreensaver.cpp)
|
||||||
list(APPEND HEADERS ui/macscreensaver.h)
|
list(APPEND SOURCES ui/macsystemtrayicon.mm)
|
||||||
|
list(APPEND SOURCES widgets/maclineedit.mm)
|
||||||
|
list(APPEND SOURCES widgets/osd_mac.mm)
|
||||||
include_directories(${GROWL}/Headers)
|
include_directories(${GROWL}/Headers)
|
||||||
else(APPLE)
|
else(APPLE)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
|
36
src/main.cpp
36
src/main.cpp
|
@ -38,6 +38,8 @@
|
||||||
#include "engines/enginebase.h"
|
#include "engines/enginebase.h"
|
||||||
#include "library/directory.h"
|
#include "library/directory.h"
|
||||||
#include "playlist/playlist.h"
|
#include "playlist/playlist.h"
|
||||||
|
#include "remote/httpserver.h"
|
||||||
|
#include "remote/zeroconf.h"
|
||||||
#include "smartplaylists/generator.h"
|
#include "smartplaylists/generator.h"
|
||||||
#include "ui/equalizer.h"
|
#include "ui/equalizer.h"
|
||||||
#include "ui/iconloader.h"
|
#include "ui/iconloader.h"
|
||||||
|
@ -111,21 +113,23 @@ int main(int argc, char *argv[]) {
|
||||||
// This must go before QApplication initialisation.
|
// This must go before QApplication initialisation.
|
||||||
mac::MacMain();
|
mac::MacMain();
|
||||||
|
|
||||||
// Bump the soft limit for the number of file descriptors from the default of 256 to
|
{
|
||||||
// the maximum (usually 10240).
|
// Bump the soft limit for the number of file descriptors from the default of 256 to
|
||||||
struct rlimit limit;
|
// the maximum (usually 10240).
|
||||||
getrlimit(RLIMIT_NOFILE, &limit);
|
struct rlimit limit;
|
||||||
|
getrlimit(RLIMIT_NOFILE, &limit);
|
||||||
|
|
||||||
// getrlimit() lies about the hard limit so we have to check sysctl.
|
// getrlimit() lies about the hard limit so we have to check sysctl.
|
||||||
int max_fd = 0;
|
int max_fd = 0;
|
||||||
size_t len = sizeof(max_fd);
|
size_t len = sizeof(max_fd);
|
||||||
sysctlbyname("kern.maxfilesperproc", &max_fd, &len, NULL, 0);
|
sysctlbyname("kern.maxfilesperproc", &max_fd, &len, NULL, 0);
|
||||||
|
|
||||||
limit.rlim_cur = max_fd;
|
limit.rlim_cur = max_fd;
|
||||||
int ret = setrlimit(RLIMIT_NOFILE, &limit);
|
int ret = setrlimit(RLIMIT_NOFILE, &limit);
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
qDebug() << "Max fd:" << max_fd;
|
qDebug() << "Max fd:" << max_fd;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -269,6 +273,14 @@ int main(int argc, char *argv[]) {
|
||||||
// Seed the random number generator
|
// Seed the random number generator
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
|
|
||||||
|
Zeroconf* zeroconf = Zeroconf::GetZeroconf();
|
||||||
|
if (zeroconf) {
|
||||||
|
HttpServer* server = new HttpServer;
|
||||||
|
server->Listen(QHostAddress::Any, 12345);
|
||||||
|
|
||||||
|
zeroconf->Publish("local", "_clementine._tcp", "Clementine", 12345);
|
||||||
|
}
|
||||||
|
|
||||||
// Window
|
// Window
|
||||||
MainWindow w;
|
MainWindow w;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
#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
|
|
@ -0,0 +1,84 @@
|
||||||
|
#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];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include "httpconnection.h"
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QTcpSocket>
|
||||||
|
|
||||||
|
HttpConnection::HttpConnection(QTcpSocket* socket)
|
||||||
|
: QObject(socket),
|
||||||
|
socket_(socket),
|
||||||
|
state_(WaitingForRequest) {
|
||||||
|
connect(socket_, SIGNAL(readyRead()), SLOT(ReadyRead()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpConnection::ReadyRead() {
|
||||||
|
switch (state_) {
|
||||||
|
case WaitingForRequest:
|
||||||
|
if (socket_->canReadLine()) {
|
||||||
|
QString line = socket_->readLine();
|
||||||
|
if (ParseRequest(line)) {
|
||||||
|
state_ = WaitingForHeaders;
|
||||||
|
} else {
|
||||||
|
state_ = Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WaitingForHeaders:
|
||||||
|
while (socket_->canReadLine()) {
|
||||||
|
QByteArray line = socket_->readLine();
|
||||||
|
if (line == "\r\n") {
|
||||||
|
DoRequest();
|
||||||
|
state_ = Finished;
|
||||||
|
} else {
|
||||||
|
// TODO: Parse headers.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state_ == Error) {
|
||||||
|
socket_->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socket_->canReadLine()) {
|
||||||
|
ReadyRead();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpConnection::ParseRequest(const QString& line) {
|
||||||
|
QStringList tokens = line.split(' ', QString::SkipEmptyParts);
|
||||||
|
if (tokens.length() != 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const QString& method = tokens[0];
|
||||||
|
if (method != "GET") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
method_ = QNetworkAccessManager::GetOperation;
|
||||||
|
path_ = tokens[1];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpConnection::DoRequest() {
|
||||||
|
socket_->write("HTTP/1.0 200 OK\r\n");
|
||||||
|
socket_->write("Content-type: application/json\r\n");
|
||||||
|
socket_->write("\r\n");
|
||||||
|
socket_->write("{content:'Hello World!'}");
|
||||||
|
socket_->close();
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef HTTPCONNECTION_H
|
||||||
|
#define HTTPCONNECTION_H
|
||||||
|
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class QTcpSocket;
|
||||||
|
|
||||||
|
class HttpConnection : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
HttpConnection(QTcpSocket* sock);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void ReadyRead();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void DoRequest();
|
||||||
|
bool ParseRequest(const QString& line);
|
||||||
|
|
||||||
|
QTcpSocket* socket_;
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
WaitingForRequest,
|
||||||
|
WaitingForHeaders,
|
||||||
|
WaitingForRequestFinish,
|
||||||
|
Finished,
|
||||||
|
Error,
|
||||||
|
} state_;
|
||||||
|
|
||||||
|
QNetworkAccessManager::Operation method_;
|
||||||
|
QString path_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,28 @@
|
||||||
|
#include "httpserver.h"
|
||||||
|
|
||||||
|
#include <QTcpSocket>
|
||||||
|
|
||||||
|
#include "httpconnection.h"
|
||||||
|
|
||||||
|
HttpServer::HttpServer(QObject* parent)
|
||||||
|
: QObject(parent) {
|
||||||
|
connect(&server_, SIGNAL(newConnection()), SLOT(NewConnection()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpServer::Listen(const QHostAddress& addr, quint16 port) {
|
||||||
|
return server_.listen(addr, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::NewConnection() {
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
QTcpSocket* socket = server_.nextPendingConnection();
|
||||||
|
HttpConnection* conn = new HttpConnection(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::ReadyRead() {
|
||||||
|
QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
|
||||||
|
Q_ASSERT(socket);
|
||||||
|
while (socket->canReadLine()) {
|
||||||
|
QByteArray line = socket->readLine();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef HTTPSERVER_H
|
||||||
|
#define HTTPSERVER_H
|
||||||
|
|
||||||
|
#include <QTcpServer>
|
||||||
|
|
||||||
|
class HttpServer : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
HttpServer(QObject* parent = 0);
|
||||||
|
bool Listen(const QHostAddress& addr, quint16 port);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void NewConnection();
|
||||||
|
void ReadyRead();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTcpServer server_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,17 @@
|
||||||
|
#include "zeroconf.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_DARWIN
|
||||||
|
#include "bonjour.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Zeroconf* Zeroconf::instance_ = NULL;
|
||||||
|
|
||||||
|
Zeroconf* Zeroconf::GetZeroconf() {
|
||||||
|
if (!instance_) {
|
||||||
|
#ifdef Q_OS_DARWIN
|
||||||
|
return new Bonjour();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance_;
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef ZEROCONF_H
|
||||||
|
#define ZEROCONF_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class Zeroconf {
|
||||||
|
public:
|
||||||
|
virtual void Publish(
|
||||||
|
const QString& domain, const QString& type, const QString& name, quint16 port) = 0;
|
||||||
|
|
||||||
|
static Zeroconf* GetZeroconf();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Zeroconf* instance_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue