#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <time.h>


#define WIN_W  400   /* ウィンドウの幅   */
#define WIN_H  300   /* ウィンドウの高さ */
#define WIN_X  100   /* ウィンドウ表示位置(X) */
#define WIN_Y  100   /* ウィンドウ表示位置(Y) */
#define BORDER 2     /* ボーダの幅      */


static  Display*       dpy;            /* ディスプレイ */
static  Window         root;           /* ルートウィンドウ */
static  Window         win;            /* 表示するウィンドウ */
static  int            screen;         /* スクリーン */
static  int            depth;          /* スクリーンの深さ */
static  XVisualInfo    visual_info;    /* ビジュアル */
static  Colormap       cmap;           /* カラーマップ */
static  unsigned long  color_pixel;    /* 切替え色に使用するピクセル値 */
static  XColor         color[2];       /* 切替えに使う色( RGB ) */


static void xblink_init( void );
static int  xblink_check_visual( void );
static void xblink_create_window( void );
static int  xblink_alloc_color( void );
static void xblink_event_loop( void );


int 
main( void )
{
	xblink_init();

	/* ビジュアルをチェックする */
	if ( xblink_check_visual() == -1 ) {
		return 1;
	}

	/* 読み書き両用カラーセルを割り当て、色を設定する */
	if ( xblink_alloc_color() == -1 ) {
		return 1;
	}

	xblink_create_window();
	XMapWindow( dpy, win );
	xblink_event_loop();

	return 0;
}


static void 
xblink_init( void )
{
	dpy = XOpenDisplay( "" );

	root   = DefaultRootWindow( dpy );
	screen = DefaultScreen( dpy );
	depth  = DefaultDepth( dpy, screen );
	cmap   = DefaultColormap( dpy, screen );
}


/*
 * PseudoColor もしくは DirectColor のビジュアル
 * がサポートされているかどうかチェックする。
 */
static int
xblink_check_visual( void )
{ 
	/* XAllocColorCells() のために、読み書き両用のビジュアルが
	   サポートされているかどうかチェック */
	if ( ! XMatchVisualInfo( dpy, screen, depth, PseudoColor, &visual_info ) ) {
		printf( "Writable visual class is not supported.\n" );
		return -1;
	}

	return 0;
}


/*
 * 読み書き両用のカラーセルを 1 個確保し、初期表示色を設定しておく。
 */
static int
xblink_alloc_color( void )
{
	unsigned long b_pixel[1], p_mask[1];
	int i;

	/* 色切替え用に、読み書き両用のカラーセルを 1 つ割り当てる */
	if ( XAllocColorCells( dpy, cmap, True, p_mask, 0, b_pixel, 1 ) == 0 ) {
		printf( "XAllocColorCells() failed\n" );
		return -1;
	}

	/* b_pixel[0] にピクセル値が格納されている。xblink.c では p_mask に意味は無い */
	color_pixel = b_pixel[0];

	if ( ! XParseColor( dpy, cmap, "red",   &color[0] ) ) {
		printf( "XParseColor() for red failed\n" );
		return -1;
	}

	if ( ! XParseColor( dpy, cmap, "black", &color[1] ) ) {
		printf( "XParseColor() for black failed\n" );
		return -1;
	}

	for ( i = 0; i < 2; i ++ ) {
		color[i].pixel = color_pixel;
		color[i].flags = DoRed | DoGreen | DoBlue;
	}

	/* 初期表示色を赤色に設定 */
	XStoreColor( dpy, cmap, &color[0] );
	return 0;
}


/*
 * ウィンドウの作成 / グラフィックスコンテキストの作成を行う。
 */
static void 
xblink_create_window( void )
{
	/* ウィンドウを作成する。この時点ではまだ画面に表示されない。*/
	win = XCreateSimpleWindow( dpy, root, 
				   WIN_X, WIN_Y, WIN_W, WIN_H, BORDER, 
				   color_pixel, color_pixel );

	XSelectInput( dpy, win, KeyPressMask );
}


static void 
xblink_event_loop( void )
{
	/* 現在の表示色を示すインデックス */
	int  current_col = 0;

	/* 最後に表示色を切替えた時刻 */
	time_t previous = time( NULL );

	time_t now;  /* 現在時刻 */
	XEvent evt;  /* イベント構造体変数 */

	while( 1 ) {
		/*
		 * まず表示色の切り替え処理を行ってから、イベント
		 * の処理を行う( どちらが先でも良いのだが)。
		 */ 

		now = time( NULL );

		/* 前回切替え時刻より 1 秒経過していたら、表示色を切替える */
		if ( now - previous >= 1 ) {
			if ( current_col == 0 ) {
				current_col ++;
			}
			else {
				current_col = 0;
			}

			/* 表示色を切替える */
			XStoreColor( dpy, cmap, &color[ current_col ] );

			/* 切替え時刻を保存 */
			previous = now;
		}

		while ( XPending( dpy ) ) {
			XNextEvent( dpy, &evt );

			switch ( evt.type ) {
				case KeyPress:
					/* リソースの解放 */
					/*
					 *  XFreeColors( 
					 *      dpy, cmap, &color_pixel, 1, planes );
					 */
					XDestroyWindow( dpy, win );
					XCloseDisplay( dpy );
					return;
			}
		}
	}
}


/* End of xblink.c */
