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
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ One-line installer that works on most Linux distributions:
curl -fsSL https://raw.githubusercontent.com/Nomadcxx/sysc-greet/master/install.sh | sudo bash
```

The installer automatically detects your package manager and works on Arch Linux, Debian/Ubuntu, Fedora, and openSUSE. It'll handle compositor selection, install dependencies, and set everything up for you.
The installer automatically detects your package manager and works on Arch Linux, Debian/Ubuntu, Fedora, and openSUSE. It'll handle greeter backend selection, install dependencies, and set everything up for you.

> Hyprland greeter support ends in ~3 months. New installs should choose niri (default) or cagebreak, its replacement.

### Build from Source

Expand All @@ -34,19 +36,21 @@ The installer walks you through compositor selection and configuration.

### Arch Linux (AUR)

Three AUR packages available depending on which compositor you're using:
Three AUR packages available depending on which greeter backend you're using:

```bash
# Recommended (niri)
# niri (default)
yay -S sysc-greet

# Hyprland variant
# Hyprland variant (deprecated, replaced by cagebreak)
yay -S sysc-greet-hyprland

# Sway variant
yay -S sysc-greet-sway
```

For cagebreak, install it from the AUR (`paru -S cagebreak`, plus `socat`) and run the installer with `SYSC_COMPOSITOR=cagebreak`.

### NixOS (Flake)

If you're on NixOS, add sysc-greet to your flake:
Expand Down Expand Up @@ -79,7 +83,7 @@ If you're on NixOS, add sysc-greet to your flake:
{
services.sysc-greet = {
enable = true;
compositor = "niri"; # or "hyprland" or "sway"
compositor = "niri"; # or "cagebreak", "sway", "hyprland" (deprecated)
};

# Optional: Set initial session for auto-login
Expand All @@ -90,8 +94,8 @@ If you're on NixOS, add sysc-greet to your flake:
}
```

