예제 #1
0
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    mainWindow = this;

    trayIconMenu = new QMenu(this);
    restoreAction = new QAction(tr("Restore"), this);
    closeAction = new QAction(tr("Quit ckb"), this);
    trayIconMenu->addAction(restoreAction);
    trayIconMenu->addAction(closeAction);
    trayIcon = new QSystemTrayIcon(QIcon(":/img/ckb-logo.png"), this);
    trayIcon->setContextMenu(trayIconMenu);
    if(!CkbSettings::get("Program/SuppressTrayIcon").toBool())
        trayIcon->show();

#ifdef Q_OS_MACX
    // Make a custom "Close" menu action for OSX, as the default one brings up the "still running" popup unnecessarily
    QMenuBar* menuBar = new QMenuBar(this);
    setMenuBar(menuBar);
    this->menuBar()->addMenu("ckb")->addAction(closeAction);
#else
    // On linux, add a handler for Ctrl+Q
    new QShortcut(QKeySequence("Ctrl+Q"), this, SLOT(quitApp()));
#endif

    connect(ui->actionExit, SIGNAL(triggered()), this, SLOT(quitApp()));
    connect(closeAction, SIGNAL(triggered()), this, SLOT(quitApp()));
    connect(restoreAction, SIGNAL(triggered()), this, SLOT(showWindow()));
    connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconClicked(QSystemTrayIcon::ActivationReason)));

    connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(cleanup()));

    eventTimer = new QTimer(this);
    eventTimer->setTimerType(Qt::PreciseTimer);
    connect(eventTimer, SIGNAL(timeout()), this, SLOT(timerTick()));
    eventTimer->start(1000 / 60);

    QCoreApplication::setOrganizationName("ckb");

    ui->tabWidget->addTab(settingsWidget = new SettingsWidget(this), configLabel);
    settingsWidget->setVersion("ckb " CKB_VERSION_STR);

    ckbGuiVersion = PARSE_CKB_VERSION(CKB_VERSION_STR);
    scanKeyboards();
}
예제 #2
0
파일: kbfirmware.cpp 프로젝트: shazron/ckb
void KbFirmware::processDownload(QNetworkReply* reply){
    if(reply->error() != QNetworkReply::NoError)
        return;
    // Update last check
    lastCheck = lastFinished = QDateTime::currentMSecsSinceEpoch();
    QByteArray data = reply->readAll();
    // Don't do anything if this is the same as the last version downloaded
    QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Sha256);
    if(hash == fwTableHash)
        return;
    fwTableHash = hash;
    if(hasGPG == UNKNOWN){
        // Check for a GPG installation
        QProcess gpg;
        gpg.start("gpg", QStringList("--version"));
        gpg.waitForFinished();
        if(gpg.error() == QProcess::FailedToStart)
            // No GPG install
            hasGPG = NO;
        else {
            QString output = QString::fromUtf8(gpg.readAll());
            // Must support RSA keys and SHA256
            if(output.contains("RSA", Qt::CaseInsensitive) && output.contains("SHA256", Qt::CaseInsensitive))
                hasGPG = YES;
            else
                hasGPG = NO;
        }
        if(!hasGPG)
            qDebug() << "No GPG detected, signature verification disabled";
    }
    if(hasGPG){
        // If GPG is available, check the signature on the file before proceeding.
        QDir tmp = QDir::temp();
        // Save file to a temporary path. Include PID to avoid conflicts
        qint64 pid = QCoreApplication::applicationPid();
        QString fwPath = tmp.absoluteFilePath(QString("ckb-%1-firmware").arg(pid));
        QFile firmware(fwPath);
        if(!firmware.open(QIODevice::WriteOnly)
                || firmware.write(data) != data.length()){
            qDebug() << "Failed to write firmware file to temporary location, aborting firmware check";
            return;
        }
        firmware.close();
        // Write GPG key
        QString keyPath = tmp.absoluteFilePath(QString("ckb-%1-key.gpg").arg(pid));
        if(!QFile::copy(":/bin/msckey.gpg", keyPath)){
            firmware.remove();
            qDebug() << "Failed to write GPG key to temporary location, aborting firmware check";
            return;
        }
        // Check signature
        QProcess gpg;
        gpg.start("gpg", QStringList("--no-default-keyring") << "--keyring" << keyPath << "--verify" << fwPath);
        gpg.waitForFinished();
        // Clean up temp files
        tmp.remove(fwPath);
        tmp.remove(keyPath);
        if(gpg.error() != QProcess::UnknownError || gpg.exitCode() != 0){
            qDebug() << "GPG couldn't verify firmware signature:";
            qDebug() << gpg.readAllStandardOutput();
            qDebug() << gpg.readAllStandardError();
            return;
        }
        // Signature good, proceed to update database
    }
    fwTable.clear();
    QStringList lines = QString::fromUtf8(data).split("\n");
    bool scan = false;
    foreach(QString line, lines){
        // Collapse whitespace
        line.replace(QRegExp("\\s+"), " ").remove(QRegExp("^\\s")).remove(QRegExp("\\s$"));
        // Skip empty or commented-out lines
        if(line.length() == 0 || line.at(0) == '#')
            continue;
        // Don't read anything until the entries begin and don't read anything after they end
        if(!scan){
            if(line == "!BEGIN FW ENTRIES")
                scan = true;
            else
                continue;
        }
        if(line == "!END FW ENTRIES")
            break;
        QStringList components = line.split(" ");
        if(components.length() != 7)
            continue;
        // "VENDOR-PRODUCT"
        QString device = components[0].toUpper() + "-" + components[1].toUpper();
        FW fw;
        fw.fwVersion = components[2].toFloat();                             // Firmware blob version
        fw.url = QUrl::fromPercentEncoding(components[3].toLatin1());       // URL to zip file
        fw.ckbVersion = PARSE_CKB_VERSION(components[4]);                   // Minimum ckb version
        fw.fileName = QUrl::fromPercentEncoding(components[5].toLatin1());  // Name of file inside zip
        fw.hash = QByteArray::fromHex(components[6].toLatin1());            // SHA256 of file inside zip
        // Update entry
        fwTable[device] = fw;
    }
