Techioz Blog

Ruby を使用してターミナルでマウス イベントをキャプチャする

概要

私が実験してきたコードは次のとおりです。

print "\e[?1003h"

begin
  loop do
    begin
      input = ARGF.read_nonblock(1024)
      puts "input: #{input.inspect}"
    rescue IO::EAGAINWaitReadable
    end
  end
ensure
  print "\e[?1003l"
end

このコードを実行すると、このようなコンテンツが端末に出力されることがわかりますが、ARGF に送信されていることを示すものはありません。 STDINも試してみました。私の read_nonblock 呼び出しでは常に IO::EAGAINWaitReadable が発生します。これは予期された動作のようです (仕様を参照)。

^[[MCn@^[[MCn?^[[MCn>^[[MCm>^[[MCm=^[[MCk;^[[MCk:^[[MCj:^[[MCi:^[[MCh:^[[MCg:^[[MCe:^[[MCd:^[[MCc:^[[MCb;^[[MCb<^[[MCc<^[[MCd<^[[MCe<^[[MCf<^[[MCg<^[[MCg;^[[MCf;^[[MCf:^[[MCe:^[[MCd:^[[MCd;^[[MCd<^[[MCe<^[[MCf<^[[MCg<^[[MCg;^[[MCg<^[[MCg;^[[MCf;^[[MCf:^[[MCe:^[[MCd:^[[MCd;^[[MCe;^[[MCe<^[[MCe;^[[MCf;^[[MCe;^[[MCe:^[[MCe;^[[MCf;^[[MCe;^[[MCd;^[[MCd<^[[MCe<^[[MCe=^[[MCf=^[[MCf<^[[MCe<^[[MCe;^[[MCd;^[[MCc;^[[MCc<^[[MCd<^[[MCe<^[[MCe;^[[MCe<^[[MCf<^[[MCf;^[[MCe;^[[MCd;^[[MCd<^[[MCe<^[[MCe=^[[MCf=^[[MCg=^[[MCh=^[[MCi>^[[MCj>^[[MCk>^[[MCl>^[[MCn?^[[MCpA^[[MCqB^[[MCrC^[[MCrD^[[MCsD^[[MCsE^[[MCtF^[[MCsG^[[MCrG^[[MCqG^[[MCpG^[[MCpH^[[MCmH^[[MCmG^[[MClG^[[MCgI^[[MCfI^[[MCeI^[[MC]I^[[MC\I^[[MC[I

解決策

独自の処理を行いたい場合は、raw モードで文字をキャプチャする必要があります。

これはうまくいくはずです: (コード構造を維持しようとしました)

require 'io/console'

at_exit { print "\e[?1003l" }

print "\e[?1003h"

STDIN.raw(intr: true) do # raw mode but allow ctrl-c
  loop do
    begin
      input = STDIN.read_nonblock(1024)
      p input: input
    rescue IO::EAGAINWaitReadable
      IO.select([STDIN])
      retry
    end
  end
end

出力:(マウスを動かしながら)

$ ruby mouse.rb
{:input=>"\e[MCYB"}
{:input=>"\e[MCZB"}
{:input=>"\e[MC[B"}
{:input=>"\e[MC\\A"}
{:input=>"\e[MC]A"}
{:input=>"\e[MC]B"}
{:input=>"\e[MC\\B"}

もちろん、そのマウス入力を有意義な方法で実際に処理する必要があります。