NIME: 使用 nodejs 快快樂樂開發輸入法
===
Lee @ JSDC 2016
https://hackmd.io/p/BJ0xsY5A#/
---
## 自我介紹
- Lee
- ~~一位上班被 Pointer 荼毒、下班陷入 Callback 地獄的工程師。~~
- 新手前端工程師 @ [新芽網路 25sprout](http://www.25sprout.com/)
- jessy1092 @ [Github](https://github.com/jessy1092)
- 閒暇之餘在 g0v 填坑。
---
## 大綱
- 緣起
- 原理
- Live Demo
- 結語
---
## 緣起
---
- [PIME - 用 Python 快速開發 Windows 的中文輸入法](http://www.slideshare.net/pcmantw/pime-python-windows-coscup-2015) (COSCUP 2015)
- 史上首次用 python 開發 TSF 輸入法
- 不需要了解 Windwos TSF 或 IMM32
- 不需要寫 C++
- Server/Client 架構
- 支援 Windows Vista - 10(32 & 64 bit)
---
## Why NodeJS?
---
~~PCMan: 我不會寫 node.js~~
---
![](https://app.kxg.io/images/jiade/fb.jpg)
---
- 開發簡單快速
- PIME IPC 使用 json 做溝通
- 可以使用眾多 node modules
- 吸引 JS 工程師寫輸入法
---
![](https://pbs.twimg.com/profile_images/378800000822867536/3f5a00acf72df93528b6bb7cd0a4fd0c.jpeg)
JS 離統一世界又邁進了一步
---
## 原理
---
```flow
st=>start: Application
e=>end: IME Server
op=>operation: Windwos TSF
op2=>operation: PIMETextService.dll
op3=>operation: libpipe.dll
op4=>operation: nodejs named pipe server
st->op->op2->op3->op4->e
```
---
- [libpipe.dll](https://github.com/EasyIME/PIME/blob/master/libpipe/libpipe.cpp)
- C named pipe server
- 隱藏 named pipe server 細節,讓 js/python 可以簡單啟動 named pipe server
- [lib/pipe.js](https://github.com/EasyIME/NIME/blob/master/lib/pipe.js)
- 使用 node-ffi binding dll
---
```sequence
Application -> IME Server: filterKeyDown: "A"
Note right of IME Server: 決定要不要處理
IME Server -->Application: return : true
Application -> IME Server: onKeyDown: "A"
Note right of IME Server: 處理 A -> B
IME Server -->Application: CommitString: "B"
Note left of Application: Application 顯示 B
Application -> IME Server: 下一次 key event
```
---
## 輸入法狀態
```flow
st=>start: init
e=>end: onDeactivate
op=>operation: onActivate
st->op->e
```
每個應用程式切換到輸入法,即建立連線( ->init->onActivate ),切離輸入法,即斷掉連線(->onDeactivate)。
---
第一個 request
```js
{
"method": "init",
"id": "{f80736aa-28db-423a-92c9-5540f501c939}",
"isWindows8Above": True,
"isUiLess": False,
"isConsole": False,
"isMetroApp": True,
"seqNum": 1
}
```
response 範例
```js
{"success": True, "seqNum": 1}
```
---
第二個 request
```js
{
"method": "onActivate",
"seqNum": 0,
"isKeyboardOpen": True
}
```
response 範例
```js
{
'customizeUI': {
'candPerRow': 3,
'candFontSize': 16,
'candFontName': 'MingLiu',
'candUseCursor': True
},
'seqNum': 0,
'setSelKeys': '1234567890',
'addPreservedKey': [{
'modifiers': 4,
'keyCode': 32,
'guid': '{f1dae0fb-8091-44a7-8a0c-3082a1515447}'
}],
'success': True,
'addButton': [{
'id': 'switch-lang',
'icon': 'icon file path',
'commandId': 1,
'tooltip': '中英文切換'
}, {
'id': 'windows-mode-icon',
'icon': 'icon file path',
'commandId': 4,
'tooltip': '中英文切換'
}, {
'id': 'switch-shape',
'icon': 'icon file path',
'commandId': 2,
'tooltip': '全形/半形切換'
}, {
'id': 'settings',
'icon': 'icon file path',
'type': 'menu',
'tooltip': '設定'
}]
}
```
---
## 按鍵狀態
```flow
st=>start: filterKeyDown
e=>end: onKeyUp
op=>operation: onKeyDown
op1=>operation: filterKeyUp
st->op->op1->e
```
---
按下 a 按鍵
```js
{
"seqNum": 2,
"charCode": 97,
"keyCode": 65,
"method": "filterKeyDown",
"isExtended": False,
"keyStates": [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0],
"scanCode": 0,
"repeatCount": 1
}
```
---
## NIME 初始化設置
ime.json
```js
{
"name":"emojime",
"version": "0.3.0",
"guid": "{A381D463-9338-4FBD-B83D-66FFB03523B3}",
"locale": "zh-TW",
"icon": "icon.ico",
"win8_icon": "",
"moduleName": "index",
"serviceName": ""
}
```
- GUID 可以使用 GUID Generator 產生
- moduleName 為 NIME 讀取的 js 檔案名稱
---
index.js
```js
module.exports = {
textReducer(request, preState) {
return preState;
},
response(request, state) {
return {success: true, seqNum: request['seqNum']};
}
}
```
---
The case convertor. Part 1.
```js
if (request['method'] === 'filterKeyDown') {
let {charCode, seqNum} = request;
let char = String.fromCharCode(charCode);
let response = {return: false, success: true, seqNum};
if ((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z')) {
response['return'] = true;
}
return response;
}
```
https://github.com/jessy1092/nime-demo
---
The case convertor. Part 2.
```js
if (request['method'] === 'onKeyDown') {
let {charCode, seqNum} = request;
let char = String.fromCharCode(charCode);
let commitString = '';
if (char >= 'a' && char <= 'z') {
commitString = char.toUpperCase();
}
if (char >= 'A' && char <= 'Z') {
commitString = char.toLowerCase();
}
return {success: true, commitString, seqNum};
}
```
https://github.com/jessy1092/nime-demo
---
server.js
```js
let nime = require('nime');
let service = require('./index');
let config = require('./ime.json');
config['textService'] = service;
let server = nime.createServer(undefined, [config]);
server.listen();
```
---
## Live Demo
- 大小寫轉換輸入法
---
## 輸入法呈現區塊
- **Commit**: 輸出字
- **Composition**: 組字框
- **Candidate**: 選字框
---
## Commit
- `commitString`: 決定要輸出的字樣
---
![](http://farm9.staticflickr.com/8626/30373831062_b74576d994_b.jpg)
```js
{
"success":true,
"seqNum":50,
"commitString":"❤️"
}
```
---
## Composition
- `compositionString`: 決定組字框的字樣
- `compositionCursor`: 決定組字框的游標位置
---
![](http://farm6.staticflickr.com/5546/30403577571_9d547e3244_b.jpg)
```js
{
"success":true,
"seqNum":92,
"compositionString":":rock",
"compositionCursor":5
}
```
---
## Candidate
- `showCandidates`: 決定是否顯示選字框
- `candidateList`: 決定選字框的選單
- `candidateCursor`: 決定選字框的游標位置
---
![](http://farm6.staticflickr.com/5497/30373669972_20b6d8f7f5_b.jpg)
```js
{
"success":true,
"seqNum":50,
"candidateList":[
"☘️ :shamrock:","☘ :shamrock:","� :rocket:"
],
"showCandidates":true
}
```
---
## Live Demo
- emoji 輸入法
- JS 注音
---
## 結語
---
## JS 開發輸入法元年!!!
---
- 撰寫文件
- 更好的 IPC 架構 ([issue #11](https://github.com/EasyIME/forum/issues/11))
- NIME 支援更多輸入法功能。語言列、偏好設定等等。
- 台語輸入法
- NIME web 模擬器
- 跨平台
---
## Reference
- [PIME - 用 Python 快速開發 Windows 的中文輸入法](http://www.slideshare.net/pcmantw/pime-python-windows-coscup-2015)
- https://github.com/EasyIME/PIME
- https://github.com/EasyIME/NIME
- https://github.com/EasyIME/emojime
- https://github.com/jessy1092/nime-demo
- https://github.com/jessy1092/nime-jszhuyin
- [libIME 架構說明](https://github.com/chewing/windows-chewing-tsf/wiki/libIME-%E6%9E%B6%E6%A7%8B%E8%AA%AA%E6%98%8E)
- https://github.com/chewing/libchewing
- [KeyCode](https://github.com/EasyIME/NIME/blob/master/lib/keyCodes.js)
{"metaMigratedAt":"2023-06-14T11:57:19.485Z","metaMigratedFrom":"Content","title":"NIME: 使用 nodejs 快快樂樂開發輸入法","breaks":true,"contributors":"[]"}