mieki256's diary



2022/09/09(金) [n年前の日記]

#1 [prog][windows] OpenGLを使ったWindows用スクリーンセーバをMinGWでコンパイル

Windows10 x64 21H2上で、OpenGLを使ったWindows用スクリーンセーバのサンプルを、MinGW を使ってコンパイル/ビルドしたい。

環境は、Windows10 x64 21H2 + MinGW (gcc 9.2.0) + freeglut 3.0.0-1。

MinGW-w64 (+ MSYS2) ではなくて、MinGW (+ MSYS) で作業。何度も何度も書くけれど、MinGW-w64 は libscrnsave.a (libscrnsavw.a) の中身が空っぽなのでスクリーンセーバをビルドできない。

参考にしたページは以下。OpenGL を使って、緑色の四角が円を描いて回るスクリーンセーバのソースが紹介されてる。リソースファイルやコンパイル手順までは紹介されてない。

_How to Scr: Writing an OpenGL Screensaver for Windows
_How to Scr: Sample Code for an OpenGL Screensaver

動作結果は以下。解像度が荒いけど雰囲気ぐらいは伝わるかと…。




ちなみに、このスクリーンセーバは OpenGL を使っているせいか、デスクトップキャプチャツール (OBS Studio 28.0.1) で数秒ほどキャプチャできなかった。デスクトップ画面が通常状態から OpenGL に切り替わる際にキャプチャできなくなるっぽい…。

MinGWにfreeglutを導入。 :

MinGW で freeglut を使えるようにする手順は、昨日メモしたのでそちらを参考に。

_MinGWでfreeglutを使ってみた

ソース。 :

ソースファイル群とアイコンをzipにして置いておく。

_greensquare_src.zip

中身は以下。
  • greensquare.cpp
  • icon.ico
  • Makefile
  • resource.h
  • resource.rc


一応、各ファイルも載せておきます。

リソースファイルは以下。ちなみに、元の参考ページではリソースファイルが掲載されてなかったので、Resource Hacker 5.1.8 を使って .scr からリソースを逆算(?)して書いてみた。

_resource.rc
/* resource.rc */

#include <windows.h>
#include <scrnsave.h>
#include "resource.h"

#define IDC_STATIC_ICON     2300
#define IDC_STATIC_TEXT     2301

/* LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US */

/* Screensaver title */
STRINGTABLE
BEGIN
  IDS_DESCRIPTION       "Green Square Example Scr"
END

/* Icon */
ID_APP  ICON    "icon.ico"

/* Dialog */
DLG_SCRNSAVECONFIGURE DIALOG DISCARDABLE  0, 0, 186, 82
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "GreenSquare Setup"
 FONT 11, "Segoe UI"
BEGIN
  DEFPUSHBUTTON "OK", IDOK, 39, 61, 50, 14 
  PUSHBUTTON    "Cancel", IDCANCEL, 107, 61, 50, 14
  AUTOCHECKBOX  "Square tumbles as it revolves", IDC_TUMBLE, 25, 35, 137, 12
  LTEXT "One option... that's all you get.", IDC_STATIC_TEXT, 17, 15, 150, 13
END
※ この日記システムの問題で、FONT指定行の見た目がおかしくなってます。行頭の空白を削除してもらえればと。


リソースファイルのヘッダファイルは以下。

_resource.h
#ifndef _RESOURCE_H_
#define _RESOURCE_H_

#define IDC_TUMBLE 1000

#define IDC_STATIC_ICON     2300
#define IDC_STATIC_TEXT     2301

#endif


C++ソースファイルは以下。ちなみにオリジナル版はレジストリの読み書きにも対応してるのだけど、いきなりレジストリに書き込むのはちょっと怖いので、以下のソースではレジストリ関連処理( GetConfig()、WriteConfig() ) は if (0) { 〜 } でコメントアウト/無効化してある。

_greensquare.cpp
// greensquare.cpp is an open-source example screensaver by Rachel Grey, lemming@alum.mit.edu.
// Paste into an IDE to compile if desired.
// I haven't chosen to include the resource file, so you'd need to provide a description string /and so forth.

#include <windows.h>
#include <scrnsave.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include "resource.h"

// get rid of these warnings: truncation from const double to float conversion from double to float
// #pragma warning(disable: 4305 4244)

//Define a Windows timer

#define TIMER 1

#define FPS 60

// globals for size of screen
static int Width, Height;

static bool bTumble = true;

// a global to keep track of the square's spinning
static GLfloat spin = 0;

/////////////////////////////////////////////////
////   INFRASTRUCTURE ENDS, SPECIFICS BEGIN   ///
////                                          ///
////    In a more complex scr, I'd put all    ///
////     the following into other files.      ///
/////////////////////////////////////////////////

