Skip to content

Add instant background image cycling via GPU-preloaded textures#9836

Closed
OmarMusayev wants to merge 2 commits intokovidgoyal:masterfrom
OmarMusayev:feature/preloaded-background-cycling
Closed

Add instant background image cycling via GPU-preloaded textures#9836
OmarMusayev wants to merge 2 commits intokovidgoyal:masterfrom
OmarMusayev:feature/preloaded-background-cycling

Conversation

@OmarMusayev
Copy link
Copy Markdown

@OmarMusayev OmarMusayev commented Apr 8, 2026

Summary

Reworked based on maintainer feedback. background_image now accepts glob patterns that resolve to a sorted list of image paths, with a new change_background_image action and lazy loading.

Changes

  • background_image accepts globs: e.g. background_image ~/backgrounds/*.png resolves to a sorted list of matching image files. Non-image files and directories are ignored. First match is used by default (backwards compatible with single paths).
  • change_background_image action: Optional argument acts as index into the image list. +N/-N = delta with wraparound. Plain number = absolute index, clamped to list size. No argument = +1.
  • Lazy loading: Each image is loaded into GPU memory on first access, then cached. Failed loads are silently removed from the list and the next image is tried.
  • kitten @ set-background-image extended: If the argument is a number or +N/-N, it acts like change_background_image on all matching OS windows.
  • Per-window index: Each OS window tracks its own position in the shared image list.

Usage

background_image ~/backgrounds/*.png
map cmd+b change_background_image
map cmd+shift+b change_background_image -1
kitten @ set-background-image +1      # cycle forward on all windows
kitten @ set-background-image 3       # jump to index 3
kitten @ set-background-image ~/bg.png # set specific image (existing behavior)

Files changed (11)

File Change
kitty/graphics.h BackgroundImageList struct
kitty/state.h New fields on GlobalState + OSWindow
kitty/state.c Core C functions: set paths, lazy load, cycle, cleanup
kitty/options/definition.py Updated background_image description
kitty/options/parse.py Glob resolution in config parser
kitty/options/types.py background_image_paths field
kitty/options/utils.py change_background_image action parser
kitty/boss.py Action method + startup hook
kitty/rc/set_background_image.py Number argument support
tools/cmd/at/set_window_logo.go Index arg detection in Go
kitty/fast_data_types.pyi Type stubs

Test plan

  • Builds on macOS (Apple Silicon)
  • Single path backwards compatibility
  • Glob resolves and sorts lexically
  • change_background_image cycles with +/-/absolute
  • Lazy loading on first access, cached after
  • Failed image removal
  • kitten @ set-background-image +1 remote control
  • Linux build

@OmarMusayev OmarMusayev force-pushed the feature/preloaded-background-cycling branch from a26dc2e to 24ad3f3 Compare April 9, 2026 00:00
@kovidgoyal
Copy link
Copy Markdown
Owner

Instead background_image should become a glob expression that resolves
to a list of paths. The paths should be sorted lexically so they are
stable. Non image files/directories that match the glob expression
should be ignored.

By default the first matching image should be used.

The change_background_image should take an optional argument that is an
integer which acts as an index into the array of images. The index
should be clamped to the array size.
If the optional argument is preceded by + or minus it should be treated
as a delta changing the index by the specified amount with wrap around.
No optional argument is the same as +1

@ set-background-image should override and set the specified image for
the specified OS windows, when the path to an image is specified. If the
path to the image is a number or + or - followed by a number, it should
act like the change_background_image action, except that it acts on all
matching OS windows rather than just the active one.

The list of background images should be lazy loaded, each item should
only be loaded on demand. If a particular item fails to load it should
be removed from the list and the next item loaded instead.

… loading

Reworked based on maintainer feedback:

- background_image now accepts glob patterns (e.g., ~/backgrounds/*.png)
  that resolve to a sorted list of image paths
- New change_background_image action: no arg = +1, +N/-N = delta with
  wraparound, plain N = absolute index clamped to list size
- Lazy loading: images loaded on first access, cached in VRAM after
- Failed image loads are silently removed from the list
- kitten @ set-background-image extended: number or +/-N arguments cycle
  through the background image list for all matching OS windows
- Per-window index tracking into shared image path list
@OmarMusayev OmarMusayev force-pushed the feature/preloaded-background-cycling branch from 24ad3f3 to 99cc202 Compare April 9, 2026 15:31
@OmarMusayev
Copy link
Copy Markdown
Author

Thanks for the feedback! I've reworked the implementation to match your suggestions:

  • background_image is now a glob expression that resolves to a sorted list of paths. Non-image files/directories are filtered out. Single paths still work (backwards compatible).
  • change_background_image action with optional argument: no arg = +1, +N/-N for delta with wraparound, plain number for absolute index clamped to list size.
  • kitten @ set-background-image extended to accept number/+N/-N arguments, acting like change_background_image on all matching OS windows.
  • Lazy loading — each image is only loaded on first access, cached in VRAM after. Failed loads are removed from the list and the next image is tried.

Let me know if there's anything else to adjust.

@kovidgoyal
Copy link
Copy Markdown
Owner

  1. Generated files *_generated.go should not be added to source control.

  2. free_bg_image_list() needs to verify that list->paths and
    list->images are not NULL before dereferencing them.

  3. Do some manual testing of kitten @ set-background-image to verify
    that it still works in both setting by path and changing from list.
    Verify with and without a socket for remote control.
    Specify the semantics of what happens when a background image is set by
    path in terms of the list. Does the current list index reset to zero? is
    it unaffected. Document that in the docs of the

  4. Improve docs of the new action change_bacground_image specifying the
    syntax for index vs increment.

  5. bg_image_idx should be unsigned unless you are actually using
    negative values

  6. You have made changes to read_window_logo() to support index
    arguments. This will break kitten @ set-window-logo. Instead use a
    separate parser function. Refactor the parsing into a common
    function that both the remote control commands use, in one case allowing
    index arguments and in the other not.

  7. set_bg_image_paths() should take a tuple not a list

  8. Dont use getattr() for background_image_paths as that breaks type
    checking. Indeed I dont really see why we need a separate
    background_image_paths at all. Instead background_image should simply be
    a tuple of paths.

  9. The whole approach to option processing is broken.
    options/parse.py is a generated file. Dont edit it directly. Instead
    change option_type for background_image to a dedicated function named
    background_image that returns a tuple of paths. Implement it in
    options/utils.py The run gen/config.py to update the generated files.

@OmarMusayev
Copy link
Copy Markdown
Author

Thanks for the detailed feedback! I've addressed all 9 items:

  1. Generated Go files — Removed cmd_set_background_image_generated.go from source control. Already covered by *_generated.go in .gitignore.

  2. NULL checks in free_bg_image_list() — Added guards on list->paths and list->images before dereferencing.

  3. Manual testing — Tested all scenarios on macOS:

    • kitten @ set-background-image /path/to/image.png ✓ (with socket)
    • kitten @ set-background-image +1 ✓ (with socket)
    • kitten @ set-background-image -- -1 ✓ (with socket, needs -- separator since -1 is parsed as a flag by the Go arg parser)
    • kitten @ set-background-image none
    • kitten @ set-window-logo ✓ (still works, not broken)
    • change_background_image keybind cycling ✓
    • Semantics when setting by path: the current list index is unaffected (documented in the set-background-image help text).
  4. Improved docschange_background_image action now documents syntax: +N/-N for delta (wraps around), plain N for absolute index (clamped). Also updated set-background-image remote control description.

  5. bg_image_idx unsigned — Changed to unsigned int in OSWindow struct. Updated all arithmetic in pychange_bg_image accordingly.

  6. Separate Go parser — Reverted read_window_logo() to upstream (path-only). Extracted read_image_file() as shared helper. Created read_background_image() that handles both index args and image paths. Updated special_parse in set_background_image.py to reference read_background_image.

  7. Tuple not listset_bg_image_paths() now takes a PyTuple via PyArg_ParseTuple with O! + &PyTuple_Type. Updated .pyi stub.

  8. Removed background_image_paths — Eliminated the separate field entirely. opts.background_image is now used directly (no more getattr()).

  9. Fixed option processing pipeline — Added dedicated background_image() function in options/utils.py returning tuple[str, ...]. Changed option_type in definition.py to 'background_image'. The generated parse.py change is now just one line (ans['background_image'] = background_image(val)). Updated to-c.h to extract the first path from the tuple for the C Options.background_image field.

Note on -1 argument: When passing negative deltas to set-background-image, the user needs -- separator (e.g., kitten @ set-background-image -- -1) because Go's CLI parser treats -1 as a flag. This is standard CLI behavior and +1 works without any separator.

- Remove *_generated.go from source control (covered by .gitignore)
- Add NULL checks in free_bg_image_list() before dereferencing
- Improve docs for change_background_image action (syntax for +N/-N/N)
- Document set-background-image remote control semantics (index, delta, path)
- Make bg_image_idx unsigned in OSWindow struct
- Refactor Go parser: extract read_image_file() shared helper,
  read_window_logo() (path only), read_background_image() (path + index)
- set_bg_image_paths() now takes a tuple instead of a list
- Remove background_image_paths field; background_image is now tuple[str, ...]
- Fix option processing: dedicated background_image() function in utils.py,
  option_type changed in definition.py, parse.py updated via codegen
- Update to-c.h to handle tuple from Python for C Options struct
@OmarMusayev OmarMusayev force-pushed the feature/preloaded-background-cycling branch from ed24546 to 7eb9785 Compare April 11, 2026 18:24
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.

2 participants