Merge pull request #8 from fbl100/mac-mirror-verified

Mac → Frame mirror: verified, with fixes for scaling, login and the cursor
This commit is contained in:
Alex Southwell authored and GitHub committed 2026-09-28 17:20:22 +10:00
commit ff2c4ebfe0
5 files changed
+94 -20

No files matched your search

+15 -13
View File
@@ -33,14 +33,19 @@ build 20260922.6101926, kernel 6.18, aarch64):
- **10.** `install-apps.sh remmina --vnc-host <mac>.local` installed Remmina as
a `--user` Flatpak over SSH and wrote the profile. The desktop's
`XDG_DATA_DIRS` includes the user Flatpak exports, so it shows up in the menu.
The Frame can reach the Mac's Screen Sharing port (5900). The Remmina
connection itself hasn't been tried in the headset yet (part of 11).
The Frame can reach the Mac's Screen Sharing port (5900).
- **11.** Answered 2026-09-27 (BUILD_ID 20260925.6191901, macOS 27.0): the
pre-seeded profile connects and shows the Mac in its own panel. It asks for
the Mac account login rather than the VNC password, needs scale-to-fit at
Retina resolutions, and doesn't show the Mac cursor without
`scripts/mac-cursor-ring.lua`. It's usable but noticeably laggy. See
[streaming.md](streaming.md).
- **Panels.** An X11 window on gamescope's `:0` with its own `STEAM_GAME` id
gets its own SteamVR overlay (`valve.steam.desktopgame.<id>`). Three were
created side by side with `panel-on-frame.sh`. See [panels.md](panels.md).
Still open: 4, 6, 7, 11 (in-headset connect), 12–15, 16 (off-LAN and after a reboot), 17–21.
Still open: 4, 6, 7, 12–15, 16 (off-LAN and after a reboot), 17–21.
## Check on the headset (in order)
@@ -70,12 +75,10 @@ Still open: 4, 6, 7, 11 (in-headset connect), 12–15, 16 (off-LAN and after a r
`ssh frame 'command -v wl-copy xclip rsync flatpak'`.
10. **Can Flatpaks be installed `--user` over SSH, and do they appear in the
headset's desktop?** Test with `./scripts/install-apps.sh remmina`.
11. **Remmina → macOS Screen Sharing:** does it connect, and is it usable at
Retina resolutions? Is the pre-seeded profile path
(`~/.var/app/org.remmina.Remmina/data/remmina/`) the one Remmina
actually reads?
12. **Moonlight Flatpak (aarch64) + Sunshine on macOS:** worth trying only if
VNC is too slow.
11. ~~**Remmina → macOS Screen Sharing**~~: answered 2026-09-27; see above
and [streaming.md](streaming.md).
12. **Moonlight Flatpak (aarch64) + Sunshine on macOS:** VNC works but is
noticeably laggy, so this is worth trying.
13. **KDE Connect**: is it preinstalled or installable on the Frame, and does
it pair with KDE Connect for macOS?
14. **Bluetooth keyboard pairing** on the Frame, for the rare times you do need
@@ -86,8 +89,9 @@ Still open: 4, 6, 7, 11 (in-headset connect), 12–15, 16 (off-LAN and after a r
runs as a lingering user service with no sudo; see [tailscale.md](tailscale.md).
Still open: reaching the Frame from outside the home network, and the service
starting after a reboot.
17. **Floating panels in the headset** (see [panels.md](panels.md)): do the
panels from `panel-on-frame.sh` show up, take input, and offer **Float in
17. **Floating panels in the headset** (see [panels.md](panels.md)): panels
from `panel-on-frame.sh` show up and take controller input (verified
2026-09-27 with `mac-screen`). Still open: do they offer **Float in
World** / **Move** / **Size**? Do floating positions survive closing and
reopening the app, or a reboot?
18. **`LEPTON_NO_CLEANUP=1 %command%`** as Lepton Development's launch
@@ -129,8 +133,6 @@ Still open: 4, 6, 7, 11 (in-headset connect), 12–15, 16 (off-LAN and after a r
- `/home` and `/etc` persist across Frame OS updates. This is inferred from
Steam Deck behaviour.
- The whole Mac → Frame desktop path (VNC → Remmina). Each part is documented
separately, but the combination is untested.
- Steam Remote Play with a Mac as host is broken. That's based on community
reports, not tested with the Frame.
- `connect.sh --harden`, `serve-bootstrap.sh` and
+2 -1
View File
@@ -93,7 +93,8 @@ controls to place each panel. See [docs/panels.md](panels.md).
| `scripts/install-apps.sh` | Mac → Frame | Install Flatpaks (Remmina, Moonlight, …) on the Frame over SSH as `--user` (**verified** with Remmina) |
| `scripts/paste-to-frame.sh` | Mac → Frame | Send the Mac clipboard (or stdin) to the Frame clipboard (**verified**) |
| `scripts/install-apk.sh` | Mac → Frame | Install APKs, each as its own persistent Lepton instance with a Steam library shortcut (`--dev`: old ADB path into Lepton Development) (**verified**; see [docs/apks.md](apks.md)) |
| `scripts/panel-on-frame.sh` | Mac → Frame | Start an app as its own floating VR panel, outside the desktop (**verified**: overlays created; in-headset placement not yet checked) |
| `scripts/panel-on-frame.sh` | Mac → Frame | Start an app as its own floating VR panel, outside the desktop (**verified**, including `mac-screen` in the headset) |
| `scripts/mac-cursor-ring.lua` | Mac | Hammerspoon script: a ring around the Mac pointer so it shows in the VNC mirror (**verified**) |
| `scripts/run-on-frame.sh` | Mac → Frame | Start an app on the headset desktop, e.g. `mac-screen` opens Remmina straight into the Mac (**verified**) |
| `scripts/frame-ui.sh` | Mac | Start the Frame Control web UI (`ui/server.py`) and open it (**verified**) |
| `scripts/apk-catalog.sh` | Mac | Refresh the rated F-Droid catalogue that Frame Control's Android section shows (**verified**) |
+33 -6
View File
@@ -34,7 +34,7 @@ flat 2D desktop streaming into a window on the Frame's Linux desktop.
| Option | Setup | Confidence | Verdict |
|---|---|---|---|
| **macOS Screen Sharing (VNC) → Remmina on the Frame** | **Mac:** System Settings → General → Sharing → Screen Sharing on → (i) → enable "VNC viewers may control screen with password". **Frame:** `./scripts/install-apps.sh remmina` from the Mac, then open Remmina in the headset and connect to `vnc://<mac>.local` | **Inferred.** Remmina is on Flathub for **aarch64** with VNC and RDP ([Flathub](https://flathub.org/apps/org.remmina.Remmina)). The Frame desktop runs Flatpaks ([UploadVR](https://www.uploadvr.com/flatpaks-open-source-steam-frame/)). macOS VNC is built in. | **Recommended.** Nothing to install on the Mac, and it's easy to set up. Latency is fine for productivity but not for games. You'll type the Mac's hostname once in Remmina on the headset, then save the profile. To avoid even that, the script can pre-seed a Remmina profile over SSH (see below). |
| **macOS Screen Sharing (VNC) → Remmina on the Frame** | **Mac:** System Settings → General → Sharing → Screen Sharing on → (i) → enable "VNC viewers may control screen with password". **Frame:** `./scripts/install-apps.sh remmina` from the Mac, then open Remmina in the headset and connect to `vnc://<mac>.local` | **Verified 2026-09-27** (Frame BUILD_ID 20260925.6191901, macOS 27.0), in its own panel via `panel-on-frame.sh mac-screen`. Remmina is on Flathub for **aarch64** with VNC and RDP ([Flathub](https://flathub.org/apps/org.remmina.Remmina)). The Frame desktop runs Flatpaks ([UploadVR](https://www.uploadvr.com/flatpaks-open-source-steam-frame/)). macOS VNC is built in. | **Recommended.** Nothing to install on the Mac, and it's easy to set up. Noticeable lag, even at lower Remmina quality settings on a good 5 GHz link, where neither Wi-Fi nor the Frame's CPU was the bottleneck. Usable for reading and coding, but not for games. You'll type the Mac's hostname once in Remmina on the headset, then save the profile. To avoid even that, the script can pre-seed a Remmina profile over SSH (see below). |
| Sunshine (Mac) → Moonlight (Frame Flatpak) | `brew install` Sunshine on the Mac, then `./scripts/install-apps.sh moonlight` | Moonlight Flatpak supports **aarch64** ([Flathub](https://flathub.org/apps/com.moonlight_stream.Moonlight)). **Sunshine on macOS is poorly supported**: install problems on Apple Silicon/Sequoia, and no virtual gamepads ([LizardByte discussion #777](https://github.com/orgs/LizardByte/discussions/777)). | Try it if VNC is too laggy. Expect some friction. |
| Steam Remote Play with the Mac as host | Steam on the Mac, Steam Link/Remote Play on the Frame | macOS-hosted Remote Play is reported broken or flaky in 2024–2026 ([Steam discussion](https://steamcommunity.com/groups/homestream/discussions/1/574921459914429988/)) | Not recommended. It's only for games, if it works at all. |
| Immersed / Virtual Desktop | Vendor apps | Immersed has a Mac agent but no known Frame client. Virtual Desktop's developer said he'd "try" to port it ([NewsBreak](https://www.newsbreak.com/news/4892834783961-virtual-desktop-dev-says-he-ll-try-to-bring-the-app-to-steam-frame)). | Not available as of 2026-09-25. Check again later. |
@@ -47,11 +47,38 @@ them on the Frame in DeoVR instead: see [vr-video.md](vr-video.md).
`scripts/install-apps.sh remmina --vnc-host <your-mac>.local` writes
`~/.var/app/org.remmina.Remmina/data/remmina/mac-screen-sharing.remmina` on the Frame over
SSH. The profile then appears in Remmina's list, and you just click it. You'll
still be asked for the VNC password in the headset the first time, unless you
choose to save it. Remmina stores passwords encrypted with a per-install key,
so the script doesn't try to write the password. (The Remmina file format is
standard; the Flatpak data path is inferred.)
SSH. The profile then appears in Remmina's list, and you just click it. It
scales the Mac's desktop to fit the window (`scale=1`, `viewmode=1`). Without
that, Remmina shows a Retina Mac's native pixels 1:1, so you see a zoomed-in
corner. (Verified 2026-09-27.)
**Expect a Mac login prompt, not the VNC password.** macOS offers Apple's own
authentication (RFB security type 30) ahead of plain VNC auth (type 2), and
Remmina picks it. So Remmina asks for your **Mac account name and login
password**; the "VNC viewers may control screen" password isn't used. To store
the password without typing it in the headset, run on the Frame:
```sh
printf '%s' "$PASSWORD" | flatpak run org.remmina.Remmina \
--update-profile ~/.var/app/org.remmina.Remmina/data/remmina/mac-screen-sharing.remmina \
--set-option password
```
Remmina encrypts it into the profile with its own key, because there's no
secret service in the SSH session. (Verified 2026-09-27.)
### The Mac's cursor
The mirror doesn't show the Mac's pointer, with either `showcursor` value.
macOS keeps the pointer out of the picture it sends, and Remmina's cursor mode
draws the cursor shape only at the Frame's own pointer, which doesn't follow
the Mac trackpad. `scripts/mac-cursor-ring.lua` works around this: a
[Hammerspoon](https://www.hammerspoon.org/) script that draws a ring around the
Mac pointer as a real window, so it's part of the mirrored picture. Setup is in
its header. (Verified 2026-09-27.)
Going the other way, pointing a controller at the panel moves the Mac's mouse,
because Remmina forwards input (`viewonly=0`).
## C. Show the iPhone's screen inside the Frame
+4
View File
@@ -59,9 +59,13 @@ colordepth=32
quality=9
viewonly=0
showcursor=1
scale=1
viewmode=1
window_maximize=1
EOF
echo \"wrote \$d/mac-screen-sharing.remmina\"
"
print "On the Mac: System Settings > General > Sharing > Screen Sharing (i) >"
print " enable 'VNC viewers may control screen with password' and set one."
print "Remmina may ask for your Mac account name + login password instead (Apple auth)."
fi
+40
View File
@@ -0,0 +1,40 @@
-- Hammerspoon: draw a ring around the Mac pointer so it shows in the VNC
-- mirror on the Frame. macOS Screen Sharing leaves the pointer out of the
-- framebuffer; a real on-screen window is captured like anything else.
--
-- Install: brew install --cask hammerspoon, then in ~/.hammerspoon/init.lua:
-- dofile("/path/to/frame-control/scripts/mac-cursor-ring.lua")
-- Toggle: ctrl+alt+cmd+M. Polls the pointer position, so no Accessibility
-- permission is needed.
local SIZE, WIDTH = 34, 3
local COLOR = { red = 1, green = 0.2, blue = 0.2, alpha = 0.9 }
local ring = hs.canvas.new({ x = 0, y = 0, w = SIZE, h = SIZE })
ring:appendElements({
type = "circle", action = "stroke",
strokeColor = COLOR, strokeWidth = WIDTH,
radius = (SIZE - WIDTH) / 2,
})
ring:level(hs.canvas.windowLevels.cursor)
ring:behavior({ "canJoinAllSpaces", "stationary", "ignoresCycle" })
local last = {}
local function follow()
local p = hs.mouse.absolutePosition()
if p.x ~= last.x or p.y ~= last.y then
ring:topLeft({ x = p.x - SIZE / 2, y = p.y - SIZE / 2 })
last = p
end
end
frameCursorRing = { canvas = ring, timer = hs.timer.new(1 / 60, follow) }
local function show() follow(); ring:show(); frameCursorRing.timer:start() end
local function hide() frameCursorRing.timer:stop(); ring:hide() end
hs.hotkey.bind({ "ctrl", "alt", "cmd" }, "M", function()
if ring:isShowing() then hide() else show() end
end)
show()