どのカテゴリにも属していない投稿[869件](38ページ目)
オレメモ
ANSIエスケープシーケンスにおいてカーソルの表示/非表示を定義。
RaspberryPi 4B / Rasbian11_32bit / コンパイラ:OS標準gcc
\e[?25h カーソルの表示
\e[?25l カーソルの非表示
相変わらずの不思議な呪文。
#C言語
ANSIエスケープシーケンスにおいてカーソルの表示/非表示を定義。
RaspberryPi 4B / Rasbian11_32bit / コンパイラ:OS標準gcc
\e[?25h カーソルの表示
\e[?25l カーソルの非表示
相変わらずの不思議な呪文。
#C言語
各所の処理タイミングで難儀しましたが、第1フェーズと呼んでもいい段階は終わりました。
まとまったのは次の通り。
1)Art-Netを受信してデコード。
2)エンコードして送信。
3)受信値を表示。
4)カーソルキーでユニバースを切り替えるなど、キーボードによる画面操作。
この程度ではありますが、これから先の基本が多く含まれているのでかなりの進歩だと自画自賛。
スッキリした画面に値がキレイに表示されると気持ち良いです。
ちなみに、画面表示を除く主処理1フェーズの所要時間は平均150usec、最大600usecくらいです(30fps:8ユニバースのArt-Netを受信/デコード/エンコード/送信する処理)。Pythonに比べたら圧倒的に速い。
今後機能を追加してどれくらい重くなっていくのかは未知数ですが、現時点では期待値込みの次第点でしょう。
次はデータ処理の本丸です。
Pythonである程度作れた処理ですが、Pythonならではの便利機能が使えないなら私にとってかなり難しいことなので、処理の手順をキッチリ考えたいと思います。
追記
処理タイミングを調整したところ1フェーズの処理時間が50usec程度減りました。小さな数値ですが比率としては大きい。
不思議なのは、受信している8ユニバース中、0ユニバースに比べ7ユニバースの処理が倍くらい速く処理時間の変動が少ないことです。出力のフレームレートをDoctorMXで計測すると同じ値なので謎です。今は動いているバグですと後々問題になりそうそうなので確認しますが、速いユニバースは1フェーズ90usec前後で安定して動いているのでこれが平均になれば御の字です。この数値で揃えば現在考えている最大規模も処理出来ると思います。
#[Art-Net]
まとまったのは次の通り。
1)Art-Netを受信してデコード。
2)エンコードして送信。
3)受信値を表示。
4)カーソルキーでユニバースを切り替えるなど、キーボードによる画面操作。
この程度ではありますが、これから先の基本が多く含まれているのでかなりの進歩だと自画自賛。
スッキリした画面に値がキレイに表示されると気持ち良いです。
ちなみに、画面表示を除く主処理1フェーズの所要時間は平均150usec、最大600usecくらいです(30fps:8ユニバースのArt-Netを受信/デコード/エンコード/送信する処理)。Pythonに比べたら圧倒的に速い。
今後機能を追加してどれくらい重くなっていくのかは未知数ですが、現時点では期待値込みの次第点でしょう。
次はデータ処理の本丸です。
Pythonである程度作れた処理ですが、Pythonならではの便利機能が使えないなら私にとってかなり難しいことなので、処理の手順をキッチリ考えたいと思います。
追記
処理タイミングを調整したところ1フェーズの処理時間が50usec程度減りました。小さな数値ですが比率としては大きい。
不思議なのは、受信している8ユニバース中、0ユニバースに比べ7ユニバースの処理が倍くらい速く処理時間の変動が少ないことです。出力のフレームレートをDoctorMXで計測すると同じ値なので謎です。今は動いているバグですと後々問題になりそうそうなので確認しますが、速いユニバースは1フェーズ90usec前後で安定して動いているのでこれが平均になれば御の字です。この数値で揃えば現在考えている最大規模も処理出来ると思います。
#[Art-Net]
諸々イイ感じに書き進められていますが、処理タイミングという一見簡単そうで実は面倒なことに取り組んでいます。
先日作ったキー入力処理とArt-Net処理を混ぜているのですが、それぞれに合った時間間隔で実行しないといけません。キー入力は100msec、Art-Netは100usecです。求められる処理間隔が3桁も違うので同じ時間間隔ではどちらかが破綻します。それぞれをそれぞれの時間間隔で実行するにはどうするか。他の処理を止めること無く処理を続けるにはどうするか。
基本は前回処理の日時と現在日時の差から経過時間を評価する方法です。一定の時間間隔で処理を呼ぶのではなく、呼ばれても経過時間が要求未満なら何もしない方法です。signal()を用いて一定時間間隔で関数を実行させるのが王道でしょうが、singal()ですと終わっているべき他処理のチェックが必要になるのでどっちもどっちです。私の書き方が悪いのかもしれませんが、signal()を多用するとバグの原因になりやすいようです。
なんにしても前回処理からの経過時間で対策してみます。そのために必要なのは現在日時の取得です。POSIX時間と呼ばれる1970年1月1日0時0分0秒からの経過秒数を取得出来るのでこれを用います。50マイクロ秒くらいの分解能が欲しいので秒単位では不足しますが、精度はともかく1ナノ秒単位で値を得られますから十分です。
下記はその値を取得するテストプログラムです。
秒とナノ秒が別々の変数で得られるので、評価を簡単にするためにひとまとめにします。ただし、int型は4バイト長(OSが32bitの場合)のためデータ長が不足しますので、8バイト長(OSが32bitの場合)の unsigned long long int型を用います。2038年問題はとりあえず気にしない。。。
Raspberry Pi 4B / Rasbian11_32bit(blueseye) / コンパイラ:OS標準gcc
#include <stdio.h>
#include <time.h>
int main( int argc, char *argv[] ) {
struct timespec now ;
unsigned long long int now_nsec ;
// 現在POSIX時を取得
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値を取得
now_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec ; // 取得値をひとまとめにする(nsec)
// 確認表示
printf( "%11ld.%09ld sec.\n", now.tv_sec, now.tv_nsec ) ; // 取得値を表示
printf( "%21lld nsec.\n", now_nsec ) ; // ひとまとめにした数値を表示
// 終了
return 0 ;
}
※ このブログシステムでは#や[が機能文字扱いなので、上記ではこれらを全角文字で書いています。空白も全角です。
◆ 参考
時間情報の取得方法と扱い方
取得に必要なのは「// 現在POSIX時を取得」以下の2行(表記は3行)です。
実際の処理では、取得した秒とナノ秒を合わせた数値がnow_nsec入りますので現在日時値とし、同じ方法で前回の処理で取得した値との差で経過時間を評価します。単位がnsecなら1秒は1000000000(1e9)ですから、100msecは100000000(1e8)、100usecは100000(1e5)です。良い意味で処理が速いC言語では多用すべき手法です。
関数化・ライブラリ化するとソースは美しいですが、2行で出来ることなので都度の直書きでもいいかなと。将来、2038年問題に対策しやすくしておくならライブラリ化しといた方がいいけど。
PICのアセンブラでも近いことをしていますが、それがPICで様々なことを実現出来ている要因だったりします。処理タイミングの管理はとても重要です。
#C言語
先日作ったキー入力処理とArt-Net処理を混ぜているのですが、それぞれに合った時間間隔で実行しないといけません。キー入力は100msec、Art-Netは100usecです。求められる処理間隔が3桁も違うので同じ時間間隔ではどちらかが破綻します。それぞれをそれぞれの時間間隔で実行するにはどうするか。他の処理を止めること無く処理を続けるにはどうするか。
基本は前回処理の日時と現在日時の差から経過時間を評価する方法です。一定の時間間隔で処理を呼ぶのではなく、呼ばれても経過時間が要求未満なら何もしない方法です。signal()を用いて一定時間間隔で関数を実行させるのが王道でしょうが、singal()ですと終わっているべき他処理のチェックが必要になるのでどっちもどっちです。私の書き方が悪いのかもしれませんが、signal()を多用するとバグの原因になりやすいようです。
なんにしても前回処理からの経過時間で対策してみます。そのために必要なのは現在日時の取得です。POSIX時間と呼ばれる1970年1月1日0時0分0秒からの経過秒数を取得出来るのでこれを用います。50マイクロ秒くらいの分解能が欲しいので秒単位では不足しますが、精度はともかく1ナノ秒単位で値を得られますから十分です。
下記はその値を取得するテストプログラムです。
秒とナノ秒が別々の変数で得られるので、評価を簡単にするためにひとまとめにします。ただし、int型は4バイト長(OSが32bitの場合)のためデータ長が不足しますので、8バイト長(OSが32bitの場合)の unsigned long long int型を用います。2038年問題はとりあえず気にしない。。。
Raspberry Pi 4B / Rasbian11_32bit(blueseye) / コンパイラ:OS標準gcc
#include <stdio.h>
#include <time.h>
int main( int argc, char *argv[] ) {
struct timespec now ;
unsigned long long int now_nsec ;
// 現在POSIX時を取得
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値を取得
now_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec ; // 取得値をひとまとめにする(nsec)
// 確認表示
printf( "%11ld.%09ld sec.\n", now.tv_sec, now.tv_nsec ) ; // 取得値を表示
printf( "%21lld nsec.\n", now_nsec ) ; // ひとまとめにした数値を表示
// 終了
return 0 ;
}
※ このブログシステムでは#や[が機能文字扱いなので、上記ではこれらを全角文字で書いています。空白も全角です。
◆ 参考
時間情報の取得方法と扱い方
取得に必要なのは「// 現在POSIX時を取得」以下の2行(表記は3行)です。
実際の処理では、取得した秒とナノ秒を合わせた数値がnow_nsec入りますので現在日時値とし、同じ方法で前回の処理で取得した値との差で経過時間を評価します。単位がnsecなら1秒は1000000000(1e9)ですから、100msecは100000000(1e8)、100usecは100000(1e5)です。良い意味で処理が速いC言語では多用すべき手法です。
関数化・ライブラリ化するとソースは美しいですが、2行で出来ることなので都度の直書きでもいいかなと。将来、2038年問題に対策しやすくしておくならライブラリ化しといた方がいいけど。
PICのアセンブラでも近いことをしていますが、それがPICで様々なことを実現出来ている要因だったりします。処理タイミングの管理はとても重要です。
#C言語
Art-Netの入口と出口は出来ましたので機能を組みます。C言語に替えた為に処理能力に余裕が出て複座なプロセス処理を多用しなくても良さそうですが、そもそもの処理をどうするか吟味しないといけません。
搭載予定の機能は、ミキサー(マージ)、プリディレイ(入力に施す)、プリプロファイルカーブ、パッチ、ポストプロファイルカーブ、ポストディレイ(出力に施す)です。スタックフェーダー機能はミキサー機能に含まれます。
さて、どうしようかな。
#[Art-Net] #C言語
搭載予定の機能は、ミキサー(マージ)、プリディレイ(入力に施す)、プリプロファイルカーブ、パッチ、ポストプロファイルカーブ、ポストディレイ(出力に施す)です。スタックフェーダー機能はミキサー機能に含まれます。
さて、どうしようかな。
#[Art-Net] #C言語
Art-Netのデコード/エンコードも出来ました。受信した1配列のバイナリデータを分類して構造体に取り込むのがデコード、項目別の構造体から送信用のバイナリデータに変換するのがエンコードです。
実験は受信値をデコードして再エンコードして送信です。照明器具としては何の芸も無い状態ですが、これが出来なきゃ先へは進めません。
topコマンドで処理負荷を見ますと、ダンプ表示ありで23%、ダンプ表示無しで7%くらいです。Art-Netのコア処理は思った以上に軽いですが、画面表示が重いっすね。完成イメージの画面表示はもっと重くなりますので、Art-Netのコア処理は画面表示とは別プロセスにしたいですね。出来ることならArt-Netのコア処理でCPUスレッドを1つ占有したいくらいです。ここは余裕をもって確実にしたい処理ですから。
今のところ製作は順調です。3月末までに1日平均3-4時間程度使えますので、「ArtNet-Engine」と呼ぶコア機能はまとめられそうです。
#[Art-Net] #C言語
実験は受信値をデコードして再エンコードして送信です。照明器具としては何の芸も無い状態ですが、これが出来なきゃ先へは進めません。
topコマンドで処理負荷を見ますと、ダンプ表示ありで23%、ダンプ表示無しで7%くらいです。Art-Netのコア処理は思った以上に軽いですが、画面表示が重いっすね。完成イメージの画面表示はもっと重くなりますので、Art-Netのコア処理は画面表示とは別プロセスにしたいですね。出来ることならArt-Netのコア処理でCPUスレッドを1つ占有したいくらいです。ここは余裕をもって確実にしたい処理ですから。
今のところ製作は順調です。3月末までに1日平均3-4時間程度使えますので、「ArtNet-Engine」と呼ぶコア機能はまとめられそうです。
#[Art-Net] #C言語
Art-Netの送信にも成功しました。
受信値を転送するだけですが、Art-Netデコーダから正常と思われる値が出力されています。
今日は終わりにしますが、大きな課題がクリア出来て大満足です。
ただ、8ユニバースを出力しているdot2が一杯いっぱいの様子。発熱も凄いし画面もコマ送りです。
ちなみにですが、recvfrom()の4番目のパラメータを「MSG_DONTWAIT」にすると受信待ちしません。先達の例では待ちアリの「0」を定義してioctl()に待ち無し(ノンブロッキング)を設定していることが多いのですが、「MSG_DONTWAIT」を使った方がストレスが無い感じ。
で、気付いたのですが、C言語は速い。速いが故の対策が必要になる始末。受送信テストでは終了のためのキー入力やタイムアウトを入れるのが面倒だったのでfor文による一定回数の繰り返しで試したのですが、受信待ちを無くした後はPythonでの実験の際に使った回数では一瞬で終わってしまいます。プログラムが間違っているのかと思う程でした。てことは、あまりにも無意味な回数recvfrom()を呼んでいることになりますので、受信が無い場合は100~500usecくらい待ちを入れた方がいいみたいです。バッファを読みに行っているだけなので気にしなくていいって話もありますが、ANSIエスケープシーケンスを用いた画面表示も適度な待ちを入れないと画面がフリッカーを起こす程です。数か月かかりましたが、C言語を勉強して良かったと思います。遅いのを対策するのは大変ですが、速いのを抑え込むのは比較的簡単ですからね。
ここまで速いとマルチプロセスを使わなくてもいいのではないか?って気持ちも芽生えます。使った方が速さ以外に都合が良いこともあるので使いますケド。
基本的な受送信が確認出来ましたので、関数化しつつArt-Net(正しくはArt-DMX)のデコード/エンコードも書きましょう。
#[Art-Net] #C言語
受信値を転送するだけですが、Art-Netデコーダから正常と思われる値が出力されています。
今日は終わりにしますが、大きな課題がクリア出来て大満足です。
ただ、8ユニバースを出力しているdot2が一杯いっぱいの様子。発熱も凄いし画面もコマ送りです。
ちなみにですが、recvfrom()の4番目のパラメータを「MSG_DONTWAIT」にすると受信待ちしません。先達の例では待ちアリの「0」を定義してioctl()に待ち無し(ノンブロッキング)を設定していることが多いのですが、「MSG_DONTWAIT」を使った方がストレスが無い感じ。
で、気付いたのですが、C言語は速い。速いが故の対策が必要になる始末。受送信テストでは終了のためのキー入力やタイムアウトを入れるのが面倒だったのでfor文による一定回数の繰り返しで試したのですが、受信待ちを無くした後はPythonでの実験の際に使った回数では一瞬で終わってしまいます。プログラムが間違っているのかと思う程でした。てことは、あまりにも無意味な回数recvfrom()を呼んでいることになりますので、受信が無い場合は100~500usecくらい待ちを入れた方がいいみたいです。バッファを読みに行っているだけなので気にしなくていいって話もありますが、ANSIエスケープシーケンスを用いた画面表示も適度な待ちを入れないと画面がフリッカーを起こす程です。数か月かかりましたが、C言語を勉強して良かったと思います。遅いのを対策するのは大変ですが、速いのを抑え込むのは比較的簡単ですからね。
ここまで速いとマルチプロセスを使わなくてもいいのではないか?って気持ちも芽生えます。使った方が速さ以外に都合が良いこともあるので使いますケド。
基本的な受送信が確認出来ましたので、関数化しつつArt-Net(正しくはArt-DMX)のデコード/エンコードも書きましょう。
#[Art-Net] #C言語
Art-Netの受信に成功しました。
先達の情報をいくつか合成しましたが、思った以上に簡単でした。
ネットワークインターフェースの指定も出来ました。
一安心ですが、次は送信しないといけません。
デコード/エンコードをせずに受信値をリレーするだけですが、これが成功しないようでは受信値を加工する努力をしても意味がありません。
#[Art-Net]
先達の情報をいくつか合成しましたが、思った以上に簡単でした。
ネットワークインターフェースの指定も出来ました。
一安心ですが、次は送信しないといけません。
デコード/エンコードをせずに受信値をリレーするだけですが、これが成功しないようでは受信値を加工する努力をしても意味がありません。
#[Art-Net]
自作のライブラリを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言語
てなわけで、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言語