|
1 | 1 | package com.cleanroommc.client.ime; |
2 | 2 |
|
| 3 | +import ca.weblite.objc.Runtime; |
| 4 | +import ca.weblite.objc.RuntimeUtils; |
| 5 | +import com.sun.jna.Callback; |
| 6 | +import com.sun.jna.Library; |
| 7 | +import com.sun.jna.Native; |
3 | 8 | import com.sun.jna.Pointer; |
4 | 9 |
|
5 | 10 | import java.util.function.Consumer; |
6 | 11 |
|
| 12 | +/** |
| 13 | + * Copied from <a href="https://github.com/reserveword/IMBlocker/pull/38">IMBlocker</a> |
| 14 | + */ |
7 | 15 | public class CocoaIMEHandler implements Consumer<Boolean> { |
| 16 | + private static boolean active = false; |
| 17 | + static private final Pointer viewClass = Runtime.INSTANCE.objc_getClass("GLFWContentView"); |
| 18 | + static private Pointer view = null; |
| 19 | + static private final InterpretKeyEventsCallback Imp; |
| 20 | + static private final InterpretKeyEventsCallback NewImp; |
8 | 21 |
|
| 22 | + |
| 23 | + static { |
| 24 | + // see https://github.com/glfw/glfw/blob/b4c3ef9d0fdf46845f3e81e5d989dab06e71e6c1/src/cocoa_window.m#L571 |
| 25 | + // Replacing the method dynamically to determine whether to send text based on state |
| 26 | + // see reference for objc_runtime's dynamic manipulation at https://developer.apple.com/documentation/objectivec/objective-c_runtime |
| 27 | + var selector = RuntimeUtils.sel("interpretKeyEvents:"); |
| 28 | + var method = Runtime.INSTANCE.class_getInstanceMethod(viewClass, selector); |
| 29 | + Imp = ObjC.INSTANCE.method_getImplementation(method); |
| 30 | + NewImp = (self, sel, eventArray) -> { |
| 31 | + if (view == null) view = self; |
| 32 | + if (!active) { |
| 33 | + var textInputContext = RuntimeUtils.cls("NSTextInputContext"); |
| 34 | + var current = RuntimeUtils.msgPointer(textInputContext, "currentInputContext"); |
| 35 | + RuntimeUtils.msg(current, "discardMarkedText"); |
| 36 | + return; |
| 37 | + } |
| 38 | + Imp.invoke(self, sel, eventArray); |
| 39 | + }; |
| 40 | + ObjC.INSTANCE.class_replaceMethod(viewClass, selector, NewImp, "v@:@"); |
| 41 | + } |
| 42 | + /** |
| 43 | + * The underlying native type is IMP, which should be a function pointer to the implementation of interpretKeyEvents: |
| 44 | + * @see <a href="https://developer.apple.com/documentation/objectivec/objective-c_runtime/imp">Documentation for IMP</a> |
| 45 | + * @see <a href="https://developer.apple.com/documentation/appkit/nsresponder/1531599-interpretkeyevents?language=objc">Documentation for interpretKeyEvents:</a> |
| 46 | + */ |
| 47 | + private interface InterpretKeyEventsCallback extends Callback { |
| 48 | + /** |
| 49 | + * @param self "this" pointer for NSObject |
| 50 | + * @param selector selector for interpretKeyEvents: |
| 51 | + * @param eventArray an array of NSEvent objects |
| 52 | + */ |
| 53 | + void invoke(Pointer self, Pointer selector, Pointer eventArray); |
| 54 | + } |
| 55 | + |
| 56 | + /** |
| 57 | + * @see <a href="https://developer.apple.com/documentation/objectivec/objective-c_runtime">Apple Developer Documentation for objc_runtime:</a> |
| 58 | + */ |
| 59 | + private interface ObjC extends Library { |
| 60 | + ObjC INSTANCE = Native.load("objc.A", ObjC.class); |
| 61 | + |
| 62 | + void class_replaceMethod(Pointer cls, Pointer selector, InterpretKeyEventsCallback imp, String types); |
| 63 | + |
| 64 | + InterpretKeyEventsCallback method_getImplementation(Pointer selector); |
| 65 | + } |
9 | 66 | @Override |
10 | 67 | public void accept(Boolean aBoolean) { |
11 | | - |
| 68 | + if (active != aBoolean) { |
| 69 | + active = aBoolean; |
| 70 | + } |
12 | 71 | } |
13 | 72 | } |
0 commit comments