Skip to content

Commit

Permalink
feat: 添加 CLI 功能
Browse files Browse the repository at this point in the history
  • Loading branch information
qwertyyb committed Sep 10, 2024
1 parent beda5d6 commit bae9080
Show file tree
Hide file tree
Showing 11 changed files with 432 additions and 1 deletion.
192 changes: 191 additions & 1 deletion Fire.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions Fire/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
var fire: Fire!
var statistics: Statistics!
var statusBar: StatusBar!
var mainAppService: MainAppServer!

func installInputSource() {
print("install input source")
Expand Down Expand Up @@ -49,6 +50,20 @@ class AppDelegate: NSObject, NSApplicationDelegate {
stop()
return false
}
if command == "--get-mode" {
connectToMainApp { mainApp in
mainApp.getMode { mode in
print(mode)
}
}
return false
}
if command == "--set-mode" {
connectToMainApp { mainApp in
mainApp.setMode(inputMode: CommandLine.arguments[2], showTip: CommandLine.arguments[3] == "true")
}
return false
}
}
return true
}
Expand All @@ -65,6 +80,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
fire = Fire.shared
statistics = Statistics.shared
statusBar = StatusBar.shared
mainAppService = MainAppServer.shared
}

func applicationWillTerminate(_ aNotification: Notification) {
Expand Down
24 changes: 24 additions & 0 deletions Fire/XPC/FireXPCClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// FireXPCClient.swift
// Fire
//
// Created by 杨永榜 on 2024/9/10.
//

import Foundation
import FireXPCService

func connectToMainApp(callback: @escaping (MainAppXPCServiceProtocol) -> Void) {
let connection = NSXPCConnection(serviceName: "com.qwertyyb.inputmethod.Fire.FireXPCService")
connection.remoteObjectInterface = NSXPCInterface(with: FireXPCServiceProtocol.self)
connection.resume()
guard let connectProxy = connection.remoteObjectProxy as? FireXPCServiceProtocol else { return }

connectProxy.getMainAppEndpoint { endpoint in
let serverConnection = NSXPCConnection(listenerEndpoint: endpoint)
serverConnection.remoteObjectInterface = NSXPCInterface(with: MainAppXPCServiceProtocol.self)
serverConnection.resume()
guard let mainApp = serverConnection.remoteObjectProxy as? MainAppXPCServiceProtocol else { return }
callback(mainApp)
}
}
44 changes: 44 additions & 0 deletions Fire/XPC/MainAppServer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// MainAppService.swift
// Fire
//
// Created by 杨永榜 on 2024/9/10.
//

import Foundation
import FireXPCService

let XPCServiceName = "com.qwertyyb.inputmethod.Fire.FireXPCService"

class MainAppServer: NSObject, NSXPCListenerDelegate {
var proxy: FireXPCServiceProtocol?
let listener: NSXPCListener
let mainAppXPCService = MainAppXPCService()
override init() {
self.listener = NSXPCListener.anonymous()
super.init()
self.listener.delegate = self

let connection = NSXPCConnection(serviceName: XPCServiceName)
connection.remoteObjectInterface = NSXPCInterface(with: FireXPCServiceProtocol.self)
connection.resume()
if let proxy = connection.remoteObjectProxy as? FireXPCServiceProtocol {
self.proxy = proxy
proxy.setMainAppEndpoint(endpoint: self.listener.endpoint) { ret in
NSLog("[MainAppService] setMainAppEndPoint reply: \(ret)")
}
}
}

func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
newConnection.exportedInterface = NSXPCInterface(with: MainAppXPCServiceProtocol.self)

newConnection.exportedObject = mainAppXPCService

newConnection.resume()

return true
}

static let shared = MainAppServer()
}
26 changes: 26 additions & 0 deletions Fire/XPC/MainAppXPCService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// MainAppXPCService.swift
// Fire
//
// Created by 杨永榜 on 2024/9/10.
//

import Foundation
import FireXPCService

public class MainAppXPCService: NSObject, MainAppXPCServiceProtocol {

public func check(with reply: @escaping (Int) -> Void) {
reply(0)
}

public func getMode(with reply: @escaping (String) -> Void) {
reply(Fire.shared.inputMode.rawValue)
}

public func setMode(inputMode: String, showTip: Bool) {
let mode = InputMode(rawValue: inputMode)
Fire.shared.toggleInputMode(mode, showTip: showTip)
}

}
16 changes: 16 additions & 0 deletions Fire/XPC/MainAppXPCServiceProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// MainAppServiceProtocol.swift
// Fire
//
// Created by 杨永榜 on 2024/9/10.
//

