Previous: 準備
Up: Xウィンドウ実習
第12回
少し特殊なイベント処理
Previous Page: 準備
Next Page: Xウィンドウ概論
第1回 Xのシステムと Xlib
ウィンドウのプログラムはイベントを通じて処理を呼び出します。
しかし、いくつかのアプリケーションでは例外的にイベントがない
時にも仕事をする必要が生じます。典型的な例は時計やスクリーン・ロック
のプログラムです。テトリスのようなゲームでも、ユーザーのイベントとは
独立してアニメーションを動かす必要があります。
このようなケースではイベント処理のループに少し工夫を加えなくてはいけません。
そのための新しい関数も必要です。(ただし1つだけで十分です。)
その新しい関数とは、XEventQueued() という名前で、イベントが何個あるか
チェックするのが仕事です。
つまり XEventQueued() が返す値が0ならば、処理すべきイベントは存在しません。
その場合はイベントとは独立の仕事を続けます。
もし XEventQueued() の返す値が0でなかったら、その場合は通常の
イベント処理を実行するようにします。
以上のような考え方にしたがって作ってみたのが次に示す時計のプログラムです。
clock.c
#include<stdio.h> #include<sys/time.h> #include<X11/Xlib.h> #include "anime0" #include "anime1" #include "anime2" #include "anime3" #include "anime4" #include "anime5" #include "anime6" #define PIX_NUM 7 /* ピクスマップの枚数 */ #define REPEAT 2 /* 動作の繰り返し回数 */ #define HOUR 2 /* 時間を更新 */ #define MINUTE 1 /* 分を更新 */ #define SECOND 0 /* 秒を更新 */void timer( int ); /* タイマーの関数の宣言 */ int tokei( char * ); /* 1秒ごとの時刻を刻むの関数の宣言 */
main() { Display *dsp; Window win; Pixmap pix[PIX_NUM]; GC gc; XEvent eve; static char *win_name = "Clock"; int i, j; Font font; char jikoku[32]; /* 表示される時刻の文字列 */ int flag; /* 時刻のどの単位で更新があったか */
dsp = XOpenDisplay( NULL );
win = XCreateSimpleWindow( dsp, DefaultRootWindow(dsp), 0, 0, 100, 100, 2, BlackPixel(dsp,0), WhitePixel(dsp,0) ); /* アニメーション用のピクスマップ */ pix[0] = XCreatePixmapFromBitmapData( dsp, win, anime0_bits, anime0_width, anime0_height, BlackPixel(dsp,0), WhitePixel(dsp,0), 8 ); pix[1] = XCreatePixmapFromBitmapData( dsp, win, anime1_bits, anime1_width, anime1_height, BlackPixel(dsp,0), WhitePixel(dsp,0), 8 ); pix[2] = XCreatePixmapFromBitmapData( dsp, win, anime2_bits, anime2_width, anime2_height, BlackPixel(dsp,0), WhitePixel(dsp,0), 8 ); pix[3] = XCreatePixmapFromBitmapData( dsp, win, anime3_bits, anime3_width, anime3_height, BlackPixel(dsp,0), WhitePixel(dsp,0), 8 ); pix[4] = XCreatePixmapFromBitmapData( dsp, win, anime4_bits, anime4_width, anime4_height, BlackPixel(dsp,0), WhitePixel(dsp,0), 8 ); pix[5] = XCreatePixmapFromBitmapData( dsp, win, anime5_bits, anime5_width, anime5_height, BlackPixel(dsp,0), WhitePixel(dsp,0), 8 ); pix[6] = XCreatePixmapFromBitmapData( dsp, win, anime6_bits, anime6_width, anime6_height, BlackPixel(dsp,0), WhitePixel(dsp,0), 8 );
gc = XCreateGC( dsp, win, NULL, NULL );
XSetForeground( dsp, gc, BlackPixel(dsp,0) ); XSetBackground( dsp, gc, WhitePixel(dsp,0) );
font= XLoadFont( dsp, "9x15bold" ); /* 時刻の表示に使う文字フォント */ XSetFont( dsp, gc, font );
XStoreName( dsp, win, win_name );
XSelectInput( dsp, win, ExposureMask );
XMapWindow( dsp, win ); XFlush( dsp );
while( True ) { if( XEventsQueued( dsp, QueuedAfterFlush ) == 0 ) /* イベントがない */ { flag = tokei( jikoku ); /* 1秒ごとに時刻を返す */ XDrawImageString( dsp, win, gc, 13, 88, jikoku, strlen(jikoku) ); /* 時刻の表示 */ XFlush( dsp );
if( flag != SECOND ) /* 分のデータが更新されたらアニメーション */ { for( j=0 ; j<REPEAT ; j++ ) for( i=0 ; i<PIX_NUM ; i++ ) { XCopyArea( dsp, pix[i], win, gc, 0, 0, anime0_width, anime0_height, 18, 16 ); XFlush( dsp ); timer( 150000 ); } } if( flag == HOUR ) /* 時間のデータが更新されたら時報 */ { system( "audioplay -v 40 /home/net/sparc05/ueda/bin/paon" ); } } else /* イベントが来たので処理する */ { XNextEvent( dsp, &eve ); switch( eve.type ) { case Expose : /* プログラム起動時に一コマ目の絵を表示する */ XCopyArea( dsp, pix[0], win, gc, 0, 0, anime0_width, anime0_height, 18, 16 ); XFlush( dsp ); break; default: break; } } } }
/* タイマーの関数の本体 */ void timer( int interval ) { struct timeval old_time, new_time; /* 時刻を記憶する構造体 */
gettimeofday( &old_time, NULL ); /* 時刻を調べるシステムコール */ while( True ) { gettimeofday( &new_time, NULL ); /* 時刻を調べるシステムコール */ if( new_time.tv_sec > old_time.tv_sec ) new_time.tv_usec += 1000000; if( new_time.tv_usec - old_time.tv_usec > interval ) return ; /* 指定された時間が過ぎていたら戻る */ } }
/* 時計の関数の本体 */ int tokei( char *jikoku ) { struct timeval old_time, new_time; /* 時刻を記憶する構造体 */ int hour, minute, second; /* 時・分・秒の数値データ */
gettimeofday( &old_time, NULL ); /* 時刻を調べるシステムコール */ while( True ) { gettimeofday( &new_time, NULL ); /* 時刻を調べるシステムコール */ if( new_time.tv_sec > old_time.tv_sec ) /* 1秒経過した */ { /* 時刻の計算 */ hour = (new_time.tv_sec / 60 / 60 + 9 ) % 12; /* 時 */ minute = new_time.tv_sec / 60 % 60; /* 分 */ second = new_time.tv_sec % 60; /* 秒 */
/* 時刻の文字列の生成 */ sprintf( jikoku, "%02d:%02d:%02d ", hour, minute, second );
if( minute == 0 && second == 0 ) return HOUR; else if( second == 0 ) return MINUTE; else return SECOND; } } }
プログラムが完成したら、
次のようにしてコンパイルし、
バックグラウンド・ジョブとして実行してみてください。
(ずっと動かしたままにしておくこと、1時間ごとに何が起こるでしょうか?)
ccx -o tokei clock.c tokei &