6.7 KiB
Snaggle
A native, Apple-silicon macOS screenshot tool modelled on Greenshot for Windows. Written in Swift with AppKit, SwiftUI and ScreenCaptureKit — no Mono, no Wine, no .NET runtime.
The original C# sources are kept in reference-greenshot/ purely as the behavioural reference
for the port. Snaggle is a rewrite, not a transpile: Greenshot's Win32 foundations
(GDI+, WinForms, RegisterHotKey, the shell tray API) have no direct macOS counterparts.
Requirements
- macOS 14 (Sonoma) or later
- Xcode 16 or later
- Apple silicon (the target builds
arm64only)
Building
open Snaggle.xcodeproj # then ⌘R
# or
xcodebuild -project Snaggle.xcodeproj -scheme Snaggle -configuration Release build
The target is configured for ad-hoc signing (CODE_SIGN_IDENTITY = "-") so it builds without a
developer account. Set your own team in Signing & Capabilities if you want a stable signature —
macOS ties Screen Recording permission to the signature, so an ad-hoc build may ask for permission
again after some rebuilds.
On first launch macOS asks for Screen Recording permission (System Settings › Privacy & Security › Screen Recording). Nothing can be captured until it is granted.
Installing (for people downloading a release)
Snaggle is ad-hoc signed rather than notarised, because notarisation requires a paid Apple Developer account. It runs perfectly well; macOS just asks you to confirm it once.
- Drag Snaggle to Applications and open it. macOS will refuse the first time, saying the developer cannot be verified.
- Open System Settings → Privacy & Security, scroll to Security, and click Open Anyway next to the message about Snaggle.
Control-clicking an app and choosing Open no longer bypasses Gatekeeper — Apple removed that
shortcut in macOS 15. If you prefer the terminal, xattr -dr com.apple.quarantine /Applications/Snaggle.app does the same job in one step.
Snaggle then needs Screen Recording permission (System Settings → Privacy & Security → Screen Recording). Grant it, then quit and reopen the app — macOS only hands the permission to a freshly launched process.
Snaggle lives in the menu bar, not the Dock: look for the viewfinder icon.
Building a release
./scripts/release.sh # ad-hoc signed, builds build/Snaggle.dmg
./scripts/release.sh --developer-id # Developer ID + notarisation (paid account)
The DMG contains the app, an Applications symlink and INSTALL.txt. For the notarised path,
set your team ID in scripts/ExportOptions.plist and store a notary credential profile first.
The app icon is generated, not hand-drawn: edit the colours or shapes in
scripts/make_appicon.py and run python3 scripts/make_appicon.py to rewrite every size in
Snaggle/Resources/Assets.xcassets/AppIcon.appiconset.
Using it
Snaggle is a menu bar app (LSUIElement), so it has no Dock icon. The camera icon in the
menu bar holds every command.
Default global hotkeys — Greenshot's PrintScreen-based defaults don't exist on a Mac keyboard, so these use combinations macOS leaves free:
| Action | Shortcut |
|---|---|
| Capture region | ⌥⇧⌘4 |
| Capture full screen | ⌥⇧⌘3 |
| Capture window | ⌥⇧⌘5 |
| Capture last region | ⌥⇧⌘6 |
All four are re-recordable in Preferences › Hotkeys.
During a capture the desktop freezes under a dimmed overlay: drag to select a region, press Space to switch to window mode and click a window, Esc to cancel. A magnifier and a live pixel-size readout follow the cursor.
Editor
| Tool | Key |
|---|---|
| Select | V |
| Rectangle / Ellipse | R / E |
| Line / Arrow | L / A |
| Freehand | F |
| Text | T |
| Highlight | H |
| Obfuscate (pixelate or blur) | O |
| Step counter | C |
| Crop | K |
Shift constrains shapes to squares/circles and lines to 45° steps. Arrow keys nudge the selection (⇧ for 10 px steps). ⌘Z / ⇧⌘Z undo and redo, including crops.
Destinations
After a capture the destination picker appears: a preview of what you grabbed, one button per destination, ⌘1…⌘9 to pick from the keyboard, Esc to discard, and "Always use this destination" to skip the dialog next time. The buttons default to Edit / Save to Disk / Copy to Clipboard / Send as Mail, matching Greenshot.
The full destination set is clipboard, save with a filename pattern, Save As…, print, email, the macOS share sheet, and "open with". Set a destination as the default (skipping the picker) in Preferences › General, from the menu bar, or via the picker's checkbox.
Filename patterns use Greenshot's syntax, ported verbatim:
${capturetime:d"yyyy-MM-dd HH_mm_ss"}-${title}
Supported variables: ${capturetime}, ${now}, ${title}, ${appname}, ${user},
${hostname}, ${mode}, ${NUM} (with :d4 for zero padding), plus the :t, :u, :l,
:s, :r parameters.
How the port maps onto Greenshot
| Greenshot (Windows) | Snaggle |
|---|---|
MainForm + tray icon |
StatusMenuController (NSStatusItem) |
CaptureForm |
CaptureOverlay (one borderless window per display) |
WindowCapture / GDI+ |
CaptureEngine (ScreenCaptureKit) |
HotkeyControl / RegisterHotKey |
HotkeyManager (Carbon RegisterEventHotKey) |
Surface + DrawableContainer |
Surface + Element hierarchy |
ImageEditorForm |
EditorWindowController + EditorCanvasView |
FilenameHelper |
FilenamePattern |
ImageIO |
ImageExporter |
IDestination implementations |
Destinations |
SettingsForm |
PreferencesView (SwiftUI) |
IniConfig / greenshot.ini |
Configuration (UserDefaults) |
Not ported
The plugin destinations (Imgur, Box, Dropbox, Confluence, Jira, Office, OCR, external command),
the speech-bubble and SVG/emoji containers, .gst templates, and Greenshot's language packs.
The destination layer is a single switch in Destinations.swift, so adding an upload target is
a small, self-contained change.
Licence and attribution
Snaggle is a derivative work of Greenshot
(Copyright © 2007-2026 Thomas Braun, Jens Klingen, Robin Krom), reimplemented in Swift for
macOS, and is released under the GNU General Public License v3.0 — the full text is in
LICENSE.
That means anyone may use, study, modify and redistribute Snaggle, including commercially, provided the source stays available under the same licence. If you distribute a built copy of Snaggle, you must also make this source available to whoever receives it.
"Greenshot" is a trademark of the Greenshot project and is used here only to describe Snaggle's origin. Snaggle is not affiliated with or endorsed by the Greenshot project, and it does not use their name, logo or branding for itself.