First attempt at cloudinit support

Experimental
This commit is contained in:
Floris Bos 2021-11-18 20:48:24 +01:00
parent 2e5cc7508a
commit 8f9fbcffeb
8 changed files with 199 additions and 32 deletions

View file

@ -23,6 +23,10 @@ Popup {
property string config property string config
property string cmdline property string cmdline
property string firstrun property string firstrun
property string cloudinit
property string cloudinitrun
property string cloudinitwrite
property string cloudinitnetwork
// background of title // background of title
Rectangle { Rectangle {
@ -496,12 +500,29 @@ Popup {
function escapeshellarg(arg) { function escapeshellarg(arg) {
return "'"+arg.replace(/'/g, "\\'")+"'" return "'"+arg.replace(/'/g, "\\'")+"'"
} }
function addCloudInit(s) {
cloudinit += s+"\n"
}
function addCloudInitWriteFile(name, content, perms) {
cloudinitwrite += "- encoding: b64\n"
cloudinitwrite += " content: "+Qt.btoa(content)+"\n"
cloudinitwrite += " owner: root:root\n"
cloudinitwrite += " path: "+name+"\n"
cloudinitwrite += " permissions: '"+perms+"'\n"
}
function addCloudInitRun(cmd) {
cloudinitrun += "- "+cmd+"\n"
}
function applySettings() function applySettings()
{ {
cmdline = "" cmdline = ""
config = "" config = ""
firstrun = "" firstrun = ""
cloudinit = ""
cloudinitrun = ""
cloudinitwrite = ""
cloudinitnetwork = ""
if (chkOverscan.checked) { if (chkOverscan.checked) {
addConfig("disable_overscan=1") addConfig("disable_overscan=1")
@ -510,15 +531,31 @@ Popup {
addFirstRun("CURRENT_HOSTNAME=`cat /etc/hostname | tr -d \" \\t\\n\\r\"`") addFirstRun("CURRENT_HOSTNAME=`cat /etc/hostname | tr -d \" \\t\\n\\r\"`")
addFirstRun("echo "+fieldHostname.text+" >/etc/hostname") addFirstRun("echo "+fieldHostname.text+" >/etc/hostname")
addFirstRun("sed -i \"s/127.0.1.1.*$CURRENT_HOSTNAME/127.0.1.1\\t"+fieldHostname.text+"/g\" /etc/hosts") addFirstRun("sed -i \"s/127.0.1.1.*$CURRENT_HOSTNAME/127.0.1.1\\t"+fieldHostname.text+"/g\" /etc/hosts")
addCloudInit("hostname: "+fieldHostname.text)
addCloudInit("manage_etc_hosts: true")
addCloudInit("packages:")
addCloudInit("- avahi-daemon")
addCloudInit("")
} }
if (chkSSH.checked) { if (chkSSH.checked) {
// First user may not be called 'pi' on all distributions, so look username up // First user may not be called 'pi' on all distributions, so look username up
addFirstRun("FIRSTUSER=`getent passwd 1000 | cut -d: -f1`"); addFirstRun("FIRSTUSER=`getent passwd 1000 | cut -d: -f1`");
addFirstRun("FIRSTUSERHOME=`getent passwd 1000 | cut -d: -f6`") addFirstRun("FIRSTUSERHOME=`getent passwd 1000 | cut -d: -f6`")
addCloudInit("users:")
addCloudInit("- name: pi")
addCloudInit(" groups: users,adm,dialout,audio,netdev,video,plugdev,sudo")
addCloudInit(" shell: /bin/bash")
if (radioPasswordAuthentication.checked) { if (radioPasswordAuthentication.checked) {
var cryptedPassword = fieldUserPassword.alreadyCrypted ? fieldUserPassword.text : imageWriter.crypt(fieldUserPassword.text) var cryptedPassword = fieldUserPassword.alreadyCrypted ? fieldUserPassword.text : imageWriter.crypt(fieldUserPassword.text)
addFirstRun("echo \"$FIRSTUSER:\""+escapeshellarg(cryptedPassword)+" | chpasswd -e") addFirstRun("echo \"$FIRSTUSER:\""+escapeshellarg(cryptedPassword)+" | chpasswd -e")
addCloudInit(" lock_passwd: false")
addCloudInit(" passwd: "+cryptedPassword)
addCloudInit("")
addCloudInit("ssh_pwauth: true")
} }
if (radioPubKeyAuthentication.checked) { if (radioPubKeyAuthentication.checked) {
var pubkey = fieldPublicKey.text.replace(/\n/g, "") var pubkey = fieldPublicKey.text.replace(/\n/g, "")
@ -527,8 +564,14 @@ Popup {
addFirstRun("install -o \"$FIRSTUSER\" -m 600 <(echo \""+pubkey+"\") \"$FIRSTUSERHOME/.ssh/authorized_keys\"") addFirstRun("install -o \"$FIRSTUSER\" -m 600 <(echo \""+pubkey+"\") \"$FIRSTUSERHOME/.ssh/authorized_keys\"")
} }
addFirstRun("echo 'PasswordAuthentication no' >>/etc/ssh/sshd_config") addFirstRun("echo 'PasswordAuthentication no' >>/etc/ssh/sshd_config")
addCloudInit(" lock_passwd: true")
addCloudInit(" ssh_authorized_keys:")
addCloudInit(" - "+pubkey)
addCloudInit(" sudo: ALL=(ALL) NOPASSWD:ALL")
} }
addFirstRun("systemctl enable ssh") addFirstRun("systemctl enable ssh")
addCloudInit("")
} }
if (chkWifi.checked) { if (chkWifi.checked) {
var wpaconfig = "country="+fieldWifiCountry.editText+"\n" var wpaconfig = "country="+fieldWifiCountry.editText+"\n"
@ -549,22 +592,39 @@ Popup {
addFirstRun("for filename in /var/lib/systemd/rfkill/*:wlan ; do") addFirstRun("for filename in /var/lib/systemd/rfkill/*:wlan ; do")
addFirstRun(" echo 0 > $filename") addFirstRun(" echo 0 > $filename")
addFirstRun("done") addFirstRun("done")
cloudinitnetwork = "version: 2\n"
cloudinitnetwork += "wifis:\n"
cloudinitnetwork += " renderer: networkd\n"
cloudinitnetwork += " wlan0:\n"
cloudinitnetwork += " dhcp4: true\n"
cloudinitnetwork += " optional: true\n"
cloudinitnetwork += " access-points:\n"
cloudinitnetwork += " "+fieldWifiSSID.text+":\n"
cloudinitnetwork += " password: \""+cryptedPsk+"\"\n"
} }
if (chkLocale.checked) { if (chkLocale.checked) {
if (chkSkipFirstUse) { if (chkSkipFirstUse) {
addFirstRun("rm -f /etc/xdg/autostart/piwiz.desktop") addFirstRun("rm -f /etc/xdg/autostart/piwiz.desktop")
addCloudInitRun("rm -f /etc/xdg/autostart/piwiz.desktop")
} }
var kbdconfig = "XKBMODEL=\"pc105\"\n"
kbdconfig += "XKBLAYOUT=\""+fieldKeyboardLayout.text+"\"\n"
kbdconfig += "XKBVARIANT=\"\"\n"
kbdconfig += "XKBOPTIONS=\"\"\n"
addFirstRun("rm -f /etc/localtime") addFirstRun("rm -f /etc/localtime")
addFirstRun("echo \""+fieldTimezone.editText+"\" >/etc/timezone") addFirstRun("echo \""+fieldTimezone.editText+"\" >/etc/timezone")
addFirstRun("dpkg-reconfigure -f noninteractive tzdata") addFirstRun("dpkg-reconfigure -f noninteractive tzdata")
addFirstRun("cat >/etc/default/keyboard <<'KBEOF'") addFirstRun("cat >/etc/default/keyboard <<'KBEOF'")
addFirstRun("XKBMODEL=\"pc105\"") addFirstRun(kbdconfig)
addFirstRun("XKBLAYOUT=\""+fieldKeyboardLayout.text+"\"")
addFirstRun("XKBVARIANT=\"\"")
addFirstRun("XKBOPTIONS=\"\"")
addFirstRun("KBEOF") addFirstRun("KBEOF")
addFirstRun("dpkg-reconfigure -f noninteractive keyboard-configuration") addFirstRun("dpkg-reconfigure -f noninteractive keyboard-configuration")
addCloudInit("timezone: "+fieldTimezone.editText)
addCloudInitWriteFile("/etc/default/keyboard", kbdconfig, '0644')
addCloudInitRun("dpkg-reconfigure -f noninteractive keyboard-configuration || true")
} }
if (firstrun.length) { if (firstrun.length) {
@ -574,10 +634,19 @@ Popup {
addFirstRun("exit 0") addFirstRun("exit 0")
/* using systemd.run_success_action=none does not seem to have desired effect /* using systemd.run_success_action=none does not seem to have desired effect
systemd then stays at "reached target kernel command line", so use reboot instead */ systemd then stays at "reached target kernel command line", so use reboot instead */
addCmdline("systemd.run=/boot/firstrun.sh systemd.run_success_action=reboot systemd.unit=kernel-command-line.target") //addCmdline("systemd.run=/boot/firstrun.sh systemd.run_success_action=reboot systemd.unit=kernel-command-line.target")
// cmdline changing moved to DownloadThread::_customizeImage()
} }
imageWriter.setImageCustomization(config, cmdline, firstrun) if (cloudinitwrite !== "") {
addCloudInit("write_files:\n"+cloudinitwrite+"\n")
}
if (cloudinitrun !== "") {
addCloudInit("runcmd:\n"+cloudinitrun+"\n")
}
imageWriter.setImageCustomization(config, cmdline, firstrun, cloudinit, cloudinitnetwork)
} }
function saveSettings() function saveSettings()

View file

@ -829,11 +829,14 @@ qint64 DownloadThread::_sectorsWritten()
return -1; return -1;
} }
void DownloadThread::setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun) void DownloadThread::setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudInitNetwork, const QByteArray &initFormat)
{ {
_config = config; _config = config;
_cmdline = cmdline; _cmdline = cmdline;
_firstrun = firstrun; _firstrun = firstrun;
_cloudinit = cloudinit;
_cloudinitNetwork = cloudInitNetwork;
_initFormat = initFormat;
} }
bool DownloadThread::_customizeImage() bool DownloadThread::_customizeImage()
@ -987,20 +990,6 @@ bool DownloadThread::_customizeImage()
emit preparationStatusUpdate(tr("Customizing image")); emit preparationStatusUpdate(tr("Customizing image"));
if (!_firstrun.isEmpty())
{
QFile f(folder+"/firstrun.sh");
if (f.open(f.WriteOnly) && f.write(_firstrun) == _firstrun.length())
{
f.close();
}
else
{
emit error(tr("Error creating firstrun.sh on FAT partition"));
return false;
}
}
if (!_config.isEmpty()) if (!_config.isEmpty())
{ {
auto configItems = _config.split('\n'); auto configItems = _config.split('\n');
@ -1041,6 +1030,85 @@ bool DownloadThread::_customizeImage()
} }
} }
if (_initFormat == "auto")
{
/* Do an attempt at auto-detecting what customization format a custom
image provided by the user supports */
QByteArray issue;
QFile fi(folder+"/issue.txt");
if (fi.exists() && fi.open(fi.ReadOnly))
{
issue = fi.readAll();
fi.close();
}
if (QFile::exists(folder+"/user-data"))
{
/* If we have user-data file on FAT partition, then it must be cloudinit */
_initFormat = "cloudinit";
qDebug() << "user-data found on FAT partition. Assuming cloudinit support";
}
else if (issue.contains("pi-gen"))
{
/* If issue.txt mentions pi-gen, and there is no user-data file assume
* it is a RPI OS flavor, and use the old systemd unit firstrun script stuff */
_initFormat = "systemd";
qDebug() << "using firstrun script invoked by systemd customization method";
}
else
{
/* Fallback to writing cloudinit file, as it does not hurt having one
* Will just have no customization if OS does not support it */
_initFormat = "cloudinit";
qDebug() << "Unknown what customization method image supports. Falling back to cloudinit";
}
}
if (!_firstrun.isEmpty() && _initFormat == "systemd")
{
QFile f(folder+"/firstrun.sh");
if (f.open(f.WriteOnly) && f.write(_firstrun) == _firstrun.length())
{
f.close();
}
else
{
emit error(tr("Error creating firstrun.sh on FAT partition"));
return false;
}
_cmdline += " systemd.run=/boot/firstrun.sh systemd.run_success_action=reboot systemd.unit=kernel-command-line.target";
}
if (!_cloudinit.isEmpty() && _initFormat == "cloudinit")
{
_cloudinit = "#cloud-config\n"+_cloudinit;
QFile f(folder+"/user-data");
if (f.open(f.WriteOnly) && f.write(_cloudinit) == _cloudinit.length())
{
f.close();
}
else
{
emit error(tr("Error creating user-data cloudinit file on FAT partition"));
return false;
}
}
if (!_cloudinitNetwork.isEmpty() && _initFormat == "cloudinit")
{
QFile f(folder+"/network-config");
if (f.open(f.WriteOnly) && f.write(_cloudinitNetwork) == _cloudinitNetwork.length())
{
f.close();
}
else
{
emit error(tr("Error creating network-config cloudinit file on FAT partition"));
return false;
}
}
if (!_cmdline.isEmpty()) if (!_cmdline.isEmpty())
{ {
QByteArray cmdline; QByteArray cmdline;

View file

@ -112,7 +112,7 @@ public:
/* /*
* Enable image customization * Enable image customization
*/ */
void setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun); void setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudinitNetwork, const QByteArray &initFormat);
/* /*
* Thread safe download progress query functions * Thread safe download progress query functions
@ -164,7 +164,7 @@ protected:
curl_off_t _startOffset; curl_off_t _startOffset;
std::atomic<std::uint64_t> _lastDlTotal, _lastDlNow, _verifyTotal, _lastVerifyNow, _bytesWritten; std::atomic<std::uint64_t> _lastDlTotal, _lastDlNow, _verifyTotal, _lastVerifyNow, _bytesWritten;
qint64 _sectorsStart; qint64 _sectorsStart;
QByteArray _url, _useragent, _buf, _filename, _lastError, _expectedHash, _config, _cmdline, _firstrun; QByteArray _url, _useragent, _buf, _filename, _lastError, _expectedHash, _config, _cmdline, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat;
char *_firstBlock; char *_firstBlock;
size_t _firstBlockSize; size_t _firstBlockSize;
static QByteArray _proxy; static QByteArray _proxy;

1
icons/ic_cog_40px.svg Normal file
View file

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><defs><style>.cls-1{fill:#f2c5c5;}.cls-2{fill:none;stroke:#f2c5c5;stroke-miterlimit:10;stroke-width:3px;}</style></defs><path class="cls-1" d="M20,16a4,4,0,1,1-4,4,4.00455,4.00455,0,0,1,4-4m0-2a6,6,0,1,0,6,6,6,6,0,0,0-6-6Z"/><path class="cls-2" d="M18.41429,7.42824,15.25814,3.11A17.506,17.506,0,0,0,7.742,7.45608l2.17348,4.88276a12.31422,12.31422,0,0,0-1.59722,2.75283l-5.29836.57179a17.50582,17.50582,0,0,0,.00577,8.68221l5.31534.5591a12.34163,12.34163,0,0,0,1.58374,2.75854L7.76287,32.55559a17.50588,17.50588,0,0,0,7.52189,4.33611L18.42662,32.568a12.34585,12.34585,0,0,0,3.18078.00781l3.15589,4.31881a17.506,17.506,0,0,0,7.51614-4.3461L30.106,27.66578a12.34511,12.34511,0,0,0,1.59709-2.7506l5.31778-.57412a17.50593,17.50593,0,0,0-.00576-8.68221l-5.31534-.5591a12.32179,12.32179,0,0,0-1.59627-2.77415l2.15518-4.87708a17.50589,17.50589,0,0,0-7.5219-4.33612L21.59487,7.43607A12.36027,12.36027,0,0,0,18.41429,7.42824Z"/></svg>

After

Width:  |  Height:  |  Size: 1,018 B

View file

@ -138,7 +138,7 @@ void ImageWriter::setEngine(QQmlApplicationEngine *engine)
} }
/* Set URL to download from */ /* Set URL to download from */
void ImageWriter::setSrc(const QUrl &url, quint64 downloadLen, quint64 extrLen, QByteArray expectedHash, bool multifilesinzip, QString parentcategory, QString osname) void ImageWriter::setSrc(const QUrl &url, quint64 downloadLen, quint64 extrLen, QByteArray expectedHash, bool multifilesinzip, QString parentcategory, QString osname, QByteArray initFormat)
{ {
_src = url; _src = url;
_downloadLen = downloadLen; _downloadLen = downloadLen;
@ -147,11 +147,13 @@ void ImageWriter::setSrc(const QUrl &url, quint64 downloadLen, quint64 extrLen,
_multipleFilesInZip = multifilesinzip; _multipleFilesInZip = multifilesinzip;
_parentCategory = parentcategory; _parentCategory = parentcategory;
_osName = osname; _osName = osname;
_initFormat = (initFormat == "none") ? "" : initFormat;
if (!_downloadLen && url.isLocalFile()) if (!_downloadLen && url.isLocalFile())
{ {
QFileInfo fi(url.toLocalFile()); QFileInfo fi(url.toLocalFile());
_downloadLen = fi.size(); _downloadLen = fi.size();
_initFormat = "auto";
} }
} }
@ -238,7 +240,7 @@ void ImageWriter::startWrite()
connect(_thread, SIGNAL(preparationStatusUpdate(QString)), SLOT(onPreparationStatusUpdate(QString))); connect(_thread, SIGNAL(preparationStatusUpdate(QString)), SLOT(onPreparationStatusUpdate(QString)));
_thread->setVerifyEnabled(_verifyEnabled); _thread->setVerifyEnabled(_verifyEnabled);
_thread->setUserAgent(QString("Mozilla/5.0 rpi-imager/%1").arg(constantVersion()).toUtf8()); _thread->setUserAgent(QString("Mozilla/5.0 rpi-imager/%1").arg(constantVersion()).toUtf8());
_thread->setImageCustomization(_config, _cmdline, _firstrun); _thread->setImageCustomization(_config, _cmdline, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat);
if (!_expectedHash.isEmpty() && _cachedFileHash != _expectedHash && _cachingEnabled) if (!_expectedHash.isEmpty() && _cachedFileHash != _expectedHash && _cachingEnabled)
{ {
@ -941,15 +943,18 @@ void ImageWriter::setSetting(const QString &key, const QVariant &value)
_settings.sync(); _settings.sync();
} }
void ImageWriter::setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun) void ImageWriter::setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudinitNetwork)
{ {
_config = config; _config = config;
_cmdline = cmdline; _cmdline = cmdline;
_firstrun = firstrun; _firstrun = firstrun;
_cloudinit = cloudinit;
_cloudinitNetwork = cloudinitNetwork;
qDebug() << "Custom config.txt entries:" << config; qDebug() << "Custom config.txt entries:" << config;
qDebug() << "Custom cmdline.txt entries:" << cmdline; qDebug() << "Custom cmdline.txt entries:" << cmdline;
qDebug() << "Custom firstuse.sh:" << firstrun; qDebug() << "Custom firstuse.sh:" << firstrun;
qDebug() << "Cloudinit:" << cloudinit;
} }
QString ImageWriter::crypt(const QByteArray &password) QString ImageWriter::crypt(const QByteArray &password)
@ -1022,6 +1027,11 @@ bool ImageWriter::hasSavedCustomizationSettings()
return result; return result;
} }
bool ImageWriter::imageSupportsCustomization()
{
return !_initFormat.isEmpty();
}
void MountUtilsLog(std::string msg) { void MountUtilsLog(std::string msg) {
Q_UNUSED(msg) Q_UNUSED(msg)
//qDebug() << "mountutils:" << msg.c_str(); //qDebug() << "mountutils:" << msg.c_str();

View file

@ -29,7 +29,7 @@ public:
void setEngine(QQmlApplicationEngine *engine); void setEngine(QQmlApplicationEngine *engine);
/* Set URL to download from, and if known download length and uncompressed length */ /* Set URL to download from, and if known download length and uncompressed length */
Q_INVOKABLE void setSrc(const QUrl &url, quint64 downloadLen = 0, quint64 extrLen = 0, QByteArray expectedHash = "", bool multifilesinzip = false, QString parentcategory = "", QString osname = ""); Q_INVOKABLE void setSrc(const QUrl &url, quint64 downloadLen = 0, quint64 extrLen = 0, QByteArray expectedHash = "", bool multifilesinzip = false, QString parentcategory = "", QString osname = "", QByteArray initFormat = "");
/* Set device to write to */ /* Set device to write to */
Q_INVOKABLE void setDst(const QString &device, quint64 deviceSize = 0); Q_INVOKABLE void setDst(const QString &device, quint64 deviceSize = 0);
@ -102,11 +102,12 @@ public:
Q_INVOKABLE bool getBoolSetting(const QString &key); Q_INVOKABLE bool getBoolSetting(const QString &key);
Q_INVOKABLE void setSetting(const QString &key, const QVariant &value); Q_INVOKABLE void setSetting(const QString &key, const QVariant &value);
Q_INVOKABLE void setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun); Q_INVOKABLE void setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudinitNetwork);
Q_INVOKABLE void setSavedCustomizationSettings(const QVariantMap &map); Q_INVOKABLE void setSavedCustomizationSettings(const QVariantMap &map);
Q_INVOKABLE QVariantMap getSavedCustomizationSettings(); Q_INVOKABLE QVariantMap getSavedCustomizationSettings();
Q_INVOKABLE void clearSavedCustomizationSettings(); Q_INVOKABLE void clearSavedCustomizationSettings();
Q_INVOKABLE bool hasSavedCustomizationSettings(); Q_INVOKABLE bool hasSavedCustomizationSettings();
Q_INVOKABLE bool imageSupportsCustomization();
Q_INVOKABLE QString crypt(const QByteArray &password); Q_INVOKABLE QString crypt(const QByteArray &password);
Q_INVOKABLE QString pbkdf2(const QByteArray &psk, const QByteArray &ssid); Q_INVOKABLE QString pbkdf2(const QByteArray &psk, const QByteArray &ssid);
@ -143,7 +144,7 @@ protected slots:
protected: protected:
QUrl _src, _repo; QUrl _src, _repo;
QString _dst, _cacheFileName, _parentCategory, _osName; QString _dst, _cacheFileName, _parentCategory, _osName;
QByteArray _expectedHash, _cachedFileHash, _cmdline, _config, _firstrun; QByteArray _expectedHash, _cachedFileHash, _cmdline, _config, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat;
quint64 _downloadLen, _extrLen, _devLen, _dlnow, _verifynow; quint64 _downloadLen, _extrLen, _devLen, _dlnow, _verifynow;
DriveListModel _drivelist; DriveListModel _drivelist;
QQmlApplicationEngine *_engine; QQmlApplicationEngine *_engine;

