implement TSF

This commit is contained in:
mii
2025-03-01 00:43:31 +09:00
parent 91e88be666
commit 3d8ffb8daa
6 changed files with 233 additions and 6 deletions

View File

@ -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 = [

24
src/com.rs Normal file
View File

@ -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<Self> {
unsafe {
let _ = CoInitialize(None);
};
Ok(Com)
}
}

View File

@ -1,4 +1,5 @@
mod edit_session;
mod thread_mgr;
pub mod tsf;
pub mod com;
mod text_store;

13
src/main.rs Normal file
View File

@ -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");
}

View File

@ -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<u32> for LockType {
}
}
#[implement(ITextStoreACP)]
pub struct TfTextStore {
ref_count: AtomicI32,
advice_sink: Mutex<AdviceSink>,
@ -99,6 +100,12 @@ impl TfTextStore {
false
}
}
pub fn cast_iunknown(&self) -> windows_core::Result<IUnknown> {
unsafe {
self.cast()
}
}
}
pub struct LockGuard<'a> {

View File

@ -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<ThreadMgr>,
doc_mgr: Option<ITfDocumentMgr>,
text_store: Option<Rc<TfTextStore>>,
context: Option<ITfContext>,
edit_cookie: u32,
func_prov: Option<ITfFunctionProvider>,
reconvert: Option<ITfFnReconversion>
}
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");
}
}