Skip to content

(3/5) The Linux host, and the devices calling what they connect to a host - #169

Open
ErickXavier wants to merge 7 commits into
Saffsanity:mainfrom
ErickXavier:feat/linux-host
Open

ErickXavier wants to merge 7 commits into
Saffsanity:mainfrom
ErickXavier:feat/linux-host

Conversation

@ErickXavier

@ErickXavier ErickXavier commented Oct 19, 2026 •

Copy link
Copy Markdown

The third of four pull requests that bring Sill to more systems, each on the one before; each builds and tests on its own. It needs the previous one merged first: until then, its diff also shows the earlier branches' commits, and only its last commits are its own.

  1. feat/android-client: the Android client, Keep Screen On, and Sill.app's device words
  2. feat/windows-client: the Windows client
  3. feat/linux-host: the Linux host, and the devices calling what they connect to a host ← this one
  4. feat/windows-host: the Windows host

The site has its own pull request (docs/site-other-platforms), independent of these four.

A Linux PC on X11 streams its own windows to the same iPhone, iPad, Android and Windows apps, over Sill for Mac's wire, TLS door and pairing. No client changes for it.

It has run only against the Kotlin client, never with an iPhone, iPad or Android app. docs/clients-plan.md lists L1–L3, and docs/ui-parity.md lists H8–H10.

What it adds

  • clients/host-core (JDK 17): the door, pairing and session of any host that isn't the Mac, over a HostPlatform.
    • The door: TLS 1.3, both ALPNs, a TLS context per connection, and the Mac's pending caps and refusal backoff.
    • The Mac's pairing window and ask rules (kind 21 included), the trust list, and an mDNS responder.
    • Sending to each device with the Mac's in-flight, keyframe and eviction rules.
    • The coordinator: catalog order, select, viewport and Aa, kinds 16/17/18, clipboard, gestures, the PC's pointer as kind 26, goodbyes.
    • The Mac's input made the PC's. A key's or button's up with nothing streaming is still delivered.
    • Sill.app's StatusText and MenuBuilder rules, and a QR encoder identical to python-qrcode's.
  • clients/host (JDK 25, fetched by Gradle):
    • LinuxPlatform: Xlib and XTest through the JDK's foreign function API.
    • FfmpegVideo: x11grab, then NVENC, Quick Sync or VAAPI, else x265.
    • Sill.app's status card and menu as a tray popup, plus Settings, the pairing window with its QR code, and the log.
    • The clipboard never sends a copy a password manager marks as concealed.
    • Flags: --headless, --pair, --self-test, --input-probe.
  • Words: the host's say "device" and "this PC", never only iPhone and iPad.
  • The devices call what they connect to a host, not a Mac (its own commit): with a PC as a host, "Connect to a Mac" and "Add a Mac…" are wrong.
    • The iOS app, the core's words and the Android and Windows clients say "Connect to a Host", "Add a Host…", "Looking for hosts…", "your host" and "the host". A host that gives no name is "Host", and a host's own name shows wherever it's known, as before. Comments and console lines keep "Mac".
    • The checks that hold these words (home-device, policy, goodbye, home-model, remote-rules, menu-state, tour) and clients/design's anchors follow, and check_copy.py still finds the core's words in the iOS app.
    • Sill.app's and the PC host's pairing windows say "Add a Host… (Add a Mac… in older versions)", for devices still on 0.5.1.
    • The App Store's screenshots still show "Connect to a Mac" until new ones are taken.
  • FFmpeg's GPL build is the only one with a software HEVC encoder, so a packaged host is distributed under GPLv3 while the source stays Apache-2.0. docs/BRIEF.md records the decision.
  • The code review's host fixes, each with a test that fails without it:
    • a TLS ticket from a pairing connection no longer resumes as a full session (HostDoorTest: 3 of 3 let in before);
    • LaunchApp only opens a listed app;
    • kind 21 goes through AskLimits;
    • a trusted key's ask gets a code;
    • a window partly off the screen is clicked against what is captured (CaptureRect).
  • Not built: the Mac's menus, the virtual display, Direct Wireless, Wayland-native windows, multiple monitors.
  • Code left for the next PR: a few Windows branches stay in shared files (HostStore's folder, Theme's dark mode, the encoder list), dormant until the Windows platform arrives in the next pull request.
  • CI: clients.yml's hosts-linux job runs the tests under Xvfb with openbox and xterm, then the packaged host's self-test. It keeps the UI screenshots.

