diff --git a/CMakeLists.txt b/CMakeLists.txt
index 399943e..cf2dfd0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -240,5 +240,12 @@ else()
install(FILES linux/rpi-imager.desktop DESTINATION share/applications)
endif()
+get_target_property(QT_TARGET_TYPE Qt5::Core TYPE)
+if(${QT_TARGET_TYPE} STREQUAL "STATIC_LIBRARY")
+ find_package(Qt5QmlImportScanner REQUIRED)
+ qt5_import_qml_plugins(${PROJECT_NAME})
+ qt5_import_plugins(${PROJECT_NAME} INCLUDE Qt5::QSvgPlugin)
+endif()
+
include_directories(${CURL_INCLUDE_DIR} ${LibArchive_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR})
-target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Quick Qt5::Svg ${CURL_LIBRARIES} ${LibArchive_LIBRARIES} ${OPENSSL_LIBRARIES} ${ATOMIC_LIBRARY} ${EXTRALIBS})
+target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Quick Qt5::Svg ${CURL_LIBRARIES} ${LibArchive_LIBRARIES} ${OPENSSL_LIBRARIES} ${ATOMIC_LIBRARY} ${EXTRALIBS})
diff --git a/driveformatthread.cpp b/driveformatthread.cpp
index 49a220b..c83113a 100644
--- a/driveformatthread.cpp
+++ b/driveformatthread.cpp
@@ -186,7 +186,7 @@ void DriveFormatThread::run()
return;
}
- proc.execute("partprobe");
+ proc.execute("partprobe", QStringList() );
args.clear();
args << fatpartition;
diff --git a/icons/ic_build_40px.png b/icons/ic_build_40px.png
deleted file mode 100644
index 4790c36..0000000
Binary files a/icons/ic_build_40px.png and /dev/null differ
diff --git a/icons/ic_build_40px.svg b/icons/ic_build_40px.svg
new file mode 100644
index 0000000..e414923
--- /dev/null
+++ b/icons/ic_build_40px.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/icons/ic_chevron_left_40px.png b/icons/ic_chevron_left_40px.png
deleted file mode 100644
index 4d3f16c..0000000
Binary files a/icons/ic_chevron_left_40px.png and /dev/null differ
diff --git a/icons/ic_chevron_left_40px.svg b/icons/ic_chevron_left_40px.svg
new file mode 100644
index 0000000..c6122c7
--- /dev/null
+++ b/icons/ic_chevron_left_40px.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/icons/ic_chevron_right_40px.png b/icons/ic_chevron_right_40px.png
deleted file mode 100644
index 2b8b449..0000000
Binary files a/icons/ic_chevron_right_40px.png and /dev/null differ
diff --git a/icons/ic_chevron_right_40px.svg b/icons/ic_chevron_right_40px.svg
new file mode 100644
index 0000000..b8996ba
--- /dev/null
+++ b/icons/ic_chevron_right_40px.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/icons/ic_close_18px.png b/icons/ic_close_18px.png
deleted file mode 100644
index 6825159..0000000
Binary files a/icons/ic_close_18px.png and /dev/null differ
diff --git a/icons/ic_computer_40px.png b/icons/ic_computer_40px.png
deleted file mode 100644
index 5cf5172..0000000
Binary files a/icons/ic_computer_40px.png and /dev/null differ
diff --git a/icons/ic_computer_40px.svg b/icons/ic_computer_40px.svg
new file mode 100644
index 0000000..e92b53c
--- /dev/null
+++ b/icons/ic_computer_40px.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/icons/ic_delete_40px.png b/icons/ic_delete_40px.png
deleted file mode 100644
index 699a10e..0000000
Binary files a/icons/ic_delete_40px.png and /dev/null differ
diff --git a/icons/ic_delete_40px.svg b/icons/ic_delete_40px.svg
new file mode 100644
index 0000000..c2bcbf3
--- /dev/null
+++ b/icons/ic_delete_40px.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/icons/ic_sd_storage_40px.png b/icons/ic_sd_storage_40px.png
deleted file mode 100644
index c0bea0a..0000000
Binary files a/icons/ic_sd_storage_40px.png and /dev/null differ
diff --git a/icons/ic_sd_storage_40px.svg b/icons/ic_sd_storage_40px.svg
new file mode 100644
index 0000000..ef75ae8
--- /dev/null
+++ b/icons/ic_sd_storage_40px.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/icons/ic_storage_40px.png b/icons/ic_storage_40px.png
deleted file mode 100644
index f5ce8d7..0000000
Binary files a/icons/ic_storage_40px.png and /dev/null differ
diff --git a/icons/ic_storage_40px.svg b/icons/ic_storage_40px.svg
new file mode 100644
index 0000000..22f5fc3
--- /dev/null
+++ b/icons/ic_storage_40px.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/icons/ic_usb_40px.png b/icons/ic_usb_40px.png
deleted file mode 100644
index 667e9fd..0000000
Binary files a/icons/ic_usb_40px.png and /dev/null differ
diff --git a/icons/ic_usb_40px.svg b/icons/ic_usb_40px.svg
new file mode 100644
index 0000000..21567e5
--- /dev/null
+++ b/icons/ic_usb_40px.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/icons/rpi2-hires.png b/icons/rpi2-hires.png
new file mode 100644
index 0000000..e997a61
Binary files /dev/null and b/icons/rpi2-hires.png differ
diff --git a/imagewriter.cpp b/imagewriter.cpp
index 69d955c..e0d6e40 100644
--- a/imagewriter.cpp
+++ b/imagewriter.cpp
@@ -476,6 +476,7 @@ void ImageWriter::pollNetwork()
if (!a.isLoopback() && a.scopeId().isEmpty())
{
/* Not a loopback or IPv6 link-local address, so online */
+ qDebug() << "IP:" << a;
_online = true;
break;
}
@@ -484,14 +485,23 @@ void ImageWriter::pollNetwork()
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)));
+
+ // Wait another 0.1 sec, as dhcpcd may not have set up nameservers yet
+ QTimer::singleShot(100, this, SLOT(syncTime()));
}
#endif
}
+void ImageWriter::syncTime()
+{
+#ifdef Q_OS_LINUX
+ 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
@@ -508,7 +518,8 @@ void ImageWriter::onTimeSyncReply(QNetworkReply *reply)
}
else
{
- // TODO: try again later?
+ qDebug() << "Error synchronizing time. Trying again in 3 seconds";
+ QTimer::singleShot(3000, this, SLOT(syncTime()));
}
reply->deleteLater();
@@ -520,6 +531,76 @@ bool ImageWriter::isEmbeddedMode()
return _embeddedMode;
}
+/* Mount any USB sticks that can contain source images under /media */
+bool ImageWriter::mountUsbSourceMedia()
+{
+ int devices = 0;
+#ifdef Q_OS_LINUX
+ QDir dir("/sys/class/block");
+ QStringList list = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+
+ if (!dir.exists("/media"))
+ dir.mkdir("/media");
+
+ for (auto devname : list)
+ {
+ if (!devname.startsWith("mmcblk0") && !QFile::symLinkTarget("/sys/class/block/"+devname).contains("/devices/virtual/"))
+ {
+ QString mntdir = "/media/"+devname;
+
+ if (dir.exists(mntdir))
+ {
+ devices++;
+ continue;
+ }
+
+ dir.mkdir(mntdir);
+ QStringList args = { "-o", "ro", QString("/dev/")+devname, mntdir };
+
+ if ( QProcess::execute("mount", args) == 0 )
+ devices++;
+ else
+ dir.rmdir(mntdir);
+ }
+ }
+#endif
+ return devices > 0;
+}
+
+QByteArray ImageWriter::getUsbSourceOSlist()
+{
+#ifdef Q_OS_LINUX
+ QJsonArray oslist;
+ QDir dir("/media");
+ QStringList medialist = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ QStringList namefilters = {"*.img", "*.zip", "*.gz", "*.xz"};
+
+ for (auto devname : medialist)
+ {
+ QDir subdir("/media/"+devname);
+ QStringList files = subdir.entryList(namefilters, QDir::Files, QDir::Name);
+ for (auto file : files)
+ {
+ QString path = "/media/"+devname+"/"+file;
+ QFileInfo fi(path);
+
+ QJsonObject f = {
+ {"name", file},
+ {"description", devname+"/"+file},
+ {"url", QUrl::fromLocalFile(path).toString() },
+ {"release_date", ""},
+ {"image_download_size", fi.size()}
+ };
+ oslist.append(f);
+ }
+ }
+
+ return QJsonDocument(oslist).toJson();
+#else
+ return QByteArray();
+#endif
+}
+
void MountUtilsLog(std::string msg) {
qDebug() << "mountutils:" << msg.c_str();
}
diff --git a/imagewriter.h b/imagewriter.h
index 2d23a30..a6edfa8 100644
--- a/imagewriter.h
+++ b/imagewriter.h
@@ -75,8 +75,16 @@ public:
/* Returns true if online */
Q_INVOKABLE bool isOnline();
+ /* Returns true if run on embedded Linux platform */
Q_INVOKABLE bool isEmbeddedMode();
+ /* Mount any USB sticks that can contain source images under /media
+ Returns true if at least one device was mounted */
+ Q_INVOKABLE bool mountUsbSourceMedia();
+
+ /* Returns a json formatted list of the OS images found on USB stick */
+ Q_INVOKABLE QByteArray getUsbSourceOSlist();
+
signals:
/* We are emiting signals with QVariant as parameters because QML likes it that way */
@@ -93,6 +101,7 @@ protected slots:
void pollProgress();
void pollNetwork();
+ void syncTime();
void onSuccess();
void onError(QString msg);
void onFileSelected(QString filename);
diff --git a/linux/linuxdrivelist.cpp b/linux/linuxdrivelist.cpp
index 77ab43d..c375ac4 100644
--- a/linux/linuxdrivelist.cpp
+++ b/linux/linuxdrivelist.cpp
@@ -48,7 +48,8 @@ namespace Drivelist
std::vector deviceList;
QProcess p;
- p.start("lsblk --bytes --json --paths --output-all");
+ QStringList args = { "--bytes", "--json", "--paths", "--output-all" };
+ p.start("lsblk", args);
p.waitForFinished(2000);
QByteArray output = p.readAll();
diff --git a/main.cpp b/main.cpp
index 0e38780..0af33c6 100644
--- a/main.cpp
+++ b/main.cpp
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
#ifndef QT_NO_WIDGETS
#include
#endif
@@ -37,6 +38,15 @@ int main(int argc, char *argv[])
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
#endif
#ifdef QT_NO_WIDGETS
+ {
+ QGuiApplication tmp(argc, argv);
+ int h = QGuiApplication::primaryScreen()->geometry().height();
+ if (h > 720)
+ {
+ qputenv("QT_SCALE_FACTOR", QByteArray::number(h / 720.0, 'f', 2));
+ }
+ }
+
QGuiApplication app(argc, argv);
#else
QApplication app(argc, argv);
diff --git a/main.qml b/main.qml
index bb4abf5..d49db15 100644
--- a/main.qml
+++ b/main.qml
@@ -13,12 +13,13 @@ import Qt.labs.settings 1.0
ApplicationWindow {
id: window
visible: true
- width: 680
- height: 420
- minimumWidth: 680
- maximumWidth: 680
- minimumHeight: 420
- maximumHeight: 420
+
+ width: imageWriter.isEmbeddedMode() ? -1 : 680
+ height: imageWriter.isEmbeddedMode() ? -1 : 420
+ minimumWidth: imageWriter.isEmbeddedMode() ? -1 : 680
+ maximumWidth: imageWriter.isEmbeddedMode() ? -1 : 680
+ minimumHeight: imageWriter.isEmbeddedMode() ? -1 : 420
+ maximumHeight: imageWriter.isEmbeddedMode() ? -1 : 420
title: qsTr("Raspberry Pi Imager v%1").arg(imageWriter.constantVersion())
@@ -50,13 +51,18 @@ ApplicationWindow {
id: bg
spacing: 0
- Image {
- id: image
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
- Layout.preferredWidth: window.width
- fillMode: Image.PreserveAspectFit
- source: "icons/rpi2.png"
+ Rectangle {
+ implicitHeight: window.height/2
+
+ Image {
+ id: image
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
+ fillMode: Image.PreserveAspectFit
+ source: imageWriter.isEmbeddedMode() ? "icons/rpi2-hires.png" : "icons/rpi2.png"
+ width: window.width
+ height: window.height/2
+ }
}
Rectangle {
@@ -347,7 +353,7 @@ ApplicationWindow {
ListElement {
url: "internal://format"
- icon: "icons/ic_delete_40px.png"
+ icon: "icons/ic_delete_40px.svg"
extract_size: 0
image_download_size: 0
extract_sha256: ""
@@ -362,7 +368,7 @@ ApplicationWindow {
ListElement {
url: ""
- icon: "icons/ic_computer_40px.png"
+ icon: "icons/ic_computer_40px.svg"
name: qsTr("Use custom")
description: qsTr("Select a custom .img from your computer")
}
@@ -379,7 +385,7 @@ ApplicationWindow {
ListElement {
url: ""
- icon: "icons/ic_chevron_left_40px.png"
+ icon: "icons/ic_chevron_left_40px.svg"
extract_size: 0
image_download_size: 0
extract_sha256: ""
@@ -421,7 +427,7 @@ ApplicationWindow {
width: 64
Image {
- source: icon == "icons/ic_build_48px.svg" ? "icons/ic_build_40px.png": icon
+ source: icon == "icons/ic_build_48px.svg" ? "icons/ic_build_40px.svg": icon
verticalAlignment: Image.AlignVCenter
height: parent.parent.parent.height
fillMode: Image.Pad
@@ -447,6 +453,8 @@ ApplicationWindow {
if (typeof(url) == "string" && url != "" && url != "internal://format") {
if (typeof(extract_sha256) != "undefined" && imageWriter.isCached(url,extract_sha256)) {
txt += "
"+qsTr("Cached on your computer")
+ } else if (url.startsWith("file://")) {
+ txt += "
"+qsTr("Local file")
} else {
txt += "
"+qsTr("Online - %1 GB download").arg((image_download_size/1073741824).toFixed(1));
}
@@ -467,7 +475,7 @@ ApplicationWindow {
}
Column {
Image {
- source: "icons/ic_chevron_right_40px.png"
+ source: "icons/ic_chevron_right_40px.svg"
visible: (typeof(subitems) == "object" && subitems.count) || (typeof(subitems_url) == "string" && subitems_url != "" && subitems_url != "internal://back")
height: parent.parent.parent.height
fillMode: Image.Pad
@@ -531,8 +539,22 @@ ApplicationWindow {
imageWriter.openFileDialog()
}
else {
- // FIXME: provide QML file dialog
- onError("Using custom images is not implemented on this platform yet.")
+ if (imageWriter.mountUsbSourceMedia()) {
+ if (subosmodel.count>1)
+ {
+ subosmodel.remove(1, subosmodel.count-1)
+ }
+
+ var oslist = JSON.parse(imageWriter.getUsbSourceOSlist())
+ for (var i in oslist) {
+ subosmodel.append(oslist[i])
+ }
+ osswipeview.setCurrentIndex(1)
+ }
+ else
+ {
+ onError(qsTr("Connect an USB stick containing images first.
The images must be located in the root folder of the USB stick."))
+ }
}
} else {
imageWriter.setSrc(url, image_download_size, extract_size, typeof(extract_sha256) != "undefined" ? extract_sha256 : "", typeof(contains_multiple_files) != "undefined" ? contains_multiple_files : false)
@@ -547,7 +569,6 @@ ApplicationWindow {
}
}
-
/*
Popup for SD card device selection
*/
@@ -658,7 +679,7 @@ ApplicationWindow {
width: 64
Image {
- source: isUsb ? "icons/ic_usb_40px.png" : isScsi ? "icons/ic_storage_40px.png" : "icons/ic_sd_storage_40px.png"
+ source: isUsb ? "icons/ic_usb_40px.svg" : isScsi ? "icons/ic_storage_40px.svg" : "icons/ic_sd_storage_40px.svg"
verticalAlignment: Image.AlignVCenter
height: parent.parent.parent.height
fillMode: Image.Pad
diff --git a/qml.qrc b/qml.qrc
index 4c80f1a..0648227 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -7,14 +7,14 @@
fonts/Roboto-Bold.ttf
fonts/Roboto-Light.ttf
fonts/Roboto-Regular.ttf
- icons/ic_chevron_right_40px.png
- icons/ic_chevron_left_40px.png
- icons/ic_computer_40px.png
- icons/ic_delete_40px.png
- icons/ic_usb_40px.png
- icons/ic_storage_40px.png
- icons/ic_sd_storage_40px.png
- icons/ic_build_40px.png
- icons/ic_close_18px.png
+ icons/rpi2-hires.png
+ icons/ic_build_40px.svg
+ icons/ic_chevron_left_40px.svg
+ icons/ic_chevron_right_40px.svg
+ icons/ic_computer_40px.svg
+ icons/ic_delete_40px.svg
+ icons/ic_sd_storage_40px.svg
+ icons/ic_storage_40px.svg
+ icons/ic_usb_40px.svg