2023年1月27日の投稿[1件]
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言語
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言語