Build changes

- Add support for embedded Linux without X, dbus, udisks, ntp, etc.
- Misc minor changes
This commit is contained in:
Floris Bos 2020-05-25 00:36:16 +02:00
parent 71eefa47cf
commit 5b072f3196
13 changed files with 189 additions and 222 deletions

View file

@ -20,21 +20,29 @@ set(CMAKE_AUTORCC ON)
# Adding headers explicity so they are displayed in Qt Creator # Adding headers explicity so they are displayed in Qt Creator
set(HEADERS config.h imagewriter.h networkaccessmanagerfactory.h nan.h drivelistitem.h drivelistmodel.h driveformatthread.h powersaveblocker.h set(HEADERS config.h imagewriter.h networkaccessmanagerfactory.h nan.h drivelistitem.h drivelistmodel.h driveformatthread.h powersaveblocker.h
downloadthread.h downloadextractthread.h acceleratedcryptographichash.h dependencies/mountutils/src/mountutils.hpp) downloadthread.h downloadextractthread.h dependencies/mountutils/src/mountutils.hpp)
# Add 3rd-party dependencies # Add dependencies
if (APPLE) if (APPLE)
set_source_files_properties("icons/rpi-imager.icns" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") set_source_files_properties("icons/rpi-imager.icns" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
set(DEPENDENCIES mac/macfile.cpp mac/macfile.h mac/Info.plist dependencies/mountutils/src/darwin/functions.cpp set(DEPENDENCIES mac/macfile.cpp mac/macfile.h mac/Info.plist dependencies/mountutils/src/darwin/functions.cpp
dependencies/drivelist/src/darwin/list.mm dependencies/drivelist/src/darwin/REDiskList.m icons/rpi-imager.icns) dependencies/drivelist/src/darwin/list.mm dependencies/drivelist/src/darwin/REDiskList.m icons/rpi-imager.icns)
enable_language(OBJC C) enable_language(OBJC C)
elseif (UNIX) elseif (UNIX)
set(DEPENDENCIES dependencies/mountutils/src/linux/functions.cpp linux/linuxdrivelist.cpp linux/udisks2api.cpp linux/udisks2api.h) set(DEPENDENCIES acceleratedcryptographichash.cpp dependencies/mountutils/src/linux/functions.cpp linux/linuxdrivelist.cpp)
find_package(Qt5DBus)
if(Qt5DBus_FOUND)
set(DEPENDENCIES ${DEPENDENCIES} linux/udisks2api.cpp linux/udisks2api.h)
set(EXTRALIBS Qt5::DBus)
message("udisks2 support enabled")
else()
message("DBUS not found. Disabling udisks2 support")
endif()
elseif (WIN32) elseif (WIN32)
set(DEPENDENCIES dependencies/mountutils/src/windows/functions.cpp dependencies/drivelist/src/windows/list.cpp set(DEPENDENCIES acceleratedcryptographichash.cpp dependencies/mountutils/src/windows/functions.cpp dependencies/drivelist/src/windows/list.cpp
windows/winfile.cpp windows/winfile.h windows/winfile.cpp windows/winfile.h
windows/rpi-imager.rc) windows/rpi-imager.rc)
set(EXTRALIBS setupapi) set(EXTRALIBS setupapi)
endif() endif()
include_directories(BEFORE .) include_directories(BEFORE .)
@ -60,10 +68,12 @@ endif()
set(SOURCES "main.cpp" "imagewriter.cpp" "networkaccessmanagerfactory.cpp" set(SOURCES "main.cpp" "imagewriter.cpp" "networkaccessmanagerfactory.cpp"
"drivelistitem.cpp" "drivelistmodel.cpp" "downloadthread.cpp" "downloadextractthread.cpp" "drivelistitem.cpp" "drivelistmodel.cpp" "downloadthread.cpp" "downloadextractthread.cpp"
"driveformatthread.cpp" "powersaveblocker.cpp" "acceleratedcryptographichash.cpp" "qml.qrc") "driveformatthread.cpp" "powersaveblocker.cpp" "qml.qrc")
find_package(OpenSSL REQUIRED) find_package(Qt5 COMPONENTS Core Quick LinguistTools Svg OPTIONAL_COMPONENTS Widgets)
find_package(Qt5 COMPONENTS Core Quick Widgets LinguistTools REQUIRED) if (Qt5Widgets_FOUND)
set(EXTRALIBS ${EXTRALIBS} Qt5::Widgets)
endif()
#qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} i18n/rpi-imager_nl.ts i18n/rpi-imager_zh_cn.ts) #qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} i18n/rpi-imager_nl.ts i18n/rpi-imager_zh_cn.ts)
qt5_add_translation(QM_FILES i18n/rpi-imager_nl.ts i18n/rpi-imager_zh_cn.ts) qt5_add_translation(QM_FILES i18n/rpi-imager_nl.ts i18n/rpi-imager_zh_cn.ts)
@ -82,6 +92,8 @@ if (WIN32)
# Target Windows 7 (needed for drivelist module) # Target Windows 7 (needed for drivelist module)
add_definitions(-DWINVER=0x0601 -D_WIN32_WINNT=0x0601) add_definitions(-DWINVER=0x0601 -D_WIN32_WINNT=0x0601)
find_package(OpenSSL REQUIRED)
# Bundled zlib # Bundled zlib
add_subdirectory(dependencies/zlib-1.2.11) add_subdirectory(dependencies/zlib-1.2.11)
set(ZLIB_LIBRARY zlibstatic) set(ZLIB_LIBRARY zlibstatic)
@ -195,9 +207,7 @@ elseif(APPLE)
find_library(CoreFoundation CoreFoundation) find_library(CoreFoundation CoreFoundation)
find_library(DiskArbitration DiskArbitration) find_library(DiskArbitration DiskArbitration)
find_library(Security Security) find_library(Security Security)
#find_package(Qt5 COMPONENTS Svg) set(EXTRALIBS ${EXTRALIBS} ${CoreFoundation} ${DiskArbitration} ${Security} ${Cocoa})
#set(EXTRALIBS ${CoreFoundation} ${DiskArbitration} ${Security} ${Cocoa} Qt5::Svg)
set(EXTRALIBS ${CoreFoundation} ${DiskArbitration} ${Security} ${Cocoa})
set_target_properties(${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE YES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/mac/Info.plist) set_target_properties(${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE YES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/mac/Info.plist)
find_program(MACDEPLOYQT "macdeployqt" PATHS "${Qt5_DIR}/../../../bin") find_program(MACDEPLOYQT "macdeployqt" PATHS "${Qt5_DIR}/../../../bin")
@ -212,8 +222,7 @@ elseif(APPLE)
else() else()
find_package(CURL REQUIRED 7.32.0) find_package(CURL REQUIRED 7.32.0)
find_package(LibArchive REQUIRED 3.2.0) find_package(LibArchive REQUIRED 3.2.0)
find_package(Qt5 COMPONENTS DBus) find_package(OpenSSL REQUIRED)
set(EXTRALIBS Qt5::DBus)
if (NOT CMAKE_CROSSCOMPILING) if (NOT CMAKE_CROSSCOMPILING)
find_program(LSBLK "lsblk") find_program(LSBLK "lsblk")
if (NOT LSBLK) if (NOT LSBLK)
@ -232,4 +241,4 @@ else()
endif() endif()
include_directories(${CURL_INCLUDE_DIR} ${LibArchive_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR}) include_directories(${CURL_INCLUDE_DIR} ${LibArchive_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Quick Qt5::Widgets ${CURL_LIBRARIES} ${LibArchive_LIBRARIES} ${OPENSSL_LIBRARIES} ${ATOMIC_LIBRARY} ${EXTRALIBS}) target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Quick Qt5::Svg ${CURL_LIBRARIES} ${LibArchive_LIBRARIES} ${OPENSSL_LIBRARIES} ${ATOMIC_LIBRARY} ${EXTRALIBS})

View file

@ -7,6 +7,11 @@
*/ */
#include <QCryptographicHash> #include <QCryptographicHash>
#ifdef Q_OS_DARWIN
typedef QCryptographicHash AcceleratedCryptographicHash;
#else
#include "openssl/sha.h" #include "openssl/sha.h"
class AcceleratedCryptographicHash class AcceleratedCryptographicHash
@ -22,4 +27,5 @@ protected:
SHA256_CTX _sha256; SHA256_CTX _sha256;
}; };
#endif
#endif // ACCELERATEDCRYPTOGRAPHICHASH_H #endif // ACCELERATEDCRYPTOGRAPHICHASH_H

View file

@ -10,6 +10,9 @@
/* Repository URL */ /* Repository URL */
#define OSLIST_URL "https://downloads.raspberrypi.org/os_list_imagingutility.json" #define OSLIST_URL "https://downloads.raspberrypi.org/os_list_imagingutility.json"
/* Time synchronization URL (only used on eglfs QPA platform, URL must be HTTP) */
#define TIME_URL "http://downloads.raspberrypi.org/os_list_imagingutility.json?time_synchronization"
/* Hash algorithm for verifying (uncompressed image) checksum */ /* Hash algorithm for verifying (uncompressed image) checksum */
#define OSLIST_HASH_ALGORITHM QCryptographicHash::Sha256 #define OSLIST_HASH_ALGORITHM QCryptographicHash::Sha256

View file

@ -1,36 +0,0 @@
#!/usr/bin/env node
/*
* Copyright 2017 resin.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
const mountUtils = require('..');
let argv = process.argv.slice(2);
let disk = argv.shift();
if (!disk) {
console.error(`Usage: node example/eject <disk>`);
process.exit(1);
}
process.env.MOUNTUTILS_DEBUG = true;
console.log('Ejecting', disk, '...');
mountUtils.eject(disk, function(error) {
console.log(error || 'OK');
});

View file

@ -1,36 +0,0 @@
#!/usr/bin/env node
/*
* Copyright 2017 resin.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
const mountUtils = require('..');
let argv = process.argv.slice(2);
let disk = argv.shift();
if (!disk) {
console.error(`Usage: node example/unmount <disk>`);
process.exit(1);
}
process.env.MOUNTUTILS_DEBUG = true;
console.log('Unmounting', disk, '...');
mountUtils.unmountDisk(disk, function(error) {
console.log(error || 'OK');
});

View file

@ -1,106 +0,0 @@
/*
* Copyright 2017 resin.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const chai = require('chai');
const mountutils = require('../');
describe('MountUtils', function() {
describe('.unmountDisk()', function() {
it('should be a function', function() {
chai.expect(mountutils.unmountDisk).to.be.a('function');
});
context('missing / wrong arguments', function() {
specify('throws on missing device', function() {
chai.expect(function() {
mountutils.unmountDisk(null, function() {});
}).to.throw(/must be a string/i);
});
specify('throws on missing callback', function() {
chai.expect(function() {
mountutils.unmountDisk('novalue');
}).to.throw(/must be a function/i);
});
});
context('invalid device', function() {
specify('device is a directory', function( done ) {
mountutils.unmountDisk( __dirname, function( error ) {
chai.expect(error.message).to.match(/Unmount failed/);
done();
});
});
specify('device is an empty string', function( done ) {
mountutils.unmountDisk( '', function( error ) {
chai.expect(error.message).to.match(/Unmount failed/);
done();
});
});
});
});
describe('.eject()', function() {
it('should be a function', function() {
chai.expect(mountutils.eject).to.be.a('function');
});
context('missing / wrong arguments', function() {
specify('throws on missing device', function() {
chai.expect(function() {
mountutils.eject(null, function() {});
}).to.throw(/must be a string/i);
});
specify('throws on missing callback', function() {
chai.expect(function() {
mountutils.eject('novalue');
}).to.throw(/must be a function/i);
});
});
context('invalid device', function() {
specify('device is a directory', function( done ) {
mountutils.eject( __dirname, function( error ) {
chai.expect(error.message).to.match(/Eject failed/);
done();
});
});
specify('device is an empty string', function( done ) {
mountutils.eject( '', function( error ) {
chai.expect(error.message).to.match(/Eject failed/);
done();
});
});
});
});
});

View file

@ -178,6 +178,7 @@ bool DownloadThread::_openAndPrepareDevice()
if (!_file.open(QIODevice::ReadWrite | QIODevice::Unbuffered)) if (!_file.open(QIODevice::ReadWrite | QIODevice::Unbuffered))
{ {
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
#ifndef QT_NO_DBUS
/* Opening device directly did not work, ask udisks2 to do it for us, /* Opening device directly did not work, ask udisks2 to do it for us,
* if necessary prompting for authorization */ * if necessary prompting for authorization */
UDisks2Api udisks; UDisks2Api udisks;
@ -187,11 +188,10 @@ bool DownloadThread::_openAndPrepareDevice()
_file.open(fd, QIODevice::ReadWrite | QIODevice::Unbuffered, QFileDevice::AutoCloseHandle); _file.open(fd, QIODevice::ReadWrite | QIODevice::Unbuffered, QFileDevice::AutoCloseHandle);
} }
else else
{
#endif #endif
{
emit error(tr("Cannot open storage device '%1'.").arg(QString(_filename))); emit error(tr("Cannot open storage device '%1'.").arg(QString(_filename)));
return false; return false;
#ifdef Q_OS_LINUX
} }
#endif #endif
} }

