keaising / im-select.nvim

Switch Input Method automatically depends on Neovim's edit mode
MIT License
161 stars 23 forks source link

Alternative to im-select.exe on Windows #20

Open kls1991 opened 7 months ago

kls1991 commented 7 months ago

I find that we can use luajit ffi to call win32api to control keyboard layout and ime state on Windows, without the burden of a extra binary and another language pack. I'm not a pro coder yet, so I'm not confident enough to make a good pull request. Below code is my personal config which doesn't support switching between keyboard-layouts, I think it's doable by similar method. If a pull request is needed, I can try to read im-select.nvim's source code and make a configurable pull request. Feel free to use below code with ❤️

-- Reference:
-- https://www.cnblogs.com/yf-zhao/p/16018481.html
-- https://zhuanlan.zhihu.com/p/425951648

local ffi = require "ffi"

ffi.cdef [[
    typedef unsigned int UINT, HWND, WPARAM;
    typedef unsigned long LPARAM, LRESULT;
    LRESULT SendMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
    HWND ImmGetDefaultIMEWnd(HWND unnamedParam1);
    HWND GetForegroundWindow();
]]

local user32 = ffi.load "user32.dll"
local imm32 = ffi.load "imm32.dll"

local ime_hwnd
local ime_group = vim.api.nvim_create_augroup("ime_toggle", { clear = true })

-- Get ime control's hwnd after InsertEnter or CmdlineEnter
-- to ensure getting correct foregroundwindow
vim.api.nvim_create_autocmd({ "InsertEnter", "CmdlineEnter" }, {
    group = ime_group,
    once = true,
    desc = "Get ime control hwnd attached to nvim window",
    callback = function()
        ime_hwnd = imm32.ImmGetDefaultIMEWnd(user32.GetForegroundWindow())
    end,
})

local WM_IME_CONTROL = 0x283
local IMC_GETCONVERSIONMODE = 0x001
local IMC_SETCONVERSIONMODE = 0x002 -- It's said this value differs on different ime, I'm not sure
local ime_mode_ch = 1025
local ime_mode_en = 0

local function set_ime_mode(mode)
    return user32.SendMessageA(ime_hwnd, WM_IME_CONTROL, IMC_SETCONVERSIONMODE, mode)
end

local function get_ime_mode()
    return user32.SendMessageA(ime_hwnd, WM_IME_CONTROL, IMC_GETCONVERSIONMODE, 0)
end

vim.api.nvim_create_autocmd({ "InsertLeave", "CmdlineLeave" }, {
    group = ime_group,
    desc = "Toggle ime to English mode on normal mode",
    callback = function()
        if ime_mode_ch == get_ime_mode() then
            set_ime_mode(ime_mode_en)
        end
    end,
})
keaising commented 7 months ago

Good idea!

I will try it ASAP. If it works as expect, I will merge it.

keaising commented 4 months ago

I don't abonden this issue, but in the last few months my PC has been far away from me, so I can't do the migration.

I will continue this work in May 2024.

keaising commented 1 month ago

Sorry for delay, but my computer is still on the way _(:з」∠)_

I will test this solution as soon as possible when I receive my computer.

R0boter commented 3 days ago

Sorry for delay, but my computer is still on the way (:з」∠)

I will test this solution as soon as possible when I receive my computer.

I tested, it's worked perfectly

R0boter commented 3 days ago

Thinks for your help