# Building Meshy on macOS

This directory contains tools for building Meshy on macOS.

## Prerequisites

### System Dependencies (Homebrew)

```bash
# Install Homebrew if not already installed
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Install required dependencies
brew install gtk4 libadwaita gstreamer meson ninja pkg-config
brew install pygobject3 python@3.11
```

### Python Dependencies

```bash
# Core dependencies
pip install pycryptodome pyserial segno

# macOS-specific BLE and system integration
pip install pyobjc-framework-CoreBluetooth
pip install pyobjc-framework-CoreLocation

# Optional: Camera/QR scanning (Phase 3)
pip install pyobjc-framework-AVFoundation
pip install pyobjc-framework-Vision
```

Alternatively, use `uv` for faster installs:

```bash
uv pip install pycryptodome pyserial segno
uv pip install pyobjc-framework-CoreBluetooth pyobjc-framework-CoreLocation
```

## Building

### Versioning Convention

Meshy follows the same versioning strategy on macOS as on Linux:

- **Stable releases:** Version from git tag (e.g., `26.06`)
  - Built when on a tagged commit
  - Matches meson.build `version: '26.06'`
  - DMG name: `Meshy-26.06.dmg`

- **Unstable/Nightly:** Date-based version (e.g., `26.06.19`)
  - Built from `main` branch or untagged commits
  - Format: `YY.MM.DD` (matching Linux date_version)
  - DMG name: `Meshy-26.06.19.dmg`

The version is automatically determined by the build scripts:
- If on a git tag → use tag version
- Otherwise → use current date in `YY.MM.DD` format

### Development Build

For development, build with meson and run directly:

```bash
meson setup builddir --prefix=/usr
meson compile -C builddir
sudo meson install -C builddir

# Run
meshy
```

### Homebrew-Based App Bundle (Recommended for Development)

Creates a small (~20 MB) bundle that uses GTK4/libadwaita from Homebrew:

```bash
# Build unsigned app bundle
./build-aux/macos/build-homebrew-bundle.sh

# Build unsigned and create DMG
./build-aux/macos/build-homebrew-bundle.sh --dmg

# Build, sign, and create DMG for distribution
./build-aux/macos/build-homebrew-bundle.sh --sign "Developer ID Application: Your Name (TEAMID)" --dmg
```

Output:
- `dist-homebrew/Meshy.app` - Application bundle
- `dist-homebrew/Meshy-${VERSION}.dmg` - DMG installer (if --dmg flag used)

**Requirements:** Users must have `brew install gtk4 libadwaita python@3.14`

### Standalone App Bundle (Recommended for Distribution)

Creates a fully self-contained (~150 MB) bundle with all dependencies:

```bash
# Build unsigned self-contained bundle
./build-aux/macos/create-standalone-bundle.sh

# Build unsigned and create DMG
./build-aux/macos/create-standalone-bundle.sh --dmg

# Build, sign, and create DMG for distribution
./build-aux/macos/create-standalone-bundle.sh --sign "Developer ID Application: Your Name (TEAMID)" --dmg
```

Output:
- `dist/Meshy.app` - Self-contained application bundle
- `dist/Meshy-${VERSION}.dmg` - DMG installer (if --dmg flag used)

**No runtime dependencies required** - users can drag-and-drop to Applications.

### Code Signing

Both build scripts support optional code signing for distribution:

**Options:**
- `--sign "IDENTITY"` - Sign with specified identity from Keychain
- `--dmg` - Create DMG installer

**Common identities:**
- `"Developer ID Application: Your Name (TEAMID)"` - For distribution outside App Store
- `"Apple Development: Your Name (TEAMID)"` - For development/testing
- `"-"` - Ad-hoc signature (default when --sign not provided, not recommended)

**Examples:**
```bash
# Unsigned (default)
./build-aux/macos/build-homebrew-bundle.sh --dmg

# Signed for distribution
./build-aux/macos/build-homebrew-bundle.sh \
  --sign "Developer ID Application: John Doe (ABC123XYZ)" \
  --dmg

# List available signing identities
security find-identity -v -p codesigning
```

**Notarization:**
After signing for distribution, you should notarize the DMG:
```bash
# Submit for notarization (requires Apple Developer account)
xcrun notarytool submit dist/Meshy-26.06.dmg \
  --apple-id "your@email.com" \
  --team-id "TEAMID" \
  --password "app-specific-password" \
  --wait

# Staple the notarization ticket
xcrun stapler staple dist/Meshy-26.06.dmg
```

## Current Implementation Status

### ✅ Platform Infrastructure
- Platform detection module (`src/platforms/__init__.py`)
- Factory pattern for platform-specific implementations
- macOS-specific UI adaptations (traffic light buttons, window controls)

### ✅ USB Serial
- USB serial device discovery for macOS (`/dev/cu.usbmodem-*`, `/dev/cu.usbserial-*`)
- Automatic detection and connection

