PREFIX    ?= /usr
DESTDIR   ?=

BINDIR     = $(DESTDIR)$(PREFIX)/bin
SHAREDIR   = $(DESTDIR)$(PREFIX)/share/eydos-loginmanager
SYSCONFDIR = $(DESTDIR)/etc
SYSTEMDDIR = $(DESTDIR)/lib/systemd/system

# Cross-compilation: set CARGO_TARGET to a Rust target triple to build and
# install for another architecture; empty builds natively into target/release.
CARGO_TARGET      ?=
CARGO_TARGET_FLAG  = $(if $(CARGO_TARGET),--target $(CARGO_TARGET),)
RELEASE_DIR        = target/$(if $(CARGO_TARGET),$(CARGO_TARGET)/,)release

.PHONY: all daemon test install install-smartcast install-ipc-dev uninstall clean distclean \
        daemon-auth daemon-vt-info daemon-spawn daemon-run daemon-client \
        start-ipc

all:
	cargo build --release $(CARGO_TARGET_FLAG)

daemon:
	cargo build --release $(CARGO_TARGET_FLAG) -p loginmanager-daemon

test:
	cargo test --release --workspace $(CARGO_TARGET_FLAG)

# --- daemon dev runs -------------------------------------------------------
#
# All daemon subcommands need root for /dev/tty0 ioctls and PAM session bring-up.
# Override the PAM service / target user / login shell on the make line:
#   make daemon-spawn PAM_SERVICE=loginmanager-daemon DAEMON_USER=alice SPAWN_SHELL=/bin/zsh

PAM_SERVICE  ?= login
DAEMON_USER  ?= $$USER
SPAWN_SHELL  ?= /bin/bash
DAEMON_CONFIG?= $(CURDIR)/loginmanager.toml
DAEMON_BIN    = $(CURDIR)/$(RELEASE_DIR)/loginmanager-daemon

daemon-auth: daemon
	sudo $(DAEMON_BIN) auth $(DAEMON_USER) --pam-service $(PAM_SERVICE)

daemon-vt-info: daemon
	sudo $(DAEMON_BIN) vt-info

daemon-spawn: daemon
	@echo "Spawning $(SPAWN_SHELL) as $(DAEMON_USER) on a fresh VT."
	@echo "When you're done in the spawned shell, type 'exit'."
	@echo "If you need to come back manually: Ctrl+Alt+F<original-VT>."
	sudo $(DAEMON_BIN) spawn $(DAEMON_USER) --pam-service $(PAM_SERVICE) --shell $(SPAWN_SHELL) --config $(DAEMON_CONFIG)

DAEMON_SOCKET ?= /tmp/loginmanager-test.sock

daemon-run: daemon
	@echo "Server listens on $(DAEMON_SOCKET). On AuthAccepted it will fork the"
	@echo "session command from $(DAEMON_CONFIG) — use a wrong password in"
	@echo "daemon-client if you only want to test IPC."
	sudo $(DAEMON_BIN) run --socket $(DAEMON_SOCKET) --config $(DAEMON_CONFIG)

daemon-client: daemon
	sudo $(DAEMON_BIN) client $(DAEMON_USER) --socket $(DAEMON_SOCKET)

# Greeter wired against the daemon IPC. Run `make daemon-run` in another
# terminal first so the socket at $(DAEMON_SOCKET) exists. The greeter
# autodetects the path via LOGINMANAGER_SOCK and picks daemon IPC over
# greetd / PAM-fallback when it is reachable.
start-ipc: all
	FORGE_MOBILE=1 FORGE_SCALE=1 LOCKSCREEN_MODE=greeter LOCKSCREEN_THEME=light LOGINMANAGER_SOCK=$(DAEMON_SOCKET) LTK_THEMES_DIR=$(CURDIR)/../ltk/themes XDG_CONFIG_HOME=/tmp/forge-test-config LOCKSCREEN_BIN=$(LOCKSCREEN_BIN) dbus-run-session forge --winit --size 480x960 --exec $(DEV_INIT)

FORGE_TEST_DIR = /tmp/forge-test-config/forge
LOCKSCREEN_BIN = $(CURDIR)/$(RELEASE_DIR)/lockscreen
DEV_INIT       = $(CURDIR)/data/dev-init

# Pass `THEME=light` or `THEME=dark` on the make line to force a mode.
THEME_FLAG = $(if $(THEME),--$(THEME),)

arm64-deps:
	sudo dpkg --add-architecture arm64
	sudo apt-get install crossbuild-essential-arm64 libstd-rust-dev:arm64 libstd-rust-1.85:arm64 libwayland-dev:arm64 libwayland-egl1:arm64 libxkbcommon-dev:arm64 libegl-dev:arm64 libgles2-mesa-dev:arm64 libpam0g-dev:arm64 libdbus-1-dev:arm64 libssl-dev:arm64