View file

@ -134,6 +134,7 @@ void DriveFormatThread::run()
{ {
/* Not running as root, try to outsource formatting to udisks2 */ /* Not running as root, try to outsource formatting to udisks2 */
#ifndef QT_NO_DBUS
UDisks2Api udisks2; UDisks2Api udisks2;
if (udisks2.formatDrive(_device)) if (udisks2.formatDrive(_device))
{ {
@ -141,8 +142,11 @@ void DriveFormatThread::run()
} }
else else
{ {
#endif
emit error(tr("Error formatting (through udisks2)")); emit error(tr("Error formatting (through udisks2)"));
#ifndef QT_NO_DBUS
} }
#endif
return; return;
} }

View file

@ -14,11 +14,19 @@
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QQmlContext> #include <QQmlContext>
#include <QProcess> #include <QProcess>
#include <QFileDialog>
#include <QStandardPaths> #include <QStandardPaths>
#include <QStorageInfo> #include <QStorageInfo>
#include <QWindow> #include <QWindow>
#include <QGuiApplication>
#include <QNetworkInterface>
#include <QHostAddress>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QDateTime>
#include <QDebug> #include <QDebug>
#ifndef QT_NO_WIDGETS
#include <QFileDialog>
#endif
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <windows.h> #include <windows.h>
@ -27,10 +35,19 @@
ImageWriter::ImageWriter(QObject *parent) ImageWriter::ImageWriter(QObject *parent)
: QObject(parent), _repo(QUrl(QString(OSLIST_URL))), _dlnow(0), _verifynow(0), : QObject(parent), _repo(QUrl(QString(OSLIST_URL))), _dlnow(0), _verifynow(0),
_engine(nullptr), _thread(nullptr), _verifyEnabled(false), _cachingEnabled(false) _engine(nullptr), _thread(nullptr), _verifyEnabled(false), _cachingEnabled(false),
_embeddedMode(false), _online(false)
{ {
connect(&_polltimer, SIGNAL(timeout()), SLOT(pollProgress())); connect(&_polltimer, SIGNAL(timeout()), SLOT(pollProgress()));
QString platform = QGuiApplication::platformName();
if (platform == "eglfs" || platform == "wayland" || platform == "linuxfb")
{
_embeddedMode = true;
connect(&_networkchecktimer, SIGNAL(timeout()), SLOT(pollNetwork()));
_networkchecktimer.start(100);
}
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
QProcess *p = new QProcess(this); QProcess *p = new QProcess(this);
p->start("net stop ShellHWDetection"); p->start("net stop ShellHWDetection");
@ -63,7 +80,7 @@ ImageWriter::ImageWriter(QObject *parent)
} }
_settings.beginGroup("caching"); _settings.beginGroup("caching");
_cachingEnabled = _settings.value("enabled", IMAGEWRITER_ENABLE_CACHE_DEFAULT).toBool(); _cachingEnabled = !_embeddedMode && _settings.value("enabled", IMAGEWRITER_ENABLE_CACHE_DEFAULT).toBool();
_cachedFileHash = _settings.value("lastDownloadSHA256").toByteArray(); _cachedFileHash = _settings.value("lastDownloadSHA256").toByteArray();
_cacheFileName = QStandardPaths::writableLocation(QStandardPaths::CacheLocation)+QDir::separator()+"lastdownload.cache"; _cacheFileName = QStandardPaths::writableLocation(QStandardPaths::CacheLocation)+QDir::separator()+"lastdownload.cache";
if (!_cachedFileHash.isEmpty()) if (!_cachedFileHash.isEmpty())
@ -377,6 +394,7 @@ void ImageWriter::onFinalizing()
void ImageWriter::openFileDialog() void ImageWriter::openFileDialog()
{ {
#ifndef QT_NO_WIDGETS
QFileDialog *fd = new QFileDialog(nullptr, tr("Select image"), QFileDialog *fd = new QFileDialog(nullptr, tr("Select image"),
QStandardPaths::writableLocation(QStandardPaths::DownloadLocation), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation),
"Image files (*.img *.zip *.gz *.xz);;All files (*.*)"); "Image files (*.img *.zip *.gz *.xz);;All files (*.*)");
@ -394,6 +412,7 @@ void ImageWriter::openFileDialog()
} }
fd->show(); fd->show();
#endif
} }
void ImageWriter::onFileSelected(QString filename) void ImageWriter::onFileSelected(QString filename)
@ -441,6 +460,66 @@ void ImageWriter::_parseCompressedFile()
qDebug() << "Parsed .zip file containing" << numFiles << "files, uncompressed size:" << _extrLen; qDebug() << "Parsed .zip file containing" << numFiles << "files, uncompressed size:" << _extrLen;
} }
bool ImageWriter::isOnline()
{
return _online || !_embeddedMode;
}
void ImageWriter::pollNetwork()
{
#ifdef Q_OS_LINUX
/* Check if we have an IP-address other than localhost */
QList<QHostAddress> addresses = QNetworkInterface::allAddresses();
foreach (QHostAddress a, addresses)
{
if (!a.isLoopback() && a.scopeId().isEmpty())
{
/* Not a loopback or IPv6 link-local address, so online */
_online = true;
break;
}
}
if (_online)
{
_networkchecktimer.stop();
qDebug() << "Network online. Synchronizing time.";
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), SLOT(onTimeSyncReply(QNetworkReply*)));
manager->head(QNetworkRequest(QUrl(TIME_URL)));
}
#endif
}
void ImageWriter::onTimeSyncReply(QNetworkReply *reply)
{
#ifdef Q_OS_LINUX
if (reply->hasRawHeader("Date"))
{
QDateTime dt = QDateTime::fromString(reply->rawHeader("Date"), Qt::RFC2822Date);
qDebug() << "Received current time from server:" << dt;
struct timeval tv = {
(time_t) dt.toSecsSinceEpoch(), 0
};
::settimeofday(&tv, NULL);
emit networkOnline();
}
else
{
// TODO: try again later?
}
reply->deleteLater();
#endif
}
bool ImageWriter::isEmbeddedMode()
{
return _embeddedMode;
}
void MountUtilsLog(std::string msg) { void MountUtilsLog(std::string msg) {
qDebug() << "mountutils:" << msg.c_str(); qDebug() << "mountutils:" << msg.c_str();
} }