Sill on Android phones, tablets and foldables, as a client of the Mac:
the Mac's windows, live, with touch, keyboard and mouse, over the same
wire, TLS door and pairing as the iPhone and iPad app, drawn to match
that app. Nothing on the Apple side changes; ci.yml ignores clients/.

- clients/core: the shared Kotlin core (standard library only): the
  wire field for field, pairing crypto and the hand-built certificate,
  TLS 1.3 with the pin as the whole of trust, one-shot mDNS, the
  session with the Mac's rules, pairing (ask, code, link, by address),
  saved Macs, the goodbye words and the redial; a model of the Mac's
  door and real HEVC samples as test fixtures.
- clients/core's ui layer (docs/ui-parity.md): the iOS app's design
  read from its own Swift: Tokens.kt generated (palette, metrics,
  sizes, curves), PhonePortraitLayout, HostSettingsLedger and
  DuoPosture ported and held to vectors swiftc computes from the iOS
  sources, the stream screen's layouts and state machine, the
  trackpad, the key row, the words (checked against the iOS sources).
  A settings change's token comes from the session alone. The Mac's
  menus: MacMenuState, the iOS app's rules for kinds 24, 25 and 27,
  with the subscription at the first window list.
- clients/android: platform APIs only, minSdk 29, MediaCodec, the key
  sealed by the Android Keystore, sill://pair links behind a
  confirmation; every screen drawn from the ui layer, with Android's
  insets, gesture exclusion, 48 dp targets and Back. Foldables: the
  system's window extensions by reflection (no AndroidX), else the
  hinge sensor, into the iOS app's fold rules; tested at the Galaxy Z
  Fold 8's 4:3 and other foldables' sizes in each posture. A Menus
  button with the Mac's menus as Android popups, read when opened.
- clients/wire-vectors and clients/design: what the Mac's and the iOS
  app's own Swift computes, checked in CI.
- clients.yml: the core's tests, the Swift checks, the Android JVM
  tests, lint and the APK.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLHTdv4VtoAFh4pYebBnTE
A switch in the Settings panel's This device group, on by default (Sill.keepScreenOn, never sent to
the Mac): while it is on and the stream screen shows, the screen never dims or locks by itself, so a
stream only watched stays in view. iOS turns off its idle timer (ScreenAwake: a claim per stream
screen, given back as it goes); Android sets FLAG_KEEP_SCREEN_ON on the stream's window. The words
are PanelCopy's and the iOS app's, as check_copy.py requires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLHTdv4VtoAFh4pYebBnTE
With an Android client, an iPhone or iPad is no longer every device that can pair with or stream
from a Mac, so Sill.app's words say "device" where they meant any of them:

- The menu's and Settings › Devices' "Pair iPhone or iPad…" is "Pair a Device…", and the pairing
  window is "Pair a Device", which says to scan the code or type the one below it (a client
  without a camera types it).
- "An iPhone or iPad Needs Sill Updated" is "A Device Needs Sill Updated".
- The network, permissions, Direct Wireless, Remote Access and clipboard footnotes say "your
  devices" or "your device"; the clipboard's footnote is ClipboardPolicy.macFootnote.
