🗐 電装工芸日記 - 舞台照明機器の製作とか -

能登半島地震で被災された方々にお見舞い申し上げます。

or 管理画面へ

2023年1月27日の投稿[1件]

Icon of admin
 ANSI-CというかLinuxのC言語ではキー入力は「待ち」が基本です。何かキーが入力されるとか、Enterを押されるまで処理が一時停止します。それでも構わないこともありますが、ダメな時もあります。
 WindowsならDxLib.hを使って様々な入力を直に得ることが出来ますが、ANSI-Cの標準ライブラリには同様の物がありません。
 そこで、キー入力があれば受け付け、無いなら飛ばして次に行く処理を作ってみました。キー入力はOSがバッファしているので、これを覗く形です。

Raspberry Pi 4B / Rasbian11_32bit(blueseye) / OS標準gcc
/* ---------------------------------
  リアルタイムにキー入力をチェックする
  --------------------------------- */
/* getcharはキー入力のキャッシュが空だと入力があるまで待つ。
  キャッシュが無いなら無いでそのまま抜けたいが、タイムアウトを設定しても
  抜けない。環境に寄るのかもしれないが、ioctl.hを用いることで
  タイムアウトが実現できた。
*/

/* ライブラリ読み込み */
#include <stdio.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>

/* 定数設定 */
#define SET 1        // ioctl.readのモードをRawにするフラグ
#define RESET 0       // ipctl.readのモードをCanonicalに戻すフラグ

/* プロトタイプ宣言 */
int get_inkey( char *get_char ) ;  // キー入力を取得する関数
  // char *get_char  取得したキーコードを返すポインタ
int set_inkey( int mode_flag ) ;  // キー入力のモードを設定する関数
  // int mode_flag  モードの設定フラグ 1(SET)=Rawモードにする / 0(CLR)=モードを戻す

/* テスト用main */
int main(void) {
  char in_char = 0 ;   // 入力されたキーコードを保存する変数
  int ret_inkey = 0 ;   // get_inkeyからの戻り値を保存する変数
  /* コンソールの設定を初期化 */
  set_inkey( SET ) ;
  /* 無限ループで入力したキーを表示する */
  for(;;) {
    // get_inkeyから1文字取得
    in_char = 0x00 ;          // 事前初期化
    ret_inkey = get_inkey( &in_char ) ; // キー取得
    /* get_inkeyの戻り値を処理 */
    // 戻り値が-1ならエラーなのでループから抜けて終了
    if( ret_inkey == -1 ) {
      break ;
    // エンターキーが押されたならループを抜けて終了
    } else if( in_char == 0x0A ) {
      break ;
    // 入力された文字を出力する・・・表示可能な文字
    } else if( in_char >= 0x20 && in_char <= 0x7F ) {
      printf( " %c<%02X>", in_char, in_char ) ;
    // 入力された文字を出力する・・・表示不可能な文字
    } else if( in_char > 0x00 && in_char <= 0xFF ) {
      printf( " <%02X>", in_char ) ;
    }
    fflush( stdout ) ; // 画面表示のスタックを吐き出す   
  }
  /* 正常終了 */
  printf( "\n" ) ;    // 念のための改行
  set_inkey( RESET ) ;  // コンソールの設定を元に戻す
  return 0 ;
}

/* キー入力を取得する */
int get_inkey( char *get_char) {
  char in_char = 0 ;     // 入力されたキーを保持
  char read_length = 0 ;   // 読み込んだバイト数

  // readを使って標準入力から1文字取得
  read_length = read( 0, &in_char, 1 ) ;
  // read_lengthが-1ならエラー、それ以外なら正常
  if( read_length != -1 ) {  // 正常なので取得値を返す
    *get_char = in_char ;
  } else {          // エラーなので以上通知
    *get_char = 0x00 ;
    set_inkey( RESET ) ;
    return -1 ;
  }
  return 0;
}

/* キー入力のモードを設定する */
int set_inkey( int mode_flag ) {
  static struct termio tty_backup;    // 変更前の設定を保持
  static struct termio tty_change;    // 変更後の設定を保持
  // モードを設定する
  if( mode_flag == SET ) {
    // 現在の設定をスタック
    ioctl(0, TCGETA, &tty_backup);
    tty_change = tty_backup;
    // 設定を変更
    tty_change.c_lflag &= ~( ECHO | ICANON ) ;   // エコーを止め、RAW モードへ変更
    tty_change.c_cc[VMIN]= 0 ;          // 0文字入力された時点で入力を受け取る
    tty_change.c_cc[VTIME] = 0.01 ;        // 何も入力がなくても1msec待つ (1 = 1/10sec)
    ioctl( 0, TCSETAF, &tty_change ) ;       // ここで設定を反映
  // モードを戻す
  } else if( mode_flag == 0 ) {
    // スタックしていた設定に戻す
    ioctl( 0, TCSETAF, &tty_backup ) ;
  }
  return 0;
}


 同じ感じでgetcharを使ってもいいような情報が多かったのですが、RaspberryPiのblueseyeではこの方法でないとキー入力があるまで一時停止してしまいます。
 Pythonでも同様の処理を書いたことがありますが、C言語の方が圧倒的にレスポンスがいいです。
 矢印キーがANSIエスケープシーケンスのコードで取得できたことには驚きましたが、ファンクションキーも含め、getcharでは取得できないこともあるキーも取得出来て満足。
 ただし、シフトキーなどの装飾キーを単体で取得することは出来ません。

#C言語

■当面の課題

桜のライトアップの季節です。花粉症の季節でもあります。
自分は平気ですが、花粉症の部下は死にそうな顔をしています。

編集

■全文検索:

複合検索窓に切り替える

■複合検索:

  • 投稿者名:
  • 投稿年月:
  • #タグ:
  • カテゴリ:
  • 出力順序:

■日付検索:

■カレンダー:

2023年1月
1234567
891011121314
15161718192021
22232425262728
293031

■カテゴリ:

■最近の投稿:

最終更新日時:
2024年4月28日(日) 10時15分49秒