View file

@ -17,6 +17,7 @@
class QQmlApplicationEngine; class QQmlApplicationEngine;
class DownloadThread; class DownloadThread;
class QNetworkReply;
class ImageWriter : public QObject class ImageWriter : public QObject
{ {
@ -71,6 +72,11 @@ public:
/* Return filename part of URL set */ /* Return filename part of URL set */
Q_INVOKABLE QString srcFileName(); Q_INVOKABLE QString srcFileName();
/* Returns true if online */
Q_INVOKABLE bool isOnline();
Q_INVOKABLE bool isEmbeddedMode();
signals: signals:
/* We are emiting signals with QVariant as parameters because QML likes it that way */ /* We are emiting signals with QVariant as parameters because QML likes it that way */
@ -81,16 +87,19 @@ signals:
void fileSelected(QVariant filename); void fileSelected(QVariant filename);
void cancelled(); void cancelled();
void finalizing(); void finalizing();
void networkOnline();
protected slots: protected slots:
void pollProgress(); void pollProgress();
void pollNetwork();
void onSuccess(); void onSuccess();
void onError(QString msg); void onError(QString msg);
void onFileSelected(QString filename); void onFileSelected(QString filename);
void onCancelled(); void onCancelled();
void onCacheFileUpdated(QByteArray sha256); void onCacheFileUpdated(QByteArray sha256);
void onFinalizing(); void onFinalizing();
void onTimeSyncReply(QNetworkReply *reply);
protected: protected:
QUrl _src, _repo; QUrl _src, _repo;
@ -99,10 +108,10 @@ protected:
quint64 _downloadLen, _extrLen, _devLen, _dlnow, _verifynow; quint64 _downloadLen, _extrLen, _devLen, _dlnow, _verifynow;
DriveListModel _drivelist; DriveListModel _drivelist;
QQmlApplicationEngine *_engine; QQmlApplicationEngine *_engine;
QTimer _polltimer; QTimer _polltimer, _networkchecktimer;
PowerSaveBlocker _powersave; PowerSaveBlocker _powersave;
DownloadThread *_thread; DownloadThread *_thread;
bool _verifyEnabled, _multipleFilesInZip, _cachingEnabled; bool _verifyEnabled, _multipleFilesInZip, _cachingEnabled, _embeddedMode, _online;
QSettings _settings; QSettings _settings;
void _parseCompressedFile(); void _parseCompressedFile();

View file

@ -103,6 +103,12 @@ namespace Drivelist
bdev["vendor"].toString().trimmed(), bdev["vendor"].toString().trimmed(),
bdev["model"].toString().trimmed() bdev["model"].toString().trimmed()
}; };
if (name == "/dev/mmcblk0")
{
dp.removeAll("");
if (dp.empty())
dp.append(QObject::tr("Internal SD card reader"));
}
QString mp = bdev["mountpoint"].toString(); QString mp = bdev["mountpoint"].toString();
if (!mp.isEmpty()) if (!mp.isEmpty())

