- Rename service module to autostart (no systemd service is used)
- Fix fork bomb: replace subprocess spawning with thread::spawn
- Replace zenity/kdialog with rfd crate for XDG Portal file picker
- Use dirs crate instead of env::var("HOME")
- Auto-start launcher before game launch for online auth
- Propagate gamemode/mangohud env vars to launcher process
- Auto-install SVG icon on startup via ensure_icon()
- Add assets/umutray.svg
- Remove stale zenity/kdialog optdepends from PKGBUILD
- Update .gitignore for .claude/ and CLAUDE.md
- Replace .map().unwrap_or(false) with .is_some_and()/.is_ok_and()
- Use path.display() instead of {:?} for user-facing messages
- Replace Option<Option<Vec<String>>> with GamescopeUpdate enum
- Replace manual parent-walking loops with .ancestors() iterators
- Simplify kill()/kill_all() signatures to return () instead of Result
- Use tokio::task::spawn_blocking instead of hand-rolled thread+oneshot
- Read /proc/self/status for UID instead of spawning id subprocess
- Build Exec= line directly in render_desktop instead of string-replace
- Bump PKGBUILD pkgrel to 6
SKIP_DIRS contained parent directories that also contain game installs,
which prevented those games from ever being scanned:
- "epic games" → all Epic games live inside this dir
- "ubisoft" → Ubisoft games at Ubisoft/Ubisoft Game Launcher/games/
- "rockstar games" → Rockstar games live directly inside this dir
- "electronic arts" → some EA games live here
SKIP_EXES already filters the individual launcher executables, so the
directory-level blocks are redundant and harmful. Trim SKIP_DIRS to only
directories that genuinely contain no game executables (battle.net,
ea desktop, gog galaxy, Wine infrastructure).
Add missing launcher path patterns to name_from_launcher_path:
- "ea games" → EA Games/<GameName>/
- "ubisoft game launcher" → Ubisoft Game Launcher/games/<GameName>/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add stage 2: read Legendary and Heroic's installed.json files from all
known locations (native + Flatpak) to build a install-path → title map.
This covers Epic games (via Legendary) and GOG games (via Heroic's GOG
store) before any fallback is needed.
Remove CamelCase/digit-boundary splitting from the fallback entirely.
If stages 2-4 all miss, nearest_dir_name() returns the closest
non-generic parent directory name, or the raw exe stem as-is. No names
are fabricated from the exe filename.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both run() and run_new() were starting with Task::none(), so the
Bootstrap font was never loaded. All icon() calls in the setup wizard
rendered as '?' glyphs. Load the font as the initial task (batched
with AutoDownload where needed) and handle FontLoaded as a no-op.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the hardcoded EXE_OVERRIDES lookup table and the unreliable PE
byte scanner. Game names are already present in the install directory —
we just need to read them from the right place.
Resolution pipeline (first hit wins):
1. Explicit name supplied by the caller
2. Manifest walk: traverse up from the exe to the game root looking for
GOG goggame-*.info (gameName) and Epic .egstore/*.item (DisplayName)
3. Launcher path: read the game name from known directory conventions
laid down by the launcher itself:
- Epic Games/<GameName>/…
- GOG Games/<GameName>/…
- steamapps/common/<GameName>/…
- Rockstar Games/<GameName>/…
4. Heuristic: nearest non-generic parent directory name, or CamelCase
stem split (unchanged, for truly custom/manual installs)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduce resolve_game_name() as the single entry point for deriving a
display name from a game executable. Resolution order:
1. Explicit name (caller-supplied)
2. Static override table for known bad stems (FactoryGame, bg3, etc.)
3. GOG goggame-*.info and Epic .egstore/*.item manifest JSON files
4. PE VERSIONINFO scan (ProductName / FileDescription, first 8 MB)
5. Heuristic fallback: parent directory name or CamelCase stem split
Remove prettify_game_name and humanise_stem; expose prettify_exe_name
as the public heuristic-only fallback. Resolved names are cached in a
process-wide LazyLock<Mutex<HashMap>> so repeated scans are free.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Step indicator (1→2→3) on both screens showing current position
- Fixed window size (520×440, non-resizable) for consistent presentation
- view_install redesigned with card-based layout matching view_picking
- Status messages colour-coded: blue=info, green=success, red=error
- Official installer shown as a badge ("✓ Official installer") — URL hidden
- Download/install progress in a styled card with byte counter
- Finished state has distinct success (green border) / failure (red border) banners
- Installation log collapsed by default behind "Show details ▼" toggle
- Removed "via umu-run" and other internal tool references from user-facing text
- Removed raw "Paste a URL…" initial status — context is clear from the card
- Window title simplified to "umutray — <Launcher Name>"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Settings panel now shows the current version and a "Rebuild & Install
Latest" button that:
1. git pull from the embedded source directory (CARGO_MANIFEST_DIR)
2. makepkg -sf in packaging/
3. pkexec pacman -U (graphical polkit auth prompt)
Reports the installed version on success; surfaces the failing step on
error. Update runs off the UI thread so the window stays responsive.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaced the flat form layout with a polished card-based design:
- Section cards with subtle borders matching the dashboard style
- Blue accent labels for section headings (Launcher, Install Location)
- Hint text explaining the Wine prefix folder
- Error status styled in orange-red instead of plain text
- Next button right-aligned with top spacing
- Header with muted subtitle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The .font() builder method was silently failing to register the font
for named lookup. Using iced::font::load() as a startup Task ensures
the font is properly loaded before any text rendering occurs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Font version in iced_fonts 0.1.1 maps GearFill to U+F3E2, not U+F3F8.
Using the wrong codepoint rendered as a question-mark placeholder.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The app now uses XDG autostart (~/.config/autostart/umutray.desktop)
managed via the Settings panel, not systemd. The service file is no
longer installed by the package.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- setup.rs: remove raw prefix/expected path labels from install view;
hide URL input when official installer is pre-filled (show green tick
instead), revealing an override field only when needed
- gui.rs: drop raw exe path from scan result rows; add per-state colour
to status indicator (green=running, blue=installed, grey=not installed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
iced_fonts 0.3 pulls in iced_widget 0.14 / iced_renderer 0.14 which
breaks release builds when used alongside iced 0.13. Pin to 0.1.x
which targets iced 0.12/0.13 and drop the unused iced_aw dependency.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Hide raw filesystem paths from all status messages; use launcher
display names instead (e.g. "Downloading Battle.net installer…")
- Simplify installer source label: show "✓ Official installer detected"
badge when URL is auto-filled
- Replace separate "Download / Prepare" + "Run installer" buttons with
a single context-aware action button whose label tracks the stage:
Download → / Downloading… / Install → / Installing…
- Remove auto-download on launcher confirm; show confirmation prompt
"Ready to download the official X installer. Press Download → to begin."
- Soften the exe-not-found error message; remove raw path and config
editing advice — direct users to the log instead
- Rename "Launch now" → "Open launcher" for clarity
- Show "✓ X installed successfully!" success banner above Close/Open
buttons when install completes and the exe is present
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add iced_aw + iced_fonts (bootstrap feature) to load the Bootstrap Icons
font. The settings button and settings header now render the gear-fill
glyph (U+F3F8) via text("\u{F3F8}").font("bootstrap-icons") instead of
the raw ⚙ character which was not in iced's bundled Inter font.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- detect::scan_games_in_prefix() walks drive_c/Program Files and
Program Files (x86) up to depth 4, skipping Windows system dirs,
the launcher's own exe, and already-configured games
- Empty game list now shows "No games added yet." with two actions:
· Scan for games — async scan of the Wine prefix, results appear
as a clickable list with + buttons to add each found exe
· Browse exe… — native file picker (zenity/kdialog) opening in
drive_c/, computes the relative path automatically
- Add-game form gains a Browse… button to pick exe from the prefix
- util::pick_file() added alongside pick_folder()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The wizard previously reached Stage::Finished and showed a status
message with no way to proceed — the user was stuck. Now:
- A Close button exits the setup window
- A Launch Now button (enabled only when the exe exists) launches
the newly installed launcher and closes the wizard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The PKGBUILD in packaging/ covers everything the Makefile did.
Local installs should use `cd packaging && makepkg -si` instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GUI dashboard (gui.rs):
- Add detect, diagnose, settings panel, per-launcher games management
- Async Kill with optimistic UI state and rollback on error
- Settings: pick_list for Proton version, browse button for compat dir,
validation, per-launcher version badge
- Detect Installed button with scan results in footer
- Diagnose card with ✓/✗ checklist per launcher
Issue #1 — Use System Proton (proton.rs):
- Extend list_installed() to scan ~/.steam/root/compatibilitytools.d,
~/.local/share/Steam/compatibilitytools.d, and
/usr/share/steam/compatibilitytools.d in addition to the configured
compat dir; deduplicates by name so the same version never appears twice
Issue #4 — Remove Systemd Service (service.rs):
- Replace systemd unit management with XDG autostart:
install() writes ~/.config/autostart/umutray.desktop instead of a
systemd unit and never calls systemctl; uninstall() removes that file;
status() checks whether the autostart entry exists
- Update GUI service_is_installed() to check the XDG autostart path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add #![forbid(unsafe_code)] to main.rs (issue #3)
- Replace raw ANSI escape codes with owo-colors crate (issue #2)
- Replace manual HOME path construction with dirs::home_dir() (issue #5)
- Ship umutray.service as a static file; service::install() substitutes
the binary path at install time instead of generating the unit at runtime
- Add packaging/PKGBUILD following Arch Rust package guidelines
- Add CLAUDE.md tracking refactor tasks
- setup.rs: clean up downloaded temp files on abort/back, save launcher
to config only after successful install, auto-start download when a
preset has an installer_url
- util.rs: add pick_folder() using zenity/kdialog subprocesses (no rfd)
- config.rs: populate installer_url for all 6 built-in presets with
official download URLs
- Document the Option<Option<Vec<String>>> gamescope pattern at main.rs:307
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Scans common Wine prefix locations (~/Games, ~/.wine, Lutris, Bottles,
Heroic) plus any user-supplied --dir paths for each configured
launcher's exe. Reports matches with four markers:
✓ already configured at that prefix
→ detected at a different prefix (--apply to update)
⚠ multiple prefixes match (ambiguous)
· not found
--apply writes the new prefix_dir back to config.toml for unambiguous
cases; ambiguous ones are skipped with a note to resolve via
`config edit`. The Setup doc comment is also refreshed since the iced
wizard landed in an earlier commit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Games live under each Launcher as a Vec<Game>; the launcher itself
never picks up overlays, only games launched through `play` do.
- config: Game struct with gamemode/mangohud/gamescope fields, plus
add_game / remove_game / set_game_flags methods.
- launcher::play_game wraps the game command as
`gamescope [args] -- gamemoderun umu-run <exe>` (each layer optional)
and sets MANGOHUD=1 when enabled.
- CLI: `play`, `games`, `config add-game`, `config remove-game`,
`config set-game-flags`.
- tray: per-game submenus with Play + checkmark toggles for GameMode /
MangoHud / Gamescope; toggles persist to disk immediately.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- tray: uninstalled launchers now show a "Setup…" entry that spawns
the setup wizard as a child process.
- setup.rs: download shows a progress bar (bytes / total), and
umu-run stdout+stderr stream into a scrollable log pane. A 250 ms
tick subscription pulls updates from the shared state.
- config add-launcher / remove-launcher CLI, with sensible defaults
for prefix_dir, gameid, and process_pattern derived from name/exe.
- diagnose: flag stale wineserver processes when no launcher is
running, suggesting `umutray kill`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lets the user paste an installer URL or local .exe path, downloads to
a temp file if needed, and runs the installer via umu-run with the
launcher's prefix, gameid, and proton path wired up. On completion,
checks for the expected exe and reports next steps.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sets license, readme, repository, keywords and categories so cargo
publish / cargo install pick up the right info. Repo URL points at
the real git.aleshym.co remote.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Binary, crate, clap app name, ksni tray id, HTTP user-agent, systemd unit,
XDG config dir (co.aleshym/umutray), README, and all log prefixes.
Config path changes from ~/.config/battlenet-manager/ to ~/.config/umutray/.
Existing users should `mv` the old directory if they've customised it;
otherwise defaults get rewritten on next run.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
config subcommand — show, path, edit ($EDITOR), and a non-interactive
set that takes --prefix / --compat-dir / --gameid. Lets users retarget
the Wine prefix without hand-editing TOML.
service subcommand — install / uninstall / status for a systemd --user
unit that autostarts the tray. install writes ~/.config/systemd/user/
battlenet-manager.service with ExecStart pointing at the current binary,
then daemon-reloads and enable --now's the unit. uninstall tears it back
down.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- proton: wrap the download sink in a small ProgressWriter that prints
percent / MiB to stderr every 1 MiB so the ~600 MB GE-Proton pull isn't
silent for minutes. No extra deps.
- tray: store the ksni Handle on the tray itself so Quit can call
shutdown() before exit(), unregistering the SNI item from D-Bus instead
of leaving a stale entry until the session bus notices the PID is gone.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- launcher: set PROTONPATH to full install path for pinned Proton versions;
the raw tag name doesn't resolve when umu-run looks it up.
- proton: stream GE-Proton tarballs straight to disk instead of buffering
~600 MB in RAM via .bytes(); add error_for_status() on all HTTP calls so
rate limits and 404s surface clearly; avoid UTF-8 trap on tar args.
- config: fail loudly when $HOME is unset instead of silently writing a Wine
prefix under /tmp.
- diagnose: replace stat+id shell-out with MetadataExt::uid().
- tray: grab handle() before spawn() consumes the service (the repo didn't
compile against ksni 0.2 as shipped).
- launcher/diagnose: escape the dot in "battle.net" pgrep patterns so the
match doesn't false-positive on our own "battlenet-manager" binary; pipe
pgrep/pkill stdio to /dev/null so PID lists don't leak into our output.
- proton: handle empty release list in pick_interactively cleanly.
- Add README, .gitignore, and commit Cargo.lock for reproducible builds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>