💖アイドルとして勤務する -理論と実践- #imas_hack
IM@S Engineer Talks - connpassで話した。
あいさつとアジェンダ
こんにちは。mzpです。
本日はアイドルアバターで勤務する話をする。
背景: リモートワークの欠点
ボクの“副業”先はリモートワークを推奨している。これには雨の日は出社が面倒くさいと思う人が多かった、名古屋だけではエンジニアを集めれなかった、など様々な事情がある。
チームメンバーの半数は遠隔地に住んでいるため、リモートワークを前提とした働き方が必要となる。
リモートワークはいくつかの利点があるが、欠点もある。 今回は「自室から勤務する」という欠点にスポットをあてる。
カメラに写る範囲に変なものを置けない。 私はリモートワークをする直前に、カメラに写る範囲から物をどかすという作業をしている。
よく写るように、部屋の照明をだいぶ明るくする必要がある。 まぶしい。
この問題は弊社固有ではないらしく、同じくリモートワークを推進するソニックガーデン社ではこのような解決案が提案されている。(テレビ会議を劇的に円滑にする簡単なノウハウ | Social Change!)
このようにカメラのうつる範囲にオフィス風のポスターをはることで、自宅にいながらオフィスで勤務しているかのようにみせることができる。
こういった問題があるため、社内でVR勤務を求める声があがりはじめた。 これは全社集会で「10年後なにをやっていたいか」という話をした結果だが「アバター勤務」がやりたいことにあげられている。
よりよい未来は自分で作っていかなければならないので、アイドルアバターによるリモートワークを開始した。
デモ
デモです。
アバター勤務の利点
これでオンラインミーティングに参加するとこのようになる。 これは今週あった朝会の様子である。
これを今年の4月から続けている。
背景を気にしないくてよくなる。
iPhone Xの顔認識は可視光を利用していないため、部屋の明さを気にする必要もない。
自分の表情が反映されるので、積極的に笑顔になろうとする意識が働く。 姿勢もよくしたほうがいい。
かわいい表情を維持するために表情に気をつかうようになる。
でもあくびをしててもかわいい。
自分が微笑むとアイドルも微笑み返してくれる。脳にいい。
だんだんと自分がかわいい気がしてくる。むしろ自分がアイドルだ。
社内の反応
同僚の反応はこのようなものだった。 だいたいみんな戸惑いつつTwitterでしゃべっていた。 Slackは静かだった。
一方で弊社の代表がわりといい話をしていた。
「チームメンバーとして見た目は関係ない、性別、年齢も関係ない。みながベストな格好で勤務してほしい」
ただ、Slackアイコンを変えたほうが反響が大きくておもしろかった。「Slackコールがアイドルからの電話になる」
社外の反応
いくつかブログに書いたところ、社外からもいくつかの反応をもらった。
「mzpさんってあの美少女の人ですか」「はいそうです」という会話をしたことである。 まさか、美少女ですかと確認されて、肯定することが生じるとは思っていなかった。
あと「今後、アバター勤務が一般化したとして、そのときは同調圧力が働いて無難なアバターになるのでは。美少女などが使えるのは先行者だけだ」という考察がよかった。 説得力がある。
仕組み
今日はエンジニアトークなので、ここからは仕組みの話をする
コンセプトとしては気軽につかえるアバターシステム。 リモートワークしながら使えるのが前提となるので、専用のコントローラやセンサーを使ったものは避けた。 理想は通常のカメラと同様に使えるアバターシステムである。
このシステムは、主に4つの部分で構成されている。 - 人間の顔をiPhoneのセンサーで認識する - 認識した結果をアイドルに変換するiPhoneアプリ - iPhoneの画面をmacOSでキャプチャする - キャプチャした結果をオンラインミーティングアプリに接続する これらを1つづつ説明していく。
iPhone Xによる顔認識
最初はiPhoneによる顔認識について話す。
iPhone XにはFace IDのために顔の形状を認識するためのセンサーが搭載されている。これは30,000以上の赤外線ドットを顔に照射することで、顔の形状を読み取る。
ARKitを用いることで、このセンサー類で検知した顔の向きや表情を取得できる。本当にさまざまな表情が取得できるので眉毛のうごきだけとっても左右上下の動きをとれることになってる。 ただ、ボクはそこまで自在に眉が動かせないのでデバッグできない。
常にカメラで顔を写すためにスタンドが必要である。 今回はライトニングドックを用いた(iPhone Lightning Dock - ホワイト - Apple(日本))。
アイドルへの変換
次はアイドルへの変換について話す
ここではiPhoneのセンサーで検知した顔の向きや表情をアバターに反映させる。開発にはUnityを用いた。
顔の向きの反映は座標系を間違えなければそれほど難しくはない。hただ失敗すると顔が異常な方向を向いてしまうので怖い。
表情の反映はいくつかの工夫が必要である。 ARKitは顔の各パーツの動きを取得しているが、アバターはモーフという単位で表情を管理している。 このギャップを埋める必要がある。
たとえば眉毛の上下運動を左右のまばたきと対応づける。 顎の位置を口の大きさに対応づけるといった工夫をしている。 また、表情はオーバーなほうが伝わりやすいので一部は係数をおおきめにかけている。
我々は目をとじるとなにも見えなくなるので、まばたきがうまく反映できているかのデバッグはむつかしい。 結果、ウインクの練習が必須となる。
アイドルは基本笑顔だが、我々の基本は無表情なので対応が難しい。 今回は無表情は笑顔、笑顔は歯をみせる笑顔に対応づけた。
眉毛をさげるといった3次元の存在である我々には難しい表情はショートカットを設ける。 ここでは口をすぼめると困った顔になるというショートカット表情を導入している。
しかしそこまで多彩な表情ができるわけではないので「泣き顔」「怒っている顔」などは割愛した。 仕事している上でそんなに利用しないだろうという判断のものだが、笑顔と困っている顔に制限されたアバターで勤務するのはだいぶディストピアっぽい。
Tスタンスではなくかわいいポーズが必要となる。 いろいろ見た中で、照れ - ニコニコ静画 (イラスト)のポーズのうちミクの後ろに腕をまわしているポーズがかわいかったので採用した。
そのまま適用するとこうなる。 とてもかわいいが、上半身と首をひねっているため長時間は厳しい。
つらいとTwitterにつぶやくと、弊社人事よりかわいいは我慢だというコメントを貰う。
そうはいっても首をいためそうだったので、ポーズは修正した。 このように胴体の角度をなくし、楽に維持できるようにした。
iPhoneのキャプチャ
次はiPhoneの画面をキャプチャする仕組みである。
ここはゲーム実況配信のためにさまざまな知見が蓄積されている。 HDMI出力をキャプチャする、AirPlay互換サーバーを使うなど様々な方法がある。
今回はmacOSを使うのでQuickTime Playerによるキャプチャを用いた。
キャプチャの技術的詳細は、ばんじゅんさんがPhotoStudioPlayerを作る過程で検討しているのでそちらを参照してほしい(デレステフォトスタジオのリアルタイム透過プレイヤーをつくった - ツバメになったバリスタ)。
オンラインミーティングへの参加
最後にオンラインミーティングへの参加である。
キャプチャした内容を仮想カメラとして認識させるにはCamTwistを用いた。
特定のアプリのみをキャプチャできるので、それでQuickTimeを選択した。
あとはオンラインミーティングアプリでCamTwistによって作られた仮想カメラを選択すれば、ミーティングに参加できる。 どのアプリを使うかは会社の都合によって変わるところだが、今回はzoomを用いた。
まとめ
まとめると、次の流れでアイドルアバターによるリモートワークを実現している。
さあ、みなさんもアイドルとしてリモートワークしましょう。
自撮り機能
このアプリにはリモートワーク用以外にもいくつかの機能を搭載している。まずはスクリーンショット保存機能。
デバッグの途中でだんだんと卯月の自取り画像を作るのがたのしくなってきたのでつけた。 よりリアリティをだすためにあえてシャッター音をつけている。
撮影ボタンなどもなるべくカメラアプリに見えるよう工夫した。
カメラロールがこのような状態になる。 かわいい。
正面より斜めのほうがいいと発見した。
見おろす感じもいい。
上からみおろすのもよい。
シャフトアニメにありがちなポーズ、通称シャフ度をしようとしたがうまくいかなかった。
笑顔とウインクと顔の角度を工夫したやつです。 これがいちばんうまく撮れたと思う。
ブルーバックスクリーン
背景をブルーバックスクリーンにする機能もある。
まるでデレステのフォトスタジオみたいですね。 ということは、さきほどばんじゅんさんの発表であったPhotoStudioPlayerとくみあせると、デスクトップ上にアイドルを呼べる。
このためにカメラ位置の調整機能もつけているので、こうすると全身を写せる。まるでデスクトップに住んでいるようになりますね。
Twitterでは「伺かだ....」というコメントを何件かもらいましたがみなさんしっていますか。ボクは謎のダメージを受ける。はてブコメントではひたすら「\e」と書かれた。
あんまり続けると老人会になるのでこれくらいにしておく。
今後の課題
目線制御をいれたい。 iPhoneのカメラをみることなくiPhoneの画面を見る技術が必要になる気がしてる。
iOS12のARKit2では単純に顔認識の向上らしいので、さらなる精度向上が見込める。 さらに舌認識がついたので対応したいが、仕事中に舌を出すシチュエーションが思いつかないので、あまり使い道はないかもしれない。
顔しか認識しないため、あまり複雑なポーズができない。 せめて手を動かしたい。OpenPoseなど姿勢推定アルゴリズムと組合せることで、手を認識できるようになるかもしれない。
専用のコントローラを用いて腕の制御をする方式も考えられるがリモートワーク支援という文脈からはずれてしまう上、手軽さがなくなってしまうので悩み所である。
類似ツール
最後に類似ツールがいくつかあるので紹介する。
パペ文字。
iOS12のFaceTime。 今年のWWDCで発表された新機能。もともとメッセージアプリでは絵文字に表情をつけて送信するアニモジという機能があったが、これが通話に拡張された。 キーノートを現地で聞いていたが、これを見た瞬間、時代が追いついてきた!と思った。
まとめ
🏭工場日記
工場実習日記の名前はシモーヌ・ヴェイユの工場日記に由来する。 この本には第二次世界大戦以前の工場で勤務した経験が綴られている。 著者の観察力が非常に高く、工場での労働について鋭い記述が多い。
- 作者: シモーヌヴェイユ,Simone Weil,田辺保
- 出版社/メーカー: 筑摩書房
- 発売日: 2014/11/10
- メディア: 文庫
- この商品を含むブログ (7件) を見る
🐾経緯・来歴
シモーヌ・ヴェイユは哲学者である。
哲学には詳しくないが、本の序文やWikipediaにある記述を要約すると次のような経緯で工場で働くことに決めたらしい。
幼ないころから優秀で、パリ高等師範学校を卒業したのち高等中学校の教授となる。 これがどれくらいの難しさなのかがいまひとつ分からないが、"教授資格者"の手当が存在しているかとや、現代のパリ高等師範学校の記述)を読む限りでは相当な難関であると想像できる。
そのなかで社会主義と出会い、その中で語られる労働者の生活を実際に体感したいと熱望するようになった。 そこで教職をしばらく休み、いくつかの工場で働きはじめた。
そのときの体験は、日記や断片的なメモ、友人にあてた手紙などに残されている。 これをまとめたものが工場日記である。
🤕消耗していく様子
さすがに工場での労働はつらかったらしく、どんどん消耗していく様子が見てとれる。 働くのがつらい、というのが何度も書いてある。
働いて食べるということは、多かれ少なかれ、苦しいことではないだろうか。
一歩一歩がわたしには苦痛だった(精神的に。 帰るときには、苦痛は肉体的になる)
朝の四時半に、眠りたいというつよい欲望がおこってきた。しかし、もう起きねばならなかった。 平日休みをとればいいという誘惑をしりぞける。
地下鉄で、若い女が「もう、がんばれそうにないわ」。 ——— わたしもだ・・・・・・。
一歩一歩歩くということに、確かな意思の力が要る。 帰宅してみれば、それでもたいへん元気になった。
一たん機械の前へ立ったら、一日八時間は、自分のたましいを殺し、思考を殺し、感情を殺し、すべてを殺さなければならない。
🤔思考の放棄
工場で淡々と同じ作業を繰り返すためには、余計なことを考えないことがコツである。 それが合わなかったらしくそれに対する悔しさも繰り返し登場する。
(昼休みに)「プリジュニク(大衆向けデパート)」で食事、くつろぐ。 工場へもどるまでの、しばらくの楽しい時間、技師たち、職工たち・・・・・・。 機械の前へもどると、また奴隷になったように思う。
こういう生活がもたらすもっともつよい誘惑に、わたしもまた、ほとんどうちかつことができないようになあった。 それは、もはや考えることをしないという誘惑である。 それだけが苦しまずにすむ、ただ一つの、唯一の方法なのだ。 ただ土曜日の午後と日曜日にだけ、わたしにも思い出や、思考の断片がもどってくる。 このわたしもまた、考える存在であったことを思いだす。
(考えるのをやめるのは) スピードをあげるための要件だ。ものを考えるのをやめなければならないということの屈辱感を、心の底から感じる。
ひまな時間は、一応理論的には、一日八時間労働なので、かなりあるはずですが、実際には、疲労のためにないのも同じことです。
考えないために給料が支払われている傾向がありそうです。
🔥周囲への評価
作業を意味を考えずに仕事している同僚たちが気にくわなかったらしく、辛辣なことを書いている。
数学の勉強をしなかったら、機械は、労働者にとって一つの神秘的存在になる。
仲間たちは、自分たちが奴隷であることを、十分に理解していないからなのだ。
決して苦しんだことのない人間の単純さ。
専門家は堕落をまねく。
🎓要求の高さ
来歴によるものだと思うが、周囲に要求する水準が高い。さらに本人は高いことを自覚してない様子なのがおもしろい。
かれは、ラテン語を二年間、ギリシア語を一年間、英語を少し勉強したことがあるそうだ。 (こういうことを、かれはなんのてらいもなく自慢する)。
(昔の教え子への手紙で) 教科書や講義の批判をするのです。 やってみると、おどろくほどたくさんな誤った理論が横行していることがわかるでしょう。 こういう遊びは非常に有益です。
その他
https://t.co/gNEsveVu6G を読みはじめたけど、いきなり「人よ、今自分は何をしているかを知れ。人おのおの、自分の仕事についてじっくり考えてみよ」という力強い言葉からはじまった
— mzp (@mzp) January 2, 2017
Wikipediaには「ヴェイユは不幸が真理に至る道になるという思想を持ってきた」との旨が書いてある
— mzp (@mzp) January 2, 2017
工場研修があったマンなので『工場日記』に書いてある内容の一部は共感できるんだけど、こんなに短い文で的確に表現できる気はしないので、あふれる才能を感じる
— mzp (@mzp) January 2, 2017
工場で製品がとにかくアホでも作れるようにしてあるのは、作業をしていると考えるのを放棄しだすからなんですよ
— 光のインターネットの闇 (@no_maddo) January 2, 2017
工場日記を読み直してるけど、日記に「今日は頭痛はない」と書くやばさよ
— mzp (@mzp) June 30, 2018
「今日はつらいことがなかった」ってのもやばいな
— mzp (@mzp) June 30, 2018
💻デスクトップマスコット
美少女として勤務するで作ったアバター表示アプリと、デレステフォトスタジオのリアルタイム透過プレイヤーをつくったのクロマキーフィルターつきiPhoneキャプチャを組合せることでデスクトップマスコットになった。
👚ポーズ
デスクトップマスコットになるには全身を写す必要があるので、Tスタンスではなくかわいいポーズが必要となる。
いろいろ見た中で、以下のポーズのうちミクの後ろに腕をまわしているポーズがかわいかったので採用した。
そのまま適用するとこうなる。
とてもかわいいが、上半身と首をひねっているため長時間は厳しい。 多少、ポーズは修正した。修正はVmd2XMLを利用した。
かわいいには我慢がつきものらしい。
かわいい と おしゃれ に
— miho (@MihoHamaguchi) 2018年6月19日
我慢はつきものです。
と、いつも長女に言って聞かせてるんだけど、どうらやらmzpも学んだらしい。 https://t.co/ozFSFdccX8
🏟背景色
クロマキー合成をするために背景を単色に切り替えれるようにした。 これはカメラのSkybox設定をいじって実現した。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ChangeBackground : MonoBehaviour { private int count = 0; public void OnClick() { count = (count + 1) % 4; switch(count) { case 0: Camera.main.clearFlags = CameraClearFlags.Skybox; Camera.main.backgroundColor = new Color(49/255.0f, 77/255.0f, 121/255.0f); break; case 1: Camera.main.clearFlags = CameraClearFlags.SolidColor; Camera.main.backgroundColor = new Color(0.15f, 0.48f, 1); break; case 2: Camera.main.clearFlags = CameraClearFlags.SolidColor; Camera.main.backgroundColor = new Color(1, 3/255.0f, 102/255.0f); break; case 3: Camera.main.clearFlags = CameraClearFlags.SolidColor; Camera.main.backgroundColor = new Color(251/255.0f, 179/255.0f, 2/255.0f); break; default: break; } } }
そのままMastodonでtootしたらコマ割りされたみたいになってよかった。アニメのアイキャッチ画像っぽい。
📷カメラ位置の調整
全身がはいるようにカメラ位置をずらした。 こうしてみると、引きで写すとかわいい衣装がよく見えて、いいのかもしれない。
その他
これもう完全に伺かでしょ pic.twitter.com/iDqYFKnlgw
— mzp (@mzp) 2018年6月20日
わたしが! デスクトップマスコットだ!!
— mzp (@mzp) 2018年6月20日
デスクトップマスコットとして生きていくぞ
— mzp (@mzp) 2018年6月18日
💎RubyKaigi 2018
RubyKaigi 2018に参加した。 RubyKaigi(仙台)、WWDC(サンノゼ)、出張(東京)というかなり強い日程になっていたがなんとか落ち着いた。
🎤セッション
Rubyの型に興味があったので、型の話を中心に聞いた。
- A practical type system for Ruby at Stripe.
- Ruby Programming with Type Checking
- Type Profiler: An analysis to guess type signatures
ASTのまわりの発表がいくつかあったので聞いた。 やっぱり型と文法には興味ひかれる。
TRICK 2018 (FINAL)は見たかったが移動と被ってしまって見られなかった。 残念。
✨ブース
開始直後にEightのTシャツを貰いにいったら写真を撮られた。美少女として勤務するで笑顔の練習をしたので、なんとか笑えた。
【RubyKaigi2018】
— 【公式】Sansan (@SansanJapan) 2018年5月31日
ネームカードであたりの出た方にTシャツをプレゼントしています👕
カラーとサイズに限りがありますので早めにブースに来てくださいね😊#rubykaigi2018 #rubykaigi#sansan #eight #eightアイマスク#misoca pic.twitter.com/fwc8npwPS7
定期的にコーヒーが提供されててうれしかった。
お弁当おいしい。 牛タンを食べにいくタイミングがなかったが、お弁当で食べれた。
⌨️SKK
SKKこと仙台高等工業学校の跡地も見にいった。
日本語入力のSKKとの関係は微妙で、関係は明言されていないが無関係のはずがないくらいの感じになっている。Wikipediaでは以下のように断言を避けている。
また、東北大学の教授により開発されていたわけであるが、新制東北大学の工学部の母体のひとつ仙台高等工業学校の略称もSKKである。
それはそれとして、校歌が刻まれた石碑があってよかった。
校歌だけあって、言葉が格調高くていい。「SKKのわかき子」「SKKの名を揚げよ」が心に染みる。
そのあと年表を見たら「SKKの名は消えた」と書いてあって、アッとなった。
🦀瑞鳳
瑞鳳のバイキングがやばいと聞いていたのでRubyKaigiの前日に泊まった。
同僚2人と泊まったが部屋が異様にひろくてびびる。
部屋の玄関が広すぎる。
聞いていたとおり夕食が豪華。好きなだけ蟹が食べれる。
肉もある。
温泉も広くて最高だった。
🍽食事
移動に追われたせいで街中で買い食いしたくらいしか仙台っぽいものを食べていない。
仙台にしかないという土鍋チーズこってりの天下一品。
ひょうたん揚げ。 かまぼこで作ったアメリカンドッグみたいな味がする。
たいやき。
⏰時短勤務
勤務時間を7時間/日とする時短勤務をはじめて1年半ほどが経過した。
かなりよいのでまとめておく。 年収の向上と同程度の価値があると感じている。
🏢業務
業務内容は大きな変化なく、支障なくできている(と思う)。 いくつかの要因がありそうだが、よく分からない。
- 8時間勤務のうち最後の1時間は疲労していることが多い。疲労していて生産性が悪い時間がなくなっているだけなので、影響は少ない。
- フレックス勤務なのでまとめて働きたい日は残業して、他の日に早く帰るなどの調整をしている。
- もともと全員の出勤時間・退勤時間はばらばらなので、自分が早く帰っても周囲の負荷が少ない。
🍻ハッピーアワー
たまたま無職・休職中の知り合いが多かったのでハッピーアワーに飲みに行っていた。なぜか寿司の写真が多い。
大半が職を得てしまったので今後は難しいかもしれない。大半の友人はフルタイムで勤務しているので、ご飯の約束をすると、どこかで時間を潰す必要が生じる。
🚶♂️通勤
自由になる時間が増えたので、これまで電車で移動していた区間を徒歩にするなどしてゆっくり通勤するようにした。
最初のうちは新鮮でよかったが、だんだん飽きてきた。 それでも気分転換になるので、たまにやっている。
通勤途中にある神社がひょうたん推しでおもしろかった。
♨️スーパー銭湯
時間に余裕ができたので、スーパー銭湯にたまに行くようになった。
気分転換と疲労回復を狙っているが、継続的に成果を出し続けるたったひとつの冴えたやりかた - Misoca開発者ブログにあるように温泉の近くに住んだほうが効率がよい気がする。
👜リモートワーク
旅先でリモートワークをし、仕事後に観光するのを何度かやった。 これは時短勤務ととても相性がよい。
旭川の街中を散歩しているときの様子。
松江の宍道湖から見た夕日はよかった。仕事終わりが日没ぎりぎりになってしまったので、あわてて走った。
雪がやばいということに目をつむれば、玉造温泉はすごく雰囲気がよかった。
✨個人活動
技術書典参加などは、自由になる時間が増えたおかげである。
執筆の負荷がだいぶ軽減されたと思う。いきなり2冊出せたのも執筆時間が確保できたのが大きい。
🎬映画を見る
会社帰りに映画を見にいきやすくなるかと思ったが、これはうまく行かなかった。
これは「開始時間が固定なので早く仕事が終わっても早く見れるわけではない」「そんなに映画が好きではない」などの理由が大きい。結局、家でNetflixを見ている。
💖美少女として勤務する
パペ文字によるVR出社 を継続している。 楽しい。
🌟アバターの変更
パペ文字に搭載されているモデル以外の美少女にもなりたくなったので、Unityでアバター表示アプリを作成した。
モデルは物述有栖を利用している。 かわいいのと、ライセンスが明確なのがよい。
実装は以下のページが参考になった。
📸自撮りボタンの追加
Unity+iOSでカメラロールにスクショを保存するまで - Qiitaを使って、スクリーンショットを手軽に撮れるようにした。
カメラロールがどんどんかわいくなっていく。
🛠ライトニングドック
常に顔を映せるようにライトニングドックを購入した。 便利なの自宅用とオフィス用を買った。
iPhoneが常に起動しつづけることになるので、だいぶ発熱する。
☺️表情
夕方になると表情が死んでくることを発見した。目が閉じ気味になって、口が開き気味になる。かわいさが半減してしまうので、積極的にほほえむように気をつけはじめた。
でもあくびしててもかわいい。
🙊周囲の様子
🙅♀️zoom.us への接続の改良(失敗)
デレステフォトスタジオのリアルタイム透過プレイヤーをつくった - ツバメになったバリスタを応用して、zoom.us への接続を改良しようとしたが
Zoom APIを用いて、iPhone画面を直接配信できるようにしようとしたが、変更できる範囲が少なくどうしようもなかった。
☕️ JavaScriptと入力メソッド
AquaSKK 4.5.0ではEscキーの扱いを改善した。 この修正のためにWebKitにおけるキーイベント配信の仕組みを追ったので、まとめる。
AquaSKK 4.5.0です。変換をキャンセルしたときに飛ぶイベントが他の入力メソッドと同じになりました。 https://t.co/fHJp12SV9b
— mzp (@mzp) 2018年5月13日
💡入力メソッドごとの差異
@mzp AquaSKKのメンテナもしくはIMギークとして御聞きしたいんですが、SafariでAquaSKK使った時に、変換をEscでキャンセルした時に飛ぶイベントがことえりとちょっと違うのってどうにかできそうでしょうか…。 https://t.co/C5BvMAUKQs ちなみに検証してくれたのはりんすきさん。
—  (@unarist) 2018年4月19日
上記Tweetにあるように、テキストを入力中にEscを押した際にJavaScriptで発火するイベントが入力メソッドごとに違う。
macOS標準の日本語入力やGoogle日本語入力ではキーコードが229のイベントが発生するが、AquaSKKではキーコードが27のイベントが発生していた。 このイベントが違うと入力メソッドを扱うJavaScriptコードが複雑化するので修正した。
🔑キーコード 229
キーコード229はUI Eventsで次のように定義されている。
If an Input Method Editor is processing key input and the event is keydown, return 229.
(訳: 入力メソッドがキー入力を処理しておりイベントが
keydown
の場合は、キーコードは229となる)
🌐WebKit
W3Cが定めているキーコードなのでブラウザ内部の処理が関係するのだろうと想定して、WebKitのコードを調べた。
WebKitはアプリケーションのUIを実現するUIプロセスとタブごとに作られるWebプロセスが協調して動作している。 キー入力の処理もこの2つのプロセスが関連している。
UIプロセス
// Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm void WebViewImpl::keyDown(NSEvent *event) { // (snip) // 入力メソッドに処理を転送する interpretKeyEvent(event, [weakThis = createWeakPtr(), capturedEvent = retainPtr(event)](BOOL handledByInputMethod, const Vector<WebCore::KeypressCommand>& commands) { ASSERT(!handledByInputMethod || commands.isEmpty()); // キーイベントに入力メソッドが処理したかどうか(handledByInputMethod)を付与して、Webプロセスに転送する if (weakThis) weakThis->m_page->handleKeyboardEvent(NativeWebKeyboardEvent(capturedEvent.get(), handledByInputMethod, weakThis->m_isTextInsertionReplacingSoftSpace, commands)); }); } void WebViewImpl::interpretKeyEvent(NSEvent *event, void(^completionHandler)(BOOL handled, const Vector<WebCore::KeypressCommand>& commands)) { // (snip) // Cocoaが提供するAPIを使ってイベントを入力メソッドに転送する [inputContext() handleEventByInputMethod:event completionHandler:[weakThis = createWeakPtr(), capturedEvent = retainPtr(event), capturedBlock = makeBlockPtr(completionHandler)](BOOL handled) { if (!weakThis) { capturedBlock(NO, { }); return; } LOG(TextInput, "... handleEventByInputMethod%s handled", handled ? "" : " not"); if (handled) { capturedBlock(YES, { }); return; } auto commands = weakThis->collectKeyboardLayoutCommandsForEvent(capturedEvent.get()); capturedBlock(NO, commands); }]; }
入力メソッドにキー入力を転送するのに非公開のAPIを利用している。愉快。公開されているAPIと違い、処理結果をコールバックで受けとれるようになっている。
// FIXME: Move to an SPI header. @interface NSTextInputContext (WKNSTextInputContextDetails) - (void)handleEvent:(NSEvent *)event completionHandler:(void(^)(BOOL handled))completionHandler; - (void)handleEventByInputMethod:(NSEvent *)event completionHandler:(void(^)(BOOL handled))completionHandler; - (BOOL)handleEventByKeyboardLayout:(NSEvent *)event; @end
Webプロセス
WebプロセスではUIプロセスで付けられたマークをもとに、元のキーイベントを配信するかキーコード229のイベントを配信するかを決めている。
// Soruce/WebCore/page/EventHandler.cpp // Match key code of composition keydown event on windows. // IE sends VK_PROCESSKEY which has value 229; const int CompositionEventKeyCode = 229; bool EventHandler::internalKeyEvent(const PlatformKeyboardEvent& initialKeyEvent) { // (snip) // キー入力が入力メソッドで処理ずみかどうかを判定する bool handledByInputMethod = keydown->defaultHandled(); if (handledByInputMethod) { // 入力メソッドで処理済みだったらキーコードを229に入れ替える keyDownEvent.setWindowsVirtualKeyCode(CompositionEventKeyCode); keydown = KeyboardEvent::create(keyDownEvent, &m_frame.windowProxy()); keydown->setTarget(element); keydown->setDefaultHandled(); } // (snip) // キーイベントを配信する element->dispatchEvent(keydown); if (handledByInputMethod) return true; // 入力メソッドで処理されていないなら処理を継続する // (snip) }
🙊余談
- JSに配信されるイベントを見るにはKeyboard Event Viewerが便利。
- WebKitを処理を追うときはXcodeでブレイクポイントを設定して追った。 Debugging WebKitが参考になる。
- WebKitのビルドに時間がかかってつらかったので、途中で会社のiMacProに接続してビルドしはじめた。 あとからビルドを始めたのに、先にビルドが終わったのでびびった。