// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package app import ( "context" "errors" "log/slog" "git.stormux.org/storm/cthulhu/internal/lifecycle" "git.stormux.org/storm/cthulhu/internal/platform/session" "git.stormux.org/storm/cthulhu/internal/platform/wayland" "git.stormux.org/storm/cthulhu/internal/platform/xorg" ) type desktopInputBackend interface { Start(context.Context) error Stop(context.Context) error } // Missing desktop keyboard capabilities must not prevent AT-SPI, speech and // braille from running on Wayland. Explicit X11 backends keep their existing // optional Xwayland startup behavior. // Other backends retain their own startup contract. The lifecycle runner still // owns cleanup, including a native listener whose startup failed. func desktopInputService(name string, backend desktopInputBackend, logger *slog.Logger) lifecycle.Service { service := lifecycle.Service{Name: name, Start: backend.Start, Stop: backend.Stop} switch backend.(type) { case *wayland.Keyboard: service.Start = func(ctx context.Context) error { err := backend.Start(ctx) if ctx.Err() != nil { return ctx.Err() } if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return err } if err != nil { logger.Warn("Wayland keyboard access unavailable; continuing with AT-SPI reading only", "error", err) } return nil } case *xorg.Listener, *xorg.CommandListener: if session.Wayland() { service.Start = func(ctx context.Context) error { return startOptionalXorg(ctx, name, backend.Start, logger) } } } return service } func startOptionalXorg(ctx context.Context, name string, start func(context.Context) error, logger *slog.Logger) error { err := start(ctx) if ctx.Err() != nil { return ctx.Err() } if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return err } if err != nil { logger.Warn("X11 input service unavailable on Wayland; continuing without it", "service", name, "error", err) } return nil }