# EventSaver

A rotating-image screensaver for shop-floor PCs. Idle bays show the slides
managed in ShopDB, so a safety notice or a shift message reaches the floor
without anyone walking to each machine.

It is a small C# WinForms screensaver (`.scr`), not part of the ShopDB server.
It is documented here because ShopDB serves its content and because a site
adopting the fleet tools needs to know how it is configured.

For how to deploy it at a new site, see
[ADOPTING-AT-ANOTHER-SITE.md](ADOPTING-AT-ANOTHER-SITE.md). This page is what it
is, how it decides what to show, and how to diagnose it.

## Two sources, one of them preferred

EventSaver reads `EventSaver.ini` from the directory it lives in, **on every
launch**. Retargeting it never needs a recompile.

```ini
# HTTP mode: pull the playlist from ShopDB and cache it locally.
url=https://shopdb.example.net/api/slides/feed?surface=shopfloor

# Folder mode: used only when url is blank. SMB or local path.
# folder=\\fileserver\shopfloor\tv

interval=10     # seconds per image, unless a slide carries its own time
shuffle=0       # 1 = random order, 0 = ordered
fadems=600      # crossfade length in ms, reserved (v1 hard-cuts)
```

**HTTP mode is the one to use.** Slides are managed in ShopDB (Slides), the
feed is public so the screensaver needs no credentials, and each PC caches what
it fetched under its own `LocalApplicationData`. A bay that cannot reach the
server keeps showing the last set it saw instead of going black - which matters,
because the failure would otherwise be visible to the whole floor.

**Folder mode** predates the server and stays as a fallback for a site with no
ShopDB instance yet, or for content nobody wants in the database.

> If `EventSaver.ini` is missing, or both `url` and `folder` are blank, there is
> no source to read and the screensaver shows nothing. That is deliberate: the
> compiled-in fallback used to be the path of the site it was first built for, so
> a missing ini silently pointed a new site at someone else's file server. It is
> now empty, and failing visibly beats displaying the wrong site's slides. Ship
> the ini.

## What decides the running order

| Situation | Behaviour |
|---|---|
| No `order.txt` | Every image shows, sorted by filename. Number them `001_`, `002_` to sequence. |
| `shuffle=1` | Random order, ignored when a playlist is present. |
| `order.txt` present | ONLY the listed files show, in the listed order, looped. |

`order.txt` is the strict-sequence option and is the whole playlist - a file not
listed does not appear. Per-slide duration is appended with a pipe:

```
# Lines starting with # or ; are ignored.
001_welcome.jpg
002_safety_week.jpg|15
003_quality_board.jpg
```

In HTTP mode you do not write `order.txt` by hand: ShopDB's feed carries the
order and each slide's own seconds, and EventSaver writes the equivalent
`order.txt` into its cache so the same playlist logic runs either way.

A running screensaver rescans about every 30 seconds, so a slide change reaches
the floor without touching any PC.

## Command-line switches

Windows calls a screensaver with these, and EventSaver answers all three:

| Switch | Meaning |
|---|---|
| `/s` | Show fullscreen. What Windows uses on idle. |
| `/c` | Configuration. Reports the resolved source and cache directory, and points at the ini. |
| `/p <hwnd>` | Preview pane. Deliberately a no-op - it keeps the Settings dialog happy without drawing a thumbnail nobody looks at. |

`EventSaver.scr /c` is the first diagnostic: it says which source it resolved
and where it is caching, which answers most "why is it showing the wrong thing"
questions immediately.

## Where it should and should not run

A screensaver over a live display is a defect, not a feature. The reference
fleet excludes:

- **Kiosks and lobby displays**, which show a live dashboard.
- **Metrology bays** - CMM, Genspect, Keyence, wax-trace - where an operator
  watches a measurement run.
- **Bays that sleep**, where the screensaver never wins anyway.

Targeting is per PC type in the GE-Enforce manifest, or by group assignment in
Intune. Decide it deliberately: the cost of getting it wrong is a screensaver
covering something someone needed to see.

## Building it

The source is `plugins/slides/client/EventSaver.cs`. No SDK required - it
compiles with the in-box .NET Framework compiler on any Windows 10 or 11
machine:

```
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe ^
  /target:winexe /out:EventSaver.scr ^
  /reference:System.dll,System.Drawing.dll,System.Windows.Forms.dll ^
  EventSaver.cs
```

Ship `EventSaver.scr` and `EventSaver.ini` together. A `.scr` is an executable
with a different extension, so it goes to `C:\Windows\System32\`.

## Diagnosing it

| Symptom | Cause to check first |
|---|---|
| Blank screen, no images | Source unreachable and cache empty. Run `/c` and open the `url` in a browser on that PC. |
| Old slides only | Feed reachable at first run, not since. The cache is doing its job; fix the network path. |
| Wrong content | Pointing at another surface, or another site's server. `/c` reports which. |
| Local ini edits revert | Correct behaviour if the config is enforced by hash. Edit the copy on the share and update its `DetectionValue`. |
| Screensaver never starts | Per-user setting missing. Check `HKCU:\Control Panel\Desktop` for `SCRNSAVE.EXE`, `ScreenSaveActive` and `ScreenSaveTimeOut`. |

The feed is a plain public endpoint, so it can always be checked from the PC
itself:

```
https://shopdb.example.net/api/slides/feed?surface=shopfloor
```

An empty `slides` array means no slides have been uploaded for that surface -
the PC is configured correctly and there is nothing to show.
