会社のPC(XP)では、ニュースサイトのRSSフィードを定期バッチで取得し、それをHTMLでデザイン→CrenaHTMLで画像化して、「マイピクチャ スライドショー」でスクリーンセーバとして表示しています。
せっかくなので、自宅PC(Vista)でも導入しようとスクリーンセーバを選択してみると、Vistaから同スクリーンセーバの仕様が変わったらしく、あえなく断念。
次に「画像を一定期間で順番に中央表示するスクリーンセーバ」くらい山ほどフリーで転がっているだろうと検索してみたら…意外に存在せず、仕方ないのでMinGWを使ってスクリーンセーバを作ってみる事にしました。
MinGWは、以前ffmpegをビルドした時はパッケージを一つ一つダウンロードして環境構築したものの途中で面倒くさくなったのでwインストーラを使って導入。スクリーンセーバのソースはこちらのコードをベースにして作業しました。 (素敵な説明とソースコードありがとうございました)
久しぶりのC言語コーディングで文字列操作に心折れつつも、いろんなものを妥協してなんとか完成。無事自宅でも導入する事ができました。
// ビルドは以下のコマンドをMSYS上で実行
// gcc -Wall -O2 screensv.cpp -static -lstdc++ -lgdiplus -lscrnsave -mwindows -o screensv.scr
#include <windows.h>
#include <tchar.h>
#include <scrnsave.h>
#include <time.h>
#include <gdiplus.h>
using namespace Gdiplus;
#define ID_TIMER 101
#define IMAGE_DIR _T("D:\\path\\to\\images\\")
#define REFRESH_TIME 10000
int g_nFiles;
LPTSTR g_pszFiles[MAX_PATH];
int GetImageFiles(LPCTSTR pszPath, LPTSTR* pszFiles) {
HANDLE hFind;
WIN32_FIND_DATA fd;
TCHAR path[MAX_PATH];
LPTSTR ext;
int len;
int cnt;
// ファイル検索ディレクトリが指定されているか判断
if ((pszPath == NULL) || (_tcslen(pszPath) == 0)) {
return -1;
}
// ファイル検索ディレクトリを準備
lstrcpyn(path, pszPath, sizeof(path));
len = _tcslen(path);
if ((path[len - 1] != _T('\\')) && (path[len - 1] != _T('/'))) {
_tcscat(path, _T("\\"));
len++;
}
_tcscat(path, _T("*"));
// 画像ファイルを検索
hFind = FindFirstFile(path, &fd);
if (hFind == INVALID_HANDLE_VALUE) {
return -1;
}
cnt = 0;
do {
// ディレクトリ文字列か判断
if (((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
|| (_tcscmp(fd.cFileName, _T("..")) == 0)
|| (_tcscmp(fd.cFileName, _T(".")) == 0)) {
continue;
}
// 画像ファイルの拡張子以外か判断
ext = _tcsrchr(fd.cFileName, _T('.'));
if ((_tcsicmp(ext, _T(".JPG")) != 0)
&& (_tcsicmp(ext, _T(".PNG")) != 0)
&& (_tcsicmp(ext, _T(".GIF")) != 0)) {
continue;
}
// 画像ファイルのパスを格納
pszFiles[cnt] = (LPTSTR)malloc(sizeof(TCHAR) * (len + _tcslen(fd.cFileName) + 1));
lstrcpyn(pszFiles[cnt], path, len + 1);
_tcscat(pszFiles[cnt], fd.cFileName);
// 画像ファイル数をカウント
cnt++;
if (cnt > MAX_PATH) {
break;
}
} while (FindNextFile(hFind, &fd));
return cnt;
}
void OnPaint(HWND hWnd, LPTSTR pszPath) {
int x;
int y;
int w;
int h;
int imageWidth;
int imageHeight;
int windowWidth;
int windowHeight;
RECT rect;
HDC hDC;
PAINTSTRUCT ps;
WCHAR wszPath[MAX_PATH];
// 変数を初期化
MultiByteToWideChar(CP_ACP, 0, pszPath, -1, wszPath, MAX_PATH);
GetClientRect(hWnd, &rect);
windowWidth = rect.right - rect.left;
windowHeight = rect.bottom - rect.top;
// 画像を描画
hDC = BeginPaint(hWnd, &ps);
Graphics graphics(hDC);
Image image(wszPath);
imageWidth = image.GetWidth();
imageHeight = image.GetHeight();
if ((double)imageWidth / imageHeight < (double)windowWidth / windowHeight) {
w = windowHeight * imageWidth / imageHeight;
h = windowHeight;
} else {
w = windowWidth;
h = windowWidth * imageHeight / imageWidth;
}
x = (windowWidth - w) / 2;
y = 0;
graphics.DrawImage(&image, x, y, w, h);
EndPaint(hWnd, &ps);
}
LRESULT WINAPI ScreenSaverProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
ULONG_PTR gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;
switch (msg) {
case WM_CREATE: {
// 初期化処理
g_nFiles = GetImageFiles(IMAGE_DIR, g_pszFiles);
srand((unsigned)time(NULL));
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
SetTimer(hWnd, ID_TIMER, REFRESH_TIME, NULL);
break;
}
case WM_TIMER: {
// タイマーイベント
InvalidateRect(hWnd, NULL, FALSE);
break;
}
case WM_PAINT: {
// 画面描画
OnPaint(hWnd, g_pszFiles[(unsigned)time(NULL) % g_nFiles]);
break;
}
case WM_DESTROY: {
// 終了処理
KillTimer(hWnd, ID_TIMER);
GdiplusShutdown(gdiplusToken);
PostQuitMessage(0);
break;
}
default: {
break;
}
}
return DefScreenSaverProc(hWnd, msg, wParam, lParam);
}
BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
return TRUE;
}
BOOL WINAPI RegisterDialogClasses(HANDLE hInst) {
return TRUE;
}