Skip to content

Commit 2d1d858

Browse files
committed
examples: one runnable example per module
Examples/Example was written against the hand-written wrappers, so it stopped compiling when those went away. Rather than port one kitchen sink, this splits it into an example per module, matching what the Rust bindings do. Each is its own executable target that prints what it does, so running one shows the shape of a binding without reading the generated source. The list covers the parts that are hardest to get right from a signature alone: callbacks, event listeners and handle ownership. None of them hang a terminal. ApplicationExample is the only one that opens a window and blocks, and it takes --dry-run to skip the loop; MessageDialogExample needs --open to actually show its modal, since open() blocks until the user dismisses it. The examples that need accessibility permission report that they could not register or monitor rather than crashing, and LaunchAtLoginExample puts the login-item registration back the way it found it. DisplayManagerDemo drops the .shared it used to go through: DisplayManager is a C++ singleton, so the generated binding is a caseless enum with static methods.
1 parent c5b65ef commit 2d1d858

16 files changed

Lines changed: 882 additions & 237 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Accessibility example — checks whether the process is trusted for
2+
// accessibility APIs, and asks for permission if not.
3+
//
4+
// On macOS enable() opens the System Settings pane; elsewhere it is a no-op
5+
// and isEnabled() reports true.
6+
//
7+
// Usage:
8+
// swift run AccessibilityExample
9+
10+
import Foundation
11+
import NativeAPI
12+
13+
let enabled = AccessibilityManager.isEnabled()
14+
print("Accessibility enabled: \(enabled)")
15+
16+
if enabled {
17+
print("Global keyboard monitoring and shortcuts will work.")
18+
exit(0)
19+
}
20+
21+
print("Requesting accessibility permission...")
22+
AccessibilityManager.enable()
23+
print("After the request: \(AccessibilityManager.isEnabled())")
24+
print(
25+
"Grant the permission in System Settings > Privacy & Security > Accessibility, "
26+
+ "then run this again.")
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Application example — wires up an app: a menu bar, a primary window, and
2+
// lifecycle events, then runs the event loop.
3+
//
4+
// This one *does* open a window and block. Pass --dry-run to exercise
5+
// everything except the loop, which is what CI does.
6+
//
7+
// Usage:
8+
// swift run ApplicationExample
9+
// swift run ApplicationExample --dry-run
10+
11+
import Foundation
12+
import NativeAPI
13+
14+
let dryRun = CommandLine.arguments.contains("--dry-run")
15+
16+
// --- 1. Lifecycle events ---
17+
let listener = Application.addListener { event in
18+
switch event {
19+
case .started: print("[app] started")
20+
case .exiting(let exitCode): print("[app] exiting (\(exitCode))")
21+
case .activated: print("[app] activated")
22+
case .deactivated: print("[app] deactivated")
23+
case .quitRequested:
24+
print("[app] quit requested")
25+
Application.quit(exitCode: 0)
26+
}
27+
}
28+
29+
print("Single instance: \(Application.isSingleInstance())")
30+
31+
// --- 2. Menu bar ---
32+
if let menuBar = Menu.create() {
33+
if let about = MenuItem.createWithLabelAndType(label: "About", type: .normal) {
34+
menuBar.addItem(item: about)
35+
}
36+
menuBar.addSeparator()
37+
if let quit = MenuItem.createWithLabelAndType(label: "Quit", type: .normal) {
38+
_ = quit.addListener { _ in Application.quit(exitCode: 0) }
39+
menuBar.addItem(item: quit)
40+
}
41+
print("Menu bar installed: \(Application.setMenuBar(menu: menuBar))")
42+
}
43+
44+
// --- 3. Primary window ---
45+
guard let window = Window.create() else {
46+
fatalError("Failed to create a window")
47+
}
48+
window.setTitle(title: "Swift Application Example")
49+
window.setSize(size: Size(width: 640, height: 480), animate: false)
50+
window.center()
51+
52+
Application.setPrimaryWindow(window: window)
53+
if let primary = Application.getPrimaryWindow() {
54+
print("Primary window: #\(primary.id)")
55+
}
56+
print("Known windows: \(Application.getAllWindows().count)")
57+
58+
// --- 4. Run ---
59+
if dryRun {
60+
print("--dry-run: skipping the event loop.")
61+
print("isRunning = \(Application.isRunning())")
62+
_ = Application.removeListener(listener)
63+
exit(0)
64+
}
65+
66+
window.show()
67+
print("Running. Close the window or press Ctrl+C to quit.")
68+
let exitCode = Application.runWithWindow(window: window)
69+
print("Exited with \(exitCode)")
70+
_ = Application.removeListener(listener)
71+
exit(exitCode)

Examples/DisplayExample/main.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Display example — enumerates the connected displays, prints their
2+
// properties, and subscribes to display change events.
3+
//
4+
// Usage:
5+
// swift run DisplayExample
6+
7+
import Foundation
8+
import NativeAPI
9+
10+
// --- 1. Events ---
11+
let listener = DisplayManager.addListener { event in
12+
switch event {
13+
case .added(let display):
14+
print("[display] added: \(display.name ?? "(unnamed)")")
15+
case .removed(let display):
16+
print("[display] removed: \(display.name ?? "(unnamed)")")
17+
case .changed(_, let oldDisplay, let newDisplay):
18+
print("[display] changed: \(oldDisplay.name ?? "?") -> \(newDisplay.name ?? "?")")
19+
}
20+
}
21+
22+
// --- 2. All displays ---
23+
let displays = DisplayManager.getAll()
24+
print("Found \(displays.count) display(s):")
25+
for (index, display) in displays.enumerated() {
26+
print("\nDisplay \(index + 1):")
27+
describe(display)
28+
}
29+
30+
// --- 3. Primary ---
31+
if let primary = DisplayManager.getPrimary() {
32+
print("\nPrimary display: \(primary.name ?? "(unnamed)")")
33+
} else {
34+
print("\nNo primary display available.")
35+
}
36+
37+
// --- 4. Cursor ---
38+
let cursor = DisplayManager.getCursorPosition()
39+
print("Cursor at (\(cursor.x), \(cursor.y))")
40+
41+
_ = DisplayManager.removeListener(listener)
42+
43+
func describe(_ display: Display) {
44+
print(" Name: \(display.name ?? "(unnamed)")")
45+
print(" Id: \(display.id ?? "(none)")")
46+
print(" Position: (\(display.position.x), \(display.position.y))")
47+
print(" Size: \(display.size.width) x \(display.size.height)")
48+
let area = display.workArea
49+
print(" Work area: (\(area.x), \(area.y)) \(area.width) x \(area.height)")
50+
print(" Scale factor: \(display.scaleFactor)")
51+
print(" Primary: \(display.isPrimary)")
52+
print(" Orientation: \(display.orientation)")
53+
print(" Refresh rate: \(display.refreshRate) Hz")
54+
print(" Bit depth: \(display.bitDepth)")
55+
}

Examples/Example/main.swift

Lines changed: 0 additions & 227 deletions
This file was deleted.

Examples/ExampleApp/Sources/DisplayManagerDemo.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ final class DisplayManagerDemoState: State<DisplayManagerDemo> {
4040
}
4141

4242
private func updateDisplayInfo() {
43-
state.displays = DisplayManager.shared.getAll()
44-
state.cursorPosition = DisplayManager.shared.getCursorPosition()
43+
state.displays = DisplayManager.getAll()
44+
state.cursorPosition = DisplayManager.getCursorPosition()
4545
state.currentWindow = WindowManager.shared.getCurrent()
4646
state.lastUpdate = Date()
4747
}

0 commit comments

Comments
 (0)