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
5 changes: 4 additions & 1 deletion .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[build]
# Static-link the C runtime so the Windows .exe ships with zero runtime
# dependencies (msvcrt). Scoped to Windows only — on Linux/macOS forcing
# crt-static pulls in static libc which most systems don't ship.
[target.'cfg(windows)']
rustflags = ["-C", "target-feature=+crt-static"]
121 changes: 120 additions & 1 deletion .github/workflows/rust.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,129 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# Build Linux binary and produce distro packages:
# portable .tar.gz, .deb, .rpm, and a portable AppImage.
build-linux:
name: Build Linux packages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true

- name: Build release binary
run: cargo build --release

- name: Build portable tarball
run: ./build_linux.sh

- name: Install packaging tools
run: |
# cargo-deb builds .deb packages
cargo install --locked cargo-deb
# cargo-generate-rpm builds .rpm packages
cargo install --locked cargo-generate-rpm

- name: Build .deb
run: cargo deb --no-strip --output RustCat-linux-x86_64.deb

- name: Build .rpm
run: cargo generate-rpm

- name: Stage .rpm artifact
shell: bash
run: |
mkdir -p rpm-out
cp target/generate-rpm/*.rpm rpm-out/RustCat-linux-x86_64.rpm

- name: Build AppImage
shell: bash
run: |
set -e
mkdir -p appimage/AppDir/usr/bin
mkdir -p appimage/AppDir/usr/share/applications
mkdir -p appimage/AppDir/usr/share/icons
cp target/release/rust_cat appimage/AppDir/usr/bin/
cp assets/rustcat.desktop appimage/AppDir/usr/share/applications/
cp assets/appIcon.ico appimage/AppDir/usr/share/icons/rustcat.ico
# AppImage desktop entry must point to the binary inside the AppDir
sed -i 's|^Exec=.*|Exec=rust_cat|' appimage/AppDir/usr/share/applications/rustcat.desktop

ARCH=$(uname -m)
export VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
export OUTPUT=RustCat-${VERSION}-${ARCH}.AppImage
wget -qO linuxdeploy https://github.com/linuxdeploy/linuxdeploy/releases/latest/download/linuxdeploy-${ARCH}.AppImage
chmod +x linuxdeploy
./linuxdeploy --appdir appimage/AppDir \
--desktop-file appimage/AppDir/usr/share/applications/rustcat.desktop \
--icon-file appimage/AppDir/usr/share/icons/rustcat.ico \
--output appimage
mv appimage/*.AppImage "${OUTPUT}"

- name: Check for release
id: is-release
shell: bash
run: |
unset IS_RELEASE
if [[ $GITHUB_REF =~ ^refs/tags/v[0-9].* ]]; then IS_RELEASE='true'; fi
echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_OUTPUT

- name: Upload Linux tarball
uses: actions/upload-artifact@v4
with:
name: RustCat-linux-x86_64.tar.gz
path: RustCat-*.tar.gz
if-no-files-found: error

- name: Upload Linux .deb
uses: actions/upload-artifact@v4
with:
name: RustCat-linux-x86_64.deb
path: RustCat-linux-x86_64.deb
if-no-files-found: error

- name: Upload Linux .rpm
uses: actions/upload-artifact@v4
with:
name: RustCat-linux-x86_64.rpm
path: rpm-out/RustCat-linux-x86_64.rpm
if-no-files-found: error

- name: Upload Linux AppImage
uses: actions/upload-artifact@v4
with:
name: RustCat-x86_64.AppImage
path: RustCat-*.AppImage
if-no-files-found: error

- name: Publish Linux release
uses: softprops/action-gh-release@v1
if: steps.is-release.outputs.IS_RELEASE
with:
files: |
RustCat-*.tar.gz
RustCat-linux-x86_64.deb
rpm-out/RustCat-linux-x86_64.rpm
RustCat-*.AppImage
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# Auto-approve Dependabot PRs if build succeeds
dependabot-auto-approve:
runs-on: ubuntu-latest
needs: [build-windows, build-macos]
needs: [build-windows, build-macos, build-linux]
if: github.actor == 'dependabot[bot]' && github.event_name == 'pull_request_target'
steps:
- name: Auto-approve PR
Expand Down
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,40 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.4.0] - 2026-07-07

### Added

- Linux/KDE support via the freedesktop StatusNotifierItem (SNI) protocol over
D-Bus — the tray cat now runs natively on KDE Plasma (and other SNI-aware
trays)
- Linux platform module mirroring the Windows/macOS architecture: CPU usage
from `/proc/stat`, settings persisted to `~/.config/rustcat/settings.conf`,
autostart via a freedesktop `~/.config/autostart/rustcat.desktop` file
- Native KDE integration: dark/light theme detection via `kreadconfig`,
dialogs via `kdialog`, system monitor via `plasma-systemmonitor`
- Nix flake (`flake.nix`) using crane, with `packages`, `devShells`, and `apps`
outputs; runtime helper tools wrapped onto `PATH`
- Distro packaging for Linux: `.deb` (cargo-deb), `.rpm` (cargo-generate-rpm),
and a portable AppImage (linuxdeploy), all built in CI alongside the
portable `.tar.gz` bundle
- `assets/rustcat.desktop` freedesktop entry and `build_linux.sh` build script

### Changed

- Scoped the `crt-static` rustflag to Windows only (`.cargo/config.toml`); it
was intended for MSVC static linking and forced static-glibc linking on
Linux, which most distros (and Nix) don't provide
- Refactored `ui_update` in `app.rs` to apply to all non-macOS targets
(Windows + Linux), since the trayicon D-Bus backend is thread-safe
- Added standard crates.io metadata (description, license, repository,
keywords, categories) to `Cargo.toml`

### Fixed

- Linux builds now link dynamically against glibc instead of failing on
missing static libc

## [2.3.0] - 2025-07-10

### Added
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 50 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
[package]
name = "rust_cat"
version = "2.3.0"
version = "2.4.0"
edition = "2021"
description = "An animated tray cat (or parrot) whose animation speed tracks real-time CPU usage"
license = "Apache-2.0"
authors = ["Bearice Ren"]
repository = "https://github.com/bearice/RustCat"
homepage = "https://github.com/bearice/RustCat"
keywords = ["tray", "cpu", "monitor", "cat", "kde"]
categories = ["system-monitoring", "gui"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand All @@ -13,6 +20,9 @@ flate2 = "1.1"
[target.'cfg(windows)'.dependencies]
winreg = "0.56.0"

[target.'cfg(target_os = "linux")'.dependencies]
dirs = "6.0"
Comment on lines +23 to +24

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Add libc dependency for Linux to enable highly efficient local time querying without spawning external processes.

[target.'cfg(target_os = "linux")'.dependencies]
dirs = "6.0"
libc = "0.2"


[target.'cfg(target_os = "macos")'.dependencies]
objc2 = "0.6"
objc2-app-kit = { version = "0.3" }
Expand Down Expand Up @@ -47,3 +57,42 @@ winres = "0.1"
OriginalFilename = "rust_cat.exe"
FileDescription = "💻 => 🐈😺😸😹😻😼😽🐈‍⬛"
LegalCopyright = "Copyright © 2021 Bearice Ren <https://github.com/bearice/RustCat>"

# --- Debian package metadata (cargo-deb) ---
# Build with: cargo deb
[package.metadata.deb]
name = "rustcat"
maintainer = "Bearice Ren <https://github.com/bearice/RustCat>"
copyright = "2021-2026, Bearice Ren <https://github.com/bearice/RustCat>"
section = "utils"
priority = "optional"
extended-description = """\
RustCat is a lightweight tray application that displays an animated cat or \
parrot icon whose animation speed reflects real-time CPU usage. On Linux it \
integrates with KDE Plasma's system tray via the freedesktop \
StatusNotifierItem (SNI) protocol over D-Bus."""
# The binary only needs the standard C runtime; the helper tools are
# recommended (the app degrades gracefully if they are absent).
depends = "$auto"
recommends = "kdialog, plasma-systemmonitor | ksysguard | gnome-system-monitor"
assets = [
["target/release/rust_cat", "usr/bin/", "755"],
["assets/rustcat.desktop", "usr/share/applications/", "644"],
["assets/appIcon.ico", "usr/share/pixmaps/rustcat.ico", "644"],
["README.md", "usr/share/doc/rustcat/README", "644"],
["LICENSE", "usr/share/doc/rustcat/LICENSE", "644"],
]

# --- RPM package metadata (cargo-generate-rpm) ---
# Build with: cargo generate-rpm
[package.metadata.generate-rpm]
name = "rustcat"
license = "Apache-2.0"
summary = "Animated tray cat whose speed tracks CPU usage"
description = "RustCat displays an animated cat or parrot tray icon whose animation speed reflects real-time CPU usage. Integrates with KDE Plasma via the StatusNotifierItem D-Bus protocol."
recommends = ["kdialog", "plasma-systemmonitor"]
assets = [
{ source = "target/release/rust_cat", dest = "/usr/bin/rust_cat", mode = "755" },
{ source = "assets/rustcat.desktop", dest = "/usr/share/applications/rustcat.desktop", mode = "644" },
{ source = "assets/appIcon.ico", dest = "/usr/share/pixmaps/rustcat.ico", mode = "644" },
]
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,44 @@ Speedy Cat Visuals: Watch your system load as a tiny cat dashes across your task

No Runtime Baggage: Written in Rust, so it’s leaner than a whisker. (In human language: It's small and uses less memory.)

Platform Flair: Supports Windows and macOS with native theme detection.
Platform Flair: Supports Windows, macOS, and Linux/KDE with native theme detection.

Auto Theme Matching: Your cat’s colors shift with your system's light/dark mode — drama-free style.

## 🧩 Installation
Visit the [Releases page](https://github.com/bearice/RustCat/releases) and grab the file. Double-click, and let the cat out.

### Linux / KDE

On Linux the tray icon uses the freedesktop StatusNotifierItem (SNI) protocol over D-Bus, so it integrates natively with KDE Plasma's system tray.

```bash
# from source (needs a Rust toolchain)
cargo build --release
# the binary is at target/release/rust_cat
```

### Nix / NixOS

A `flake.nix` is provided:

```bash
# run directly
nix run github:bearice/RustCat

# build into a profile / your config
nix build .#default
# -> result/bin/rust_cat, plus result/share/applications/rustcat.desktop

# dev shell
nix develop
```

Runtime helper tools (`kdialog`, `plasma-systemmonitor`) are wrapped onto `PATH` automatically; the app degrades gracefully if a tool is missing.

> Build note for packagers: the repo's `.cargo/config.toml` only enables
> `crt-static` on Windows. On Linux the build links dynamically against glibc,
> which is what most distros (and Nix) expect.

## 💬 Quote from the Dev
“RustCat doesn’t monitor your CPU. It vibes with it.” — Bearice
9 changes: 9 additions & 0 deletions assets/rustcat.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Desktop Entry]
Type=Application
Name=RustCat
Comment=CPU usage monitor with an animated tray cat
Exec=rust_cat
Icon=rustcat
Terminal=false
Categories=System;Monitor;
Keywords=CPU;monitor;tray;cat;
58 changes: 58 additions & 0 deletions build_linux.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/bash

# Linux build script for RustCat
# This script handles all Linux-specific build tasks including:
# - Building the release binary
# - Bundling the binary with a .desktop entry and icon into a tarball
#
# The binary is dynamically linked against glibc. For a fully portable,
# reproducible build use the Nix flake (`nix build .#default`).

set -e

echo "🐧 Starting Linux build process..."

# Configuration
APP_NAME="RustCat"
BIN_NAME="rust_cat"
VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
ARCH=$(uname -m)
PKG_DIR="${APP_NAME}-${VERSION}-linux-${ARCH}"

echo "📦 Building release binary..."
cargo build --release

# Verify the binary
echo "✅ Verifying binary..."
file target/release/${BIN_NAME}

# Assemble a redistributable package directory
echo "🎨 Assembling package..."
rm -rf "${PKG_DIR}"
mkdir -p "${PKG_DIR}/bin" "${PKG_DIR}/share/applications" "${PKG_DIR}/share/pixmaps"

cp target/release/${BIN_NAME} "${PKG_DIR}/bin/"
cp assets/rustcat.desktop "${PKG_DIR}/share/applications/"
cp assets/appIcon.ico "${PKG_DIR}/share/pixmaps/rustcat.ico"

# A small install/uninstall helper for users not on Nix
cat > "${PKG_DIR}/install.sh" <<'INSTALL_EOF'
#!/bin/bash
# Simple installer: copies the bundled files into ~/.local
set -e
PREFIX="${PREFIX:-${HOME}/.local}"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

install -Dm755 "${SCRIPT_DIR}/bin/rust_cat" "${PREFIX}/bin/rust_cat"
install -Dm644 "${SCRIPT_DIR}/share/applications/rustcat.desktop" "${PREFIX}/share/applications/rustcat.desktop"
install -Dm644 "${SCRIPT_DIR}/share/pixmaps/rustcat.ico" "${PREFIX}/share/pixmaps/rustcat.ico"

echo "Installed RustCat to ${PREFIX}. Run with: rust_cat"
INSTALL_EOF
chmod +x "${PKG_DIR}/install.sh"

# Archive it
echo "📦 Creating tarball..."
tar -czf "${PKG_DIR}.tar.gz" "${PKG_DIR}"

echo "✅ Done: ${PKG_DIR}.tar.gz"
Loading
Loading