Re: [PATCH 1/4] tools: add spectrum-installer

Johannes Süllner
2026-01-11T18:30:39Z

[-- Attachment #1: Type: text/plain, Size: 7939 bytes --]

On Sun Jan 4, 2026 at 10:13 PM CET, Demi Marie Obenour wrote:
> On 1/4/26 09:00, Johannes Süllner wrote:
>> Written in Rust using the Iced GUI library. Uses `lsblk-rs` to offer a
>> list of disks where the user can choose one to install Spectrum on.
>> Uses `systemd-repart` for the installation, which is running in an
>> embedded terminal window using `iced-term`.
>>
>> Signed-off-by: Johannes Süllner <johannes.suellner@mailbox.org>
>> ---
...
>> diff --git a/tools/spectrum-installer/shell.nix b/tools/spectrum-installer/shell.nix
>> new file mode 100644
>> index 0000000..060009b
>> --- /dev/null
>> +++ b/tools/spectrum-installer/shell.nix
>> @@ -0,0 +1,30 @@
>> +# SPDX-License-Identifier: MIT
>> +# SPDX-FileCopyrightText: 2025 iced contributors
>> +
>> +# from https://github.com/iced-rs/iced/blob/master/DEPENDENCIES.md
>> +# last updated 2025-11-11
>> +{ pkgs ? import <nixpkgs> {} }:
>> +
>> +pkgs.mkShell rec {
>> + buildInputs = with pkgs; [
>> + cargo
>> + lldb
>> + expat
>> + fontconfig
>> + freetype
>> + freetype.dev
>> + libGL
>> + pkg-config
>> + rustfmt
>> + xorg.libX11
>> + xorg.libXcursor
>> + xorg.libXi
>> + xorg.libXrandr
>> + wayland
>> + libxkbcommon
>> + ];
>
> Is it possible to drop the X11 libraries? The installer should be
> Wayland-only. If upstream Iced can't be built without X11 support,
> feel free to ignore this.

Yes, it is possible to drop them. I just did not tried until now,
but building without them in a `nix-shell --pure` environment works.

They are gone now; I also updated the whole develompent shell file as
the upstream reference (https://github.com/iced-rs/iced/blob/master/DEPENDENCIES.md)
also changed.

>> diff --git a/tools/spectrum-installer/src/main.rs b/tools/spectrum-installer/src/main.rs
>> new file mode 100644
>> index 0000000..2ca9dfa
>> --- /dev/null
>> +++ b/tools/spectrum-installer/src/main.rs
>> @@ -0,0 +1,52 @@
>> +// SPDX-License-Identifier: EUPL-1.2+
>> +// SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
>> +
>> +mod pages;
>> +
>> +use iced::Element;
>> +use iced::Size;
>> +use iced::Subscription;
>> +use iced::Task;
>> +use pages::Event;
>> +
>> +fn main() -> iced::Result {
>> + iced::application(
>> + "Spectrum installer",
>> + SpectrumInstaller::update,
>> + SpectrumInstaller::view,
>> + )
>> + .window_size(Size::new(pages::WINDOW_WIDTH, pages::WINDOW_HEIGHT))
>> + .font(iced_fonts::BOOTSTRAP_FONT_BYTES)
>> + .resizable(false)
>> + .subscription(SpectrumInstaller::subscription)
>> + .run()
>> +}
>
> Have you tested with HiDPI displays? I think it would be better for
> the installer to be fullscreen, or at least have a size proportional
> to the display it is on.
>
> I suspect that hard-coded sizes are an antipattern, though I don't know
> what the best alternative would be. Ideally there would be an option
> to zoom in and out, so that those who need larger buttons and text can
> get that. Is there a way to ask Iced to size things automatically?

I did not test on a HiDPI screen this before, but did so now and it
would scale just fine in latest PopOS with Cosmic.

I agree a large font mode or zoom-options would be good for
accessibility. I'm happy to implement this at some future point.

Personally, I do not like fullscreen installers too much, perhaps
because I think there is not enough content that would justify
fullscreen. Not a strong opinion though.

As a sidenote, resizing of the installer already causes the widgets
to resize as well (font size stays the same, though). You can
currently resize the window in Weston, where the application's wish to
not be resizable is not honored.

>> diff --git a/tools/spectrum-installer/src/pages/completion.rs b/tools/spectrum-installer/src/pages/completion.rs
>> new file mode 100644
>> index 0000000..2ea9734
>> --- /dev/null
>> +++ b/tools/spectrum-installer/src/pages/completion.rs
...
>> + fn view(&self) -> Element<'_, Event> {
>> + let main_element = match &self.install_result {
>> + Ok(_) => {
>> + center_in_container!(text("Spectrum installation succeeded!").size(TITLE_TEXT_SIZE)).into()
>> + }
>> + Err((code, log)) => {
>> + column![
>> + Space::with_height(MARGIN_VERTICAL),
>> + center_horizontal_in_container!(text("Spectrum installation failed :( ").size(TITLE_TEXT_SIZE)),
>> + Space::with_height(MARGIN_VERTICAL),
>> + center_horizontal_in_container!(Scrollable::new(text(format!("You can reach out to the team via Chat or the \"discuss\" mailing list.\n\
>> + See https://spectrum-os.org/doc/contributing/communication.html for details.\n\
>> + The following information may be relevant:\n\n\
>> + Error code: {}\n\nError log:\n{}", code, log)))
>> + .width(WINDOW_WIDTH - 2.0 * MARGIN_LR)
>> + .height(500)
>> + .direction(Direction::Vertical(Scrollbar::new()))),
>> + ]
>> + .into()
>> + }
>> + };
>
> I see no harm in including the full email address of the discuss
> mailing list (discuss@spectrum-os.org). Also, it would be useful to
> display a QR code that someone can scan using their mobile device.
> This avoids having to do clumsy and error-prone manual transcription,
> and may allow for including more lines of output.

I guess you're right, I've included the full email address now.

I like your idea to have a QR code for the error log and just
implemented it.

>> + String::from(
>> + [
>> + "( /usr/bin/systemd-repart \
>> + --definitions=/usr/share/spectrum-installer/repart.d \
>> + --empty=force \
>> + --dry-run=no ",
>> + &device.fullname.display().to_string(),
>
> Missing shell argument escaping and '-o pipefail'. The latter might
> require bash instead of sh.

Good catch, fixed. Works with sh as well.

>> diff --git a/tools/spectrum-installer/src/pages/layout.rs b/tools/spectrum-installer/src/pages/layout.rs
>> new file mode 100644
>> index 0000000..e7095bd
>> --- /dev/null
>> +++ b/tools/spectrum-installer/src/pages/layout.rs
...
>> +
>> +pub fn disk_element(device: &BlockDevice) -> Element<'static, Event> {
>> + let icon: Element<Event> = text(Icon::Hdd.to_string())
>> + .font(ICON_FONT)
>> + .size(DISK_ICON_SIZE)
>> + .into();
>> +
>> + let disk_size = if let Ok(Some(capacity)) = device.capacity() {
>> + let mut size_factor: f32 = (capacity * 512) as f32;
>
> Why f32 and not f64?

No particular reason, I changed it to f64.

>> diff --git a/tools/spectrum-installer/src/pages/mod.rs b/tools/spectrum-installer/src/pages/mod.rs
>> new file mode 100644
>> index 0000000..cd020f1
>> --- /dev/null
>> +++ b/tools/spectrum-installer/src/pages/mod.rs
>> @@ -0,0 +1,43 @@
>> +// SPDX-License-Identifier: EUPL-1.2+
>> +// SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
>> +
>> +use iced::{Subscription, Task};
>> +
>> +mod completion;
>> +mod confirmation;
>> +mod disk_selection;
>> +mod installation;
>> +mod layout;
>> +mod welcome;
>> +
>> +pub const WINDOW_WIDTH: f32 = 750.0;
>> +pub const WINDOW_HEIGHT: f32 = 650.0;
>> +
>> +pub const MARGIN_VERTICAL: f32 = 25.0;
>> +pub const MARGIN_LR: f32 = 15.0;
>
> Why f32 and not f64?

Iced expects 32 bit floats for sizes.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 269 bytes --]