|
| 1 | +using System.Runtime.CompilerServices; |
1 | 2 | using System.Runtime.InteropServices; |
2 | 3 | using ImGuiNET; |
3 | 4 | using StArray.ModManager.Android.Native; |
@@ -67,42 +68,88 @@ public static void InstallInputHooks() |
67 | 68 | IsInitialized = true; |
68 | 69 | } |
69 | 70 |
|
70 | | - /// <summary>在 Android 输入消费完成后读取本次输出事件。</summary> |
71 | | - [NativeHook("libinput.so", "_ZN7android13InputConsumer7consumeEPNS_26InputEventFactoryInterfaceEblPjPPNS_10InputEventE", |
72 | | - Convention = CallingConvention.Cdecl)] |
73 | | - public unsafe static int OnConsume( |
74 | | - void* thiz, |
75 | | - void* factory, |
76 | | - bool consumeBatches, |
77 | | - long frameTime, |
78 | | - uint* outSeq, |
79 | | - void** outEvent) |
| 71 | + /* |
| 72 | + /// <summary>触摸事件 Hook 回调</summary> |
| 73 | + [NativeHook("libinput.so","_ZN7android13InputConsumer14consumeSamplesEPNS_26InputEventFactoryInterfaceERNS0_5BatchEmPjPPNS_10InputEventE")] |
| 74 | + public unsafe static long OnConsumeSamples(void* thiz,void* factory, IntPtr batch, |
| 75 | + ulong count, uint* outSeq, void** outEvent) |
80 | 76 | { |
81 | | - var result = OnConsumeOriginal( |
82 | | - thiz, |
83 | | - factory, |
84 | | - consumeBatches, |
85 | | - frameTime, |
86 | | - outSeq, |
87 | | - outEvent); |
88 | | - if (outEvent != null && *outEvent != null) |
89 | | - DispatchInputEvent(new IntPtr(*outEvent)); |
| 77 | + var result = OnConsumeSamplesOriginal(thiz,factory, batch, count, outSeq, outEvent); |
| 78 | + if (IsInitialized && *outEvent != null) ImGuiImplAndroid.HandleInputEvent(new IntPtr(*outEvent)); |
90 | 79 | return result; |
91 | 80 | } |
92 | | - |
93 | | - /// <summary> |
94 | | - /// 把一个原生输入事件同时送往 ImGui 和 <see cref="InputEvents"/> 订阅方。 |
95 | | - /// </summary> |
96 | | - /// <remarks> |
97 | | - /// 输入广播不依赖 ImGui 上下文是否已经初始化;这样异步输入 Mod 可以在 |
98 | | - /// 菜单、暂停和非游玩阶段继续收到原始事件并自行决定是否消费。 |
99 | | - /// </remarks> |
100 | | - private static void DispatchInputEvent(IntPtr inputEvent) |
| 81 | + |
| 82 | + [NativeHook("libinput.so","_ZN7android13InputConsumer7consumeEPNS_26InputEventFactoryInterfaceEblPjPPNS_10InputEventE")] |
| 83 | + public unsafe static long OnConsume(void* thiz, void* factory, bool consumeBatches, ulong frameTime, uint* outSeq, void** outEvent) |
101 | 84 | { |
| 85 | + var result = OnConsumeOriginal(thiz, factory, consumeBatches, frameTime, outSeq, outEvent); |
| 86 | + if (IsInitialized && *outEvent != null) ImGuiImplAndroid.HandleInputEvent(new IntPtr(*outEvent)); |
| 87 | + return result; |
| 88 | + }*/ |
| 89 | + |
| 90 | + [NativeHook("GetInitializeMotionEventAddress")] |
| 91 | + public unsafe static bool OnInitializeMotionEvent(void* @event, void* message) |
| 92 | + { |
| 93 | + var result = OnInitializeMotionEventOriginal(@event, message); |
| 94 | + var x = AndroidInput.AMotionEvent_getX(new(@event), 0); |
| 95 | + var y = AndroidInput.AMotionEvent_getY(new(@event), 0); |
102 | 96 | if (InputEvents.HasSubscribers) |
103 | | - InputEvents.RaiseFrom(inputEvent); |
104 | | - if (IsInitialized) |
105 | | - ImGuiImplAndroid.HandleInputEvent(inputEvent); |
| 97 | + InputEvents.RaiseFrom(new IntPtr(@event)); |
| 98 | + ImGuiImplAndroid.HandleInputEvent(new IntPtr(@event)); |
| 99 | + return result; |
| 100 | + } |
| 101 | + |
| 102 | + private static nint GetInitializeMotionEventAddress() |
| 103 | + { |
| 104 | + byte?[] sig = NativeFuncResolver.ParseHexPattern( |
| 105 | + "e8 0f 19 fc fd 7b 01 a9 fc 6f 02 a9 fa 67 03 a9 " + |
| 106 | + "f8 5f 04 a9 f6 57 05 a9 f4 4f 06 a9 fd 43 00 91"); |
| 107 | + var r = new NativeFuncResolver("/system/lib64/libinput.so"); |
| 108 | + long rva = r.FindSymbolRva("_ZN7android13InputConsumer21initializeMotionEventEPNS_11MotionEventEPKNS_12InputMessageE"); |
| 109 | + try |
| 110 | + { |
| 111 | + if (rva < 0) |
| 112 | + { |
| 113 | + var text = r.TextBytes; |
| 114 | + long textAddr = r.TextBaseAddress; |
| 115 | + int pos = 0; |
| 116 | + while ((pos = NativeFuncResolver.Search(text, sig, pos)) >= 0) |
| 117 | + { |
| 118 | + int ctxEnd = Math.Min(pos + 60, text.Length); |
| 119 | + bool hasMrs = false, hasLdr = false; |
| 120 | + for (int j = pos + 32; j <= ctxEnd - 4; j += 4) |
| 121 | + { |
| 122 | + int inst = BitConverter.ToInt32(text.AsSpan(j)); |
| 123 | + if (!hasMrs && (inst & 0xffffffe0) == 0xd53bd040) |
| 124 | + hasMrs = true; |
| 125 | + if (!hasLdr && (inst & 0xffe003e0) == 0xb9400020) |
| 126 | + { |
| 127 | + int imm12 = (inst >> 10) & 0xfff; |
| 128 | + if (imm12 * 4 == 0xc) hasLdr = true; |
| 129 | + } |
| 130 | + if (hasMrs && hasLdr) break; |
| 131 | + } |
| 132 | + if (hasMrs && hasLdr) |
| 133 | + { |
| 134 | + rva = textAddr + pos; |
| 135 | + if (pos >= 4) |
| 136 | + { |
| 137 | + int prev = BitConverter.ToInt32(text.AsSpan(pos - 4)); |
| 138 | + if (prev == unchecked((int)0xd503233f)) rva -= 4; |
| 139 | + } |
| 140 | + break; |
| 141 | + } |
| 142 | + pos++; |
| 143 | + } |
| 144 | + if (rva < 0) throw new KeyNotFoundException("initializeMotionEvent not found by signature."); |
| 145 | + } |
| 146 | + } |
| 147 | + catch (Exception ex) |
| 148 | + { |
| 149 | + Logger.Error(nameof(ImGuiInputHandler), ex.ToString()); |
| 150 | + } |
| 151 | + r.Load(); |
| 152 | + return r.GetFuncPtr(rva); |
106 | 153 | } |
107 | 154 |
|
108 | 155 | private static JavaClass? s_utilsClass; |
|
0 commit comments