2026年1月18日日曜日

Linux getchar()でKeyが押されるまでブロッキングされちゃうの嫌なんだけど....

googleの窓にアバウトな質問を投げるとAIが答えてくれるので便利な世の中です.
 「linuxのC言語でKBDから1文字づつ入力してstringに連結して"\n"で終了するcode」
でもgetchar()だとKeyが押されるまでブロッキングされちゃいますね.

周期的に動く別の仕事があるのでそっちはthreadでやればいいんだけど、getchar()でmain()がブロックされるのはチト困るんだが・・・ getchar()ってブロッキング関数なうえにtimeout無いから行ったきりで嫌なんだなぁ.

そこで再びAIに聞きました.
 「linuxでKBDから1文字入力のブロッキングしないやり方」
AIは答えてくれますが、STDINをノンブロッキングに変更するのが回答でした.

へぇ~意外と面倒なんですねぇ.ノンブロックのgetchar()類似関数があるのかと思ってたんだけどそうじゃなかった....

↓AIのお答え
    // 1. 端末をカノニカルモードから非カノニカルモードへ変更(Enterなしで読み込むため)
    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_lflag &= ~(ICANON | ECHO); // エコー(入力文字の表示)もオフにする
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);

    // 2. 標準入力を非ブロッキングに設定
    oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
    fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);

なんだなんだぁ? 2手間もかかんの?

でも動いたみたいよ.

ナイスざぁます >AI

かしこ

7 件のコメント:

  1. murasaki
    AIは神ですねー。
    でも、なんか副作用がありそうな方法ですね・・。

    返信削除
    返信
    1. killした後でbashのKBD動作が変わるみたい
      本当はkillの前に元に戻すべきなんだろうけどめんどくさ

      削除
    2. murasaki
      手軽にやろうとするといろいろ厄介ごと抱え込むし、少し普通と違うことしようとすると結構Linuxって牙をむいてくるイメージ。
      unix時代からの1文字入出力という超シンプル思考がLinuxでもまだ生きている感じですね。
      キーコードをデバイスから直接読むやり方が組込みっぽいかもですね。面倒ですが。

      削除
    3. linuxで有難いのは、
      探せば情報があるとこかな
      windowsは非公開だから困ったときにわからんくて

      削除
  2. 「fgetc(stdin) ノンブロック」でAI検索したら、こんな回答が・・・

    方法1:fcntl を使って stdin をノンブロッキングにする (POSIX準拠, Linux/macOS)
    ※↑ と同じやり方

    方法2:select() または poll() で入力チェックする
    fgetc を使う前に、select 関数で stdin にデータが届いているか確認します。データがない場合は fgetc を呼ばないことで、ブロッキングを回避します。
    #include
    #include
    #include

    int is_stdin_ready() {
    struct timeval tv = {0, 0};
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(STDIN_FILENO, &fds);
    // タイムアウトを0にして即座にチェック
    return select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv) > 0;
    }

    int main() {
    printf("文字を入力: ");
    while (1) {
    if (is_stdin_ready()) {
    int c = fgetc(stdin);
    if (c == 'q') break;
    printf("入力: %c\n", c);
    }
    // 他の処理をここで行う
    }
    return 0;
    }
    ※「select 関数」なんて、あるの知らなかったわ。

    方法3:ncurses ライブラリを使用する (推奨)
    ターミナル入力を制御する標準的なライブラリである ncurses を使うのが最も簡単で確実です。nodelay(stdscr, TRUE) を設定すると、getch() がノンブロッキングになります。
    ※「ncurses ライブラリ」って、よく聞くけど、こういう時に使うのか。

    注意点
    ・カノニカルモード: デフォルトのターミナルは1行単位で入力を待つ(カノニカルモード)ため、1文字ずつノンブロックで読み込むには、上記の termios 設定や ncurses を使ってターミナルを raw/cbreak モードにする必要があります。
    ・CPU負荷: ノンブロッキングループ内でスリープ(usleep)を入れないと、データがない間、CPUを100%使用してしまうため注意が必要です。
    ※まぁ、そうだよね。C は「低級言語」なので、この辺もちゃんと面倒見ないといけないですね。

    返信削除
    返信
    1. 2はなんかやだけど、
      3はイイかんじかしら

      削除
  3. あれ、なんか、#include や タブが消えてる。。。何なんだ。

    返信削除