View file

@ -192,7 +192,7 @@ ApplicationWindow {
return return
} }
if (!optionspopup.initialized && imageWriter.hasSavedCustomizationSettings()) { if (!optionspopup.initialized && imageWriter.imageSupportsCustomization() && imageWriter.hasSavedCustomizationSettings()) {
usesavedsettingspopup.openPopup() usesavedsettingspopup.openPopup()
} else { } else {
confirmwritepopup.askForConfirmation() confirmwritepopup.askForConfirmation()
@ -205,7 +205,7 @@ ApplicationWindow {
ColumnLayout { ColumnLayout {
id: columnLayout3 id: columnLayout3
Layout.columnSpan: 3 Layout.columnSpan: 3
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
Text { Text {
id: progressText id: progressText
@ -255,6 +255,17 @@ ApplicationWindow {
font.family: roboto.name font.family: roboto.name
Accessible.onPressAction: clicked() Accessible.onPressAction: clicked()
} }
Image {
id: customizebutton
source: "icons/ic_cog_40px.svg"
visible: false
MouseArea {
anchors.fill: parent
onClicked: {
optionspopup.openPopup()
}
}
}
} }
} }
} }
@ -375,6 +386,7 @@ ApplicationWindow {
description: qsTr("Go back to main menu") description: qsTr("Go back to main menu")
tooltip: "" tooltip: ""
website: "" website: ""
init_format: ""
} }
} }
@ -416,6 +428,7 @@ ApplicationWindow {
description: qsTr("Format card as FAT32") description: qsTr("Format card as FAT32")
tooltip: "" tooltip: ""
website: "" website: ""
init_format: ""
} }
ListElement { ListElement {
@ -767,6 +780,7 @@ ApplicationWindow {
title: qsTr("Warning") title: qsTr("Warning")
onYes: { onYes: {
writebutton.enabled = false writebutton.enabled = false
customizebutton.visible = false
cancelwritebutton.enabled = true cancelwritebutton.enabled = true
cancelwritebutton.visible = true cancelwritebutton.visible = true
cancelverifybutton.enabled = true cancelverifybutton.enabled = true
@ -892,6 +906,7 @@ ApplicationWindow {
function resetWriteButton() { function resetWriteButton() {
progressText.visible = false progressText.visible = false
progressBar.visible = false progressBar.visible = false
customizebutton.visible = imageWriter.imageSupportsCustomization()
osbutton.enabled = true osbutton.enabled = true
dstbutton.enabled = true dstbutton.enabled = true
writebutton.visible = true writebutton.visible = true
@ -931,6 +946,7 @@ ApplicationWindow {
if (imageWriter.readyToWrite()) { if (imageWriter.readyToWrite()) {
writebutton.enabled = true writebutton.enabled = true
} }
customizebutton.visible = imageWriter.imageSupportsCustomization()
} }
function onCancelled() { function onCancelled() {
@ -1059,12 +1075,13 @@ ApplicationWindow {
} }
} }
} else { } else {
imageWriter.setSrc(d.url, d.image_download_size, d.extract_size, typeof(d.extract_sha256) != "undefined" ? d.extract_sha256 : "", typeof(d.contains_multiple_files) != "undefined" ? d.contains_multiple_files : false, ospopup.categorySelected, d.name) imageWriter.setSrc(d.url, d.image_download_size, d.extract_size, typeof(d.extract_sha256) != "undefined" ? d.extract_sha256 : "", typeof(d.contains_multiple_files) != "undefined" ? d.contains_multiple_files : false, ospopup.categorySelected, d.name, typeof(d.init_format) != "undefined" ? d.init_format : "")
osbutton.text = d.name osbutton.text = d.name
ospopup.close() ospopup.close()
if (imageWriter.readyToWrite()) { if (imageWriter.readyToWrite()) {
writebutton.enabled = true writebutton.enabled = true
} }
customizebutton.visible = imageWriter.imageSupportsCustomization()
} }
} }

View file

@ -28,5 +28,6 @@
<file>UseSavedSettingsPopup.qml</file> <file>UseSavedSettingsPopup.qml</file>
<file>icons/ic_info_16px.png</file> <file>icons/ic_info_16px.png</file>
<file>icons/ic_info_12px.png</file> <file>icons/ic_info_12px.png</file>
<file>icons/ic_cog_40px.svg</file>
</qresource> </qresource>
</RCC> </RCC>