Skip to content

Implement the screen saver - #2

Merged
winebarrel merged 1 commit into
mainfrom
implement-screen-saver
Aug 10, 2026
Merged

winebarrel merged 1 commit into
mainfrom
implement-screen-saver

Conversation

@winebarrel

Copy link
Copy Markdown
Owner

Ports Neco's cat to a .saver bundle, following the layout of
winebarrel/Macstify: a Sources/
target that builds NecoSaver.saver and a Preview/ host app for development,
plus the Makefile and CI that go with them.

What is carried over from Neco

Sprites.swift, the sprite expansion, the cat's brain and the litter marks come
from Neco — copied, not shared, since the
sprite data never changes and Neko.swift is settled. Two changes make them work
under ScreenSaver.framework:

  • Neko reads an injected field rect instead of NSScreen. A saver gets one
    view per display and may only draw inside its own bounds.
  • There is no cursor to chase behind the shield window, so the cat always runs
    Neco's wander mode. Cursor tracking is removed rather than made switchable.

The two overlay NSPanels collapse into one ScreenSaverView that paints the
background, the mess and the cats in that order, and the 60fps Timer becomes
animateOneFrame(). Settings live in ScreenSaverDefaults, not
UserDefaults.standard, which would otherwise resolve to a different domain
depending on which host loaded the bundle.

SpriteCache now vends CGImage instead of NSImage, so the engine draws into
the CGContext it is handed with no dependency on the current
NSGraphicsContext — which is what lets the preview app render offscreen.

Options

Edited through a configure sheet built in code (no nib to locate at runtime):

Option Default Range
Cats 1 1–8 Each one wanders on its own.
Size 64 pt 32–192 pt Walking speed and the size of the mess scale with it.
Speed 1.00× 0.2–3.0
Background 0% 0–100% Grey level.
Paw prints on
Scratch marks on

The litter ink takes the opposite end of the greyscale from the background: the
marks are a fixed dark colour in Neco, which would leave them invisible on the
black default here.

Not done

"Show the real desktop behind the cat" is not possible in a .saver — the saver
is a CGShieldingWindowLevel() window covering the screen, and a snapshot
approach would need screen-recording TCC granted to legacyScreenSaver rather
than to us. The background is a plain grey instead.

Verified

  • xcodebuild build analyze passes for both schemes
  • swiftlint --strict, swiftlint analyze --strict and swiftformat --lint are clean
  • The built bundle loads and its principalClass resolves, i.e. @objc(NecoSaverView)
    matches NSPrincipalClass
  • Offscreen renders check the black and white backgrounds, several cats at once,
    and the Options sheet layout
  • The .saver contains only Info.plist and the executable — no entitlements
    copied over from the app, no resources

Not verified: behaviour in System Settings on a real machine (make install,
then killall legacyScreenSaver), multi-display, and the live thumbnail preview.

Ports Neco's cat to a .saver bundle, following the layout of winebarrel/Macstify:
a `Sources/` target that builds NecoSaver.saver and a `Preview/` host app for
development, plus the Makefile and CI that go with them.

Sprites.swift, the sprite expansion, the cat's brain and the litter marks are
carried over from Neco. Two changes make them work under ScreenSaver.framework:

- Neko reads an injected `field` rect instead of NSScreen. A saver gets one view
  per display and may only draw inside its own bounds.
- There is no cursor to chase behind the shield window, so the cat always runs
  Neco's wander mode. Cursor tracking is gone rather than switchable.

The two overlay NSPanels collapse into one ScreenSaverView that paints the
background, the mess and the cats in order, and the 60fps Timer becomes
animateOneFrame(). Settings live in ScreenSaverDefaults, not UserDefaults.standard,
and are edited through a configure sheet built in code: cats, size, speed,
background, paw prints, scratch marks.

Marks and cat sprites scale together, and the litter ink flips to the opposite end
of the greyscale from the background so it stays visible either way.
@winebarrel
winebarrel merged commit cd82d91 into main Aug 10, 2026
3 checks passed
@winebarrel
winebarrel deleted the implement-screen-saver branch August 10, 2026 01:42
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