-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTSFManager.cs
More file actions
170 lines (147 loc) · 6.81 KB
/
TSFManager.cs
File metadata and controls
170 lines (147 loc) · 6.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
using System;
using System.Collections.Generic;
using Hacknet;
using ImeSharp;
using Microsoft.Xna.Framework;
using SDL2;
namespace KernelFix
{
/// <summary>
/// 管理 ImeSharp 的 TSF 初始化、回调,并提供候选词数据给自绘模块。
/// </summary>
public static class TSFManager
{
/// <summary>候选词列表</summary>
public static List<string> Candidates = new List<string>();
/// <summary>当前高亮候选词索引</summary>
public static int CandidateSelection = 0;
/// <summary>是否已成功初始化</summary>
public static bool Initialized { get; private set; } = false;
// 在 TSFManager 类内部添加一个静态字段,用于暂存未配对的高代理项
private static char? highSurrogateBuffer = null;
private static IntPtr sdlWindowHandle = IntPtr.Zero;
/// <summary>
/// 初始化 TSF。传入 SDL 窗口句柄,内部提取原生 HWND 并调用 ImeSharp。
/// </summary>
/// <param name="windowHandle">SDL 窗口句柄(Game1.Window.Handle)</param>
public static void Initialize(IntPtr windowHandle)
{
if (Initialized || windowHandle == IntPtr.Zero) return;
sdlWindowHandle = windowHandle;
// 获取原生 Win32 窗口句柄(TSF 必需)
var wmInfo = new SDL.SDL_SysWMinfo();
wmInfo.version.major = 2; // SDL2-CS 要求设置版本,否则调用失败
wmInfo.version.minor = 0;
if (SDL.SDL_GetWindowWMInfo(sdlWindowHandle, ref wmInfo) == SDL.SDL_bool.SDL_FALSE)
{
Console.WriteLine("[TSFManager] SDL_GetWindowWMInfo failed!");
return;
}
IntPtr nativeHwnd = wmInfo.info.win.window;
if (nativeHwnd == IntPtr.Zero)
{
Console.WriteLine("[TSFManager] Native HWND is null!");
return;
}
try
{
// 让窗口获得焦点,确保 TSF 可关联
SDL.SDL_RaiseWindow(sdlWindowHandle);
SDL.SDL_SetWindowInputFocus(sdlWindowHandle);
System.Threading.Thread.Sleep(100); // 给系统一点反应时间
InputMethod.TextInputCallback = OnTextInput;
InputMethod.TextCompositionCallback = OnTextComposition;
// showOSImeWindow: false 阻止系统自绘候选窗(由我们自己画)
InputMethod.Initialize(nativeHwnd, showOSImeWindow: false);
InputMethod.Enabled = true;
Initialized = true;
Console.WriteLine("[TSFManager] ImeSharp TSF initialized successfully.");
}
catch (Exception ex)
{
Console.WriteLine($"[TSFManager] Initialization failed: {ex.Message}");
}
}
/// <summary>
/// 最终文字上屏回调。过滤控制字符,并确保代理对被完整组合后再注入。
/// </summary>
private static void OnTextInput(char character)
{
// 忽略控制字符和低 ASCII 控制码
if (char.IsControl(character) || character < 32) return;
// 情况1:当前字符是高代理项(\uD800-\uDBFF)
if (char.IsHighSurrogate(character))
{
// 如果之前已经有一个未配对的高代理,先将其丢弃(或可记录警告)
if (highSurrogateBuffer.HasValue)
{
if (KernelFix.Debug)
Console.WriteLine("[TSFManager] Discarding unpaired high surrogate before new high surrogate.");
}
highSurrogateBuffer = character;
return; // 等待低代理项到来
}
// 情况2:当前字符是低代理项(\uDC00-\uDFFF)
if (char.IsLowSurrogate(character))
{
if (highSurrogateBuffer.HasValue)
{
// 配对成功:组合成完整码点并注入
string completed = new string(new[] { highSurrogateBuffer.Value, character });
highSurrogateBuffer = null;
IMEManager.InjectText(completed);
}
else
{
// 孤立的低代理:忽略它(或注入替换字符 \uFFFD)
if (KernelFix.Debug)
Console.WriteLine("[TSFManager] Orphan low surrogate ignored.");
}
return;
}
// 情况3:普通 BMP 字符(U+0000..U+D7FF 或 U+E000..U+FFFF)
// 如果存在未配对的高代理,先丢弃它(意味着之前的高代理是孤儿的)
if (highSurrogateBuffer.HasValue)
{
if (KernelFix.Debug)
Console.WriteLine("[TSFManager] Discarding unpaired high surrogate before BMP character.");
highSurrogateBuffer = null;
}
IMEManager.InjectText(character.ToString());
}
/// <summary>
/// 组合文本更新及候选词列表刷新回调。
/// </summary>
private static void OnTextComposition(string composition, int cursorPos)
{
if (KernelFix.Debug)
Console.WriteLine($"[TSFManager] Composition: \"{composition}\", cursor={cursorPos}");
IMEManager.CompositionString = composition;
// 更新候选词列表
Candidates.Clear();
CandidateSelection = InputMethod.CandidateSelection;
for (int i = 0; i < InputMethod.CandidatePageSize; i++)
{
string cand = InputMethod.CandidateList[i].ToString();
if (!string.IsNullOrEmpty(cand))
Candidates.Add(cand);
if (KernelFix.Debug)
Console.WriteLine($"[TSFManager] Candidate[{i}] = \"{cand}\"");
}
if (KernelFix.Debug)
Console.WriteLine($"[TSFManager] Candidate count: {Candidates.Count}, selection: {CandidateSelection}");
}
/// <summary>
/// 每帧更新系统输入法的文本输入矩形(即使候选窗隐藏,也需要正确设置以保证光标对齐)。
/// </summary>
public static void UpdateTextInputRect(Rectangle terminalBounds)
{
if (!Initialized || sdlWindowHandle == IntPtr.Zero) return;
// 将终端内坐标转换为屏幕坐标
SDL.SDL_GetWindowPosition(sdlWindowHandle, out int wx, out int wy);
int x = wx + terminalBounds.X + 3;
int y = wy + terminalBounds.Y + terminalBounds.Height - 30;
InputMethod.SetTextInputRect(x, y, 200, 20);
}
}
}