開発

[binllion] rawモード

crateの追加

では端末を制御してrawモードにするためにcrateを使うようにしましょう。このような機能を提供するcrateは複数ありますが今回はcrosstermを使用します。

下記のcargo addコマンド1を実行してみましょう。

このコマンドでCargo.tomlの[dependencies]セクションがアップデートされてcrosstermの情報が追加されます。

$ cargo add crossterm
    Updating crates.io index
      Adding crossterm v0.27.0 to dependencies.
             Features:
             + bracketed-paste
             + events
             + windows
             - event-stream
             - filedescriptor
             - serde
             - use-dev-tty
    Updating crates.io indexCode language: PHP (php)

crateの探し方

crates.ioというサイトでcrateを収集しています。こちらでキーワードを使って検索し目的のcrateを見つけることができると思います。

類似サイトにLib.rsAwesome Rustなどがあります。

cargo search コマンドでcrate名が検索結果に表示されれば使用可能と思われます。

その他、cargoには様々な機能があります。こちらThe Cargo Bookを参照してください。

$ cargo search crossterm
crossterm = "0.27.0"            # A crossplatform terminal library for manipulating terminals.
crossterm_utils = "0.4.0"       # Common logic used by the crossterm crates.
crossterm_style = "0.5.2"       # A cross-platform library styling the terminal output.
crossterm_cursor = "0.4.0"      # A cross-platform library for moving the terminal cursor.
crossterm_input = "0.5.0"       # A cross-platform library for reading userinput.
crossterm_screen = "0.3.2"      # A cross-platform library for raw and alternate screen.
crossterm_terminal = "0.3.2"    # A cross-platform library for doing terminal related actions.
ibox-crossterm = "0.23.2"       # A fork of crossterm made for ibox to get the stderr position of the cursor. NOT FOR GENERAL USE…
crosstermion = "0.14.0"         # utilities for `crossterm`, without ties to `termion`
bevy_crossterm = "0.4.0"        # Develop terminal games with crossterm and Bevy
... and 193 crates more (use --limit N to see more)Code language: PHP (php)

crosstermについて

crosstermの機能2についてざっくりと図に表すと下記にようになるかと思います。

使い方としては現在表示されているメイン画面とは別の画面に切り替えることができます。

別画面に切り替えてアプリケーションの画面描画をバッファを通じて行います。

rawモードの制御、キー入力などのイベント処理、カーソル処理、スタイリングしたテキストの表示などでアプリケーションの画面を作っていくことができそうです。

rawモードへGo!

これで準備ができました。

crosstermを使ったコードにしてみましょう。cargo buildすると新しくcrosstermもコンパイルされている様子が表示されると思います。

cargo runして、これを実行すると今度はキー入力がすぐに出力されるようになります。

// raw_mode

// 標準ライブラリの読み込み
use std::io::{self, Read};
// crosstermの読み込み
use crossterm::terminal::disable_raw_mode;
use crossterm::terminal::enable_raw_mode;

// main 関数
fn main() {
    // raw モードに移行
    enable_raw_mode().unwrap();

    // STDINからbyte列を読み込み
    for input_byte in io::stdin().bytes() {
        // 読み込んだbyteをcharに変換して束縛
        let output_char = input_byte.unwrap() as char;

        // 読み込んだ値を出力
        println!("{}", output_char);

        // qが入力されたらbreak
        if output_char == 'q' {
            // raw モードを解除
            disable_raw_mode().unwrap();
            break;
        }
    }
}Code language: PHP (php)

キー入力の表示

rawモードでの入力がどのように機能するかを知るために、読み取った各バイトを出力してみよう。

各文字のバイナリ表現、数値のASCII値、そして印字可能な文字であればその文字を表示します。

これを行うには、main()を以下のように変更します

// keypresses

// 標準ライブラリの読み込み
use std::io::{self, Read};
// crosstermの読み込み
use crossterm::terminal::disable_raw_mode;
use crossterm::terminal::enable_raw_mode;

// main 関数
fn main() {
    // raw モードに移行
    enable_raw_mode().unwrap();

    // STDINからbyte列を読み込み
    for input_byte in io::stdin().bytes() {
        // シャドーイング
        let input_byte = input_byte.unwrap();
        // 読み込んだbyteをcharに変換して束縛
        let output_char = input_byte as char;

        // 読み込んだ値を出力
        if output_char.is_control() {
            println!("Binary: {0:08b} ASCII: {0:#03} \r", input_byte);
        } else {
            println!(
                "Binary: {0:08b} ASCII: {0:#03} Character: {1:#?}\r",
                input_byte, output_char
            );
        }

        // qが入力されたらbreak
        if output_char == 'q' {
            // raw モードを解除
            disable_raw_mode().unwrap();
            break;
        }
    }
}Code language: PHP (php)

実行例はこのようになります。

Characterが表示されていないものがありますが、これは

Binary: 00000011 ASCII: 003はCtrl+C
Binary: 00000100 ASCII: 004はCtrl+D

を押したケースになります。

Binary: 01100001 ASCII: 097 Character: 'a'
Binary: 01100010 ASCII: 098 Character: 'b'
Binary: 01100011 ASCII: 099 Character: 'c'
Binary: 00000011 ASCII: 003
Binary: 00000100 ASCII: 004
Binary: 01110001 ASCII: 113 Character: 'q'Code language: HTTP (http)

ここまでのまとめ

さて、新しいプログラムを使っていくつかのことを学びました。

canonicalモードはタイプライターテレタイプ端末のような動作をしていました。rawモードではこのような動きではなく即座にキーが押されると結果が出力されました。

コンピューター内部では、文字は単なる数字であり、数字はメモリ上では0と1(これを「ビット」と呼ぶ)で表現されます(2進数)。例えば、メモリのどこかに01100001があるとします(8ビットをByteと呼ぶので、これは1Byteです)。

2進数を10進数に変換するのは簡単で、01100001を10進数に変換すると97という数字になります。このような数値を文字にマッピングする規格が存在します。(ASCIIコード

新しいプログラムにaを入力すると、a、097、01100001の3つが出力されます。

キーボードから読み取ったバイナリを文字として扱うようプログラムに簡単に教えることができるのもこのためです。

基本的に、私たちはコンピューターに「この数字を文字として扱ってください」と伝えただけなのです。

ASCIIコード0~31は制御文字と呼ばれるすべて印刷不可能な文字で(ベル、キャリッジ・リターン、改行もこの中にある)、127は制御文字です。

したがって、is_control()は単純に入力値がこの範囲かどうかを調べることになります。

再度プログラムを実行してEscapeキーやEnterキー、Homeキーなど様々なキー入力を試してみましょう。


  1. このコマンドはRust 1.62.0より使えるようになったようです。筆者は直接Cargo.tomlを編集するやり方が好みです。 ↩︎
  2. crosstermの説明についてはこちらが参考になります。 ↩︎

GitHubにコードをアップロードしています。0.1.0バージョン

コードのコメントに書かれているfirst_stepなどをcargoコマンドに渡すと実行できます。

# Example
$ cargo run --example first_stepCode language: Bash (bash)
管理人

Recent Posts

情報セキュリティマネジメント試験取得への道

スキルアップを図るべく情報セキ…

2か月 ago

ファイナンシャルプランナー3級試験取得への道

スキルアップを図るべくファイナ…

2か月 ago

[rust] New Type Patternを使ってみる

DDDの考えを取り入れることで…

5か月 ago

RustでDDDの要素を取り入れてみる

前回SOLID原則というものを…

5か月 ago

期間限定!書籍無料キャンペーン2025

「mdBookではじめるKin…

5か月 ago