SDLとwxWidgetsを一緒に使った

ウィンドウにメニューとか付けたくなったので、あれこれ考えた結果wxWidgetsを使うことにした。まずwxWidgets単体でウィンドウの表示、メニューの表示まで行えるのを確認して、それではSDLと一緒に使おうと思ったらやり方を思いつかなかった。そこで、ググって先人の成果を拝見することにした。
http://ilaliart.sourceforge.jp/cgi-bin/MySketch/ms.cgi?t=sketch&blogid=&ShowDiary_file=/SDL/1164731974
最初に見つけたのがここ。公開されているソースが手元でもコンパイル出来るところまでは確認したが、結局描画処理とか更新処理をどこに書けば良いのか分からなかった。それはそれとして、過去ログにはSDLwxWidgetsについていろいろ書かれているので今後も参考にしたい。
[CODEpendent] wx-sdl tutorial
先のブログで紹介されていたもの。シンプルな実装にホッとするが、あまりのシンプルさにちょっと不安になったり、main関数の姿が見えなかったりしたので、これはこれで置いといて、別のものを探すことにした。
primitive: product
最後はここのgl_kanjiに含まれるツールのソースコード。こちらもシンプルでわかりやすい実装で助かった。結局これをベースにすることにした。
で、書いたものは以下の通り。一応自分の中で咀嚼して書き直したつもりなんだけど、実質丸写しだよね。main関数からwxEntry関数を呼び出すことで双方の起動方法のつじつまを合わせた気でいるけど、どうなんだろう。

#include <wx/wxprec.h>

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif

#include <wx/dcbuffer.h>

#include <SDL.h>

// =============
// MyPanelクラス
// =============

class MyPanel : public wxPanel
{
  DECLARE_CLASS(MyPanel)
  DECLARE_EVENT_TABLE()
public:
  MyPanel(wxWindow *parent);
protected:
  void OnPaint(wxPaintEvent &event);
  void OnIdle(wxIdleEvent &event);
private:
  SDL_Surface *screen;
};

BEGIN_EVENT_TABLE(MyPanel, wxPanel)
  EVT_PAINT(MyPanel::OnPaint)
  EVT_IDLE(MyPanel::OnIdle)
END_EVENT_TABLE()

IMPLEMENT_CLASS(MyPanel, wxPanel)

MyPanel::MyPanel(wxWindow *parent)
  : wxPanel(parent, wxID_ANY), screen(NULL)
{
}

void MyPanel::OnPaint(wxPaintEvent &event)
{
  if (screen == NULL) {
    return;
  }

  if (SDL_MUSTLOCK(screen)) {
    if (SDL_LockSurface(screen) < 0) {
      return;
    }
  }

  wxBitmap bmp(wxImage(screen->w, screen->h, static_cast<unsigned char *>(screen->pixels), true));

  if (SDL_MUSTLOCK(screen)) {
    SDL_UnlockSurface(screen);
  }

  wxBufferedPaintDC dc(this, bmp);
}


void MyPanel::OnIdle(wxIdleEvent &event)
{
  // ここら辺にキー入力や、更新処理を書くはず(きっと)

  if (screen == NULL) {
    int width, height;
    GetSize(&width, &height);
    screen = SDL_AllocSurface(SDL_SWSURFACE, width, height, 24, 0, 0, 0, 0);
    if (screen == NULL) {
      return;
    }
  }

  if (SDL_MUSTLOCK(screen)) {
    if (SDL_LockSurface(screen) < 0) {
      return;
    }
  }

  SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0x00, 0x60, 0x00));

  // ここら辺に描画処理を書くのかもしれない(多分)

  if (SDL_MUSTLOCK(screen)) {
    SDL_UnlockSurface(screen);
  }

  Refresh(false);

  SDL_Delay(20);  // 適当すぎる
}

// =============
// MyFrameクラス
// =============

class MyFrame : public wxFrame
{
  DECLARE_CLASS(MyFrame)
  DECLARE_EVENT_TABLE()
public:
  MyFrame();
  MyPanel &GetPanel() const;
private:
  MyPanel *panel;
};

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
END_EVENT_TABLE()

IMPLEMENT_CLASS(MyFrame, wxFrame)

MyFrame::MyFrame()
  : wxFrame(NULL, -1, wxT("SDL + wxWidgets のサンプル"))
{
  panel = new MyPanel(this);
}

inline MyPanel &MyFrame::GetPanel() const
{
  return *panel;
}

// ===========
// MyAppクラス
// ===========

class MyApp : public wxApp
{
  DECLARE_CLASS(MyApp)
public:
  bool OnInit();
  int OnRun();
  int OnExit();
private:
  MyFrame *frame;
};

IMPLEMENT_APP_NO_MAIN(MyApp)
IMPLEMENT_CLASS(MyApp, wxApp)

bool MyApp::OnInit()
{
  if (SDL_Init(SDL_INIT_VIDEO) < 0) {
    return false;
  }

  frame = new MyFrame;
  frame->SetClientSize(640, 480);
  frame->Show(true);
  SetTopWindow(frame);

  return true;
}

int MyApp::OnRun()
{
  wxIdleEvent event;
  event.SetEventObject(&frame->GetPanel());
  frame->GetPanel().AddPendingEvent(event);
  return wxApp::OnRun();
}

int MyApp::OnExit()
{
  SDL_Quit();

  return 0;
}

DECLARE_APP(MyApp)

// ======
// その他
// ======

int main(int argc, char *argv[])
{
  return wxEntry(argc, argv);
}

ちなみに上記のMyPanel::OnIdle()ではSDL_Delay()を使って適当に時間をつぶしているが、もう少しましな方法を使いつつ実際にいくつか描画も行いながらfpsを測ったら30前後だった。今作っているものは肝心な部分はスレッド+WaitableTimerを使った定期呼び出しを行っていてfpsは無視できるため、画面の更新頻度は少なめでも良いのでこのままで行く。ということで、SDLwxWidgetsの同時利用について何とかなった、ような気がする。