Add Plymouth configuration setup (#4555)

* feat: add plymouth config

* refactor: move plymouth to bootleader menu
This commit is contained in:
Mário Victor Ribeiro Silva 2026-06-10 03:44:03 -03:00 committed by GitHub
parent c6cb7b319d
commit 73d78b1aa9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 109 additions and 9 deletions

View File

@ -3,7 +3,7 @@ from typing import override
from archinstall.lib.menu.abstract_menu import AbstractSubMenu
from archinstall.lib.menu.helpers import Confirmation, Selection
from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration
from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration, PlymouthTheme
from archinstall.lib.translationhandler import tr
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
@ -66,6 +66,13 @@ class BootloaderMenu(AbstractSubMenu[BootloaderConfiguration]):
key='removable',
enabled=removable_enabled,
),
MenuItem(
text=tr('Plymouth'),
action=self._select_plymouth,
value=self._bootloader_conf.plymouth,
preview_action=self._prev_plymouth,
key='plymouth',
),
]
def _prev_bootloader(self, item: MenuItem) -> str | None:
@ -85,6 +92,11 @@ class BootloaderMenu(AbstractSubMenu[BootloaderConfiguration]):
return tr('Will install to /EFI/BOOT/ (removable location, safe default)')
return tr('Will install to custom location with NVRAM entry')
def _prev_plymouth(self, item: MenuItem) -> str | None:
if item.value:
return f'{tr("Plymouth")}: {item.value.value}'
return None
@override
async def show(self) -> BootloaderConfiguration:
_ = await super().show()
@ -117,6 +129,9 @@ class BootloaderMenu(AbstractSubMenu[BootloaderConfiguration]):
return bootloader
async def _select_plymouth(self, preset: PlymouthTheme | None) -> PlymouthTheme | None:
return await select_plymouth_theme(preset)
async def _select_uki(self, preset: bool) -> bool:
prompt = tr('Would you like to use unified kernel images?') + '\n'
@ -215,3 +230,24 @@ async def select_bootloader(
return result.get_value()
case ResultType.Reset:
raise ValueError('Unhandled result type')
async def select_plymouth_theme(preset: PlymouthTheme | None = None) -> PlymouthTheme | None:
items = [MenuItem(t.value, value=t) for t in PlymouthTheme]
group = MenuItemGroup(items, sort_items=False)
group.set_selected_by_value(preset.value if preset else None)
result = await Selection[PlymouthTheme](
group,
header=tr('Select Plymouth theme'),
allow_reset=True,
allow_skip=True,
).show()
match result.type_:
case ResultType.Skip:
return preset
case ResultType.Reset:
return None
case ResultType.Selection:
return PlymouthTheme(result.get_value())

View File

@ -32,7 +32,7 @@ from archinstall.lib.locale.utils import verify_keyboard_layout, verify_x11_keyb
from archinstall.lib.log import debug, error, info, log, logger, warn
from archinstall.lib.mirror.mirror_handler import MirrorListHandler
from archinstall.lib.models.application import ZramAlgorithm
from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration
from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration, PlymouthTheme
from archinstall.lib.models.device import (
DiskEncryption,
DiskLayoutConfiguration,
@ -1755,6 +1755,28 @@ class Installer:
self._helper_flags['bootloader'] = 'refind'
def _install_plymouth(self, plymouth: PlymouthTheme) -> None:
debug(f'Installing plymouth with theme: {plymouth.value}')
self.add_additional_packages(['plymouth'])
for param in ('quiet', 'splash'):
if param not in self._kernel_params:
self._kernel_params.append(param)
if 'plymouth' not in self._hooks:
for hook, insert_after in [('encrypt', False), ('sd-encrypt', False), ('systemd', True), ('filesystems', False), ('keyboard', True)]:
try:
idx = self._hooks.index(hook)
self._hooks.insert(idx + (1 if insert_after else 0), 'plymouth')
break
except ValueError:
continue
else:
self._hooks.append('plymouth')
self.arch_chroot(f'plymouth-set-default-theme {plymouth.value}')
self.mkinitcpio(['-P'])
def _config_uki(
self,
root: PartitionModification | LvmVolume,
@ -1807,10 +1829,7 @@ class Installer:
error('Error generating initramfs (continuing anyway)')
def add_bootloader(
self,
bootloader: Bootloader,
uki_enabled: bool = False,
bootloader_removable: bool = False,
self, bootloader: Bootloader, uki_enabled: bool = False, bootloader_removable: bool = False, plymouth: PlymouthTheme | None = None
) -> None:
"""
Adds a bootloader to the installation instance.
@ -1824,6 +1843,7 @@ class Installer:
:param bootloader: Type of bootloader to be added
:param uki_enabled: Whether to use unified kernel images
:param bootloader_removable: Whether to install to removable media location (UEFI only, for GRUB and Limine)
:param plymouth: Optional Plymouth theme to install and configure
"""
for plugin in plugins.values():
@ -1859,6 +1879,9 @@ class Installer:
warn(f'Bootloader {bootloader.value} lacks removable support; disabling.')
bootloader_removable = False
if plymouth is not None:
self._install_plymouth(plymouth)
if uki_enabled:
keep_initramfs = (
bootloader == Bootloader.Grub

View File

@ -60,15 +60,48 @@ class Bootloader(Enum):
return cls(bootloader)
class PlymouthTheme(Enum):
BGRT = 'bgrt'
FADE = 'fade-in'
GLOW = 'glow'
SCRIPT = 'script'
SOLAR = 'solar'
SPINNER = 'spinner'
SPINFINITY = 'spinfinity'
TRIBAR = 'tribar'
TEXT = 'text'
DETAILS = 'details'
@classmethod
def from_arg(cls, plymouth: str | None) -> Self | None:
if plymouth is None:
return None
plymouth = plymouth.lower()
values = [e.value for e in cls]
if plymouth not in values:
warn(f'Invalid plymouth value "{plymouth}". Allowed values: {", ".join(values)}')
sys.exit(1)
return cls(plymouth)
@dataclass
class BootloaderConfiguration(SubConfig):
bootloader: Bootloader
uki: bool = False
removable: bool = True
plymouth: PlymouthTheme | None = None
@override
def json(self) -> dict[str, Any]:
return {'bootloader': self.bootloader.json(), 'uki': self.uki, 'removable': self.removable}
data = {'bootloader': self.bootloader.json(), 'uki': self.uki, 'removable': self.removable}
if self.plymouth is not None:
data['plymouth'] = self.plymouth.value
return data
@override
def summary(self) -> list[str]:
@ -78,6 +111,8 @@ class BootloaderConfiguration(SubConfig):
out.append(tr('UKI enabled'))
if self.removable:
out.append(tr('Removable'))
if self.plymouth is not None:
out.append(tr('Plymouth "{}"').format(self.plymouth.value))
return out
@ -86,14 +121,16 @@ class BootloaderConfiguration(SubConfig):
bootloader = Bootloader.from_arg(config.get('bootloader', ''), skip_boot)
uki = config.get('uki', False)
removable = config.get('removable', True)
return cls(bootloader=bootloader, uki=uki, removable=removable)
plymouth = PlymouthTheme.from_arg(config.get('plymouth', None))
return cls(bootloader=bootloader, uki=uki, removable=removable, plymouth=plymouth)
@classmethod
def get_default(cls, uefi: bool, skip_boot: bool = False) -> Self:
bootloader = Bootloader.get_default(uefi, skip_boot)
removable = uefi and bootloader.has_removable_support()
uki = uefi and bootloader.has_uki_support()
return cls(bootloader=bootloader, uki=uki, removable=removable)
plymouth = None
return cls(bootloader=bootloader, uki=uki, removable=removable, plymouth=plymouth)
def preview(self, uefi: bool) -> str:
text = f'{tr("Bootloader")}: {self.bootloader.value}'
@ -112,4 +149,7 @@ class BootloaderConfiguration(SubConfig):
removable_string = tr('Disabled')
text += f'{tr("Removable")}: {removable_string}'
text += '\n'
if self.plymouth is not None:
text += f'{tr("Plymouth")}: {self.plymouth.value}'
text += '\n'
return text

View File

@ -119,6 +119,7 @@ def perform_installation(
config.bootloader_config.bootloader,
config.bootloader_config.uki,
config.bootloader_config.removable,
config.bootloader_config.plymouth,
)
if config.network_config: