Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,5 @@ AppDir
*.spec
site/
.desloppify/

*.geany
.briefcase/
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "lufus"
version = "1.0.1b1" # keep synced with [tool.briefcase] version
version = "1.0.0" # keep synced with [tool.briefcase] version
description = "A rufus clone written in Python and designed to work with Linux."
readme = "README.md"
license = "MIT"
Expand All @@ -29,7 +29,7 @@ lufus = "lufus.__main__:main"
[tool.briefcase]
project_name = "Lufus"
bundle = "com.github.hog185"
version = "1.0.1b1" # keep synced with [project] version
version = "1.0.0" # keep synced with [project] version
url = "https://github.com/Hog185/Lufus"
license.file = "LICENSE"
author = "Hog185"
Expand Down
21 changes: 11 additions & 10 deletions scripts/build-appimage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ set -euo pipefail
ls -la

# Install system dependencies
echo "------------ Installing system libraries ------------"
apt-get update && apt-get upgrade -y
INSTALLER="apt-get install -y"
if [[ -f requirements-system.txt ]]; then
$INSTALLER $(cat requirements-system.txt) >> appimage-setup.log
echo "System libraries installed."
else
echo "requirements-system.txt not found!"
exit 1
fi
## COMMENTED BECAUSE ALREADY HANDELED IN YAML
# echo "------------ Installing system libraries ------------"
# apt-get update && apt-get upgrade -y
# INSTALLER="apt-get install -y"
# if [[ -f requirements-system.txt ]]; then
# $INSTALLER $(cat requirements-system.txt) >> appimage-setup.log
# echo "System libraries installed."
# else
# echo "requirements-system.txt not found!"
# exit 1
# fi

if command -v python3 &>/dev/null; then
PYTHON=python3
Expand Down
59 changes: 52 additions & 7 deletions src/lufus/drives/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,11 @@ def check_device_bad_blocks() -> bool:
probe.returncode,
)
except Exception as exc:
log.warning("Could not probe sector size for %s: %s. Using default block size.", drive, exc)
log.warning(
"Could not probe sector size for %s: %s. Using default block size.",
drive,
exc,
)

# -s = show progress, -v = verbose output
# -n = non-destructive read-write test (safe default)
Expand Down Expand Up @@ -316,10 +320,30 @@ def _status(msg: str) -> None:
"NTFS",
"ntfs-3g",
),
1: ("mkfs.vfat", lambda: ["-I", "-s", str(sectors_per_cluster), "-F", "32", raw_device], "FAT32", "dosfstools"),
2: ("mkfs.exfat", lambda: ["-b", str(block_size), raw_device], "exFAT", "exfatprogs or exfat-utils"),
3: ("mkfs.ext4", lambda: ["-b", str(block_size), raw_device], "ext4", "e2fsprogs"),
4: ("mkudffs", lambda: ["--blocksize=" + str(sector_size), raw_device], "UDF", "udftools"),
1: (
"mkfs.vfat",
lambda: ["-I", "-s", str(sectors_per_cluster), "-F", "32", raw_device],
"FAT32",
"dosfstools",
),
2: (
"mkfs.exfat",
lambda: ["-b", str(block_size), raw_device],
"exFAT",
"exfatprogs or exfat-utils",
),
3: (
"mkfs.ext4",
lambda: ["-b", str(block_size), raw_device],
"ext4",
"e2fsprogs",
),
4: (
"mkudffs",
lambda: ["--blocksize=" + str(sector_size), raw_device],
"UDF",
"udftools",
),
}

if fs_type not in fs_configs:
Expand Down Expand Up @@ -367,14 +391,30 @@ def _apply_partition_scheme(drive: str) -> None:
# GPT — used for UEFI targets
subprocess.run([_find_tool("parted"), "-s", raw_device, "mklabel", "gpt"], check=True)
subprocess.run(
[_find_tool("parted"), "-s", raw_device, "mkpart", "primary", "1MiB", "100%"],
[
_find_tool("parted"),
"-s",
raw_device,
"mkpart",
"primary",
"1MiB",
"100%",
],
check=True,
)
else:
# MBR — used for BIOS/legacy targets
subprocess.run([_find_tool("parted"), "-s", raw_device, "mklabel", "msdos"], check=True)
subprocess.run(
[_find_tool("parted"), "-s", raw_device, "mkpart", "primary", "1MiB", "100%"],
[
_find_tool("parted"),
"-s",
raw_device,
"mkpart",
"primary",
"1MiB",
"100%",
],
check=True,
)
log.info("Partition scheme %s applied to %s.", scheme_name, raw_device)
Expand All @@ -388,6 +428,11 @@ def _apply_partition_scheme(drive: str) -> None:


def drive_repair() -> None:
# todo:
# add smartctl check if possible
# use fsck to prevent deletion of files for repair
# use testdisk for partition recovery if possible
# do dd if=/dev/zero of=/dev/sdX bs=1M count=10 conv=notrunc before sfdisk use
_, drive, _ = _get_mount_and_drive()
if not drive:
log.error("No drive node found. Cannot repair.")
Expand Down
136 changes: 132 additions & 4 deletions src/lufus/gui/dialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,26 @@
from PyQt6.QtWidgets import (
QApplication,
QComboBox,
QCheckBox,
QDialog,
QFileDialog,
QFrame,
QHBoxLayout,
QLabel,
QLineEdit,
QMessageBox,
QPushButton,
QTextEdit,
QVBoxLayout,
)
from PyQt6.QtCore import Qt, pyqtSignal
from PyQt6.QtGui import QFont
from PyQt6.QtCore import Qt, pyqtSignal, QRegularExpression, QUrl
from PyQt6.QtGui import QFont, QRegularExpressionValidator, QDesktopServices
import sys

from lufus import state as states
from lufus.gui.constants import THEME_DIR, _find_resource_dir
from lufus.gui.scale import Scale
from lufus.lufus_logging import get_logger


class LogWindow(QDialog):
Expand All @@ -28,7 +32,6 @@ def __init__(self, parent=None):
self._T = parent._T if parent else {}
self._S: Scale = parent._S if parent else None
self.setWindowTitle(self._T.get("log_window_title", "Log Window"))

if self._S:
# apply scaled dimensions
self.resize(self._S.px(650), self._S.px(450))
Expand Down Expand Up @@ -139,13 +142,32 @@ def __init__(self, parent=None):
sep.setFrameShadow(QFrame.Shadow.Sunken)
layout.addWidget(sep)

# version
lbl_ver = QLabel(states.version)
lbl_ver.setStyleSheet(f"font-family: {font_family}; font-size: {tool_pt}pt;")
lbl_ver.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(lbl_ver)

# im lying ily (context text something area, whatever)
self.about_text = QTextEdit()
self.about_text.setReadOnly(True)
self.about_text.setObjectName("aboutContent")
self.about_text.setFrameShape(QFrame.Shape.NoFrame)
self.about_text.setStyleSheet(f"font-family: {font_family}; font-size: {tool_pt}pt;")
layout.addWidget(self.about_text, 1)
btn_row0 = QHBoxLayout()
btn_discord = QPushButton(self._T.get("btn_discord", "Join Discord Server"))
btn_discord.setFixedWidth(self._S.px(300) if self._S else 90)
btn_discord.clicked.connect(self.discord_open)
btn_row0.addWidget(btn_discord, alignment=Qt.AlignmentFlag.AlignCenter)
layout.addLayout(btn_row0)

btn_row1 = QHBoxLayout()
btn_github = QPushButton(self._T.get("btn_github", "Open Github Repo"))
btn_github.setFixedWidth(self._S.px(300) if self._S else 90)
btn_github.clicked.connect(self.github_open)
btn_row1.addWidget(btn_github, alignment=Qt.AlignmentFlag.AlignCenter)
layout.addLayout(btn_row1)

btn_row = QHBoxLayout()
# close button or smth, whatever
Expand All @@ -154,9 +176,16 @@ def __init__(self, parent=None):
btn_close.clicked.connect(self.hide)
btn_row.addWidget(btn_close, alignment=Qt.AlignmentFlag.AlignCenter)
layout.addLayout(btn_row)

self.setLayout(layout)

def discord_open(self):
url = QUrl("https://discord.gg/4G6FeBwsxb")
QDesktopServices.openUrl(url)

def github_open(self):
url = QUrl("https://github.com/Hog185/Lufus")
QDesktopServices.openUrl(url)