// Initialize OpenGL
static void InitGL(HWND hWnd, HDC &hDC, HGLRC &hRC)
{
  PIXELFORMATDESCRIPTOR pfd;
  ZeroMemory(&pfd, sizeof pfd);
  pfd.nSize = sizeof pfd;
  pfd.nVersion = 1;
  //pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; //blaine's
  pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
  pfd.iPixelType = PFD_TYPE_RGBA;
  pfd.cColorBits = 24;

  hDC = GetDC(hWnd);

  int i = ChoosePixelFormat(hDC, &pfd);
  SetPixelFormat(hDC, i, &pfd);

  hRC = wglCreateContext(hDC);
  wglMakeCurrent(hDC, hRC);
}

// Shut down OpenGL
static void CloseGL(HWND hWnd, HDC hDC, HGLRC hRC)
{
  wglMakeCurrent(NULL, NULL);
  wglDeleteContext(hRC);
  ReleaseDC(hWnd, hDC);
}


void SetupAnimation(int Width, int Height)
{
  //window resizing stuff
  glViewport(0, 0, (GLsizei) Width, (GLsizei) Height);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  if (0)
    {
      glOrtho(-300, 300, -240, 240, 25, 75);
    }
  else
    {
      float w = 300;
      float h = ((w * 2) * Height / Width) / 2;
      glOrtho(-w, w, -h, h, 25, 75);
    }

  glMatrixMode(GL_MODELVIEW);

  glLoadIdentity();
  gluLookAt(0.0, 0.0, 50.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
  //camera xyz, the xyz to look at, and the up vector (+y is up)

  //background
  glClearColor(0.0, 0.0, 0.0, 0.0); //0.0s is black

  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

  // glShadeModel(GL_FLAT);
  glShadeModel(GL_SMOOTH);
}

void OnTimer(HDC hDC) //increment and display
{
  float xvals[] = {-30.0, -30.0, 30.0,  30.0};
  float yvals[] = {-30.0,  30.0, 30.0, -30.0};

  spin = spin + 1;

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glPushMatrix();

  glRotatef(spin, 0.0, 0.0, 1.0);
  glTranslatef(120, 0, 0);
  glRotatef(spin * ((bTumble) ? -4.0 : -1.0), 0.0, 0.0, 1.0);

  //draw the square (rotated to be a diamond)

  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  glColor3f(0.1, 1.0, 0.3); // set green color

  glBegin(GL_POLYGON);

  for (int i = 0; i < 4; i++)
    glVertex2f(xvals[i], yvals[i]);

  glEnd();

  glPopMatrix();

  glFlush();
  SwapBuffers(hDC);
}

void CleanupAnimation()
{
  // didn't create any objects, so no need to clean them up
}


/////////   REGISTRY ACCESS FUNCTIONS     ///////////

void GetConfig()
{
  if (0)
    {
      // get configuration from registry

      HKEY key;
      //DWORD lpdw;

      if (RegOpenKeyEx(HKEY_CURRENT_USER,
                       "Software\\GreenSquare", //lpctstr
                       0,                       //reserved
                       KEY_QUERY_VALUE,
                       &key) == ERROR_SUCCESS)
        {
          DWORD dsize = sizeof(bTumble);
          DWORD dwtype =  0;

          RegQueryValueEx(key, "Tumble", NULL, &dwtype,
                          (BYTE*)&bTumble, &dsize);

          //Finished with key
          RegCloseKey(key);
        }
      else //key isn't there yet--set defaults
        {
          bTumble = true;
        }
    }
  else
    {
      bTumble = true;
    }
}

void WriteConfig(HWND hDlg)
{
  if (0)
    {
      HKEY key;
      DWORD lpdw;

      const char* null_str = "";
      LPSTR str = const_cast<LPSTR>(null_str);

      if (RegCreateKeyEx(HKEY_CURRENT_USER,
                         "Software\\GreenSquare", // lpctstr
                         0,                       // reserved
                         str,                     // ptr to null-term string specifying the object type of this key
                         REG_OPTION_NON_VOLATILE,
                         KEY_WRITE,
                         NULL,
                         &key,
                         &lpdw) == ERROR_SUCCESS)

        {
          RegSetValueEx(key, "Tumble", 0, REG_DWORD,
                        (BYTE*)&bTumble, sizeof(bTumble));

          //Finished with keys
          RegCloseKey(key);
        }
    }
}

//////////////////////////////////////////////////
////   INFRASTRUCTURE -- THE THREE FUNCTIONS   ///
//////////////////////////////////////////////////

// Screen Saver Procedure
LRESULT WINAPI ScreenSaverProc(HWND hWnd, UINT message,
                               WPARAM wParam, LPARAM lParam)
{
  static HDC hDC;
  static HGLRC hRC;
  static RECT rect;

  switch (message)
    {
      case WM_CREATE:
        // get window dimensions
        GetClientRect(hWnd, &rect);
        Width = rect.right;
        Height = rect.bottom;

        // get configuration from registry
        GetConfig();

        // setup OpenGL, then animation
        InitGL(hWnd, hDC, hRC);
        SetupAnimation(Width, Height);

        // set timer to tick every 10 ms
        SetTimer(hWnd, TIMER, (int)(1000 / FPS), NULL);
        return 0;

      case WM_TIMER:
        OnTimer(hDC);       // animate!
        return 0;

      case WM_DESTROY:
        KillTimer(hWnd, TIMER);
        CleanupAnimation();
        CloseGL(hWnd, hDC, hRC);
        return 0;
    }

  return DefScreenSaverProc(hWnd, message, wParam, lParam);
}

BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg, UINT message,
                                       WPARAM wParam, LPARAM lParam)
{
  //InitCommonControls();
  //would need this for slider bars or other common controls

  HWND aCheck;

  switch (message)
    {
      case WM_INITDIALOG:
        LoadString(hMainInstance, IDS_DESCRIPTION, szAppName, 40);

        GetConfig();

        aCheck = GetDlgItem(hDlg, IDC_TUMBLE);
        SendMessage(aCheck, BM_SETCHECK, (bTumble) ? BST_CHECKED : BST_UNCHECKED, 0);
        return TRUE;

      case WM_COMMAND:
        switch (LOWORD(wParam))
          {
            case IDC_TUMBLE:
              bTumble = (IsDlgButtonChecked(hDlg, IDC_TUMBLE) == BST_CHECKED);
              return TRUE;

            //cases for other controls would go here

            case IDOK:
              WriteConfig(hDlg);  // get info from controls
              EndDialog(hDlg, LOWORD(wParam) == IDOK);
              return TRUE;

            case IDCANCEL:
              EndDialog(hDlg, LOWORD(wParam) == IDOK);
              return TRUE;
          }

        return FALSE;
    }  //end command switch

  return FALSE;
}