- Where only an iPhone or iPad can do it, the words still say so: the USB cable pairs an iPhone or
  iPad by itself (Settings › Devices' footnote, the cable rule and its report).

The iPhone and iPad app, and the Kotlin core, name the menu item in their pairing messages
("On the Mac, choose Pair a Device… in the Sill menu"), so they change with it, and so do the
checks that hold those words (home-device, policy, home-model, clipboard, and the fixtures of
compatibility, goodbye and door-policy) and their mutants. A device on this build facing Sill for
Mac 0.5.1 is told the new name, which that Mac's menu does not have yet; a 0.5.1 device facing
this Sill.app the old one. docs/DEVELOPMENT.md and the App Store review notes follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLHTdv4VtoAFh4pYebBnTE
- README: "Other systems (in development)" with Sill for Android, Keep Screen On in How it works
  (not in a release yet), how to build the Android client, and clients/'s exception to the Swift
  rule in Contributing.
- SECURITY.md: an Android device among those that control a Mac, and clients/ in scope.
- The issue forms ask for the device and its system, an Android one too.
- docs/clients-plan.md's decision 11 and the release checklist: a client's binary is never
  released on a v* tag or as GitHub's latest release, which Sill.app's update check, the site's
  download link and release.yml all read.
- CLAUDE.md: Keep Screen On, Sill.app's device words and the review's fixes in this branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLHTdv4VtoAFh4pYebBnTE
Sill on Windows PCs, as a client of the Mac, on the Android client's
core and drawn to match the iPad app: Swing, HEVC decoded by FFmpeg
7.1.1 through bytedeco (Direct3D 11 when the GPU has it, else the CPU;
the one third-party runtime dependency, Windows only, recorded in
BRIEF), Ctrl as ⌘, the wheel as a scroll gesture, and jpackage (an MSI
with WiX 3; Sill --self-test).

- clients/swing-ui: the iOS app's controls from the core's tokens and
  curves (bar buttons, key caps, the Aa ruler, the window strip and its
  lights, the drawer, the Settings panel's rows, switches, the
  trackpad well), reused by the PC host.
- clients/desktop: every control at StreamLayout's rectangles, opened
  and closed through StreamUi, the Settings panel on the ledger; the
  native frame, dark scrollbars, Esc and Tab, per-monitor scaling; the
  Menus button with the Mac's menus as a cascading menu, each read
  from the Mac when it opens, shortcuts at the right.

clients.yml gains the client's tests, its real windows under Xvfb at
100 % and 200 % (screenshots kept) and a Windows job (the tests and the
packaged runtime's self-test).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLHTdv4VtoAFh4pYebBnTE
A Linux computer on X11 is now a Sill host: the iPhone and iPad app,
the Android app and the Windows client list it beside the Macs, pair
with its code and stream its windows, over the Mac's own wire, TLS
door and pairing, so no client changed.

- clients/host-core: the door (TLS 1.3, both ALPNs, a TLS context per
  connection, the Mac's pending caps and refusal backoff), the pairing
  window and ask rules, the trust list, an mDNS responder, per-device
  sending with the Mac's in-flight, keyframe and eviction rules, and
  the coordinator (catalog order, select, viewport and Aa, kinds
  16/17/18, clipboard, gestures, the PC's pointer, goodbyes), with the
  Mac's input made the PC's. A device launches only an app the host
  listed.
- clients/host: Xlib/XTest through the JDK's foreign function API
  (JDK 25), FFmpeg x11grab and HEVC (NVENC, QSV, VAAPI, else x265 from
  the GPL build), --headless, --self-test, --input-probe, jpackage.
- The UI matches Sill.app (docs/ui-parity.md): its status card and
  menu as a tray popup, Settings, the pairing window with a QR code
  identical to python-qrcode's, and the log, in macOS 26's colours,
  light or dark as the PC is set. Its words say "device" and "this
  PC", not iPhone and iPad.
- The clipboard as Sill.app shares it: Settings' switch, and a copy a
  password manager marks as concealed never read.
- Tests: host-core against the real Kotlin client on a fake platform,
  the review's findings each with a test; the Linux host end to end
  under Xvfb with openbox and xterm; the UI at 1x and 2x. CI's
  hosts-linux job runs them and the packaged host's self-test.

The Windows host follows as a change of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLHTdv4VtoAFh4pYebBnTE
With a Linux PC as a host, "Connect to a Mac" and "Add a Mac…" are
wrong on every device. The iOS app, the core's words and the Android
and Windows clients now say "Connect to a Host", "Add a Host…",
"Looking for hosts…", "your host" and "the host"; a host that gives no
name is "Host"; a host's own name is shown wherever it is known, as
before. Comments and console lines keep "Mac".

- The checks that hold these words (home-device, policy, goodbye,
  home-model, remote-rules, menu-state, tour) and clients/design's
  anchors follow; check_copy.py still finds the core's words in the
  iOS app.
- Sill.app's and the PC host's pairing windows say "Add a Host… (Add
  a Mac… in older versions)", for devices still on 0.5.1.
- The pairing link's default name stays "Mac", as StreamProtocol's.
- Docs: DEVELOPMENT, ui-parity, clients-plan, the App Store notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLHTdv4VtoAFh4pYebBnTE
@ErickXavier ErickXavier changed the title The Linux host, and the devices calling what they connect to a host The Linux host, and the devices calling what they connect to a host (3/4) Oct 19, 2026
@ErickXavier ErickXavier changed the title The Linux host, and the devices calling what they connect to a host (3/4) (3/4) The Linux host, and the devices calling what they connect to a host Oct 19, 2026
@ErickXavier ErickXavier changed the title (3/4) The Linux host, and the devices calling what they connect to a host (3/5) The Linux host, and the devices calling what they connect to a host Oct 19, 2026
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