mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-12 00:32:17 -04:00
dms-greeter: Update dankinstall greeter automation w/distro packages
This commit is contained in:
@@ -18,7 +18,7 @@ func init() {
|
|||||||
runCmd.Flags().MarkHidden("daemon-child")
|
runCmd.Flags().MarkHidden("daemon-child")
|
||||||
|
|
||||||
// Add subcommands to greeter
|
// Add subcommands to greeter
|
||||||
greeterCmd.AddCommand(greeterSyncCmd, greeterEnableCmd, greeterStatusCmd)
|
greeterCmd.AddCommand(greeterInstallCmd, greeterSyncCmd, greeterEnableCmd, greeterStatusCmd)
|
||||||
|
|
||||||
// Add subcommands to setup
|
// Add subcommands to setup
|
||||||
setupCmd.AddCommand(setupBindsCmd, setupLayoutCmd, setupColorsCmd, setupAlttabCmd, setupOutputsCmd, setupCursorCmd, setupWindowrulesCmd)
|
setupCmd.AddCommand(setupBindsCmd, setupLayoutCmd, setupColorsCmd, setupAlttabCmd, setupOutputsCmd, setupCursorCmd, setupWindowrulesCmd)
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ func (a *ArchDistribution) detectAccountsService() deps.Dependency {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *ArchDistribution) detectDMSGreeter() deps.Dependency {
|
func (a *ArchDistribution) detectDMSGreeter() deps.Dependency {
|
||||||
return a.detectPackage("dms-greeter", "DankMaterialShell greetd greeter", a.packageInstalled("greetd-dms-greeter-git"))
|
return a.detectOptionalPackage("dms-greeter", "DankMaterialShell greetd greeter", a.packageInstalled("greetd-dms-greeter-git"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *ArchDistribution) packageInstalled(pkg string) bool {
|
func (a *ArchDistribution) packageInstalled(pkg string) bool {
|
||||||
|
|||||||
@@ -102,6 +102,19 @@ func (b *BaseDistribution) detectPackage(name, description string, installed boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BaseDistribution) detectOptionalPackage(name, description string, installed bool) deps.Dependency {
|
||||||
|
status := deps.StatusMissing
|
||||||
|
if installed {
|
||||||
|
status = deps.StatusInstalled
|
||||||
|
}
|
||||||
|
return deps.Dependency{
|
||||||
|
Name: name,
|
||||||
|
Status: status,
|
||||||
|
Description: description,
|
||||||
|
Required: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (b *BaseDistribution) detectGit() deps.Dependency {
|
func (b *BaseDistribution) detectGit() deps.Dependency {
|
||||||
return b.detectCommand("git", "Version control system")
|
return b.detectCommand("git", "Version control system")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ func (d *DebianDistribution) detectAccountsService() deps.Dependency {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DebianDistribution) detectDMSGreeter() deps.Dependency {
|
func (d *DebianDistribution) detectDMSGreeter() deps.Dependency {
|
||||||
return d.detectPackage("dms-greeter", "DankMaterialShell greetd greeter", d.packageInstalled("dms-greeter"))
|
return d.detectOptionalPackage("dms-greeter", "DankMaterialShell greetd greeter", d.packageInstalled("dms-greeter"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DebianDistribution) packageInstalled(pkg string) bool {
|
func (d *DebianDistribution) packageInstalled(pkg string) bool {
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ func (f *FedoraDistribution) detectAccountsService() deps.Dependency {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *FedoraDistribution) detectDMSGreeter() deps.Dependency {
|
func (f *FedoraDistribution) detectDMSGreeter() deps.Dependency {
|
||||||
return f.detectPackage("dms-greeter", "DankMaterialShell greetd greeter", f.packageInstalled("dms-greeter"))
|
return f.detectOptionalPackage("dms-greeter", "DankMaterialShell greetd greeter", f.packageInstalled("dms-greeter"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FedoraDistribution) getPrerequisites() []string {
|
func (f *FedoraDistribution) getPrerequisites() []string {
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ const (
|
|||||||
PhaseAURPackages
|
PhaseAURPackages
|
||||||
PhaseCursorTheme
|
PhaseCursorTheme
|
||||||
PhaseConfiguration
|
PhaseConfiguration
|
||||||
|
PhaseGreeterSetup
|
||||||
PhaseComplete
|
PhaseComplete
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ func (o *OpenSUSEDistribution) packageInstalled(pkg string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *OpenSUSEDistribution) detectDMSGreeter() deps.Dependency {
|
func (o *OpenSUSEDistribution) detectDMSGreeter() deps.Dependency {
|
||||||
return o.detectPackage("dms-greeter", "DankMaterialShell greetd greeter", o.packageInstalled("dms-greeter"))
|
return o.detectOptionalPackage("dms-greeter", "DankMaterialShell greetd greeter", o.packageInstalled("dms-greeter"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OpenSUSEDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping {
|
func (o *OpenSUSEDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping {
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ func (u *UbuntuDistribution) detectAccountsService() deps.Dependency {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *UbuntuDistribution) detectDMSGreeter() deps.Dependency {
|
func (u *UbuntuDistribution) detectDMSGreeter() deps.Dependency {
|
||||||
return u.detectPackage("dms-greeter", "DankMaterialShell greetd greeter", u.packageInstalled("dms-greeter"))
|
return u.detectOptionalPackage("dms-greeter", "DankMaterialShell greetd greeter", u.packageInstalled("dms-greeter"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UbuntuDistribution) packageInstalled(pkg string) bool {
|
func (u *UbuntuDistribution) packageInstalled(pkg string) bool {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import (
|
|||||||
"github.com/sblinch/kdl-go/document"
|
"github.com/sblinch/kdl-go/document"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DetectDMSPath checks for DMS installation following XDG Base Directory specification
|
|
||||||
func DetectDMSPath() (string, error) {
|
func DetectDMSPath() (string, error) {
|
||||||
return config.LocateDMSConfig()
|
return config.LocateDMSConfig()
|
||||||
}
|
}
|
||||||
@@ -37,7 +36,6 @@ func DetectGreeterGroup() string {
|
|||||||
return "greeter"
|
return "greeter"
|
||||||
}
|
}
|
||||||
|
|
||||||
// DetectCompositors checks which compositors are installed
|
|
||||||
func DetectCompositors() []string {
|
func DetectCompositors() []string {
|
||||||
var compositors []string
|
var compositors []string
|
||||||
|
|
||||||
@@ -51,7 +49,6 @@ func DetectCompositors() []string {
|
|||||||
return compositors
|
return compositors
|
||||||
}
|
}
|
||||||
|
|
||||||
// PromptCompositorChoice asks user to choose between compositors
|
|
||||||
func PromptCompositorChoice(compositors []string) (string, error) {
|
func PromptCompositorChoice(compositors []string) (string, error) {
|
||||||
fmt.Println("\nMultiple compositors detected:")
|
fmt.Println("\nMultiple compositors detected:")
|
||||||
for i, comp := range compositors {
|
for i, comp := range compositors {
|
||||||
@@ -292,11 +289,9 @@ func TryInstallGreeterPackage(logFunc func(string), sudoPassword string) bool {
|
|||||||
|
|
||||||
// CopyGreeterFiles installs the dms-greeter wrapper and sets up cache directory
|
// CopyGreeterFiles installs the dms-greeter wrapper and sets up cache directory
|
||||||
func CopyGreeterFiles(dmsPath, compositor string, logFunc func(string), sudoPassword string) error {
|
func CopyGreeterFiles(dmsPath, compositor string, logFunc func(string), sudoPassword string) error {
|
||||||
// Check if dms-greeter is already in PATH
|
|
||||||
if utils.CommandExists("dms-greeter") {
|
if utils.CommandExists("dms-greeter") {
|
||||||
logFunc("✓ dms-greeter wrapper already installed")
|
logFunc("✓ dms-greeter wrapper already installed")
|
||||||
} else {
|
} else {
|
||||||
// Install the wrapper script
|
|
||||||
assetsDir := filepath.Join(dmsPath, "Modules", "Greetd", "assets")
|
assetsDir := filepath.Join(dmsPath, "Modules", "Greetd", "assets")
|
||||||
wrapperSrc := filepath.Join(assetsDir, "dms-greeter")
|
wrapperSrc := filepath.Join(assetsDir, "dms-greeter")
|
||||||
|
|
||||||
@@ -333,7 +328,6 @@ func CopyGreeterFiles(dmsPath, compositor string, logFunc func(string), sudoPass
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create cache directory with proper permissions
|
|
||||||
cacheDir := "/var/cache/dms-greeter"
|
cacheDir := "/var/cache/dms-greeter"
|
||||||
if err := runSudoCmd(sudoPassword, "mkdir", "-p", cacheDir); err != nil {
|
if err := runSudoCmd(sudoPassword, "mkdir", "-p", cacheDir); err != nil {
|
||||||
return fmt.Errorf("failed to create cache directory: %w", err)
|
return fmt.Errorf("failed to create cache directory: %w", err)
|
||||||
@@ -354,14 +348,90 @@ func CopyGreeterFiles(dmsPath, compositor string, logFunc func(string), sudoPass
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnsureACLInstalled installs the acl package (setfacl/getfacl) if not already present
|
||||||
|
func EnsureACLInstalled(logFunc func(string), sudoPassword string) error {
|
||||||
|
if utils.CommandExists("setfacl") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logFunc("setfacl not found – installing acl package...")
|
||||||
|
|
||||||
|
osInfo, err := distros.GetOSInfo()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect OS: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config, exists := distros.Registry[osInfo.Distribution.ID]
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("unsupported distribution for automatic acl installation: %s", osInfo.Distribution.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
var installCmd *exec.Cmd
|
||||||
|
|
||||||
|
switch config.Family {
|
||||||
|
case distros.FamilyArch:
|
||||||
|
if sudoPassword != "" {
|
||||||
|
installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "pacman -S --needed --noconfirm acl")
|
||||||
|
} else {
|
||||||
|
installCmd = exec.CommandContext(ctx, "sudo", "pacman", "-S", "--needed", "--noconfirm", "acl")
|
||||||
|
}
|
||||||
|
|
||||||
|
case distros.FamilyFedora:
|
||||||
|
if sudoPassword != "" {
|
||||||
|
installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "dnf install -y acl")
|
||||||
|
} else {
|
||||||
|
installCmd = exec.CommandContext(ctx, "sudo", "dnf", "install", "-y", "acl")
|
||||||
|
}
|
||||||
|
|
||||||
|
case distros.FamilySUSE:
|
||||||
|
if sudoPassword != "" {
|
||||||
|
installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "zypper install -y acl")
|
||||||
|
} else {
|
||||||
|
installCmd = exec.CommandContext(ctx, "sudo", "zypper", "install", "-y", "acl")
|
||||||
|
}
|
||||||
|
|
||||||
|
case distros.FamilyUbuntu, distros.FamilyDebian:
|
||||||
|
if sudoPassword != "" {
|
||||||
|
installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "apt-get install -y acl")
|
||||||
|
} else {
|
||||||
|
installCmd = exec.CommandContext(ctx, "sudo", "apt-get", "install", "-y", "acl")
|
||||||
|
}
|
||||||
|
|
||||||
|
case distros.FamilyGentoo:
|
||||||
|
if sudoPassword != "" {
|
||||||
|
installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "emerge --ask n sys-fs/acl")
|
||||||
|
} else {
|
||||||
|
installCmd = exec.CommandContext(ctx, "sudo", "emerge", "--ask", "n", "sys-fs/acl")
|
||||||
|
}
|
||||||
|
|
||||||
|
case distros.FamilyNix:
|
||||||
|
return fmt.Errorf("on NixOS, please add pkgs.acl to your configuration.nix")
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported distribution family for automatic acl installation: %s", config.Family)
|
||||||
|
}
|
||||||
|
|
||||||
|
installCmd.Stdout = os.Stdout
|
||||||
|
installCmd.Stderr = os.Stderr
|
||||||
|
if err := installCmd.Run(); err != nil {
|
||||||
|
return fmt.Errorf("failed to install acl: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logFunc("✓ acl package installed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetupParentDirectoryACLs sets ACLs on parent directories to allow traversal
|
// SetupParentDirectoryACLs sets ACLs on parent directories to allow traversal
|
||||||
func SetupParentDirectoryACLs(logFunc func(string), sudoPassword string) error {
|
func SetupParentDirectoryACLs(logFunc func(string), sudoPassword string) error {
|
||||||
|
if err := EnsureACLInstalled(logFunc, sudoPassword); err != nil {
|
||||||
|
logFunc(fmt.Sprintf("⚠ Warning: could not install acl package: %v", err))
|
||||||
|
logFunc(" ACL permissions will be skipped; theme sync may not work correctly.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if !utils.CommandExists("setfacl") {
|
if !utils.CommandExists("setfacl") {
|
||||||
logFunc("⚠ Warning: setfacl command not found. ACL support may not be available on this filesystem.")
|
// setfacl still not found after install attempt (e.g. unsupported filesystem)
|
||||||
logFunc(" If theme sync doesn't work, you may need to install acl package:")
|
logFunc("⚠ Warning: setfacl still not available after install attempt; skipping ACL setup.")
|
||||||
logFunc(" - Fedora/RHEL: sudo dnf install acl")
|
|
||||||
logFunc(" - Debian/Ubuntu: sudo apt-get install acl")
|
|
||||||
logFunc(" - Arch: sudo pacman -S acl")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,7 +464,6 @@ func SetupParentDirectoryACLs(logFunc func(string), sudoPassword string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set ACL to allow greeter user read+execute permission (for session discovery)
|
|
||||||
if err := runSudoCmd(sudoPassword, "setfacl", "-m", fmt.Sprintf("u:%s:rx", owner), dir.path); err != nil {
|
if err := runSudoCmd(sudoPassword, "setfacl", "-m", fmt.Sprintf("u:%s:rx", owner), dir.path); err != nil {
|
||||||
logFunc(fmt.Sprintf("⚠ Warning: Failed to set ACL on %s: %v", dir.desc, err))
|
logFunc(fmt.Sprintf("⚠ Warning: Failed to set ACL on %s: %v", dir.desc, err))
|
||||||
logFunc(fmt.Sprintf(" You may need to run manually: setfacl -m u:%s:x %s", owner, dir.path))
|
logFunc(fmt.Sprintf(" You may need to run manually: setfacl -m u:%s:x %s", owner, dir.path))
|
||||||
@@ -429,7 +498,6 @@ func SetupDMSGroup(logFunc func(string), sudoPassword string) error {
|
|||||||
if err == nil && strings.Contains(string(groupsOutput), group) {
|
if err == nil && strings.Contains(string(groupsOutput), group) {
|
||||||
logFunc(fmt.Sprintf("✓ %s is already in %s group", currentUser, group))
|
logFunc(fmt.Sprintf("✓ %s is already in %s group", currentUser, group))
|
||||||
} else {
|
} else {
|
||||||
// Add current user to greeter group for file access permissions
|
|
||||||
if err := runSudoCmd(sudoPassword, "usermod", "-aG", group, currentUser); err != nil {
|
if err := runSudoCmd(sudoPassword, "usermod", "-aG", group, currentUser); err != nil {
|
||||||
return fmt.Errorf("failed to add %s to %s group: %w", currentUser, group, err)
|
return fmt.Errorf("failed to add %s to %s group: %w", currentUser, group, err)
|
||||||
}
|
}
|
||||||
@@ -469,7 +537,6 @@ func SetupDMSGroup(logFunc func(string), sudoPassword string) error {
|
|||||||
logFunc(fmt.Sprintf("✓ Set group permissions for %s", dir.desc))
|
logFunc(fmt.Sprintf("✓ Set group permissions for %s", dir.desc))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up ACLs on parent directories to allow greeter user traversal
|
|
||||||
if err := SetupParentDirectoryACLs(logFunc, sudoPassword); err != nil {
|
if err := SetupParentDirectoryACLs(logFunc, sudoPassword); err != nil {
|
||||||
return fmt.Errorf("failed to setup parent directory ACLs: %w", err)
|
return fmt.Errorf("failed to setup parent directory ACLs: %w", err)
|
||||||
}
|
}
|
||||||
@@ -917,14 +984,12 @@ user = "%s"
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine wrapper command path
|
// If dmsPath is empty (packaged greeter), omit -p; wrapper finds /usr/share/quickshell/dms-greeter
|
||||||
wrapperCmd := "dms-greeter"
|
wrapperCmd := "dms-greeter"
|
||||||
if !utils.CommandExists("dms-greeter") {
|
if !utils.CommandExists("dms-greeter") {
|
||||||
wrapperCmd = "/usr/local/bin/dms-greeter"
|
wrapperCmd = "/usr/local/bin/dms-greeter"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build command based on compositor and dms path
|
|
||||||
// When dmsPath is empty (packaged greeter), omit -p; wrapper finds /usr/share/quickshell/dms-greeter
|
|
||||||
compositorLower := strings.ToLower(compositor)
|
compositorLower := strings.ToLower(compositor)
|
||||||
var command string
|
var command string
|
||||||
if dmsPath == "" {
|
if dmsPath == "" {
|
||||||
@@ -1068,3 +1133,140 @@ func runSudoCmd(sudoPassword string, command string, args ...string) error {
|
|||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkSystemdEnabled(service string) (string, error) {
|
||||||
|
cmd := exec.Command("systemctl", "is-enabled", service)
|
||||||
|
output, _ := cmd.Output()
|
||||||
|
return strings.TrimSpace(string(output)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DisableConflictingDisplayManagers(sudoPassword string, logFunc func(string)) error {
|
||||||
|
conflictingDMs := []string{"gdm", "gdm3", "lightdm", "sddm", "lxdm", "xdm", "cosmic-greeter"}
|
||||||
|
for _, dm := range conflictingDMs {
|
||||||
|
state, err := checkSystemdEnabled(dm)
|
||||||
|
if err != nil || state == "" || state == "not-found" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch state {
|
||||||
|
case "enabled", "enabled-runtime", "static", "indirect", "alias":
|
||||||
|
logFunc(fmt.Sprintf("Disabling conflicting display manager: %s", dm))
|
||||||
|
if err := runSudoCmd(sudoPassword, "systemctl", "disable", "--now", dm); err != nil {
|
||||||
|
logFunc(fmt.Sprintf("⚠ Warning: Failed to disable %s: %v", dm, err))
|
||||||
|
} else {
|
||||||
|
logFunc(fmt.Sprintf("✓ Disabled %s", dm))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableGreetd unmasks and enables greetd, forcing it over any other DM.
|
||||||
|
func EnableGreetd(sudoPassword string, logFunc func(string)) error {
|
||||||
|
state, err := checkSystemdEnabled("greetd")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to check greetd state: %w", err)
|
||||||
|
}
|
||||||
|
if state == "not-found" {
|
||||||
|
return fmt.Errorf("greetd service not found; ensure greetd is installed")
|
||||||
|
}
|
||||||
|
if state == "masked" || state == "masked-runtime" {
|
||||||
|
logFunc(" Unmasking greetd...")
|
||||||
|
if err := runSudoCmd(sudoPassword, "systemctl", "unmask", "greetd"); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmask greetd: %w", err)
|
||||||
|
}
|
||||||
|
logFunc(" ✓ Unmasked greetd")
|
||||||
|
}
|
||||||
|
logFunc(" Enabling greetd service (--force)...")
|
||||||
|
if err := runSudoCmd(sudoPassword, "systemctl", "enable", "--force", "greetd"); err != nil {
|
||||||
|
return fmt.Errorf("failed to enable greetd: %w", err)
|
||||||
|
}
|
||||||
|
logFunc("✓ greetd enabled")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnsureGraphicalTarget(sudoPassword string, logFunc func(string)) error {
|
||||||
|
cmd := exec.Command("systemctl", "get-default")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
logFunc(fmt.Sprintf("⚠ Warning: could not get default systemd target: %v", err))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
current := strings.TrimSpace(string(output))
|
||||||
|
if current == "graphical.target" {
|
||||||
|
logFunc("✓ Default target is already graphical.target")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
logFunc(fmt.Sprintf(" Setting default target to graphical.target (was: %s)...", current))
|
||||||
|
if err := runSudoCmd(sudoPassword, "systemctl", "set-default", "graphical.target"); err != nil {
|
||||||
|
return fmt.Errorf("failed to set graphical target: %w", err)
|
||||||
|
}
|
||||||
|
logFunc("✓ Default target set to graphical.target")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoSetupGreeter performs the full non-interactive greeter setup
|
||||||
|
func AutoSetupGreeter(compositor, sudoPassword string, logFunc func(string)) error {
|
||||||
|
if IsGreeterPackaged() && HasLegacyLocalGreeterWrapper() {
|
||||||
|
return fmt.Errorf("legacy manual wrapper detected at /usr/local/bin/dms-greeter; " +
|
||||||
|
"remove it before using packaged dms-greeter: sudo rm -f /usr/local/bin/dms-greeter")
|
||||||
|
}
|
||||||
|
|
||||||
|
logFunc("Ensuring greetd is installed...")
|
||||||
|
if err := EnsureGreetdInstalled(logFunc, sudoPassword); err != nil {
|
||||||
|
return fmt.Errorf("greetd install failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dmsPath := ""
|
||||||
|
if !IsGreeterPackaged() {
|
||||||
|
detected, err := DetectDMSPath()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("DMS installation not found: %w", err)
|
||||||
|
}
|
||||||
|
dmsPath = detected
|
||||||
|
logFunc(fmt.Sprintf("✓ Found DMS at: %s", dmsPath))
|
||||||
|
} else {
|
||||||
|
logFunc("✓ Using packaged dms-greeter (/usr/share/quickshell/dms-greeter)")
|
||||||
|
}
|
||||||
|
|
||||||
|
logFunc("Setting up dms-greeter group and permissions...")
|
||||||
|
if err := SetupDMSGroup(logFunc, sudoPassword); err != nil {
|
||||||
|
logFunc(fmt.Sprintf("⚠ Warning: group/permissions setup error: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
logFunc("Copying greeter files...")
|
||||||
|
if err := CopyGreeterFiles(dmsPath, compositor, logFunc, sudoPassword); err != nil {
|
||||||
|
return fmt.Errorf("failed to copy greeter files: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logFunc("Configuring greetd...")
|
||||||
|
greeterPathForConfig := ""
|
||||||
|
if !IsGreeterPackaged() {
|
||||||
|
greeterPathForConfig = dmsPath
|
||||||
|
}
|
||||||
|
if err := ConfigureGreetd(greeterPathForConfig, compositor, logFunc, sudoPassword); err != nil {
|
||||||
|
return fmt.Errorf("failed to configure greetd: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logFunc("Synchronizing DMS configurations...")
|
||||||
|
if err := SyncDMSConfigs(dmsPath, compositor, logFunc, sudoPassword); err != nil {
|
||||||
|
logFunc(fmt.Sprintf("⚠ Warning: config sync error: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
logFunc("Checking for conflicting display managers...")
|
||||||
|
if err := DisableConflictingDisplayManagers(sudoPassword, logFunc); err != nil {
|
||||||
|
logFunc(fmt.Sprintf("⚠ Warning: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
logFunc("Enabling greetd service...")
|
||||||
|
if err := EnableGreetd(sudoPassword, logFunc); err != nil {
|
||||||
|
return fmt.Errorf("failed to enable greetd: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logFunc("Ensuring graphical.target as default...")
|
||||||
|
if err := EnsureGraphicalTarget(sudoPassword, logFunc); err != nil {
|
||||||
|
logFunc(fmt.Sprintf("⚠ Warning: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
logFunc("✓ DMS greeter setup complete")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/distros"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/distros"
|
||||||
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/greeter"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -80,19 +81,24 @@ func (m Model) viewDependencyReview() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
note := ""
|
||||||
|
if dep.Name == "dms-greeter" {
|
||||||
|
note = m.styles.Subtle.Render(" (selection replaces your current display manager)")
|
||||||
|
}
|
||||||
|
|
||||||
var line string
|
var line string
|
||||||
if i == m.selectedDep {
|
if i == m.selectedDep {
|
||||||
line = fmt.Sprintf("▶ %s%s%-25s %s", reinstallMarker, variantMarker, dep.Name, status)
|
line = fmt.Sprintf("▶ %s%s%-25s %s", reinstallMarker, variantMarker, dep.Name, status)
|
||||||
if dep.Version != "" {
|
if dep.Version != "" {
|
||||||
line += fmt.Sprintf(" (%s)", dep.Version)
|
line += fmt.Sprintf(" (%s)", dep.Version)
|
||||||
}
|
}
|
||||||
line = m.styles.SelectedOption.Render(line)
|
line = m.styles.SelectedOption.Render(line) + note
|
||||||
} else {
|
} else {
|
||||||
line = fmt.Sprintf(" %s%s%-25s %s", reinstallMarker, variantMarker, dep.Name, status)
|
line = fmt.Sprintf(" %s%s%-25s %s", reinstallMarker, variantMarker, dep.Name, status)
|
||||||
if dep.Version != "" {
|
if dep.Version != "" {
|
||||||
line += fmt.Sprintf(" (%s)", dep.Version)
|
line += fmt.Sprintf(" (%s)", dep.Version)
|
||||||
}
|
}
|
||||||
line = m.styles.Normal.Render(line)
|
line = m.styles.Normal.Render(line) + note
|
||||||
}
|
}
|
||||||
|
|
||||||
b.WriteString(line)
|
b.WriteString(line)
|
||||||
@@ -115,6 +121,13 @@ func (m Model) updateDetectingDepsState(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.state = StateError
|
m.state = StateError
|
||||||
} else {
|
} else {
|
||||||
m.dependencies = depsMsg.deps
|
m.dependencies = depsMsg.deps
|
||||||
|
// dms-greeter is opt-in skipped by default
|
||||||
|
for _, dep := range depsMsg.deps {
|
||||||
|
if dep.Name == "dms-greeter" {
|
||||||
|
m.disabledItems["dms-greeter"] = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
m.state = StateDependencyReview
|
m.state = StateDependencyReview
|
||||||
}
|
}
|
||||||
return m, m.listenForLogs()
|
return m, m.listenForLogs()
|
||||||
@@ -230,6 +243,41 @@ func (m Model) installPackages() tea.Cmd {
|
|||||||
// Convert installer messages to TUI messages
|
// Convert installer messages to TUI messages
|
||||||
go func() {
|
go func() {
|
||||||
for msg := range installerProgressChan {
|
for msg := range installerProgressChan {
|
||||||
|
// Run optional greeter setup
|
||||||
|
if msg.Phase == distros.PhaseComplete && msg.IsComplete && msg.Error == nil {
|
||||||
|
greeterSelected := false
|
||||||
|
for _, dep := range m.dependencies {
|
||||||
|
if dep.Name == "dms-greeter" && !m.disabledItems["dms-greeter"] {
|
||||||
|
greeterSelected = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if greeterSelected {
|
||||||
|
compositorName := "niri"
|
||||||
|
if m.selectedWM == 1 {
|
||||||
|
compositorName = "Hyprland"
|
||||||
|
}
|
||||||
|
m.packageProgressChan <- packageInstallProgressMsg{
|
||||||
|
progress: 0.92,
|
||||||
|
step: "Configuring DMS greeter...",
|
||||||
|
logOutput: "Starting automated greeter setup...",
|
||||||
|
}
|
||||||
|
greeterLogFunc := func(line string) {
|
||||||
|
m.packageProgressChan <- packageInstallProgressMsg{
|
||||||
|
progress: 0.94,
|
||||||
|
step: "Configuring DMS greeter...",
|
||||||
|
logOutput: line,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := greeter.AutoSetupGreeter(compositorName, m.sudoPassword, greeterLogFunc); err != nil {
|
||||||
|
m.packageProgressChan <- packageInstallProgressMsg{
|
||||||
|
progress: 0.96,
|
||||||
|
step: "Greeter setup warning",
|
||||||
|
logOutput: fmt.Sprintf("⚠ Greeter auto-setup warning (non-fatal): %v", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
tuiMsg := packageInstallProgressMsg{
|
tuiMsg := packageInstallProgressMsg{
|
||||||
progress: msg.Progress,
|
progress: msg.Progress,
|
||||||
step: msg.Step,
|
step: msg.Step,
|
||||||
|
|||||||
Reference in New Issue
Block a user