Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ jobs:
cp docs/MACOS.md dist.noindex/README-MACOS.md
cp LICENSE NOTICE dist.noindex/
cd dist.noindex && tar czf latch-macos-arm64.tar.gz Latch.app README-MACOS.md LICENSE NOTICE
# A 4.0 updater downloads brolink-macos-arm64.tar.gz and installs the
# BroLink.app inside it. The same app under that folder name is what
# lets it update at all; on its first run it moves itself to Latch.app
# A 4.0 updater downloads only its original archive and app names.
# The same app under that folder name lets it update at all; on its
# first run it moves itself to Latch.app
# and takes over the launch agent (crates/host/src/unix_setup.rs,
# adopt_legacy_bundle). Nothing in 4.1 or later reads this asset.
- name: package for 4.0 updaters
Expand Down Expand Up @@ -108,7 +108,7 @@ jobs:
throw "engine archive is not $($env:ENGINE_TAG): expected $($env:ENGINE_SHA256), got $got"
}
Compress-Archive -Path dist\* -DestinationPath latch-windows-x64.zip
# A 4.0 Mac takes brolink-host.exe out of brolink-windows-x64.zip and
# A 4.0 Mac takes the host executable from its original archive and
# sends it to each PC. The new program under the old name is what lets
# it update the PC; nothing in 4.1 or later reads this asset.
- name: package for 4.0 updaters
Expand Down
32 changes: 15 additions & 17 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,36 +159,34 @@ GitHub release notes, so each version gets a heading of the form

## 4.1.1 (2026-10-02)

- Setting up a Windows PC that an earlier version had set up (when the
app was called BroLink) now registers the `LatchStream` service. Setup
- Setting up a Windows PC that a version before 4.1.0 had set up now
registers the `LatchStream` service. Setup
moved the engine's folder and removed the old service, but treated the
running engine as needing nothing more, so the new service was never
registered and the engine ran only once someone had signed in.

## 4.1.0 (2026-10-02)

- **BroLink is now Latch, by Bardbro.** The app, the repository
- **Introducing Latch, by Bardbro.** The app, the repository
(`github.com/MrBeldum/latch`), the release files (`latch-macos-arm64.tar.gz`,
`latch-windows-x64.zip`), the executables (`Latch.app`, `latch-host.exe`),
the services and login items, the environment variables (`LATCH_*`) and
bardbro.com all carry the new name, with a new logo: a square with a
corner cut away, and a blue square in the cut. Bardbro is the
project behind it, for cloud gaming.
- An installed BroLink updates itself to Latch. Settings, the pairing
identity, paired devices and the streaming engine carry over. On a Mac
the app moves from `BroLink.app` to `Latch.app` the first time it runs
and its login item with it; on Linux `latch.service` replaces
`brolink.service`; on Windows the host keeps working in place, and
`install-host.ps1` followed by **Set up as administrator** moves it to
`%LOCALAPPDATA%\Latch`, the `LatchStream` service and the new firewall
rule names. Machines on either name still find each other, and a 4.1 Mac
can still update a 4.0 PC. The release also carries
`brolink-macos-arm64.tar.gz` and `brolink-windows-x64.zip`, which are the
same builds under the old file names, because a 4.0 updater looks for
nothing else. Everything that still says BroLink is in
- Older installs update to Latch. Settings, the pairing identity, paired
devices and the streaming engine carry over. On a Mac the app moves to
`Latch.app` the first time it runs and its login item with it; on Linux
`latch.service` replaces the old service; on Windows the host keeps
working in place, and `install-host.ps1` followed by **Set up as
administrator** moves it to `%LOCALAPPDATA%\Latch`, the `LatchStream`
service and the new firewall rule names. Updated and older machines
still find each other, and a 4.1 Mac can still update a 4.0 PC. Releases
also include compatibility downloads under the filenames required by
4.0 updaters. Runtime compatibility identifiers are documented in
`crates/core/src/legacy.rs` and `crates/host/src/legacy.rs`.
- `LATCH_DATA_DIR` and `LATCH_GITHUB_TOKEN` replace the `BROLINK_` names,
which still work. The Docker node and relay read `LATCH_*`.
- `LATCH_DATA_DIR` and `LATCH_GITHUB_TOKEN` replace the old environment
variable names, which still work. The Docker node and relay read `LATCH_*`.
- The About page names Bardbro and links to bardbro.com. The accent
colour for focus and progress is the logo's blue, where it was violet.
- The window is redesigned around one design system, in the product
Expand Down
29 changes: 14 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,24 +107,23 @@ relay, as this project's own VPS does: [deploy/native/README.md](deploy/native/R
The packet relay (when two NATs cannot punch through) is a separate kit:
[deploy/relay/RELAY.md](deploy/relay/RELAY.md).

## Coming from BroLink
## Upgrading from versions before 4.1.0

