From 3d8ffb8daa72604b3ab87d09c0435501a3baffe7 Mon Sep 17 00:00:00 2001 From: mii Date: Sat, 1 Mar 2025 00:43:31 +0900 Subject: [PATCH] implement TSF --- Cargo.toml | 4 + src/com.rs | 24 ++++++ src/lib.rs | 1 + src/main.rs | 13 ++++ src/text_store.rs | 11 ++- src/tsf.rs | 186 +++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 233 insertions(+), 6 deletions(-) create mode 100644 src/com.rs create mode 100644 src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 655cc32..a67234b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,10 @@ tracing = "0.1" tracing-subscriber = "0.1" anyhow = "1.0.86" +[[bin]] +name = "main" +path = "src/main.rs" + [dependencies.windows] version = "0.56.0" features = [ diff --git a/src/com.rs b/src/com.rs new file mode 100644 index 0000000..cb505ab --- /dev/null +++ b/src/com.rs @@ -0,0 +1,24 @@ +use anyhow::Result; +use tracing::debug; +use windows::Win32::System::Com::{CoInitialize, CoUninitialize}; + +pub struct Com; + +impl Drop for Com { + fn drop(&mut self) { + debug!("Dropping Com instance"); + unsafe { + CoUninitialize(); + debug!("CoUninitialize called"); + }; + } +} + +impl Com { + pub fn new() -> Result { + unsafe { + let _ = CoInitialize(None); + }; + Ok(Com) + } +} diff --git a/src/lib.rs b/src/lib.rs index 8cb1f80..180b852 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ mod edit_session; mod thread_mgr; pub mod tsf; +pub mod com; mod text_store; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8e7b528 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,13 @@ +use iatjc_rs::tsf::TSF; +use iatjc_rs::com::Com; + +fn main() { + tracing_subscriber::fmt::init(); + + let _com = Com::new().unwrap(); + + let mut tsf_main = TSF::new(); + tsf_main.initialize().unwrap(); + + println!("TSF initialized successfully"); +} \ No newline at end of file diff --git a/src/text_store.rs b/src/text_store.rs index a8654e4..7a21408 100644 --- a/src/text_store.rs +++ b/src/text_store.rs @@ -1,7 +1,7 @@ use std::sync::{atomic::{AtomicI32, Ordering}, Mutex, RwLock}; -use windows::{Win32::{Foundation::{HWND, POINT, RECT, E_INVALIDARG, E_NOINTERFACE, E_NOTIMPL, E_UNEXPECTED, S_OK, BOOL}, System::{Com::{IDataObject, FORMATETC}, Ole::CONNECT_E_ADVISELIMIT}, UI::TextServices::{ITextStoreACP, ITextStoreACPSink, ITextStoreACP_Impl, TEXT_STORE_LOCK_FLAGS, TEXT_STORE_TEXT_CHANGE_FLAGS, TS_AS_TEXT_CHANGE, TS_ATTRVAL, TS_E_NOLOCK, TS_E_SYNCHRONOUS, TS_LF_READ, TS_LF_READWRITE, TS_LF_SYNC, TS_RT_PLAIN, TS_SD_LOADING, TS_SD_READONLY, TS_SELECTION_ACP, TS_SS_REGIONS, TS_STATUS, TS_ST_NONE, TS_TEXTCHANGE}}}; -use windows_core::{IUnknown, IUnknownImpl, Interface, HRESULT}; +use windows::{Win32::{Foundation::{HWND, POINT, RECT, E_INVALIDARG, E_NOINTERFACE, E_NOTIMPL, E_UNEXPECTED, S_OK, BOOL}, System::{Com::{IDataObject, FORMATETC}, Ole::CONNECT_E_ADVISELIMIT}, UI::TextServices::{ITextStoreACP, ITextStoreACPSink, ITextStoreACP_Impl, TEXT_STORE_LOCK_FLAGS, TS_AS_TEXT_CHANGE, TS_ATTRVAL, TS_E_NOLOCK, TS_E_SYNCHRONOUS, TS_LF_READ, TS_LF_READWRITE, TS_LF_SYNC, TS_RT_PLAIN, TS_SD_LOADING, TS_SD_READONLY, TS_SELECTION_ACP, TS_SS_REGIONS, TS_STATUS, TS_ST_NONE, TS_TEXTCHANGE}}}; +use windows_core::{implement, IUnknown, IUnknownImpl, Interface, HRESULT}; fn flag_check(value: u32, flag: u32) -> bool { (value & flag) == flag @@ -31,6 +31,7 @@ impl From for LockType { } } +#[implement(ITextStoreACP)] pub struct TfTextStore { ref_count: AtomicI32, advice_sink: Mutex, @@ -99,6 +100,12 @@ impl TfTextStore { false } } + + pub fn cast_iunknown(&self) -> windows_core::Result { + unsafe { + self.cast() + } + } } pub struct LockGuard<'a> { diff --git a/src/tsf.rs b/src/tsf.rs index 8ddbf54..141ef9a 100644 --- a/src/tsf.rs +++ b/src/tsf.rs @@ -1,11 +1,189 @@ -pub struct TSF { +use std::{ops::Deref, rc::Rc}; +use anyhow::Result; + +use windows::Win32::UI::TextServices::{ITfContext, ITfDocumentMgr, ITfFnReconversion, ITfFunctionProvider, GUID_SYSTEM_FUNCTIONPROVIDER}; +use windows_core::{IUnknown, Interface}; +use tracing::{debug, error, info, instrument, warn, span, Level}; + +use crate::{text_store::TfTextStore, thread_mgr::ThreadMgr}; + +pub struct TSF { + client_id: u32, + thread_mgr: Option, + doc_mgr: Option, + text_store: Option>, + context: Option, + edit_cookie: u32, + func_prov: Option, + reconvert: Option } impl TSF { - pub fn new() -> TSF { - TSF { - + #[instrument(name = "tsf_new", level = "debug", skip_all)] + pub fn new() -> Self { + info!("Creating new TSF instance"); + Self { + client_id: 0, + thread_mgr: None, + doc_mgr: None, + text_store: None, + context: None, + edit_cookie: 0, + func_prov: None, + reconvert: None } } + + #[instrument(name = "tsf_initialize", level = "debug", skip_all, err)] + pub fn initialize(&mut self) -> Result<()> { + let span = span!(Level::INFO, "initialize_tsf"); + let _enter = span.enter(); + + info!("Initializing TSF"); + + debug!("Creating thread manager"); + self.thread_mgr = Some(ThreadMgr::new()?); + let thread_mgr = self.thread_mgr.as_ref().unwrap(); + debug!("Thread manager created successfully"); + + debug!("Creating document manager"); + let doc_mgr = thread_mgr.create_document_manager()?; + self.doc_mgr = Some(doc_mgr); + debug!("Document manager created successfully"); + + debug!("Activating thread manager"); + self.client_id = thread_mgr.activate()?; + debug!("Thread manager activated with client_id: {}", self.client_id); + + debug!("Creating text store"); + self.text_store = Some(Rc::new(TfTextStore::new())); + let text_store = self.text_store.as_ref().unwrap(); + debug!("Text store created successfully"); + + let doc_mgr = self.doc_mgr.as_ref().unwrap(); + + debug!("Creating context with client_id: {}", self.client_id); + let (context, edit_cookie) = unsafe { + let mut context = None; + let mut edit_cookie = 0; + debug!("Casting text store to IUnknown"); + let text_store = text_store.deref() as *const _ as *mut IUnknown; + debug!("Creating context"); + let result = doc_mgr.CreateContext(self.client_id, 0, Some(&*text_store), &mut context, &mut edit_cookie); + if result.is_err() { + error!("Failed to create context: {:?}", result); + return Err(anyhow::anyhow!("Failed to create context: {:?}", result)); + } + + (context.unwrap(), edit_cookie) + }; + debug!("Context created successfully with edit_cookie: {}", edit_cookie); + + self.context = Some(context); + self.edit_cookie = edit_cookie; + + debug!("Pushing context to document manager"); + unsafe { + match doc_mgr.Push(self.context.as_ref().unwrap()) { + Ok(_) => debug!("Context pushed successfully"), + Err(e) => { + error!("Failed to push context: {:?}", e); + return Err(e.into()); + } + } + } + + debug!("Getting function provider"); + let func_prov = match thread_mgr.get_function_provider(&GUID_SYSTEM_FUNCTIONPROVIDER) { + Ok(fp) => { + debug!("Function provider retrieved successfully"); + fp + }, + Err(e) => { + error!("Failed to get function provider: {:?}", e); + return Err(e); + } + }; + self.func_prov = Some(func_prov); + + if let Some(func_prov) = &self.func_prov { + debug!("Getting reconversion function"); + let reconvert: ITfFnReconversion = unsafe { + match func_prov.GetFunction(&windows_core::GUID::zeroed(), &ITfFnReconversion::IID) { + Ok(func) => { + match func.cast() { + Ok(reconv) => { + debug!("Reconversion function retrieved and cast successfully"); + reconv + }, + Err(e) => { + error!("Failed to cast function to ITfFnReconversion: {:?}", e); + return Err(e.into()); + } + } + }, + Err(e) => { + error!("Failed to get reconversion function: {:?}", e); + return Err(e.into()); + } + } + }; + + self.reconvert = Some(reconvert); + } + + debug!("Setting focus to document manager"); + unsafe { + match thread_mgr.thread_mgr.SetFocus(Some(self.doc_mgr.as_ref().unwrap())) { + Ok(_) => debug!("Focus set successfully"), + Err(e) => { + error!("Failed to set focus: {:?}", e); + return Err(e.into()); + } + } + } + + info!("TSF initialized successfully"); + Ok(()) + } + + #[instrument(name = "tsf_uninitialize", level = "debug", skip_all)] + pub fn uninitialize(&mut self) { + info!("Uninitializing TSF"); + + if let Some(thread_mgr) = &self.thread_mgr { + debug!("Deactivating thread manager"); + unsafe { + match thread_mgr.thread_mgr.Deactivate() { + Ok(_) => debug!("Thread manager deactivated successfully"), + Err(e) => warn!("Failed to deactivate thread manager: {:?}", e) + } + } + } + + debug!("Clearing resources"); + self.reconvert = None; + debug!("Reconversion function cleared"); + + self.func_prov = None; + debug!("Function provider cleared"); + + self.edit_cookie = 0; + debug!("Edit cookie reset"); + + self.context = None; + debug!("Context cleared"); + + self.text_store = None; + debug!("Text store cleared"); + + self.doc_mgr = None; + debug!("Document manager cleared"); + + self.thread_mgr = None; + debug!("Thread manager cleared"); + + info!("TSF uninitialized successfully"); + } } \ No newline at end of file