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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
**/*.rs.bk
Cargo.lock
Cargo.lock
/.idea
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ winapi = { version = "0.3", features = [
"hidusage",
"shellapi",
"imm",
"minwindef",
"ntdef"
] }

[target.'cfg(target_os = "android")'.dependencies]
Expand Down
6 changes: 6 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,4 +233,10 @@ pub trait EventHandler {
/// `ctx.dropped_file_path()`, and for wasm targets the file bytes
/// can be requested with `ctx.dropped_file_bytes()`.
fn files_dropped_event(&mut self) {}

/// Get ime preedit text
fn on_ime_preedit(&mut self, _text: &str) {}

/// Get ime commit text before preedit
fn on_ime_commit(&mut self, _text: Option<&str>) {}
}
9 changes: 8 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
}
/// This for now is Android specific since the process can continue running but the display
/// is restarted. We support reinitializing the display.
fn set_or_replace_display(display: native::NativeDisplayData) {

Check warning on line 102 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Build (macos-latest, x86_64-apple-darwin)

function `set_or_replace_display` is never used

Check warning on line 102 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Build (macos-latest, x86_64-apple-ios)

function `set_or_replace_display` is never used

Check warning on line 102 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Build (macos-latest, aarch64-apple-ios)

function `set_or_replace_display` is never used

Check warning on line 102 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, x86_64-unknown-linux-gnu)

function `set_or_replace_display` is never used

Check warning on line 102 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, x86_64-pc-windows-gnu)

function `set_or_replace_display` is never used

Check warning on line 102 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Build (windows-latest, x86_64-pc-windows-msvc)

function `set_or_replace_display` is never used

Check warning on line 102 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, wasm32-unknown-unknown)

function `set_or_replace_display` is never used

Check warning on line 102 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, aarch64-unknown-linux-gnu)

function `set_or_replace_display` is never used

Check warning on line 102 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, armv7-unknown-linux-gnueabihf)

function `set_or_replace_display` is never used
if let Some(m) = NATIVE_DISPLAY.get() {
// Replace existing display
*m.lock().unwrap() = display;
Expand Down Expand Up @@ -410,7 +410,9 @@
/// # Arguments
/// * `enabled` - `true` to enable IME (for text input), `false` to disable (for game controls)
pub fn set_ime_enabled(enabled: bool) {
let d = native_display().lock().unwrap();
let mut d = native_display().lock().unwrap();
d.ime_enabled = enabled;

#[cfg(target_os = "android")]
{
let _ = enabled; // IME control not applicable on Android
Expand All @@ -423,6 +425,11 @@
.unwrap();
}
}

pub fn is_ime_enabled() -> bool {
let d = native_display().lock().unwrap();
d.ime_enabled
}

#[cfg(target_vendor = "apple")]
pub fn apple_gfx_api() -> crate::conf::AppleGfxApi {
Expand Down
4 changes: 3 additions & 1 deletion src/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub(crate) struct NativeDisplayData {
pub screen_width: i32,
pub screen_height: i32,
pub screen_position: (u32, u32),
pub ime_enabled: bool,
pub dpi_scale: f32,
pub high_dpi: bool,
pub quit_requested: bool,
Expand All @@ -23,7 +24,7 @@ pub(crate) struct NativeDisplayData {
pub clipboard: Box<dyn Clipboard>,
pub dropped_files: DroppedFiles,
pub blocking_event_loop: bool,

#[cfg(target_vendor = "apple")]
pub view: crate::native::apple::frameworks::ObjcId,
#[cfg(target_os = "ios")]
Expand All @@ -48,6 +49,7 @@ impl NativeDisplayData {
screen_width,
screen_height,
screen_position: (0, 0),
ime_enabled: false,
dpi_scale: 1.,
high_dpi: false,
quit_requested: false,
Expand Down
6 changes: 3 additions & 3 deletions src/native/linux_x11/xi_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,9 @@ impl LibXi {
let raw_event = xcookie.data as *mut xi_input::XIRawEvent;

// Data returned from Xlib is not guaranteed to be aligned
let ptr = (*raw_event).raw_values as *const f64;
let dx = std::ptr::read_unaligned(ptr);
let dy = std::ptr::read_unaligned(ptr.add(1));
let ptr = (*raw_event).raw_values as *const u8;
let dx = std::ptr::read_unaligned(ptr as *const f64);
let dy = std::ptr::read_unaligned(ptr.add(1) as *const f64);

(self.XFreeEventData)(display, &mut (*xcookie) as *mut _);

Expand Down
95 changes: 65 additions & 30 deletions src/native/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@
},
};

// IME constants
// IME composition string flags
/// Composition String
const GCS_COMPSTR: DWORD = 0x0008;
/// Result String
const GCS_RESULTSTR: DWORD = 0x0800;
const GCS_CURSORPOS: DWORD = 0x0100;
const GCS_DELTASTART: DWORD = 0x0200;

// IME message constants
const WM_IME_SETCONTEXT: UINT = 0x0281;
Expand Down Expand Up @@ -202,6 +207,7 @@

self.user_cursor = cursor_icon != CursorIcon::Default;
}

fn set_window_size(&mut self, new_width: u32, new_height: u32) {
let mut x = 0;
let mut y = 0;
Expand Down Expand Up @@ -250,7 +256,7 @@
if unsafe { GetClientRect(self.wnd, &mut rect as *mut _ as _) } != 0 {
let mut new_rect = rect;
new_rect.right = new_rect.right - new_rect.left + new_x as i32;
new_rect.bottom = new_rect.bottom - new_rect.top + new_y as i32;

Check warning on line 259 in src/native/windows.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, x86_64-pc-windows-gnu)

value assigned to `new_rect` is never read

Check warning on line 259 in src/native/windows.rs

View workflow job for this annotation

GitHub Actions / Build (windows-latest, x86_64-pc-windows-msvc)

value assigned to `new_rect` is never read
unsafe {
SetWindowPos(
self.wnd,
Expand Down Expand Up @@ -590,50 +596,75 @@
}
WM_IME_COMPOSITION => {
let flags = lparam as u32;
let himc = ImmGetContext(hwnd);

// Extract and dispatch the result string manually to avoid duplicates
if (flags & GCS_RESULTSTR) != 0 {
let himc = ImmGetContext(hwnd);
if !himc.is_null() {
let len = ImmGetCompositionStringW(himc, GCS_RESULTSTR, std::ptr::null_mut(), 0);
if !himc.is_null() {
let mut should_notify_end = false;

// Composition String
if (flags & GCS_COMPSTR) != 0 {
let len = ImmGetCompositionStringW(himc, GCS_COMPSTR, std::ptr::null_mut(), 0);
if len > 0 {
let mut buffer: Vec<u16> = vec![0; (len as usize / 2) + 1];
let actual_len = ImmGetCompositionStringW(
himc,
GCS_RESULTSTR,
buffer.as_mut_ptr() as *mut _,
len as u32
);
if actual_len > 0 {
let char_count = actual_len as usize / 2;
let mods = key_mods();
// Send chars in order
for i in 0..char_count {
let chr = buffer[i];
if let Some(c) = char::from_u32(chr as u32) {
event_handler.char_event(c, mods, false);
}
}
ImmGetCompositionStringW(himc, GCS_COMPSTR, buffer.as_mut_ptr() as *mut _, len as u32);
let char_count = len as usize / 2;
let preedit_str = String::from_utf16_lossy(&buffer[..char_count]);

event_handler.on_ime_preedit(&preedit_str);
} else {
should_notify_end = true;
}
}


// Check Cursor
if (flags & GCS_CURSORPOS) != 0 || (flags & GCS_DELTASTART) != 0 {
let cursor_pos_len = ImmGetCompositionStringW(himc, GCS_CURSORPOS, std::ptr::null_mut(), 0);
let delta_start_len = ImmGetCompositionStringW(himc, GCS_DELTASTART, std::ptr::null_mut(), 0);

if cursor_pos_len == 0 && delta_start_len == 0 {
let comp_len = ImmGetCompositionStringW(himc, GCS_COMPSTR, std::ptr::null_mut(), 0);
if comp_len == 0 {
should_notify_end = true;
}
}
ImmReleaseContext(hwnd, himc);
}
return 0;

// Result String
if (flags & GCS_RESULTSTR) != 0 {
let len = ImmGetCompositionStringW(himc, GCS_RESULTSTR, std::ptr::null_mut(), 0);
if len > 0 {
let mut buffer: Vec<u16> = vec![0; (len as usize / 2) + 1];
ImmGetCompositionStringW(himc, GCS_RESULTSTR, buffer.as_mut_ptr() as *mut _, len as u32);
let char_count = len as usize / 2;
let result_str = String::from_utf16_lossy(&buffer[..char_count]);
event_handler.on_ime_commit(Some(&result_str));
should_notify_end = false;
} else {
should_notify_end = true;
}
}

if should_notify_end {
event_handler.on_ime_commit(None);
}
ImmReleaseContext(hwnd, himc);
}

// For non-result messages (composition state updates), pass to DefWindowProc
return DefWindowProcW(hwnd, umsg, wparam, lparam);
return 0;
}
WM_IME_SETCONTEXT => {
let fShow = HIWORD(wparam as _) != 0;

Check warning on line 657 in src/native/windows.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, x86_64-pc-windows-gnu)

variable `fShow` should have a snake case name

Check warning on line 657 in src/native/windows.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, x86_64-pc-windows-gnu)

unused variable: `fShow`

Check warning on line 657 in src/native/windows.rs

View workflow job for this annotation

GitHub Actions / Build (windows-latest, x86_64-pc-windows-msvc)

variable `fShow` should have a snake case name

Check warning on line 657 in src/native/windows.rs

View workflow job for this annotation

GitHub Actions / Build (windows-latest, x86_64-pc-windows-msvc)

unused variable: `fShow`

let user_disabled = IME_USER_DISABLED.load(std::sync::atomic::Ordering::Relaxed);

// If user explicitly disabled IME, don't auto-restore
if user_disabled {
if !user_disabled {
// 返回 1 表示 "我处理了,别画默认框"
return 1;
} else {
return 0;
}

// Must pass to DefWindowProc to enable IME properly
return DefWindowProcW(hwnd, umsg, wparam, lparam);

Check warning on line 667 in src/native/windows.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, x86_64-pc-windows-gnu)

unreachable statement

Check warning on line 667 in src/native/windows.rs

View workflow job for this annotation

GitHub Actions / Build (windows-latest, x86_64-pc-windows-msvc)

unreachable statement
}
WM_IME_STARTCOMPOSITION => {
// Offset for candidate window below composition position
Expand Down Expand Up @@ -687,6 +718,8 @@
return DefWindowProcW(hwnd, umsg, wparam, lparam);
}
WM_INPUTLANGCHANGEREQUEST | WM_INPUTLANGCHANGE => {
event_handler.on_ime_commit(None);

// Pass input language change messages to default handler
return DefWindowProcW(hwnd, umsg, wparam, lparam);
}
Expand Down Expand Up @@ -797,6 +830,8 @@
return DefWindowProcW(hwnd, umsg, wparam, lparam);
}
WM_KILLFOCUS => {
event_handler.on_ime_commit(None);

return DefWindowProcW(hwnd, umsg, wparam, lparam);
}
_ => {}
Expand Down
Loading