Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 66 additions & 3 deletions RimeWithWeasel/RimeWithWeasel.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "stdafx.h"
#include "stdafx.h"
#include <logging.h>
#include <RimeWithWeasel.h>
#include <StringAlgorithm.hpp>
Expand All @@ -24,6 +24,31 @@ typedef enum { COLOR_ABGR = 0, COLOR_ARGB, COLOR_RGBA } ColorFormat;
using namespace weasel;

static RimeApi* rime_api;

namespace {
bool TryGetLangIdFromConfig(RimeConfig* config,
const char* key,
int* commit_langid) {
if (!config || !key || !commit_langid)
return false;

char buffer[LOCALE_NAME_MAX_LENGTH] = {0};
if (!rime_api->config_get_string(config, key, buffer, sizeof(buffer) - 1))
return false;

const auto locale_name = u8tow(buffer);
if (locale_name.empty())
return false;

const LCID lcid =
LocaleNameToLCID(locale_name.c_str(), LOCALE_ALLOW_NEUTRAL_NAMES);
if (!lcid)
return false;

*commit_langid = LANGIDFROMLCID(lcid);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TryGetLangIdFromConfig writes the resulting LANGID into an int without any bounds/type enforcement. Since this value is later serialized over IPC and cast to LANGID, it’s safer to explicitly constrain it to 0..0xFFFF (and treat 0 as “not set”) before storing/sending.

Suggested change
*commit_langid = LANGIDFROMLCID(lcid);
const unsigned long langid = static_cast<unsigned long>(LANGIDFROMLCID(lcid));
if (langid == 0 || langid > 0xFFFFul)
return false;
*commit_langid = static_cast<int>(langid);

Copilot uses AI. Check for mistakes.
return true;
}

WeaselSessionId _GenerateNewWeaselSessionId(SessionStatusMap sm, DWORD pid) {
if (sm.empty())
return (WeaselSessionId)(pid + 1);
Expand All @@ -33,6 +58,7 @@ WeaselSessionId _GenerateNewWeaselSessionId(SessionStatusMap sm, DWORD pid) {
int expand_ibus_modifier(int m) {
return (m & 0xff) | ((m & 0xff00) << 16);
}
} // namespace

RimeWithWeaselHandler::RimeWithWeaselHandler(UI* ui)
: m_ui(ui),
Expand Down Expand Up @@ -195,6 +221,7 @@ DWORD RimeWithWeaselHandler::AddSession(LPWSTR buffer, EatLine eat) {
std::string schema_id = status.schema_id;
m_last_schema_id = schema_id;
_LoadSchemaSpecificSettings(ipc_id, schema_id);
_LoadLanguageOverrideConfig(ipc_id, schema_id);
_LoadAppInlinePreeditSet(ipc_id, true);
_UpdateInlinePreeditStatus(ipc_id);
_RefreshTrayIcon(session_id, _UpdateUICallback);
Expand Down Expand Up @@ -617,6 +644,41 @@ void RimeWithWeaselHandler::_LoadSchemaSpecificSettings(
rime_api->config_close(&config);
}

void RimeWithWeaselHandler::_LoadLanguageOverrideConfig(
WeaselSessionId ipc_id,
const std::string& schema_id) {
SessionStatus& session_status = get_session_status(ipc_id);
session_status.commit_langid = 0;

const auto load_global_fallback = [&]() {
RimeConfig weasel_config = {};
if (!rime_api->config_open("weasel", &weasel_config))
return;

int commit_langid = 0;
if (TryGetLangIdFromConfig(&weasel_config, "commit_locale",
&commit_langid)) {
session_status.commit_langid = commit_langid;
}
rime_api->config_close(&weasel_config);
};

RimeConfig schema_config = {};
if (!schema_id.empty() &&
rime_api->schema_open(schema_id.c_str(), &schema_config)) {
int commit_langid = 0;
if (TryGetLangIdFromConfig(&schema_config, "schema/commit_locale",
&commit_langid)) {
session_status.commit_langid = commit_langid;
} else {
load_global_fallback();
}
rime_api->config_close(&schema_config);
} else {
load_global_fallback();
}
}

void RimeWithWeaselHandler::_LoadAppInlinePreeditSet(WeaselSessionId ipc_id,
bool ignore_app_name) {
SessionStatus& session_status = get_session_status(ipc_id);
Expand Down Expand Up @@ -895,8 +957,8 @@ bool RimeWithWeaselHandler::_Respond(WeaselSessionId ipc_id, EatLine eat) {

// configuration information
actions.push_back("config");
body.append(L"config.inline_preedit=")
.append(std::to_wstring((int)session_status.style.inline_preedit))
body.append(L"config.commit_langid=")
.append(std::to_wstring(session_status.commit_langid))
.append(L"\n");

// style
Expand Down Expand Up @@ -1451,6 +1513,7 @@ void RimeWithWeaselHandler::_GetStatus(Status& stat,
if (schema_id != m_last_schema_id) {
session_status.__synced = false;
m_last_schema_id = schema_id;
_LoadLanguageOverrideConfig(ipc_id, schema_id);
if (schema_id != ".default") { // don't load for schema select menu
bool inline_preedit = session_status.style.inline_preedit;
_LoadSchemaSpecificSettings(ipc_id, schema_id);
Expand Down
7 changes: 3 additions & 4 deletions WeaselIPC/Configurator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ Configurator::~Configurator() {}

void Configurator::Store(Deserializer::KeyType const& key,
std::wstring const& value) {
if (!m_pTarget->p_context || key.size() < 2)
if (!m_pTarget->p_config || key.size() < 2)
return;
bool bool_value = (!value.empty() && value != L"0");
if (key[1] == L"inline_preedit") {
m_pTarget->p_config->inline_preedit = bool_value;
if (key[1] == L"commit_langid") {
m_pTarget->p_config->commit_langid = _wtoi(value.c_str());
}
Comment on lines 15 to 21
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parsing commit_langid with _wtoi provides no error reporting and accepts negative / out-of-range values, which then flow into TSF language property setting. Prefer a checked parse (e.g., wcstol with endptr) and clamp/reject values outside the valid LANGID range to keep IPC robust against malformed inputs.

Copilot uses AI. Check for mistakes.
}
5 changes: 5 additions & 0 deletions WeaselTSF/Composition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ STDAPI CStartCompositionEditSession::DoEditSession(TfEditCookie ec) {
*/
if (!_inlinePreeditEnabled) {
pRangeComposition->SetText(ec, TF_ST_CORRECTION, L" ", 1);
_pTextService->_SetRangeLanguage(ec, _pContext, pRangeComposition);
}

/* set selection */
Expand Down Expand Up @@ -278,6 +279,8 @@ STDAPI CInlinePreeditEditSession::DoEditSession(TfEditCookie ec) {
static_cast<LONG>(preedit.length()))) != S_OK)
return E_FAIL;

_pTextService->_SetRangeLanguage(ec, _pContext, pRangeComposition);

/* TODO: Check the availability and correctness of these values */
int sel_cursor = -1;
for (size_t i = 0; i < _context->preedit.attributes.size(); i++) {
Expand Down Expand Up @@ -354,6 +357,8 @@ STDMETHODIMP CInsertTextEditSession::DoEditSession(TfEditCookie ec) {
static_cast<LONG>(_text.length()))))
return E_FAIL;

_pTextService->_SetRangeLanguage(ec, _pContext, pRange);

/* update the selection to an insertion point just past the inserted text. */
pRange->Collapse(ec, TF_ANCHOR_END);

Expand Down
20 changes: 20 additions & 0 deletions WeaselTSF/DisplayAttribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@ BOOL WeaselTSF::_SetCompositionDisplayAttributes(TfEditCookie ec,
return (hr == S_OK);
}

BOOL WeaselTSF::_SetRangeLanguage(TfEditCookie ec,
_In_ ITfContext* pContext,
ITfRange* pRange) {
if (!pRange || !_textLangId)
return FALSE;

ITfProperty* pLangIdProperty = nullptr;
HRESULT hr = E_FAIL;

if (SUCCEEDED(pContext->GetProperty(GUID_PROP_LANGID, &pLangIdProperty))) {
VARIANT var;
var.vt = VT_I4;
var.lVal = _textLangId;
hr = pLangIdProperty->SetValue(ec, pRange, &var);
pLangIdProperty->Release();
Comment on lines +64 to +69
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VARIANT var is used without VariantInit(). Even though only VT_I4 is assigned, initializing the VARIANT avoids uninitialized padding/fields and aligns with COM best practices (same pattern also exists in _SetCompositionDisplayAttributes).

Copilot uses AI. Check for mistakes.
}

return hr == S_OK;
}

BOOL WeaselTSF::_InitDisplayAttributeGuidAtom() {
ITfCategoryMgr* pCategoryMgr = nullptr;
HRESULT hr =
Expand Down
8 changes: 5 additions & 3 deletions WeaselTSF/EditSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ STDAPI WeaselTSF::DoEditSession(TfEditCookie ec) {
_UpdateLanguageBar(_status);

if (ok) {
bool inline_preedit = _cand->style().inline_preedit;
_textLangId = static_cast<LANGID>(config.commit_langid);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Casting config.commit_langid (int) to LANGID will silently truncate out-of-range values. Please validate/clamp to the valid LANGID range (0..0xFFFF) and treat invalid values as “no override” (or log) to avoid setting an unintended language ID.

Suggested change
_textLangId = static_cast<LANGID>(config.commit_langid);
if (config.commit_langid >= 0 && config.commit_langid <= 0xFFFF) {
_textLangId = static_cast<LANGID>(config.commit_langid);
}

Copilot uses AI. Check for mistakes.
if (!commit.empty()) {
Comment on lines 18 to 21
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_textLangId is stored as a mutable WeaselTSF member, but the actual language assignment happens later in separately requested (potentially async) edit sessions (_StartComposition/_InsertText/_ShowInlinePreedit). If multiple edit sessions are queued, _textLangId can be overwritten before those sessions run, causing the wrong LANGID to be applied to the range. Consider capturing the langid per edit-session instance (store it in the edit session object) or applying the GUID_PROP_LANGID value within the same edit session that sets the text.

Copilot uses AI. Check for mistakes.
// For auto-selecting, commit and preedit can both exist.
// Commit and close the original composition first.
if (!_IsComposing()) {
_StartComposition(_pEditSessionContext,
_fCUASWorkaroundEnabled && !config.inline_preedit);
_fCUASWorkaroundEnabled && !inline_preedit);
}
_InsertText(_pEditSessionContext, commit);
_EndComposition(_pEditSessionContext, false);
Expand All @@ -31,11 +33,11 @@ STDAPI WeaselTSF::DoEditSession(TfEditCookie ec) {
}
if (_status.composing && !_IsComposing()) {
_StartComposition(_pEditSessionContext,
_fCUASWorkaroundEnabled && !config.inline_preedit);
_fCUASWorkaroundEnabled && !inline_preedit);
} else if (!_status.composing && _IsComposing()) {
_EndComposition(_pEditSessionContext, true);
}
if (_IsComposing() && config.inline_preedit) {
if (_IsComposing() && inline_preedit) {
_ShowInlinePreedit(_pEditSessionContext, context);
}
_UpdateCompositionWindow(_pEditSessionContext);
Expand Down
4 changes: 4 additions & 0 deletions WeaselTSF/WeaselTSF.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ class WeaselTSF : public ITfTextInputProcessorEx,
BOOL _SetCompositionDisplayAttributes(TfEditCookie ec,
_In_ ITfContext* pContext,
ITfRange* pRangeComposition);
BOOL _SetRangeLanguage(TfEditCookie ec,
_In_ ITfContext* pContext,
ITfRange* pRange);
BOOL _InitDisplayAttributeGuidAtom();

com_ptr<ITfThreadMgr> _GetThreadMgr() { return _pThreadMgr; }
Expand Down Expand Up @@ -227,6 +230,7 @@ class WeaselTSF : public ITfTextInputProcessorEx,

/* IME status */
weasel::Status _status;
LANGID _textLangId = 0;

// guidatom for the display attibute.
TfGuidAtom _gaDisplayAttributeInput;
Expand Down
9 changes: 8 additions & 1 deletion include/RimeWithWeasel.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,15 @@ typedef std::map<std::string, AppOptions, CaseInsensitiveCompare>
AppOptionsByAppName;

struct SessionStatus {
SessionStatus() : style(weasel::UIStyle()), __synced(false), session_id(0) {
SessionStatus()
: style(weasel::UIStyle()),
commit_langid(0),
__synced(false),
session_id(0) {
RIME_STRUCT(RimeStatus, status);
}
weasel::UIStyle style;
int commit_langid;
RimeStatus status;
bool __synced;
RimeSessionId session_id;
Expand Down Expand Up @@ -71,6 +76,8 @@ class RimeWithWeaselHandler : public weasel::RequestHandler {
void _UpdateUI(WeaselSessionId ipc_id);
void _LoadSchemaSpecificSettings(WeaselSessionId ipc_id,
const std::string& schema_id);
void _LoadLanguageOverrideConfig(WeaselSessionId ipc_id,
const std::string& schema_id);
void _LoadAppInlinePreeditSet(WeaselSessionId ipc_id,
bool ignore_app_name = false);
bool _ShowMessage(weasel::Context& ctx, weasel::Status& status);
Expand Down
6 changes: 3 additions & 3 deletions include/WeaselIPCData.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,9 @@ struct Status {

// 用於向前端告知設置信息
struct Config {
Config() : inline_preedit(false) {}
void reset() { inline_preedit = false; }
bool inline_preedit;
Config() : commit_langid(0) {}
void reset() { commit_langid = 0; }
int commit_langid;
};

struct UIStyle {
Expand Down
Loading