Skip to content

Refactor whole project - #1

Merged
ahsand97 merged 15 commits into
masterfrom
wip-refactor-whole-codebase
Aug 31, 2026
Merged

ahsand97 merged 15 commits into
masterfrom
wip-refactor-whole-codebase

Conversation

@ahsand97

Copy link
Copy Markdown
Owner

🪟 orcsome3: Python 3 rewrite, Cython backend, and a round of stability fixes

This PR ports my previous implementation of orcsome3 to a modern Python 3 / Cython foundation and adds a bunch of features and fixes on top.


🏗️ The rewrite itself

  • 🔥 Deleted the old orcsome3/orcsome/*.py (ctypes-based) implementation entirely.
  • 🧬 New Cython backend (orcsome3_backend.pyx + .pxd bindings) wrapping Xlib, libev, Cairo, ImageMagick 7, gd, and resvg.
  • 🐍 Typed Python wrapper layers on top: orcsome3/libs/xlib.py and orcsome3/libs/ev.py — the only two modules allowed to import the compiled .so.
  • 🪟 Brand-new window_manager.py: WindowManager, Window, decorators (on_key, on_button, on_create, on_manage, on_destroy, on_property_change, on_focus/on_unfocus, on_map/on_unmap/on_configure, on_client_message, on_timer, on_init/on_deinit).
  • 🔔 New notify.py — Freedesktop desktop notifications over dbus-next.
  • 🖱️ New tray.py — a StatusNotifierItem tray icon + dbusmenu, previously nonexistent.
  • 📐 Fully typed: orcsome3_backend.pyi stub (auto-generated from the .pyx via tools/generate_backend_stub.py), py.typed shipped, everything passes mypy + basedpyright (typeCheckingMode = "all").

📦 Packaging & native build system

  • 🆕 pyproject.toml (PEP 517/621), MANIFEST.in, Makefile — full modernization, replacing the old setup.cfg.
  • ⚙️ New orcsome3/libs/build/ — a whole native build engine that downloads and statically links libev, Cairo, ImageMagick 7, gd, resvg (and their image-format dependencies: libpng, libjpeg-turbo, libwebp, libtiff, openjpeg, djvulibre, openexr, libraw, …) directly into orcsome3_backend.so.
  • ✅ Net effect: the only external runtime dependency is X11 (libX11, libXext, libXss, libXtst). pip install orcsome3 needs nothing else — no separate ImageMagick/Cairo/libev install, ever.
  • 🎨 Because ImageMagick is statically linked, Window.set_icon() accepts SVG or PNG/JPEG/GIF/BMP/ICO/WebP/TIFF/… — not just one format.
  • 🧰 New make targets: native / native-fast / native-rebuild, stubs / stubs-check, format / format-check, dev, run, test.

🧪 Type-checking & lint tooling

  • 🔍 tools/check_named_args.py — signature-aware checker that fails on func(a) when func(a=a) is possible (project-wide keyword-args convention).
  • 🔎 tools/check_explicit_types.py — enforces explicit annotations on local/module bindings, even redundant ones (a: int = 5).
  • 📝 tools/generate_backend_stub.py — generates orcsome3_backend.pyi straight from the .pyx AST, real Enum classes instead of Any.
  • 🧪 tests/test_unit.py + tests/test_x11.py — new test suite (X11 tests skip cleanly with no $DISPLAY; grab-delivery tests prefer a nested Xephyr/Xvfb server).

✨ Tray icon overhaul

  • 🖼️ Icons are rasterized on the fly via resvg straight into the D-Bus IconPixmap/AttentionIconPixmap bytes — no files are ever copied to ~/.local/share/icons, so nothing is left behind on uninstall.
  • 🔴 New orcsome3-error.svg (red-dot badge) shown as a real AttentionIcon while the config has a load error, plus a matching tooltip.
  • 📋 New right-click menu rows: Config: <path> (which file is loaded) and, while broken, Error: ... (the exact exception).
  • 📂 New Change config file... row — only appears if zenity or kdialog is installed (zero forced dependency) — switches the active config live via a new SIGUSR2 signal, no restart.
  • 🔔 Fires a real desktop notification on a config error too, when a notification daemon is available.

🧭 Decorator API unification

  • 🔁 on_create / on_manage / on_destroy / on_property_change / on_focus / on_unfocus now all call back as (window, event), matching on_key / on_button / on_map / on_unmap / on_configure / on_client_message — one consistent pattern across every decorator.
  • ❓ on_create's event is Optional[XCreateWindowEvent] — None during the startup sweep of pre-existing windows (there's no real CreateNotify for those); on_manage skips that sweep, so its event is always real.
  • 🪝 wm.event_window still works as a fallback side-channel.

…sage, and enhance orcsome3_backend.pyi with new event types. Update .gitignore and pyproject.toml for build adjustments.
…hecks, update CI gates documentation, and modify test files to suppress exceptions. Adjust skip settings in type checking tools to exclude tests.
Runtime fixes:
- Fix use-after-free crash on startup/on window-open: WindowManager's X11
  IOWatcher was a bare local variable, freed by the GC while libev still held
  a pointer to it (confirmed via coredumpctl: SIGSEGV in fd_reify).
- Release the GIL around the main event loop (nogil ev_run) so the
  notification/tray D-Bus thread is no longer starved while the loop is idle
  — this is what caused the tray icon to render wrong/blank until some X11
  activity happened to interrupt the loop.
- Fix a shutdown race where a still-running daemon thread writing to stdout
  during interpreter finalization aborted the process (SIGABRT).
- Restore working SIGINT handling in run.py (left disabled by leftover debug
  code) and remove the debug print spam that caused a runaway CPU loop.
- A broken config.py (syntax error, exception on import) no longer crashes
  orcsome3 on the first load or on reload — it logs the error and keeps
  running with no handlers installed until the next successful reload.

Tray icon:
- Rewrite icon delivery: SVGs are rasterized on the fly via resvg (already a
  build dependency) straight into the D-Bus IconPixmap/AttentionIconPixmap —
  nothing is copied into ~/.local/share/icons, so nothing is left behind on
  uninstall.
- Add a hand-authored orcsome3-error.svg (red-dot badge), shown via a real
  AttentionIcon while the config has a load error, plus a tooltip, an error
  row in the right-click menu, and a desktop notification when a notification
  daemon is available.
- Add a "Config: <path>" info row and a "Change config file..." row (only
  shown if zenity/kdialog is installed) that switches config files live via
  SIGUSR2, no restart needed.
- Fix Quit not working: the dbusmenu click handler required a prior
  AboutToShow that some hosts don't reliably send before the click event.
- Drop the redundant "Reload config" menu entry (the file watcher already
  reloads automatically); SIGUSR1 still works for scripted/external reloads.
- Standardize internal logging through the existing `logging` module instead
  of a mix of ad-hoc prints, so output is consistent and D-Bus chatter is
  DEBUG-level instead of always-on.

Decorator API:
- Unify on_create/on_manage/on_destroy/on_property_change/on_focus/on_unfocus
  onto the same (window, event) callback signature already used by
  on_key/on_button/on_map/on_unmap/on_configure/on_client_message.
  on_create's event is Optional[XCreateWindowEvent] (None for windows that
  already existed before orcsome3 started); on_manage guarantees a real one.
  wm.event_window still works as a fallback.

Docs:
- Update the README to match: tray menu, non-fatal config errors, the new
  decorator signatures, static linking (no runtime deps besides X11), and
  ImageMagick-backed icon format support for set_icon.
- Move the "hide title bar when maximized" example to the front as the most
  common use case.

Also carries existing WIP tooling changes on this branch: a PR-validation
GitHub Actions workflow (format/lint/stub/test gates, callable from the
publish workflow too), CI make targets (format-check, stubs-check), and a
named-args checker exception for Mapping key arguments.
@github-actions

github-actions Bot commented Aug 31, 2026 •

Copy link
Copy Markdown

PR Validation Results

✅ All checks passed!

Check Status
Code formatting (ruff format) ✅
Lint (ruff, mypy, basedpyright, named-args, explicit-types) ✅
Backend stub (orcsome3_backend.pyi) ✅
Tests (unittest) ✅

…le and pyproject.toml, ensuring consistent environment setup across local and CI workflows.
…ow to ensure linting runs after installing dependencies.
… modify CI workflow to streamline linting process after dependency installation.
…ing shutil.rmtree. Update build command in libraries.py to specify the "lib" target for make. Add CMAKE_INSTALL_LIBDIR flag in recipes.py for libjpeg to ensure compatibility with various distributions.
… for LibRaw compilation, ensuring proper handling of C++ sources.
This update ensures that when the --force-rebuild option is used, the persistent cache located at ~/.cache/orcsome3/libs is also bypassed, allowing for a complete rebuild of libraries rather than restoring from cache.
@ahsand97
ahsand97 merged commit a6ced6a into master Aug 31, 2026
1 check passed
@ahsand97
ahsand97 deleted the wip-refactor-whole-codebase branch August 31, 2026 05:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant