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

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

or 管理画面へ

No.491

Icon of admin
 キー入力の処理を書き直してみました。
 ファンクションキーやカーソルキーなどの複数バイトでキーコードを出すキーも扱えます。shiftやAltなどの装飾キーは拾えませんケド。
 入力の待ち時間はありません。get_inkey()はすぐ戻ってきます。戻り値はキーコードの長さを表しますが、0なら入力が無かったと判別出来ます。
 得られるのは標準入力からのASCIIコードです。read()はキャッシュを読むだけでタイミング次第では取得値に複数のキーコードが混じることがありますが、これをキー単体のコードとして仕分けています。キャッシュの一番前のキーコードだけを取り出す単純な仕分けで、想定外のキーコードは読み飛ばしていますが十分でした。
 キーコードで何かをするにはswitch文やif文でそのコードを仕分ける必要があります。
 正常に動かすなら、100msec(1/10秒)毎以下でget_inkey()を読みに行く必要があります。
 定数設定とプロトタイプ宣言をヘッダーファイルに書けばincludeしてライブラリとして使えます。mainはコメント化して使った方がいいですけど。

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

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

/* 定数設定 */
#define SET 1        // ioctl.readのモードをRawにするフラグ
#define RESET 0       // ipctl.readのモードをCanonicalに戻すフラグ
#define LENGTH_STACK 32   // キー入力のスタック長 状況によるが、長めにしないと取り出しが半端になる
#define ESC 0x1B      // ASCIIコード ESC
#define BRACKET 0x5B    // ASCIIコード [

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

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

/* キー入力を取得する */
int get_inkey( char *get_char) {
  // char *get_char                  // キーコードを返すためのchar配列のポインタ
  int i ;                       // for用変数
  char in_char[ LENGTH_STACK ] = { 0x00 };      // 入力されたキーを取得
  int read_length ;                  // read()戻り値 キーコードのバイト数
  int put_length = 0 ;                // 戻り値 キーコードのバイト数

  /* get_charの初期化 */
  for( i = 0; i < LENGTH_STACK; i++ ) {        // すべての値を0x00にする
    get_char[ i ] = 0x00 ;
  }
  /* キー入力を読み取る */
  read_length = read( 0, &in_char, LENGTH_STACK ) ;  // read_lengthに取得バイト数、in_charにキーコードを得る

  /* 取得値の仕分け */
  if( read_length < 0 ) {               // 取得エラーなのでエラーを返して終了
    return -1 ;
  } else if( read_length == 0 ) {           // キーコードを得られなかったのでゼロを返す
    return 0 ;
  } else if( read_length == 1             // 取得が1バイトか先頭バイトがESC(0x1B)以外なら先頭バイトを返す
       || in_char[ 0 ] != ESC ) {
    get_char[ 0 ] = in_char[ 0 ] ;         // 値コピー
    return 1 ;
  } else if( read_length > 2             // 3バイト以上で先頭2バイトが0x1B(ESC)、0x5B([)なら
       && in_char[ 0 ] == ESC          // ファンクションキーまたはカーソルキー
       && in_char[ 1 ] == BRACKET ) {
    get_char[ 0 ] = ESC ;              // 0バイト目
    get_char[ 1 ] = BRACKET ;            // 1バイト目
    put_length = 2;                 // 戻り値設定
    for( i = 2; i < read_length; i++ ) {      // 2バイト目以降をコピー
      if( in_char[ i ] == ESC ) break ;      // 末尾にESCがあれば終了
        get_char[ i ] = in_char[ i ] ;     // 値コピー
        put_length++ ;             // 戻り値インクリメント
    }
    return put_length ;               // 戻り値送って終了
  }
  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;
}

 ※ このブログシステムでは#や[が機能文字扱いなので、上記ではこれらを全角文字で書いています。

#C言語

■当面の課題

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

編集

■複合検索:

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

■日付検索:

■カレンダー:

2023年1月
1234567
891011121314
15161718192021
22232425262728
293031

■カテゴリ:

■最近の投稿:

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