arm64:
	CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \
	PKG_CONFIG=aarch64-linux-gnu-pkg-config PKG_CONFIG_ALLOW_CROSS=1 \
	cargo build --release --target aarch64-unknown-linux-gnu

# Mobile-mode targets pass FORGE_MOBILE=1 so forge skips its built-in
# sm.puri.OSK0 shim and squeekboard owns the name. forge starts squeekboard
# itself only in a logind greeter session, so these targets exec
# data/dev-init, which starts it on the nested compositor and then runs
# $(LOCKSCREEN_BIN).
# FORGE_SCALE=1 keeps the preview at 1×: forge scales a portrait window 2×
# for a real HiDPI phone, which on a 1× laptop renders the 480x960 window's
# content at double size (and its 240-logical surface floors the fluid
# fonts). Forcing scale 1 previews the mobile UI at a sensible size here.
# `dbus-run-session` gives forge --winit its own session bus instead of
# inheriting the host's — without it, in a host that already runs
# forge+crustace, the host forge owns sm.puri.OSK0 on the user bus and
# both the nested forge's property writes and squeekboard's registration
# attempt collide with that owner.
start: all
	FORGE_MOBILE=1 FORGE_SCALE=1 LOCKSCREEN_MODE=greeter LOCKSCREEN_THEME=light LTK_THEMES_DIR=$(CURDIR)/../ltk/themes LOCKSCREEN_BIN=$(LOCKSCREEN_BIN) dbus-run-session forge --winit --size 480x960 --exec $(DEV_INIT)

start-dark: all
	FORGE_MOBILE=1 FORGE_SCALE=1 LOCKSCREEN_MODE=greeter LOCKSCREEN_THEME=dark LTK_THEMES_DIR=$(CURDIR)/../ltk/themes XDG_CONFIG_HOME=/tmp/forge-test-config LOCKSCREEN_BIN=$(LOCKSCREEN_BIN) dbus-run-session forge --winit --size 480x960 --exec $(DEV_INIT)

# Session-lock variant (no username field, no power buttons): current user
# verified via local PAM. Swipe up reveals the password field.
start-lock: all
	FORGE_MOBILE=1 FORGE_SCALE=1 LOCKSCREEN_MODE=lock LOCKSCREEN_THEME=light LTK_THEMES_DIR=$(CURDIR)/../ltk/themes XDG_CONFIG_HOME=/tmp/forge-test-config LOCKSCREEN_BIN=$(LOCKSCREEN_BIN) dbus-run-session forge --winit --size 480x960 --exec $(DEV_INIT)

start-lock-dark: all
	FORGE_MOBILE=1 FORGE_SCALE=1 LOCKSCREEN_MODE=lock LOCKSCREEN_THEME=dark LTK_THEMES_DIR=$(CURDIR)/../ltk/themes XDG_CONFIG_HOME=/tmp/forge-test-config LOCKSCREEN_BIN=$(LOCKSCREEN_BIN) dbus-run-session forge --winit --size 480x960 --exec $(DEV_INIT)

start-desktop: all
	FORGE_SCALE=1 LOCKSCREEN_MODE=greeter LOCKSCREEN_THEME=light LTK_THEMES_DIR=$(CURDIR)/../ltk/themes XDG_CONFIG_HOME=/tmp/forge-test-config forge --winit --size 1600x900 --exec $(LOCKSCREEN_BIN)

start-desktop-dark: all
	FORGE_SCALE=1.75 LOCKSCREEN_MODE=greeter LOCKSCREEN_THEME=dark LTK_THEMES_DIR=$(CURDIR)/../ltk/themes XDG_CONFIG_HOME=/tmp/forge-test-config forge --winit --size 1600x900 --exec $(LOCKSCREEN_BIN)

smartcast: all
	mkdir -p $(FORGE_TEST_DIR)
	printf '#!/bin/sh\nLTK_THEMES_DIR=$(CURDIR)/../ltk/themes $(CURDIR)/target/release/smartcast $(THEME_FLAG) &\n' > $(FORGE_TEST_DIR)/initrc
	chmod +x $(FORGE_TEST_DIR)/initrc
	XDG_CONFIG_HOME=/tmp/forge-test-config forge --winit --size 1600x900

