mumble-voip_mumble/src/mumble/G15LCDEngine_helper.cpp

168 lines
4.8 KiB
C++

// Copyright 2010-2023 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#include "G15LCDEngine_helper.h"
#include "MumbleApplication.h"
static LCDEngine *G15LCDEngineNew() {
return new G15LCDEngineHelper();
}
static LCDEngineRegistrar registrarLCDEngine(G15LCDEngineNew);
G15LCDEngineHelper::G15LCDEngineHelper() : LCDEngine() {
bRunning = false;
bUnavailable = true;
#if defined(Q_OS_WIN)
qsHelperExecutable = QString::fromLatin1("\"%1/mumble-g15-helper.exe\"")
.arg(MumbleApplication::instance()->applicationVersionRootPath());
#elif defined(Q_OS_MAC)
qsHelperExecutable = QString::fromLatin1("\"%1/mumble-g15-helper\"")
.arg(MumbleApplication::instance()->applicationVersionRootPath());
#endif
qpHelper = new QProcess(this);
qpHelper->setObjectName(QLatin1String("Helper"));
qpHelper->setWorkingDirectory(qApp->applicationDirPath());
/*
* Call our helper to detect whether any Logitech Gamepanel devices
* are available on the system.
*/
qpHelper->start(qsHelperExecutable, QStringList(QLatin1String("/detect")));
qpHelper->waitForFinished();
if (qpHelper->exitCode() != 0) {
qWarning("G15LCDEngine_lglcd: Logitech LCD Manager not detected.");
return;
}
qlDevices << new G15LCDDeviceHelper(this);
bUnavailable = false;
QMetaObject::connectSlotsByName(this);
}
G15LCDEngineHelper::~G15LCDEngineHelper() {
setProcessStatus(false);
}
QList< LCDDevice * > G15LCDEngineHelper::devices() const {
return qlDevices;
}
void G15LCDEngineHelper::setProcessStatus(bool run) {
if (bUnavailable)
return;
if (run && !bRunning) {
bRunning = true;
qpHelper->start(qsHelperExecutable, QStringList(QLatin1String("/mumble")));
if (!qpHelper->waitForStarted(2000)) {
qWarning("G15LCDEngine_lglcd: Unable to launch G15 helper.");
bRunning = false;
return;
}
} else if (!run && bRunning) {
bRunning = false;
qpHelper->kill();
qpHelper->waitForFinished();
}
}
void G15LCDEngineHelper::on_Helper_finished(int exitCode, QProcess::ExitStatus status) {
/* Skip the signal if we killed ourselves. */
if (!bRunning)
return;
if (status == QProcess::CrashExit) {
qWarning("G15LCDEngine_lglcd: Helper process crashed. Restarting.");
qpHelper->start(qsHelperExecutable, QStringList(QLatin1String("/mumble")));
} else if (status == QProcess::NormalExit && exitCode != 0) {
qWarning("G15LCDEngine_lglcd: Helper process exited. Exit code was: `%i'. Not attempting recovery.", exitCode);
bUnavailable = true;
}
}
bool G15LCDEngineHelper::framebufferReady() const {
return !bUnavailable && (qpHelper->state() == QProcess::Running);
}
void G15LCDEngineHelper::submitFrame(bool alert, unsigned char *buf, qint64 len) {
char pri = alert ? 1 : 0;
if ((qpHelper->write(&pri, 1) != 1) || (qpHelper->write(reinterpret_cast< char * >(buf), len) != len))
qWarning("G15LCDEngine_lglcd: failed to write");
}
/* -- */
G15LCDDeviceHelper::G15LCDDeviceHelper(G15LCDEngineHelper *e) : LCDDevice(), bEnabled(false) {
engine = e;
}
G15LCDDeviceHelper::~G15LCDDeviceHelper() {
}
bool G15LCDDeviceHelper::enabled() {
return bEnabled;
}
void G15LCDDeviceHelper::setEnabled(bool b) {
engine->setProcessStatus(b);
bEnabled = b;
}
void G15LCDDeviceHelper::blitImage(QImage *img, bool alert) {
Q_ASSERT(img);
int len = G15_MAX_FBMEM_BITS;
uchar *tmp = img->bits();
uchar buf[G15_MAX_FBMEM];
if (!engine->framebufferReady())
return;
if (!bEnabled)
return;
/*
* The amount of copying/conversion we're doing is hideous.
*
* To draw to the LCD display using Logitech's SDK, we need to pass
* it a byte array (in which each byte represents a single pixel on
* the LCD.)
*
* Unfortunately, there's no way out, really. We *could* perhaps draw
* directly to a monochrome "bytemap" (via Format_Indexed8, and a mono-
* chrome colormap), but QPainter simply doesn't want to draw to a
* QImage of Format_Indexed8.
*
* (What's even worse is that the byte array passed to the Logitech SDK
* isn't even the native format of the LCD. It has to convert it once
* more, when it receives a frame.)
*/
for (int i = 0; i < len; i++) {
int idx = i * 8;
buf[idx + 7] = tmp[i] & 0x80 ? 0xff : 0x00;
buf[idx + 6] = tmp[i] & 0x40 ? 0xff : 0x00;
buf[idx + 5] = tmp[i] & 0x20 ? 0xff : 0x00;
buf[idx + 4] = tmp[i] & 0x10 ? 0xff : 0x00;
buf[idx + 3] = tmp[i] & 0x08 ? 0xff : 0x00;
buf[idx + 2] = tmp[i] & 0x04 ? 0xff : 0x00;
buf[idx + 1] = tmp[i] & 0x02 ? 0xff : 0x00;
buf[idx + 0] = tmp[i] & 0x01 ? 0xff : 0x00;
}
engine->submitFrame(alert, buf, G15_MAX_FBMEM);
}
QString G15LCDDeviceHelper::name() const {
return QString::fromLatin1("Logitech Gamepanel");
}
QSize G15LCDDeviceHelper::size() const {
return QSize(G15_MAX_WIDTH, G15_MAX_HEIGHT);
}