From 5b072f319620b7c490757f4f012e274ef6a3ea4a Mon Sep 17 00:00:00 2001 From: Floris Bos Date: Mon, 25 May 2020 00:36:16 +0200 Subject: [PATCH] Build changes - Add support for embedded Linux without X, dbus, udisks, ntp, etc. - Misc minor changes --- CMakeLists.txt | 49 ++++---- acceleratedcryptographichash.h | 6 + config.h | 3 + dependencies/mountutils/example/eject.js | 36 ------ dependencies/mountutils/example/unmount.js | 36 ------ .../mountutils/tests/mountutils.spec.js | 106 ------------------ downloadthread.cpp | 4 +- driveformatthread.cpp | 4 + imagewriter.cpp | 85 +++++++++++++- imagewriter.h | 13 ++- linux/linuxdrivelist.cpp | 6 + main.cpp | 13 ++- main.qml | 50 ++++++--- 13 files changed, 189 insertions(+), 222 deletions(-) delete mode 100644 dependencies/mountutils/example/eject.js delete mode 100644 dependencies/mountutils/example/unmount.js delete mode 100644 dependencies/mountutils/tests/mountutils.spec.js diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ef55ec..399943e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,21 +20,29 @@ set(CMAKE_AUTORCC ON) # 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 - 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) -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 - dependencies/drivelist/src/darwin/list.mm dependencies/drivelist/src/darwin/REDiskList.m icons/rpi-imager.icns) -enable_language(OBJC C) + 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 + dependencies/drivelist/src/darwin/list.mm dependencies/drivelist/src/darwin/REDiskList.m icons/rpi-imager.icns) + enable_language(OBJC C) 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) -set(DEPENDENCIES dependencies/mountutils/src/windows/functions.cpp dependencies/drivelist/src/windows/list.cpp - windows/winfile.cpp windows/winfile.h - windows/rpi-imager.rc) -set(EXTRALIBS setupapi) + set(DEPENDENCIES acceleratedcryptographichash.cpp dependencies/mountutils/src/windows/functions.cpp dependencies/drivelist/src/windows/list.cpp + windows/winfile.cpp windows/winfile.h + windows/rpi-imager.rc) + set(EXTRALIBS setupapi) endif() include_directories(BEFORE .) @@ -60,10 +68,12 @@ endif() set(SOURCES "main.cpp" "imagewriter.cpp" "networkaccessmanagerfactory.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 Widgets LinguistTools REQUIRED) +find_package(Qt5 COMPONENTS Core Quick LinguistTools Svg OPTIONAL_COMPONENTS Widgets) +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_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) add_definitions(-DWINVER=0x0601 -D_WIN32_WINNT=0x0601) + find_package(OpenSSL REQUIRED) + # Bundled zlib add_subdirectory(dependencies/zlib-1.2.11) set(ZLIB_LIBRARY zlibstatic) @@ -195,9 +207,7 @@ elseif(APPLE) find_library(CoreFoundation CoreFoundation) find_library(DiskArbitration DiskArbitration) find_library(Security Security) - #find_package(Qt5 COMPONENTS Svg) - #set(EXTRALIBS ${CoreFoundation} ${DiskArbitration} ${Security} ${Cocoa} Qt5::Svg) - set(EXTRALIBS ${CoreFoundation} ${DiskArbitration} ${Security} ${Cocoa}) + set(EXTRALIBS ${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) find_program(MACDEPLOYQT "macdeployqt" PATHS "${Qt5_DIR}/../../../bin") @@ -212,8 +222,7 @@ elseif(APPLE) else() find_package(CURL REQUIRED 7.32.0) find_package(LibArchive REQUIRED 3.2.0) - find_package(Qt5 COMPONENTS DBus) - set(EXTRALIBS Qt5::DBus) + find_package(OpenSSL REQUIRED) if (NOT CMAKE_CROSSCOMPILING) find_program(LSBLK "lsblk") if (NOT LSBLK) @@ -232,4 +241,4 @@ else() endif() 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}) diff --git a/acceleratedcryptographichash.h b/acceleratedcryptographichash.h index 4e87227..598dfa6 100644 --- a/acceleratedcryptographichash.h +++ b/acceleratedcryptographichash.h @@ -7,6 +7,11 @@ */ #include + +#ifdef Q_OS_DARWIN +typedef QCryptographicHash AcceleratedCryptographicHash; +#else + #include "openssl/sha.h" class AcceleratedCryptographicHash @@ -22,4 +27,5 @@ protected: SHA256_CTX _sha256; }; +#endif #endif // ACCELERATEDCRYPTOGRAPHICHASH_H diff --git a/config.h b/config.h index b81922c..a068cfa 100644 --- a/config.h +++ b/config.h @@ -10,6 +10,9 @@ /* Repository URL */ #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 */ #define OSLIST_HASH_ALGORITHM QCryptographicHash::Sha256 diff --git a/dependencies/mountutils/example/eject.js b/dependencies/mountutils/example/eject.js deleted file mode 100644 index f74bec7..0000000 --- a/dependencies/mountutils/example/eject.js +++ /dev/null @@ -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 `); - process.exit(1); -} - -process.env.MOUNTUTILS_DEBUG = true; - -console.log('Ejecting', disk, '...'); - -mountUtils.eject(disk, function(error) { - console.log(error || 'OK'); -}); diff --git a/dependencies/mountutils/example/unmount.js b/dependencies/mountutils/example/unmount.js deleted file mode 100644 index 2f7027f..0000000 --- a/dependencies/mountutils/example/unmount.js +++ /dev/null @@ -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 `); - process.exit(1); -} - -process.env.MOUNTUTILS_DEBUG = true; - -console.log('Unmounting', disk, '...'); - -mountUtils.unmountDisk(disk, function(error) { - console.log(error || 'OK'); -}); diff --git a/dependencies/mountutils/tests/mountutils.spec.js b/dependencies/mountutils/tests/mountutils.spec.js deleted file mode 100644 index 9ca71f2..0000000 --- a/dependencies/mountutils/tests/mountutils.spec.js +++ /dev/null @@ -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(); - }); - }); - - }); - - }); - -}); diff --git a/downloadthread.cpp b/downloadthread.cpp index 2518a36..d8d6c26 100644 --- a/downloadthread.cpp +++ b/downloadthread.cpp @@ -178,6 +178,7 @@ bool DownloadThread::_openAndPrepareDevice() if (!_file.open(QIODevice::ReadWrite | QIODevice::Unbuffered)) { #ifdef Q_OS_LINUX +#ifndef QT_NO_DBUS /* Opening device directly did not work, ask udisks2 to do it for us, * if necessary prompting for authorization */ UDisks2Api udisks; @@ -187,11 +188,10 @@ bool DownloadThread::_openAndPrepareDevice() _file.open(fd, QIODevice::ReadWrite | QIODevice::Unbuffered, QFileDevice::AutoCloseHandle); } else - { #endif + { emit error(tr("Cannot open storage device '%1'.").arg(QString(_filename))); return false; -#ifdef Q_OS_LINUX } #endif } diff --git a/driveformatthread.cpp b/driveformatthread.cpp index 48ed680..49a220b 100644 --- a/driveformatthread.cpp +++ b/driveformatthread.cpp @@ -134,6 +134,7 @@ void DriveFormatThread::run() { /* Not running as root, try to outsource formatting to udisks2 */ +#ifndef QT_NO_DBUS UDisks2Api udisks2; if (udisks2.formatDrive(_device)) { @@ -141,8 +142,11 @@ void DriveFormatThread::run() } else { +#endif emit error(tr("Error formatting (through udisks2)")); +#ifndef QT_NO_DBUS } +#endif return; } diff --git a/imagewriter.cpp b/imagewriter.cpp index ec98571..69d955c 100644 --- a/imagewriter.cpp +++ b/imagewriter.cpp @@ -14,11 +14,19 @@ #include #include #include -#include #include #include #include +#include +#include +#include +#include +#include +#include #include +#ifndef QT_NO_WIDGETS +#include +#endif #ifdef Q_OS_WIN #include @@ -27,10 +35,19 @@ ImageWriter::ImageWriter(QObject *parent) : 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())); + 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 QProcess *p = new QProcess(this); p->start("net stop ShellHWDetection"); @@ -63,7 +80,7 @@ ImageWriter::ImageWriter(QObject *parent) } _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(); _cacheFileName = QStandardPaths::writableLocation(QStandardPaths::CacheLocation)+QDir::separator()+"lastdownload.cache"; if (!_cachedFileHash.isEmpty()) @@ -377,6 +394,7 @@ void ImageWriter::onFinalizing() void ImageWriter::openFileDialog() { +#ifndef QT_NO_WIDGETS QFileDialog *fd = new QFileDialog(nullptr, tr("Select image"), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation), "Image files (*.img *.zip *.gz *.xz);;All files (*.*)"); @@ -394,6 +412,7 @@ void ImageWriter::openFileDialog() } fd->show(); +#endif } void ImageWriter::onFileSelected(QString filename) @@ -441,6 +460,66 @@ void ImageWriter::_parseCompressedFile() 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 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) { qDebug() << "mountutils:" << msg.c_str(); } diff --git a/imagewriter.h b/imagewriter.h index f56e068..2d23a30 100644 --- a/imagewriter.h +++ b/imagewriter.h @@ -17,6 +17,7 @@ class QQmlApplicationEngine; class DownloadThread; +class QNetworkReply; class ImageWriter : public QObject { @@ -71,6 +72,11 @@ public: /* Return filename part of URL set */ Q_INVOKABLE QString srcFileName(); + /* Returns true if online */ + Q_INVOKABLE bool isOnline(); + + Q_INVOKABLE bool isEmbeddedMode(); + signals: /* We are emiting signals with QVariant as parameters because QML likes it that way */ @@ -81,16 +87,19 @@ signals: void fileSelected(QVariant filename); void cancelled(); void finalizing(); + void networkOnline(); protected slots: void pollProgress(); + void pollNetwork(); void onSuccess(); void onError(QString msg); void onFileSelected(QString filename); void onCancelled(); void onCacheFileUpdated(QByteArray sha256); void onFinalizing(); + void onTimeSyncReply(QNetworkReply *reply); protected: QUrl _src, _repo; @@ -99,10 +108,10 @@ protected: quint64 _downloadLen, _extrLen, _devLen, _dlnow, _verifynow; DriveListModel _drivelist; QQmlApplicationEngine *_engine; - QTimer _polltimer; + QTimer _polltimer, _networkchecktimer; PowerSaveBlocker _powersave; DownloadThread *_thread; - bool _verifyEnabled, _multipleFilesInZip, _cachingEnabled; + bool _verifyEnabled, _multipleFilesInZip, _cachingEnabled, _embeddedMode, _online; QSettings _settings; void _parseCompressedFile(); diff --git a/linux/linuxdrivelist.cpp b/linux/linuxdrivelist.cpp index 5ac5be9..77ab43d 100644 --- a/linux/linuxdrivelist.cpp +++ b/linux/linuxdrivelist.cpp @@ -103,6 +103,12 @@ namespace Drivelist bdev["vendor"].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(); if (!mp.isEmpty()) diff --git a/main.cpp b/main.cpp index 045087f..0e38780 100644 --- a/main.cpp +++ b/main.cpp @@ -13,11 +13,13 @@ #include "imagewriter.h" #include "drivelistmodel.h" #include "networkaccessmanagerfactory.h" -#include #include #include #include #include +#ifndef QT_NO_WIDGETS +#include +#endif static QTextStream cerr(stderr); @@ -29,12 +31,16 @@ static void consoleMsgHandler(QtMsgType, const QMessageLogContext &, const QStri int main(int argc, char *argv[]) { - QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #ifdef Q_OS_WIN // prefer ANGLE (DirectX) over desktop OpenGL - QApplication::setAttribute(Qt::AA_UseOpenGLES); + QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); #endif +#ifdef QT_NO_WIDGETS + QGuiApplication app(argc, argv); +#else QApplication app(argc, argv); +#endif app.setOrganizationName("Raspberry Pi"); app.setOrganizationDomain("raspberrypi.org"); 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(cancelled()), qmlwindow, SLOT(onCancelled())); qmlwindow->connect(&imageWriter, SIGNAL(finalizing()), qmlwindow, SLOT(onFinalizing())); + qmlwindow->connect(&imageWriter, SIGNAL(networkOnline()), qmlwindow, SLOT(fetchOSlist())); int rc = app.exec(); return rc; diff --git a/main.qml b/main.qml index 19847ec..bb4abf5 100644 --- a/main.qml +++ b/main.qml @@ -36,6 +36,16 @@ ApplicationWindow { } } + Shortcut { + sequence: StandardKey.Quit + context: Qt.ApplicationShortcut + onActivated: { + if (!progressBar.visible) { + Qt.quit() + } + } + } + ColumnLayout { id: bg spacing: 0 @@ -358,17 +368,9 @@ ApplicationWindow { } Component.onCompleted: { - 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]) - } - }) + if (imageWriter.isOnline()) { + fetchOSlist(); + } } } @@ -525,7 +527,13 @@ ApplicationWindow { osswipeview.setCurrentIndex(1) } } 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 { 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 @@ -787,7 +795,7 @@ ApplicationWindow { Material.foreground: "#ffffff" Material.background: "#c51a4a" font.family: roboto.name - visible: false + visible: imageWriter.isEmbeddedMode() } Button { @@ -795,7 +803,7 @@ ApplicationWindow { text: qsTr("CONTINUE") onClicked: { msgpopup.close() - quitbutton.visible = false + quitbutton.visible = imageWriter.isEmbeddedMode() } Material.foreground: "#ffffff" Material.background: "#c51a4a" @@ -931,4 +939,18 @@ ApplicationWindow { function onFinalizing() { 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]) + } + }) + } }