install:
	install -Dm755 $(RELEASE_DIR)/loginmanager-daemon $(BINDIR)/loginmanager-daemon
	install -Dm755 $(RELEASE_DIR)/lockscreen          $(BINDIR)/lockscreen
	install -Dm755 data/loginmanager-greeter          $(BINDIR)/loginmanager-greeter
	install -Dm755 data/greeter-init                  $(SHAREDIR)/greeter-init
	# Root-owned and read-only: it carries the session command the daemon
	# executes, so the greeter user has no business writing to it. What the
	# greeter persists lives in /var/lib/eydos-loginmanager (see postinst).
	install -Dm644 loginmanager.toml                  $(SYSCONFDIR)/eydos/loginmanager.toml
	install -Dm644 data/pam.d/loginmanager-daemon     $(SYSCONFDIR)/pam.d/loginmanager-daemon
	install -Dm644 data/pam.d/loginmanager-greeter    $(SYSCONFDIR)/pam.d/loginmanager-greeter
	install -Dm644 data/pam.d/loginmanager-autologin  $(SYSCONFDIR)/pam.d/loginmanager-autologin
	install -Dm644 data/pam.d/loginmanager-fingerprint $(SYSCONFDIR)/pam.d/loginmanager-fingerprint
	install -Dm644 loginmanager-daemon.service        $(SYSTEMDDIR)/loginmanager-daemon.service
	# The compositor owns the power key; keep logind from acting on it too.
	install -Dm644 data/logind-power-key.conf         $(SYSCONFDIR)/systemd/logind.conf.d/10-eydos-powerkey.conf
	# Where the greeter persists what it may write (last username, text
	# scale) — deliberately not the system config it reads.
	install -d $(DESTDIR)/var/lib/eydos-loginmanager
	install -d -m 0700 $(DESTDIR)/var/lib/loginmanager-daemon
	@if [ -z "$(DESTDIR)" ] && getent passwd _loginmanager >/dev/null 2>&1; then \
		chown _loginmanager:_loginmanager /var/lib/eydos-loginmanager ; \
	fi

install-smartcast:
	install -Dm755 $(RELEASE_DIR)/smartcast $(BINDIR)/smartcast

# The protocol crate as a cargo registry source, for clients built
# outside this workspace (eydos-settings). The manifest is rewritten
# because the in-tree one inherits its fields from the workspace.
IPC_VERSION  = $(shell sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1)
IPC_REGISTRY = $(DESTDIR)$(PREFIX)/share/cargo/registry/loginmanager-ipc-$(IPC_VERSION)

install-ipc-dev:
	install -d $(IPC_REGISTRY)
	cp -r crates/loginmanager-ipc/src $(IPC_REGISTRY)/
	sed -e 's/^version *= *{ workspace = true }/version      = "$(IPC_VERSION)"/' \
	    -e 's/^edition *= *{ workspace = true }/edition      = "2024"/' \
	    -e 's/^rust-version *= *{ workspace = true }/rust-version = "1.85.0"/' \
	    -e 's/^authors *= *{ workspace = true }/authors      = ["Liberux Team <pedro.echanove@liberux.net>"]/' \
	    -e 's/^license *= *{ workspace = true }/license      = "LGPL-2.1-only"/' \
	    -e 's/^serde *= *{ workspace = true }/serde      = { version = "1.0", features = ["derive"] }/' \
	    -e 's/^tokio *= *{ workspace = true, optional = true }/tokio      = { version = "1.35", features = ["full"], optional = true }/' \
	    -e 's/^tokio *= *{ workspace = true }/tokio      = { version = "1.35", features = ["full"] }/' \
	    -e 's/^zeroize *= *{ workspace = true }/zeroize    = "1"/' \
	    crates/loginmanager-ipc/Cargo.toml > $(IPC_REGISTRY)/Cargo.toml
	echo '{"files":{},"package":null}' > $(IPC_REGISTRY)/.cargo-checksum.json

uninstall:
	rm -f $(BINDIR)/loginmanager-daemon
	rm -f $(BINDIR)/lockscreen
	rm -f $(BINDIR)/loginmanager-greeter
	rm -f $(SHAREDIR)/greeter-init
	rm -f $(SYSCONFDIR)/eydos/loginmanager.toml
	rm -f $(SYSCONFDIR)/pam.d/loginmanager-daemon
	rm -f $(SYSCONFDIR)/pam.d/loginmanager-greeter
	rm -f $(SYSCONFDIR)/pam.d/loginmanager-autologin
	rm -f $(SYSCONFDIR)/pam.d/loginmanager-fingerprint
	rm -f $(SYSTEMDDIR)/loginmanager-daemon.service
	rm -f $(SYSCONFDIR)/systemd/logind.conf.d/10-eydos-powerkey.conf

clean:
	dh_clean
	cargo clean

distclean: clean
	rm -f Cargo.lock