### ✅ BLE (CoreBluetooth)
- Full CoreBluetooth implementation via PyObjC
- Device scanning with NUS service UUID filter
- Device pairing and connection
- GATT service and characteristic discovery
- Bidirectional data transfer (TX/RX characteristics)
- Thread-safe callbacks to GTK main loop
- Automatic reconnection handling

### ✅ Location Services (CoreLocation)
- CoreLocation integration via PyObjC
- Permission handling (Info.plist)
- Location updates when app is in use
- Thread-safe coordinate updates to GTK

### ✅ Camera & QR Scanner (AVFoundation + Vision)
- Native camera access via AVFoundation
- Real-time camera preview using custom GdkPaintable
- QR code detection using Vision framework
- Matches Linux UI/UX with native macOS backend

### ✅ Notifications
- Native macOS notification center integration
- UserNotifications framework via PyObjC
- Permission handling
- Notification delivery and display

### ✅ System Integration
- Login items (auto-start on login)
- App activation handling
- Proper window lifecycle management
- GResource bundling for icons and UI files

## Platform Differences

### Bluetooth
- **Linux:** Uses BlueZ D-Bus API
- **macOS:** Uses CoreBluetooth framework via PyObjC
  - Pairing handled automatically by system
  - No explicit passkey dialogs (system manages)

### USB Serial
- **Linux:** `/dev/ttyACM*`, `/dev/ttyUSB*`
- **macOS:** `/dev/cu.usbserial-*`, `/dev/cu.usbmodem-*`
  - No udev rules needed

### Location
- **Linux:** GeoClue2 D-Bus API
- **macOS:** CoreLocation framework (requires Info.plist permission)

### Camera
- **Linux:** XDG Portal + PipeWire
- **macOS:** AVFoundation + Vision framework

## Known Issues & Workarounds

1. **Emoji Rendering Crash (Bus Error 10)**
   - **Issue:** CoreText backend crashes when rendering certain emojis
   - **Workaround:** Using `PANGOCAIRO_BACKEND=fc` (fontconfig) instead of CoreText
   - **Status:** Temporary workaround in place, upstream Pango issue #871

2. **Map Widget Instability**
   - **Issue:** Shumate map widget can crash with GdkMacos backend
   - **Workaround:** Wrapped in try/except with graceful fallback
   - **Status:** Works but may fail on some macOS versions

3. **Window Maximize Behavior**
   - **Issue:** GTK4 maximize not native macOS full-screen
   - **Status:** Disabled maximize button, using macOS traffic lights

## Contributing

When adding macOS-specific code:
1. Use the platform detection utilities in `src/platforms/__init__.py`
2. Place platform-specific implementations in `src/platforms/macos/`
3. Keep Linux implementation working (test on both platforms)
4. Use `GLib.idle_add()` for callbacks from Objective-C to GTK main thread

## Testing

### USB Serial
1. Connect MeshCore device via USB
2. Launch Meshy
3. Go to Connection view → USB tab
4. Verify device appears as `/dev/cu.usbmodem-*` or `/dev/cu.usbserial-*`
5. Test connection and data transfer

### BLE (CoreBluetooth)
1. Ensure MeshCore device is in BLE mode and advertising
2. Launch Meshy
3. Grant Bluetooth permissions when prompted
4. Go to Connection view → BLE tab
5. Click "Scan for devices"
6. Verify device appears with NUS service
7. Connect and test data transfer

### Location Services
1. Launch Meshy
2. Grant location permissions when prompted (Info.plist description shown)
3. Navigate to a view that uses location
4. Verify location updates work correctly

### Camera & QR Scanner
1. Launch Meshy
2. Grant camera permissions when prompted
3. Go to Contacts → + → Scan QR Code
4. Verify camera preview shows live video
5. Point at a QR code
6. Verify QR code is detected and scanned within 2-3 seconds

### Notifications
1. Launch Meshy
2. Grant notification permissions when prompted
3. Send a message to trigger a notification
4. Verify notification appears in macOS Notification Center

### Build Scripts
```bash
# Test Homebrew bundle build
./build-aux/macos/build-homebrew-bundle.sh
open dist-homebrew/Meshy.app

# Test standalone bundle build
./build-aux/macos/create-standalone-bundle.sh
open dist/Meshy.app

# Test DMG creation
./build-aux/macos/build-homebrew-bundle.sh --dmg
# Verify dist-homebrew/Meshy-${VERSION}.dmg exists

# Test versioning
# On tag: should use tag version (e.g., 26.06)
git tag -a 26.06.test -m "Test tag"
./build-aux/macos/build-homebrew-bundle.sh --dmg
# Should create Meshy-26.06.test.dmg

# Off tag: should use date version (e.g., 26.06.19)
git checkout main
./build-aux/macos/build-homebrew-bundle.sh --dmg
# Should create Meshy-YY.MM.DD.dmg
```