By default, the NixOS module does not install `niri`, `hyprland`, or `sway`.
Install your chosen compositor yourself, or set `niriPackage`, `hyprlandPackage`,
By default, the NixOS module does not install `niri`, `cagebreak`, `hyprland`, or `sway`.
Install your chosen backend yourself, or set `niriPackage`, `cagebreakPackage`, `hyprlandPackage`,
or `swayPackage` if you want the module to install and use a specific package.
If your compositor is managed elsewhere, set `compositorCommand` to the exact
command greetd should run.
Expand Down
79 changes: 67 additions & 12 deletions cmd/installer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ type model struct {
needsGreetd bool
uninstallMode bool
selectedOption int // 0 = Install, 1 = Uninstall
selectedCompositor string // "niri", "hyprland", or "sway"
selectedCompositor string // "niri", "cagebreak", "sway", or "hyprland"
compositorIndex int // Current selection in compositor menu
debugMode bool // Show verbose output
logFile *os.File // Installer log file
Expand Down Expand Up @@ -340,7 +340,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case "down", "j":
if m.step == stepWelcome && m.selectedOption < 1 {
m.selectedOption++
} else if m.step == stepCompositorSelect && m.compositorIndex < 2 {
} else if m.step == stepCompositorSelect && m.compositorIndex < 3 {
m.compositorIndex++
}
case "enter":
Expand Down Expand Up @@ -373,14 +373,15 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
} else if m.step == stepCompositorSelect {
// Set compositor based on selection
compositors := []string{"niri", "hyprland", "sway"}
compositors := []string{"niri", "cagebreak", "sway", "hyprland"}
m.selectedCompositor = compositors[m.compositorIndex]

// Validate compositor is installed
compositorBinaries := map[string][]string{
"niri": {"niri"},
"hyprland": {"Hyprland", "hyprland"},
"sway": {"sway"},
"niri": {"niri"},
"hyprland": {"Hyprland", "hyprland"},
"sway": {"sway"},
"cagebreak": {"cagebreak"},
}

compositorInstalled := false
Expand Down Expand Up @@ -563,9 +564,10 @@ func (m model) renderCompositorSelect() string {
name string
desc string
}{
{"niri", "Tiling compositor with scrollable workspaces"},
{"hyprland", "Dynamic tiling compositor with extensive features"},
{"niri", "Tiling compositor with scrollable workspaces (default)"},
{"cagebreak", "Minimal tiling kiosk; replaces hyprland for the greeter"},
{"sway", "Stable i3-compatible tiling compositor"},
{"hyprland", "Deprecated — greeter support ending in ~3 months; migrate to cagebreak"},
}

for i, comp := range compositors {
Expand All @@ -577,7 +579,11 @@ func (m model) renderCompositorSelect() string {
b.WriteString(" " + comp.desc + "\n\n")
}

b.WriteString(lipgloss.NewStyle().Foreground(FgMuted).Render("The greeter will work identically on all compositors"))
b.WriteString(lipgloss.NewStyle().Foreground(FgMuted).Render("Hyprland greeter support ends in ~3 months; cagebreak replaces it"))
if m.compositorIndex == 3 {
b.WriteString("\n")
b.WriteString(lipgloss.NewStyle().Foreground(ErrorColor).Render("⚠ Hyprland will be removed from the greeter in ~3 months. Use cagebreak or niri instead."))
}

// Show errors if any
if len(m.errors) > 0 {
Expand Down Expand Up @@ -947,12 +953,35 @@ func installKitty(m *model) error {
return nil
}

// installCagebreakArch installs cagebreak on Arch: official repos first, then
// an AUR helper run as the invoking user (AUR helpers refuse to run as root).
func installCagebreakArch() error {
if err := exec.Command("pacman", "-S", "--noconfirm", "cagebreak").Run(); err == nil {
return nil
}
helperPath := detectAURHelper()
if helperPath == "" {
return fmt.Errorf("cagebreak is AUR-only and no AUR helper found — install it first: paru -S cagebreak (or yay -S cagebreak)")
}
var cmd *exec.Cmd
if sudoUser := os.Getenv("SUDO_USER"); sudoUser != "" {
cmd = exec.Command("sudo", "-u", sudoUser, helperPath, "-S", "--noconfirm", "cagebreak")
} else {
cmd = exec.Command(helperPath, "-S", "--noconfirm", "cagebreak")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("cagebreak AUR install failed — install it manually with %s -S cagebreak", helperPath)
}
return nil
}

func installCompositor(m *model) error {
// Map compositor selection to binary names
compositorBinaries := map[string][]string{
"niri": {"niri"},
"hyprland": {"Hyprland", "hyprland"},
"sway": {"sway"},
"niri": {"niri"},
"hyprland": {"Hyprland", "hyprland"},
"sway": {"sway"},
"cagebreak": {"cagebreak"},
}

// Check if compositor already installed
Expand Down Expand Up @@ -996,6 +1025,8 @@ func installCompositor(m *model) error {
cmd = exec.Command("pacman", "-S", "--noconfirm", "hyprland")
case "sway":
cmd = exec.Command("pacman", "-S", "--noconfirm", "sway")
case "cagebreak":
return installCagebreakArch()
}

case "apt":
Expand All @@ -1007,6 +1038,8 @@ func installCompositor(m *model) error {
return fmt.Errorf("hyprland not in standard apt repos - see https://hyprland.org for installation")
case "sway":
cmd = exec.Command("apt-get", "install", "-y", "sway")
case "cagebreak":
return fmt.Errorf("cagebreak not in standard apt repos — build from https://github.com/project-repo/cagebreak")
}

case "dnf":
Expand All @@ -1018,6 +1051,8 @@ func installCompositor(m *model) error {
return fmt.Errorf("hyprland not in standard dnf repos - see https://hyprland.org for installation")
case "sway":
cmd = exec.Command("dnf", "install", "-y", "sway")
case "cagebreak":
return fmt.Errorf("cagebreak not in standard dnf repos — build from https://github.com/project-repo/cagebreak")
}

case "yum":
Expand Down Expand Up @@ -1612,6 +1647,25 @@ exec "XDG_CACHE_HOME=/tmp/greeter-cache HOME=/var/lib/greeter kitty --start-as=f
configPath = "/etc/greetd/sway-greeter-config"
greetdCommand = "sway --unsupported-gpu -c /etc/greetd/sway-greeter-config"

case "cagebreak":
compositorConfig = `# sysc-greet cagebreak greeter config — used ONLY by the greetd greeter session
# greetd runs: cagebreak -e -c /etc/greetd/cagebreak-greeter-config
# -e enables the IPC socket used to quit cagebreak after login (requires socat)
# Keyboard layout: set XKB_DEFAULT_LAYOUT/XKB_DEFAULT_VARIANT in greetd's command env

background 0.0 0.0 0.0

# gSlapper wallpaper daemon (wlr-layer-shell client, same as niri/sway setup)
exec HOME=/var/lib/greeter /usr/local/bin/sysc-greet --wallpaper-daemon

# Greeter UI; when it exits (login or shutdown), quit cagebreak so greetd can start the session
exec XDG_CACHE_HOME=/tmp/greeter-cache HOME=/var/lib/greeter kitty --start-as=fullscreen --config=/etc/greetd/kitty.conf /usr/local/bin/sysc-greet; echo quit | socat - UNIX-CONNECT:"$CAGEBREAK_SOCKET"

# No bind/definekey lines on purpose: no compositor keybindings reachable from the greeter
`
configPath = "/etc/greetd/cagebreak-greeter-config"
greetdCommand = "cagebreak -e -c /etc/greetd/cagebreak-greeter-config"

default:
return fmt.Errorf("unknown compositor: %s", m.selectedCompositor)
}
Expand Down Expand Up @@ -1702,6 +1756,7 @@ func removeConfigs(m *model) error {
"/etc/greetd/niri-greeter-config.kdl",
"/etc/greetd/hyprland-greeter-config.conf",
"/etc/greetd/sway-greeter-config",
"/etc/greetd/cagebreak-greeter-config",
}

for _, path := range paths {
Expand Down
14 changes: 14 additions & 0 deletions config/cagebreak-greeter-config
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# sysc-greet cagebreak greeter config — used ONLY by the greetd greeter session
# greetd runs: cagebreak -e -c /etc/greetd/cagebreak-greeter-config
# -e enables the IPC socket used to quit cagebreak after login (requires socat)
# Keyboard layout: set XKB_DEFAULT_LAYOUT/XKB_DEFAULT_VARIANT in greetd's command env

background 0.0 0.0 0.0

# gSlapper wallpaper daemon (wlr-layer-shell client, same as niri/sway setup)
exec HOME=/var/lib/greeter /usr/local/bin/sysc-greet --wallpaper-daemon

# Greeter UI; when it exits (login or shutdown), quit cagebreak so greetd can start the session
exec XDG_CACHE_HOME=/tmp/greeter-cache HOME=/var/lib/greeter kitty --start-as=fullscreen --config=/etc/greetd/kitty.conf /usr/local/bin/sysc-greet; echo quit | socat - UNIX-CONNECT:"$CAGEBREAK_SOCKET"

# No bind/definekey lines on purpose: no compositor keybindings reachable from the greeter
79 changes: 79 additions & 0 deletions docs-src/compositors/cagebreak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Cagebreak Setup

Cagebreak is a tiling kiosk compositor forked from Cage. It replaces Hyprland for the greetd greeter session, which is [deprecated](hyprland.md). Niri remains the default greeter compositor.

Cagebreak supports wlr-layer-shell, so gSlapper wallpapers work the same as under niri and sway. Wallpapers appear on secondary monitors; the primary output is covered by the greeter TUI, which draws its own background effects.

The greeter config is ~10 lines, defines no keybindings, and quits the compositor through cagebreak's IPC socket after login. Tracked in [issue #69](https://github.com/Nomadcxx/sysc-greet/issues/69).

## Install Cagebreak

=== "Arch Linux"

Cagebreak is in the AUR, not the official repos. socat is required to quit the compositor after login.

```bash
paru -S cagebreak # or: yay -S cagebreak
sudo pacman -S socat
```

=== "NixOS"

```nix
services.sysc-greet = {
enable = true;
compositor = "cagebreak";
# optional: cagebreakPackage = pkgs.cagebreak;
};
```

=== "Other distros"

Check your package manager or build from [project-repo/cagebreak](https://github.com/project-repo/cagebreak).

## greetd Config

=== "Installer"

```bash
SYSC_COMPOSITOR=cagebreak sudo ./install.sh
```

=== "Manual"

Edit `/etc/greetd/config.toml`:

```toml
[terminal]
vt = 1

[default_session]
command = "cagebreak -e -c /etc/greetd/cagebreak-greeter-config"
user = "greeter"
```

`-e` enables the IPC socket. The greeter config uses it to quit cagebreak after login.

The installer and packages place the greeter config at `/etc/greetd/cagebreak-greeter-config`.

## Keyboard Layout

Cagebreak reads the standard XKB environment variables. For a non-US layout, wrap the greetd command:

```toml
command = "env XKB_DEFAULT_LAYOUT=de XKB_DEFAULT_VARIANT=nodeadkeys cagebreak -e -c /etc/greetd/cagebreak-greeter-config"
```

## Migrating from Hyprland

1. Install cagebreak and socat (see above)
2. Re-run the installer: `SYSC_COMPOSITOR=cagebreak sudo ./install.sh`
3. Reboot, or `sudo systemctl restart greetd` from a TTY

Your Hyprland desktop session is unaffected. Only the boot greeter changes.

## Troubleshooting

- **No wallpaper on a secondary monitor:** check gSlapper is installed and `/var/cache/sysc-greet/` is readable by the greeter user. Single-monitor setups never show a wallpaper; the TUI covers the only output.
- **Greeter restarts in a loop:** run `cagebreak -e -c /etc/greetd/cagebreak-greeter-config` from a TTY to see the error. A config parse error makes cagebreak exit immediately and greetd relaunches it.
- **Stuck after login:** verify socat is installed. Without it the compositor never quits and greetd waits forever.
7 changes: 7 additions & 0 deletions docs-src/compositors/hyprland.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Hyprland Setup

!!! warning "Deprecated"
sysc-greet ends Hyprland greeter support in ~3 months. It remains functional until then.

Migrate to [cagebreak](cagebreak.md), the Hyprland replacement, or [niri](niri.md), the default.

Your Hyprland desktop session is unaffected. Only the boot greeter changes.

Configuration for running sysc-greet with the Hyprland Wayland compositor.

## greetd Config
Expand Down
18 changes: 12 additions & 6 deletions docs-src/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@ curl -fsSL https://raw.githubusercontent.com/Nomadcxx/sysc-greet/master/install.
```

The interactive installer will prompt you to:
1. Choose your compositor (niri, hyprland, or sway)
2. Configure compositor settings
1. Choose your greeter backend (niri default, cagebreak, sway, or hyprland deprecated)
2. Configure backend settings
3. Install dependencies automatically

> Hyprland greeter support ends in ~3 months. Migrate to [cagebreak](../compositors/cagebreak.md) or [niri](../compositors/niri.md).

## Manual Build

### Prerequisites

- Go 1.25+
- greetd
- Wayland compositor (niri, hyprland, or sway)
- Wayland backend: niri (default), cagebreak, sway, or hyprland (deprecated)
- kitty (terminal emulator)
- gSlapper (wallpaper daemon)
- swww (legacy wallpaper daemon, optional fallback)
Expand All @@ -45,17 +47,21 @@ go run ./cmd/installer/

## Arch Linux (AUR)

sysc-greet provides three AUR packages for different compositors:
sysc-greet provides AUR packages for different greeter backends:

```bash
# Recommended (niri)
# niri (default)
yay -S sysc-greet

# Hyprland variant
# Hyprland variant (deprecated, replaced by cagebreak)
yay -S sysc-greet-hyprland

# Sway variant
yay -S sysc-greet-sway

# Cagebreak: install from AUR, then run the installer
paru -S cagebreak && sudo pacman -S socat
SYSC_COMPOSITOR=cagebreak curl -fsSL .../install.sh | sudo bash
```

## Pre-built Packages
Expand Down
Loading