// needed for SCRNSAVE.LIB
BOOL WINAPI RegisterDialogClasses(HANDLE hInst)
{
  return TRUE;
}


アイコンファイルは以下。中に icon.ico が入ってる。

_icon.cio.zip

コンパイルの仕方。 :

Makefile は以下。

_Makefile
greensquare.scr: greensquare.o resource.o
        g++ greensquare.o resource.o -o greensquare.scr -static -lstdc++ -lgcc -lscrnsave -lopengl32 -lglu32 -lgdi32 -lcomctl32 -lwinmm -mwindows

greensquare.o: greensquare.cpp resource.h
        g++ -c greensquare.cpp

resource.o: resource.rc resource.h icon.ico
        windres resource.rc resource.o

.PHONY: clean
clean:
        rm -f *.scr
        rm -f *.o

Makefile があれば、make と打つだけでコンパイル/ビルドできる。ちなみに、make clean で *.o と *.scr を削除できる。

一応説明しておくと…。
  • windres resource.rc resource.o で、リソースファイル resource.rc を .o に変換する。
  • g++ -c greensquare.cpp で、.cpp をコンパイルして .o を生成。
  • g++ greensquare.o resource.o -o greensquare.scr ... で、.oファイル群をリンクして .scr を生成。

リンク時の指定については以下。
  • -static : スタティックリンクを指定。
  • -lstdc++ -lgcc : C言語の標準的なライブラリをリンク。
  • -lscrnsave : スクリーンセーバ用ライブラリをリンク。libscrnsave.a (libscrnsavw.a) をリンクする。
  • -lopengl32 -lglu32 : OpenGLのライブラリをリンク。
  • -lgdi32 : GDIライブラリをリンク。
  • -lcomctl32 : よく分かってないけれど、これをつけないと「*.dll が見つからない」と言われて .scr が動作しなかった。
  • -lwinmm : Windowsのタイマー関係ライブラリ、らしい。
  • -mwindows : Windows用アプリケーション(GUIアプリ)であることを指定。

動作確認。 :

greensquare.scr を右クリックして動作確認。
  • 「Test」を選ぶと、フルスクリーン表示の動作確認ができる。
  • 「構成」を選ぶと、スクリーンセーバ設定画面で「設定」をクリックした時の設定ダイアログが表示される。

MinGW で生成したので、この .scr は32bit版。それをWindows10 64bit上で動かしているので、C:\Windows\SysWOW64\ 以下に .scr をコピーして動作確認。スクリーンセーバとしても動いてくれた。

とりあえず、これで OpenGL を使ったスクリーンセーバを MinGW でコンパイル/ビルドできることは分かった。後は、OpenGL を使った描画処理部分を ―― OnTimer() のあたりを改造していけば、それらしいスクリーンセーバを作れそうな予感。

以上です。

過去ログ表示

Prev - 2022/09 - Next
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project