Skip to content

Commit cd2ef05

Browse files
committed
refactor(ipc): 模式切换响应统一走 StatusUpdate, 删除 ModeChanged 协议
把 lshift/SystemModeSwitch/LangBar OnClick 三条同步操作的响应类型统一 为 StatusUpdate (自包含完整状态 + iconLabel), 删除半成品 ModeChanged。 理由: ModeChanged 只携带 chineseMode 一个 bit, 任务栏图标 / fullWidth / punct 等字段必须靠后续 STATE_PUSH 补齐, 中间态导致 "模式对了图标错了" 的视觉不一致; StatusUpdate 一发到位, 响应路径自包含。 C++ 侧统一改造: - KeyEventSink::ProcessResponse 删 case ResponseType::ModeChanged - LangBarItemButton::OnClick 改用 StatusUpdate 回应 + UpdateFullStatus - LangBarItemButton::_MsgWndProc WM_UPDATE_STATUS 改走 TextService:: UpdateFullStatus (而非本类版本), 顺带同步 _SetConversionMode + _SetOpenCloseCompartment, 修复 push pipe 路径下 TSF compartment drift - TextService::OnChange / HandleCtrlSpaceToggle 三处 ResponseType:: ModeChanged 改为 StatusUpdate - IPCClient.cpp 删 CMD_MODE_CHANGED decode 分支 - BinaryProtocol.h 删 CMD_MODE_CHANGED + ResponseType::ModeChanged enum Go 侧统一改造: - MessageHandler 接口 HandleToggleMode/HandleSystemModeSwitch 改返回 (*StatusUpdateData, commitText string) - coordinator.HandleSystemModeSwitch / HandleToggleMode 同步签名 - server_handler 两个 handler 走 encodeStatusUpdate; CommitOnSwitch 边路仍走 CommitText 携带 ModeChanged bit, 状态由 push pipe 兜底 - 删 ResponseTypeModeChanged / CmdModeChanged / EncodeModeChanged 保留: STATUS_MODE_CHANGED (push payload flag) / COMMIT_FLAG_MODE_CHANGED (CommitText payload flag) / KeyEventResult.ModeChanged (InsertText combo 标志), 这些是不同语义的位标志。
1 parent 80309cd commit cd2ef05

11 files changed

Lines changed: 93 additions & 85 deletions

File tree

wind_input/internal/bridge/deferred_handler.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,11 @@ func (d *DeferredHandler) HandleIMEActivated(processID uint32) *StatusUpdateData
116116
return d.loadingStatus()
117117
}
118118

