Lee @ JSDC 2016
https://hackmd.io/p/BJ0xsY5A#/
PCMan: 我不會寫 node.js
JS 離統一世界又邁進了一步
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
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
st=>start: init
e=>end: onDeactivate
op=>operation: onActivate
st->op->e
每個應用程式切換到輸入法,即建立連線( ->init->onActivate ),切離輸入法,即斷掉連線(->onDeactivate)。
第一個 request
{
"method": "init",
"id": "{f80736aa-28db-423a-92c9-5540f501c939}",
"isWindows8Above": True,
"isUiLess": False,
"isConsole": False,
"isMetroApp": True,
"seqNum": 1
}
response 範例
{"success": True, "seqNum": 1}
第二個 request
{
"method": "onActivate",
"seqNum": 0,
"isKeyboardOpen": True
}
response 範例
{
'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': '設定'
}]
}
st=>start: filterKeyDown
e=>end: onKeyUp
op=>operation: onKeyDown
op1=>operation: filterKeyUp
st->op->op1->e
按下 a 按鍵
{
"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
}
ime.json
{
"name":"emojime",
"version": "0.3.0",
"guid": "{A381D463-9338-4FBD-B83D-66FFB03523B3}",
"locale": "zh-TW",
"icon": "icon.ico",
"win8_icon": "",
"moduleName": "index",
"serviceName": ""
}
index.js
module.exports = {
textReducer(request, preState) {
return preState;
},
response(request, state) {
return {success: true, seqNum: request['seqNum']};
}
}
The case convertor. Part 1.
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;
}
The case convertor. Part 2.
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};
}
server.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();
commitString
: 決定要輸出的字樣{
"success":true,
"seqNum":50,
"commitString":"❤️"
}
compositionString
: 決定組字框的字樣compositionCursor
: 決定組字框的游標位置{
"success":true,
"seqNum":92,
"compositionString":":rock",
"compositionCursor":5
}
showCandidates
: 決定是否顯示選字框candidateList
: 決定選字框的選單candidateCursor
: 決定選字框的游標位置{
"success":true,
"seqNum":50,
"candidateList":[
"☘️ :shamrock:","☘ :shamrock:","� :rocket:"
],
"showCandidates":true
}