mirror of
https://github.com/mii443/iatjc-rs.git
synced 2025-08-22 16:05:39 +00:00
implement TSF
This commit is contained in:
@ -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
24
src/com.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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
13
src/main.rs
Normal 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");
|
||||
}
|
@ -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> {
|
||||
|
186
src/tsf.rs
186
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<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");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user