mirror of
https://github.com/ApfelTeeSaft/vanilla.git
synced 2026-08-27 03:43:41 +00:00
288 lines
7.5 KiB
C++
288 lines
7.5 KiB
C++
#include "backend.h"
|
|
|
|
#include <QCoreApplication>
|
|
#include <QDir>
|
|
#include <QMessageBox>
|
|
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <vanilla.h>
|
|
|
|
#include "../pipe/pipe.h"
|
|
|
|
void vanillaEventHandler(void *context, int type, const char *data, size_t dataLength)
|
|
{
|
|
Backend *backend = static_cast<Backend*>(context);
|
|
|
|
switch (type) {
|
|
case VANILLA_EVENT_VIDEO:
|
|
emit backend->videoAvailable(QByteArray(data, dataLength));
|
|
break;
|
|
case VANILLA_EVENT_AUDIO:
|
|
emit backend->audioAvailable(QByteArray(data, dataLength));
|
|
break;
|
|
case VANILLA_EVENT_VIBRATE:
|
|
emit backend->vibrate(*data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Backend::Backend(QObject *parent) : QObject(parent)
|
|
{
|
|
m_pipe = nullptr;
|
|
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);
|
|
}
|
|
}
|
|
|
|
Backend::~Backend()
|
|
{
|
|
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();
|
|
}
|
|
}
|
|
|
|
void ignoreByte(int pipe)
|
|
{
|
|
uint8_t ignore;
|
|
read(pipe, &ignore, sizeof(ignore));
|
|
}
|
|
|
|
void writeByte(int pipe, uint8_t byte)
|
|
{
|
|
write(pipe, &byte, sizeof(byte));
|
|
}
|
|
|
|
void Backend::interrupt()
|
|
{
|
|
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();
|
|
}
|
|
}
|
|
|
|
void writeNullTermString(int pipe, const QString &s)
|
|
{
|
|
QByteArray sc = s.toUtf8();
|
|
write(pipe, sc.constData(), sc.size());
|
|
writeByte(pipe, 0);
|
|
}
|
|
|
|
void Backend::requestIDR()
|
|
{
|
|
if (m_pipe) {
|
|
m_pipeMutex.lock();
|
|
writeByte(m_pipeOut, VANILLA_PIPE_IN_REQ_IDR);
|
|
m_pipeMutex.unlock();
|
|
} else {
|
|
vanilla_request_idr();
|
|
}
|
|
}
|
|
|
|
void Backend::connectToConsole(const QString &wirelessInterface)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
void Backend::updateTouch(int x, int y)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
void Backend::setButton(int button, int32_t value)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
void Backend::setRegion(int region)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
void Backend::sync(const QString &wirelessInterface, uint16_t code)
|
|
{
|
|
if (m_pipe) {
|
|
// Request pipe to sync
|
|
m_pipeMutex.lock();
|
|
|
|
// Write control code
|
|
writeByte(m_pipeOut, VANILLA_PIPE_IN_SYNC);
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
} else {
|
|
QByteArray wirelessInterfaceC = wirelessInterface.toUtf8();
|
|
int r = vanilla_sync_with_console(wirelessInterfaceC.constData(), code);
|
|
emit syncCompleted(r == VANILLA_SUCCESS);
|
|
}
|
|
}
|
|
|
|
void Backend::setUpPipes(const QByteArray &in, const QByteArray &out)
|
|
{
|
|
m_pipeIn = open(in.constData(), O_RDONLY);
|
|
if (m_pipeIn == -1) {
|
|
QMessageBox::critical(nullptr, tr("Pipe Error"), tr("Failed to create in pipe: %1").arg(strerror(errno)));
|
|
}
|
|
m_pipeOut = open(out.constData(), O_WRONLY);
|
|
if (m_pipeOut == -1) {
|
|
QMessageBox::critical(nullptr, tr("Pipe Error"), tr("Failed to create out pipe: %1").arg(strerror(errno)));
|
|
}
|
|
|
|
printf("Established connection with backend\n");
|
|
}
|
|
|
|
BackendPipe::BackendPipe(QObject *parent) : QObject(parent)
|
|
{
|
|
m_process = nullptr;
|
|
}
|
|
|
|
BackendPipe::~BackendPipe()
|
|
{
|
|
waitForFinished();
|
|
}
|
|
|
|
void BackendPipe::waitForFinished()
|
|
{
|
|
if (m_process) {
|
|
m_process->waitForFinished();
|
|
}
|
|
}
|
|
|
|
void BackendPipe::start()
|
|
{
|
|
const QString pipe_bin = QDir(QCoreApplication::applicationDirPath()).filePath(QStringLiteral("vanilla-pipe"));
|
|
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);});
|
|
m_process->start(QStringLiteral("pkexec"), {pipe_bin});
|
|
}
|
|
|
|
void BackendPipe::receivedData()
|
|
{
|
|
while (m_process->canReadLine()) {
|
|
QByteArray a = m_process->readLine().trimmed();
|
|
// Their out is our in which is why these are flipped
|
|
if (m_pipeOutFilename.isEmpty()) {
|
|
if (QFile::exists(a)) {
|
|
m_pipeOutFilename = a;
|
|
}
|
|
} else if (m_pipeInFilename.isEmpty()) {
|
|
if (QFile::exists(a)) {
|
|
m_pipeInFilename = a;
|
|
emit pipesAvailable(m_pipeInFilename, m_pipeOutFilename);
|
|
}
|
|
} else {
|
|
printf("%s\n", a.constData());
|
|
}
|
|
}
|
|
} |