原生 JS 实现 VS Code 自动切换输入法状态!这次没有AHK
上一篇文章:使用 AHK 在 VS Code 中根据上下文自动切换输入法状态 给出一个使用 ahk 在 VSCode 自动切换输入法的方法。不过这个方法实际上很蹩脚,一点都不优(zhuang)雅(bi)。一直想能不能直接使用 js 实现这个,但是 js 大多数是用来搞前端,对 winAPI
没什么支持,因此颇费了一番周折。
直到发现一个可以用来调用 winAPI
的包叫做 node-ffi
,以及它的 升级版 node-ffi-napi
,才算是拿到了这个接口。测试性能比原来的好了十倍,使用 AHK 大概需要 20毫秒,现在约1-3 毫秒就能完成切换。
效果和原来大同小异,但是这次 直接安装插件即可,无需其他操作:
插件已经发布到插件市场:Shift IM for Math
。如果不关心实现过程,那么 Enjoy it!
(原来的插件 Ultra IME toggler
不再维护,仅作学习交流使用)
漫漫搜寻
其实从去年的这个时候刚刚接触 js 时,就萌生了这个想法,无奈那时连 Node.js 还都不知道,也不知道去哪搜索。前段时间使用 AHK 成功实现自动输入法切换,然后这个想法重新回来了,决定花点时间解决这个问题。
翻了一下 AHK 的帮助文档,了解到上次提到的核心代码:
hWnd := winGetID("A")
DefaultIMEWnd := DllCall("imm32\ImmGetDefaultIMEWnd", "Uint", hWnd , "Uint")
SendMessage(
0x283, ; Message : WM_IME_CONTROL
0x002, ; wParam : IMC_SETCONVERSIONMODE
1025, ; lParam : (1025-CN; 0-EN)
, ; Control : (Window)
; Retrieves the default window handle to the IME class.
"ahk_id " DefaultIMEWnd
)
实际上 SendMessage
以及 ImmGetDefaultIMEWnd
都是C中定义的函数,而AHK直接把它拿来用了。我们看一下这个输入法控制的脚本是怎么工作的:
- 使用
winGetID
拿到当前窗口句柄 - 使用
ImmGetDefaultIMEWnd
拿到对应输入法的句柄 - 向输入法发送消息
- 发送消息为 “控制输入法”
WM_IME_CONTROL
,值为0x283
wParam
参数给0x002
,用以设定输入法状态lParam
参数给1025
或0
,分别对应中英文
- 发送消息为 “控制输入法”
接下来问题,就是如何将这一流程搬到 JS 上去。搜索一番后,找到了这个包:node-ffi
,可惜已经停更了,没法在新 node
上跑。于是改用 node-ffi-napi
包。
安装问题
以下默认装好了 Node.js
和 npm
。
话不多说,直接 npm install ffi-napi
。然后,运气不好,一串鲜红的 ERR 让人心凉了半截!当然去搜报错,才搜到装这个包的正确姿势。
首先,由于需要调系统的API,包含 C++ 代码,所以肯定需要编译的,因此首先下载 windows-build-tools
,这是个 VS 2017 的编译套件,还有 python 2.x
等。在管理员的powershell中执行:npm i -g windows-build-tools
这是个漫长的过程,需要下载很长时间。装好后会发现开始菜单多了这些东西:
在出现上述内容之前,不要动你的命令行!即使它很卡,因为下载上述工具需要几个 G ,颇费一番工夫。运气好的话安装完成,否则就真的卡住了:这不是电脑的问题,是上面安装脚本的bug。解决方法参照这里:npm安装windows-build-tools时卡在Successfully installed Python 2.7 。大致是强行给一个日志文件,这是管用的。
然后参考这里:node-ffi从入门到放弃(安装篇) ,大致要做这些事:
- 设置python路径,特别是如果电脑上有 python3 更要做这件事:
npm config set PYTHON %PYTHON2%
- 设置 vs 版本:
npm config set msvs_version 2017
- 安装 node-gyp :
npm i -g node-gyp
如果还报错,可以清理一下缓存,或者重启一下计算机。
如果报错,可以试着装一下 node-gyp 编译包:npx node-gyp install --dist-url=https://npm.taobao.org/mirrors/node
(如果提示找不到 npx
,则去掉前面的 npx
)。
然后才能开心地引入 node-ffi-napi
: 在当前文件夹执行 npm install ffi-napi
。
这样终于做完准备工作了!
柳暗花明
现在终于可以做一个测试。先测试下一个广为流传的示例:
// 引入 ffi 包
const ffi = require("ffi-napi");
function TEXT(text) {
return Buffer.from(`${text}\0`, "ucs2");
}
// 使用 ffi.Library 定义函数库,第一个参数传所在的 dll ,第二个参数传需要的函数。
// 函数后面分别是输出和输入类型
const user32 = new ffi.Library("user32", {
MessageBoxW: ["int32", ["int32", "string", "string", "int32"]],
});
// 调用,会弹出一个窗口
const OK_or_Cancel = user32.MessageBoxW(0, TEXT("Hello from Node.js!"), TEXT("Hello, World!"), 1);
console.log(OK_or_Cancel);
运行,果然弹出一个提示窗口:
测试正常,可以进行下一步的操作了。
回到原来的代码,再贴一次:
hWnd := winGetID("A")
DefaultIMEWnd := DllCall("imm32\ImmGetDefaultIMEWnd", "Uint", hWnd , "Uint")
SendMessage(
0x283, ; Message : WM_IME_CONTROL
0x002, ; wParam : IMC_SETCONVERSIONMODE
1025, ; lParam : (1025-CN; 0-EN)
, ; Control : (Window)
; Retrieves the default window handle to the IME class.
"ahk_id " DefaultIMEWnd
)
显然我们需要引入 SendMessage
,可是并没有。查文档后使用 SendMessageW
替换。此函数原型为:
LRESULT SendMessageW (
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM IParam
)
可以不管这些奇怪的类型,四个参数分别给 long
int32
int32
int32
,返回值给 int32
。给出:
const ffi = require('ffi-napi');
const user32 = new ffi.Library("user32", {
"SendMessageW": ['int32', ['long', 'int32', 'int32', 'int32']],
});
然后分别填入这四个参数。暂时跳过第一个,剩下三个给 0x283
,0x002
,1025
。这就是与上述 AHK 的代码相对应的 Msg
wParam
lParam
。注意,最后一个参数给 1025
对应中文状态,0
对应英文状态。
当然,如果 wParam
给 0x001
,就是检测当前输入法状态,可以接收一下返回值。此时 lParam
可以不给。
然后回来看第一个 hWnd
,要填入一个句柄。当然是要填输入法的句柄了;这个句柄从 AHK 的代码中可以看出,使用 imm32.dll
中的 ImmGetDefaultIMEWnd
获取,于是另定义一个 ffi.Library
:
const imm = new ffi.Library("imm32" ,{
"ImmGetDefaultIMEWnd": ["int32" , ["int32"]]
});
上述 AHK 代码中的 DllCall
函数的参数提示我们,此 ImmGetDefaultIMEWnd
返回一个 UInt
,且需要接受 UInt
型的 hWnd
。这与上述函数库中的引入是一致的(当然这里简单粗暴,都给了 int32
的型)。
而原来程序中的 winGetID("A")
得到的句柄是当前窗口的句柄,于是查阅资料,在 WinAPI 中可以使用 GetForegroundWindow()
函数,其同样存在于 user32.dll
,用类似上述的方法引用。
然后可以调用这些函数了。
这样补完全部代码,实现一个英文切中文的程序:
const ffi = require('ffi-napi');
const user32 = new ffi.Library("user32", {
"SendMessageW": ['int32', ['long', 'int32', 'int32', 'int32']],
"GetForegroundWindow":["int32",[]]
});
const imm = new ffi.Library("imm32" ,{
"ImmGetDefaultIMEWnd": ["int32" , ["int32"]]
});
var hwnd = user32.GetForegroundWindow()
var defaultIMEWnd = imm.ImmGetDefaultIMEWnd(hwnd)
user32.SendMessageW(defaultIMEWnd,0x283,0x002,1025);
在 Node.js
中跑一下,效果如下,注意输入法状态:
成功!
编写插件
这样就可以写一个 VS Code 插件,仍是和 上一篇文章 一样,拿到当前的上下文标记之后自动切换即可。于是可以在写 LaTeX
的时候少按几次 shift 了!
插件地址:Shift IM for Math
GitHub地址,欢迎来star~
原ahk版本的地址,欢迎学习、交流~
原文链接:https://www.cnblogs.com/yf-zhao/p/16032543.html 转载请注明出处!
原生 JS 实现 VS Code 自动切换输入法状态!这次没有AHK的更多相关文章
- 使用 AHK 在 VS Code 中根据上下文自动切换输入法状态
平常在VS Code打公式,中英文切换一直狂点 Shift 手都快按断了,于是试图用 AutoHotKey 搞一些自动切换输入法程序,让它根据当前输入环境自动切输入法. 之前在网上搜到的是切换键盘的( ...
- 原生js+css3实现图片自动切换,图片轮播
运用CSS3transition及opacity属性 制作图片轮播动画 自己这两天根据用js来控制触发CSS3中transition属性,从而写出来的以CSS3动画为基础,js控制过程的图片轮播 运用 ...
- VS Code - Vim 插件自动切换输入法
前言: 在使用 Linux 的过程中,vim 是一个不错的编辑器,以至于多数人将其用成了习惯,在没有 vim 的环境下还是习惯用 vim 的快捷键来编辑文本.所以便有开发者们为众多的 IDE 和文本编 ...
- 在 Visual Studio 等编辑器/IDE中自动切换输入法,不需要手动的有没有?
使用Visual Studio写代码,经常遇到的一个问题就是切换中文输入法麻烦,输入完注释//,要切换到中文,输入完引号,要输入中文,然后还需要切换回来,有没有? 有时候中文输入法忽然失效有没有?明明 ...
- 原生js实现淘宝图片切换
这个淘宝图片切换具体效果就是:鼠标移上底部一行中的小图片,上面大图片区域就会显示对应的图片. gif图片看起来还挺酷的,其实实现很简单,用原生js绑定事件改变大图片区域的src. 上代码,html部分 ...
- 原生js实现多组图片切换
这几天一直在练习原生js写效果,需要理清自己的逻辑,做了一个切换多组图片的效果: css样式: * { margin: 0; padding: 0; } body { background: #303 ...
- WIN10环境下点击通知栏图标时自动切换输入法导致图标位置变动
这个问题由来已久,每当点击系统右下角任务栏中的按钮时,原本是搜狗输入法就会自动变成“US [ 中文(简体,中国) ]”,图标会自动错位,导致响应的是其他功能. 假设上图是正常的环境,此时我点击电池图标 ...
- HTML+JS+DOM【选项卡自动切换】
最终效果图(鼠标无操作会自动切换选项卡): <!DOCTYPE html> <html> <head> <meta charset="gb2312& ...
- Vue结合原生js实现自定义组件自动生成
就目前三大前端主流数据驱动框架(vue,ng,react)而言,均具有创建自定义组件的api,但都是必须先做到事先写好挂载点,这个挂载点可以是原有静态元素标签也可以是自定义模板:对于多种组件通过同一数 ...
随机推荐
- PlatformIO 创建 libopencm3 + FreeRTOS 项目
PlatformIO: libopencm3 + FreeRTOS 以下步骤基于常见的 Bluepill STM32F103C8T6, 也适用于其它 libopencm3 支持的MCU型号 方案一: ...
- Oracle - Trunc() 函数截取日期&截取数值
Oracle TRUNC函数可以截取数字和日期类型:截取日期:select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual; --显示当前时间 s ...
- Solution -「LOCAL」「cov. HDU 6864」找朋友
\(\mathcal{Description}\) Link.(几乎一致) 给定 \(n\) 个点 \(m\) 条边的仙人掌和起点 \(s\),边长度均为 \(1\).令 \(d(u)\) 表 ...
- v78.01 鸿蒙内核源码分析(消息映射篇) | 剖析LiteIpc(下)进程通讯机制 | 百篇博客分析OpenHarmony源码
百篇博客分析|本篇为:(消息映射篇) | 剖析LiteIpc(下)进程通讯机制 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析( ...
- 防世界之Web_warmup
题目: 啥都没有,右键打开页面源代码查看一下 发现source.php源文件,输入http://111.200.241.244:53776/source.php看看 <?php high ...
- Vue框架简介和环境搭建
前言: 此篇随笔为个人学习前端框架Vue,js的技术笔记,主要记录一些自己在学习Vue框架的心得体会和技术总结,作为回顾和笔记使用. 这种写博客的方式,对刚开始学习Vue框架的我,也是一种激励,我相信 ...
- 当TIME_WAIT状态的TCP正常挥手,收到SYN后…
摘要:今天就来讨论下这个问题,在TCP正常挥手过程中,处于TIME_WAIT状态的连接,收到相同四元组的SYN后会发生什么? 本文分享自华为云社区<在TIME_WAIT状态的TCP连接,收到SY ...
- (二)ECMA 335 解析 /ECMA 334
C#被ECMA组织,定义为了<ECMA334>标准化语言. 什么概念? 比如说,上一次成为ECMA标准的语言是Javascript.即<ECMA262>标准. <ECMA ...
- prometheus-数据展示之grafana部署和数据源配置
1.监控pods . prometheus再部署以后,会自带cAdvisor.结果如下: 2.K8S集群状态监控.需要使用kube-state-metrics插件.部署以后 kubernetes. ...
- Python回顾笔记(此讲大致说明,详情请看之前的笔记)
内容概要 数据分析(numpy,pandas,matplib) 数据清洗 爬虫 teableau软件 今日内容概要 Python知识回顾 数据分析 ipython模块 anaconda软件 numpy ...