From 198408af44991e8ab91df48aeffd7a938d4f647f Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Thu, 12 Sep 2024 08:32:43 +0900 Subject: [PATCH] wip --- libIME/idl/libime2.idl | 19 ++- libIME/src/gfx.rs | 129 +++++++++++++++ libIME/src/lib.rs | 1 + libIME/src/window/candidate_window.rs | 223 ++++++++++++++++++++++++++ libIME/src/window/message_window.rs | 57 ++----- libIME/src/window/mod.rs | 24 +-- 6 files changed, 390 insertions(+), 63 deletions(-) create mode 100644 libIME/src/gfx.rs create mode 100644 libIME/src/window/candidate_window.rs diff --git a/libIME/idl/libime2.idl b/libIME/idl/libime2.idl index d84f53d..152aefe 100644 --- a/libIME/idl/libime2.idl +++ b/libIME/idl/libime2.idl @@ -1,4 +1,5 @@ import "unknwn.idl"; +import "msctf.idl"; [ object, @@ -24,6 +25,14 @@ interface IWindow : IUnknown LRESULT wndProc(UINT msg, WPARAM wp, LPARAM lp); } +typedef struct _KeyEvent { + UINT type; + UINT keyCode; + UINT charCode; + LPARAM lParapm; + BYTE keyStates[256]; +} KeyEvent; + [ object, uuid(d4eee9d6-60a0-4169-b3b8-d99f66ebe61a), @@ -31,7 +40,14 @@ local ] interface ICandidateWindow : IWindow { - void setFontSize(float size); + void setFontSize(DWORD fontSize); + void add(LPCWSTR item, WCHAR selKey); + WCHAR currentSelKey(); + void clear(); + void setCandPerRow(int n); + void setUseCursor(boolean use); + boolean filterKeyEvent([in] KeyEvent *keyEvent); + boolean hasResult(); } [ @@ -49,5 +65,6 @@ interface IMessageWindow : IWindow [local] void LibIME2Init(); [local] void CreateImeWindow([out] void **window); [local] void CreateMessageWindow(HWND parent, [out] void **messagewindow); +[local] void CreateCandidateWindow(HWND parent, [out] void **candidatewindow); [local] IWindow *ImeWindowFromHwnd(HWND hwnd); [local] boolean ImeWindowRegisterClass(HINSTANCE hinstance); \ No newline at end of file diff --git a/libIME/src/gfx.rs b/libIME/src/gfx.rs new file mode 100644 index 0000000..b655ce7 --- /dev/null +++ b/libIME/src/gfx.rs @@ -0,0 +1,129 @@ +use windows::core::*; +use windows::Foundation::Numerics::*; +use windows::Win32::Foundation::*; +use windows::Win32::Graphics::Direct2D::Common::*; +use windows::Win32::Graphics::Direct2D::*; +use windows::Win32::Graphics::Direct3D::*; +use windows::Win32::Graphics::Direct3D11::*; +use windows::Win32::Graphics::DirectWrite::*; +use windows::Win32::Graphics::Dxgi::Common::*; +use windows::Win32::Graphics::Dxgi::*; +use windows::Win32::Graphics::Gdi::*; + +pub(crate) fn create_color(gdi_color_index: SYS_COLOR_INDEX) -> D2D1_COLOR_F { + let color = unsafe { GetSysColor(gdi_color_index) }; + D2D1_COLOR_F { + r: (color & 0xFF) as f32 / 255.0, + g: ((color >> 8) & 0xFF) as f32 / 255.0, + b: ((color >> 16) & 0xFF) as f32 / 255.0, + a: 1.0, + } +} + +pub(crate) fn create_brush( + target: &ID2D1DeviceContext, + color: D2D1_COLOR_F, +) -> Result { + let properties = D2D1_BRUSH_PROPERTIES { + opacity: 0.8, + transform: Matrix3x2::identity(), + }; + + unsafe { target.CreateSolidColorBrush(&color, Some(&properties)) } +} + +pub(crate) fn create_device_with_type(drive_type: D3D_DRIVER_TYPE) -> Result { + let flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + + let mut device = None; + + unsafe { + D3D11CreateDevice( + None, + drive_type, + None, + flags, + None, + D3D11_SDK_VERSION, + Some(&mut device), + None, + None, + ) + .map(|()| device.unwrap()) + } +} + +pub(crate) fn create_device() -> Result { + let mut result = create_device_with_type(D3D_DRIVER_TYPE_HARDWARE); + + if let Err(err) = &result { + if err.code() == DXGI_ERROR_UNSUPPORTED { + result = create_device_with_type(D3D_DRIVER_TYPE_WARP); + } + } + + result +} + +pub(crate) fn create_render_target( + factory: &ID2D1Factory1, + device: &ID3D11Device, +) -> Result { + unsafe { + let d2device = factory.CreateDevice(&device.cast::()?)?; + + let target = d2device.CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE)?; + + target.SetUnitMode(D2D1_UNIT_MODE_DIPS); + + Ok(target) + } +} + +pub(crate) fn get_dxgi_factory(device: &ID3D11Device) -> Result { + let dxdevice = device.cast::()?; + unsafe { dxdevice.GetAdapter()?.GetParent() } +} + +pub(crate) fn create_swapchain_bitmap( + swapchain: &IDXGISwapChain1, + target: &ID2D1DeviceContext, +) -> Result<()> { + let surface: IDXGISurface = unsafe { swapchain.GetBuffer(0)? }; + + let props = D2D1_BITMAP_PROPERTIES1 { + pixelFormat: D2D1_PIXEL_FORMAT { + format: DXGI_FORMAT_B8G8R8A8_UNORM, + alphaMode: D2D1_ALPHA_MODE_IGNORE, + }, + dpiX: 96.0, + dpiY: 96.0, + bitmapOptions: D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, + ..Default::default() + }; + + unsafe { + let bitmap = target.CreateBitmapFromDxgiSurface(&surface, Some(&props))?; + target.SetTarget(&bitmap); + }; + + Ok(()) +} + +pub(crate) fn create_swapchain(device: &ID3D11Device, window: HWND) -> Result { + let factory = get_dxgi_factory(device)?; + + let props = DXGI_SWAP_CHAIN_DESC1 { + Format: DXGI_FORMAT_B8G8R8A8_UNORM, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount: 2, + SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, + ..Default::default() + }; + + unsafe { factory.CreateSwapChainForHwnd(device, window, &props, None, None) } +} diff --git a/libIME/src/lib.rs b/libIME/src/lib.rs index 96906b0..f100365 100644 --- a/libIME/src/lib.rs +++ b/libIME/src/lib.rs @@ -1,3 +1,4 @@ +mod gfx; mod window; #[cxx::bridge] diff --git a/libIME/src/window/candidate_window.rs b/libIME/src/window/candidate_window.rs new file mode 100644 index 0000000..744be6e --- /dev/null +++ b/libIME/src/window/candidate_window.rs @@ -0,0 +1,223 @@ +use std::{ + cell::{Cell, RefCell}, + ffi::{c_int, c_uint}, +}; + +use windows::core::*; +use windows::Foundation::Numerics::*; +use windows::Win32::Foundation::*; +use windows::Win32::Graphics::Direct2D::Common::*; +use windows::Win32::Graphics::Direct2D::*; +use windows::Win32::Graphics::Direct3D::*; +use windows::Win32::Graphics::Direct3D11::*; +use windows::Win32::Graphics::DirectWrite::*; +use windows::Win32::Graphics::Dxgi::Common::*; +use windows::Win32::Graphics::Dxgi::*; +use windows::Win32::Graphics::Gdi::*; +use windows::Win32::UI::WindowsAndMessaging::*; + +use super::{IWindow, IWindow_Impl, IWindow_Vtbl, Window}; + +#[derive(Debug)] +#[repr(C)] +struct KeyEvent { + r#type: c_uint, + key_code: c_uint, + char_code: c_uint, + lparam: LPARAM, + key_states: [u8; 256], +} + +#[interface("d4eee9d6-60a0-4169-b3b8-d99f66ebe61a")] +unsafe trait ICandidateWindow: IWindow { + fn set_font_size(&self, font_size: u32); + fn add(&self, item: PCWSTR, sel_key: u16); + fn current_sel_key(&self) -> u16; + fn clear(&self); + fn set_cand_per_row(&self, n: c_int); + fn set_use_cursor(&self, r#use: bool); + fn filter_key_event(&self, key_event: *const KeyEvent) -> bool; + fn has_result(&self) -> bool; +} + +#[derive(Debug)] +#[implement(ICandidateWindow, IWindow)] +struct CandidateWindow { + items: RefCell>, + sel_keys: RefCell>, + current_sel: Cell, + cand_per_row: Cell, + use_cursor: Cell, + factory: ID2D1Factory1, + dwrite_factory: IDWriteFactory1, + text_format: RefCell, + target: RefCell>, + swapchain: RefCell>, + brush: RefCell>, + dpi: f32, + window: ComObject, +} + +impl CandidateWindow { + fn recalculate_size(&self) -> Result<()> { + todo!() + } + // FIXME: extract + fn resize_swap_chain(&self, width: u32, height: u32) -> Result<()> { + let target = self.target.borrow(); + let swapchain = self.swapchain.borrow(); + + if target.is_some() { + let target = target.as_ref().unwrap(); + let swapchain = swapchain.as_ref().unwrap(); + unsafe { target.SetTarget(None) }; + + if unsafe { + swapchain + .ResizeBuffers( + 0, + width, + height, + DXGI_FORMAT_UNKNOWN, + DXGI_SWAP_CHAIN_FLAG(0), + ) + .is_ok() + } { + create_swapchain_bitmap(swapchain, target)?; + } else { + self.target.take(); + self.swapchain.take(); + self.brush.take(); + } + + self.on_paint()?; + } + + Ok(()) + } + fn on_paint(&self) -> Result<()> { + todo!() + } +} + +impl ICandidateWindow_Impl for CandidateWindow_Impl { + unsafe fn set_font_size(&self, font_size: u32) { + todo!() + } + + unsafe fn add(&self, item: PCWSTR, sel_key: u16) { + self.items.borrow_mut().push(item.to_hstring().unwrap()); + self.sel_keys.borrow_mut().push(sel_key); + } + + unsafe fn current_sel_key(&self) -> u16 { + *self.sel_keys.borrow().get(self.current_sel.get()).unwrap() + } + + unsafe fn clear(&self) { + todo!() + } + + unsafe fn set_cand_per_row(&self, n: c_int) { + if n as usize != self.cand_per_row.get() { + self.cand_per_row.set(n as usize); + self.recalculate_size(); + } + } + + unsafe fn set_use_cursor(&self, r#use: bool) { + self.use_cursor.set(r#use); + if self.is_visible() { + self.refresh(); + } + } + + unsafe fn filter_key_event(&self, key_event: *const KeyEvent) -> bool { + todo!() + } + + unsafe fn has_result(&self) -> bool { + todo!() + } +} + +impl IWindow_Impl for CandidateWindow_Impl { + unsafe fn hwnd(&self) -> HWND { + self.window.hwnd() + } + + unsafe fn create(&self, parent: HWND, style: u32, ex_style: u32) -> bool { + self.window.create(parent, style, ex_style) + } + + unsafe fn destroy(&self) { + self.window.destroy() + } + + unsafe fn is_visible(&self) -> bool { + self.window.is_visible() + } + + unsafe fn is_window(&self) -> bool { + self.window.is_window() + } + + unsafe fn r#move(&self, x: c_int, y: c_int) { + self.window.r#move(x, y) + } + + unsafe fn size(&self, width: *mut c_int, height: *mut c_int) { + self.window.size(width, height) + } + + unsafe fn resize(&self, width: c_int, height: c_int) { + self.window.resize(width, height) + } + + unsafe fn client_rect(&self, rect: *mut RECT) { + self.window.client_rect(rect) + } + + unsafe fn rect(&self, rect: *mut RECT) { + self.window.rect(rect) + } + + unsafe fn show(&self) { + self.window.show() + } + + unsafe fn hide(&self) { + self.window.hide() + } + + unsafe fn refresh(&self) { + self.window.refresh() + } + + unsafe fn wnd_proc(&self, msg: c_uint, wp: WPARAM, lp: LPARAM) -> LRESULT { + // match msg { + // WM_PAINT => { + // let mut ps = PAINTSTRUCT::default(); + // BeginPaint(self.hwnd(), &mut ps); + // let _ = self.on_paint(); + // let _ = EndPaint(self.hwnd(), &ps); + // LRESULT(0) + // } + // WM_TIMER => { + // if wp.0 == ID_TIMEOUT { + // self.hide(); + // KillTimer(self.hwnd(), ID_TIMEOUT).expect("failed to kill timer"); + // } + // LRESULT(0) + // } + // WM_NCDESTROY => { + // self.target.take(); + // self.swapchain.take(); + // self.brush.take(); + // LRESULT(0) + // } + // _ => DefWindowProcW(self.hwnd(), msg, wp, lp), + // } + LRESULT(0) + } +} diff --git a/libIME/src/window/message_window.rs b/libIME/src/window/message_window.rs index 068e4df..ebf28ab 100644 --- a/libIME/src/window/message_window.rs +++ b/libIME/src/window/message_window.rs @@ -5,49 +5,18 @@ use std::{ ops::Deref, }; -use windows::{ - core::{h, implement, interface, w, ComObject, ComObjectInner, Interface, PCWSTR}, - Foundation::Numerics::Matrix3x2, - Win32::{ - Foundation::{HWND, LPARAM, LRESULT, RECT, WPARAM}, - Graphics::{ - Direct2D::{ - Common::{D2D1_ALPHA_MODE_IGNORE, D2D1_COLOR_F, D2D1_PIXEL_FORMAT, D2D_RECT_F}, - D2D1CreateFactory, ID2D1DeviceContext, ID2D1Factory1, ID2D1SolidColorBrush, - D2D1_BITMAP_OPTIONS_CANNOT_DRAW, D2D1_BITMAP_OPTIONS_TARGET, - D2D1_BITMAP_PROPERTIES1, D2D1_BRUSH_PROPERTIES, D2D1_DEVICE_CONTEXT_OPTIONS_NONE, - D2D1_DRAW_TEXT_OPTIONS_NONE, D2D1_FACTORY_OPTIONS, - D2D1_FACTORY_TYPE_SINGLE_THREADED, D2D1_UNIT_MODE_DIPS, - }, - Direct3D::{D3D_DRIVER_TYPE, D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP}, - Direct3D11::{ - D3D11CreateDevice, ID3D11Device, D3D11_CREATE_DEVICE_BGRA_SUPPORT, - D3D11_SDK_VERSION, - }, - DirectWrite::{ - DWriteCreateFactory, IDWriteFactory1, IDWriteTextFormat, - DWRITE_FACTORY_TYPE_SHARED, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, - DWRITE_FONT_WEIGHT_NORMAL, DWRITE_MEASURING_MODE_NATURAL, DWRITE_TEXT_METRICS, - }, - Dxgi::{ - Common::{DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_SAMPLE_DESC}, - IDXGIDevice, IDXGIFactory2, IDXGISurface, IDXGISwapChain1, DXGI_ERROR_UNSUPPORTED, - DXGI_PRESENT, DXGI_SWAP_CHAIN_DESC1, DXGI_SWAP_CHAIN_FLAG, - DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, DXGI_USAGE_RENDER_TARGET_OUTPUT, - }, - Gdi::{ - BeginPaint, EndPaint, GetSysColor, COLOR_INFOBK, COLOR_INFOTEXT, PAINTSTRUCT, - SYS_COLOR_INDEX, - }, - }, - UI::WindowsAndMessaging::{ - DefWindowProcW, GetClientRect, KillTimer, SetWindowPos, HWND_TOPMOST, SWP_NOACTIVATE, - SWP_NOMOVE, WM_NCDESTROY, WM_PAINT, WM_TIMER, WS_CLIPCHILDREN, WS_EX_TOOLWINDOW, - WS_EX_TOPMOST, WS_POPUP, - }, - }, -}; -use windows_core::{Result, HSTRING}; +use windows::core::*; +use windows::Foundation::Numerics::*; +use windows::Win32::Foundation::*; +use windows::Win32::Graphics::Direct2D::Common::*; +use windows::Win32::Graphics::Direct2D::*; +use windows::Win32::Graphics::Direct3D::*; +use windows::Win32::Graphics::Direct3D11::*; +use windows::Win32::Graphics::DirectWrite::*; +use windows::Win32::Graphics::Dxgi::Common::*; +use windows::Win32::Graphics::Dxgi::*; +use windows::Win32::Graphics::Gdi::*; +use windows::Win32::UI::WindowsAndMessaging::*; use super::{IWindow, IWindow_Impl, IWindow_Vtbl, Window}; @@ -320,7 +289,7 @@ impl IWindow_Impl for MessageWindow_Impl { self.brush.take(); LRESULT(0) } - _ => DefWindowProcW(self.hwnd(), msg, wp, lp), + _ => self.window.wnd_proc(msg, wp, lp), } } } diff --git a/libIME/src/window/mod.rs b/libIME/src/window/mod.rs index 19cf2a2..09be715 100644 --- a/libIME/src/window/mod.rs +++ b/libIME/src/window/mod.rs @@ -5,24 +5,12 @@ use std::{ ptr::null_mut, }; -use windows::{ - core::{implement, interface, w, IUnknown, IUnknown_Vtbl, Interface, Weak}, - Win32::{ - Foundation::{FALSE, HINSTANCE, HWND, LPARAM, LRESULT, RECT, WPARAM}, - Graphics::Gdi::{ - GetMonitorInfoW, InvalidateRect, MonitorFromRect, HBRUSH, MONITORINFO, - MONITOR_DEFAULTTONEAREST, - }, - UI::WindowsAndMessaging::{ - CreateWindowExW, DefWindowProcW, DestroyWindow, GetClientRect, GetWindowRect, IsWindow, - IsWindowVisible, LoadCursorW, MoveWindow, RegisterClassExW, SetWindowPos, ShowWindow, - CS_IME, HICON, HWND_TOP, IDC_ARROW, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOZORDER, SW_HIDE, - SW_SHOWNA, WINDOW_EX_STYLE, WINDOW_STYLE, WM_NCDESTROY, WNDCLASSEXW, - }, - }, -}; -use windows_core::PCWSTR; +use windows::core::*; +use windows::Win32::Foundation::*; +use windows::Win32::Graphics::Gdi::*; +use windows::Win32::UI::WindowsAndMessaging::*; +mod candidate_window; mod message_window; thread_local! { @@ -259,7 +247,7 @@ impl IWindow_Impl for Window_Impl { unsafe fn refresh(&self) { if !self.hwnd().is_invalid() { - let _ = InvalidateRect(self.hwnd(), None, FALSE); + let _ = InvalidateRect(self.hwnd(), None, true); } }