class SettingsDialog(QDialog):
# signals for when settings change :D
Expand Down Expand Up @@ -246,3 +275,102 @@ def _detect_themes():
# user themes follow the same folder structure :D
custom = sorted(p.parent.name for p in user_themes_dir.glob("*/*_theme.json"))
return builtin, custom


class WinTweaks(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
re = QRegularExpression("^[a-zA-Z0-9_]*$")
validator = QRegularExpressionValidator(re)
self.setWindowTitle("Windows Tweaks (MAY BREAK! USE CAUTION)")
self.setFixedSize(600, 300)
self.ask_label = QLabel("Do you want to customize your windows installation?")
self.hardware_checkbox = QCheckBox("Remove requirement for 4GB+ RAM, Secure Boot and TPM 2.0")
self.hardware_checkbox.stateChanged.connect(self.update_winhardware)
self.microsoft_checkbox = QCheckBox("Remove requirement for an online Microsoft Account")
self.microsoft_checkbox.stateChanged.connect(self.update_winmicrosoftacc)
self.localacc_checkbox = QCheckBox("Create a local account with username:")
self.localacc_checkbox.stateChanged.connect(self.update_winlocalaccchk)
self.username_input = QLineEdit()
self.username_input.setMaxLength(20)
self.username_input.setValidator(validator)
self.username_input.setPlaceholderText("Enter username here...")
self.microsoft_checkbox.toggled.connect(self.localacc_checkbox.setEnabled)
self.localacc_checkbox.toggled.connect(self.username_input.setEnabled)
self.username_input.setEnabled(self.localacc_checkbox.isChecked())
self.username_input.textChanged.connect(self.sync_username)
self.data_checkbox = QCheckBox("Disable data collection (skip privacy questions)")
self.data_checkbox.stateChanged.connect(self.update_winprivacy)
self.applytweaks_btn = QPushButton("Apply")
self.applytweaks_btn.clicked.connect(self.applywintweaks)
self.canceltweaks_btn = QPushButton("Cancel")
self.canceltweaks_btn.clicked.connect(self.reject) # closes window
layout = QVBoxLayout()
layout.setSpacing(15)
layout.setContentsMargins(20, 20, 20, 20)
layout.addWidget(self.ask_label, alignment=Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.hardware_checkbox)
layout.addWidget(self.microsoft_checkbox)
layout.addWidget(self.localacc_checkbox)
layout.addWidget(self.username_input)
layout.addWidget(self.data_checkbox)
button_layout = QHBoxLayout()
button_layout.addWidget(self.applytweaks_btn)
button_layout.addWidget(self.canceltweaks_btn)
layout.addLayout(button_layout)
layout.addStretch(1)
self.setLayout(layout)

def log_message(self, msg):
# delegate logging to parent window if available, otherwise use module logger
parent = self.parent()
if parent is not None and hasattr(parent, "log_message"):
parent.log_message(msg)
else:
get_logger("wintweaks").info(msg)

def update_winhardware(self):
# update winhardware req disable setting
states.win_hardware_bypass = 1 if self.hardware_checkbox.isChecked() else 0
self.log_message(
f"Windows hardware requirement disable: {'enabled' if self.hardware_checkbox.isChecked() else 'disabled'}"
)

def update_winmicrosoftacc(self):
# update microsoft acc disable setting
states.win_microsoft_acc = 1 if self.microsoft_checkbox.isChecked() else 0
self.log_message(
f"Microsoft account requirement disable: {'enabled' if self.microsoft_checkbox.isChecked() else 'disabled'}"
)

def update_winlocalaccchk(self):
# update local acc setting
states.win_local_acc_chk = 1 if self.localacc_checkbox.isChecked() else 0
self.log_message(
f"Windows Local Account Add: {'enabled' if self.localacc_checkbox.isChecked() else 'disabled'}"
)

def sync_username(self, new_username):
# changes local username
states.win_local_acc = new_username

def update_winprivacy(self):
# update win privacy setting
states.win_privacy = 1 if self.data_checkbox.isChecked() else 0
self.log_message(
f"Windows privacy questions disable: {'enabled' if self.data_checkbox.isChecked() else 'disabled'}"
)
# main tweaks apply logic function

def applywintweaks(self):
# closes window
self.accept()


# for debug
# if __name__ == "__main__":
# # Standard boilerplate for testing the class standalone
# app = QApplication(sys.argv)
# window = WinTweaks()
# window.exec()
# sys.exit(app.exec())
Loading
Loading