mirror of
https://github.com/vanilla-wiiu/vanilla.git
synced 2025-01-22 08:11:47 -05:00
implemented UDP mode
This commit is contained in:
parent
f26d42ce14
commit
6290f18769
13 changed files with 673 additions and 336 deletions
|
@ -9,6 +9,7 @@ set(CMAKE_AUTORCC ON)
|
|||
add_executable(vanilla-gui
|
||||
audiohandler.cpp
|
||||
backend.cpp
|
||||
backendinitdialog.cpp
|
||||
gamepadhandler.cpp
|
||||
keymap.cpp
|
||||
inputconfigdialog.cpp
|
||||
|
|
443
app/backend.cpp
443
app/backend.cpp
|
@ -3,7 +3,9 @@
|
|||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkDatagram>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
@ -30,216 +32,226 @@ void vanillaEventHandler(void *context, int type, const char *data, size_t dataL
|
|||
|
||||
Backend::Backend(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_pipe = nullptr;
|
||||
}
|
||||
|
||||
void Backend::init()
|
||||
{
|
||||
emit ready();
|
||||
}
|
||||
|
||||
BackendViaLocalRoot::BackendViaLocalRoot(const QString &wirelessInterface, QObject *parent) : Backend(parent)
|
||||
{
|
||||
m_wirelessInterface = wirelessInterface;
|
||||
}
|
||||
|
||||
BackendViaPipe::BackendViaPipe(QObject *parent) : Backend(parent)
|
||||
{
|
||||
}
|
||||
|
||||
BackendViaNamedPipe::BackendViaNamedPipe(const QString &wirelessInterface, QObject *parent) : BackendViaPipe(parent)
|
||||
{
|
||||
m_pipeIn = -1;
|
||||
m_pipeOut = -1;
|
||||
m_interrupt = 0;
|
||||
|
||||
// If not running as root, use pipe
|
||||
if ((geteuid() != 0)) {
|
||||
m_pipeThread = new QThread(this);
|
||||
m_pipeThread->start();
|
||||
|
||||
m_pipe = new BackendPipe();
|
||||
m_pipe->moveToThread(m_pipeThread);
|
||||
|
||||
connect(m_pipe, &BackendPipe::pipesAvailable, this, &Backend::setUpPipes);
|
||||
|
||||
QMetaObject::invokeMethod(m_pipe, &BackendPipe::start, Qt::QueuedConnection);
|
||||
}
|
||||
m_pipeThread = new QThread(this);
|
||||
m_pipe = new BackendPipe(wirelessInterface, this);
|
||||
connect(m_pipe, &BackendPipe::pipesAvailable, this, &BackendViaNamedPipe::setUpPipes);
|
||||
connect(m_pipe, &BackendPipe::closed, this, &BackendViaNamedPipe::closed);
|
||||
}
|
||||
|
||||
Backend::~Backend()
|
||||
void BackendViaNamedPipe::init()
|
||||
{
|
||||
if (m_pipe) {
|
||||
m_pipeMutex.lock();
|
||||
uint8_t cc = VANILLA_PIPE_IN_QUIT;
|
||||
write(m_pipeOut, &cc, sizeof(cc));
|
||||
m_pipeMutex.unlock();
|
||||
|
||||
m_pipe->deleteLater();
|
||||
m_pipeThread->quit();
|
||||
m_pipeThread->wait();
|
||||
}
|
||||
m_pipe->setParent(nullptr);
|
||||
m_pipeThread->start();
|
||||
m_pipe->moveToThread(m_pipeThread);
|
||||
QMetaObject::invokeMethod(m_pipe, &BackendPipe::start, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void ignoreByte(int pipe)
|
||||
BackendViaUdp::BackendViaUdp(const QHostAddress &backendAddr, quint16 backendPort, QObject *parent) : BackendViaPipe(parent)
|
||||
{
|
||||
uint8_t ignore;
|
||||
read(pipe, &ignore, sizeof(ignore));
|
||||
m_socket = new BackendUdpWrapper(backendAddr, backendPort, this);
|
||||
connect(m_socket, &BackendUdpWrapper::socketReady, this, &BackendViaUdp::socketReady);
|
||||
connect(m_socket, &BackendUdpWrapper::receivedData, this, &BackendViaUdp::receivedData, Qt::DirectConnection);
|
||||
connect(m_socket, &BackendUdpWrapper::closed, this, &BackendViaUdp::closed);
|
||||
connect(m_socket, &BackendUdpWrapper::error, this, &BackendViaUdp::error);
|
||||
|
||||
m_socketThread = new QThread(this);
|
||||
}
|
||||
|
||||
void writeByte(int pipe, uint8_t byte)
|
||||
BackendViaUdp::~BackendViaUdp()
|
||||
{
|
||||
write(pipe, &byte, sizeof(byte));
|
||||
m_socket->deleteLater();
|
||||
m_socketThread->quit();
|
||||
m_socketThread->wait();
|
||||
}
|
||||
|
||||
void Backend::interrupt()
|
||||
void BackendViaUdp::init()
|
||||
{
|
||||
if (m_pipe) {
|
||||
m_pipeMutex.lock();
|
||||
uint8_t cc = VANILLA_PIPE_IN_INTERRUPT;
|
||||
write(m_pipeOut, &cc, sizeof(cc));
|
||||
m_pipeMutex.unlock();
|
||||
} else {
|
||||
vanilla_stop();
|
||||
}
|
||||
m_socketThread->start();
|
||||
m_socket->setParent(nullptr);
|
||||
m_socket->moveToThread(m_socketThread);
|
||||
QMetaObject::invokeMethod(m_socket, &BackendUdpWrapper::start, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void writeNullTermString(int pipe, const QString &s)
|
||||
void BackendViaPipe::quitPipe()
|
||||
{
|
||||
QByteArray sc = s.toUtf8();
|
||||
write(pipe, sc.constData(), sc.size());
|
||||
writeByte(pipe, 0);
|
||||
pipe_control_code cmd;
|
||||
cmd.code = VANILLA_PIPE_IN_QUIT;
|
||||
writeToPipe(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void Backend::requestIDR()
|
||||
BackendViaNamedPipe::~BackendViaNamedPipe()
|
||||
{
|
||||
if (m_pipe) {
|
||||
m_pipeMutex.lock();
|
||||
writeByte(m_pipeOut, VANILLA_PIPE_IN_REQ_IDR);
|
||||
m_pipeMutex.unlock();
|
||||
} else {
|
||||
vanilla_request_idr();
|
||||
}
|
||||
quitPipe();
|
||||
|
||||
m_pipe->deleteLater();
|
||||
m_pipeThread->quit();
|
||||
m_pipeThread->wait();
|
||||
}
|
||||
|
||||
void Backend::connectToConsole(const QString &wirelessInterface)
|
||||
void BackendViaLocalRoot::interrupt()
|
||||
{
|
||||
if (m_pipe) {
|
||||
// Request pipe to connect
|
||||
m_pipeMutex.lock();
|
||||
writeByte(m_pipeOut, VANILLA_PIPE_IN_CONNECT);
|
||||
writeNullTermString(m_pipeOut, wirelessInterface);
|
||||
m_pipeMutex.unlock();
|
||||
|
||||
uint8_t cc;
|
||||
while (true) {
|
||||
ssize_t read_size = read(m_pipeIn, &cc, sizeof(cc));
|
||||
if (read_size == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cc == VANILLA_PIPE_OUT_EOF) {
|
||||
break;
|
||||
} else if (cc == VANILLA_PIPE_OUT_DATA) {
|
||||
// Read event
|
||||
read(m_pipeIn, &cc, sizeof(cc));
|
||||
|
||||
// Read data size
|
||||
uint64_t data_size;
|
||||
read(m_pipeIn, &data_size, sizeof(data_size));
|
||||
|
||||
void *buf = malloc(data_size);
|
||||
read(m_pipeIn, buf, data_size);
|
||||
|
||||
vanillaEventHandler(this, cc, (const char *) buf, data_size);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QByteArray wirelessInterfaceC = wirelessInterface.toUtf8();
|
||||
vanilla_connect_to_console(wirelessInterfaceC.constData(), vanillaEventHandler, this);
|
||||
}
|
||||
vanilla_stop();
|
||||
}
|
||||
|
||||
void Backend::updateTouch(int x, int y)
|
||||
void BackendViaPipe::interrupt()
|
||||
{
|
||||
if (m_pipe) {
|
||||
m_pipeMutex.lock();
|
||||
writeByte(m_pipeOut, VANILLA_PIPE_IN_TOUCH);
|
||||
int32_t touchX = x;
|
||||
int32_t touchY = y;
|
||||
write(m_pipeOut, &x, sizeof(x));
|
||||
write(m_pipeOut, &y, sizeof(y));
|
||||
m_pipeMutex.unlock();
|
||||
} else {
|
||||
vanilla_set_touch(x, y);
|
||||
}
|
||||
pipe_control_code cmd;
|
||||
cmd.code = VANILLA_PIPE_IN_INTERRUPT;
|
||||
writeToPipe(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void Backend::setButton(int button, int32_t value)
|
||||
void BackendViaLocalRoot::requestIDR()
|
||||
{
|
||||
if (m_pipe) {
|
||||
m_pipeMutex.lock();
|
||||
writeByte(m_pipeOut, VANILLA_PIPE_IN_BUTTON);
|
||||
int32_t buttonSized = button;
|
||||
write(m_pipeOut, &buttonSized, sizeof(buttonSized));
|
||||
write(m_pipeOut, &value, sizeof(value));
|
||||
m_pipeMutex.unlock();
|
||||
} else {
|
||||
vanilla_set_button(button, value);
|
||||
}
|
||||
vanilla_request_idr();
|
||||
}
|
||||
|
||||
void Backend::setRegion(int region)
|
||||
void BackendViaPipe::requestIDR()
|
||||
{
|
||||
if (m_pipe) {
|
||||
m_pipeMutex.lock();
|
||||
writeByte(m_pipeOut, VANILLA_PIPE_IN_REGION);
|
||||
int8_t regionSized = region;
|
||||
write(m_pipeOut, ®ionSized, sizeof(regionSized));
|
||||
m_pipeMutex.unlock();
|
||||
} else {
|
||||
vanilla_set_region(region);
|
||||
}
|
||||
pipe_control_code cmd;
|
||||
cmd.code = VANILLA_PIPE_IN_REQ_IDR;
|
||||
writeToPipe(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void Backend::setBatteryStatus(int status)
|
||||
void BackendViaLocalRoot::connectToConsole()
|
||||
{
|
||||
if (m_pipe) {
|
||||
m_pipeMutex.lock();
|
||||
writeByte(m_pipeOut, VANILLA_PIPE_IN_BATTERY);
|
||||
int8_t batterySized = status;
|
||||
write(m_pipeOut, &batterySized, sizeof(batterySized));
|
||||
m_pipeMutex.unlock();
|
||||
} else {
|
||||
vanilla_set_battery_status(status);
|
||||
}
|
||||
QByteArray wirelessInterfaceC = m_wirelessInterface.toUtf8();
|
||||
vanilla_connect_to_console(wirelessInterfaceC.constData(), vanillaEventHandler, this);
|
||||
}
|
||||
|
||||
void Backend::sync(const QString &wirelessInterface, uint16_t code)
|
||||
void BackendViaPipe::connectToConsole()
|
||||
{
|
||||
if (m_pipe) {
|
||||
// Request pipe to sync
|
||||
m_pipeMutex.lock();
|
||||
|
||||
// Write control code
|
||||
writeByte(m_pipeOut, VANILLA_PIPE_IN_SYNC);
|
||||
// Request pipe to connect
|
||||
pipe_control_code conn_cmd;
|
||||
conn_cmd.code = VANILLA_PIPE_IN_CONNECT;
|
||||
writeToPipe(&conn_cmd, sizeof(conn_cmd));
|
||||
|
||||
// Write WPS code
|
||||
write(m_pipeOut, &code, sizeof(code));
|
||||
|
||||
// Write wireless interface
|
||||
writeNullTermString(m_pipeOut, wirelessInterface);
|
||||
|
||||
m_pipeMutex.unlock();
|
||||
|
||||
// See if pipe accepted our request to sync
|
||||
uint8_t cc;
|
||||
read(m_pipeIn, &cc, sizeof(cc));
|
||||
if (cc != VANILLA_PIPE_ERR_SUCCESS) {
|
||||
emit syncCompleted(false);
|
||||
return;
|
||||
uint8_t cmd[UINT16_MAX];
|
||||
while (true) {
|
||||
ssize_t read_size = readFromPipe(cmd, sizeof(cmd));
|
||||
if (read_size == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Wait for sync status
|
||||
read(m_pipeIn, &cc, sizeof(cc));
|
||||
if (cc == VANILLA_PIPE_OUT_SYNC_STATE) {
|
||||
read(m_pipeIn, &cc, sizeof(cc));
|
||||
emit syncCompleted(cc == VANILLA_SUCCESS);
|
||||
} else {
|
||||
emit syncCompleted(false);
|
||||
pipe_control_code *cc = (pipe_control_code *) cmd;
|
||||
|
||||
if (cc->code == VANILLA_PIPE_OUT_EOF) {
|
||||
break;
|
||||
} else if (cc->code == VANILLA_PIPE_OUT_DATA) {
|
||||
pipe_data_command *event = (pipe_data_command *) cmd;
|
||||
event->data_size = ntohs(event->data_size);
|
||||
vanillaEventHandler(this, event->event_type, (const char *) event->data, event->data_size);
|
||||
}
|
||||
} else {
|
||||
QByteArray wirelessInterfaceC = wirelessInterface.toUtf8();
|
||||
int r = vanilla_sync_with_console(wirelessInterfaceC.constData(), code);
|
||||
emit syncCompleted(r == VANILLA_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
void Backend::setUpPipes(const QString &in, const QString &out)
|
||||
void BackendViaLocalRoot::updateTouch(int x, int y)
|
||||
{
|
||||
vanilla_set_touch(x, y);
|
||||
}
|
||||
|
||||
void BackendViaPipe::updateTouch(int x, int y)
|
||||
{
|
||||
pipe_touch_command cmd;
|
||||
cmd.base.code = VANILLA_PIPE_IN_TOUCH;
|
||||
cmd.x = htonl(x);
|
||||
cmd.y = htonl(y);
|
||||
writeToPipe(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void BackendViaLocalRoot::setButton(int button, int32_t value)
|
||||
{
|
||||
vanilla_set_button(button, value);
|
||||
}
|
||||
|
||||
void BackendViaPipe::setButton(int button, int32_t value)
|
||||
{
|
||||
pipe_button_command cmd;
|
||||
cmd.base.code = VANILLA_PIPE_IN_BUTTON;
|
||||
cmd.id = htonl(button);
|
||||
cmd.value = htonl(value);
|
||||
writeToPipe(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void BackendViaLocalRoot::setRegion(int region)
|
||||
{
|
||||
vanilla_set_region(region);
|
||||
}
|
||||
|
||||
void BackendViaPipe::setRegion(int region)
|
||||
{
|
||||
pipe_region_command cmd;
|
||||
cmd.base.code = VANILLA_PIPE_IN_REGION;
|
||||
cmd.region = region;
|
||||
writeToPipe(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void BackendViaLocalRoot::setBatteryStatus(int status)
|
||||
{
|
||||
vanilla_set_battery_status(status);
|
||||
}
|
||||
|
||||
void BackendViaPipe::setBatteryStatus(int status)
|
||||
{
|
||||
pipe_battery_command cmd;
|
||||
cmd.base.code = VANILLA_PIPE_IN_BATTERY;
|
||||
cmd.battery = status;
|
||||
writeToPipe(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void BackendViaLocalRoot::sync(uint16_t code)
|
||||
{
|
||||
QByteArray wirelessInterfaceC = m_wirelessInterface.toUtf8();
|
||||
int r = vanilla_sync_with_console(wirelessInterfaceC.constData(), code);
|
||||
emit syncCompleted(r == VANILLA_SUCCESS);
|
||||
}
|
||||
|
||||
void BackendViaPipe::sync(uint16_t code)
|
||||
{
|
||||
// Request pipe to sync
|
||||
pipe_sync_command cmd;
|
||||
cmd.base.code = VANILLA_PIPE_IN_SYNC;
|
||||
cmd.code = code;
|
||||
writeToPipe(&cmd, sizeof(cmd));
|
||||
|
||||
// See if pipe accepted our request to sync
|
||||
uint8_t cc;
|
||||
readFromPipe(&cc, sizeof(cc));
|
||||
if (cc != VANILLA_PIPE_ERR_SUCCESS) {
|
||||
emit syncCompleted(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for sync status
|
||||
readFromPipe(&cc, sizeof(cc));
|
||||
if (cc == VANILLA_PIPE_OUT_SYNC_STATE) {
|
||||
readFromPipe(&cc, sizeof(cc));
|
||||
emit syncCompleted(cc == VANILLA_SUCCESS);
|
||||
} else {
|
||||
emit syncCompleted(false);
|
||||
}
|
||||
}
|
||||
|
||||
void BackendViaNamedPipe::setUpPipes(const QString &in, const QString &out)
|
||||
{
|
||||
QByteArray inUtf8 = in.toUtf8();
|
||||
QByteArray outUtf8 = out.toUtf8();
|
||||
|
@ -253,11 +265,13 @@ void Backend::setUpPipes(const QString &in, const QString &out)
|
|||
}
|
||||
|
||||
printf("Established connection with backend\n");
|
||||
emit ready();
|
||||
}
|
||||
|
||||
BackendPipe::BackendPipe(QObject *parent) : QObject(parent)
|
||||
BackendPipe::BackendPipe(const QString &wirelessInterface, QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_process = nullptr;
|
||||
m_wirelessInterface = wirelessInterface;
|
||||
}
|
||||
|
||||
BackendPipe::~BackendPipe()
|
||||
|
@ -281,12 +295,12 @@ void BackendPipe::start()
|
|||
m_process = new QProcess(this);
|
||||
m_process->setReadChannel(QProcess::StandardError);
|
||||
connect(m_process, &QProcess::readyReadStandardError, this, &BackendPipe::receivedData);
|
||||
//connect(m_pipe, &QProcess::finished, this, [this](int code){printf("closed??? %i\n", code);});
|
||||
connect(m_process, &QProcess::finished, this, &BackendPipe::closed);
|
||||
|
||||
m_pipeOutFilename = QStringLiteral("/tmp/vanilla-fifo-in-%0").arg(QString::number(QDateTime::currentMSecsSinceEpoch()));
|
||||
m_pipeInFilename = QStringLiteral("/tmp/vanilla-fifo-out-%0").arg(QString::number(QDateTime::currentMSecsSinceEpoch()));
|
||||
|
||||
m_process->start(QStringLiteral("pkexec"), {pipe_bin, QStringLiteral("-pipe"), m_pipeOutFilename, m_pipeInFilename});
|
||||
m_process->start(QStringLiteral("pkexec"), {pipe_bin, m_wirelessInterface, QStringLiteral("-pipe"), m_pipeOutFilename, m_pipeInFilename});
|
||||
}
|
||||
|
||||
void BackendPipe::receivedData()
|
||||
|
@ -299,4 +313,97 @@ void BackendPipe::receivedData()
|
|||
printf("%s\n", a.constData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t BackendViaNamedPipe::readFromPipe(void *data, size_t length)
|
||||
{
|
||||
return read(m_pipeIn, data, length);
|
||||
}
|
||||
|
||||
ssize_t BackendViaNamedPipe::writeToPipe(const void *data, size_t length)
|
||||
{
|
||||
return write(m_pipeOut, data, length);
|
||||
}
|
||||
|
||||
BackendUdpWrapper::BackendUdpWrapper(const QHostAddress &backendAddr, quint16 backendPort, QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_backendAddress = backendAddr;
|
||||
m_backendPort = backendPort;
|
||||
|
||||
m_socket = new QUdpSocket(this);
|
||||
connect(m_socket, &QUdpSocket::readyRead, this, &BackendUdpWrapper::readPendingDatagrams);
|
||||
}
|
||||
|
||||
void BackendUdpWrapper::start()
|
||||
{
|
||||
bool connected = false;
|
||||
|
||||
for (m_frontendPort = 10100; m_frontendPort < 10200; m_frontendPort++) {
|
||||
printf("Trying to bind to port %u\n", m_frontendPort);
|
||||
if (m_socket->bind(QHostAddress::Any, m_frontendPort)) {
|
||||
printf("Bound to port %u\n", m_frontendPort);
|
||||
connected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (connected) {
|
||||
emit socketReady(m_frontendPort);
|
||||
} else {
|
||||
printf("Failed to bind to port\n");
|
||||
emit error(tr("Failed to bind to UDP port"));
|
||||
emit closed();
|
||||
}
|
||||
}
|
||||
|
||||
void BackendUdpWrapper::readPendingDatagrams()
|
||||
{
|
||||
while (m_socket->hasPendingDatagrams()) {
|
||||
QNetworkDatagram datagram = m_socket->receiveDatagram();
|
||||
emit receivedData(datagram.data());
|
||||
}
|
||||
}
|
||||
|
||||
void BackendUdpWrapper::write(const QByteArray &data)
|
||||
{
|
||||
m_socket->writeDatagram(data, m_backendAddress, m_backendPort);
|
||||
}
|
||||
|
||||
ssize_t BackendViaUdp::readFromPipe(void *data, size_t length)
|
||||
{
|
||||
m_readMutex.lock();
|
||||
if (m_buffer.size() < length) {
|
||||
m_readWaitCond.wait(&m_readMutex);
|
||||
}
|
||||
memcpy(data, m_buffer.constData(), length);
|
||||
m_buffer.remove(0, length);
|
||||
m_readMutex.unlock();
|
||||
return length;
|
||||
}
|
||||
|
||||
ssize_t BackendViaUdp::writeToPipe(const void *data, size_t length)
|
||||
{
|
||||
QMetaObject::invokeMethod(m_socket, "write", Q_ARG(QByteArray, QByteArray((const char *) data, length)));
|
||||
return length;
|
||||
}
|
||||
|
||||
void BackendViaUdp::receivedData(const QByteArray &data)
|
||||
{
|
||||
m_readMutex.lock();
|
||||
m_buffer.append(data);
|
||||
m_readWaitCond.wakeAll();
|
||||
m_readMutex.unlock();
|
||||
}
|
||||
|
||||
void BackendViaUdp::socketReady(quint16 port)
|
||||
{
|
||||
pipe_control_code cmd;
|
||||
cmd.code = VANILLA_PIPE_IN_BIND;
|
||||
writeToPipe(&cmd, sizeof(cmd));
|
||||
|
||||
uint8_t cc;
|
||||
readFromPipe(&cc, sizeof(cc));
|
||||
if (cc == VANILLA_PIPE_OUT_BOUND_SUCCESSFUL) {
|
||||
emit ready();
|
||||
}
|
||||
}
|
152
app/backend.h
152
app/backend.h
|
@ -1,16 +1,19 @@
|
|||
#ifndef BACKEND_H
|
||||
#define BACKEND_H
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QThread>
|
||||
#include <QUdpSocket>
|
||||
#include <QWaitCondition>
|
||||
|
||||
class BackendPipe : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BackendPipe(QObject *parent = nullptr);
|
||||
BackendPipe(const QString &wirelessInterface, QObject *parent = nullptr);
|
||||
|
||||
virtual ~BackendPipe() override;
|
||||
|
||||
|
@ -22,6 +25,7 @@ public slots:
|
|||
signals:
|
||||
void pipesAvailable(const QString &in, const QString &out);
|
||||
void portAvailable(uint16_t port);
|
||||
void closed();
|
||||
|
||||
private slots:
|
||||
void receivedData();
|
||||
|
@ -32,6 +36,7 @@ private:
|
|||
QString m_pipeInFilename;
|
||||
uint16_t m_serverPort;
|
||||
uint16_t m_clientPort;
|
||||
QString m_wirelessInterface;
|
||||
|
||||
};
|
||||
|
||||
|
@ -41,9 +46,13 @@ class Backend : public QObject
|
|||
public:
|
||||
Backend(QObject *parent = nullptr);
|
||||
|
||||
virtual ~Backend() override;
|
||||
|
||||
void interrupt();
|
||||
// These are all commands that can be issued to the backend. They are re-entrant and can be called at any time.
|
||||
virtual void interrupt() = 0;
|
||||
virtual void updateTouch(int x, int y) = 0;
|
||||
virtual void setButton(int button, int32_t value) = 0;
|
||||
virtual void requestIDR() = 0;
|
||||
virtual void setRegion(int region) = 0;
|
||||
virtual void setBatteryStatus(int status) = 0;
|
||||
|
||||
signals:
|
||||
void videoAvailable(const QByteArray &packet);
|
||||
|
@ -51,27 +60,144 @@ signals:
|
|||
void vibrate(bool on);
|
||||
void errorOccurred();
|
||||
void syncCompleted(bool success);
|
||||
void ready();
|
||||
void closed();
|
||||
void error(const QString &err);
|
||||
|
||||
public slots:
|
||||
void sync(const QString &wirelessInterface, uint16_t code);
|
||||
void connectToConsole(const QString &wirelessInterface);
|
||||
void updateTouch(int x, int y);
|
||||
void setButton(int button, int32_t value);
|
||||
void requestIDR();
|
||||
void setRegion(int region);
|
||||
void setBatteryStatus(int status);
|
||||
// These slots must be called with Qt::QueuedConnection to start the event loops in the backend's thread
|
||||
virtual void init();
|
||||
virtual void sync(uint16_t code) = 0;
|
||||
virtual void connectToConsole() = 0;
|
||||
|
||||
};
|
||||
|
||||
class BackendViaLocalRoot : public Backend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BackendViaLocalRoot(const QString &wirelessInterface, QObject *parent = nullptr);
|
||||
|
||||
virtual void interrupt() override;
|
||||
virtual void updateTouch(int x, int y) override;
|
||||
virtual void setButton(int button, int32_t value) override;
|
||||
virtual void requestIDR() override;
|
||||
virtual void setRegion(int region) override;
|
||||
virtual void setBatteryStatus(int status) override;
|
||||
|
||||
public slots:
|
||||
virtual void sync(uint16_t code) override;
|
||||
virtual void connectToConsole() override;
|
||||
|
||||
private:
|
||||
QString m_wirelessInterface;
|
||||
|
||||
};
|
||||
|
||||
class BackendViaPipe : public Backend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BackendViaPipe(QObject *parent = nullptr);
|
||||
|
||||
virtual void interrupt() override;
|
||||
virtual void updateTouch(int x, int y) override;
|
||||
virtual void setButton(int button, int32_t value) override;
|
||||
virtual void requestIDR() override;
|
||||
virtual void setRegion(int region) override;
|
||||
virtual void setBatteryStatus(int status) override;
|
||||
|
||||
public slots:
|
||||
virtual void sync(uint16_t code) override;
|
||||
virtual void connectToConsole() override;
|
||||
|
||||
protected slots:
|
||||
virtual ssize_t readFromPipe(void *data, size_t length) = 0;
|
||||
virtual ssize_t writeToPipe(const void *data, size_t length) = 0;
|
||||
|
||||
void quitPipe();
|
||||
|
||||
};
|
||||
|
||||
class BackendViaNamedPipe : public BackendViaPipe
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BackendViaNamedPipe(const QString &wirelessInterface, QObject *parent = nullptr);
|
||||
virtual ~BackendViaNamedPipe() override;
|
||||
|
||||
public slots:
|
||||
virtual void init() override;
|
||||
|
||||
private:
|
||||
BackendPipe *m_pipe;
|
||||
QThread *m_pipeThread;
|
||||
int m_pipeIn;
|
||||
int m_pipeOut;
|
||||
QMutex m_pipeMutex;
|
||||
QAtomicInt m_interrupt;
|
||||
|
||||
protected slots:
|
||||
virtual ssize_t readFromPipe(void *data, size_t length) override;
|
||||
virtual ssize_t writeToPipe(const void *data, size_t length) override;
|
||||
|
||||
private slots:
|
||||
void setUpPipes(const QString &in, const QString &out);
|
||||
|
||||
};
|
||||
|
||||
class BackendUdpWrapper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BackendUdpWrapper(const QHostAddress &backendAddr, quint16 backendPort, QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void start();
|
||||
void write(const QByteArray &data);
|
||||
|
||||
signals:
|
||||
void receivedData(const QByteArray &data);
|
||||
void socketReady(quint16 port);
|
||||
void closed();
|
||||
void error(const QString &err);
|
||||
|
||||
private:
|
||||
QUdpSocket *m_socket;
|
||||
QHostAddress m_backendAddress;
|
||||
quint16 m_frontendPort;
|
||||
quint16 m_backendPort;
|
||||
|
||||
private slots:
|
||||
void readPendingDatagrams();
|
||||
|
||||
};
|
||||
|
||||
class BackendViaUdp : public BackendViaPipe
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BackendViaUdp(const QHostAddress &backendAddr, quint16 backendPort, QObject *parent = nullptr);
|
||||
virtual ~BackendViaUdp() override;
|
||||
|
||||
public slots:
|
||||
virtual void init() override;
|
||||
|
||||
protected slots:
|
||||
virtual ssize_t readFromPipe(void *data, size_t length) override;
|
||||
virtual ssize_t writeToPipe(const void *data, size_t length) override;
|
||||
|
||||
private:
|
||||
BackendUdpWrapper *m_socket;
|
||||
QThread *m_socketThread;
|
||||
|
||||
QByteArray m_buffer;
|
||||
|
||||
QMutex m_readMutex;
|
||||
QWaitCondition m_readWaitCond;
|
||||
|
||||
private slots:
|
||||
void receivedData(const QByteArray &data);
|
||||
void socketReady(quint16 port);
|
||||
|
||||
};
|
||||
|
||||
#endif // BACKEND_H
|
11
app/backendinitdialog.cpp
Normal file
11
app/backendinitdialog.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include "backendinitdialog.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
BackendInitDialog::BackendInitDialog(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
QVBoxLayout *l = new QVBoxLayout(this);
|
||||
|
||||
l->addWidget(new QLabel(tr("Initializing backend...")));
|
||||
}
|
13
app/backendinitdialog.h
Normal file
13
app/backendinitdialog.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef BACKEND_INIT_DIALOG_H
|
||||
#define BACKEND_INIT_DIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class BackendInitDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BackendInitDialog(QWidget *parent = nullptr);
|
||||
};
|
||||
|
||||
#endif // BACKEND_INIT_DIALOG_H
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/wireless.h>
|
||||
#include <vanilla.h>
|
||||
|
||||
#include "backendinitdialog.h"
|
||||
#include "inputconfigdialog.h"
|
||||
#include "keymap.h"
|
||||
#include "syncdialog.h"
|
||||
|
@ -35,6 +36,8 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
|
|||
QMessageBox::critical(this, tr("SDL2 Error"), tr("SDL2 failed to initialize. Controller support will be unavailable."));
|
||||
}
|
||||
|
||||
m_backend = nullptr;
|
||||
|
||||
qRegisterMetaType<uint16_t>("uint16_t");
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
|
@ -71,6 +74,7 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
|
|||
configLayout->addWidget(new QLabel(tr("Wi-Fi Adapter: "), connectionConfigGroupBox), row, 0);
|
||||
|
||||
m_wirelessInterfaceComboBox = new QComboBox(connectionConfigGroupBox);
|
||||
connect(m_wirelessInterfaceComboBox, &QComboBox::currentIndexChanged, this, &MainWindow::closeBackend);
|
||||
configLayout->addWidget(m_wirelessInterfaceComboBox, row, 1);
|
||||
|
||||
row++;
|
||||
|
@ -84,7 +88,6 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
|
|||
m_connectBtn = new QPushButton(connectionConfigGroupBox);
|
||||
m_connectBtn->setCheckable(true);
|
||||
//m_connectBtn->setEnabled(vanilla_has_config()); // TODO: Implement this properly through the pipe at some point
|
||||
m_backend = nullptr;
|
||||
setConnectedState(false);
|
||||
connect(m_connectBtn, &QPushButton::clicked, this, &MainWindow::setConnectedState);
|
||||
configLayout->addWidget(m_connectBtn, row, 0, 1, 2);
|
||||
|
@ -209,9 +212,6 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
|
|||
|
||||
configOuterLayout->addStretch();
|
||||
|
||||
m_backend = new Backend();
|
||||
startObjectOnThread(m_backend);
|
||||
|
||||
m_videoDecoder = new VideoDecoder();
|
||||
connect(m_recordBtn, &QPushButton::clicked, m_videoDecoder, &VideoDecoder::enableRecording);
|
||||
startObjectOnThread(m_videoDecoder);
|
||||
|
@ -224,18 +224,10 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
|
|||
startObjectOnThread(m_audioHandler);
|
||||
QMetaObject::invokeMethod(m_audioHandler, &AudioHandler::run, Qt::QueuedConnection);
|
||||
|
||||
connect(m_backend, &Backend::videoAvailable, m_videoDecoder, &VideoDecoder::sendPacket);
|
||||
connect(m_backend, &Backend::audioAvailable, m_videoDecoder, &VideoDecoder::sendAudio);
|
||||
connect(m_backend, &Backend::syncCompleted, this, [this](bool e){if (e) m_connectBtn->setEnabled(true);});
|
||||
connect(m_videoDecoder, &VideoDecoder::frameReady, m_viewer, &Viewer::setImage);
|
||||
connect(m_videoDecoder, &VideoDecoder::recordingError, this, &MainWindow::recordingError);
|
||||
connect(m_videoDecoder, &VideoDecoder::recordingFinished, this, &MainWindow::recordingFinished);
|
||||
connect(m_videoDecoder, &VideoDecoder::requestIDR, m_backend, &Backend::requestIDR, Qt::DirectConnection);
|
||||
connect(m_backend, &Backend::audioAvailable, m_audioHandler, &AudioHandler::write);
|
||||
connect(m_backend, &Backend::vibrate, m_gamepadHandler, &GamepadHandler::vibrate, Qt::DirectConnection);
|
||||
connect(m_viewer, &Viewer::touch, m_backend, &Backend::updateTouch, Qt::DirectConnection);
|
||||
connect(m_gamepadHandler, &GamepadHandler::gamepadsChanged, this, &MainWindow::populateControllers);
|
||||
connect(m_gamepadHandler, &GamepadHandler::buttonStateChanged, m_backend, &Backend::setButton, Qt::DirectConnection);
|
||||
connect(m_viewer, &Viewer::keyPressed, m_gamepadHandler, &GamepadHandler::keyPressed, Qt::DirectConnection);
|
||||
connect(m_viewer, &Viewer::keyReleased, m_gamepadHandler, &GamepadHandler::keyReleased, Qt::DirectConnection);
|
||||
|
||||
|
@ -258,8 +250,7 @@ MainWindow::~MainWindow()
|
|||
|
||||
m_videoDecoder->deleteLater();
|
||||
|
||||
m_backend->interrupt();
|
||||
m_backend->deleteLater();
|
||||
closeBackend();
|
||||
|
||||
for (QThread *t : m_threadMap) {
|
||||
t->quit();
|
||||
|
@ -305,14 +296,22 @@ void MainWindow::populateWirelessInterfaces()
|
|||
while (tmp)
|
||||
{
|
||||
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_PACKET) {
|
||||
if (isWireless(tmp->ifa_name, nullptr))
|
||||
m_wirelessInterfaceComboBox->addItem(tmp->ifa_name);
|
||||
if (isWireless(tmp->ifa_name, nullptr)) {
|
||||
QString s = tmp->ifa_name;
|
||||
m_wirelessInterfaceComboBox->addItem(s, s);
|
||||
}
|
||||
}
|
||||
|
||||
tmp = tmp->ifa_next;
|
||||
}
|
||||
|
||||
freeifaddrs(addrs);
|
||||
|
||||
if (m_wirelessInterfaceComboBox->count() > 0) {
|
||||
m_wirelessInterfaceComboBox->insertSeparator(m_wirelessInterfaceComboBox->count());
|
||||
}
|
||||
|
||||
m_wirelessInterfaceComboBox->addItem(tr("External Server..."));
|
||||
}
|
||||
|
||||
void MainWindow::populateMicrophones()
|
||||
|
@ -340,33 +339,77 @@ void MainWindow::populateControllers()
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void MainWindow::initBackend(T func)
|
||||
{
|
||||
if (!m_backend) {
|
||||
BackendInitDialog *d = new BackendInitDialog(this);
|
||||
d->open();
|
||||
|
||||
QString localWirelessIntf = m_wirelessInterfaceComboBox->currentData().toString();
|
||||
if (localWirelessIntf.isEmpty()) {
|
||||
// TODO: Prompt for UDP server address
|
||||
m_backend = new BackendViaUdp(QHostAddress::LocalHost, 10200);
|
||||
} else if ((geteuid() != 0)) {
|
||||
// If not root, use named pipe
|
||||
m_backend = new BackendViaNamedPipe(localWirelessIntf);
|
||||
} else {
|
||||
// If root, use lib locally
|
||||
m_backend = new BackendViaLocalRoot(localWirelessIntf);
|
||||
}
|
||||
|
||||
connect(m_backend, &Backend::closed, d, &BackendInitDialog::deleteLater);
|
||||
connect(m_backend, &Backend::ready, d, &BackendInitDialog::deleteLater);
|
||||
|
||||
connect(m_backend, &Backend::closed, this, &MainWindow::closeBackend);
|
||||
connect(m_backend, &Backend::ready, this, func);
|
||||
connect(m_backend, &Backend::error, this, &MainWindow::showBackendError);
|
||||
|
||||
connect(m_backend, &Backend::videoAvailable, m_videoDecoder, &VideoDecoder::sendPacket);
|
||||
connect(m_backend, &Backend::audioAvailable, m_videoDecoder, &VideoDecoder::sendAudio);
|
||||
connect(m_backend, &Backend::syncCompleted, this, [this](bool e){if (e) m_connectBtn->setEnabled(true);});
|
||||
connect(m_videoDecoder, &VideoDecoder::requestIDR, m_backend, &Backend::requestIDR, Qt::DirectConnection);
|
||||
connect(m_backend, &Backend::audioAvailable, m_audioHandler, &AudioHandler::write);
|
||||
connect(m_backend, &Backend::vibrate, m_gamepadHandler, &GamepadHandler::vibrate, Qt::DirectConnection);
|
||||
connect(m_viewer, &Viewer::touch, m_backend, &Backend::updateTouch, Qt::DirectConnection);
|
||||
connect(m_gamepadHandler, &GamepadHandler::buttonStateChanged, m_backend, &Backend::setButton, Qt::DirectConnection);
|
||||
|
||||
startObjectOnThread(m_backend);
|
||||
QMetaObject::invokeMethod(m_backend, &Backend::init, Qt::QueuedConnection);
|
||||
} else {
|
||||
func();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::showSyncDialog()
|
||||
{
|
||||
SyncDialog *d = new SyncDialog(this);
|
||||
d->setup(m_backend, m_wirelessInterfaceComboBox->currentText());
|
||||
connect(d, &SyncDialog::finished, d, &SyncDialog::deleteLater);
|
||||
d->open();
|
||||
initBackend([this]{
|
||||
SyncDialog *d = new SyncDialog(this);
|
||||
d->setup(m_backend);
|
||||
connect(d, &SyncDialog::finished, d, &SyncDialog::deleteLater);
|
||||
d->open();
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::setConnectedState(bool on)
|
||||
{
|
||||
m_wirelessInterfaceComboBox->setEnabled(!on);
|
||||
m_syncBtn->setEnabled(!on);
|
||||
m_connectBtn->setChecked(on);
|
||||
m_connectBtn->setText(on ? tr("Disconnect") : tr("Connect"));
|
||||
if (on) {
|
||||
m_connectBtn->setText(tr("Disconnect"));
|
||||
initBackend([this]{
|
||||
QMetaObject::invokeMethod(m_backend, &Backend::connectToConsole, Qt::QueuedConnection);
|
||||
|
||||
QMetaObject::invokeMethod(m_backend, "connectToConsole", Qt::QueuedConnection, Q_ARG(QString, m_wirelessInterfaceComboBox->currentText()));
|
||||
|
||||
updateVolumeAxis();
|
||||
updateRegion();
|
||||
updateBatteryStatus();
|
||||
updateVolumeAxis();
|
||||
updateRegion();
|
||||
updateBatteryStatus();
|
||||
});
|
||||
} else {
|
||||
if (m_backend) {
|
||||
m_backend->interrupt();
|
||||
}
|
||||
|
||||
m_connectBtn->setText(tr("Connect"));
|
||||
|
||||
m_viewer->setImage(QImage());
|
||||
}
|
||||
}
|
||||
|
@ -390,7 +433,7 @@ void MainWindow::exitFullScreen()
|
|||
|
||||
void MainWindow::updateVolumeAxis()
|
||||
{
|
||||
m_backend->setButton(VANILLA_AXIS_VOLUME, m_volumeSlider->value() * 0xFF / m_volumeSlider->maximum());
|
||||
if (m_backend) m_backend->setButton(VANILLA_AXIS_VOLUME, m_volumeSlider->value() * 0xFF / m_volumeSlider->maximum());
|
||||
}
|
||||
|
||||
void MainWindow::volumeChanged(int v)
|
||||
|
@ -471,10 +514,25 @@ void MainWindow::updateRegionFromComboBox()
|
|||
|
||||
void MainWindow::updateRegion()
|
||||
{
|
||||
m_backend->setRegion(m_regionComboBox->currentData().toInt());
|
||||
if (m_backend) m_backend->setRegion(m_regionComboBox->currentData().toInt());
|
||||
}
|
||||
|
||||
void MainWindow::updateBatteryStatus()
|
||||
{
|
||||
m_backend->setBatteryStatus(m_batteryStatusComboBox->currentData().toInt());
|
||||
if (m_backend) m_backend->setBatteryStatus(m_batteryStatusComboBox->currentData().toInt());
|
||||
}
|
||||
|
||||
void MainWindow::closeBackend()
|
||||
{
|
||||
setConnectedState(false);
|
||||
if (m_backend) {
|
||||
m_backend->interrupt();
|
||||
m_backend->deleteLater();
|
||||
m_backend = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::showBackendError(const QString &err)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Backend Error"), err);
|
||||
}
|
|
@ -32,6 +32,9 @@ private:
|
|||
|
||||
void updateVolumeAxis();
|
||||
|
||||
template<typename T>
|
||||
void initBackend(T func);
|
||||
|
||||
Viewer *m_viewer;
|
||||
|
||||
QComboBox *m_wirelessInterfaceComboBox;
|
||||
|
@ -82,6 +85,10 @@ private slots:
|
|||
|
||||
void takeScreenshot();
|
||||
|
||||
void closeBackend();
|
||||
|
||||
void showBackendError(const QString &err);
|
||||
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
|
|
|
@ -139,14 +139,13 @@ void SyncDialog::launchSync()
|
|||
code += m_code[i] * intPow(10, g_symbolCount - 1 - i);
|
||||
}
|
||||
|
||||
SyncProgressDialog *progressDialog = new SyncProgressDialog(m_backend, m_wirelessInterface, code, this->parentWidget());
|
||||
SyncProgressDialog *progressDialog = new SyncProgressDialog(m_backend, code, this->parentWidget());
|
||||
progressDialog->open();
|
||||
connect(progressDialog, &SyncProgressDialog::finished, this, &SyncProgressDialog::deleteLater);
|
||||
this->close();
|
||||
}
|
||||
|
||||
void SyncDialog::setup(Backend *backend, const QString &wirelessInterface)
|
||||
void SyncDialog::setup(Backend *backend)
|
||||
{
|
||||
m_backend = backend;
|
||||
m_wirelessInterface = wirelessInterface;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ class SyncDialog : public QDialog
|
|||
public:
|
||||
SyncDialog(QWidget *parent = nullptr);
|
||||
|
||||
void setup(Backend *backend, const QString &wirelessInterface);
|
||||
void setup(Backend *backend);
|
||||
|
||||
private:
|
||||
void updateLabels();
|
||||
|
@ -28,7 +28,6 @@ private:
|
|||
QHBoxLayout *m_buttonLayout;
|
||||
|
||||
Backend *m_backend;
|
||||
QString m_wirelessInterface;
|
||||
|
||||
private slots:
|
||||
void buttonClicked();
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#include "mainwindow.h"
|
||||
|
||||
SyncProgressDialog::SyncProgressDialog(Backend *backend, const QString &wirelessInterface, uint16_t code, QWidget *parent) : QDialog(parent)
|
||||
SyncProgressDialog::SyncProgressDialog(Backend *backend, uint16_t code, QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
||||
|
@ -53,7 +53,7 @@ SyncProgressDialog::SyncProgressDialog(Backend *backend, const QString &wireless
|
|||
|
||||
m_backend = backend;
|
||||
connect(m_backend, &Backend::syncCompleted, this, &SyncProgressDialog::syncReturned);
|
||||
QMetaObject::invokeMethod(m_backend, "sync", Q_ARG(QString, wirelessInterface), Q_ARG(uint16_t, code));
|
||||
QMetaObject::invokeMethod(m_backend, "sync", Q_ARG(uint16_t, code));
|
||||
}
|
||||
|
||||
void SyncProgressDialog::syncReturned(bool success)
|
||||
|
|
|
@ -12,7 +12,7 @@ class SyncProgressDialog : public QDialog
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SyncProgressDialog(Backend *backend, const QString &wirelessInterface, uint16_t code, QWidget *parent = nullptr);
|
||||
SyncProgressDialog(Backend *backend, uint16_t code, QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
virtual void done(int r) override;
|
||||
|
@ -22,7 +22,6 @@ private:
|
|||
Backend *m_backend;
|
||||
QLabel *m_headerLabel;
|
||||
QLabel *m_statusLabel;
|
||||
QString m_wirelessInterface;
|
||||
uint16_t m_wpsCode;
|
||||
bool m_cancelled;
|
||||
|
||||
|
|
188
pipe/main.c
188
pipe/main.c
|
@ -32,84 +32,46 @@ int open_fifo(const char *name, int mode)
|
|||
return f;
|
||||
}
|
||||
|
||||
uint8_t buffer[1048576];
|
||||
size_t buffer_pos = 0;
|
||||
pthread_mutex_t buffer_mutex;
|
||||
int fd_in = 0, fd_out = 0;
|
||||
in_addr_t udp_client_addr = 0;
|
||||
in_port_t udp_client_port = 0;
|
||||
struct sockaddr_in udp_client = {0};
|
||||
|
||||
void buffer_start()
|
||||
ssize_t write_pipe(const void *buf, size_t size)
|
||||
{
|
||||
pthread_mutex_lock(&buffer_mutex);
|
||||
}
|
||||
|
||||
void buffer_write(const void *data, size_t length)
|
||||
{
|
||||
memcpy(buffer + buffer_pos, data, length);
|
||||
buffer_pos += length;
|
||||
}
|
||||
|
||||
void buffer_finish()
|
||||
{
|
||||
if (fd_out != 0) {
|
||||
write(fd_out, buffer, buffer_pos);
|
||||
}
|
||||
|
||||
if (udp_client_addr != 0 && udp_client_port != 0) {
|
||||
struct sockaddr_in address;
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = udp_client_addr;
|
||||
address.sin_port = htons(udp_client_port);
|
||||
sendto(fd_in, buffer, buffer_pos, 0, (const struct sockaddr *) &address, sizeof(address));
|
||||
}
|
||||
|
||||
buffer_pos = 0;
|
||||
pthread_mutex_unlock(&buffer_mutex);
|
||||
}
|
||||
|
||||
void buffer_write_single(void *data, size_t length)
|
||||
{
|
||||
buffer_start();
|
||||
buffer_write(data, length);
|
||||
buffer_finish();
|
||||
sendto(fd_in, buf, size, 0, (const struct sockaddr *) &udp_client, sizeof(udp_client));
|
||||
}
|
||||
|
||||
pthread_mutex_t action_mutex;
|
||||
int action_ended = 0;
|
||||
int write_control_code(uint8_t code)
|
||||
{
|
||||
buffer_write_single(&code, 1);
|
||||
struct pipe_control_code cmd;
|
||||
cmd.code = code;
|
||||
write_pipe(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void event_handler(void *context, int event_type, const char *data, size_t data_size)
|
||||
{
|
||||
uint8_t event_sized = event_type;
|
||||
uint64_t data_size_sized = data_size;
|
||||
uint8_t control_code = VANILLA_PIPE_OUT_DATA;
|
||||
struct pipe_data_command cmd;
|
||||
|
||||
buffer_start();
|
||||
buffer_write(&control_code, sizeof(control_code));
|
||||
buffer_write(&event_sized, sizeof(event_sized));
|
||||
buffer_write(&data_size_sized, sizeof(data_size_sized));
|
||||
buffer_write(data, data_size);
|
||||
buffer_finish();
|
||||
cmd.base.code = VANILLA_PIPE_OUT_DATA;
|
||||
cmd.event_type = event_type;
|
||||
cmd.data_size = htons(data_size);
|
||||
memcpy(cmd.data, data, data_size);
|
||||
|
||||
write_pipe(&cmd, sizeof(cmd)-sizeof(cmd.data)+data_size);
|
||||
}
|
||||
|
||||
void write_sync_state(uint8_t success)
|
||||
{
|
||||
uint8_t cc = VANILLA_PIPE_OUT_SYNC_STATE;
|
||||
|
||||
buffer_start();
|
||||
buffer_write(&cc, sizeof(cc));
|
||||
buffer_write(&success, sizeof(success));
|
||||
buffer_finish();
|
||||
struct pipe_sync_state_command cmd;
|
||||
cmd.base.code = VANILLA_PIPE_OUT_SYNC_STATE;
|
||||
cmd.state = success;
|
||||
write_pipe(&cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
#define WIRELESS_INTERFACE_MAX_LEN 100
|
||||
struct sync_args
|
||||
{
|
||||
char wireless_interface[WIRELESS_INTERFACE_MAX_LEN];
|
||||
const char *wireless_interface;
|
||||
uint16_t code;
|
||||
};
|
||||
void *sync_command(void *a)
|
||||
|
@ -128,7 +90,7 @@ void *sync_command(void *a)
|
|||
|
||||
struct connect_args
|
||||
{
|
||||
char wireless_interface[WIRELESS_INTERFACE_MAX_LEN];
|
||||
const char *wireless_interface;
|
||||
};
|
||||
void *connect_command(void *a)
|
||||
{
|
||||
|
@ -144,16 +106,6 @@ void *connect_command(void *a)
|
|||
return (void *) (size_t) r;
|
||||
}
|
||||
|
||||
void read_string(int fd, char *buf, size_t max)
|
||||
{
|
||||
for (size_t i = 0; i < max; i++) {
|
||||
read(fd, &buf[i], 1);
|
||||
if (buf[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lib_logger(const char *fmt, va_list args)
|
||||
{
|
||||
vfprintf(stderr, fmt, args);
|
||||
|
@ -180,18 +132,20 @@ void vapipelog(const char *str, ...)
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
if (argc < 3) {
|
||||
goto show_help;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "-pipe")) {
|
||||
if (argc < 4) {
|
||||
const char *wireless_interface = argv[1];
|
||||
|
||||
if (!strcmp(argv[2], "-pipe")) {
|
||||
if (argc < 5) {
|
||||
printf("-pipe requires <in-fifo> and <out-fifo>\n");
|
||||
goto show_help;
|
||||
}
|
||||
|
||||
const char *pipe_in_filename = argv[2];
|
||||
const char *pipe_out_filename = argv[3];
|
||||
const char *pipe_in_filename = argv[3];
|
||||
const char *pipe_out_filename = argv[4];
|
||||
|
||||
umask(0000);
|
||||
|
||||
|
@ -202,30 +156,18 @@ int main(int argc, char **argv)
|
|||
|
||||
if ((fd_out = open_fifo(pipe_out_filename, O_WRONLY)) == -1) return 1;
|
||||
if ((fd_in = open_fifo(pipe_in_filename, O_RDONLY)) == -1) return 1;
|
||||
} else if (!strcmp(argv[1], "-udp")) {
|
||||
if (argc < 5) {
|
||||
printf("-udp requires <server-port> <client-address> <client-port>\n");
|
||||
} else if (!strcmp(argv[2], "-udp")) {
|
||||
if (argc < 4) {
|
||||
printf("-udp requires <server-port>\n");
|
||||
goto show_help;
|
||||
}
|
||||
|
||||
uint16_t server_port = atoi(argv[2]);
|
||||
uint16_t server_port = atoi(argv[3]);
|
||||
if (server_port == 0) {
|
||||
printf("UDP port provided was invalid\n");
|
||||
goto show_help;
|
||||
}
|
||||
|
||||
udp_client_addr = inet_addr(argv[3]);
|
||||
if (udp_client_addr == 0) {
|
||||
printf("Client IP provided was invalid\n");
|
||||
goto show_help;
|
||||
}
|
||||
|
||||
udp_client_port = atoi(argv[4]);
|
||||
if (udp_client_port == 0) {
|
||||
printf("Client port provided was invalid\n");
|
||||
goto show_help;
|
||||
}
|
||||
|
||||
struct sockaddr_in address;
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
|
@ -239,22 +181,23 @@ int main(int argc, char **argv)
|
|||
|
||||
fprintf(stderr, "READY\n");
|
||||
} else {
|
||||
printf("Unknown mode '%s'\n", argv[1]);
|
||||
printf("Unknown mode '%s'\n", argv[2]);
|
||||
goto show_help;
|
||||
}
|
||||
|
||||
uint8_t control_code;
|
||||
char read_buffer[2048];
|
||||
ssize_t read_size;
|
||||
pthread_t current_action = 0;
|
||||
int m_quit = 0;
|
||||
|
||||
vanilla_install_logger(lib_logger);
|
||||
|
||||
pthread_mutex_init(&buffer_mutex, NULL);
|
||||
pthread_mutex_init(&action_mutex, NULL);
|
||||
|
||||
while (!m_quit) {
|
||||
read_size = read(fd_in, &control_code, 1);
|
||||
struct sockaddr_in sender_addr;
|
||||
socklen_t sender_addr_len = sizeof(sender_addr);
|
||||
read_size = recvfrom(fd_in, read_buffer, sizeof(read_buffer), 0, (struct sockaddr *) &sender_addr, &sender_addr_len);
|
||||
if (read_size == 0) {
|
||||
continue;
|
||||
}
|
||||
|
@ -268,16 +211,22 @@ int main(int argc, char **argv)
|
|||
}
|
||||
pthread_mutex_unlock(&action_mutex);
|
||||
|
||||
switch (control_code) {
|
||||
struct pipe_control_code *control_code = (struct pipe_control_code *) read_buffer;
|
||||
switch (control_code->code) {
|
||||
case VANILLA_PIPE_IN_SYNC:
|
||||
{
|
||||
if (current_action != 0) {
|
||||
write_control_code(VANILLA_PIPE_ERR_BUSY);
|
||||
} else {
|
||||
struct pipe_sync_command *sync_cmd = (struct pipe_sync_command *) read_buffer;
|
||||
sync_cmd->code = ntohs(sync_cmd->code);
|
||||
|
||||
struct sync_args *args = (struct sync_args *) malloc(sizeof(struct sync_args));
|
||||
read(fd_in, &args->code, sizeof(args->code));
|
||||
read_string(fd_in, args->wireless_interface, sizeof(args->wireless_interface));
|
||||
args->code = sync_cmd->code;
|
||||
args->wireless_interface = wireless_interface;
|
||||
|
||||
write_control_code(VANILLA_PIPE_ERR_SUCCESS);
|
||||
|
||||
pthread_create(¤t_action, NULL, sync_command, args);
|
||||
}
|
||||
break;
|
||||
|
@ -288,7 +237,7 @@ int main(int argc, char **argv)
|
|||
write_control_code(VANILLA_PIPE_ERR_BUSY);
|
||||
} else {
|
||||
struct connect_args *args = (struct connect_args *) malloc(sizeof(struct connect_args));
|
||||
read_string(fd_in, args->wireless_interface, sizeof(args->wireless_interface));
|
||||
args->wireless_interface = wireless_interface;
|
||||
write_control_code(VANILLA_PIPE_ERR_SUCCESS);
|
||||
pthread_create(¤t_action, NULL, connect_command, args);
|
||||
}
|
||||
|
@ -296,20 +245,18 @@ int main(int argc, char **argv)
|
|||
}
|
||||
case VANILLA_PIPE_IN_BUTTON:
|
||||
{
|
||||
int32_t button_id;
|
||||
int32_t button_value;
|
||||
read(fd_in, &button_id, sizeof(button_id));
|
||||
read(fd_in, &button_value, sizeof(button_value));
|
||||
vanilla_set_button(button_id, button_value);
|
||||
struct pipe_button_command *btn_cmd = (struct pipe_button_command *) read_buffer;
|
||||
btn_cmd->id = ntohl(btn_cmd->id);
|
||||
btn_cmd->value = ntohl(btn_cmd->value);
|
||||
vanilla_set_button(btn_cmd->id, btn_cmd->value);
|
||||
break;
|
||||
}
|
||||
case VANILLA_PIPE_IN_TOUCH:
|
||||
{
|
||||
int32_t touch_x;
|
||||
int32_t touch_y;
|
||||
read(fd_in, &touch_x, sizeof(touch_x));
|
||||
read(fd_in, &touch_y, sizeof(touch_y));
|
||||
vanilla_set_touch(touch_x, touch_y);
|
||||
struct pipe_touch_command *touch_cmd = (struct pipe_touch_command *) read_buffer;
|
||||
touch_cmd->x = ntohl(touch_cmd->x);
|
||||
touch_cmd->y = ntohl(touch_cmd->y);
|
||||
vanilla_set_touch(touch_cmd->x, touch_cmd->y);
|
||||
break;
|
||||
}
|
||||
case VANILLA_PIPE_IN_INTERRUPT:
|
||||
|
@ -327,26 +274,39 @@ int main(int argc, char **argv)
|
|||
}
|
||||
case VANILLA_PIPE_IN_REGION:
|
||||
{
|
||||
int8_t region;
|
||||
read(fd_in, ®ion, sizeof(region));
|
||||
vanilla_set_region(region);
|
||||
struct pipe_region_command *region_cmd = (struct pipe_region_command *) read_buffer;
|
||||
vanilla_set_region(region_cmd->region);
|
||||
break;
|
||||
}
|
||||
case VANILLA_PIPE_IN_BATTERY:
|
||||
{
|
||||
int8_t battery;
|
||||
read(fd_in, &battery, sizeof(battery));
|
||||
vanilla_set_battery_status(battery);
|
||||
struct pipe_battery_command *battery_cmd = (struct pipe_battery_command *) read_buffer;
|
||||
vanilla_set_battery_status(battery_cmd->battery);
|
||||
break;
|
||||
}
|
||||
case VANILLA_PIPE_IN_QUIT:
|
||||
m_quit = 1;
|
||||
break;
|
||||
case VANILLA_PIPE_IN_BIND:
|
||||
{
|
||||
struct pipe_bind_command *bind_cmd = (struct pipe_bind_command *) read_buffer;
|
||||
|
||||
// Send success message back to client
|
||||
uint8_t cc = VANILLA_PIPE_OUT_BOUND_SUCCESSFUL;
|
||||
sendto(fd_in, &cc, sizeof(cc), 0, (const struct sockaddr *) &sender_addr, sizeof(sender_addr));
|
||||
|
||||
// Add client to list
|
||||
udp_client = sender_addr;
|
||||
|
||||
char addr_buf[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &udp_client.sin_addr.s_addr, addr_buf, INET_ADDRSTRLEN);
|
||||
printf("BIND %s:%u\n", addr_buf, ntohs(udp_client.sin_port));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&action_mutex);
|
||||
pthread_mutex_destroy(&buffer_mutex);
|
||||
|
||||
close(fd_in);
|
||||
close(fd_out);
|
||||
|
@ -354,7 +314,7 @@ int main(int argc, char **argv)
|
|||
return 0;
|
||||
|
||||
show_help:
|
||||
printf("Usage: %s <mode> [args]\n\n", argv[0]);
|
||||
printf("Usage: %s <wireless-interface> <mode> [args]\n\n", argv[0]);
|
||||
printf("vanilla-pipe provides a way to connect a frontend to Vanilla's backend when they\n");
|
||||
printf("aren't able to run on the same device or in the same environment (e.g. when the \n");
|
||||
printf("backend must run as root but the frontend must run as user).\n\n");
|
||||
|
|
59
pipe/pipe.h
59
pipe/pipe.h
|
@ -2,7 +2,7 @@
|
|||
#define VANILLA_PIPE_H
|
||||
|
||||
// Control codes that our pipe will receive
|
||||
enum PipeOpCodes
|
||||
enum pipe_opcodes
|
||||
{
|
||||
VANILLA_PIPE_IN_SYNC = 1,
|
||||
VANILLA_PIPE_IN_CONNECT,
|
||||
|
@ -11,12 +11,14 @@ enum PipeOpCodes
|
|||
VANILLA_PIPE_IN_REQ_IDR,
|
||||
VANILLA_PIPE_IN_REGION,
|
||||
VANILLA_PIPE_IN_BATTERY,
|
||||
VANILLA_PIPE_IN_BIND,
|
||||
|
||||
VANILLA_PIPE_IN_INTERRUPT = 0x1E,
|
||||
VANILLA_PIPE_IN_QUIT = 0x1F,
|
||||
|
||||
VANILLA_PIPE_OUT_DATA = 0x20,
|
||||
VANILLA_PIPE_OUT_SYNC_STATE,
|
||||
VANILLA_PIPE_OUT_BOUND_SUCCESSFUL,
|
||||
VANILLA_PIPE_OUT_EOF,
|
||||
|
||||
// Errors that our pipe will send
|
||||
|
@ -26,4 +28,59 @@ enum PipeOpCodes
|
|||
VANILLA_PIPE_ERR_BUSY
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct pipe_control_code
|
||||
{
|
||||
uint8_t code;
|
||||
};
|
||||
|
||||
struct pipe_sync_command
|
||||
{
|
||||
struct pipe_control_code base;
|
||||
uint16_t code;
|
||||
};
|
||||
|
||||
struct pipe_button_command
|
||||
{
|
||||
struct pipe_control_code base;
|
||||
int32_t id;
|
||||
int32_t value;
|
||||
};
|
||||
|
||||
struct pipe_touch_command
|
||||
{
|
||||
struct pipe_control_code base;
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
};
|
||||
|
||||
struct pipe_region_command
|
||||
{
|
||||
struct pipe_control_code base;
|
||||
int8_t region;
|
||||
};
|
||||
|
||||
struct pipe_battery_command
|
||||
{
|
||||
struct pipe_control_code base;
|
||||
int8_t battery;
|
||||
};
|
||||
|
||||
struct pipe_sync_state_command
|
||||
{
|
||||
struct pipe_control_code base;
|
||||
uint8_t state;
|
||||
};
|
||||
|
||||
struct pipe_data_command
|
||||
{
|
||||
struct pipe_control_code base;
|
||||
uint8_t event_type;
|
||||
uint16_t data_size;
|
||||
uint8_t data[UINT16_MAX];
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif // VANILLA_PIPE_H
|
Loading…
Reference in a new issue