Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions PKGBUILD
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ url="https://github.com/Advnirr/lufux"
license=('GPL3')
depends=('python-gobject' 'gtk4' 'libadwaita' 'wimlib' 'rsync' 'parted' 'polkit' 'libarchive')
makedepends=('git')
source=("main.py" "windows_logic.py" "universal_logic.py" "deps_logic.py" "lufux.desktop" "lufux.svg")
sha256sums=('SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP')
source=("main.py" "windows_logic.py" "windows_togo_logic.py" "universal_logic.py" "deps_logic.py" "lufux.desktop" "lufux.svg")
sha256sums=('SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP')

package() {
install -d "${pkgdir}/usr/share/lufux"

install -Dm755 "${srcdir}/main.py" "${pkgdir}/usr/share/lufux/main.py"
install -Dm644 "${srcdir}/windows_logic.py" "${pkgdir}/usr/share/lufux/windows_logic.py"
install -Dm644 "${srcdir}/windows_togo_logic.py" "${pkgdir}/usr/share/lufux/windows_togo_logic.py"
install -Dm644 "${srcdir}/universal_logic.py" "${pkgdir}/usr/share/lufux/universal_logic.py"
install -Dm644 "${srcdir}/deps_logic.py" "${pkgdir}/usr/share/lufux/deps_logic.py"

Expand Down
34 changes: 29 additions & 5 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def save_config(cfg):

# importing logic
from windows_logic import get_windows_script
from windows_togo_logic import get_windows_togo_script
from universal_logic import get_linux_script
from deps_logic import check_dependencies, get_distro_info, get_install_cmd

Expand Down Expand Up @@ -78,6 +79,8 @@ def get_locale_dict():
"partition_scheme": "Схема разделов (для Windows):",
"scheme_gpt": "GPT (UEFI / FAT32)",
"scheme_mbr": "MBR (Legacy BIOS / NTFS)",
"wtg_mode": "Windows To Go (запускаемая Windows на USB)",
"wtg_summary": "Windows To Go (GPT / UEFI)",
"summary_drive": "Целевой накопитель",
"summary_iso": "Выбранный образ",
"summary_os": "Определенная система",
Expand Down Expand Up @@ -135,6 +138,8 @@ def get_locale_dict():
"partition_scheme": "Partition Scheme (for Windows):",
"scheme_gpt": "GPT (UEFI / FAT32)",
"scheme_mbr": "MBR (Legacy BIOS / NTFS)",
"wtg_mode": "Windows To Go (bootable Windows on USB)",
"wtg_summary": "Windows To Go (GPT / UEFI)",
"summary_drive": "Target Drive",
"summary_iso": "Selected ISO",
"summary_os": "Detected OS",
Expand Down Expand Up @@ -300,18 +305,33 @@ def setup_page_iso(self):
box.append(self.scheme_label)

self.scheme_dropdown = Gtk.DropDown.new_from_strings([T["scheme_gpt"], T["scheme_mbr"]])
self.scheme_dropdown.set_visible(False)
self.scheme_dropdown.set_visible(False)
self.scheme_dropdown.set_size_request(280, -1)
box.append(self.scheme_dropdown)

# Windows To Go: deploy a runnable Windows instead of installer media.
# Forces GPT/UEFI, so the scheme selector is hidden while it is on.
self.wtg_check = Gtk.CheckButton(label=T["wtg_mode"])
self.wtg_check.set_visible(False)
self.wtg_check.connect("toggled", self.on_wtg_toggled)
box.append(self.wtg_check)

self.os_dropdown.connect("notify::selected", self.on_os_changed)

return page

def on_os_changed(self, dropdown, param):
is_windows = dropdown.get_selected() == 0
self.scheme_label.set_visible(is_windows)
self.scheme_dropdown.set_visible(is_windows)
self.wtg_check.set_visible(is_windows)
wtg = is_windows and self.wtg_check.get_active()
self.scheme_label.set_visible(is_windows and not wtg)
self.scheme_dropdown.set_visible(is_windows and not wtg)

def on_wtg_toggled(self, check):
# Windows To Go is always GPT/UEFI, so hide the scheme picker
wtg = check.get_active()
self.scheme_label.set_visible(not wtg)
self.scheme_dropdown.set_visible(not wtg)

def setup_page_summary(self):
page = Adw.StatusPage(title=T["step_summary"], icon_name="emblem-system-symbolic")
Expand Down Expand Up @@ -419,7 +439,9 @@ def update_summary_data(self):
os_text = T["os_win"] if os_idx == 0 else T["os_lin"] if os_idx == 1 else T["os_other"]
self.sum_os.set_subtitle(os_text)

if os_idx == 0:
if os_idx == 0 and self.wtg_check.get_active():
self.sum_scheme.set_subtitle(T["wtg_summary"])
elif os_idx == 0:
scheme_idx = self.scheme_dropdown.get_selected()
scheme_text = T["scheme_gpt"] if scheme_idx == 0 else T["scheme_mbr"]
self.sum_scheme.set_subtitle(scheme_text)
Expand Down Expand Up @@ -718,7 +740,9 @@ def worker_thread(self, iso, dev):
scheme_idx = self.scheme_dropdown.get_selected()
scheme = "gpt" if scheme_idx == 0 else "mbr"

if os_idx == 0:
if os_idx == 0 and self.wtg_check.get_active():
script = get_windows_togo_script()
elif os_idx == 0:
script = get_windows_script(scheme)
else:
script = get_linux_script()
Expand Down
107 changes: 107 additions & 0 deletions windows_togo_logic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import os

# Locals
def get_locale_dict():
lang = os.environ.get('LANG', '')
if lang.startswith('ru'):
return {
# russian locals
"prep": "Подготовка накопителя...",
"part": "Создание разделов (ESP + Windows)...",
"no_image": "В образе не найден install.wim/install.esd",
"apply": "Развёртывание Windows на накопитель (это надолго)...",
"boot": "Установка загрузчика UEFI...",
"sync": "Синхронизация ввода-вывода (sync)..."
}
return {
# english locals
"prep": "Preparing drive...",
"part": "Creating partitions (ESP + Windows)...",
"no_image": "install.wim/install.esd not found in the image",
"apply": "Deploying Windows to the drive (this takes a while)...",
"boot": "Installing the UEFI bootloader...",
"sync": "Syncing I/O (sync)..."
}

def get_windows_togo_script(img_index=1):
# iso/dev come in as $1/$2; img_index is the WIM image to deploy.
# A real, bootable Windows is applied to the drive (not an installer):
# GPT with a FAT32 ESP + NTFS Windows partition, the WIM is expanded with
# wimlib and the UEFI bootloader is taken from the deployed image.
T = get_locale_dict()

# img_index is an int chosen in Python, never user free-text
idx = int(img_index)

script = f"""#!/bin/bash
set -e

ISO_PATH="$1"
DEV_PATH="$2"
IMG_INDEX="{idx}"

ISO_MNT=$(mktemp -d /tmp/lufux_iso.XXXXXX)
WIN_MNT=$(mktemp -d /tmp/lufux_win.XXXXXX)
EFI_MNT=$(mktemp -d /tmp/lufux_efi.XXXXXX)

cleanup() {{
umount "$ISO_MNT" "$WIN_MNT" "$EFI_MNT" 2>/dev/null || true
rmdir "$ISO_MNT" "$WIN_MNT" "$EFI_MNT" 2>/dev/null || true
}}
trap cleanup EXIT

echo "STATUS: {T['prep']}"
umount "$DEV_PATH"* 2>/dev/null || true
wipefs -a "$DEV_PATH"

echo "STATUS: {T['part']}"
# GPT: partition 1 = FAT32 ESP, partition 2 = NTFS Windows
parted -s "$DEV_PATH" mklabel gpt
parted -s "$DEV_PATH" mkpart ESP fat32 1MiB 1025MiB
parted -s "$DEV_PATH" set 1 esp on
parted -s "$DEV_PATH" mkpart Windows ntfs 1025MiB 100%
sleep 2

mkfs.vfat -F 32 -n "ESP" "${{DEV_PATH}}1"
mkfs.ntfs -f -L "Windows" "${{DEV_PATH}}2"

mount -o loop,ro "$ISO_PATH" "$ISO_MNT"
mount "${{DEV_PATH}}2" "$WIN_MNT"

# locate the Windows image inside the ISO
TF=""
[ -f "$ISO_MNT/sources/install.wim" ] && TF="$ISO_MNT/sources/install.wim"
[ -f "$ISO_MNT/sources/install.esd" ] && TF="$ISO_MNT/sources/install.esd"
if [ -z "$TF" ]; then
echo "STATUS: {T['no_image']}"
exit 1
fi

echo "STATUS: {T['apply']}"
# expand the chosen edition straight onto the NTFS partition
wimlib-imagex apply "$TF" "$IMG_INDEX" "$WIN_MNT" 2>&1

echo "STATUS: {T['boot']}"
mount "${{DEV_PATH}}1" "$EFI_MNT"
mkdir -p "$EFI_MNT/EFI/Microsoft/Boot" "$EFI_MNT/EFI/Boot"

# UEFI bootloader from the deployed Windows
if [ -f "$WIN_MNT/Windows/Boot/EFI/bootmgfw.efi" ]; then
cp "$WIN_MNT/Windows/Boot/EFI/bootmgfw.efi" "$EFI_MNT/EFI/Boot/bootx64.efi"
cp "$WIN_MNT/Windows/Boot/EFI/bootmgfw.efi" "$EFI_MNT/EFI/Microsoft/Boot/bootmgfw.efi"
fi
# boot resources (fonts, locale, etc.)
if [ -d "$WIN_MNT/Windows/Boot/EFI" ]; then
cp -r "$WIN_MNT/Windows/Boot/EFI/." "$EFI_MNT/EFI/Microsoft/Boot/" 2>/dev/null || true
fi
# BCD store seeded from the OS image template
if [ -f "$WIN_MNT/Windows/System32/config/BCD-Template" ]; then
cp "$WIN_MNT/Windows/System32/config/BCD-Template" "$EFI_MNT/EFI/Microsoft/Boot/BCD"
fi

echo "STATUS: {T['sync']}"
sync

echo "STATUS: DONE"
"""
return script