47 Commits

Author SHA1 Message Date
funman300 4845ebe4f8 fix: update all dependencies, remove pelite and directories crates
- Update iced 0.13 -> 0.14 (API migration: application builder, theme, font loading)
- Update ksni 0.2 -> 0.3 (blocking API)
- Update dirs 5 -> 6, toml 0.8 -> 1, reqwest 0.12 -> 0.13, rfd 0.15 -> 0.17, iced_fonts 0.1 -> 0.3
- Remove pelite dependency (PE file parsing was unreliable and unnecessary)
- Remove directories dependency (consolidated with dirs crate)

Closes #8, #9, #10
2026-04-19 15:30:46 -07:00
funman300 c1893f9f64 refactor: rename service to autostart, fix fork bomb, add rfd file picker, use dirs crate, auto-start launcher, propagate overlays, ensure icon
- 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
2026-04-19 13:02:32 -07:00
funman300 2f4f1c64d2 refactor: idiomatic Rust cleanup and quality improvements
- 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
2026-04-19 11:29:42 -07:00
funman300 8447581fe6 detect: fix SKIP_DIRS blocking game discovery for Epic, Ubisoft, Rockstar
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>
2026-04-19 10:56:42 -07:00
funman300 a0ee01cd5d detect: read Legendary/Heroic installed.json, remove name generation
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>
2026-04-19 10:52:55 -07:00
funman300 aeed52d6dd setup: load Bootstrap Icons font so icons render in the wizard
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>
2026-04-19 10:45:42 -07:00
funman300 2b538a286a detect: resolve game names from install directory structure, not guesswork
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>
2026-04-19 10:40:09 -07:00
funman300 e213377a95 detect: replace exe-name heuristic with multi-stage resolution pipeline
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>
2026-04-19 10:31:43 -07:00
funman300 f3f5046265 chore: cleanup for push to main
- Remove CLAUDE.md, TODO.md (dev-only task trackers)
- Remove umutray.service (unused systemd unit)
- Remove .vscode/settings.json (stale Makefile ref)
- Add src/theme.rs (shared palette/styling module)
- Update .gitignore: exclude .vscode/, packaging build artifacts
- Fix README: add gui command, correct service description
- Delete ~1.3GB packaging build artifacts from working tree

