---
title: マウスでターミナルにお絵描きをしてみよう
tags: 
author: [murasuke](https://docswell.com/user/4962106)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/37K99NX47D.jpg?width=480
description: マウスでターミナルにお絵描きをしてみよう by murasuke
published: May 01, 26
canonical: https://docswell.com/s/4962106/K4NEQQ-2026-05-01-080540
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/37K99NX47D.jpg)

マウスでターミナルにお絵描きをし
てみよう
～ ちょっとだけターミナルの仕組みも ～
2025/06/09 俺たちの勉強会 #2
#orestudy


# Page. 2

![Page Image](https://bcdn.docswell.com/page/LJ3WWV5DJ5.jpg)

自己紹介：murasuke
・株式会社 ツールラボ 開発部 所属
PHPで自社Webサービスの開発をやっています
開発環境のモダン化(React+Tailwind)が最近の楽しみです
・20年ほどSEやってましたが、プログラムを書く仕事が少なくなり(年齢的に？）
さみしくなってきたので・・・
・今日が初LTです、よろしくお願いします
・今週末、社員旅行の自由時間に、(吞みながら)勝手にLT大会やります！


# Page. 3

![Page Image](https://bcdn.docswell.com/page/8JDKK8D2EG.jpg)

なぜこんなことをやってみようと思ったのか？
「Windows TerminalがSixel画像表示をサポート」でターミナルは、
文字だけではなく、画像も表示できることを知りました
ターミナルで可能な機能
●
●
●
●
文字の装飾（太字、文字の色の指定、二重下線をひく）
画像の表示（正確にはピクセル単位の出力）
任意の位置への文字出力
マウスイベントの通知など
このなかで「マウスイベントを受け取る」、「任意の位置への文字出力」
に着目し、お絵描きプログラム作れたら面白いじゃん！と思った次第です


# Page. 4

![Page Image](https://bcdn.docswell.com/page/VEPKK8XL78.jpg)

ちょっと横道① ターミナルエミュレーターの歴史
テレタイプ端末：
タイプライターに電話回線を
つなげたような機械
キー入力すると即時で相手側にも
印字される
コンピューターの入出力にも使わ
れるようになる
Jamie - Flickr: Telex machine TTY
https://commons.wikimedia.org/w/index.php?curid=19282428


# Page. 5

![Page Image](https://bcdn.docswell.com/page/27VVVNL67Q.jpg)

ちょっと横道① ターミナルエミュレーターの歴史
ビデオ端末 (VT100など)：
ディスプレイに文字を表示するようになった端末
・端末はキーを押しても即時で
画面には表示されない。
接続先のコンピューターから
エコーバックを受けて
その文字を表示している
・エスケープシーケンスによる
表示の制御、文字装飾も
できるようになった


# Page. 6

![Page Image](https://bcdn.docswell.com/page/5JGLLKX27L.jpg)

ちょっと横道① ターミナルエミュレーターの歴史
ビデオ端末 (VT100など)：
ビデオ端末はアプリと直接接続されません。カーネル内にある、
ラインディシプリンが中継し、表示や行単位の編集を受け持っています
行編集の例：「123」を「124」と間違えたので、1文字消して「3」を入力すると
ラインディシプリンから「124(BS)(Space)(BS)3」とエコーバックされます
⇒
「カーソルを戻す(BS)」＋「空白で上書き」＋「カーソルを戻す」という
３つのアクションが行われることで画面から１文字消去する仕組みになっています


# Page. 7

![Page Image](https://bcdn.docswell.com/page/47QYYN89EP.jpg)

ちょっと横道② ターミナルエミュレーターとTTY(PTY)
ターミナルエミュレーター：
VT100などの振る舞いをソフトウェアで再現したもの
カーネル(ラインディシプリン)は簡易的な
(行単位の)編集しか行えません
viやシェルといった高機能なアプリはRawモード
に切り替え、アプリ自体でエコーバックを行うこと
で高度な編集が可能になります
お絵かきシェルスクリプトでは、マウスのイベントを随時処理するため、
Rawモードを使います


# Page. 8

![Page Image](https://bcdn.docswell.com/page/KE4WWG9RJ1.jpg)

ここまでのまとめ
●
ターミナルは、キー押下を接続先に送信し「エコー」を表示している
（自身で「キー入力」を表示しているわけではない）
●
マウスもイベントとしてアプリへ送信される
（要：エスケープシーケンスでモード切替え）
●
ラインディシプリンがバッファリング(行単位の編集)を行っている。
アプリ側で制御するにはRawモードに変更する必要がある


# Page. 9

![Page Image](https://bcdn.docswell.com/page/L71YYDKVJG.jpg)

お絵描きシェルスクリプト全体の流れ
● 初期設定(Rawモードへの切替、マウスイベントの有効化)
● メインループ:
イベントの受信、ボタンイベントの判断、描画
a. イベントのデータ解析: 座標・ボタン判定
b. 描画: エスケープシーケンスで任意の位置に文字／色を出力
c. ターミナル下部にマウスの位置情報を表示する


# Page. 10

![Page Image](https://bcdn.docswell.com/page/G7WGGYD3E2.jpg)

１. Rawモードへの切替、マウスイベントの有効化
マウスイベント有効にするため、エスケープシーケンスを送ります
※ `\033`は`ESC`の8進表記です(`\e`や`\x1b`と書いても同じ)
stty raw -echo # ターミナルrawモード &amp; echoオフ
echo -ne &quot;\033[?1003h&quot; # ボタン状態に関係なく、マウスが動くたびにイベントを送信
echo -ne &quot;\033[?1006h&quot; # SGR形式のイベント通知(座標)有効化


# Page. 11

![Page Image](https://bcdn.docswell.com/page/4JZLLXGGE3.jpg)

２.メインループ: イベントの受信、ボタンイベントの判断
イベントを読み込み(read)、マウスイベント以外を無視
● ESC：0x1B (エスケープシーケンス開始)
● [&lt;：マウスレポート開始(固定) ・・・この場合だけ次の処理へ
while true; do
IFS= read -rsn1 char
# ESC(0x1B,033) の場合、マウスイベントかどうかをチェック
if [[ $char == $&#039;\033&#039; ]]; then
# 続く2文字を読み取る
IFS= read -rsn2 seq
if [[ $seq == &quot;[&lt;&quot; ]]; then
# マウスイベントを処理する


# Page. 12

![Page Image](https://bcdn.docswell.com/page/YE6WW4YQEV.jpg)

ターミナルからのイベントを受信(読み取り)
入力されたデータを読み取り、変数(char)にセットします
IFS= read -rsn1 char
# 押されたキーを表示
echo $char
IFS=
フィールド区切り文字(Internal Field Separator)を空にして、読み取り時に分割されるのを防ぐ
read
入力を読み取って変数に代入するBashのビルトインコマンド
-r
バックスラッシュ（\）を特別扱いせず、そのまま読み取る
-s
入力をエコーしない（画面に表示されないようにする）
-n1
1文字だけ読み取る。n1 の代わりに n3 にすれば3文字読み取りになる
char
読み取った文字を代入する変数名


# Page. 13

![Page Image](https://bcdn.docswell.com/page/GE5MMQG2E4.jpg)

ａ.イベントのデータ解析: 座標・ボタン判定
ボタンコード(b)、座標(x,y)、ボタンの押下状況(M) を変数にセットします
⇒イベントのデータ形式(ESC[&lt;b;x;yM)は次ページで
# マウスイベントデータを読み出す
IFS= read -rsn15 mouse_data
# 最期の(M|m)を取り除く
mouse_data=${mouse_data%%[Mm]*}
# ; で分割して 変数 b, x, y にセット
IFS=&#039;;&#039; read -r b x y &lt;&lt;&lt; &quot;$mouse_data&quot;


# Page. 14

![Page Image](https://bcdn.docswell.com/page/97299PY1JR.jpg)

マウスイベントデータ形式
# ESC [ &lt; b ; x ; y M
\x1b[&lt;b;x;yM
●
●
●
●
●
●
カンマ(;)で分割して、値を取り出します
ESC：0x1B (エスケープシーケンス開始)
[&lt;：マウスレポート開始(固定)
b：ボタンコード
x：列番号
y：行番号
M ：M ボタンを押下、m ボタンをリリース
b: ボタンコード一覧
0: 左ボタン押下
2: 右ボタン押下
32: 左ボタンドラッグ中
34: 右ボタンドラッグ中
# 例えば、10列(x)、20行(y)の位置で左(0)クリック(M)した場合、以下のデータを受け取る
# ボタン0 → &lt;b&gt; = 0
# x=10 → &lt;x&gt; = 10
# y=20 → &lt;y&gt; = 20
# ボタン押下 → M
echo -ne &quot;\x1b[&lt;0;10;20M&quot;


# Page. 15

![Page Image](https://bcdn.docswell.com/page/DJY445G47M.jpg)

ｂ.描画: エスケープシーケンスで任意の位置に文字／色を出力
マウスの位置に文字を出力するため、エスケープシーケンス(\033[行;列H)を使います
# ボタン判定
case $b in
0 | 32) # 左ボタンクリック、ドラッグしている場合
# (x, y) に 赤色で&quot;*&quot; を描画
printf &quot;\033[31m\033[%d;%dH*\033[0m&quot; &quot;$y&quot; &quot;$x&quot;
;;
2 | 34) # 右ボタンクリック、ドラッグしている場合
# (x, y) に 黄色で&quot;@&quot; を描画
printf &quot;\033[1;33m\033[%d;%dH@\033[0m&quot; &quot;$y&quot; &quot;$x&quot;
;;
esac


# Page. 16

![Page Image](https://bcdn.docswell.com/page/V7NYYNG6E8.jpg)

ｃ.ターミナル下部にマウスの位置情報を表示する
画面の左下にマウスの情報(座標)を表示します
# ターミナル下部にマウスの位置情報を表示する
printf &quot;\033[999;1H&quot; # 画面の下へ移動
echo -n &quot;button=$b, x=$x, y=$y &quot;


# Page. 17

![Page Image](https://bcdn.docswell.com/page/YJ9PPRWR73.jpg)

デモ／結果イメージ
時間があれば実際に
お絵描きしてみます


# Page. 18

![Page Image](https://bcdn.docswell.com/page/GJ8DDWNGJD.jpg)

まとめ
今後やってみたいこと
● 文字単位の座標ではなく、ピクセル単位でも座標が取得できる(環境依存)
● 文字単位ではなく、ピクセル単位で描画もできる
⇒ もっと精密な絵を描くことができるので、いつかやりたい
ご清聴ありがとうございました