View file

@ -13,11 +13,13 @@
#include "imagewriter.h" #include "imagewriter.h"
#include "drivelistmodel.h" #include "drivelistmodel.h"
#include "networkaccessmanagerfactory.h" #include "networkaccessmanagerfactory.h"
#include <QtWidgets/QApplication>
#include <QMessageLogContext> #include <QMessageLogContext>
#include <QQuickWindow> #include <QQuickWindow>
#include <QTranslator> #include <QTranslator>
#include <QLocale> #include <QLocale>
#ifndef QT_NO_WIDGETS
#include <QtWidgets/QApplication>
#endif
static QTextStream cerr(stderr); static QTextStream cerr(stderr);
@ -29,12 +31,16 @@ static void consoleMsgHandler(QtMsgType, const QMessageLogContext &, const QStri
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
// prefer ANGLE (DirectX) over desktop OpenGL // prefer ANGLE (DirectX) over desktop OpenGL
QApplication::setAttribute(Qt::AA_UseOpenGLES); QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
#endif #endif
#ifdef QT_NO_WIDGETS
QGuiApplication app(argc, argv);
#else
QApplication app(argc, argv); QApplication app(argc, argv);
#endif
app.setOrganizationName("Raspberry Pi"); app.setOrganizationName("Raspberry Pi");
app.setOrganizationDomain("raspberrypi.org"); app.setOrganizationDomain("raspberrypi.org");
app.setApplicationName("Imager"); app.setApplicationName("Imager");
@ -146,6 +152,7 @@ int main(int argc, char *argv[])
qmlwindow->connect(&imageWriter, SIGNAL(fileSelected(QVariant)), qmlwindow, SLOT(onFileSelected(QVariant))); qmlwindow->connect(&imageWriter, SIGNAL(fileSelected(QVariant)), qmlwindow, SLOT(onFileSelected(QVariant)));
qmlwindow->connect(&imageWriter, SIGNAL(cancelled()), qmlwindow, SLOT(onCancelled())); qmlwindow->connect(&imageWriter, SIGNAL(cancelled()), qmlwindow, SLOT(onCancelled()));
qmlwindow->connect(&imageWriter, SIGNAL(finalizing()), qmlwindow, SLOT(onFinalizing())); qmlwindow->connect(&imageWriter, SIGNAL(finalizing()), qmlwindow, SLOT(onFinalizing()));
qmlwindow->connect(&imageWriter, SIGNAL(networkOnline()), qmlwindow, SLOT(fetchOSlist()));
int rc = app.exec(); int rc = app.exec();
return rc; return rc;

View file

@ -36,6 +36,16 @@ ApplicationWindow {
} }
} }
Shortcut {
sequence: StandardKey.Quit
context: Qt.ApplicationShortcut
onActivated: {
if (!progressBar.visible) {
Qt.quit()
}
}
}
ColumnLayout { ColumnLayout {
id: bg id: bg
spacing: 0 spacing: 0
@ -358,17 +368,9 @@ ApplicationWindow {
} }
Component.onCompleted: { Component.onCompleted: {
httpRequest(imageWriter.constantOsListUrl(), function (x) { if (imageWriter.isOnline()) {
var o = JSON.parse(x.responseText) fetchOSlist();
if (!"os_list" in o) { }
onError(qsTr("Error parsing os_list.json"))
return;
}
var oslist = o["os_list"]
for (var i in oslist) {
osmodel.insert(osmodel.count-2, oslist[i])
}
})
} }
} }
@ -525,7 +527,13 @@ ApplicationWindow {
osswipeview.setCurrentIndex(1) osswipeview.setCurrentIndex(1)
} }
} else if (url == "") { } else if (url == "") {
imageWriter.openFileDialog() if (!imageWriter.isEmbeddedMode()) {
imageWriter.openFileDialog()
}
else {
// FIXME: provide QML file dialog
onError("Using custom images is not implemented on this platform yet.")
}
} else { } else {
imageWriter.setSrc(url, image_download_size, extract_size, typeof(extract_sha256) != "undefined" ? extract_sha256 : "", typeof(contains_multiple_files) != "undefined" ? contains_multiple_files : false) imageWriter.setSrc(url, image_download_size, extract_size, typeof(extract_sha256) != "undefined" ? extract_sha256 : "", typeof(contains_multiple_files) != "undefined" ? contains_multiple_files : false)
osbutton.text = name osbutton.text = name
@ -787,7 +795,7 @@ ApplicationWindow {
Material.foreground: "#ffffff" Material.foreground: "#ffffff"
Material.background: "#c51a4a" Material.background: "#c51a4a"
font.family: roboto.name font.family: roboto.name
visible: false visible: imageWriter.isEmbeddedMode()
} }
Button { Button {
@ -795,7 +803,7 @@ ApplicationWindow {
text: qsTr("CONTINUE") text: qsTr("CONTINUE")
onClicked: { onClicked: {
msgpopup.close() msgpopup.close()
quitbutton.visible = false quitbutton.visible = imageWriter.isEmbeddedMode()
} }
Material.foreground: "#ffffff" Material.foreground: "#ffffff"
Material.background: "#c51a4a" Material.background: "#c51a4a"
@ -931,4 +939,18 @@ ApplicationWindow {
function onFinalizing() { function onFinalizing() {
progressText.text = qsTr("Finalizing...") progressText.text = qsTr("Finalizing...")
} }
function fetchOSlist() {
httpRequest(imageWriter.constantOsListUrl(), function (x) {
var o = JSON.parse(x.responseText)
if (!"os_list" in o) {
onError(qsTr("Error parsing os_list.json"))
return;
}
var oslist = o["os_list"]
for (var i in oslist) {
osmodel.insert(osmodel.count-2, oslist[i])
}
})
}
} }