2023年2月 この範囲を時系列順で読む この範囲をファイルに出力する
自作のライブラリをincludeしてコンパイルするには少し面倒があります。
例えば、
製作するアプリケーションファイル test_inkey
メインのソースファイル test_inkey.c
ライブラリのソースファイル std_inkey.c
とした場合、
$ gcc -Wall -lm -o test_inkey -I ./ test_inkey.c std_inkey.c
などとコンソールからコマンドを打たないといけません。
Cのソースファイルが一つならVSCodeからショートカットで一発なんですけどね。
この場合、makefileを作っておけばmakeコマンドで一発です。
次がわかりやすい。
Makefile の書き方 (C 言語)
makeコマンドの使い方も丁寧に説明してくれています。
#C言語
例えば、
製作するアプリケーションファイル test_inkey
メインのソースファイル test_inkey.c
ライブラリのソースファイル std_inkey.c
とした場合、
$ gcc -Wall -lm -o test_inkey -I ./ test_inkey.c std_inkey.c
などとコンソールからコマンドを打たないといけません。
Cのソースファイルが一つならVSCodeからショートカットで一発なんですけどね。
この場合、makefileを作っておけばmakeコマンドで一発です。
次がわかりやすい。
Makefile の書き方 (C 言語)
makeコマンドの使い方も丁寧に説明してくれています。
#C言語
2023年1月 この範囲を時系列順で読む この範囲をファイルに出力する
てなわけで、socketの実験に入る下準備が整いました。Pythonで出来たことですからC言語(gcc)で出来ないことではないでしょう。
Art-Netエンジンを構成するにはFIFOというか循環配列というかリングキャッシュというかQueueも勉強しないとけません。例えば1024個の配列を作ったとして、これがリング状に繋がったイメージで使います。1024で折り返すカウンタを使って配列の基点位置を表すだけですが、カウンタの計算モジュールだけでもライブラリ化しないと面倒かなと。
これはDelayで必要な機能です。一定の時間間隔でDMXのデータを保存し続ければ配列サイズが許す範囲で過去情報を取り出せます。つまりDelayになります。受信毎の保存でないことが肝ですが、往年のテープエコーと要領は同じです。
C言語はボチボチ書けるようになってきましたし、先達の情報も読み取れるようになってきました。パッチマシンとしての完成は先としても、この閑散期にArt-Netエンジンだけでも完成させたいです。
あとは、コマンド入力と処理の方法も考えないといけません。
ルールマップに基づいた入力制限とか、入力されたコマンドや数値のスタック方法とか、それの表示とかです。入力値はコマンドと数値の文字列で処理関数に渡すつもりですが、それをどの様に解析して実行するかも案外難しい。
#[Art-Net] #C言語
Art-Netエンジンを構成するにはFIFOというか循環配列というかリングキャッシュというかQueueも勉強しないとけません。例えば1024個の配列を作ったとして、これがリング状に繋がったイメージで使います。1024で折り返すカウンタを使って配列の基点位置を表すだけですが、カウンタの計算モジュールだけでもライブラリ化しないと面倒かなと。
これはDelayで必要な機能です。一定の時間間隔でDMXのデータを保存し続ければ配列サイズが許す範囲で過去情報を取り出せます。つまりDelayになります。受信毎の保存でないことが肝ですが、往年のテープエコーと要領は同じです。
C言語はボチボチ書けるようになってきましたし、先達の情報も読み取れるようになってきました。パッチマシンとしての完成は先としても、この閑散期にArt-Netエンジンだけでも完成させたいです。
あとは、コマンド入力と処理の方法も考えないといけません。
ルールマップに基づいた入力制限とか、入力されたコマンドや数値のスタック方法とか、それの表示とかです。入力値はコマンドと数値の文字列で処理関数に渡すつもりですが、それをどの様に解析して実行するかも案外難しい。
#[Art-Net] #C言語
キー入力の処理を書き直してみました。
ファンクションキーやカーソルキーなどの複数バイトでキーコードを出すキーも扱えます。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言語
ファンクションキーやカーソルキーなどの複数バイトでキーコードを出すキーも扱えます。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言語
本業は当面の課題を一通りやっつけました。
追われる感じが無いって幸せ。
そんぢゃ今夜はC言語の勉強!!、といきたいところですが、ここ数日、朝から晩まで10時間以上図面書きをやりますと帰宅してからヤル気はおきません。今夜は脳味噌を休ませてアタマを切替えます。
明日と明後日は急ぎの要件が無いので、キー入力の実験、これまでに実験したネタの整理、socketによるArt-Net処理の実験に手を付けましょう。
キー入力はioctl.hとtermios.hを用いて設定しread()で読み込む方法を用いていますが、これってデバイスドライバを書く基本なんだそうです。
宛先を設定し、read()で取り込み、write()で送り出すという極めてシンプルな構成ですが、合理主義者の神達がローレベルの制御に対し案件毎に違ったやり方を定義するとは思えませんので納得です。
肝心なところは先達のサンプルプログラムをコピペしただけなので何が何だかよくわかりませんが、デバイスドライバが書けたら守備範囲が広くなりそうです。
#C言語
追われる感じが無いって幸せ。
そんぢゃ今夜はC言語の勉強!!、といきたいところですが、ここ数日、朝から晩まで10時間以上図面書きをやりますと帰宅してからヤル気はおきません。今夜は脳味噌を休ませてアタマを切替えます。
明日と明後日は急ぎの要件が無いので、キー入力の実験、これまでに実験したネタの整理、socketによるArt-Net処理の実験に手を付けましょう。
キー入力はioctl.hとtermios.hを用いて設定しread()で読み込む方法を用いていますが、これってデバイスドライバを書く基本なんだそうです。
宛先を設定し、read()で取り込み、write()で送り出すという極めてシンプルな構成ですが、合理主義者の神達がローレベルの制御に対し案件毎に違ったやり方を定義するとは思えませんので納得です。
肝心なところは先達のサンプルプログラムをコピペしただけなので何が何だかよくわかりませんが、デバイスドライバが書けたら守備範囲が広くなりそうです。
#C言語
キー入力はファンクションキーや矢印キーが複数バイトのデータなので、先のプログラムではこれらを正しく判別出来ません。0x1Bを得てもESCキーなのか矢印キーなのか区別できないからです。
これらのデータが一度にキャッシュに入って連続した読み出しとなる(タイムアウト無しに連続して読み出せる)ならいいですが、そうでないと面倒です。いずれにしてもこの辺りの挙動をチェックする必要があります。今は1バイトのcharとして処理していますが、8バイトくらいのchar配列、つまり文字列として処理することになりそうです。
#C言語
これらのデータが一度にキャッシュに入って連続した読み出しとなる(タイムアウト無しに連続して読み出せる)ならいいですが、そうでないと面倒です。いずれにしてもこの辺りの挙動をチェックする必要があります。今は1バイトのcharとして処理していますが、8バイトくらいのchar配列、つまり文字列として処理することになりそうです。
#C言語
キー入力の処理が出来ましたが、願わくば押し下げと解放も拾いたいところではあります。
ですが、ハードウェアが汎用ですし、キーボードが押されたことを検知しているのではなく、入力された文字コードを得る処理ですので、これ以上細かいことはもう少し勉強しないとダメかな。
socketの検証は場所も時間も潤沢に必要なので、もう少し本業を終わらせないと手を付けられませんが、キー入力によるコマンド処理をどうしようか考えています。
入力されたアルファベットをコマンドショートカットとし、数字は数字として扱うことを基本にしようと思います。キーに対して処理を割り付けるのではなく、得たASCIIテキストに対して処理を割り付ければ標準入力だろうがUARTだろうがsocketだろうが何でも良く、ハードウェアに合わせた入口さえ作ればその先の処理は同じでいけます。こうするのは専用入力端末が壊れた時に汎用のキーボードでも対応したいからです。
では、得たASCIIテキストをどう処理するかです。
入力されたASCIIテキストは文字列として普通に扱えばいいでしょう。画面にコマンド列を表示したいならそれを変換すればいいし、コマンドを実行するなら関数に文字列として投げればいい。
せめて、入力に制限をかけてありえない文字列にならないようにした方がいいでしょう。「コレの後にありえる入力はコレ」といった制限です。一種のルールマップを作り、条件に合致しない入力は応答しないかエラーを出すのです。数値に最大値の制限もかけたいですが、これはコマンド実行関数側の仕事であって入力時にチェックするものでもないかな?
#[Art-Net] #C言語
ですが、ハードウェアが汎用ですし、キーボードが押されたことを検知しているのではなく、入力された文字コードを得る処理ですので、これ以上細かいことはもう少し勉強しないとダメかな。
socketの検証は場所も時間も潤沢に必要なので、もう少し本業を終わらせないと手を付けられませんが、キー入力によるコマンド処理をどうしようか考えています。
入力されたアルファベットをコマンドショートカットとし、数字は数字として扱うことを基本にしようと思います。キーに対して処理を割り付けるのではなく、得たASCIIテキストに対して処理を割り付ければ標準入力だろうがUARTだろうがsocketだろうが何でも良く、ハードウェアに合わせた入口さえ作ればその先の処理は同じでいけます。こうするのは専用入力端末が壊れた時に汎用のキーボードでも対応したいからです。
では、得たASCIIテキストをどう処理するかです。
入力されたASCIIテキストは文字列として普通に扱えばいいでしょう。画面にコマンド列を表示したいならそれを変換すればいいし、コマンドを実行するなら関数に文字列として投げればいい。
せめて、入力に制限をかけてありえない文字列にならないようにした方がいいでしょう。「コレの後にありえる入力はコレ」といった制限です。一種のルールマップを作り、条件に合致しない入力は応答しないかエラーを出すのです。数値に最大値の制限もかけたいですが、これはコマンド実行関数側の仕事であって入力時にチェックするものでもないかな?
#[Art-Net] #C言語
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言語
棚上げしていた本業に取り組んでおりますが、ここ10日ほどは残業することも精神的に追われることもない日常を送っているので心身共に回復しております。
VSCodeのオカゲですが、Pythonと同じ感覚でC言語を使えています。覚えなきゃならないことはまだまだ沢山ありますが、実行ファイルの完成まで1クリックですし、コンパイルエラーの説明がとても丁寧でわかりやすいし、コンパイルの時間もPythonのインタプリタ翻訳と大差ありませんので、方言違いのPythonを書いている様な使い勝手です。相当昔の記憶によりますが、リンカーの処理を勝手にやってくれるだけでも楽ってもんです。
C言語の取り扱いにはローレベルのコンピュータの知識と摩訶不思議な記述の習得が不可欠ですが、慣れてくるとPythonよりも書いてて楽しいですね。コンピュータを動かしているって実感がPICのアセンブラ並に強いからでしょうか。何をしているかも所要時間もわからない便利なだけのコマンドでハードウェアの制御をするとすべてが遠くに思えて面白くありません。この感覚が変人たる所以かもしれませんが、私の基本はハードウェアを直に動かすPICのアセンブラなんだと改めて思います。
この先数日は棚上げしていた本業(某劇場の舞台資料)の仕上げをせねばなりませんが、これが終われば半月くらいは自由研究に没頭できそうです。新規のご相談も頂いているので進捗次第ではわかりませんが。。。
もちろん、息抜きにC言語の学習と試作はしますよ。
#C言語
VSCodeのオカゲですが、Pythonと同じ感覚でC言語を使えています。覚えなきゃならないことはまだまだ沢山ありますが、実行ファイルの完成まで1クリックですし、コンパイルエラーの説明がとても丁寧でわかりやすいし、コンパイルの時間もPythonのインタプリタ翻訳と大差ありませんので、方言違いのPythonを書いている様な使い勝手です。相当昔の記憶によりますが、リンカーの処理を勝手にやってくれるだけでも楽ってもんです。
C言語の取り扱いにはローレベルのコンピュータの知識と摩訶不思議な記述の習得が不可欠ですが、慣れてくるとPythonよりも書いてて楽しいですね。コンピュータを動かしているって実感がPICのアセンブラ並に強いからでしょうか。何をしているかも所要時間もわからない便利なだけのコマンドでハードウェアの制御をするとすべてが遠くに思えて面白くありません。この感覚が変人たる所以かもしれませんが、私の基本はハードウェアを直に動かすPICのアセンブラなんだと改めて思います。
この先数日は棚上げしていた本業(某劇場の舞台資料)の仕上げをせねばなりませんが、これが終われば半月くらいは自由研究に没頭できそうです。新規のご相談も頂いているので進捗次第ではわかりませんが。。。
もちろん、息抜きにC言語の学習と試作はしますよ。
#C言語
ダンプ表示のライブラリは出来ました。何の変哲もない16進数のカラム表示です。
データ配列のポインタ、表示データ数、列数、行数、最上行値のポインタなどを引数にして関数を呼ぶと表示します。
地味ですが、こういったライブラリがあると開発の効率が良くなるのでは?という想いで作ってみました。後でDMXデータを表示する画面を作る際にも同様のことをするので習作でもあります。
以下オレメモです。
ANSIエスケープシーケンスにおいてパラメータに変数を用いたい場合。
// カーソルをx=10,y=5にして文字列を表示する
#define ESC 0x1B
int main(void) {
int x = 10 ;
int y = 5 ;
printf("%c[%d:%dHカーソルを指定の位置に", ESC, x, y ) ;
return 0 ;
}
※ このブログシステムでは#や[が機能文字扱いなので、上記ではこれらを全角文字で書いています。
などとします。大昔のN-BASICの「LOCATE + PRINT」みたいなことをしています。
printfの文字列中のエスケープを\eと直で書くと後に続く%dが機能しませんが、#defineで定義したESC(0x1B)を%cで読み込めば機能します。
もちろん、printfのESCの位置に0x1Bと直書きしてもいいのですが、#defineした方が読みやすいと思います。
C言語ではこういった不思議な記述が多いです。正規表現的でもありますが、バイナリ的な指向が各所に出てきます。
アセンブラに慣れた身に違和感はありませんが、理解に苦しむ要素かもしれません。
#C言語
データ配列のポインタ、表示データ数、列数、行数、最上行値のポインタなどを引数にして関数を呼ぶと表示します。
地味ですが、こういったライブラリがあると開発の効率が良くなるのでは?という想いで作ってみました。後でDMXデータを表示する画面を作る際にも同様のことをするので習作でもあります。
以下オレメモです。
ANSIエスケープシーケンスにおいてパラメータに変数を用いたい場合。
// カーソルをx=10,y=5にして文字列を表示する
#define ESC 0x1B
int main(void) {
int x = 10 ;
int y = 5 ;
printf("%c[%d:%dHカーソルを指定の位置に", ESC, x, y ) ;
return 0 ;
}
※ このブログシステムでは#や[が機能文字扱いなので、上記ではこれらを全角文字で書いています。
などとします。大昔のN-BASICの「LOCATE + PRINT」みたいなことをしています。
printfの文字列中のエスケープを\eと直で書くと後に続く%dが機能しませんが、#defineで定義したESC(0x1B)を%cで読み込めば機能します。
もちろん、printfのESCの位置に0x1Bと直書きしてもいいのですが、#defineした方が読みやすいと思います。
C言語ではこういった不思議な記述が多いです。正規表現的でもありますが、バイナリ的な指向が各所に出てきます。
アセンブラに慣れた身に違和感はありませんが、理解に苦しむ要素かもしれません。
#C言語
本業では社内のサーバーやパソコンのメンテをしていますが、処理の待ち時間でC言語のお勉強。
時間についてです。
DMXを扱うには無受信1秒でタイムアウトしなければなりませんし、Delayを構成するには1/30~1/50秒程度のインターバルタイムで受信値をスタックしなければなりません。
ですので、msec(1/1000)クラスの時間評価が必要です。
システムが持つ時間情報を扱うには<time.h>を用い、エポック秒やPOSIX時間などと呼ばれる基準日時(1970年1月1日0時0分0秒(UTC))からの経過時間(secとnsec)を使うのがよいと思います。有効な精度は10usec程度の様ですが、今現在書いている処理では十分です。これを処理の節目でスタックして経過時間を評価するのですが、年月日時でパラメータが分かれているよりもトータル秒数の方が扱いが簡単です。23時59分59秒の1秒後に0時0分0秒にならないタイマで継続した時間評価をしたいのでこの方がいいと思います。
ただ、スタックする変数のバイト長には気を付けないといけません。secもnsecもそれぞれ4バイト数なので評価をするには8バイト数で考えないといけないからです。64bitOSならint型でも8バイト数なことが多いので問題ありませんが、私はRaspberryPiで32bitOSを使っているのでint型は4バイト数になります。対策には8バイト数である「(unsigned) long long int」型を使います。ちょっとクセがある型なので計算やprintfで注意しないといけませんケドね。
#C言語
時間についてです。
DMXを扱うには無受信1秒でタイムアウトしなければなりませんし、Delayを構成するには1/30~1/50秒程度のインターバルタイムで受信値をスタックしなければなりません。
ですので、msec(1/1000)クラスの時間評価が必要です。
システムが持つ時間情報を扱うには<time.h>を用い、エポック秒やPOSIX時間などと呼ばれる基準日時(1970年1月1日0時0分0秒(UTC))からの経過時間(secとnsec)を使うのがよいと思います。有効な精度は10usec程度の様ですが、今現在書いている処理では十分です。これを処理の節目でスタックして経過時間を評価するのですが、年月日時でパラメータが分かれているよりもトータル秒数の方が扱いが簡単です。23時59分59秒の1秒後に0時0分0秒にならないタイマで継続した時間評価をしたいのでこの方がいいと思います。
ただ、スタックする変数のバイト長には気を付けないといけません。secもnsecもそれぞれ4バイト数なので評価をするには8バイト数で考えないといけないからです。64bitOSならint型でも8バイト数なことが多いので問題ありませんが、私はRaspberryPiで32bitOSを使っているのでint型は4バイト数になります。対策には8バイト数である「(unsigned) long long int」型を使います。ちょっとクセがある型なので計算やprintfで注意しないといけませんケドね。
#C言語
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105