Simple, private screenshot tool for Linux desktops.
Screenux focuses on a clean capture flow: take a screenshot, optionally annotate it, and save locally with safe defaults.
- ๐งญ Clean interface with one primary action and clear status messages
- ๐ Local-first behavior (no cloud upload flow)
- ๐ผ๏ธ Wayland-friendly capture via desktop portal APIs
- ๐ Practical folder defaults (
Pictures/Screenshots, then Home fallback)
- Capture with
Take Screenshot - Default global hotkey:
Ctrl+Print - Status updates:
Ready,Capturing...,Saved: <path>,Cancelled,Failed: <reason> - Built-in editor for quick annotations (shapes/text)
- Screenshot preview/editor opens in a separate window from the main app controls
- Editor toolbar tool icons use bundled light/dark PNG assets with theme-aware selection (fallback when SVG loaders are unavailable in some
.debruntimes) - Editor color picker supports older GTK4 runtimes used by some distro
.debinstalls - Editor zoom controls with
Best fitand quick presets (33%to2000%) - Timestamped output names with safe, non-overwriting writes
- Packaged app icon for desktop launcher integration
- Wider default window for comfortable hotkey editing, with centered initial presentation (best-effort by desktop/session)
This is the clearest end-user path if you want a system install (/usr/bin, app launcher, icon).
Requirements:
- Docker
sudoaccessdpkg(already present on Debian/Ubuntu)
- Build the package:
mkdir -p dist-deb && docker build -f Dockerfile.deb -t app-deb . && docker run --rm -v "$PWD/dist-deb:/out" app-deb- Install the generated
.deb:
sudo dpkg -i dist-deb/screenux-screenshot_*_amd64.deb- If
dpkgreports missing dependencies, fix and retry:
sudo apt-get install -f -y
sudo dpkg -i dist-deb/screenux-screenshot_*_amd64.deb- Validate the install:
screenux-screenshot --help || true
which screenux-screenshotExpected after install:
- CLI available at
/usr/bin/screenux-screenshot - Desktop launcher visible in app menu
- Icon installed at
/usr/share/icons/hicolor/256x256/apps/io.github.rafa.ScreenuxScreenshot.png(PNG primary)
Remove later (optional):
sudo apt remove -y screenux-screenshot./install-screenux.sh --bundle /path/to/screenux-screenshot.flatpakThe installer creates a desktop entry and installs app icons so launcher/taskbar icon lookup works reliably: PNG primary at ~/.local/share/icons/hicolor/256x256/apps/io.github.rafa.ScreenuxScreenshot.png plus complementary SVG assets in ~/.local/share/icons/hicolor/scalable/apps/ (including io.github.rafa.ScreenuxScreenshot-light.svg and io.github.rafa.ScreenuxScreenshot-dark.svg). It refreshes the local icon cache when GTK cache tools are available.
Optional GNOME Print Screen shortcut:
./install-screenux.sh --bundle /path/to/screenux-screenshot.flatpak --print-screenThis maps Print to screenux-screenshot --capture, which opens Screenux directly in the final capture/edit stage (without stopping on the main screen first).
If Screenux is already installed for your user, you can rerun:
./install-screenux.shOptional global CLI command (screenux):
sudo tee /usr/local/bin/screenux >/dev/null <<'EOF'
#!/usr/bin/env bash
/home/${USER}/dev/Screenux/screenux-screenshot
EOF
sudo chmod +x /usr/local/bin/screenux./uninstall-screenux.shPreserve app data in ~/.var/app/io.github.rafa.ScreenuxScreenshot:
./uninstall-screenux.sh --preserve-user-data- Launch the app.
- Click
Take Screenshot. - Confirm or cancel in the system screenshot flow.
- (Optional) annotate in the separate preview/editor window.
- Save and check the status line for the resulting file path.
Save folder behavior:
- Default target is
Pictures/Screenshots(created automatically when possible). - If
Pictures/Screenshotsis unavailable or not writable, Screenux falls back to Home. - You can change the destination from the app (
Save toโChangeโฆ).
Global hotkey behavior:
- Default shortcut is
Ctrl+Print. - On GNOME/Linux, when your selected shortcut matches a native screenshot binding (including clipboard variants like
Ctrl+Print), Screenux first tries to disable that native binding so your shortcut can be used directly. - If the shortcut is still unavailable, Screenux falls back to
Ctrl+Shift+S(thenCtrl+Alt+S, thenAlt+Shift+S, thenSuper+Shift+S). - On GNOME, the shortcut is persisted as a GNOME custom shortcut and works when the app is closed.
- On non-GNOME desktops, global shortcut support is best-effort while the app is running.
- Shortcut config is stored at
~/.config/screenux/settings.jsonasglobal_hotkey(nulldisables it). - While the shortcut field is focused, press the key combo and Screenux builds it automatically (example:
Ctrl + S). - You can apply with
EnterorApply. - You can return to default with
Default, or clear/disable withClear(on GNOME this also restores native Print screenshot bindings). - Set
SCREENUX_LOG_LEVEL=INFOto emit hotkey telemetry to stderr (registration resolution and--capturedetection/handling) when debugging shortcut issues.
src/screenux_screenshot.py: app entrypoint, config/path helpers, offline enforcementsrc/screenux_window.py: GTK window, portal flow, save-folder pickersrc/screenux_editor.py: annotation editor and secure file writingsrc/screenux_hotkey.py: hotkey normalization, fallback logic, and GNOME shortcut registrationtests/: automated tests for path, window, screenshot, and editor logicscreenux-screenshot: launcher script for host and Flatpak runtime
python3 -m pip install -r requirements-dev.txt./screenux-screenshotRun fast checks locally:
python3 -m py_compile src/screenux_screenshot.py
pytest -qManual sanity checklist:
- App starts with
Ready. - Capture shows
Capturing...and returns to interactive state. - Save shows
Saved: <path>and writes a file. - Cancel shows
Cancelled. - Invalid/failed portal path shows
Failed: ...with a clear reason.
Main workflow: .github/workflows/ci.yml
Runs automatically on:
- Pull requests targeting
main - Pushes to
main - Published releases
Quality gates include:
- Compile validation (
python -m compileall -q src) - Automated tests (
pytest -q) - Security checks (
bandit,pip-audit) - Shell script hardening (
ShellCheck,shfmt, policy checks, installer SHA256 artifact) - Dependency checks (
pip check, dependency review action with warning fallback when GitHub Dependency graph is disabled) - Build/package validation (launcher, Flatpak manifest, desktop entry, Docker Compose, Docker build)
- Debian package assurance (Docker
.debbuild, control/path integrity checks, no setuid/setgid/world-writable payload files, SHA256 report, startup/size budget checks)
Release artifacts workflow: .github/workflows/release-artifacts.yml
- Builds
screenux-screenshot.flatpak - Generates
screenux-screenshot.flatpak.sha256 - Verifies installability in CI before publishing
- Uploads artifacts to workflow results and GitHub Release assets
- Uses least-privilege job permissions
Use Docker for reproducible tests and checks (GUI screenshot capture is not intended for Docker):
# optional one-time setup
cp .env.example .env
export LOCAL_UID=$(id -u) LOCAL_GID=$(id -g)
docker compose build dev
docker compose run --rm dev python3 -m py_compile src/screenux_screenshot.py
docker compose run --rm dev pytest -q
docker compose run --rm dev bandit -q -r src -x testsNotes:
devruns as host UID/GID (LOCAL_UID/LOCAL_GID, default1000:1000) to avoid root-owned files.- You can store
LOCAL_UIDandLOCAL_GIDin.envto avoid exporting every session. .envmust be at repository root (same directory asdocker-compose.yml) for Compose auto-loading..envconfig is for Docker Compose only; Screenux runtime does not read it.- Python bytecode and pytest cache are disabled in container runs to reduce bind-mount noise/permission issues.
Build a .deb into ./dist-deb/:
mkdir -p dist-deb && docker build -f Dockerfile.deb -t app-deb . && docker run --rm -v "$PWD/dist-deb:/out" app-debExpected artifact:
dist-deb/screenux-screenshot_<version>_amd64.deb
Notes:
- The
.debbuild uses project-local PyInstaller hooks inpackaging/pyinstaller_hooks/to force GTK4 GI collection and avoid mixed GTK/Cairo runtime library mismatches.
Requirements:
flatpakflatpak-builder
Install tools (examples):
# Debian/Ubuntu
sudo apt-get install -y flatpak flatpak-builder
# Fedora
sudo dnf install -y flatpak flatpak-builder
# Arch
sudo pacman -S --needed flatpak flatpak-builderBuild a local bundle and install with Print Screen mapping:
make build-flatpak-bundle FLATPAK_BUNDLE=./screenux-screenshot.flatpak
make install-print-screen BUNDLE=./screenux-screenshot.flatpakmake build-flatpak-bundle now auto-checks Flatpak build deps and, when missing, installs org.gnome.Platform//47 and org.gnome.Sdk//47 from Flathub in user scope.
flatpak-builder --force-clean build-dir flatpak/io.github.rafa.ScreenuxScreenshot.json
flatpak-builder --run build-dir flatpak/io.github.rafa.ScreenuxScreenshot.json screenux-screenshotFlatpak permissions stay intentionally narrow (portal access + Desktop filesystem).
- Offline-only runtime behavior blocks networking and DNS calls.
- Screenshot sources are validated as local, readable
file://URIs. - Config parsing is defensive (invalid/non-object/oversized files are ignored).
- Save operations use exclusive file creation to avoid accidental overwrite.
See CONTRIBUTING.md for development workflow and PR guidance.
This project is licensed under the MIT License. See LICENSE.