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
73 changes: 40 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ A macOS menu-bar utility that hands off Magic Keyboard, Magic Trackpad, and Magi

This is a security-hardened fork of [HoshimuraYuto/blue-switch](https://github.com/HoshimuraYuto/blue-switch). The original ships an unauthenticated, unencrypted LAN protocol that lets anyone on the same Wi-Fi take over your Bluetooth peripherals or spoof notifications. This fork replaces that channel with a sealed, mutually-authenticated channel keyed by a 12-character pairing code you share between your two Macs — with a massively improved UI/UX over the original: a guided pairing flow, inline status feedback, per-peripheral switching, a needs-attention menu-bar icon, and safe preflight-and-rollback handoffs.

<p align="center">
<img src="docs/assets/menu.png" alt="The Magic Switch menu-bar dropdown" width="340"><br>
<em>It lives in the menu bar: click a Mac to hand over everything, or a single peripheral to move just that one.</em>
</p>

## Installation

1. Grab the latest build from the [releases page](https://github.com/MegaManSec/magic-switch/releases).
Expand All @@ -17,46 +22,40 @@ This is a security-hardened fork of [HoshimuraYuto/blue-switch](https://github.c

## Setup

Four Settings tabs to know — two of them use the word "pair" in different senses, which can be confusing:
Magic Switch has four Settings tabs, and two of them use the word "pair" in different senseswhich trips people up. **Pairing** is the cryptographic key shared between the two *Macs* (required, and set up inside Magic Switch); the Bluetooth pairing of your *peripherals* is a separate thing, done in System Settings. Do everything below on **both** Macs.

- **Peripheral** — the Bluetooth devices Magic Switch hands back and forth (Magic Keyboard / Mouse / Trackpad).
- **Device** — the *other Mac on your network* you're swapping with.
- **Pairing** — a cryptographic shared key between the two Macs. *Required.* This is **not** the Bluetooth pairing in step 1 — that's between your peripherals and each Mac, done in System Settings. This one is between the two Macs themselves, done inside Magic Switch.
- **Other** — app preferences: **Launch at Login**, two peripheral-handling toggles (**Release peripherals when this Mac sleeps** and **Reconnect peripherals if they drop** — see [Troubleshooting](#troubleshooting)), the installed version, and update notifications (see [Updates](#updates)).
### 1. Pair your peripherals to each Mac (System Settings)

<p align="center">
<img src="docs/assets/peripheral-tab.png" alt="Peripheral tab with each control labelled" width="600"><br>
<em>Peripheral tab — register the Magic devices you want to hand off.</em>
</p>
In **System Settings → Bluetooth on each Mac**, pair your Magic Keyboard / Mouse / Trackpad to *that* Mac the normal macOS way — **each peripheral has to be paired to both Macs**. Apple's Magic devices remember multiple hosts but only connect to one at a time; Magic Switch flips which Mac currently holds a peripheral, but it doesn't create those pairings for you. (Once that's done, you won't re-pair by hand on every switch — Magic Switch handles the handoff.)

Then launch Magic Switch, grant **Bluetooth** and **Local Network** when prompted, and right-click the menu-bar icon → **Settings**.

### 2. Peripheral tab — choose what to manage

Tick the Magic devices you want Magic Switch to hand back and forth. Each row's leading icon shows the detected device type; click it to override the type or reset it to Automatic.

<p align="center">
<img src="docs/assets/device-tab.png" alt="Device tab with each control labelled" width="600"><br>
<em>Device tab — pick the other Mac, sync peripherals to it, and check it's reachable.</em>
<img src="docs/assets/peripheral-tab.png" alt="Peripheral tab showing registered and available peripherals" width="600"><br>
<em>Peripheral tab — register the Magic devices you want to hand off. Each row's leading icon shows the detected device type.</em>
</p>

<p align="center">
<img src="docs/assets/other-tab.png" alt="Other tab with each control labelled" width="600"><br>
<em>Other tab — Launch at Login, license info, version, and a manual update check.</em>
<img src="docs/assets/peripheral-type-picker.png" alt="Peripheral tab with a row's type picker open" width="600"><br>
<em>That leading icon is also a picker — Magic Switch auto-detects the type (keyboard, mouse, trackpad, headphones, AirPods, microphone), and you can override it or set it back to Automatic.</em>
</p>

Do this on **both** Macs.
### 3. Device tab — pick the other Mac

1. **In System Settings → Bluetooth on each Mac**, pair your Magic Keyboard / Mouse / Trackpad to *that* Mac the normal macOS way — **each peripheral has to be paired to both Macs**. Apple's Magic devices remember multiple hosts but only connect to one at a time; Magic Switch flips which Mac currently holds a peripheral, but it doesn't create those pairings for you — set them up on both Macs in System Settings first. (Once that's done, you won't re-pair by hand on every switch — Magic Switch handles the handoff.)
2. Launch Magic Switch. Grant **Bluetooth** and **Local Network** permission when prompted.
3. Right-click the menu-bar icon → Settings:
- **Peripheral** tab: tick the Magic devices you want Magic Switch to manage.
- **Device** tab: pick the other Mac from "Available Devices."
4. **Pairing** tab — *required*:
- On one Mac, click "Generate Code." A twelve-character code appears.
- On the other, click "Enter Code" and type it in.
- Both Macs should show the same eight-character fingerprint after pairing. If they don't, you typed the code wrong.
5. Sync your peripheral list to the other Mac: on the **Device** tab, find it under **Connected Devices** and click its **share button** (the box-with-an-up-arrow icon, beside **Ping**). A "Synced N peripherals to …" line appears under the row on success. The button is greyed out while that Mac is offline.
Choose the other Mac under **Available Devices**. It shows up once it's on the same network running Magic Switch; a greyed-out row means it isn't reachable right now.

Until step 4 completes, the switch action and peripheral sync refuse to talk to the peer.
<p align="center">
<img src="docs/assets/device-tab.png" alt="Device tab showing the connected Mac and available devices" width="600"><br>
<em>Device tab — pick the other Mac, sync peripherals to it, and check it's reachable.</em>
</p>

### The pairing flow
### 4. Pairing tab — link the two Macs (required)

Pairing all happens in the **Pairing** tab — generate a code on one Mac and enter it on the other (either direction works; both Macs derive the same key from the same code).
Generate a twelve-character code on one Mac and enter it on the other; either direction works, since both Macs derive the same key from the same code. They should then show the same eight-character fingerprint — if they differ, the code was mistyped. Until this is done, switching and peripheral sync refuse to talk to the peer.

<p align="center">
<img src="docs/assets/pairing-not-paired.png" alt="Pairing tab before pairing" width="600"><br>
Expand All @@ -74,6 +73,19 @@ Pairing all happens in the **Pairing** tab — generate a code on one Mac and en
<em>After pairing — both Macs show the same fingerprint. If they differ, the code was mistyped.</em>
</p>

### 5. Sync your peripherals to the other Mac

On the **Device** tab, find the other Mac under **Connected Devices** and click its **Share** button (the box-with-an-up-arrow, beside **Ping**). A "Synced N peripherals to …" line confirms it. The button is greyed out while that Mac is offline.

### Other tab — preferences

**Launch at Login**, two peripheral-handling toggles (**Release peripherals when this Mac sleeps** and **Reconnect peripherals if they drop** — see [Troubleshooting](#troubleshooting)), the installed version, and update notifications (see [Updates](#updates)).

<p align="center">
<img src="docs/assets/other-tab.png" alt="Other tab showing app preferences" width="600"><br>
<em>Other tab — Launch at Login, the sleep-release and auto-reconnect toggles, license info, version, and a manual update check.</em>
</p>

## Usage

| Action | Result |
Expand All @@ -83,12 +95,7 @@ Pairing all happens in the **Pairing** tab — generate a code on one Mac and en
| Menu → a peripheral | Switch just that one peripheral. Checkmark = currently on this Mac |
| Menu → Settings | Open the Settings window |

The menu-bar icon also signals state: a **warning triangle** means Magic Switch needs attention (not paired, or Bluetooth off/denied) — hover for the reason; **up/down arrows** flash briefly while peripherals are moving between Macs.

<p align="center">
<img src="docs/assets/menu.png" alt="The menu-bar menu with actions labelled" width="320"><br>
<em>The menu — click a Mac to move everything, or a single peripheral to move just that one.</em>
</p>
The menu-bar icon also signals state: a **warning triangle** means Magic Switch needs attention (not paired, or Bluetooth off/denied) — hover for the reason; **up/down arrows** flash briefly while peripherals are moving between Macs (the dropdown is pictured at the top of this README).

## Updates

Expand Down
123 changes: 123 additions & 0 deletions docs/annotate-screenshots.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#!/bin/bash
# Regenerate the annotated README screenshots in docs/assets/ from the raw
# captures in docs/assets/raw/. Adds numbered yellow badges + a bottom legend,
# matching the project's existing screenshot style.
#
# Repeatable: recapture a raw screenshot into docs/assets/raw/ (or tweak the
# badge/legend config below) and re-run:
#
# ./docs/annotate-screenshots.sh
#
# Requires ImageMagick v7 (`magick`) and the referenced macOS system fonts.
set -euo pipefail

DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
RAW="$DIR/assets/raw"
OUT="$DIR/assets"
BADGE_DIR="$(mktemp -d)"
trap 'rm -rf "$BADGE_DIR"' EXIT

YELLOW="#FFD60A"
LIGHT="#EAEAEA"
NUMFONT="/System/Library/Fonts/Supplemental/Arial Bold.ttf" # badge + legend numbers
TEXTFONT="/System/Library/Fonts/SFNS.ttf" # legend body (San Francisco)

# Pre-render badge glyphs 1..6 (36px circle, centered black number).
for n in 1 2 3 4 5 6; do
magick -size 40x40 xc:none \
-fill "$YELLOW" -draw "circle 20,20 20,2" \
-gravity center -font "$NUMFONT" -pointsize 23 -fill black -annotate +0+0 "$n" \
"$BADGE_DIR/$n.png"
done

# annotate NAME BG BOTTOM_PAD LEGEND_X LEGEND_Y0 LEGEND_DY BADGES LEGEND [LEGEND_PT]
# reads $RAW/NAME.png, writes $OUT/NAME.png
# BADGES : newline-separated "num badge_cx badge_cy target_x target_y"
# LEGEND : newline-separated "num|text" (blank num => unnumbered continuation)
annotate() {
local name="$1" bg="$2" pad="$3" lx="$4" ly0="$5" ldy="$6" badges="$7" legend="$8"
local legpt="${9:-27}"
local input="$RAW/$name.png" output="$OUT/$name.png"
local W H; read W H < <(magick identify -format "%w %h\n" "$input")

local args=( "$input" )
# Pad below for an over-long legend; chop the window's 1px bottom edge first so
# the join to the flat background is seamless.
[ "$pad" -gt 0 ] && args+=( -gravity South -chop 0x2 -background "$bg" -gravity NorthWest -extent "${W}x$((H+pad))" )
args+=( -gravity NorthWest )

# Leader lines first (the badge circle is drawn on top, hiding the stub).
args+=( -stroke "$YELLOW" -strokewidth 3 -fill none )
while IFS= read -r b; do
[ -z "$b" ] && continue
set -- $b; args+=( -draw "line $2,$3 $4,$5" )
done <<< "$badges"

# Badges: composite pre-rendered glyphs on top of the leader stubs.
args+=( -stroke none )
while IFS= read -r b; do
[ -z "$b" ] && continue
set -- $b
args+=( "(" "$BADGE_DIR/$1.png" -geometry "+$(($2-20))+$(($3-20))" ")" -composite )
done <<< "$badges"

# Legend (yellow number + light body text), one line per entry.
local i=0
while IFS= read -r l; do
[ -z "$l" ] && continue
local num="${l%%|*}" text="${l#*|}" y=$((ly0 + i*ldy))
args+=( -font "$NUMFONT" -pointsize "$legpt" -fill "$YELLOW" -annotate "+${lx}+${y}" "$num" )
args+=( -font "$TEXTFONT" -pointsize "$legpt" -fill "$LIGHT" -annotate "+$((lx+34))+${y}" "$text" )
i=$((i+1))
done <<< "$legend"

args+=( "$output" )
magick "${args[@]}"
echo "wrote $output ($(magick identify -format '%wx%h' "$output"))"
}

# ---- Peripheral tab ----
annotate peripheral-tab "#292929" 0 88 583 47 \
"2 838 205 895 205
3 1108 352 1108 312
1 1050 483 1092 483" \
"1|Register a Magic device so Magic Switch can manage and hand it off
2|Release — hand this peripheral to the other Mac
3|Remove — stop managing this peripheral"

# ---- Peripheral tab: type picker (single badge) ----
annotate peripheral-type-picker "#292929" 0 88 730 47 \
"1 185 158 118 200" \
"1|Click a peripheral's icon to pick a type — or Automatic to auto-detect"

# ---- Device tab ----
annotate device-tab "#282828" 0 88 465 47 \
"1 932 272 932 230
2 1021 272 1021 230
3 1097 272 1097 230
4 1075 322 1102 322" \
"1|Ping — check the other Mac is reachable
2|Share — sync your registered peripherals to that Mac
3|Remove — forget this Mac
4|Refresh — rescan the network for nearby Macs"

# ---- Other tab (legend extends below the window) ----
annotate other-tab "#292929" 150 88 715 47 \
"1 1055 148 1085 148
2 1055 240 1085 240
3 1055 332 1085 332
4 1078 424 1108 424
5 160 655 160 612" \
"1|Launch at Login — start Magic Switch when you log in
2|Release peripherals to the other Mac when this Mac sleeps
3|Reconnect peripherals automatically if they drop
4|License Information — open-source license details
5|Check for Updates — check now (status shows on the right)"

# ---- Menu (translucent material background, smaller legend) ----
annotate menu "#2D2D30" 150 24 405 36 \
"1 435 72 380 72
2 435 223 380 223" \
"1|Click a Mac — move all peripherals there
2|Click a peripheral — move just that one
|✓ = it's on this Mac now" 20
Binary file modified docs/assets/device-tab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/other-tab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/peripheral-tab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/peripheral-type-picker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.