ユーザーがキーを押すまでループするプログラムをC(Linux)で記述しようとしていますが、各ループを続行するためにキーを押す必要はありません。
これを行う簡単な方法はありますか?おそらくselect()
でできると思いますが、それは大変な作業のようです。
または、キャッチする方法はありますか ctrl-c ノンブロッキングioの代わりにプログラムが閉じる前にクリーンアップを実行するキープレス?
すでに述べたように、sigaction
を使用してctrl-cをトラップするか、select
を使用して標準入力をトラップできます。
ただし、後者の方法では、行単位モードではなく文字単位モードになるようにTTYを設定する必要があることに注意してください。後者がデフォルトです-テキスト行を入力した場合、実行キーを押すまで実行中のプログラムの標準入力に送信されません。
tcsetattr()
関数を使用してICANONモードをオフにし、おそらくECHOも無効にする必要があります。メモリから、プログラムの終了時に端末をICANONモードに戻す必要もあります!
完全を期すために、Unix TTYをセットアップし、DOS <conio.h>
関数kbhit()
およびgetch()
をエミュレートするコードをいくつか紹介します(nb:エラーチェックなし!)。 :
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <termios.h>
struct termios orig_termios;
void reset_terminal_mode()
{
tcsetattr(0, TCSANOW, &orig_termios);
}
void set_conio_terminal_mode()
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
/* register cleanup handler, and set the new terminal mode */
atexit(reset_terminal_mode);
cfmakeraw(&new_termios);
tcsetattr(0, TCSANOW, &new_termios);
}
int kbhit()
{
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv);
}
int getch()
{
int r;
unsigned char c;
if ((r = read(0, &c, sizeof(c))) < 0) {
return r;
} else {
return c;
}
}
int main(int argc, char *argv[])
{
set_conio_terminal_mode();
while (!kbhit()) {
/* do some work */
}
(void)getch(); /* consume the character */
}
UNIXシステムでは、sigaction
呼び出しを使用して、Control + Cキーシーケンスを表すSIGINT
シグナルのシグナルハンドラを登録できます。シグナルハンドラーは、ループでチェックされるフラグを設定して、適切にブレークすることができます。
おそらくkbhit();
が必要です
_//Example will loop until a key is pressed
#include <conio.h>
#include <iostream>
using namespace std;
int main()
{
while(1)
{
if(kbhit())
{
break;
}
}
}
_
これはすべての環境で機能するとは限りません。ポータブルな方法は、監視スレッドを作成し、getch();
にフラグを設定することです
Cursesライブラリーはこの目的に使用できます。もちろん、select()
とシグナルハンドラーもある程度使用できます。
ノンブロッキングキーボード入力を取得する別の方法は、デバイスファイルを開いて読み取ることです!
/ dev/input/event *の1つを探しているデバイスファイルを知っている必要があります。 cat/proc/bus/input/devicesを実行して、必要なデバイスを見つけることができます。
このコードは私のために機能します(管理者として実行)。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
int main(int argc, char** argv)
{
int fd, bytes;
struct input_event data;
const char *pDevice = "/dev/input/event2";
// Open Keyboard
fd = open(pDevice, O_RDONLY | O_NONBLOCK);
if(fd == -1)
{
printf("ERROR Opening %s\n", pDevice);
return -1;
}
while(1)
{
// Read Keyboard Data
bytes = read(fd, &data, sizeof(data));
if(bytes > 0)
{
printf("Keypress value=%x, type=%x, code=%x\n", data.value, data.type, data.code);
}
else
{
// Nothing read
sleep(1);
}
}
return 0;
}
Control-Cをキャッチするだけで満足している場合は、これで完了です。本当に非ブロッキングI/Oが必要で、cursesライブラリが必要ない場合は、ロック、ストック、およびバレルを AT&T sfio
library に移動することもできます。 C stdio
でパターン化された素晴らしいライブラリですが、より柔軟で、スレッドセーフで、パフォーマンスが向上しています。 (sfioは安全で高速なI/Oの略です。)
これを行うための移植可能な方法はありませんが、select()は良い方法かもしれません。 http://c-faq.com/osdep/readavail.html をご覧ください。
これを行うための関数を次に示します。 POSIXシステムに付属のtermios.h
が必要です。
#include <termios.h>
void stdin_set(int cmd)
{
struct termios t;
tcgetattr(1,&t);
switch (cmd) {
case 1:
t.c_lflag &= ~ICANON;
break;
default:
t.c_lflag |= ICANON;
break;
}
tcsetattr(1,0,&t);
}
これを分解すると、tcgetattr
は現在の端末情報を取得し、t
に保存します。 cmd
が1の場合、t
のローカル入力フラグは非ブロッキング入力に設定されます。それ以外の場合はリセットされます。次に、tcsetattr
は標準入力をt
に変更します。
プログラムの最後で標準入力をリセットしないと、シェルで問題が発生します。