15

私のプログラムの 1 つは、小さな tui を描画するために ncurses を使用しています。私の目標の 1 つは、他の curses 実装にかなり移植できるようにすることです。これは、サイズ変更操作で端末エミュレーターによって発行された SIGWINCH を自分でキャッチし、tui を更新して、変更されたジオメトリを順守する (ncurses のサイズ変更機能に依存しない) ことを意味します。POSIX (私が知る限り) ではsig_atomic_t、シグナル ハンドラー内の変数へのアクセスしか許可されていないため、シグナル ハンドラーを別の状態に設定しました。メイン ループで、プログラムは状態が変化したかどうかを確認し、必要に応じて tui を更新します。

しかし今、getchシグナルが到着したときにプログラムがハングアップするという問題があります。ncurses のドキュメントには、処理されたシグナルが決して中断しないと記載されています。これは、入力キーが押されるまで、tui のサイズが更新されないことを意味します。

中断するポータブルな方法はありますgetchか? 私の現在のアプローチはungetch、シグナルハンドラーのダミーキーですが、これが許可されているかどうかはわかりません。実際、シグナルハンドラでcurses関数を使用できるかどうかに関するドキュメントは見つかりませんでした。この問題を正しく処理する方法はありますか?

よろしく

4

4 に答える 4

1

FreeBSD ドキュメントgetchから、割り込み getch は使用しているシステムに依存します:

移植性を懸念するプログラマーは、次の 2 つのケースのいずれかに備えておく必要があります。(a) シグナルの受信によって getch が中断されない。(b) シグナル受信により getch が中断され、errno が EINTR に設定された ERR が返されます。ncurses の実装では、処理されたシグナルが getch を中断することはありません。

スレッドの使用を検討する必要があると思います.1つのスレッドは、 getch() を使用し、それらを処理するメインスレッドにデータを渡すために課金されます. シグナル SIGWINCH が送信されたときに getch() を使用するスレッドを強制終了し、必要に応じて再起動することができます。しかし、メイン スレッドは getch() によってブロックされないため、getch() を使用するスレッドを強制終了しても無駄かもしれません。

このページで説明されているように、ncurses なしでノンブロッキング入力を取得できます。ただし、失敗した場合、 read は値 <= 0 を返すため、例のread(STDIN_FILENO, &c, sizeof(char))代わりに を使用して入力を読み取ることができます。fgetc()

于 2015-05-25T07:51:18.963 に答える
1

@Emilien に同意すると、これはおそらくncurses - resizing glitchの複製です。

ただし、OP は状況を示すために動作するコードを表示しませんでした。

機能が無効になっている ( 2011 年まで) OpenBSD を除いて、 getch はKEY_RESIZEonを返す必要がありSIGWINCHます。これを無効にする理由は、 2007 年sig_atomic_tに対処された懸念事項でした(たとえば、 OpenBSD の ncurses には USE_SIGWINCH がありません)。

それ以外では、取得できない通常の理由は、(ncurses 内のハンドラーの実行を妨げる) ハンドラーをKEY_RESIZE確立した場合です。それがncursesSIGWINCHの実際の問題でした-サイズ変更の不具合(提案された回答はどれも対処されていません)。

endwin/refresh ソリューションはよく知られています ( 1997 年以降の ncurses FAQ のSIGWINCH (サイズ変更イベント) の処理を​​参照してください)。マニュアルページのresizeterm移植性セクションに要約されています。そこを読めばわかる

  • KEY_RESIZEが定義されている場合、実装は ncurses と多かれ少なかれ互換性のあるハンドラーをサポートしますSIGWINCH
  • そうでない場合は、endwin/refresh ソリューションが利用可能なすべてです。

ちなみに、受け入れられた回答の ncurses のcurs_getch(3x)マニュアル ページから引用されたコメントは、ブロッキングについてではなくSIGWINCH、シグナルが読み取りを中断したときにアプリケーションにエラーが返されないようにするためのライブラリ内の回避策について言及しています。そのマニュアル ページの移植性セクション全体を読めば、それは明らかです。

もちろん、Emacs は ncurses のtermcapインターフェイスのみを使用し、curses アプリケーションではありません。したがって、それがどのように処理されるかについてのコメントSIGWINCHは無関係です。

于 2016-03-09T10:07:19.823 に答える
0

しばらく経ちましたが、ハンドラーから longjmp できると確信しています。ただし、ncurses でどのように機能するかはわかりません。

http://www.csl.mtu.edu/cs4411.ck/www/NOTES/non-local-goto/sig-1.html

編集: 私は新しい答えを持っています ******************************

ノンブロッキング読み取りがあなたのチケットだと思います。以下は私にとって完璧に機能します。また、私の実装では、サイズ変更イベントが発生したときに整数値「410」を取得しますが、このバージョンはそのサイズ変更フラグを使用していません。これを試してみませんか?

タイムアウトを 10mS に設定しています。これは、何も起こっていないときに文字の代わりに ERR を 1 秒あたり 100 回返し、サイズ変更をすぐにキャッチします。1 秒あたり 100 回ではありません... 私のローエンド マシンでは、この 8 分間は cput 'time' コマンドにも登録されません (0.000 ユーザーと sys が使用されます)。

#include <curses.h>
#include <signal.h>

int resized = 0;

void handle_resize(int sig)
{
  resized = 1;
}

int main(int argc, char * argv[])
{
   WINDOW * w;
   w = initscr();
   timeout(10); // getch returns ERR if no data within X ms
   cbreak();
   noecho();

   signal(SIGWINCH, handle_resize);

   int c, i;

   while (1) {
      c = getch();

      if(resized) {
         resized = 0;
         endwin();
         refresh();
         clear();

         mvprintw(0, 0, "COLS = %d, LINES = %d", COLS, LINES);
         for (i = 0; i < COLS; i++)
            mvaddch(1, i, '*');

         refresh();
      } 

      if (c != ERR) {
         mvprintw(2, 0, "Got character %c (%d)\n", c, c);
      }
  }//while 1

   endwin();
   return 0;
}
于 2014-12-03T08:43:20.077 に答える