Beginnings of remote control support.

This commit is contained in:
John Maguire 2010-12-30 13:03:36 +00:00
parent 45a7780f95
commit ce65c95580
10 changed files with 330 additions and 20 deletions

View File

@ -147,6 +147,10 @@ set(SOURCES
radio/savedradio.cpp
radio/somafmservice.cpp
remote/httpserver.cpp
remote/httpconnection.cpp
remote/zeroconf.cpp
smartplaylists/generator.cpp
smartplaylists/generatorinserter.cpp
smartplaylists/querygenerator.cpp
@ -314,6 +318,9 @@ set(HEADERS
radio/savedradio.h
radio/somafmservice.h
remote/httpserver.h
remote/httpconnection.h
smartplaylists/generator.h
smartplaylists/generatorinserter.h
smartplaylists/generatormimedata.h
@ -513,19 +520,19 @@ if(HAVE_LIBLASTFM)
)
endif(HAVE_LIBLASTFM)
# OSDs
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 devices/macdevicelister.h)
list(APPEND HEADERS ui/macscreensaver.h)
list(APPEND HEADERS ui/macsystemtrayicon.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 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)
else(APPLE)
if(WIN32)

View File

@ -38,6 +38,8 @@
#include "engines/enginebase.h"
#include "library/directory.h"
#include "playlist/playlist.h"
#include "remote/httpserver.h"
#include "remote/zeroconf.h"
#include "smartplaylists/generator.h"
#include "ui/equalizer.h"
#include "ui/iconloader.h"
@ -111,21 +113,23 @@ int main(int argc, char *argv[]) {
// This must go before QApplication initialisation.
mac::MacMain();
// Bump the soft limit for the number of file descriptors from the default of 256 to
// the maximum (usually 10240).
struct rlimit limit;
getrlimit(RLIMIT_NOFILE, &limit);
{
// Bump the soft limit for the number of file descriptors from the default of 256 to
// the maximum (usually 10240).
struct rlimit limit;
getrlimit(RLIMIT_NOFILE, &limit);
// getrlimit() lies about the hard limit so we have to check sysctl.
int max_fd = 0;
size_t len = sizeof(max_fd);
sysctlbyname("kern.maxfilesperproc", &max_fd, &len, NULL, 0);
// getrlimit() lies about the hard limit so we have to check sysctl.
int max_fd = 0;
size_t len = sizeof(max_fd);
sysctlbyname("kern.maxfilesperproc", &max_fd, &len, NULL, 0);
limit.rlim_cur = max_fd;
int ret = setrlimit(RLIMIT_NOFILE, &limit);
limit.rlim_cur = max_fd;
int ret = setrlimit(RLIMIT_NOFILE, &limit);
if (ret == 0) {
qDebug() << "Max fd:" << max_fd;
if (ret == 0) {
qDebug() << "Max fd:" << max_fd;
}
}
#endif
@ -269,6 +273,14 @@ int main(int argc, char *argv[]) {
// Seed the random number generator
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
MainWindow w;

18
src/remote/bonjour.h Normal file
View File

@ -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

84
src/remote/bonjour.mm Normal file
View File

@ -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];
}
}

View File

@ -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();
}

View File

@ -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

28
src/remote/httpserver.cpp Normal file
View File

@ -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();
}
}

20
src/remote/httpserver.h Normal file
View File

@ -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

17
src/remote/zeroconf.cpp Normal file
View File

@ -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_;
}

17
src/remote/zeroconf.h Normal file
View File

@ -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