Code changes from prior session (already committed locally):
- Tray icon launches alongside GUI, close dialog with minimize-to-tray
- Theme module extraction, button shadow fixes, UI polish
- Game detection filtering, prettify_game_name, Battle.net fix
2026-04-19 02:05:10 -07:00
funman300 4e204d4bf7 detect: filter blizzard tools, error/repair/diagnostic exes 2026-04-19 01:08:24 -07:00
funman300 d3ac300b91 Redesign launcher cards: icon buttons, proton badge, pill toggles, sub-cards, better header 2026-04-19 01:04:30 -07:00
funman300 3c1742174b gui: overhaul games section with polished professional layout 2026-04-19 00:56:52 -07:00
funman300 b81c7fd863 detect: filter out launcher tools and non-game exes from game scan 2026-04-19 00:53:36 -07:00
funman300 a1afa59f1a settings: add Launch Protontricks button 2026-04-19 00:47:26 -07:00
funman300 32c6e1fce0 setup: auto-download official installer for existing launchers too 2026-04-19 00:40:04 -07:00
funman300 20509fb488 gui: transition to tray on close, auto-download official installers in setup 2026-04-19 00:36:22 -07:00
funman300 f645b58470 chore(settings): remove self-update / Rebuild & Install feature
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 23:52:07 -07:00
funman300 9ad1e6a745 refactor(setup): complete UX overhaul of the install wizard
- 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>
2026-04-18 23:49:36 -07:00
funman300 f70498158a feat(settings): add Rebuild & Install self-update button
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>
2026-04-18 23:41:07 -07:00
funman300 3c78e1586f refactor(setup): redesign launcher picking screen
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>
2026-04-18 23:37:26 -07:00
funman300 0c22e23ad3 fix(gui): load Bootstrap Icons font via Task instead of builder
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>
2026-04-18 23:34:22 -07:00
funman300 108f385973 fix(gui): correct Bootstrap Icons codepoint for gear icon
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>
2026-04-18 23:28:12 -07:00
funman300 d97a13e289 chore(packaging): remove systemd service from PKGBUILD, bump pkgrel
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>
2026-04-18 23:24:12 -07:00
funman300 2e51b2e788 refactor(ui): cleaner setup wizard and dashboard card visuals
- 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>
2026-04-18 23:19:30 -07:00
funman300 c4587b0729 fix: downgrade iced_fonts to 0.1 for iced 0.13 compatibility
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>
2026-04-18 23:12:52 -07:00
funman300 d4f0515a82 refactor(setup): clean up setup wizard UX
- 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>
2026-04-18 23:06:32 -07:00
funman300 156bb460a0 feat(gui): replace settings ⚙ placeholder with Bootstrap cog icon
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>
2026-04-18 23:06:22 -07:00
funman300 9134d3bab0 feat(gui): auto-detect games in Wine prefix and browse for exe
- 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>
2026-04-18 19:50:48 -07:00
funman300 74f21b6b75 fix(setup): add Close and Launch Now buttons after install completes
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>
2026-04-18 19:43:55 -07:00
funman300 05a12b7cee chore: remove Makefile in favour of packaging/PKGBUILD
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>
2026-04-18 19:37:43 -07:00
funman300 f170171895 feat: GUI dashboard, system Proton scanning, and XDG autostart
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>
2026-04-18 19:28:22 -07:00
funman300 9b7e474e80 refactor: apply CLAUDE.md code quality improvements and add packaging
- 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>
2026-04-18 19:28:10 -07:00
funman300 f2f584febf new file: Makefile
new file:   TODO.md
	modified:   src/config.rs
	modified:   src/detect.rs
	modified:   src/diagnose.rs
	new file:   src/gui.rs
	modified:   src/main.rs
	modified:   src/service.rs
	modified:   src/setup.rs
	modified:   src/tray.rs
	new file:   src/util.rs
	new file:   umutray.desktop
2026-04-17 23:12:47 -07:00
funman300 4c918e673b Note in README that the project is vibe-coded by Claude
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 21:41:47 -07:00
funman300 1bacf345f0 Apply rustfmt pass across all modules
Pure whitespace normalization — no logic changes. Mostly:
- collapsing multi-line match/if arms rustfmt prefers inline
- inlining short `with_context`/`ok_or_else` closures
- reformatting nested method chains for consistency

Build and clippy stay clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 21:35:27 -07:00
funman300 e72ee69c14 Add detect command to find installed launchers on disk
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>
2026-04-17 21:33:48 -07:00
funman300 b72c642223 Add per-game overlay toggles (gamemode, mangohud, gamescope)
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>
2026-04-17 17:24:55 -07:00
funman300 22fa1efabf Add setup tray entry, wizard progress/log, config add/remove, stale-wine check
- 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>
2026-04-17 12:46:47 -07:00
funman300 14eccf4ef0 Replace setup CLI stub with iced-based wizard
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>
2026-04-17 12:39:09 -07:00
funman300 7e5ed3d447 modified: README.md
modified:   src/config.rs
	modified:   src/diagnose.rs
	modified:   src/launcher.rs
	modified:   src/main.rs
	new file:   src/setup.rs
	modified:   src/tray.rs
2026-04-16 21:43:58 -07:00
funman300 336c5d908e Add MIT LICENSE and crates.io metadata
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>
2026-04-16 17:33:12 -07:00
funman300 6fe558ad00 Rename project: battlenet-manager → umutray
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>
2026-04-16 17:28:13 -07:00
funman300 f7738d215b Add config and service subcommands
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>
2026-04-16 17:25:48 -07:00
funman300 8908c15974 Add download progress and graceful tray shutdown
- 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>
2026-04-16 17:21:15 -07:00
funman300 7de6f6d938 Fix launcher/download bugs, add README and Cargo.lock
- 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>
2026-04-16 17:18:54 -07:00
funman300 246ad03266 first commit 2026-04-16 13:32:19 -07:00
funman300 1559ee5f2b Initial commit: battlenet-manager tray daemon and CLI 2026-04-16 13:28:17 -07:00