2011年3月29日

MinGWを使ってスクリーンセーバを作ってみた

会社の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;
}