Cross-platform .NET wrapper for Apple's InputMethodKit (IMK). This library provides a managed API to build macOS input methods in C#/.NET. On non-macOS platforms, it compiles as a safe no-op implementation, allowing cross-platform development.
- IMKServer - Create and manage input method servers
- IMKCandidates - Display candidate windows with customizable appearance
- IMKInputController subclassing - Implement input controllers in pure C# via ObjC runtime class registration
- Cross-platform - Compiles on all platforms; functional on macOS only
- No external dependencies - Uses P/Invoke to ObjC runtime directly
- .NET 10 SDK (Preview)
- macOS 10.14+ for runtime functionality
dotnet add package vChewing.Utils.MacOS.InputMethodKitusing vChewing.Utils.MacOS.InputMethodKit.Api;
// Create server with bundle identifier
var server = IMKServer.Create("MyInputMethod", "com.example.myim");
if (server.IsValid) {
Console.WriteLine("IMKServer created successfully");
}using vChewing.Utils.MacOS.InputMethodKit.Api;
using vChewing.Utils.MacOS.InputMethodKit.Api.Bindings;
// Create candidate window
var candidates = IMKCandidates.Create(server, IMKCandidatePanelType.SingleColumnScrolling);
// Set candidates
candidates.SetCandidateData(new[] { "候選1", "候選2", "候選3" });
// Configure appearance (macOS 10.14+)
candidates.SetFontSize(16.0);
candidates.WindowLevel = 1000;
// Show/hide
candidates.Show(0);
candidates.Hide();using vChewing.Utils.MacOS.InputMethodKit.Api;
public class MyInputController : InputControllerBase {
private string _buffer = "";
public override bool HandleInput(string text, long keyCode, ulong modifiers, IntPtr client) {
if (string.IsNullOrEmpty(text)) return false;
_buffer += text;
// Process input...
return true;
}
public override string? GetComposedString(IntPtr client) {
return _buffer;
}
public override void CommitComposition(IntPtr client) {
_buffer = "";
}
}
// Register with ObjC runtime (macOS only)
#if MACOS
using vChewing.Utils.MacOS.InputMethodKit.Native.MacOS;
// Native AOT compatible factory-based registration (recommended)
var controllerClassName = InputControllerFactory.RegisterController(
() => new MyInputController(),
"MyIMController"
);
// Or use generic version (not AOT compatible, will trigger analyzer warning)
// var controllerClassName = InputControllerFactory.RegisterController<MyInputController>();
var server = IMKServer.CreateWithController("MyIM", controllerClassName);
#endif| Method/Property | Description |
|---|---|
Create(name, bundleId) |
Create server with bundle identifier |
CreateWithController(name, controllerClass) |
Create server with registered controller class |
IsValid |
Whether the server is valid |
BundleIdentifier |
The bundle identifier |
Bundle |
Native NSBundle pointer |
PaletteWillTerminate |
Palette termination flag |
LastKeyEventWasDeadKey |
Dead key detection |
| Method/Property | Description |
|---|---|
Create(server, panelType) |
Create candidate window |
Show(locationHint) |
Show the window |
Hide() |
Hide the window |
IsVisible |
Visibility state |
SetCandidateData(string[]) |
Set candidate strings |
GetCandidates() |
Get current candidates |
SelectedCandidate |
Currently selected index |
SelectCandidateWithIdentifier(id) |
Select by identifier |
PanelType |
Get/set panel type |
DismissesAutomatically |
Auto-dismiss behavior |
WindowLevel |
Window level (10.14+) |
SetFontSize(double) |
Set font size (10.14+) |
HandleKeyboardEvent(event) |
Handle keyboard event (10.14+) |
Override these methods to implement your input logic:
| Method | Description |
|---|---|
HandleInput(text, keyCode, modifiers, client) |
Handle keyboard input |
HandleEvent(eventPtr, client) |
Handle raw NSEvent |
HandleCommand(selector, client) |
Handle command selectors |
GetComposedString(client) |
Return composed string |
GetCandidates(client) |
Return candidate array |
CommitComposition(client) |
Commit the composition |
ActivateServer(sender) |
Called on activation |
DeactivateServer(sender) |
Called on deactivation |
src/vChewing.Utils.MacOS.InputMethodKit/
├── Api/
│ ├── IMKServer.cs # Managed IMKServer wrapper
│ ├── IMKCandidates.cs # Managed IMKCandidates wrapper
│ ├── InputControllerBase.cs # Base class for input controllers
│ ├── InputMethodKitManager.cs # Factory and helpers
│ └── Bindings/
│ ├── IMKServerBindings.cs
│ ├── IMKCandidatesBindings.cs
│ ├── IMKInputControllerBindings.cs
│ ├── IMKServerInputBindings.cs
│ ├── IMKTextInputBindings.cs
│ └── ObjCTypes.cs
└── Native/
├── MacOS/
│ ├── MacIMKBridge.cs # ObjC runtime P/Invoke
│ ├── MacIMKServer.cs # macOS IMKServer impl
│ ├── MacIMKCandidates.cs # macOS IMKCandidates impl
│ ├── MacInputMethodKitManager.cs
│ ├── InputControllerFactory.cs # ObjC class registration
│ └── ObjCRuntimeRegistration.cs # Low-level ObjC runtime
└── Noop/
└── NoopInputMethodKitManager.cs # Cross-platform no-op
dotnet builddotnet testdotnet pack -c Release src/vChewing.Utils.MacOS.InputMethodKit/vChewing.Utils.MacOS.InputMethodKit.csproj -o ./nupkgsThis project is licensed under the MIT License.