From 0f28bb1a3e04b6bee051f20ee621ffb05c2052ce Mon Sep 17 00:00:00 2001 From: millionart Date: Fri, 1 May 2026 20:34:15 +0800 Subject: [PATCH] feat: commit dictated text through TSF edit sessions --- .../app/windows-ime/OpenLessIme.vcxproj | 2 + .../app/windows-ime/src/edit_session.cpp | 81 +++++++++++++++++++ .../app/windows-ime/src/edit_session.h | 23 ++++++ .../app/windows-ime/src/text_service.cpp | 47 ++++++++++- 4 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 openless-all/app/windows-ime/src/edit_session.cpp create mode 100644 openless-all/app/windows-ime/src/edit_session.h diff --git a/openless-all/app/windows-ime/OpenLessIme.vcxproj b/openless-all/app/windows-ime/OpenLessIme.vcxproj index ec863ef7..d27520d7 100644 --- a/openless-all/app/windows-ime/OpenLessIme.vcxproj +++ b/openless-all/app/windows-ime/OpenLessIme.vcxproj @@ -76,11 +76,13 @@ + + diff --git a/openless-all/app/windows-ime/src/edit_session.cpp b/openless-all/app/windows-ime/src/edit_session.cpp new file mode 100644 index 00000000..1e4210b1 --- /dev/null +++ b/openless-all/app/windows-ime/src/edit_session.cpp @@ -0,0 +1,81 @@ +#include "edit_session.h" + +#include + +OpenLessEditSession::OpenLessEditSession(ITfContext* context, + std::wstring text) + : context_(context), text_(std::move(text)) { + if (context_ != nullptr) { + context_->AddRef(); + } +} + +OpenLessEditSession::~OpenLessEditSession() { + if (context_ != nullptr) { + context_->Release(); + context_ = nullptr; + } +} + +STDMETHODIMP OpenLessEditSession::QueryInterface(REFIID iid, void** object) { + if (object == nullptr) { + return E_POINTER; + } + *object = nullptr; + + if (iid == IID_IUnknown || iid == IID_ITfEditSession) { + *object = static_cast(this); + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) OpenLessEditSession::AddRef() { + return static_cast(InterlockedIncrement(&ref_count_)); +} + +STDMETHODIMP_(ULONG) OpenLessEditSession::Release() { + const ULONG count = static_cast(InterlockedDecrement(&ref_count_)); + if (count == 0) { + delete this; + } + return count; +} + +STDMETHODIMP OpenLessEditSession::DoEditSession(TfEditCookie edit_cookie) { + if (context_ == nullptr) { + return E_UNEXPECTED; + } + + ITfInsertAtSelection* insert_at_selection = nullptr; + HRESULT hr = context_->QueryInterface(IID_ITfInsertAtSelection, + reinterpret_cast( + &insert_at_selection)); + if (FAILED(hr)) { + return hr; + } + + ITfRange* query_range = nullptr; + hr = insert_at_selection->InsertTextAtSelection( + edit_cookie, TF_IAS_QUERYONLY, text_.c_str(), + static_cast(text_.size()), &query_range); + if (query_range != nullptr) { + query_range->Release(); + query_range = nullptr; + } + + if (SUCCEEDED(hr)) { + ITfRange* committed_range = nullptr; + hr = insert_at_selection->InsertTextAtSelection( + edit_cookie, 0, text_.c_str(), static_cast(text_.size()), + &committed_range); + if (committed_range != nullptr) { + committed_range->Release(); + } + } + + insert_at_selection->Release(); + return hr; +} diff --git a/openless-all/app/windows-ime/src/edit_session.h b/openless-all/app/windows-ime/src/edit_session.h new file mode 100644 index 00000000..5ab96bbd --- /dev/null +++ b/openless-all/app/windows-ime/src/edit_session.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +class OpenLessEditSession final : public ITfEditSession { + public: + OpenLessEditSession(ITfContext* context, std::wstring text); + OpenLessEditSession(const OpenLessEditSession&) = delete; + OpenLessEditSession& operator=(const OpenLessEditSession&) = delete; + ~OpenLessEditSession() override; + + STDMETHODIMP QueryInterface(REFIID iid, void** object) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + STDMETHODIMP DoEditSession(TfEditCookie edit_cookie) override; + + private: + LONG ref_count_ = 1; + ITfContext* context_ = nullptr; + std::wstring text_; +}; diff --git a/openless-all/app/windows-ime/src/text_service.cpp b/openless-all/app/windows-ime/src/text_service.cpp index 7a0caa79..ed911832 100644 --- a/openless-all/app/windows-ime/src/text_service.cpp +++ b/openless-all/app/windows-ime/src/text_service.cpp @@ -1,5 +1,9 @@ #include "text_service.h" +#include + +#include "edit_session.h" + extern LONG g_object_count; OpenLessTextService::OpenLessTextService() { @@ -84,8 +88,47 @@ HRESULT OpenLessTextService::SubmitTextFromPipe( const std::wstring& session_id, const std::wstring& text) { UNREFERENCED_PARAMETER(session_id); - UNREFERENCED_PARAMETER(text); - return E_NOTIMPL; + + if (thread_mgr_ == nullptr || client_id_ == TF_CLIENTID_NULL) { + return E_UNEXPECTED; + } + + ITfDocumentMgr* document_mgr = nullptr; + HRESULT hr = thread_mgr_->GetFocus(&document_mgr); + if (FAILED(hr)) { + return hr; + } + if (document_mgr == nullptr) { + return E_FAIL; + } + + ITfContext* context = nullptr; + hr = document_mgr->GetTop(&context); + document_mgr->Release(); + document_mgr = nullptr; + if (FAILED(hr)) { + return hr; + } + if (context == nullptr) { + return E_FAIL; + } + + auto* session = new (std::nothrow) OpenLessEditSession(context, text); + if (session == nullptr) { + context->Release(); + return E_OUTOFMEMORY; + } + + HRESULT edit_result = S_OK; + hr = context->RequestEditSession(client_id_, session, + TF_ES_SYNC | TF_ES_READWRITE, &edit_result); + session->Release(); + context->Release(); + + if (FAILED(hr)) { + return hr; + } + return edit_result; } HRESULT OpenLessTextService::StartIpcServer() {