Initial commit
This commit is contained in:
commit
1ac6367f25
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
*.pro.user
|
||||||
|
Makefile
|
||||||
|
moc_*
|
||||||
|
*.o
|
||||||
|
*.so.*
|
||||||
|
ui_*
|
||||||
|
*.so
|
||||||
|
qcp/qcp
|
16
libqcp/libqcp.pro
Normal file
16
libqcp/libqcp.pro
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# -------------------------------------------------
|
||||||
|
# Project created by QtCreator 2010-07-21T19:59:16
|
||||||
|
# -------------------------------------------------
|
||||||
|
QT += network
|
||||||
|
|
||||||
|
# QT -= gui
|
||||||
|
TARGET = qcp
|
||||||
|
TEMPLATE = lib
|
||||||
|
DEFINES += LIBQCP_LIBRARY
|
||||||
|
SOURCES += qcp.cpp \
|
||||||
|
qcpmodel.cpp
|
||||||
|
HEADERS += qcp.h \
|
||||||
|
libqcp_global.h \
|
||||||
|
qcpmodel.h
|
||||||
|
TARGETDEPS += ../libqtavahi/libqtavahi.so
|
||||||
|
LIBS += ../libqtavahi/libqtavahi.so
|
12
libqcp/libqcp_global.h
Normal file
12
libqcp/libqcp_global.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef LIBQCP_GLOBAL_H
|
||||||
|
#define LIBQCP_GLOBAL_H
|
||||||
|
|
||||||
|
#include <QtCore/qglobal.h>
|
||||||
|
|
||||||
|
#if defined(LIBQCP_LIBRARY)
|
||||||
|
# define LIBQCPSHARED_EXPORT Q_DECL_EXPORT
|
||||||
|
#else
|
||||||
|
# define LIBQCPSHARED_EXPORT Q_DECL_IMPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // LIBQCP_GLOBAL_H
|
61
libqcp/qcp.cpp
Normal file
61
libqcp/qcp.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#include "qcp.h"
|
||||||
|
|
||||||
|
#define SERVICETYPE "_qcp._tcp"
|
||||||
|
|
||||||
|
namespace QCP {
|
||||||
|
Browse::Browse(QObject *parent)
|
||||||
|
: QObject(parent), m_av_browse(new Avahi::Browse(SERVICETYPE, this)) {
|
||||||
|
connect(m_av_browse, SIGNAL(found(Avahi::Service)), this, SLOT(foundService(Avahi::Service)));
|
||||||
|
connect(m_av_browse, SIGNAL(lost(Avahi::Service)), this, SLOT(lostService(Avahi::Service)));
|
||||||
|
connect(m_av_browse, SIGNAL(started()), this, SIGNAL(started()));
|
||||||
|
connect(m_av_browse, SIGNAL(stopped()), this, SIGNAL(stopped()));
|
||||||
|
}
|
||||||
|
|
||||||
|
WatchShare *Browse::watch(const Avahi::Service &service, QObject *parent) {
|
||||||
|
ShareData *sd = shareData(service);
|
||||||
|
WatchShare *ws;
|
||||||
|
if (!sd) return 0;
|
||||||
|
|
||||||
|
sd->watcherList.append(ws = new WatchShare(parent, Share(this, service)));
|
||||||
|
ws->m_list_it = sd->watcherList.end()-1;
|
||||||
|
|
||||||
|
return ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Browse::removeWatch(WatcherList::iterator it, const Avahi::Service &service) {
|
||||||
|
ShareData *sd = shareData(service);
|
||||||
|
if (!sd || WatcherList::iterator() == it) return;
|
||||||
|
|
||||||
|
sd->watcherList.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Browse::foundService(Avahi::Service s) {
|
||||||
|
ShareData *sd = new ShareData();
|
||||||
|
s.setData(sd);
|
||||||
|
emit found(Share(this, s));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Browse::lostService(Avahi::Service s) {
|
||||||
|
emit lost(Share(this, s));
|
||||||
|
ShareData *sd = shareData(s);
|
||||||
|
if (0 != sd) {
|
||||||
|
while (!sd->watcherList.empty()) {
|
||||||
|
WatchShare *ws = sd->watcherList.first();
|
||||||
|
emit ws->lost();
|
||||||
|
if (ws == sd->watcherList.first()) {
|
||||||
|
ws->m_list_it = WatcherList::iterator();
|
||||||
|
sd->watcherList.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete sd;
|
||||||
|
}
|
||||||
|
sd->data = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WatchShare *Share::watch(QObject *parent) {
|
||||||
|
return m_browse->watch(m_service, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
107
libqcp/qcp.h
Normal file
107
libqcp/qcp.h
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#ifndef QCP_H
|
||||||
|
#define QCP_H
|
||||||
|
|
||||||
|
#include "libqcp_global.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QLinkedList>
|
||||||
|
|
||||||
|
#include "../libqtavahi/qtavahi.h"
|
||||||
|
|
||||||
|
namespace QCP {
|
||||||
|
class LIBQCPSHARED_EXPORT Share;
|
||||||
|
class LIBQCPSHARED_EXPORT WatchShare;
|
||||||
|
class LIBQCPSHARED_EXPORT Browse;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace QCP {
|
||||||
|
/* The private data of the Avahi::Service instances belongs to QCP ! :) */
|
||||||
|
|
||||||
|
class LIBQCPSHARED_EXPORT Browse : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
friend class QCP::Share;
|
||||||
|
friend class QCP::WatchShare;
|
||||||
|
|
||||||
|
typedef QLinkedList<WatchShare*> WatcherList;
|
||||||
|
struct ShareData {
|
||||||
|
WatcherList watcherList;
|
||||||
|
void *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
Browse(QObject *parent = 0);
|
||||||
|
|
||||||
|
Avahi::Browse* avahiBrowse() { return m_av_browse; }
|
||||||
|
|
||||||
|
inline bool start() { return m_av_browse->start(); }
|
||||||
|
inline void stop() { m_av_browse->stop(); }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void found(QCP::Share s);
|
||||||
|
void lost(QCP::Share s);
|
||||||
|
|
||||||
|
void started();
|
||||||
|
void stopped();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void foundService(Avahi::Service s);
|
||||||
|
void lostService(Avahi::Service s);
|
||||||
|
|
||||||
|
private:
|
||||||
|
WatchShare *watch(const Avahi::Service &service, QObject *parent);
|
||||||
|
void removeWatch(WatcherList::iterator it, const Avahi::Service &service);
|
||||||
|
|
||||||
|
static inline ShareData* shareData(const Avahi::Service &service) {
|
||||||
|
return static_cast<ShareData*>(service.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
Avahi::Browse *m_av_browse;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LIBQCPSHARED_EXPORT Share {
|
||||||
|
private:
|
||||||
|
friend class QCP::Browse;
|
||||||
|
friend class QCP::WatchShare;
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline Share(QCP::Browse *browse, Avahi::Service service) : m_browse(browse), m_service(service) { }
|
||||||
|
|
||||||
|
public:
|
||||||
|
Avahi::Service avahiService() const { return m_service; }
|
||||||
|
|
||||||
|
WatchShare *watch(QObject *parent = 0);
|
||||||
|
|
||||||
|
void* data() const { Browse::ShareData *sd = Browse::shareData(m_service); return sd ? sd->data : 0; }
|
||||||
|
void setData(void *data) const { Browse::ShareData *sd = Browse::shareData(m_service); if (sd) sd->data = data; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QCP::Browse *m_browse;
|
||||||
|
Avahi::Service m_service;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LIBQCPSHARED_EXPORT WatchShare : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class QCP::Share;
|
||||||
|
friend class QCP::Browse;
|
||||||
|
private:
|
||||||
|
inline WatchShare(QObject *parent, Share share) : QObject(parent), m_share(share) { }
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~WatchShare() {
|
||||||
|
m_share.m_browse->removeWatch(m_list_it, m_share.m_service);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Share share() { return m_share; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void lost();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Share m_share;
|
||||||
|
Browse::WatcherList::iterator m_list_it;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // QCP_H
|
91
libqcp/qcpmodel.cpp
Normal file
91
libqcp/qcpmodel.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#include "qcpmodel.h"
|
||||||
|
|
||||||
|
namespace QCP {
|
||||||
|
|
||||||
|
namespace model {
|
||||||
|
Entry::Entry(Row *parent, const Avahi::Service &s, const QCP::Share &share)
|
||||||
|
: Avahi::model::Entry(parent, s), m_share(share) {
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant Entry::data(int col, int role) {
|
||||||
|
switch (role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
switch (col) {
|
||||||
|
case 0: return service.hostname();
|
||||||
|
case 1: return QString("%1:%2").arg(service.address().toString()).arg(service.port());
|
||||||
|
case 2: return "Hello World!";
|
||||||
|
}
|
||||||
|
// case Qt::UserRole
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
EntryList::EntryList(Row *parent, const Avahi::ServiceKey &k)
|
||||||
|
: Avahi::model::EntryList(parent, k) { }
|
||||||
|
|
||||||
|
int EntryList::columnCount() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant EntryList::data(int col, int role) {
|
||||||
|
switch (role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
switch (col) {
|
||||||
|
case 0: return key.name;
|
||||||
|
}
|
||||||
|
// case Qt::UserRole
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
Avahi::model::Entry* EntryList::newEntry(const Avahi::Service &s, void *priv) {
|
||||||
|
QCP::Share *share = static_cast< QCP::Share* >(priv);
|
||||||
|
return new Entry(this, s, *share);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Root::columnCount() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntryList* Root::newList(const Avahi::ServiceKey &k) {
|
||||||
|
return new EntryList(this, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BrowseModel::BrowseModel(QObject *parent)
|
||||||
|
: Avahi::BrowseModel(new QCP::model::Root(), parent), m_browse(new QCP::Browse(this)) {
|
||||||
|
connect(m_browse, SIGNAL(found(QCP::Share)), this, SLOT(foundShare(QCP::Share)));
|
||||||
|
connect(m_browse, SIGNAL(lost(QCP::Share)), this, SLOT(lostShare(QCP::Share)));
|
||||||
|
|
||||||
|
m_browse->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant BrowseModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
||||||
|
if (orientation != Qt::Horizontal) return QVariant();
|
||||||
|
switch (role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
switch (section) {
|
||||||
|
case 0: return "Name";
|
||||||
|
case 1: return "Address";
|
||||||
|
case 2: return "Version";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowseModel::foundShare(QCP::Share s) {
|
||||||
|
insert(s.avahiService(), &s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowseModel::lostShare(QCP::Share s) {
|
||||||
|
remove(s.avahiService());
|
||||||
|
}
|
||||||
|
}
|
59
libqcp/qcpmodel.h
Normal file
59
libqcp/qcpmodel.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef QCPMODEL_H
|
||||||
|
#define QCPMODEL_H
|
||||||
|
|
||||||
|
#include "qcp.h"
|
||||||
|
#include "../libqtavahi/qtavahimodel.h"
|
||||||
|
|
||||||
|
namespace QCP {
|
||||||
|
namespace model {
|
||||||
|
class LIBQCPSHARED_EXPORT Entry : public Avahi::model::Entry {
|
||||||
|
public:
|
||||||
|
Entry(Row *parent, const Avahi::Service &s, const QCP::Share &share);
|
||||||
|
|
||||||
|
virtual QVariant data(int col, int role);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QCP::Share m_share;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LIBQCPSHARED_EXPORT EntryList : public Avahi::model::EntryList {
|
||||||
|
public:
|
||||||
|
EntryList(Row *parent, const Avahi::ServiceKey &k);
|
||||||
|
|
||||||
|
virtual int columnCount();
|
||||||
|
|
||||||
|
virtual QVariant data(int col, int role);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual Avahi::model::Entry* newEntry(const Avahi::Service &s, void *priv);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LIBQCPSHARED_EXPORT Root : public Avahi::model::Root {
|
||||||
|
public:
|
||||||
|
virtual int columnCount();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual EntryList* newList(const Avahi::ServiceKey &k);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class LIBQCPSHARED_EXPORT BrowseModel : public Avahi::BrowseModel {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
BrowseModel(QObject *parent = 0);
|
||||||
|
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||||
|
|
||||||
|
QCP::Browse *browse() { return m_browse; }
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void foundShare(QCP::Share s);
|
||||||
|
void lostShare(QCP::Share s);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QCP::Browse *m_browse;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // QCPMODEL_H
|
17
libqtavahi/libqtavahi.pro
Normal file
17
libqtavahi/libqtavahi.pro
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# -------------------------------------------------
|
||||||
|
# Project created by QtCreator 2010-07-23T21:04:59
|
||||||
|
# -------------------------------------------------
|
||||||
|
QT += network
|
||||||
|
QT -= gui
|
||||||
|
TARGET = qtavahi
|
||||||
|
TEMPLATE = lib
|
||||||
|
DEFINES += LIBQTAVAHI_LIBRARY
|
||||||
|
SOURCES += qtavahi.cpp \
|
||||||
|
qtavahimodel.cpp
|
||||||
|
HEADERS += qtavahi.h \
|
||||||
|
libqtavahi_global.h \
|
||||||
|
qtavahi_p.h \
|
||||||
|
qtavahimodel.h
|
||||||
|
LIBS += -lavahi-common \
|
||||||
|
-lavahi-client \
|
||||||
|
-lavahi-qt4
|
12
libqtavahi/libqtavahi_global.h
Normal file
12
libqtavahi/libqtavahi_global.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef LIBQTAVAHI_GLOBAL_H
|
||||||
|
#define LIBQTAVAHI_GLOBAL_H
|
||||||
|
|
||||||
|
#include <QtCore/qglobal.h>
|
||||||
|
|
||||||
|
#if defined(LIBQTAVAHI_LIBRARY)
|
||||||
|
# define LIBQTAVAHISHARED_EXPORT Q_DECL_EXPORT
|
||||||
|
#else
|
||||||
|
# define LIBQTAVAHISHARED_EXPORT Q_DECL_IMPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // LIBQTAVAHI_GLOBAL_H
|
563
libqtavahi/qtavahi.cpp
Normal file
563
libqtavahi/qtavahi.cpp
Normal file
@ -0,0 +1,563 @@
|
|||||||
|
|
||||||
|
#include "qtavahi.h"
|
||||||
|
|
||||||
|
#include <avahi-client/client.h>
|
||||||
|
#include <avahi-client/lookup.h>
|
||||||
|
#include <avahi-client/publish.h>
|
||||||
|
|
||||||
|
#include <avahi-common/alternative.h>
|
||||||
|
#include <avahi-common/error.h>
|
||||||
|
#include <avahi-common/malloc.h>
|
||||||
|
|
||||||
|
#include <avahi-qt4/qt-watch.h>
|
||||||
|
|
||||||
|
#include <QSize>
|
||||||
|
#include <QtEndian>
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
namespace Avahi {
|
||||||
|
|
||||||
|
static QHostAddress fromAvahiAddress(const AvahiAddress *address, AvahiIfIndex interface) {
|
||||||
|
switch (address->proto) {
|
||||||
|
case AVAHI_PROTO_INET:
|
||||||
|
return QHostAddress(qFromBigEndian((quint32) address->data.ipv4.address));
|
||||||
|
case AVAHI_PROTO_INET6: {
|
||||||
|
struct sockaddr_in6 a;
|
||||||
|
memset(&a, 0, sizeof(a));
|
||||||
|
a.sin6_family = AF_INET6;
|
||||||
|
a.sin6_scope_id = interface;
|
||||||
|
memcpy(a.sin6_addr.s6_addr, address->data.ipv6.address, 16);
|
||||||
|
return QHostAddress((const sockaddr*) &a);
|
||||||
|
}
|
||||||
|
case AVAHI_PROTO_UNSPEC:
|
||||||
|
return QHostAddress();
|
||||||
|
}
|
||||||
|
return QHostAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace priv {
|
||||||
|
|
||||||
|
static void browsep_client_callback(AvahiClient *c, AvahiClientState state, void * userdata);
|
||||||
|
static void browsep_browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event,
|
||||||
|
const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* userdata);
|
||||||
|
static void browsep_resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event,
|
||||||
|
const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address,
|
||||||
|
uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* userdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Avahi {
|
||||||
|
namespace priv {
|
||||||
|
|
||||||
|
class BrowseP {
|
||||||
|
public:
|
||||||
|
static const ::AvahiPoll *m_qtpoll;
|
||||||
|
|
||||||
|
QString m_serviceType;
|
||||||
|
|
||||||
|
bool m_initialized;
|
||||||
|
bool m_running;
|
||||||
|
|
||||||
|
::AvahiClient *m_client;
|
||||||
|
::AvahiServiceBrowser *m_browser;
|
||||||
|
|
||||||
|
QByteArray m_service_type;
|
||||||
|
|
||||||
|
Avahi::Browse *m_parent;
|
||||||
|
|
||||||
|
QHash<ServiceKey, Service> m_list;
|
||||||
|
|
||||||
|
BrowseP(QString servicetype, Browse *parent)
|
||||||
|
: m_serviceType(servicetype),
|
||||||
|
m_initialized(false), m_running(false), m_client(0), m_browser(0),
|
||||||
|
m_service_type(servicetype.toUtf8()), m_parent(parent) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!m_qtpoll) {
|
||||||
|
if (!(m_qtpoll = avahi_qt_poll_get())) {
|
||||||
|
qWarning("avahi_qt_poll_get failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(m_client = avahi_client_new(m_qtpoll, (AvahiClientFlags) 0, browsep_client_callback, this, &err))) {
|
||||||
|
qWarning("avahi_client_new failed: %s", avahi_strerror(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearList() {
|
||||||
|
QList<Service> l = m_list.values();
|
||||||
|
m_list.clear();
|
||||||
|
for (int i = 0; i < l.size(); ++i) {
|
||||||
|
emit m_parent->lost(l.at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void release() {
|
||||||
|
stop();
|
||||||
|
|
||||||
|
if (!m_initialized) return;
|
||||||
|
|
||||||
|
if (m_client) {
|
||||||
|
avahi_client_free(m_client);
|
||||||
|
m_client = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
~BrowseP() {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void client_callback(AvahiClient *c, AvahiClientState state) {
|
||||||
|
switch (state) {
|
||||||
|
case AVAHI_CLIENT_S_RUNNING:
|
||||||
|
break;
|
||||||
|
case AVAHI_CLIENT_FAILURE:
|
||||||
|
qWarning("avahi client failure: %s", avahi_strerror(avahi_client_errno(c)));
|
||||||
|
release();
|
||||||
|
break;
|
||||||
|
case AVAHI_CLIENT_S_COLLISION:
|
||||||
|
case AVAHI_CLIENT_S_REGISTERING:
|
||||||
|
break;
|
||||||
|
case AVAHI_CLIENT_CONNECTING:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event,
|
||||||
|
const char *name, const char *type, const char *domain, AvahiLookupResultFlags /* flags */) {
|
||||||
|
if (!m_browser) m_browser = b;
|
||||||
|
Q_ASSERT(m_browser == b);
|
||||||
|
|
||||||
|
if (protocol != AVAHI_PROTO_INET && protocol != AVAHI_PROTO_INET6) return; /* ignore non IPv[46] for now */
|
||||||
|
|
||||||
|
/* Called whenever a new services becomes available on the LAN or is removed from the LAN */
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case AVAHI_BROWSER_FAILURE:
|
||||||
|
qWarning("(Browser) %s", avahi_strerror(avahi_client_errno(m_client)));
|
||||||
|
release();
|
||||||
|
return;
|
||||||
|
|
||||||
|
case AVAHI_BROWSER_NEW:
|
||||||
|
qDebug("(Browser) NEW: service '%s' of type '%s' in domain '%s'", name, type, domain);
|
||||||
|
|
||||||
|
/* We ignore the returned resolver object. In the callback
|
||||||
|
* function we free it. If the server is terminated before
|
||||||
|
* the callback function is called the server will free
|
||||||
|
* the resolver for us. */
|
||||||
|
|
||||||
|
if (!(avahi_service_resolver_new(m_client, interface, protocol, name, type, domain, protocol, (AvahiLookupFlags) 0, browsep_resolve_callback, this))) {
|
||||||
|
qWarning("Failed to resolve service '%s' on '%s': %s", name, domain, avahi_strerror(avahi_client_errno(m_client)));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AVAHI_BROWSER_REMOVE: {
|
||||||
|
ServiceKey k(interface, protocol, name, domain);
|
||||||
|
Service s = m_list.value(k);
|
||||||
|
if (s.valid()) {
|
||||||
|
m_list.remove(k);
|
||||||
|
emit m_parent->lost(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AVAHI_BROWSER_ALL_FOR_NOW:
|
||||||
|
break;
|
||||||
|
case AVAHI_BROWSER_CACHE_EXHAUSTED:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event,
|
||||||
|
const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address,
|
||||||
|
uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags /* flags */) {
|
||||||
|
Q_ASSERT(r);
|
||||||
|
|
||||||
|
// if (protocol != AVAHI_PROTO_INET) goto done; /* ignore non IPv4 for now */
|
||||||
|
if (protocol != AVAHI_PROTO_INET && protocol != AVAHI_PROTO_INET6) goto done; /* ignore non IPv[46] for now */
|
||||||
|
|
||||||
|
/* Called whenever a service has been resolved successfully or timed out */
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case AVAHI_RESOLVER_FAILURE:
|
||||||
|
qDebug("(Resolver) Failed to resolve service '%s' of type '%s' at '%s' in domain '%s': %s", name, type, host_name, domain, avahi_strerror(avahi_client_errno(m_client)));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AVAHI_RESOLVER_FOUND: {
|
||||||
|
qDebug("(Resolver) found service '%s' of type '%s' at '%s' in domain '%s'", name, type, host_name, domain);
|
||||||
|
QHash<QString, QString> qtxt;
|
||||||
|
AvahiStringList *psl;
|
||||||
|
for (psl = txt ; psl ; psl = psl->next) {
|
||||||
|
QString e = QString::fromUtf8((const char*) psl->text, psl->size), key, value;
|
||||||
|
int eqPos = e.indexOf(QChar('"'));
|
||||||
|
if (eqPos == 0) continue; /* ignore empty keys */
|
||||||
|
if (eqPos < -1) { /* "boolean" */
|
||||||
|
key = e;
|
||||||
|
} else {
|
||||||
|
key = e.left(eqPos - 1);
|
||||||
|
value = e.mid(eqPos + 1);
|
||||||
|
}
|
||||||
|
key = key.toLower();
|
||||||
|
qtxt.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceKey k(interface, protocol, name, domain);
|
||||||
|
Service s = m_list.value(k);
|
||||||
|
if (s.valid()) {
|
||||||
|
m_list.remove(k);
|
||||||
|
emit m_parent->lost(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
s = Service(QString::fromUtf8(name), QString::fromUtf8(host_name), QString::fromUtf8(domain), fromAvahiAddress(address, interface), interface, protocol, port, qtxt);
|
||||||
|
m_list.insert(k, s);
|
||||||
|
emit m_parent->found(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
avahi_service_resolver_free(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool start() {
|
||||||
|
if (m_running) return true;
|
||||||
|
if (!m_initialized) return false;
|
||||||
|
|
||||||
|
clearList();
|
||||||
|
|
||||||
|
if (NULL == (m_browser = avahi_service_browser_new(m_client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, m_service_type.constData(), NULL, (AvahiLookupFlags) 0, browsep_browse_callback, this))) {
|
||||||
|
qWarning("Failed to create service browser: %s", avahi_strerror(avahi_client_errno(m_client)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit m_parent->started();
|
||||||
|
m_running = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
clearList();
|
||||||
|
if (m_running) {
|
||||||
|
emit m_parent->stopped();
|
||||||
|
m_running = false;
|
||||||
|
}
|
||||||
|
if (m_browser) {
|
||||||
|
avahi_service_browser_free(m_browser);
|
||||||
|
m_browser = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const AvahiPoll *BrowseP::m_qtpoll = 0;
|
||||||
|
|
||||||
|
static void browsep_client_callback(AvahiClient *c, AvahiClientState state, void * userdata) {
|
||||||
|
BrowseP *p = static_cast<BrowseP*>(userdata);
|
||||||
|
p->client_callback(c, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void browsep_browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event,
|
||||||
|
const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* userdata) {
|
||||||
|
BrowseP *p = static_cast<BrowseP*>(userdata);
|
||||||
|
p->browse_callback(b, interface, protocol, event, name, type, domain, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void browsep_resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event,
|
||||||
|
const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address,
|
||||||
|
uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* userdata) {
|
||||||
|
BrowseP *p = static_cast<BrowseP*>(userdata);
|
||||||
|
p->resolve_callback(r, interface, protocol, event, name, type, domain, host_name, address, port, txt, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void announcep_client_callback(AvahiClient *c, AvahiClientState state, void * userdata);
|
||||||
|
static void announcep_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata);
|
||||||
|
|
||||||
|
class AnnounceP {
|
||||||
|
public:
|
||||||
|
static const AvahiPoll *m_qtpoll;
|
||||||
|
|
||||||
|
QString m_serviceType, m_name;
|
||||||
|
quint16 m_port;
|
||||||
|
QHash<QString,QString> m_txt;
|
||||||
|
|
||||||
|
bool m_initialized;
|
||||||
|
bool m_running;
|
||||||
|
|
||||||
|
AvahiClient *m_client;
|
||||||
|
AvahiEntryGroup *m_group;
|
||||||
|
|
||||||
|
QByteArray m_service_type;
|
||||||
|
|
||||||
|
Announce *m_parent;
|
||||||
|
|
||||||
|
char *m_raw_name; /* avahi string */
|
||||||
|
QList<QByteArray> m_txt_list;
|
||||||
|
::AvahiStringList *m_raw_txt;
|
||||||
|
|
||||||
|
AnnounceP(QString serviceType, QString name, quint16 port, QHash<QString,QString> txt, Announce *parent)
|
||||||
|
: m_serviceType(serviceType), m_name(name), m_port(port), m_txt(txt),
|
||||||
|
m_initialized(false), m_running(false), m_client(0), m_group(0),
|
||||||
|
m_service_type(serviceType.toUtf8()), m_parent(parent),
|
||||||
|
m_raw_name(avahi_strdup(name.toUtf8().constData())), m_raw_txt(0) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
QHashIterator<QString, QString> i(m_txt);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
QByteArray a;
|
||||||
|
i.next();
|
||||||
|
if (i.value().isNull()) {
|
||||||
|
m_txt_list.append(a = i.key().toUtf8());
|
||||||
|
} else {
|
||||||
|
m_txt_list.append(a = QString("%1=%2").arg(i.key(), i.value()).toUtf8());
|
||||||
|
}
|
||||||
|
AvahiStringList *l = avahi_string_list_new(a.constData(), NULL);
|
||||||
|
l->next = m_raw_txt;
|
||||||
|
m_raw_txt = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_qtpoll) {
|
||||||
|
if (!(m_qtpoll = avahi_qt_poll_get())) {
|
||||||
|
qWarning("avahi_qt_poll_get failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(m_client = avahi_client_new(m_qtpoll, (AvahiClientFlags) 0, announcep_client_callback, this, &err))) {
|
||||||
|
qWarning("avahi_client_new failed: %s", avahi_strerror(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_initialized = true;
|
||||||
|
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void release() {
|
||||||
|
if (m_running) {
|
||||||
|
m_running = false;
|
||||||
|
}
|
||||||
|
if (!m_initialized) return;
|
||||||
|
|
||||||
|
if (m_group) {
|
||||||
|
qDebug("avahi unregister service");
|
||||||
|
avahi_entry_group_reset(m_group);
|
||||||
|
avahi_entry_group_free(m_group);
|
||||||
|
m_group = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_client) {
|
||||||
|
avahi_client_free(m_client);
|
||||||
|
m_client = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_initialized = false;
|
||||||
|
|
||||||
|
emit m_parent->stopped();
|
||||||
|
}
|
||||||
|
|
||||||
|
~AnnounceP() {
|
||||||
|
release();
|
||||||
|
if (m_raw_name) {
|
||||||
|
avahi_free(m_raw_name);
|
||||||
|
m_raw_name = 0;
|
||||||
|
}
|
||||||
|
if (m_raw_txt) {
|
||||||
|
avahi_string_list_free(m_raw_txt);
|
||||||
|
m_raw_txt = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void client_callback(AvahiClient *c, AvahiClientState state) {
|
||||||
|
m_client = c;
|
||||||
|
switch (state) {
|
||||||
|
case AVAHI_CLIENT_S_RUNNING:
|
||||||
|
create_services();
|
||||||
|
break;
|
||||||
|
case AVAHI_CLIENT_FAILURE:
|
||||||
|
qDebug("avahi client failure");
|
||||||
|
release();
|
||||||
|
break;
|
||||||
|
case AVAHI_CLIENT_S_COLLISION:
|
||||||
|
case AVAHI_CLIENT_S_REGISTERING:
|
||||||
|
if (m_group) {
|
||||||
|
avahi_entry_group_reset(m_group);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AVAHI_CLIENT_CONNECTING:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state) {
|
||||||
|
m_group = g;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case AVAHI_ENTRY_GROUP_ESTABLISHED :
|
||||||
|
emit m_parent->established();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AVAHI_ENTRY_GROUP_COLLISION :
|
||||||
|
{
|
||||||
|
char *n = avahi_alternative_service_name(m_raw_name);
|
||||||
|
avahi_free(m_raw_name);
|
||||||
|
m_raw_name = n;
|
||||||
|
m_name = QString::fromUtf8(m_raw_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* And recreate the services */
|
||||||
|
avahi_entry_group_reset(m_group);
|
||||||
|
create_services();
|
||||||
|
/* same name should result in AVAHI_ERR_COLLISION in create_services, emit collision there. */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AVAHI_ENTRY_GROUP_FAILURE :
|
||||||
|
qDebug("Entry group failure: %s", avahi_strerror(avahi_client_errno(m_client)));
|
||||||
|
|
||||||
|
release();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AVAHI_ENTRY_GROUP_UNCOMMITED:
|
||||||
|
case AVAHI_ENTRY_GROUP_REGISTERING:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool create_services() {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!m_group) {
|
||||||
|
if (!(m_group = avahi_entry_group_new(m_client, announcep_entry_group_callback, this))) {
|
||||||
|
qDebug("avahi_entry_group_new failed: %s", avahi_strerror(avahi_client_errno(m_client)));
|
||||||
|
release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
again:
|
||||||
|
if (avahi_entry_group_is_empty(m_group)) {
|
||||||
|
if ((ret = avahi_entry_group_add_service_strlst(m_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, (AvahiPublishFlags) 0, m_raw_name, m_service_type.constData(), NULL, NULL, m_port, m_raw_txt)) < 0) {
|
||||||
|
|
||||||
|
if (ret == AVAHI_ERR_COLLISION)
|
||||||
|
goto collision;
|
||||||
|
|
||||||
|
qDebug("Failed to add '%s': %s", m_service_type.constData(), avahi_strerror(ret));
|
||||||
|
release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((ret = avahi_entry_group_commit(m_group)) < 0) {
|
||||||
|
qDebug("Failed to commit entry group: %s", avahi_strerror(ret));
|
||||||
|
release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
collision:
|
||||||
|
qDebug("avahi name collision: %s, trying a new one", m_raw_name);
|
||||||
|
{
|
||||||
|
char *n = avahi_alternative_service_name(m_raw_name);
|
||||||
|
avahi_free(m_raw_name);
|
||||||
|
m_raw_name = n;
|
||||||
|
m_name = QString::fromUtf8(m_raw_name);
|
||||||
|
}
|
||||||
|
avahi_entry_group_reset(m_group);
|
||||||
|
emit m_parent->collision();
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool start() {
|
||||||
|
if (m_running) return true;
|
||||||
|
if (!m_initialized) return false;
|
||||||
|
|
||||||
|
m_running = true;
|
||||||
|
emit m_parent->started();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const AvahiPoll *AnnounceP::m_qtpoll = 0;
|
||||||
|
|
||||||
|
static void announcep_client_callback(AvahiClient *c, AvahiClientState state, void * userdata) {
|
||||||
|
AnnounceP *p = static_cast<AnnounceP*>(userdata);
|
||||||
|
p->client_callback(c, state);
|
||||||
|
}
|
||||||
|
static void announcep_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
|
||||||
|
AnnounceP *p = static_cast<AnnounceP*>(userdata);
|
||||||
|
p->entry_group_callback(g, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* public interfaces */
|
||||||
|
|
||||||
|
Browse::Browse(QString serviceType, QObject *parent)
|
||||||
|
: QObject(parent), m_priv(new priv::BrowseP(serviceType, this)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Browse::~Browse() {
|
||||||
|
delete m_priv;
|
||||||
|
m_priv = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Browse::start() {
|
||||||
|
return m_priv->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Browse::stop() {
|
||||||
|
m_priv->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Browse::serviceType() const {
|
||||||
|
return m_priv->m_serviceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Service> Browse::currentList() const {
|
||||||
|
return m_priv->m_list.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Announce::Announce(QString serviceType, QString name, quint16 port, QHash<QString,QString> txt, QObject *parent)
|
||||||
|
: QObject(parent), m_priv(new priv::AnnounceP(serviceType, name, port, txt, this)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Announce::~Announce() {
|
||||||
|
delete m_priv;
|
||||||
|
m_priv = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Announce::start() {
|
||||||
|
return m_priv->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Announce::stop() {
|
||||||
|
m_priv->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Announce::serviceType() const {
|
||||||
|
return m_priv->m_serviceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Announce::name() const {
|
||||||
|
return m_priv->m_name;
|
||||||
|
}
|
||||||
|
quint16 Announce::port() const {
|
||||||
|
return m_priv->m_port;
|
||||||
|
}
|
||||||
|
QHash<QString, QString> Announce::txt() const {
|
||||||
|
return m_priv->m_txt;
|
||||||
|
}
|
||||||
|
QString Announce::txt(QString key) const {
|
||||||
|
return m_priv->m_txt.value(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
186
libqtavahi/qtavahi.h
Normal file
186
libqtavahi/qtavahi.h
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#ifndef QTAVAHI_H
|
||||||
|
#define QTAVAHI_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "libqtavahi_global.h"
|
||||||
|
|
||||||
|
namespace Avahi {
|
||||||
|
class LIBQTAVAHISHARED_EXPORT Service;
|
||||||
|
class LIBQTAVAHISHARED_EXPORT ServiceKey;
|
||||||
|
class LIBQTAVAHISHARED_EXPORT Browse;
|
||||||
|
class LIBQTAVAHISHARED_EXPORT Announce;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we need this before #include <QHash> so it finds our qHash function */
|
||||||
|
inline uint qHash(const Avahi::ServiceKey& k);
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
#include <QAtomicInt>
|
||||||
|
#include <QString>
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
#include "qtavahi_p.h"
|
||||||
|
|
||||||
|
namespace Avahi {
|
||||||
|
|
||||||
|
class LIBQTAVAHISHARED_EXPORT Service {
|
||||||
|
public:
|
||||||
|
inline Service() : m_priv(0) { }
|
||||||
|
inline Service(QString name, QString hostname, QString domain, QHostAddress address, int interface, int protocol, quint16 port, QHash<QString, QString> txt)
|
||||||
|
: m_priv(new Avahi::priv::ServiceP(name, hostname, domain, address, interface, protocol, port, txt)) { }
|
||||||
|
|
||||||
|
inline Service(const Service &s) : m_priv(0) { assign(s.m_priv); }
|
||||||
|
inline Service &operator=(const Service &s) { assign(s.m_priv); return *this; }
|
||||||
|
|
||||||
|
~Service() { assign(0); }
|
||||||
|
|
||||||
|
inline QString name() const { return m_priv ? m_priv->m_name : QString(); }
|
||||||
|
inline QString hostname() const { return m_priv ? m_priv->m_hostname : QString(); }
|
||||||
|
inline QString domain() const { return m_priv ? m_priv->m_domain : QString(); }
|
||||||
|
inline QHostAddress address() const { return m_priv ? m_priv->m_address : QHostAddress(); }
|
||||||
|
inline quint16 port() const { return m_priv ? m_priv->m_port : 0; }
|
||||||
|
inline QHash<QString, QString> txt() const { return m_priv ? m_priv->m_txt : QHash<QString, QString>(); }
|
||||||
|
inline QString txt(QString key) const { return m_priv ? m_priv->m_txt.value(key) : QString(); }
|
||||||
|
inline bool valid() const { return 0 != m_priv; }
|
||||||
|
|
||||||
|
/* save custom data for a service (the service is probably associated with a Browse instance, so all
|
||||||
|
code using the same Browse instance uses the same private data here */
|
||||||
|
inline void* data() const { return m_priv ? m_priv->m_data : 0; }
|
||||||
|
inline void setData(void *data) const { if (m_priv) m_priv->m_data = data; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Avahi::ServiceKey;
|
||||||
|
|
||||||
|
inline void assign(Avahi::priv::ServiceP *p) {
|
||||||
|
if (p == m_priv) return;
|
||||||
|
if (p) p->ref();
|
||||||
|
if (m_priv) m_priv->deref();
|
||||||
|
m_priv = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
Avahi::priv::ServiceP *m_priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LIBQTAVAHISHARED_EXPORT ServiceKey {
|
||||||
|
public:
|
||||||
|
inline ServiceKey(int interface, int protocol, const char *name, const char *domain)
|
||||||
|
: interface(interface), protocol(protocol), name(QString::fromUtf8(name)), domain(QString::fromUtf8(domain)) {
|
||||||
|
}
|
||||||
|
inline ServiceKey(int interface, int protocol, QString name, QString domain)
|
||||||
|
: interface(interface), protocol(protocol), name(name), domain(domain) {
|
||||||
|
}
|
||||||
|
inline ServiceKey(const Service &s)
|
||||||
|
: interface(-1), protocol(-1), name(), domain() {
|
||||||
|
if (s.m_priv) {
|
||||||
|
interface = s.m_priv->m_interface;
|
||||||
|
protocol = s.m_priv->m_protocol;
|
||||||
|
name = s.m_priv->m_name;
|
||||||
|
domain = s.m_priv->m_domain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int interface; /* AvahiIfIndex */
|
||||||
|
int protocol; /* AvahiProtocol */
|
||||||
|
QString name;
|
||||||
|
QString domain;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool operator==(const ServiceKey &a, const ServiceKey &b) {
|
||||||
|
return a.interface == b.interface && a.protocol == b.protocol && a.name == b.name && a.domain == b.domain;
|
||||||
|
}
|
||||||
|
inline bool operator<(const ServiceKey &a, const ServiceKey &b) {
|
||||||
|
if (a.interface < b.interface) return true;
|
||||||
|
if (a.interface > b.interface) return false;
|
||||||
|
if (a.protocol < b.protocol) return true;
|
||||||
|
if (a.protocol > b.protocol) return false;
|
||||||
|
if (a.name < b.name) return true;
|
||||||
|
if (a.name > b.name) return false;
|
||||||
|
return a.domain < b.domain;
|
||||||
|
}
|
||||||
|
inline bool operator>(const ServiceKey &a, const ServiceKey &b) { return b < a; }
|
||||||
|
inline bool operator<=(const ServiceKey &a, const ServiceKey &b) { return !(b < a); }
|
||||||
|
inline bool operator>=(const ServiceKey &a, const ServiceKey &b) { return !(a < b); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class LIBQTAVAHISHARED_EXPORT Browse : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(Browse);
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef Avahi::priv::Protocol Protocol;
|
||||||
|
inline Browse(QString servtype, Protocol prot, QObject *parent = 0) {
|
||||||
|
Browse(Avahi::priv::serv_prot2str(servtype, prot), parent);
|
||||||
|
}
|
||||||
|
Browse(QString serviceType, QObject *parent = 0);
|
||||||
|
virtual ~Browse();
|
||||||
|
|
||||||
|
bool start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
QString serviceType() const;
|
||||||
|
|
||||||
|
QList<Service> currentList() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void clearList();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void found(Avahi::Service i);
|
||||||
|
void lost(Avahi::Service i);
|
||||||
|
|
||||||
|
void started();
|
||||||
|
void stopped();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Avahi::priv::BrowseP *m_priv;
|
||||||
|
|
||||||
|
friend class Avahi::priv::BrowseP;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LIBQTAVAHISHARED_EXPORT Announce : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(Announce);
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef Avahi::priv::Protocol Protocol;
|
||||||
|
inline Announce(QString servtype, Protocol prot, QString name, quint16 port, QHash<QString,QString> txt, QObject *parent = 0) {
|
||||||
|
Announce(Avahi::priv::serv_prot2str(servtype, prot), name, port, txt, parent);
|
||||||
|
}
|
||||||
|
Announce(QString serviceType, QString name, quint16 port, QHash<QString,QString> txt, QObject *parent = 0);
|
||||||
|
virtual ~Announce();
|
||||||
|
|
||||||
|
QString serviceType() const;
|
||||||
|
QString name() const;
|
||||||
|
quint16 port() const;
|
||||||
|
QHash<QString, QString> txt() const;
|
||||||
|
QString txt(QString key) const;
|
||||||
|
|
||||||
|
bool start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void established(); /* the name may have been changed after collisions */
|
||||||
|
void collision();
|
||||||
|
void started();
|
||||||
|
void stopped();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Avahi::priv::AnnounceP *m_priv;
|
||||||
|
|
||||||
|
friend class Avahi::priv::AnnounceP;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
inline uint qHash(const Avahi::ServiceKey& k) {
|
||||||
|
return qHash(k.interface ^ k.protocol) ^ qHash(k.name) ^ qHash(k.domain);
|
||||||
|
}
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // QTAVAHI_H
|
50
libqtavahi/qtavahi_p.h
Normal file
50
libqtavahi/qtavahi_p.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#ifndef QTAVAHI_P_H
|
||||||
|
#define QTAVAHI_P_H
|
||||||
|
|
||||||
|
namespace Avahi {
|
||||||
|
|
||||||
|
namespace priv {
|
||||||
|
class LIBQTAVAHISHARED_EXPORT ServiceP;
|
||||||
|
class BrowseP;
|
||||||
|
class AnnounceP;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Avahi {
|
||||||
|
namespace priv {
|
||||||
|
class LIBQTAVAHISHARED_EXPORT ServiceP {
|
||||||
|
private:
|
||||||
|
friend class Avahi::Service;
|
||||||
|
friend class Avahi::ServiceKey;
|
||||||
|
friend class Avahi::Browse;
|
||||||
|
inline ServiceP(QString name, QString hostname, QString domain, QHostAddress address, int interface, int protocol, quint16 port, QHash<QString, QString> txt)
|
||||||
|
: m_ref(1), m_name(name), m_hostname(hostname), m_domain(domain), m_address(address), m_interface(interface), m_protocol(protocol), m_port(port), m_txt(txt), m_data(0) { }
|
||||||
|
|
||||||
|
Q_DISABLE_COPY(ServiceP);
|
||||||
|
|
||||||
|
inline void ref() { m_ref.ref(); }
|
||||||
|
inline void deref() { if (!m_ref.deref()) { delete this; } }
|
||||||
|
|
||||||
|
QAtomicInt m_ref;
|
||||||
|
|
||||||
|
QString m_name, m_hostname, m_domain;
|
||||||
|
QHostAddress m_address;
|
||||||
|
int m_interface, m_protocol;
|
||||||
|
quint16 m_port;
|
||||||
|
QHash<QString, QString> m_txt;
|
||||||
|
|
||||||
|
void* m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum { TCP, UDP } Protocol;
|
||||||
|
inline LIBQTAVAHISHARED_EXPORT QString serv_prot2str(QString service, Protocol prot) {
|
||||||
|
switch (prot) {
|
||||||
|
case TCP: return QString("_%1._tcp").arg(service); break;
|
||||||
|
case UDP: return QString("_%1._udp").arg(service); break;
|
||||||
|
default: return service;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // QTAVAHI_P_H
|
91
libqtavahi/qtavahimodel.cpp
Normal file
91
libqtavahi/qtavahimodel.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#include "qtavahimodel.h"
|
||||||
|
|
||||||
|
namespace Avahi {
|
||||||
|
namespace model {
|
||||||
|
Row::~Row() { }
|
||||||
|
|
||||||
|
int Row::columnCount() { return 0; } /* columns of child rows */
|
||||||
|
Row* Row::index(int /* row */) { return 0; }
|
||||||
|
int Row::rowCount() { return 0; }
|
||||||
|
|
||||||
|
Row* RowList::index(int row) {
|
||||||
|
return list.value(row);
|
||||||
|
}
|
||||||
|
int RowList::rowCount() {
|
||||||
|
return list.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant Root::data(int , int ) { return QVariant(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BrowseModel::BrowseModel(Avahi::model::Root *root, QObject *parent)
|
||||||
|
: QAbstractItemModel(parent), m_root(root) { }
|
||||||
|
|
||||||
|
BrowseModel::Row *BrowseModel::getRow(const QModelIndex &index) const {
|
||||||
|
if (index.isValid()) {
|
||||||
|
return static_cast< Row* >(index.internalPointer());
|
||||||
|
} else {
|
||||||
|
return m_root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int BrowseModel::columnCount(const QModelIndex & parent) const {
|
||||||
|
Row *row = getRow(parent);
|
||||||
|
return row ? row->columnCount() : 0;
|
||||||
|
}
|
||||||
|
QVariant BrowseModel::data(const QModelIndex & index, int role) const {
|
||||||
|
Row *row = getRow(index);
|
||||||
|
return row ? row->data(index.column(), role) : QVariant();
|
||||||
|
}
|
||||||
|
QModelIndex BrowseModel::index(int row, int column, const QModelIndex & parent) const {
|
||||||
|
Row *r = getRow(parent);
|
||||||
|
if (!r) return QModelIndex(); /* invalid */
|
||||||
|
Row *child = r->index(row);
|
||||||
|
if (!child) return QModelIndex(); /* invalid */
|
||||||
|
return createIndex(row, column, (void*) child);
|
||||||
|
}
|
||||||
|
QModelIndex BrowseModel::parent(const QModelIndex & index) const {
|
||||||
|
Row *row = getRow(index);
|
||||||
|
if (!row) return QModelIndex(); /* invalid */
|
||||||
|
Row *p = row->parent();
|
||||||
|
if (!p || p == m_root) return QModelIndex();
|
||||||
|
return createIndex(0, 0, (void*) p);
|
||||||
|
}
|
||||||
|
int BrowseModel::rowCount(const QModelIndex & parent) const {
|
||||||
|
Row *row = getRow(parent);
|
||||||
|
return row ? row->rowCount() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Avahi::ServiceKey BrowseModel::key(const Avahi::Service &s) {
|
||||||
|
Avahi::ServiceKey k(s);
|
||||||
|
k.interface = k.protocol = -1;
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowseModel::insert(const Avahi::Service &s, void *priv) {
|
||||||
|
Avahi::ServiceKey k = key(s);
|
||||||
|
if (m_services.contains(k)) {
|
||||||
|
EntryList *el = m_services.value(k);
|
||||||
|
beginInsertRows(createIndex(0, 0, (void*) el), 0, 0);
|
||||||
|
el->list.push_front(el->newEntry(s, priv));
|
||||||
|
endInsertRows();
|
||||||
|
} else {
|
||||||
|
EntryList *el = m_root->newList(k);
|
||||||
|
QMap<Avahi::ServiceKey, EntryList*> tmp(m_services);
|
||||||
|
tmp.insert(k, el);
|
||||||
|
int row = tmp.keys().indexOf(k);
|
||||||
|
beginInsertRows(QModelIndex(), row, row);
|
||||||
|
m_services = tmp;
|
||||||
|
m_root->list.insert(row, el);
|
||||||
|
endInsertRows();
|
||||||
|
beginInsertRows(createIndex(0, 0, (void*) el), 0, 0);
|
||||||
|
el->list.push_front(el->newEntry(s, priv));
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowseModel::remove(const Avahi::Service &) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
115
libqtavahi/qtavahimodel.h
Normal file
115
libqtavahi/qtavahimodel.h
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
#ifndef QTAVAHIMODEL_H
|
||||||
|
#define QTAVAHIMODEL_H
|
||||||
|
|
||||||
|
#include "qtavahi.h"
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
namespace Avahi {
|
||||||
|
class BrowseModel;
|
||||||
|
|
||||||
|
namespace model {
|
||||||
|
class Row {
|
||||||
|
public:
|
||||||
|
Row(Row *parent) : m_parent(parent) { }
|
||||||
|
Row() : m_parent(0) { }
|
||||||
|
virtual ~Row();
|
||||||
|
|
||||||
|
virtual int columnCount(); /* columns of child rows */
|
||||||
|
virtual QVariant data(int col, int role) = 0;
|
||||||
|
virtual Row* index(int row);
|
||||||
|
virtual int rowCount();
|
||||||
|
|
||||||
|
Row *parent() { return m_parent; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Row *m_parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RowList : public Row {
|
||||||
|
public:
|
||||||
|
typedef QList< Row* > List;
|
||||||
|
|
||||||
|
RowList(Row *parent) : Row(parent) { }
|
||||||
|
RowList() : Row() { }
|
||||||
|
|
||||||
|
virtual int columnCount() = 0; /* columns of child rows */
|
||||||
|
virtual Row* index(int row);
|
||||||
|
virtual int rowCount();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class Avahi::BrowseModel;
|
||||||
|
|
||||||
|
List list;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Entry : public Row {
|
||||||
|
public:
|
||||||
|
Entry(Row *parent, const Avahi::Service &s) : Row(parent), service(s) { }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class Avahi::BrowseModel;
|
||||||
|
|
||||||
|
Avahi::Service service;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EntryList : public RowList {
|
||||||
|
public:
|
||||||
|
EntryList(Row *parent, const Avahi::ServiceKey &k) : RowList(parent), key(k) { }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class Avahi::BrowseModel;
|
||||||
|
|
||||||
|
virtual Entry* newEntry(const Avahi::Service &s, void *priv) = 0;
|
||||||
|
|
||||||
|
Avahi::ServiceKey key;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Root : public RowList {
|
||||||
|
public:
|
||||||
|
virtual QVariant data(int , int );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class Avahi::BrowseModel;
|
||||||
|
|
||||||
|
virtual EntryList* newList(const Avahi::ServiceKey &k) = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BrowseModel : public QAbstractItemModel {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
protected:
|
||||||
|
typedef Avahi::model::Row Row;
|
||||||
|
typedef Avahi::model::RowList RowList;
|
||||||
|
typedef Avahi::model::Entry Entry;
|
||||||
|
typedef Avahi::model::EntryList EntryList;
|
||||||
|
|
||||||
|
Avahi::model::Root *m_root;
|
||||||
|
|
||||||
|
BrowseModel(Avahi::model::Root *root, QObject *parent = 0);
|
||||||
|
|
||||||
|
Row *getRow(const QModelIndex &index) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
int columnCount(const QModelIndex & parent) const;
|
||||||
|
QVariant data(const QModelIndex & index, int role) const;
|
||||||
|
QModelIndex index(int row, int column, const QModelIndex & parent) const;
|
||||||
|
QModelIndex parent(const QModelIndex & index) const;
|
||||||
|
int rowCount(const QModelIndex & parent) const;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual Avahi::ServiceKey key(const Avahi::Service &s);
|
||||||
|
|
||||||
|
void insert(const Avahi::Service &s, void *priv);
|
||||||
|
void remove(const Avahi::Service &s);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Avahi::Browse *m_browse;
|
||||||
|
QMap<Avahi::ServiceKey, EntryList*> m_services;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // QCPMODEL_H
|
1
qcp/libqcp.so.1
Symbolic link
1
qcp/libqcp.so.1
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../libqcp/libqcp.so.1
|
1
qcp/libqtavahi.so.1
Symbolic link
1
qcp/libqtavahi.so.1
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../libqtavahi/libqtavahi.so.1
|
9
qcp/main.cpp
Normal file
9
qcp/main.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include <QtGui/QApplication>
|
||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
return a.exec();
|
||||||
|
}
|
26
qcp/mainwindow.cpp
Normal file
26
qcp/mainwindow.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent) :
|
||||||
|
QMainWindow(parent),
|
||||||
|
ui(new Ui::MainWindow) {
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
m_browsemodel = new QCP::BrowseModel(this);
|
||||||
|
ui->viewShares->setModel(m_browsemodel);
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow() {
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::changeEvent(QEvent *e) {
|
||||||
|
QMainWindow::changeEvent(e);
|
||||||
|
switch (e->type()) {
|
||||||
|
case QEvent::LanguageChange:
|
||||||
|
ui->retranslateUi(this);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
27
qcp/mainwindow.h
Normal file
27
qcp/mainwindow.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
#include "../libqcp/qcp.h"
|
||||||
|
#include "../libqcp/qcpmodel.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
MainWindow(QWidget *parent = 0);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void changeEvent(QEvent *e);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
QCP::BrowseModel *m_browsemodel;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MAINWINDOW_H
|
55
qcp/mainwindow.ui
Normal file
55
qcp/mainwindow.ui
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>600</width>
|
||||||
|
<height>400</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MainWindow</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralWidget">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Shares</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeView" name="viewShares"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menuBar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>600</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QToolBar" name="mainToolBar">
|
||||||
|
<attribute name="toolBarArea">
|
||||||
|
<enum>TopToolBarArea</enum>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="toolBarBreak">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusBar"/>
|
||||||
|
</widget>
|
||||||
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
21
qcp/qcp.pro
Normal file
21
qcp/qcp.pro
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#-------------------------------------------------
|
||||||
|
#
|
||||||
|
# Project created by QtCreator 2010-07-24T12:24:31
|
||||||
|
#
|
||||||
|
#-------------------------------------------------
|
||||||
|
|
||||||
|
QT += network
|
||||||
|
|
||||||
|
TARGET = qcp
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
|
||||||
|
SOURCES += main.cpp\
|
||||||
|
mainwindow.cpp
|
||||||
|
|
||||||
|
HEADERS += mainwindow.h
|
||||||
|
|
||||||
|
FORMS += mainwindow.ui
|
||||||
|
|
||||||
|
TARGETDEPS += ../libqcp/libqcp.so ../libqtavahi/libqtavahi.so
|
||||||
|
LIBS += ../libqcp/libqcp.so ../libqtavahi/libqtavahi.so
|
Loading…
Reference in New Issue
Block a user