30分後に「目を休めよう!」と知らせてくれるWindowアプリ

C言語

これは自分用に作った。

仕事柄、近距離でノートパソコンの液晶画面ばかり見ている。気づけば何時間も目の前の平らな面から発せられるブルーライトを見ているのだから目に良いはずが無い。目は宝だ
そこで30分毎に休憩を知らせてくれるアプリを作ろうと思った。仕事中であれば、音はなくても気づくものが良い。パソコンの画面いっぱいに表示する仕様にした。

実行すると何も起きない。

30分経つと作業中の画面の上にいきなり次のようなウインドウが表示される。

スピーカがオンであれば、「ピローン」というシステム通知音とともにウインドウが表示されると思う。

それだけである。(画面のクオリティは現在のわたしが持てる最大限の能力を使った)

目を休めて再度仕事にかかる場合は、ウインドウにあるボタンを押すとまたウインドウは消えて30分後に同じウインドウが表示される、という自分仕様のプログラムだ。

C言語開発環境がパソコンにインストールされていれば、以下のプログラムをコンパイルして実行ファイルを作ればWindowsXP以降のパソコンで動作可能。

コンパイルはBorlandのC++ Compiler5.5で確認した。

C言語ソースコード(Windows APIを使用している)

コンパイル方法は以下ソースコードに書いてある。
30分後にメッセージと画像が表示されるが画像ファイルは800×600ピクセルのBMPファイルであればなんでもよい。画像ファイル名とメッセージ文の変更はプログラムの最初の方にあるdefine文で可能。

画像ファイルと実行ファイルは同じフォルダに無いと画像が表示されない。

timer30.c

/*
	timer30.c: 30分後に画像表示で「目を休めよう!」と知らせてくれるWindowAPIを使ったアプリ
	
		必要な画像ファイル
			ファイル名:	timer30.bmp
			サイズ:	800 x 600 pixels

		コンパイル方法
			Borland C++ Compiler5.5
				bcc32 -W timer30.c
			GCC	コンパイラ
				gcc -o timer30 timer30.c -mwindows
*/
#include <windows.h>

#define FILENAME			TEXT("timer30.bmp")		/* 描画する画像ファイル */
#define DRAW_STRING		TEXT("目を休めよう!")	/* 表示メッセージ */

#define ID_TIMER			1
#define TIMER_MINUTES		30						/* 目を休める時間(分単位) */
#define TIMER				TIMER_MINUTES * 60

#define CHILD_ID			1						/* ボタンID */