Latch was called BroLink until 4.1.0. An installed BroLink updates itself
to Latch and keeps its settings, its pairing and its streaming engine, and
every machine that updates can still talk to one that has not yet.
An older install updates to Latch and keeps its settings, pairing and
streaming engine. Updated machines can still talk to ones that have not
yet updated.

- **Mac:** the app moves from `BroLink.app` to `Latch.app` the first time
it runs, and its login item with it.
- **Mac:** the app moves to `Latch.app` the first time it runs, and its
login item moves with it.
- **Windows:** the host updates in place and keeps working. To move it to
the new folder and names (`%LOCALAPPDATA%\Latch`, `latch-host.exe`, the
`LatchStream` service), run `install-host.ps1` from the new zip, then
**Set up as administrator** once on the **Sharing** tab.
- **Linux:** the first start registers `latch.service` and retires
`brolink.service`. The files in `deploy/native/` have new names; see
[deploy/native/README.md](deploy/native/README.md).

The environment variables beginning `BROLINK_` are now `LATCH_`; for
`LATCH_DATA_DIR` and `LATCH_GITHUB_TOKEN` the old names still work.
`%LOCALAPPDATA%\Latch`, `latch-host.exe` and the `LatchStream` service,
run `install-host.ps1` from the new zip, then **Set up as administrator**
once on the **Sharing** tab.
- **Linux:** the first start registers `latch.service` and retires the old
service. For a native desktop, see [deploy/native/README.md](deploy/native/README.md).

Use `LATCH_DATA_DIR` and `LATCH_GITHUB_TOKEN` for environment overrides.
The equivalent names from versions before 4.1.0 still work.

## Updates

Expand Down
2 changes: 1 addition & 1 deletion crates/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ semver = "1"
# pasteboard's change count.
[target.'cfg(target_os = "macos")'.dependencies]
objc2 = "0.5"
objc2-app-kit = { version = "0.2", features = ["NSApplication", "NSMenu", "NSMenuItem", "NSPasteboard", "NSResponder"] }
objc2-app-kit = { version = "0.2", features = ["NSApplication", "NSEvent", "NSMenu", "NSMenuItem", "NSPasteboard", "NSResponder"] }
objc2-foundation = { version = "0.2", features = ["NSString", "NSThread"] }

[dev-dependencies]
Expand Down
1 change: 1 addition & 0 deletions crates/client/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub const VK_INSERT: i16 = 0x2D;
pub const VK_DELETE: i16 = 0x2E;
pub const VK_ESCAPE: i16 = 0x1B;
pub const VK_TAB: i16 = 0x09;
pub const VK_CAPITAL: i16 = 0x14;
pub const VK_RETURN: i16 = 0x0D;
pub const VK_SNAPSHOT: i16 = 0x2C;
pub const VK_C: i16 = 0x43;
Expand Down
27 changes: 25 additions & 2 deletions crates/client/src/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
//! place in the menu but lose their shortcuts.
//!
//! And the pasteboard's change count, which tells whether the Mac's
//! clipboard has changed since the PC last copied something.
//! clipboard has changed since the PC last copied something. And whether
//! Caps Lock is on: egui has neither a key nor a modifier for it.
//!
//! Elsewhere both do nothing.

Expand All @@ -34,7 +35,7 @@ fn equivalent(item: Item, stream_owns_keys: bool) -> &'static str {
mod imp {
use super::{equivalent, Item};
use objc2::sel;
use objc2_app_kit::{NSApplication, NSPasteboard};
use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSPasteboard};
use objc2_foundation::{MainThreadMarker, NSString};
use std::cell::Cell;

Expand Down Expand Up @@ -79,6 +80,13 @@ mod imp {
// its change count.
Some(unsafe { NSPasteboard::generalPasteboard().changeCount() })
}

pub fn caps_lock() -> Option<bool> {
// SAFETY: a class getter for the modifier keys' state right now,
// usable from any thread.
let flags = unsafe { NSEvent::modifierFlags_class() };
Some(flags.contains(NSEventModifierFlags::NSEventModifierFlagCapsLock))
}
}

#[cfg(not(target_os = "macos"))]
Expand All @@ -88,6 +96,10 @@ mod imp {
pub fn pasteboard_changes() -> Option<isize> {
None
}

pub fn caps_lock() -> Option<bool> {
None
}
}

/// While `owned`, ⌘Q, ⌘H and ⌥⌘H go to the stream (and so to the PC)
Expand All @@ -102,6 +114,11 @@ pub fn pasteboard_changes() -> Option<isize> {
imp::pasteboard_changes()
}

/// Whether Caps Lock is on on this Mac. None where that is not known.
pub fn caps_lock() -> Option<bool> {
imp::caps_lock()
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -119,4 +136,10 @@ mod tests {
stream_owns_keys(true);
stream_owns_keys(false);
}

#[test]
fn caps_lock_is_known_on_a_mac() {
// Read through the window server, from any thread.
assert_eq!(caps_lock().is_some(), cfg!(target_os = "macos"));
}
}
Loading
Loading