haku-maiのブログ

インフラエンジニアですが、アプリも作ります。

xterm.jsでキーボード入力を受け付ける方法

本記事で行うこと

  • xterm.jsを利用して、キーボード入力を受け付けるhtmlを作成する。
  • ※環境はnode.jsを使いますが、最後にCDNサイトを使ってhtmlファイルのみで行う方法も記載しておきます。

モチベーション

  • vscodeのterminalなどで採用されているxterm.jsをつかってプロダクトを作ろうと思ったため。
  • xterm.jsの使い方を開発メンバーと共有したかったため。

環境

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.7
BuildVersion:   19H15

$ node -v
v12.18.3

xterm.jsのインストール

versionは本記事を書いたときの最新版を利用します。

$ npm install --save xterm@4.9.0

Hello xterm.js

まず公式ページにあるとおり、html上にterminal windowを表示してみます。
github.com

<!doctype html>
  <html>
    <head>
      <link rel="stylesheet" href="node_modules/xterm/css/xterm.css" />
      <script src="node_modules/xterm/lib/xterm.js"></script>
    </head>
    <body>
      <div id="terminal"></div>
      <script>
        let term = new Terminal();
        term.open(document.getElementById('terminal'));
        term.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ')
      </script>
    </body>
  </html>

headのpathはインストールした場所を指定してください。
作成したhtmlファイルにブラウザでアクセスすると以下のようにterminalを表示できます。
f:id:n-guitar:20201114190900p:plain

  • term.openでterminalを開いて、term.writeでterminalに文字を書き込めるようです。

この状態だとterminalに向かってキーボードを打っても何も反応しないのですが、
せっかくなのでこの状態で、Chromeの開発者モードで少し遊んで見たいと思います。

Chromeの開発者モードのConsoleを開き以下のように打ち込んで見ると画面が更新されました。

f:id:n-guitar:20201114191438p:plain:w600

term.write("書き込んでみた")

f:id:n-guitar:20201114191719p:plain:w600

Consoleにtermを打ち込むと何やら使えそうな関数が色々出てくるので、公式サイトで使い方を見てみます。

term

f:id:n-guitar:20201114192211p:plain:w600

xtermjs.org Doc -> Class: Terminalを参照しいくつか試した結果を書いておきます。

  • 書き込んで改行する。
term.writeln("改行する")
  • 書き込んでpromptを表示する。 今後Enterを押した時に必ずこれを実行すれば良さそうです。
term.write('\r\n$ ')
  • terminalに書き込まれたものをresetする。 clearが入力された時にこれを実行すれば良さそうな気がします。
term.reset()

キーボード入力を受け付けるhtmlを作成する。

公式が的供しているdemoサイトを参考に以下を作りました。 xterm.js/client.ts at master · xtermjs/xterm.js · GitHub

<!doctype html>
  <html>
    <head>
      <link rel="stylesheet" href="node_modules/xterm/css/xterm.css" />
      <script src="node_modules/xterm/lib/xterm.js"></script>
    </head>
    <body>
      <div id="terminal"></div>
      <script>
        let term = new Terminal();
        term.open(document.getElementById('terminal'));
        function runFakeTerminal() {
          if (term._initialized) {
            return;
          }
          term._initialized = true;
          term.prompt = () => {
            term.write('\r\n$ ');
          };

          term.writeln('Welcome to xterm.js');
          term.writeln('This is a local terminal emulation, without a real terminal in the back-end.');
          term.writeln('Type some keys and commands to play around.');
          term.writeln('');
          term.prompt();

          term.onKey(e => {
            console.log(e)
            const ev = e.domEvent
            const printable = !ev.altKey && !ev.ctrlKey && !ev.metaKey

            if (ev.keyCode === 13) {
              term.prompt();
            } else if (ev.keyCode === 8) {
              if (term._core.buffer.x > 2) {
                term.write('\b \b');
              }
            } else if (printable) {
              term.write(e.key);
            }
          });
        }
        runFakeTerminal()
      </script>
    </body>
  </html>

これでキーボード入力を受け付けるようになりました。
f:id:n-guitar:20201114201055p:plain:w600

少しだけ解説すると・・・

  • term.prompt関数は呼ばれるとterm.write('\r\n$ ')を実行しバッファ2の『$ 』を書き込みます。
  • keyCode === 13はEnter
  • keyCode === 8はBackspace
  • term._core.buffer.x > 2はtermのバッファが3以上の時1文字消します。
  • printable = !ev.altKey && !ev.ctrlKey && !ev.metaKeyはこの条件がtrueの時の判定に利用
  • else if (printable) {term.write(e.key);でprintableがtrueの時にterm.write(e.key)して入力をterminal上に書き込みます。

キーボード入力とコードの対応表 KeyboardEvent.keyCode - Web API | MDN

CDNサイト版

手っ取り早く試してみたい人は以下をhtmlファイルで保存すればnode.jsなしでも実行できます。
CDNサイトがある限り。

<!doctype html>
  <html>
    <head>
      <link rel="stylesheet" href="https://unpkg.com/xterm@4.9.0/css/xterm.css">
      <script src="https://unpkg.com/xterm@4.9.0/lib/xterm.js"></script>
    </head>
    <body>
      <div id="terminal"></div>
      <script>
        let term = new Terminal();
        term.open(document.getElementById('terminal'));
        function runFakeTerminal() {
          if (term._initialized) {
            return;
          }
          term._initialized = true;
          term.prompt = () => {
            term.write('\r\n$ ');
          };

          term.writeln('Welcome to xterm.js');
          term.writeln('This is a local terminal emulation, without a real terminal in the back-end.');
          term.writeln('Type some keys and commands to play around.');
          term.writeln('');
          term.prompt();

          term.onKey(e => {
            console.log(e)
            const ev = e.domEvent
            const printable = !ev.altKey && !ev.ctrlKey && !ev.metaKey

            if (ev.keyCode === 13) {
              term.prompt();
            } else if (ev.keyCode === 8) {
              if (term._core.buffer.x > 2) {
                term.write('\b \b');
              }
            } else if (printable) {
              term.write(e.key);
            }
          });
        }
        runFakeTerminal()
      </script>
    </body>
  </html>