119-
func (d *DeferredHandler) HandleToggleMode() (commitText string, chineseMode bool) {
119+
func (d *DeferredHandler) HandleToggleMode() (status *StatusUpdateData, commitText string) {
120120
if h := d.getHandler(); h != nil {
121121
return h.HandleToggleMode()
122122
}
123-
return "", false
123+
return d.loadingStatus(), ""
124124
}
125125

126126
func (d *DeferredHandler) HandleCapsLockState(on bool) {
@@ -155,11 +155,11 @@ func (d *DeferredHandler) HandleModeNotify(data ModeNotifyData) {
155155
}
156156
}
157157

158-
func (d *DeferredHandler) HandleSystemModeSwitch(chineseMode bool) (commitText string) {
158+
func (d *DeferredHandler) HandleSystemModeSwitch(chineseMode bool) (status *StatusUpdateData, commitText string) {
159159
if h := d.getHandler(); h != nil {
160160
return h.HandleSystemModeSwitch(chineseMode)
161161
}
162-
return ""
162+
return d.loadingStatus(), ""
163163
}
164164

165165
func (d *DeferredHandler) HandleShowContextMenu(screenX, screenY int) {

wind_input/internal/bridge/protocol.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ const (
1010
ResponseTypeClearComposition ResponseType = "clear_composition"
1111
ResponseTypeAck ResponseType = "ack"
1212
ResponseTypePassThrough ResponseType = "pass_through" // Key not handled, pass to system
13-
ResponseTypeModeChanged ResponseType = "mode_changed"
1413
ResponseTypeStatusUpdate ResponseType = "status_update"
1514
ResponseTypeConsumed ResponseType = "consumed"
1615
ResponseTypeInsertTextWithCursor ResponseType = "insert_text_with_cursor" // 插入文本并定位光标
@@ -71,7 +70,7 @@ type KeyEventResult struct {
7170
Type ResponseType
7271
Text string // For InsertText
7372
CaretPos int // For UpdateComposition
74-
ChineseMode bool // For ModeChanged
73+
ChineseMode bool // New mode (used with InsertText + ModeChanged combo)
7574
ModeChanged bool // Whether mode was also changed (for InsertText + mode change combo)
7675
NewComposition string // New composition text after commit (inline preedit: actual text; non-inline: empty)
7776
HasNewComposition bool // Whether to restart composition after commit (set for both inline and non-inline when there is remaining input)
@@ -118,16 +117,22 @@ type MessageHandler interface {
118117
HandleFocusGained(processID uint32) *StatusUpdateData
119118
HandleIMEDeactivated()
120119
HandleIMEActivated(processID uint32) *StatusUpdateData
121-
HandleToggleMode() (commitText string, chineseMode bool)
120+
// HandleToggleMode toggles the input mode. Returns the resulting full status
121+
// (含 iconLabel) so the response can be self-contained. commitText carries
122+
// pending input when CommitOnSwitch is enabled and we switch out of Chinese.
123+
HandleToggleMode() (status *StatusUpdateData, commitText string)
122124
HandleCapsLockState(on bool)
123125
HandleMenuCommand(command string) *StatusUpdateData
124126
HandleClientDisconnected(activeClients int)
125127
// Barrier mechanism for async commit
126128
HandleCommitRequest(data CommitRequestData) *CommitResultData
127129
// Mode notification from TSF (local toggle)
128130
HandleModeNotify(data ModeNotifyData)
129-
// System mode switch (Ctrl+Space): system has decided the target mode, must follow
130-
HandleSystemModeSwitch(chineseMode bool) (commitText string)
131+
// HandleSystemModeSwitch handles a TSF-driven mode switch where the system
132+
// has *already decided* the target mode (e.g. Ctrl+Space). Go must follow,
133+
// not toggle. Returns the resulting full status; commitText set when
134+
// CommitOnSwitch fires.
135+
HandleSystemModeSwitch(chineseMode bool) (status *StatusUpdateData, commitText string)
131136
// Context menu request from TSF (screen coordinates)
132137
HandleShowContextMenu(screenX, screenY int)
133138
// Selection changed outside of composition (from ITfTextEditSink::OnEndEdit)

wind_input/internal/bridge/server_handler.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -197,10 +197,6 @@ func (s *Server) handleKeyEvent(payload []byte, clientID int) []byte {
197197
case ResponseTypeClearComposition:
198198
return s.codec.EncodeClearComposition()
199199

200-
case ResponseTypeModeChanged:
201-
s.logger.Debug("Returning ModeChanged response", "clientID", clientID, "chineseMode", result.ChineseMode)
202-
return s.codec.EncodeModeChanged(result.ChineseMode)
203-
204200
case ResponseTypeStatusUpdate:
205201
// 模式切换走这条:自包含 iconLabel,C++ 端 StatusUpdate handler 立刻
206202
// UpdateFullStatus → 刷新任务栏图标,不依赖 push pipe。
@@ -349,40 +345,53 @@ func (s *Server) handleCommitRequest(payload []byte, clientID int) []byte {
349345
func (s *Server) handleToggleMode(clientID int) []byte {
350346
s.logger.Info("Toggle mode request from UI", "clientID", clientID)
351347

352-
// Call handler to toggle mode
353-
commitText, chineseMode := s.handler.HandleToggleMode()
348+
// 统一架构:Go 决定最终状态后以 StatusUpdate 回应,C++ 端走 UpdateFullStatus
349+
// 一并同步内部 mirror + TSF compartments + LangBar UI。
350+
status, commitText := s.handler.HandleToggleMode()
354351

355352
s.logger.Debug("Toggle mode result", "clientID", clientID,
356-
"chineseMode", chineseMode, "commitText", commitText)
353+
"hasCommit", commitText != "", "hasStatus", status != nil)
357354

358-
// Return ModeChanged response (with optional commit text if there was pending input)
355+
// commitText 路径仍走 CommitText(带 ModeChanged bit);后续 push pipe 会推送
356+
// 完整 status,C++ 端 LangBar 一致性由 push 路径保障。
359357
if commitText != "" {
358+
chineseMode := false
359+
if status != nil {
360+
chineseMode = status.ChineseMode
361+
}
360362
return s.codec.EncodeCommitText(commitText, "", true, chineseMode, false)
361363
}
362-
return s.codec.EncodeModeChanged(chineseMode)
364+
if status == nil {
365+
return s.codec.EncodeAck()
366+
}
367+
return s.encodeStatusUpdate(status)
363368
}
364369

365370
func (s *Server) handleSystemModeSwitch(payload []byte, clientID int) []byte {
366371
if len(payload) < 4 {
367372
s.logger.Error("System mode switch payload too short", "clientID", clientID)
368-
return s.codec.EncodeModeChanged(false)
373+
return s.codec.EncodeAck()
369374
}
370375

371-
// Parse flags (same format as StatusFlags)
376+
// Parse flags (same format as StatusFlags). 注意:这是系统已经决定好的目标模式,
377+
// Go 必须 follow 而非 toggle。
372378
flags := binary.LittleEndian.Uint32(payload[0:4])
373379
chineseMode := (flags & ipc.StatusChineseMode) != 0
374380

375381
s.logger.Info("System mode switch", "clientID", clientID, "targetMode", chineseMode)
376382

377-
commitText := s.handler.HandleSystemModeSwitch(chineseMode)
383+
status, commitText := s.handler.HandleSystemModeSwitch(chineseMode)
378384

379385
s.logger.Debug("System mode switch result", "clientID", clientID,
380-
"chineseMode", chineseMode, "commitText", commitText)
386+
"chineseMode", chineseMode, "hasCommit", commitText != "")
381387

382388
if commitText != "" {
383389
return s.codec.EncodeCommitText(commitText, "", true, chineseMode, false)
384390
}
385-
return s.codec.EncodeModeChanged(chineseMode)
391+
if status == nil {
392+
return s.codec.EncodeAck()
393+
}
394+
return s.encodeStatusUpdate(status)
386395
}
387396

388397
func (s *Server) handleMenuCommand(payload []byte, clientID int) []byte {

wind_input/internal/coordinator/handle_mode.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,15 @@ func (c *Coordinator) HandleModeNotify(data bridge.ModeNotifyData) {
4242
// HandleSystemModeSwitch handles system-initiated mode switch (e.g., Ctrl+Space).
4343
// Unlike HandleToggleMode, the target mode is decided by the system — Go must follow.
4444
// Returns commitText if CommitOnSwitch is enabled and there's pending input.
45-
func (c *Coordinator) HandleSystemModeSwitch(chineseMode bool) (commitText string) {
45+
func (c *Coordinator) HandleSystemModeSwitch(chineseMode bool) (status *bridge.StatusUpdateData, commitText string) {
4646
c.mu.Lock()
4747
defer c.mu.Unlock()
4848

49-
// If mode is already the same, nothing to do
49+
// If mode is already the same, nothing to do — still return current full status
50+
// so caller can echo it back to C++ for any drift recovery.
5051
if c.chineseMode == chineseMode {
5152
c.logger.Debug("System mode switch: already in target mode", "chineseMode", chineseMode)
52-
return ""
53+
return c.buildStatusUpdate(), ""
5354
}
5455

5556
// 切换模式 = 短语终止符,通知造词策略(码表自动造词)
@@ -90,11 +91,11 @@ func (c *Coordinator) HandleSystemModeSwitch(chineseMode bool) (commitText strin
9091
// Broadcast state to toolbar and all TSF clients
9192
c.broadcastState()
9293

93-
return commitText
94+
return c.buildStatusUpdate(), commitText
9495
}
9596

96-
// HandleToggleMode toggles the input mode and returns the new state
97-
func (c *Coordinator) HandleToggleMode() (commitText string, chineseMode bool) {
97+
// HandleToggleMode toggles the input mode and returns the resulting full status.
98+
func (c *Coordinator) HandleToggleMode() (status *bridge.StatusUpdateData, commitText string) {
9899
c.mu.Lock()
99100
defer c.mu.Unlock()
100101

@@ -137,7 +138,7 @@ func (c *Coordinator) HandleToggleMode() (commitText string, chineseMode bool) {
137138
// Broadcast state to toolbar and all TSF clients
138139
c.broadcastState()
139140

140-
return commitText, c.chineseMode
141+
return c.buildStatusUpdate(), commitText
141142
}
142143

143144
// HandleCapsLockState shows Caps Lock indicator (A/a) and updates toolbar

wind_input/internal/ipc/binary_codec.go

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -326,27 +326,6 @@ func (c *BinaryCodec) EncodeUpdateComposition(text string, caretPos int) []byte
326326
return result
327327
}
328328

329-
// EncodeModeChanged encodes a mode changed response
330-
// Format: StatusFlags (4 bytes)
331-
func (c *BinaryCodec) EncodeModeChanged(chineseMode bool) []byte {
332-
var flags uint32
333-
if chineseMode {
334-
flags |= StatusChineseMode
335-
}
336-
flags |= StatusModeChanged
337-
338-
header := c.EncodeHeader(CmdModeChanged, 4)
339-
340-
payload := make([]byte, 4)
341-
binary.LittleEndian.PutUint32(payload[0:4], flags)
342-
343-
result := make([]byte, 0, HeaderSize+4)
344-
result = append(result, header...)
345-
result = append(result, payload...)
346-
347-
return result
348-
}
349-
350329
// EncodeStatusUpdate encodes a full status update response
351330
// Format: StatusHeader (12 bytes) + keyHash values + trailing UTF-8 icon label
352331
func (c *BinaryCodec) EncodeStatusUpdate(chineseMode, fullWidth, chinesePunct, toolbarVisible, capsLock bool,

wind_input/internal/ipc/binary_protocol.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ const (
4343
CmdUpdateComposition uint16 = 0x0102 // Update composition (preedit)
4444
CmdClearComposition uint16 = 0x0103 // Clear composition
4545
CmdCommitResult uint16 = 0x0105 // Commit result (response to COMMIT_REQUEST)
46-
CmdModeChanged uint16 = 0x0201 // Mode changed
4746
CmdStatusUpdate uint16 = 0x0202 // Full status update
4847
CmdStatePush uint16 = 0x0206 // State push (broadcast to all clients)
4948
CmdServiceReady uint16 = 0x0207 // Go service connected push pipe, TSF should sync state

wind_tsf/include/BinaryProtocol.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ constexpr uint16_t CMD_COMMIT_TEXT = 0x0101; // Commit text
4646
constexpr uint16_t CMD_UPDATE_COMPOSITION = 0x0102; // Update composition
4747
constexpr uint16_t CMD_CLEAR_COMPOSITION = 0x0103; // Clear composition
4848
constexpr uint16_t CMD_COMMIT_RESULT = 0x0105; // Commit result (response to COMMIT_REQUEST)
49-
constexpr uint16_t CMD_MODE_CHANGED = 0x0201; // Mode changed
49+
// 0x0201 (CMD_MODE_CHANGED) removed: 所有模式切换响应统一走 CMD_STATUS_UPDATE
5050
constexpr uint16_t CMD_STATUS_UPDATE = 0x0202; // Full status update
5151
constexpr uint16_t CMD_STATE_PUSH = 0x0206; // State push (broadcast to all clients)
5252
constexpr uint16_t CMD_SERVICE_READY = 0x0207; // Go service connected push pipe, TSF should sync state
@@ -372,7 +372,6 @@ enum class ResponseType
372372
CommitText,
373373
UpdateComposition,
374374
ClearComposition,
375-
ModeChanged,
376375
StatusUpdate,
377376
SyncHotkeys,
378377
Consumed,
@@ -398,7 +397,7 @@ struct ParsedResponse
398397
int caretPos = 0;
399398
int cursorOffset = 0; // For InsertTextWithCursor: chars to move left from end
400399

401-
// For StatusUpdate / ModeChanged
400+
// For StatusUpdate
402401
uint32_t statusFlags = 0;
403402

404403
// Icon label for taskbar display (from Go service, e.g., "中", "英", "A", "拼", "五")

wind_tsf/include/IPCClient.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ struct ServiceResponse
5252
std::wstring composition;
5353
int caretPos = 0;
5454

55-
// For StatusUpdate / ModeChanged
55+
// For StatusUpdate
5656
uint32_t statusFlags = 0;
5757

5858
// Icon label for taskbar display (from Go service, e.g., "中", "英", "A", "拼", "五")
@@ -149,7 +149,7 @@ class CIPCClient
149149
BOOL SendModeNotify(bool chineseMode, bool clearInput);
150150

151151
// Send toggle mode request (sync, from UI click)
152-
// Go service will toggle mode and return ModeChanged response
152+
// Go service will toggle mode and return StatusUpdate response (full state + iconLabel)
153153
BOOL SendToggleMode(ServiceResponse& response);
154154

155155
// System mode switch (Ctrl+Space): sync request to Go with target mode

wind_tsf/src/IPCClient.cpp

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ BOOL CIPCClient::SendSystemModeSwitch(bool chineseMode, ServiceResponse& respons
811811

812812
_LogInfo(L"Sending system_mode_switch (sync): chineseMode=%d", chineseMode);
813813

814-
// Send sync - wait for response (CommitText or ModeChanged)
814+
// Send sync - wait for response (CommitText or StatusUpdate)
815815
if (!_SendBinaryMessage(CMD_SYSTEM_MODE_SWITCH, &flags, sizeof(flags)))
816816
{
817817
return FALSE;
@@ -834,7 +834,7 @@ BOOL CIPCClient::SendToggleMode(ServiceResponse& response)
834834

835835
_LogInfo(L"Sending toggle_mode (sync)");
836836

837-
// Send sync - wait for ModeChanged response
837+
// Send sync - wait for StatusUpdate response (carries full state + iconLabel)
838838
if (!_SendBinaryMessage(CMD_TOGGLE_MODE, nullptr, 0))
839839
{
840840
return FALSE;
@@ -1029,20 +1029,6 @@ BOOL CIPCClient::_ParseResponse(const IpcHeader& header, const std::vector<uint8
10291029
}
10301030
break;
10311031

1032-
case CMD_MODE_CHANGED:
1033-
{
1034-
response.type = ResponseType::ModeChanged;
1035-
1036-
if (payload.size() >= 4)
1037-
{
1038-
response.statusFlags = *reinterpret_cast<const uint32_t*>(payload.data());
1039-
response.chineseMode = (response.statusFlags & STATUS_CHINESE_MODE) != 0;
1040-
}
1041-
1042-
_LogDebug(L"Response: ModeChanged chineseMode=%d", response.chineseMode);
1043-
}
1044-
break;
1045-
10461032
case CMD_STATUS_UPDATE:
10471033
{
10481034
response.type = ResponseType::StatusUpdate;

wind_tsf/src/LangBarItemButton.cpp

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,9 @@ STDAPI CLangBarItemButton::OnClick(TfLBIClick click, POINT pt, const RECT* prcAr
289289
if (_bKeyboardDisabled)
290290
return S_OK;
291291

292-
// Left click: Toggle mode via Go service (all state changes go through Go)
292+
// Left click: Toggle mode via Go service (all state changes go through Go).
293+
// Go 端以 StatusUpdate 回应(含 iconLabel 完整状态),C++ 端走 UpdateFullStatus
294+
// 一并同步 _bChineseMode/_bFullWidth 等 mirror + TSF compartments + LangBar UI。
293295
if (_pTextService != nullptr)
294296
{
295297
CIPCClient* pIPCClient = _pTextService->GetIPCClient();
@@ -298,10 +300,22 @@ STDAPI CLangBarItemButton::OnClick(TfLBIClick click, POINT pt, const RECT* prcAr
298300
ServiceResponse response;
299301
if (pIPCClient->SendToggleMode(response))
300302
{
301-
// Apply mode change from Go service response
302-
if (response.type == ResponseType::ModeChanged)
303+
if (response.type == ResponseType::StatusUpdate)
303304
{
304-
_pTextService->SetInputMode(response.chineseMode);
305+
_pTextService->UpdateFullStatus(
306+
response.IsChineseMode(),
307+
response.IsFullWidth(),
308+
response.IsChinesePunct(),
309+
response.IsToolbarVisible(),
310+
response.IsCapsLock(),
311+
response.iconLabel.empty() ? nullptr : response.iconLabel.c_str()
312+
);
313+
}
314+
else if (response.type == ResponseType::CommitText && !response.text.empty())
315+
{
316+
// CommitOnSwitch 边路: 先把 pending 输入 commit,状态由随后到达的 push pipe 同步
317+
_pTextService->CommitText(response.text);
318+
_pTextService->SetInputMode(response.IsChineseMode());
305319
}
306320
}
307321
// If IPC fails, don't toggle locally - keep state consistent with Go
@@ -679,10 +693,26 @@ LRESULT CALLBACK CLangBarItemButton::_MsgWndProc(HWND hwnd, UINT msg, WPARAM wPa
679693
if (pThis != nullptr && pData != nullptr)
680694
{
681695
WIND_LOG_DEBUG(L"MsgWndProc: Processing WM_UPDATE_STATUS\n");
682-
// Call UpdateFullStatus on the UI thread (with icon label from Go service)
683-
pThis->UpdateFullStatus(pData->bChineseMode, pData->bFullWidth,
684-
pData->bChinesePunct, pData->bToolbarVisible, pData->bCapsLock,
685-
pData->iconLabel[0] != L'\0' ? pData->iconLabel : nullptr);
696+
// 走 TextService::UpdateFullStatus 而非本类的 UpdateFullStatus —— 前者会
697+
// 顺带 _SetOpenCloseCompartment + _SetConversionMode 同步 TSF 全局 compartments,
698+
// 否则 push pipe 推过来的状态变更会让 _bChineseMode 与 TSF 系统 compartment
699+
// 出现 drift, 表现为 Ctrl+Space 失效 / 任务栏图标不刷新。
700+
// TextService::UpdateFullStatus 内部会 cascade 调本类的 UpdateFullStatus,
701+
// 因此 LangBar UI 仍然会被刷新。
702+
if (pThis->_pTextService != nullptr)
703+
{
704+
pThis->_pTextService->UpdateFullStatus(
705+
pData->bChineseMode, pData->bFullWidth,
706+
pData->bChinesePunct, pData->bToolbarVisible, pData->bCapsLock,
707+
pData->iconLabel[0] != L'\0' ? pData->iconLabel : nullptr);
708+
}
709+
else
710+
{
711+
// Fallback: 没有 TextService 时只刷新 LangBar UI
712+
pThis->UpdateFullStatus(pData->bChineseMode, pData->bFullWidth,
713+
pData->bChinesePunct, pData->bToolbarVisible, pData->bCapsLock,
714+
pData->iconLabel[0] != L'\0' ? pData->iconLabel : nullptr);
715+
}
686716
}
687717

688718
// Free the data allocated by sender

0 commit comments

Comments
 (0)