Initial commit: uskey - macOS keyboard remapper
Features: - Menu bar GUI with enable/disable toggle - JSON-based configuration system - File-based logging with debug support - CGEventTap-based key remapping - Custom app icon support - DMG installer packaging Core Components: - AppDelegate: Application lifecycle and initialization - EventTapManager: Event tap creation and management with proper pointer lifetime - KeyMapper: Key mapping logic and configuration loading - StatusBarController: Menu bar UI and user interactions - Logger: File and console logging with configurable levels - Config: JSON configuration parser with default creation Build System: - build-app.sh: Creates macOS .app bundle with icon - build-dmg.sh: Generates distributable DMG installer - create-icon.sh: Converts PNG to .icns format Documentation: - README.md: User guide and troubleshooting - BUILD.md: Build instructions and packaging - DEBUG.md: Debugging guide with log access 🤖 Generated with [Qoder](https://qoder.com)
This commit is contained in:
95
Sources/AppDelegate.swift
Normal file
95
Sources/AppDelegate.swift
Normal file
@@ -0,0 +1,95 @@
|
||||
@preconcurrency import Cocoa
|
||||
@preconcurrency import ApplicationServices
|
||||
|
||||
@MainActor
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
private var statusBarController: StatusBarController?
|
||||
private var eventTapManager: EventTapManager?
|
||||
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
Logger.setup()
|
||||
|
||||
Logger.info("uskey - macOS Keyboard Remapper")
|
||||
Logger.info("================================")
|
||||
Logger.info("App bundle path: \(Bundle.main.bundlePath)")
|
||||
|
||||
let config = loadConfig()
|
||||
Logger.logLevel = config.log.level
|
||||
|
||||
let keyMapper = KeyMapper(fromConfig: config)
|
||||
keyMapper.printMappings()
|
||||
|
||||
Logger.info("Checking accessibility permissions...")
|
||||
if !checkAccessibilityPermissions() {
|
||||
Logger.error("Accessibility permissions not granted!")
|
||||
showAccessibilityAlert()
|
||||
return
|
||||
}
|
||||
Logger.info("Accessibility permissions granted")
|
||||
|
||||
eventTapManager = EventTapManager(keyMapper: keyMapper)
|
||||
statusBarController = StatusBarController(eventTapManager: eventTapManager!, config: config)
|
||||
|
||||
statusBarController?.setupStatusBar()
|
||||
|
||||
Logger.info("Attempting to start event monitoring...")
|
||||
if eventTapManager!.start() {
|
||||
Logger.info("Event monitoring started successfully")
|
||||
} else {
|
||||
Logger.error("Failed to start event monitoring - check if app has accessibility permissions")
|
||||
}
|
||||
|
||||
Logger.info("Application ready")
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ notification: Notification) {
|
||||
eventTapManager?.stop()
|
||||
Logger.info("Application terminated")
|
||||
Logger.cleanup()
|
||||
}
|
||||
|
||||
private func loadConfig() -> Config {
|
||||
let configPath = Config.getConfigPath()
|
||||
Logger.info("Loading config from: \(configPath)")
|
||||
|
||||
do {
|
||||
let config = try Config.load(from: configPath)
|
||||
Logger.info("Configuration loaded successfully")
|
||||
return config
|
||||
} catch {
|
||||
Logger.warning("Failed to load config: \(error.localizedDescription)")
|
||||
Logger.info("Creating default configuration...")
|
||||
|
||||
do {
|
||||
try Config.createDefault(at: configPath)
|
||||
Logger.info("Default configuration created at: \(configPath)")
|
||||
return try Config.load(from: configPath)
|
||||
} catch {
|
||||
Logger.error("Failed to create default config: \(error.localizedDescription)")
|
||||
Logger.warning("Using fallback configuration")
|
||||
return Config()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func checkAccessibilityPermissions() -> Bool {
|
||||
let trusted = AXIsProcessTrusted()
|
||||
Logger.debug("AXIsProcessTrusted result: \(trusted)")
|
||||
|
||||
if !trusted {
|
||||
Logger.info("Requesting accessibility permissions...")
|
||||
let options = [kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true] as CFDictionary
|
||||
AXIsProcessTrustedWithOptions(options)
|
||||
}
|
||||
return trusted
|
||||
}
|
||||
|
||||
private func showAccessibilityAlert() {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Accessibility Permissions Required"
|
||||
alert.informativeText = "uskey needs accessibility permissions to remap keyboard keys.\n\nPlease grant permissions in:\nSystem Preferences > Privacy & Security > Accessibility\n\nAfter granting permissions, restart the app."
|
||||
alert.alertStyle = .warning
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.runModal()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user