예제 #3
0
void MainWindow::scanKeyboards(){
    QString rootdev = devpath.arg(0);
    QFile connected(rootdev + "/connected");
    if(!connected.open(QIODevice::ReadOnly)){
        // No root controller - remove all keyboards
        while(ui->tabWidget->count() > 1)
            ui->tabWidget->removeTab(0);
        foreach(KbWidget* w, kbWidgets)
            w->deleteLater();
        kbWidgets.clear();
        settingsWidget->setStatus("Driver inactive");
        ckbDaemonVersion = INFINITY;
        daemonVStr.clear();
        return;
    }
    // Check daemon version
    QFile version(rootdev + "/version");
    if(version.open(QIODevice::ReadOnly)){
        daemonVStr = QString::fromUtf8(version.readLine()).trimmed();
        version.close();
    } else
        daemonVStr = "<unavailable>";
    ckbDaemonVersion = PARSE_CKB_VERSION(daemonVStr);

    // Scan connected devices
    foreach(KbWidget* w, kbWidgets)
        w->active(false);
    QString line;
    while((line = connected.readLine().trimmed()) != ""){
        QStringList components = line.trimmed().split(" ");
        if(components.length() < 2)
            continue;
        QString path = components[0], serial = components[1];
        // Connected already?
        KbWidget* widget = 0;
        foreach(KbWidget* w, kbWidgets){
            if(w->device && w->device->matches(path, serial)){
                widget = w;
                w->active(true);
                break;
            }
        }
        if(widget)
            continue;
        // Add the keyboard
        widget = new KbWidget(this, path, "Devices");
        if(!widget->isActive()){
            delete widget;
            continue;
        }
        kbWidgets.append(widget);
        int count = ui->tabWidget->count();
        ui->tabWidget->insertTab(count - 1, widget, widget->name());
        if(ui->tabWidget->currentIndex() == count)
            ui->tabWidget->setCurrentIndex(count - 1);
        connect(eventTimer, SIGNAL(timeout()), widget->device, SLOT(frameUpdate()));
    }
    connected.close();

    // Remove any devices not found in the connected list
    bool updateShown = false;
    foreach(KbWidget* w, kbWidgets){
        if(w->isActive()){
            if(!updateShown){
                // Display firmware upgrade notification if a new version is available (and user has automatic updates enabled)
                if(CkbSettings::get("Program/DisableAutoFWCheck").toBool())
                    continue;
                float version = KbFirmware::versionForBoard(w->device->features);
                if(version > w->device->firmware.toFloat()){
                    if(w->hasShownNewFW)
                        continue;
                    w->hasShownNewFW = true;
                    w->updateFwButton();
                    // Don't display more than one of these at once
                    updateShown = true;
                    // Don't run this method here because it will lock up the timer and prevent devices from working properly
                    // Use a queued invocation instead
                    metaObject()->invokeMethod(this, "showFwUpdateNotification", Qt::QueuedConnection, Q_ARG(QWidget*, w), Q_ARG(float, version));
                }
            }
            w->saveIfNeeded();
        } else {
            int i = kbWidgets.indexOf(w);
            ui->tabWidget->removeTab(i);
            kbWidgets.removeAt(i);
            w->deleteLater();
        }
    }