Cross-platform text selection monitoring for Flutter desktop apps via FFI.
Detects text selections in any application on Windows, macOS, and Linux using platform accessibility APIs (UIAutomation, AXAPI, X11 PRIMARY selection).
Ported from selection-hook (Node.js
N-API) to C-ABI for dart:ffi — preserving ~95% of the upstream native code.
- Text selection detection with full metadata (coordinates, method, position level)
- Mouse events — down, up, move, wheel (wheel direction + per-axis data)
- Keyboard events — unified MDN
KeyboardEvent.keyvalues + platform vk codes - Clipboard read/write (macOS, Windows)
- Program filtering — include/exclude lists for selection events
- Passive mode — only detect on manual
getCurrentSelection() - macOS fullscreen detection
- Pure FFI — no platform channels, lower latency for high-frequency events
| Feature | macOS | Windows | Linux X11 | Linux Wayland |
|---|---|---|---|---|
| Text selection | ✅ | ✅ | ✅ | partial (code present, not buildable) |
| Mouse events | ✅ | ✅ | ✅ | — |
| Keyboard events | ✅ | ✅ | ✅ | — |
| Clipboard read/write | ✅ | ✅ | — | — |
- Flutter >= 3.3.0 / Dart >= 3.12.0
- macOS 10.14+, Windows 7+, Linux (X11)
The app must be granted Accessibility permission:
- Go to System Settings > Privacy & Security > Accessibility
- Enable your application
- Restart the application
The first time start() is called, macOS may prompt for permission automatically.
If denied or dismissed, you must enable it manually.
Hot reload warning: After hot reload, the native hook instance is recreated. Stop monitoring before reloading, then restart.
import 'package:selection_hook_flutter/selection_hook_flutter.dart';
void main() {
final hook = SelectionHook.instance;
// Listen for text selections
hook.onTextSelection.listen((event) {
print('Selected: "${event.text}" in ${event.programName}');
});
// Optionally listen for mouse/keyboard events
hook.onMouseEvent.listen((event) {
print('Mouse: ${event.eventType} at (${event.x}, ${event.y})');
});
hook.start(); // May prompt for accessibility on macOS
}| Method / Getter | Description |
|---|---|
start() |
Start monitoring. May throw StateError if accessibility not granted. |
stop() |
Stop monitoring. Blocks until no in-flight callbacks. |
dispose() |
Stop + release all native resources. Idempotent. |
getCurrentSelection() |
Synchronous snapshot of current selection (or null). |
configure({...}) |
Set config before start(). Options: debug, enableMouseMove, enableClipboard, selectionPassiveMode. |
writeClipboard(text) |
Write text to clipboard (macOS/Windows). |
readClipboard() |
Read text from clipboard (macOS/Windows). Returns String?. |
enableMouseMove() |
Enable high-CPU mouse move events. Call before start(). |
disableMouseMove() |
Disable mouse move events (default). |
isRunning |
bool getter — whether the hook is actively monitoring. |
| Stream | Type |
|---|---|
onTextSelection |
Stream<TextSelectionEvent> |
onMouseEvent |
Stream<MouseEvent> |
onKeyboardEvent |
Stream<KeyboardEvent> |
| Field | Type | Description |
|---|---|---|
text |
String |
Selected text (UTF-8) |
programName |
String |
Bundle/executable name of source app |
startX, startY |
int |
Selection start coordinates (screen pixels) |
endX, endY |
int |
Selection end coordinates |
method |
int |
Selection method (11=AXAPI, 1=UIA, 22=PRIMARY, 99=Clipboard) |
posLevel |
int |
Position detail level (0–4) |
isFullscreen |
bool |
Source window fullscreen (macOS only) |
| Field | Type | Description |
|---|---|---|
x, y |
int |
Screen coordinates |
button |
int |
Button: -1=None, 0=Left, 1=Middle, 2=Right, 3=Back, 4=Forward |
eventType |
int |
0=down, 1=up, 2=move, 3=wheel |
flag |
int |
Wheel direction: 1=Up/Right, -1=Down/Left |
Computed getters: isMouseDown, isMouseUp, isMouseMove, isMouseWheel.
| Field | Type | Description |
|---|---|---|
uniKey |
String |
MDN KeyboardEvent.key value |
vkCode |
int |
Platform virtual key code |
isSys |
bool |
Modifier key pressed (Alt/Ctrl/Win/Cmd/Fn) |
flags |
int |
Platform flags |
Dart (SelectionHook singleton)
→ selection_hook_impl.dart (FFI wrapper)
→ NativeCallable.listener (for thread callbacks)
→ src/bridge/c_api.h (C-ABI, 20 functions)
├── src/bridge/c_api_mac.mm → src/mac/selection_hook_core.mm (CGEventTap + AXAPI)
├── src/bridge/c_api_win.cc → src/windows/selection_hook_core.cc (WH_*_LL + UIAutomation)
└── src/bridge/c_api_linux.cc → src/linux/selection_hook_core.cc (XRecord + XFixes)
No platform channels. Callbacks from native OS threads are marshalled via
NativeCallable.listener. Per-platform bridges delegate to platform cores
that implement selection detection (drag, double-click, shift-click) with
text extraction via accessibility APIs or clipboard fallback.
MIT — see LICENSE.
This project is a port of selection-hook by 0xfullex, also MIT licensed. Upstream copyright included in LICENSE.