import Foundation

/// The protocol that this service will vend as its API. This protocol will also need to be visible to the process hosting the service.
@objc public protocol MainAppXPCServiceProtocol {
func check(with reply: @escaping (Int) -> Void)
func getMode(with reply: @escaping(String) -> Void)
func setMode(inputMode: String, showTip: Bool)
}

8 changes: 8 additions & 0 deletions FireXPCService/FireXPCService.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
</dict>
</plist>
21 changes: 21 additions & 0 deletions FireXPCService/FireXPCService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// FireXPCService.swift
// FireXPCService
//
// Created by 杨永榜 on 2024/9/10.
//

import Foundation

/// This object implements the protocol which we have defined. It provides the actual behavior for the service. It is 'exported' by the service to make it available to the process hosting the service over an NSXPCConnection.
public class FireXPCService: NSObject, FireXPCServiceProtocol {
public func getMainAppEndpoint(with reply: @escaping (NSXPCListenerEndpoint) -> Void) {
reply(self.mainAppEndpoint!)
}

var mainAppEndpoint: NSXPCListenerEndpoint?
public func setMainAppEndpoint(endpoint: NSXPCListenerEndpoint, with reply: @escaping (Int) -> Void) {
self.mainAppEndpoint = endpoint
reply(0)
}
}
35 changes: 35 additions & 0 deletions FireXPCService/FireXPCServiceProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// FireXPCServiceProtocol.swift
// FireXPCService
//
// Created by 杨永榜 on 2024/9/10.
//

import Foundation

/// The protocol that this service will vend as its API. This protocol will also need to be visible to the process hosting the service.
@objc public protocol FireXPCServiceProtocol {
func setMainAppEndpoint(endpoint: NSXPCListenerEndpoint, with reply: @escaping (Int) -> Void)

func getMainAppEndpoint(with reply: @escaping (NSXPCListenerEndpoint) -> Void)
}

/*
To use the service from an application or other process, use NSXPCConnection to establish a connection to the service by doing something like this:

connectionToService = NSXPCConnection(serviceName: "com.qwertyyb.inputmethod.Fire.FireXPCService")
connectionToService.remoteObjectInterface = NSXPCInterface(with: FireXPCServiceProtocol.self)
connectionToService.resume()

Once you have a connection to the service, you can use it like this:

if let proxy = connectionToService.remoteObjectProxy as? FireXPCServiceProtocol {
proxy.performCalculation(firstNumber: 23, secondNumber: 19) { result in
NSLog("Result of calculation is: \(result)")
}
}

And, when you are finished with the service, clean up the connection like this:

connectionToService.invalidate()
*/
11 changes: 11 additions & 0 deletions FireXPCService/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>XPCService</key>
<dict>
<key>ServiceType</key>
<string>Application</string>
</dict>
</dict>
</plist>
40 changes: 40 additions & 0 deletions FireXPCService/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// main.swift
// FireXPCService
//
// Created by 杨永榜 on 2024/9/10.
//

import Foundation

class ServiceDelegate: NSObject, NSXPCListenerDelegate {

var fireXPCService: FireXPCService = FireXPCService()

/// This method is where the NSXPCListener configures, accepts, and resumes a new incoming NSXPCConnection.
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {

// Configure the connection.
// First, set the interface that the exported object implements.
newConnection.exportedInterface = NSXPCInterface(with: FireXPCServiceProtocol.self)

// Next, set the object that the connection exports. All messages sent on the connection to this service will be sent to the exported object to handle. The connection retains the exported object.
newConnection.exportedObject = self.fireXPCService

// Resuming the connection allows the system to deliver more incoming messages.
newConnection.resume()

// Returning true from this method tells the system that you have accepted this connection. If you want to reject the connection for some reason, call invalidate() on the connection and return false.
return true
}
}

// Create the delegate for the service.
let delegate = ServiceDelegate()

// Set up the one NSXPCListener for this service. It will handle all incoming connections.
let listener = NSXPCListener.service()
listener.delegate = delegate

// Resuming the serviceListener starts this service. This method does not return.
listener.resume()

0 comments on commit bae9080

Please sign in to comment.