RECT rcDisplay;			/* デスクトップサイズ取得用 */
HFONT hFont1;			/* フォント作成用 */

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	static TCHAR appName[] = DRAW_STRING;
	HWND hWnd, hDeskTopWnd;
	MSG msg;
	WNDCLASS wndclass;
	HWND hWnd_button;		/* ボタン作成用 */

	/* デスクトップのサイズを取得 */
	hDeskTopWnd = GetDesktopWindow();
	GetWindowRect(hDeskTopWnd, &rcDisplay);
	
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);	
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = appName;
	
	if(!RegisterClass(&wndclass)){
		MessageBox(NULL, TEXT("ウインドウクラスの作成に失敗しました"),
					NULL, MB_OK);
		return 0;
	}
	/* ウインドウ作成 */
	hWnd = CreateWindow(appName, DRAW_STRING,
						WS_OVERLAPPEDWINDOW | WS_VISIBLE,
						CW_USEDEFAULT, CW_USEDEFAULT,
						CW_USEDEFAULT, CW_USEDEFAULT,
						NULL, NULL, hInstance, NULL);
	/* ボタンコントロール作成 */
	hWnd_button = CreateWindowA("button", "休めたよ(タイマー再開)", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
		830, 550, 250, 50, hWnd, (HMENU)CHILD_ID, hInstance, NULL);
	
	if(hWnd == NULL || hWnd_button == NULL){
		MessageBox(NULL, TEXT("ウインドウの作成に失敗しました"), NULL, MB_OK);
		return 0;
	}
	
	ShowWindow(hWnd, 0);	/* ウィンドウを隠したまま開く */
	UpdateWindow(hWnd);
	
	while(GetMessage(&msg, NULL, 0, 0)>0){
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	
	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HBITMAP	hb;		/* ビットマップハンドル */
	PAINTSTRUCT ps;
	static BITMAP bp;			/* ビットマップ構造体 */
	static HDC mhdc;			/* メモリデバイスコンテキスト */
	HDC hdc;				/* デバイスコンテキストハンドル取得用 */
	
	int timer;
	int centerX, centerY;
	
	timer = TIMER;		/* タイマー設定(何分後に表示するか?) */

	switch(message){
		case WM_DESTROY:
			KillTimer(hwnd, ID_TIMER);
			DeleteObject(hb);		/* ビットマップハンドルを削除 */
			DeleteDC(mhdc);			/* メモリデバイスコンテキストを破棄 */
			PostQuitMessage(0);
			return 0;
		case WM_COMMAND:
			switch(LOWORD(wParam)){
				case CHILD_ID:	/* ボタンが押されたら */
					/* タイマー再開 */
					SetTimer(hwnd, ID_TIMER, timer * 1000, NULL);
					ShowWindow(hwnd, 0);	/* ウィンドウを隠す */
			}
			break;
		case WM_CREATE:
			hdc = GetDC(hwnd);		/* デバイスコンテキストハンドルを取得 */
			mhdc = CreateCompatibleDC(hdc);
			ReleaseDC(hwnd, hdc);
			/* 画像ファイル読み込み */
			hb = (HBITMAP)LoadImage(NULL, FILENAME, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
			/* メモリデバイスコンテキストにビットマップを設定 */
			SelectObject(mhdc, hb);
			/* 画像サイズ取得 */
			GetObject(hb, sizeof(BITMAP), &bp);
			/* タイマー開始 */
			SetTimer(hwnd, ID_TIMER, timer * 1000, NULL);
			return 0;
		case WM_TIMER:
			MessageBeep(-1);
			KillTimer(hwnd, ID_TIMER);
			InvalidateRect(hwnd, NULL, FALSE);
			ShowWindow(hwnd, 3);	/* ウインドウ最大化 */
			return 0;
		case WM_PAINT:
			hdc = BeginPaint(hwnd, &ps);
			/* フォント作成 */
			hFont1 = CreateFont(56, 0,	/* 高さ, 幅 */
				0, 0, FW_BOLD, 			/* 傾き(反時計回り), 文字の向き, 太字(0にするとデフォルト) */
				FALSE, FALSE, FALSE,		/* Italic,  下線,  打消し線 */
				SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS,	/* キャラクタセット, 物理フォントの検索方法 */
				CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,		/* はみ出した場合の処理,  属性 */
				VARIABLE_PITCH | FF_ROMAN, NULL);			/* ピッチ、ファミリ,  タイプフェイスのポインタ */
			/* フォントをセット */
			SelectObject(hdc, hFont1);

			/* センター座標計算 */
			centerX = rcDisplay.right / 2 - bp.bmWidth / 2;
			centerY = rcDisplay.bottom / 2 - bp.bmHeight / 2;
			/* 画像を画面中央に描画 */
			BitBlt(hdc, centerX, centerY, bp.bmWidth, bp.bmHeight, mhdc, 0, 0, SRCCOPY);
			/* デバイスコンテキストに文字を描画 */
			SetTextColor(hdc, RGB(0, 0, 0));			/* 文字色(黒) */
			SetBkColor(hdc, TRANSPARENT);			/* 背景色は透明 */
			SetBkMode(hdc, TRANSPARENT);			/* 背景を塗りつぶさない */
			TextOut(hdc, 665, 210, DRAW_STRING, lstrlen(DRAW_STRING));
			/* HFONTオブジェクトを破棄 */
			DeleteObject(hFont1);

			EndPaint(hwnd, &ps);
			return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}

データ一式ダウンロード

実行ファイルを含めたファイル一式はこちらからダウンロードできます。
ただし、実行ファイル(timer30.exe)がzipファイルに入っているのでセキュリティの警告はでますが問題はないです。

内容は

timer30.c(プログラム)
timer30.bmp(画像ファイル)
timer30.exe(コンパイル済みの実行ファイル)

参考にしたURL

文字の描画とデバイスコンテキストの説明
 https://shizenkarasuzon.hatenablog.com/entry/2020/02/25/171829

ボタンの作成
 https://bituse.info/winapi/5

ShowWindow関数のドキュメント(マイクロソフト)
 https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow

WindowsAPIは古い技術だ。でもWindowsAPIを使うと古いWindowsパソコン(95とかXP)でも動作可能なGUIプログラムが作成できる。Windows限定ではあるが環境を問わないアプリが作れるのはある意味面白い。

コメント

タイトルとURLをコピーしました