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.abstract_menu import AbstractSubMenu
from archinstall.lib.menu.helpers import Confirmation, Selection 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.lib.translationhandler import tr
from archinstall.tui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType from archinstall.tui.result import ResultType
@ -66,6 +66,13 @@ class BootloaderMenu(AbstractSubMenu[BootloaderConfiguration]):
key='removable', key='removable',
enabled=removable_enabled, 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: 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 /EFI/BOOT/ (removable location, safe default)')
return tr('Will install to custom location with NVRAM entry') 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 @override
async def show(self) -> BootloaderConfiguration: async def show(self) -> BootloaderConfiguration:
_ = await super().show() _ = await super().show()
@ -117,6 +129,9 @@ class BootloaderMenu(AbstractSubMenu[BootloaderConfiguration]):
return bootloader 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: async def _select_uki(self, preset: bool) -> bool:
prompt = tr('Would you like to use unified kernel images?') + '\n' prompt = tr('Would you like to use unified kernel images?') + '\n'
@ -215,3 +230,24 @@ async def select_bootloader(
return result.get_value() return result.get_value()
case ResultType.Reset: case ResultType.Reset:
raise ValueError('Unhandled result type') 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.log import debug, error, info, log, logger, warn
from archinstall.lib.mirror.mirror_handler import MirrorListHandler from archinstall.lib.mirror.mirror_handler import MirrorListHandler
from archinstall.lib.models.application import ZramAlgorithm 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 ( from archinstall.lib.models.device import (
DiskEncryption, DiskEncryption,
DiskLayoutConfiguration, DiskLayoutConfiguration,
@ -1755,6 +1755,28 @@ class Installer:
self._helper_flags['bootloader'] = 'refind' 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( def _config_uki(
self, self,
root: PartitionModification | LvmVolume, root: PartitionModification | LvmVolume,
@ -1807,10 +1829,7 @@ class Installer:
error('Error generating initramfs (continuing anyway)') error('Error generating initramfs (continuing anyway)')
def add_bootloader( def add_bootloader(
self, self, bootloader: Bootloader, uki_enabled: bool = False, bootloader_removable: bool = False, plymouth: PlymouthTheme | None = None
bootloader: Bootloader,
uki_enabled: bool = False,
bootloader_removable: bool = False,
) -> None: ) -> None:
""" """
Adds a bootloader to the installation instance. Adds a bootloader to the installation instance.
@ -1824,6 +1843,7 @@ class Installer:
:param bootloader: Type of bootloader to be added :param bootloader: Type of bootloader to be added
:param uki_enabled: Whether to use unified kernel images :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 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(): for plugin in plugins.values():
@ -1859,6 +1879,9 @@ class Installer:
warn(f'Bootloader {bootloader.value} lacks removable support; disabling.') warn(f'Bootloader {bootloader.value} lacks removable support; disabling.')
bootloader_removable = False bootloader_removable = False
if plymouth is not None:
self._install_plymouth(plymouth)
if uki_enabled: if uki_enabled:
keep_initramfs = ( keep_initramfs = (
bootloader == Bootloader.Grub bootloader == Bootloader.Grub

View File

@ -60,15 +60,48 @@ class Bootloader(Enum):
return cls(bootloader) 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 @dataclass
class BootloaderConfiguration(SubConfig): class BootloaderConfiguration(SubConfig):
bootloader: Bootloader bootloader: Bootloader
uki: bool = False uki: bool = False
removable: bool = True removable: bool = True
plymouth: PlymouthTheme | None = None
@override @override
def json(self) -> dict[str, Any]: 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 @override
def summary(self) -> list[str]: def summary(self) -> list[str]:
@ -78,6 +111,8 @@ class BootloaderConfiguration(SubConfig):
out.append(tr('UKI enabled')) out.append(tr('UKI enabled'))
if self.removable: if self.removable:
out.append(tr('Removable')) out.append(tr('Removable'))
if self.plymouth is not None:
out.append(tr('Plymouth "{}"').format(self.plymouth.value))
return out return out
@ -86,14 +121,16 @@ class BootloaderConfiguration(SubConfig):
bootloader = Bootloader.from_arg(config.get('bootloader', ''), skip_boot) bootloader = Bootloader.from_arg(config.get('bootloader', ''), skip_boot)
uki = config.get('uki', False) uki = config.get('uki', False)
removable = config.get('removable', True) 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 @classmethod
def get_default(cls, uefi: bool, skip_boot: bool = False) -> Self: def get_default(cls, uefi: bool, skip_boot: bool = False) -> Self:
bootloader = Bootloader.get_default(uefi, skip_boot) bootloader = Bootloader.get_default(uefi, skip_boot)
removable = uefi and bootloader.has_removable_support() removable = uefi and bootloader.has_removable_support()
uki = uefi and bootloader.has_uki_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: def preview(self, uefi: bool) -> str:
text = f'{tr("Bootloader")}: {self.bootloader.value}' text = f'{tr("Bootloader")}: {self.bootloader.value}'
@ -112,4 +149,7 @@ class BootloaderConfiguration(SubConfig):
removable_string = tr('Disabled') removable_string = tr('Disabled')
text += f'{tr("Removable")}: {removable_string}' text += f'{tr("Removable")}: {removable_string}'
text += '\n' text += '\n'
if self.plymouth is not None:
text += f'{tr("Plymouth")}: {self.plymouth.value}'
text += '\n'
return text return text

View File

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