# Flatpak manifest for LinuxPop. # # Build locally with: # flatpak-builder --user --install --force-clean build-dir \ # packaging/flatpak/io.github.GaimsDevSoftware.LinuxPop.yml # # Then run: # flatpak run io.github.GaimsDevSoftware.LinuxPop # # Notes: # - LinuxPop runs on X11 and on KDE Plasma 6 / Wayland. The sandbox # needs both sockets, keyboard simulation, system tray, notifications, # and (on KDE) talk access to KWin + KGlobalAccel. # - xdotool / xclip / wmctrl are not in any freedesktop runtime. They # are built as Flatpak modules below from upstream source. # - X11 global hotkey grabs work via the forwarded X11 socket. # # Wayland/KDE tools (bundled below, like the X11 ones): # - wl-clipboard (wl-copy/wl-paste): selection + clipboard on Wayland, # the way the X11 backend uses xclip. Works over the shared Wayland # socket inside the sandbox. # - gtk-layer-shell: positions the popup as a layer-shell surface # (a plain GTK window can't be placed on Wayland). Built with # introspection so the GtkLayerShell-0.1 typelib is loadable by gi. # - ydotool + ydotoold: key injection on Wayland. ydotoold writes to # /dev/uinput, which needs the --device=all finish-arg. Bundling it # means users don't have to install a host tool for Cut/Paste/Backspace. # Neither ships in GNOME Platform 46, so both are built from source # below with real upstream URL + sha256. # # RUNTIME VERSION: org.gnome.Platform//49 (GNOME 46 is EOL). 49 was # verified to ship GTK3, the Handy-1 typelib, and python3-gi on Python # 3.13 - the bundled Python wheels are cp313 to match. Re-check the # latest supported GNOME runtime at submission time. app-id: io.github.GaimsDevSoftware.LinuxPop runtime: org.gnome.Platform # GNOME 46 went EOL (2025-04); 49 is current and verified to still ship # GTK3 (libgtk-3 + Gtk-3.0 typelib), the Handy-1 typelib, and python3-gi # (Python 3.13). The bundled Python wheels below must match cp313. runtime-version: '49' sdk: org.gnome.Sdk command: linuxpop finish-args: - --share=ipc # Dual X11/Wayland: the Flathub-recommended combo is wayland + fallback-x11 # (NOT a plain x11 socket alongside them). fallback-x11 grants X11 only when # there's no Wayland session, so the X11 backend still works under XWayland. - --socket=wayland # KDE Plasma 6 Wayland backend - --socket=fallback-x11 # X11 / XWayland backend - --socket=pulseaudio # text-to-speech (espeak-ng) audio output - --share=network # AI plugins (ChatGPT URL prefill, Ollama, etc.) # No filesystem perms: the app's ~/.config/linuxpop + ~/.cache/linuxpop # resolve to the sandbox's private per-app XDG dirs automatically (the # linter flags explicit grants for these as unnecessary). xdg-open of # selected paths/URLs goes through the OpenURI portal. - --talk-name=org.freedesktop.Notifications - --talk-name=org.kde.StatusNotifierWatcher # system tray - --talk-name=org.kde.kglobalaccel # global hotkey (linter-accepted) # The ONE finish-arg that needs a Flathub exception. The native Wayland # popup anchors at the cursor / selection via KWin scripting # (workspace.cursorPos). Wayland exposes NO protocol or XDG portal for a # client's global pointer position, and KWin scripting is the only source on # Plasma - hence the talk-name. Requested explicitly in the submission PR. - --talk-name=org.kde.KWin # App-scoped runtime dir (NOT host /tmp) for the tiny, short-lived script # KWin's loadScript() reads by path: $XDG_RUNTIME_DIR/linuxpop maps to the # same host path KWin can open, so no broad /tmp grant is needed and the # host-tmp linter error is gone. - --filesystem=xdg-run/linuxpop:create # NOTE: no broad home/host filesystem grant. "Open file/folder" runs the # host's xdg-open via flatpak-spawn --host (see actions.open_path), so it # opens any selected path without home access - and the app keeps its own # writable per-app config dir (a home grant would flip $HOME to the real, # here read-only, home and break settings writes). - --env=TESSDATA_PREFIX=/app/share/tessdata # bundled OCR language data (eng+nor) # "Run in terminal" / run-command actions execute the user's command on the # HOST through flatpak-spawn --host. Running it inside the sandbox would hit # the wrong tools and filesystem. This is a broad permission, but it is # exactly what the feature does, and LinuxPop is self-distributed (not Flathub). - --talk-name=org.freedesktop.Flatpak - --device=dri # GTK rendering - --device=all # ydotoold needs /dev/uinput for key injection cleanup: - /include - /share/man - /share/doc - /lib/pkgconfig - '*.la' - '*.a' modules: # ----- libhandy 1 ----- # GNOME's adaptive widget toolkit. LinuxPop uses Hdy.PreferencesWindow # and Hdy.ActionRow. - name: libhandy buildsystem: meson config-opts: - -Dexamples=false - -Dtests=false - -Dvapi=false sources: - type: archive url: https://download.gnome.org/sources/libhandy/1.8/libhandy-1.8.3.tar.xz sha256: 05b497229073ff557f10b326e074c5066f8743a302d4820ab97bcb5cd2dab087 # ----- libayatana-appindicator (via Flathub shared-modules) ----- # System tray support. This pulls in libdbusmenu, ayatana-ido, # libayatana-indicator, intltool, and libayatana-appindicator itself, # all configured the way Flathub's reviewers know works. Trying to # hand-roll this fights two layers of missing pkgconfig deps that # aren't in GNOME 46 Platform. - shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json # ----- xdotool ----- # Keyboard simulation. Used by AI plugins for paste mode and by # clipboard-picker's "paste at cursor" feature. - name: xdotool buildsystem: simple build-commands: - make PREFIX=/app - make PREFIX=/app install sources: - type: archive url: https://github.com/jordansissel/xdotool/releases/download/v3.20211022.1/xdotool-3.20211022.1.tar.gz sha256: 96f0facfde6d78eacad35b91b0f46fecd0b35e474c03e00e30da3fdd345f9ada # ----- libXmu ----- # xclip's configure script needs X11/Xmu/Atoms.h, which isn't in the # GNOME 46 SDK. libXmu is a small X.org library; build it here so # xclip below finds the header. - name: libXmu buildsystem: autotools sources: - type: archive url: https://www.x.org/releases/individual/lib/libXmu-1.3.1.tar.xz sha256: 81a99e94c4501e81c427cbaa4a11748b584933e94b7a156830c3621256857bc4 cleanup: - /include - /lib/pkgconfig - "*.la" # ----- xclip ----- # X11 clipboard. LinuxPop reads PRIMARY/CLIPBOARD via this. - name: xclip buildsystem: autotools sources: - type: archive url: https://github.com/astrand/xclip/archive/refs/tags/0.13.tar.gz sha256: ca5b8804e3c910a66423a882d79bf3c9450b875ac8528791fb60ec9de667f758 # ----- wmctrl ----- # Window listing/activation for the AI paste flow. The dvdhrm/wmctrl # GitHub mirror went 404, and the original Tomas Styblo SourceForge # project's download URLs are also broken now. Debian's source pool # is the most reliable upstream that's still available. - name: wmctrl buildsystem: autotools sources: - type: archive url: https://deb.debian.org/debian/pool/main/w/wmctrl/wmctrl_1.07.orig.tar.gz sha256: d78a1efdb62f18674298ad039c5cbdb1edb6e8e149bb3a8e3a01a4750aa3cca9 # ----- python-xlib ----- # Pure-Python Xlib bindings. Used for XFixes selection events + the # raw keysym/keycode dance in hotkey.py. # # Use the pre-built wheel from PyPI rather than the sdist. The sdist's # setup.py declares setup_requires=['setuptools-scm'] which causes # setuptools to phone home for that build-time dep -- and Flatpak's # build sandbox has no network, so it fails. - name: python3-xlib buildsystem: simple build-commands: - pip3 install --prefix=/app --no-build-isolation --no-deps python_xlib-0.33-py2.py3-none-any.whl sources: - type: file url: https://files.pythonhosted.org/packages/fc/b8/ff33610932e0ee81ae7f1269c890f697d56ff74b9f5b2ee5d9b7fa2c5355/python_xlib-0.33-py2.py3-none-any.whl sha256: c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398 # ----- pillow (for QR code plugin) ----- # Use the manylinux wheel rather than the sdist. Pillow's sdist # requires a full C toolchain + JPEG/PNG/freetype/etc. headers; the # manylinux wheel is self-contained and pip-installable as-is. - name: python3-pillow buildsystem: simple build-commands: - pip3 install --prefix=/app --no-build-isolation --no-deps pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl sources: - type: file url: https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl sha256: 03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9 # ----- gtk-layer-shell ----- # Positions the popup as a Wayland layer-shell surface - the Wayland/KDE # backend can't place a plain GTK window. introspection=true so the # GtkLayerShell-0.1 typelib is available to PyGObject; vapi off to avoid # a vala/vapigen build dep. Builds against the runtime's GTK3. - name: gtk-layer-shell buildsystem: meson config-opts: - -Dexamples=false - -Ddocs=false - -Dtests=false - -Dvapi=false - -Dintrospection=true sources: - type: archive url: https://github.com/wmww/gtk-layer-shell/archive/refs/tags/v0.10.1.tar.gz sha256: 88c3a3e0a5300532f3d368d5df64838a87f1fb85273f22d41df0a6b8d0ec59c6 # ----- wl-clipboard ----- # wl-copy / wl-paste - selection + clipboard capture on Wayland, the way # the X11 backend uses xclip. Pure meson, only needs wayland-client + # wayland-protocols from the SDK. - name: wl-clipboard buildsystem: meson config-opts: # Shell completions auto-detect absolute /usr paths (fish in # particular), which fail to install into the /app prefix in the # build sandbox. The app only needs the wl-copy/wl-paste binaries. - -Dfishcompletiondir=no - -Dzshcompletiondir=no sources: - type: archive url: https://github.com/bugaevc/wl-clipboard/archive/refs/tags/v2.3.0.tar.gz sha256: b4dc560973f0cd74e02f817ffa2fd44ba645a4f1ea94b7b9614dacc9f895f402 # ----- ydotool ----- # Key injection on Wayland. ydotool v1.x is pure C99 with no external # dependencies and only needs CMake. The companion ydotoold daemon holds # a persistent uinput device; the wrapper script starts it at launch. # --device=all grants /dev/uinput access inside the sandbox. # We patch out the manpage subdir so we don't need scdoc in the SDK. - name: ydotool buildsystem: cmake-ninja config-opts: - -DCMAKE_POLICY_VERSION_MINIMUM=3.5 sources: - type: archive url: https://github.com/ReimuNotMoe/ydotool/archive/refs/tags/v1.0.4.tar.gz sha256: ba075a43aa6ead51940e892ecffa4d0b8b40c241e4e2bc4bd9bd26b61fde23bd - type: patch path: ydotool-no-docs.patch # ----- python3-dbus (dbus-python) ----- # The Wayland/KDE backend uses dbus-python for KGlobalAccel (global # hotkeys) and KWin scripting over D-Bus; it is NOT in the GNOME runtime, # so without it global hotkeys + KWin cursor/active-window queries fail in # the sandbox ("No module named 'dbus'"). dbus-gmain is vendored in the # tarball, so the meson build needs no network. - name: python3-dbus buildsystem: meson sources: - type: archive url: https://dbus.freedesktop.org/releases/dbus-python/dbus-python-1.3.2.tar.gz sha256: ad67819308618b5069537be237f8e68ca1c7fcc95ee4a121fe6845b1418248f8 # ----- PySide6 (Qt6) for the system-tray subprocess ----- # tray_qt.py uses QSystemTrayIcon (StatusNotifierItem + DBusMenu) and # QtSvg to recolour the tray glyph. The GNOME runtime has no Qt, so bundle # PySide6-Essentials (Core/Gui/Widgets/DBus/Svg + platform plugins) and # its shiboken6 runtime. abi3 wheels -> run on the runtime's Python 3.13. # Without this the tray subprocess dies ("could not connect") in-sandbox. - name: python3-pyside6 buildsystem: simple build-commands: - pip3 install --prefix=/app --no-build-isolation --no-deps shiboken6-6.11.1-cp310-abi3-manylinux_2_34_x86_64.whl pyside6_essentials-6.11.1-cp310-abi3-manylinux_2_34_x86_64.whl sources: - type: file url: https://files.pythonhosted.org/packages/c7/9b/e0355d8897b5c150770f1d95718aad17d432fcc9c035c04f3f58427d4693/shiboken6-6.11.1-cp310-abi3-manylinux_2_34_x86_64.whl sha256: 9a8bccfafc8805254cabcfa1edfaf55cd52889f4998c91ad0d9a4433fb1bcdbe - type: file url: https://files.pythonhosted.org/packages/5c/49/0e1237c4400bec7e335d2c4eeb49bc40d9fd88a9ac44ca9083ce1abdc308/pyside6_essentials-6.11.1-cp310-abi3-manylinux_2_34_x86_64.whl sha256: e3ef7027b41e4e55fadb56e3b3257dc8ee92154b639fe67fc4c8e05e9d976c60 # ----- LinuxPop itself ----- # ----- QR code generator (qr_code plugin) ----- - name: qrencode buildsystem: autotools sources: - type: archive url: https://github.com/fukuchi/libqrencode/archive/refs/tags/v4.1.1.tar.gz sha256: 5385bc1b8c2f20f3b91d258bf8ccc8cf62023935df2d2676b5b67049f31a049c cleanup: - /include - /lib/pkgconfig - '*.la' # ----- Text-to-speech (speak plugin): pcaudiolib (audio backend) + espeak-ng ----- - name: pcaudiolib buildsystem: autotools sources: - type: archive url: https://github.com/espeak-ng/pcaudiolib/archive/refs/tags/1.2.tar.gz sha256: 44b9d509b9eac40a0c61585f756d76a7b555f732e8b8ae4a501c8819c59c6619 cleanup: - /include - /lib/pkgconfig - '*.la' - name: espeak-ng buildsystem: autotools sources: - type: archive url: https://github.com/espeak-ng/espeak-ng/archive/refs/tags/1.51.tar.gz sha256: f0e028f695a8241c4fa90df7a8c8c5d68dcadbdbc91e758a97e594bbb0a3bdbf cleanup: - /include - /lib/pkgconfig - '*.la' # ----- Screen OCR: leptonica + tesseract + language data (eng, nor) ----- # Capture happens through the XDG Screenshot portal (no screenshot binary # needed in the sandbox); these provide the recognition engine. - name: leptonica buildsystem: autotools sources: - type: archive url: https://github.com/DanBloomberg/leptonica/releases/download/1.85.0/leptonica-1.85.0.tar.gz sha256: 3745ae3bf271a6801a2292eead83ac926e3a9bc1bf622e9cd4dd0f3786e17205 cleanup: - /include - /lib/pkgconfig - '*.la' - name: tesseract buildsystem: autotools config-opts: - --disable-openmp - --disable-legacy sources: - type: archive url: https://github.com/tesseract-ocr/tesseract/archive/refs/tags/5.5.0.tar.gz sha256: f2fb34ca035b6d087a42875a35a7a5c4155fa9979c6132365b1e5a28ebc3fc11 cleanup: - /include - /lib/pkgconfig - '*.la' - name: tessdata buildsystem: simple build-commands: - install -Dm644 eng.traineddata /app/share/tessdata/eng.traineddata - install -Dm644 nor.traineddata /app/share/tessdata/nor.traineddata sources: - type: file url: https://github.com/tesseract-ocr/tessdata_fast/raw/main/eng.traineddata sha256: 7d4322bd2a7749724879683fc3912cb542f19906c83bcc1a52132556427170b2 - type: file url: https://github.com/tesseract-ocr/tessdata_fast/raw/main/nor.traineddata sha256: 0451eb4f8049ae78196806bf878a389a2f40f1386fe038568cf4441226ba6ef2 - name: linuxpop buildsystem: simple build-commands: # 1. Copy the Python sources into the runtime - mkdir -p /app/share/linuxpop # NB: platform_backend/ is a package dir (X11 + Wayland/KDE backends) # - `*.py` alone misses it and the app dies with ModuleNotFoundError. # No `|| true` here: a missing source should fail the build loudly. - cp -r *.py platform_backend icons plugins_repo /app/share/linuxpop/ # 1b. Bake the version. The sandbox has no .git at runtime, so resolve it # now (git tag, with a metainfo fallback) into a _version.py the app # reads first - keeps the About dialog in sync with the release. - bash -c 'printf "VERSION = \"%s\"\n" "$(bash packaging/gen-version.sh)" > /app/share/linuxpop/_version.py' # 2. Wrapper script on PATH - install -Dm755 packaging/flatpak/linuxpop.wrapper /app/bin/linuxpop # 3. Desktop entry + AppStream metadata - install -Dm644 packaging/io.github.GaimsDevSoftware.LinuxPop.desktop /app/share/applications/io.github.GaimsDevSoftware.LinuxPop.desktop - install -Dm644 packaging/io.github.GaimsDevSoftware.LinuxPop.metainfo.xml /app/share/metainfo/io.github.GaimsDevSoftware.LinuxPop.metainfo.xml # 4. App icon at the canonical Flatpak path - install -Dm644 icons/linuxpop.svg /app/share/icons/hicolor/scalable/apps/io.github.GaimsDevSoftware.LinuxPop.svg sources: - type: dir path: ../..