From a31fd72f1e6491cb7c4556096288e0e2e7f3978a Mon Sep 17 00:00:00 2001 From: Softer Date: Sat, 6 Jun 2026 16:08:26 +0300 Subject: [PATCH 1/3] Add users to seat group when seatd is selected When a desktop profile uses seatd for seat access, the user must be in the seat group for the compositor to access input devices. Without this, sway/hyprland/niri/labwc fail to start after installation. --- archinstall/default_profiles/desktops/hyprland.py | 12 ++++++++++-- archinstall/default_profiles/desktops/labwc.py | 12 ++++++++++-- archinstall/default_profiles/desktops/niri.py | 12 ++++++++++-- archinstall/default_profiles/desktops/sway.py | 12 ++++++++++-- archinstall/default_profiles/desktops/utils.py | 15 +++++++++++++++ 5 files changed, 55 insertions(+), 8 deletions(-) diff --git a/archinstall/default_profiles/desktops/hyprland.py b/archinstall/default_profiles/desktops/hyprland.py index 0bf46b96db..7a84fcdaa6 100644 --- a/archinstall/default_profiles/desktops/hyprland.py +++ b/archinstall/default_profiles/desktops/hyprland.py @@ -1,8 +1,12 @@ -from typing import override +from typing import TYPE_CHECKING, override -from archinstall.default_profiles.desktops.utils import select_seat_access +from archinstall.default_profiles.desktops.utils import provision_seat_access, select_seat_access from archinstall.default_profiles.profile import CustomSetting, DisplayServerType, GreeterType, Profile, ProfileType +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + from archinstall.lib.models.users import User + class HyprlandProfile(Profile): def __init__(self) -> None: @@ -45,6 +49,10 @@ def services(self) -> list[str]: return [pref] return [] + @override + def provision(self, install_session: Installer, users: list[User]) -> None: + provision_seat_access(install_session, users, self.custom_settings.get(CustomSetting.SeatAccess)) + @override async def do_on_select(self) -> None: default = self.custom_settings.get(CustomSetting.SeatAccess, None) diff --git a/archinstall/default_profiles/desktops/labwc.py b/archinstall/default_profiles/desktops/labwc.py index 48fe344e13..1a0fa8b22c 100644 --- a/archinstall/default_profiles/desktops/labwc.py +++ b/archinstall/default_profiles/desktops/labwc.py @@ -1,8 +1,12 @@ -from typing import override +from typing import TYPE_CHECKING, override -from archinstall.default_profiles.desktops.utils import select_seat_access +from archinstall.default_profiles.desktops.utils import provision_seat_access, select_seat_access from archinstall.default_profiles.profile import CustomSetting, DisplayServerType, GreeterType, Profile, ProfileType +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + from archinstall.lib.models.users import User + class LabwcProfile(Profile): def __init__(self) -> None: @@ -39,6 +43,10 @@ def services(self) -> list[str]: return [pref] return [] + @override + def provision(self, install_session: Installer, users: list[User]) -> None: + provision_seat_access(install_session, users, self.custom_settings.get(CustomSetting.SeatAccess)) + @override async def do_on_select(self) -> None: default = self.custom_settings.get(CustomSetting.SeatAccess, None) diff --git a/archinstall/default_profiles/desktops/niri.py b/archinstall/default_profiles/desktops/niri.py index d8db75f5da..bc08d327ce 100644 --- a/archinstall/default_profiles/desktops/niri.py +++ b/archinstall/default_profiles/desktops/niri.py @@ -1,8 +1,12 @@ -from typing import override +from typing import TYPE_CHECKING, override -from archinstall.default_profiles.desktops.utils import select_seat_access +from archinstall.default_profiles.desktops.utils import provision_seat_access, select_seat_access from archinstall.default_profiles.profile import CustomSetting, DisplayServerType, GreeterType, Profile, ProfileType +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + from archinstall.lib.models.users import User + class NiriProfile(Profile): def __init__(self) -> None: @@ -47,6 +51,10 @@ def services(self) -> list[str]: return [pref] return [] + @override + def provision(self, install_session: Installer, users: list[User]) -> None: + provision_seat_access(install_session, users, self.custom_settings.get(CustomSetting.SeatAccess)) + @override async def do_on_select(self) -> None: default = self.custom_settings.get(CustomSetting.SeatAccess, None) diff --git a/archinstall/default_profiles/desktops/sway.py b/archinstall/default_profiles/desktops/sway.py index 7a038dc0ba..161b8ee17f 100644 --- a/archinstall/default_profiles/desktops/sway.py +++ b/archinstall/default_profiles/desktops/sway.py @@ -1,8 +1,12 @@ -from typing import override +from typing import TYPE_CHECKING, override -from archinstall.default_profiles.desktops.utils import select_seat_access +from archinstall.default_profiles.desktops.utils import provision_seat_access, select_seat_access from archinstall.default_profiles.profile import CustomSetting, DisplayServerType, GreeterType, Profile, ProfileType +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + from archinstall.lib.models.users import User + class SwayProfile(Profile): def __init__(self) -> None: @@ -49,6 +53,10 @@ def services(self) -> list[str]: return [pref] return [] + @override + def provision(self, install_session: Installer, users: list[User]) -> None: + provision_seat_access(install_session, users, self.custom_settings.get(CustomSetting.SeatAccess)) + @override async def do_on_select(self) -> None: default = self.custom_settings.get(CustomSetting.SeatAccess, None) diff --git a/archinstall/default_profiles/desktops/utils.py b/archinstall/default_profiles/desktops/utils.py index 03e85aa567..a28320b3cb 100644 --- a/archinstall/default_profiles/desktops/utils.py +++ b/archinstall/default_profiles/desktops/utils.py @@ -1,16 +1,31 @@ from enum import Enum +from typing import TYPE_CHECKING from archinstall.lib.menu.helpers import Selection from archinstall.lib.translationhandler import tr from archinstall.tui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.result import ResultType +if TYPE_CHECKING: + from archinstall.lib.installer import Installer + from archinstall.lib.models.users import User + class SeatAccess(Enum): seatd = 'seatd' polkit = 'polkit' +def provision_seat_access( + install_session: Installer, + users: list[User], + seat_access: str | None, +) -> None: + if seat_access == SeatAccess.seatd.value: + for user in users: + install_session.arch_chroot(f'usermod -a -G seat {user.username}') + + async def select_seat_access(profile_name: str, default: str | None) -> SeatAccess: header = tr('{} needs access to your seat').format(profile_name) header += f' ({tr("collection of hardware devices i.e. keyboard, mouse")})' + '\n' From 2d175312d9cda24bacfc0a336bff772ce3b6ab6d Mon Sep 17 00:00:00 2001 From: Softer Date: Mon, 8 Jun 2026 13:16:16 +0300 Subject: [PATCH 2/3] Move seat access provisioning into DesktopProfile.provision() The four Wayland profiles (Hyprland, Sway, niri, labwc) each duplicated a provision() override calling provision_seat_access(). Move that into the base DesktopProfile.provision() loop, which already iterates the selected profiles, and read CustomSetting.SeatAccess from each. The None check now lives at the call site (walrus), so provision_seat_access() takes a plain str. This removes the per-profile overrides and their TYPE_CHECKING imports. --- archinstall/default_profiles/desktop.py | 6 +++++- archinstall/default_profiles/desktops/hyprland.py | 12 ++---------- archinstall/default_profiles/desktops/labwc.py | 12 ++---------- archinstall/default_profiles/desktops/niri.py | 12 ++---------- archinstall/default_profiles/desktops/sway.py | 12 ++---------- archinstall/default_profiles/desktops/utils.py | 2 +- 6 files changed, 14 insertions(+), 42 deletions(-) diff --git a/archinstall/default_profiles/desktop.py b/archinstall/default_profiles/desktop.py index b70f9f11b2..f919535742 100644 --- a/archinstall/default_profiles/desktop.py +++ b/archinstall/default_profiles/desktop.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING, Self, override -from archinstall.default_profiles.profile import DisplayServerType, GreeterType, Profile, ProfileType, SelectResult +from archinstall.default_profiles.desktops.utils import provision_seat_access +from archinstall.default_profiles.profile import CustomSetting, DisplayServerType, GreeterType, Profile, ProfileType, SelectResult from archinstall.lib.log import info from archinstall.lib.menu.helpers import Selection from archinstall.lib.profile.profiles_handler import profile_handler @@ -94,6 +95,9 @@ def provision(self, install_session: Installer, users: list[User]) -> None: for profile in self.current_selection: profile.provision(install_session, users) + if seat_access := profile.custom_settings.get(CustomSetting.SeatAccess): + provision_seat_access(install_session, users, seat_access) + @override def install(self, install_session: Installer) -> None: # Install common packages for all desktop environments diff --git a/archinstall/default_profiles/desktops/hyprland.py b/archinstall/default_profiles/desktops/hyprland.py index 7a84fcdaa6..0bf46b96db 100644 --- a/archinstall/default_profiles/desktops/hyprland.py +++ b/archinstall/default_profiles/desktops/hyprland.py @@ -1,12 +1,8 @@ -from typing import TYPE_CHECKING, override +from typing import override -from archinstall.default_profiles.desktops.utils import provision_seat_access, select_seat_access +from archinstall.default_profiles.desktops.utils import select_seat_access from archinstall.default_profiles.profile import CustomSetting, DisplayServerType, GreeterType, Profile, ProfileType -if TYPE_CHECKING: - from archinstall.lib.installer import Installer - from archinstall.lib.models.users import User - class HyprlandProfile(Profile): def __init__(self) -> None: @@ -49,10 +45,6 @@ def services(self) -> list[str]: return [pref] return [] - @override - def provision(self, install_session: Installer, users: list[User]) -> None: - provision_seat_access(install_session, users, self.custom_settings.get(CustomSetting.SeatAccess)) - @override async def do_on_select(self) -> None: default = self.custom_settings.get(CustomSetting.SeatAccess, None) diff --git a/archinstall/default_profiles/desktops/labwc.py b/archinstall/default_profiles/desktops/labwc.py index 1a0fa8b22c..48fe344e13 100644 --- a/archinstall/default_profiles/desktops/labwc.py +++ b/archinstall/default_profiles/desktops/labwc.py @@ -1,12 +1,8 @@ -from typing import TYPE_CHECKING, override +from typing import override -from archinstall.default_profiles.desktops.utils import provision_seat_access, select_seat_access +from archinstall.default_profiles.desktops.utils import select_seat_access from archinstall.default_profiles.profile import CustomSetting, DisplayServerType, GreeterType, Profile, ProfileType -if TYPE_CHECKING: - from archinstall.lib.installer import Installer - from archinstall.lib.models.users import User - class LabwcProfile(Profile): def __init__(self) -> None: @@ -43,10 +39,6 @@ def services(self) -> list[str]: return [pref] return [] - @override - def provision(self, install_session: Installer, users: list[User]) -> None: - provision_seat_access(install_session, users, self.custom_settings.get(CustomSetting.SeatAccess)) - @override async def do_on_select(self) -> None: default = self.custom_settings.get(CustomSetting.SeatAccess, None) diff --git a/archinstall/default_profiles/desktops/niri.py b/archinstall/default_profiles/desktops/niri.py index bc08d327ce..d8db75f5da 100644 --- a/archinstall/default_profiles/desktops/niri.py +++ b/archinstall/default_profiles/desktops/niri.py @@ -1,12 +1,8 @@ -from typing import TYPE_CHECKING, override +from typing import override -from archinstall.default_profiles.desktops.utils import provision_seat_access, select_seat_access +from archinstall.default_profiles.desktops.utils import select_seat_access from archinstall.default_profiles.profile import CustomSetting, DisplayServerType, GreeterType, Profile, ProfileType -if TYPE_CHECKING: - from archinstall.lib.installer import Installer - from archinstall.lib.models.users import User - class NiriProfile(Profile): def __init__(self) -> None: @@ -51,10 +47,6 @@ def services(self) -> list[str]: return [pref] return [] - @override - def provision(self, install_session: Installer, users: list[User]) -> None: - provision_seat_access(install_session, users, self.custom_settings.get(CustomSetting.SeatAccess)) - @override async def do_on_select(self) -> None: default = self.custom_settings.get(CustomSetting.SeatAccess, None) diff --git a/archinstall/default_profiles/desktops/sway.py b/archinstall/default_profiles/desktops/sway.py index 161b8ee17f..7a038dc0ba 100644 --- a/archinstall/default_profiles/desktops/sway.py +++ b/archinstall/default_profiles/desktops/sway.py @@ -1,12 +1,8 @@ -from typing import TYPE_CHECKING, override +from typing import override -from archinstall.default_profiles.desktops.utils import provision_seat_access, select_seat_access +from archinstall.default_profiles.desktops.utils import select_seat_access from archinstall.default_profiles.profile import CustomSetting, DisplayServerType, GreeterType, Profile, ProfileType -if TYPE_CHECKING: - from archinstall.lib.installer import Installer - from archinstall.lib.models.users import User - class SwayProfile(Profile): def __init__(self) -> None: @@ -53,10 +49,6 @@ def services(self) -> list[str]: return [pref] return [] - @override - def provision(self, install_session: Installer, users: list[User]) -> None: - provision_seat_access(install_session, users, self.custom_settings.get(CustomSetting.SeatAccess)) - @override async def do_on_select(self) -> None: default = self.custom_settings.get(CustomSetting.SeatAccess, None) diff --git a/archinstall/default_profiles/desktops/utils.py b/archinstall/default_profiles/desktops/utils.py index a28320b3cb..261151b471 100644 --- a/archinstall/default_profiles/desktops/utils.py +++ b/archinstall/default_profiles/desktops/utils.py @@ -19,7 +19,7 @@ class SeatAccess(Enum): def provision_seat_access( install_session: Installer, users: list[User], - seat_access: str | None, + seat_access: str, ) -> None: if seat_access == SeatAccess.seatd.value: for user in users: From 1bfea415c9a80164f79987dac69a249e602d94dd Mon Sep 17 00:00:00 2001 From: Softer Date: Tue, 9 Jun 2026 11:52:15 +0300 Subject: [PATCH 3/3] Use top-level imports in seat access utils The Installer and User imports were under a TYPE_CHECKING guard, but they cause no circular import (bspwm.py already imports both at top level). Move them to module scope per review feedback. --- archinstall/default_profiles/desktops/utils.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/archinstall/default_profiles/desktops/utils.py b/archinstall/default_profiles/desktops/utils.py index 261151b471..b179ad5061 100644 --- a/archinstall/default_profiles/desktops/utils.py +++ b/archinstall/default_profiles/desktops/utils.py @@ -1,15 +1,12 @@ from enum import Enum -from typing import TYPE_CHECKING +from archinstall.lib.installer import Installer from archinstall.lib.menu.helpers import Selection +from archinstall.lib.models.users import User from archinstall.lib.translationhandler import tr from archinstall.tui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.result import ResultType -if TYPE_CHECKING: - from archinstall.lib.installer import Installer - from archinstall.lib.models.users import User - class SeatAccess(Enum): seatd = 'seatd'