SDL_WINDOWIDでOpenGLが動いた

いったんwxWidgets+wxGLCanvasを使うことにしたのだが、
今度はLinuxのwxGTK側でSegmentationFault発生。
じつは、うちのGentooでは動かないことが発覚した。

再考した結果、
SDL_WINDOWIDがWindowsでうまくいかないところを追求する方向に転進。
SDLのソースを入手して、
デバッグ版を作ってVCのデバッガで丹念に死亡箇所を追跡してみることにした。

死亡寸前に通るところはここ。
src/video/windib/SDL_dibevents.c:240行〜

default: {
        /* Only post the event if we're watching for it */
        if ( SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE ) {
                SDL_SysWMmsg wmmsg;

                SDL_VERSION(&wmmsg.version);
                wmmsg.hwnd = hwnd;
                wmmsg.msg = msg;
                wmmsg.wParam = wParam;
                wmmsg.lParam = lParam;
                posted = SDL_PrivateSysWMEvent(&wmmsg);

        /* DJM: If the user isn't watching for private
                messages in her SDL event loop, then pass it
                along to any win32 specific window proc.
         */
        } else if (userWindowProc) {
                return CallWindowProc(userWindowProc, hwnd, msg, wParam, lParam);
        }

SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE
がfalseなので
return CallWindowProc(userWindowProc, hwnd, msg, wParam, lParam);
を通るが、この中で死ぬ。
でここでコールしているのはGtk::Socketが元々もっていたWindowプロシージャ
SDLが知らないイベントが来たので元のプロシージャに渡すという動作を
しておりました。
そこで、
SDL_PrivateSysWMEventが何をしているのかは知らないが
SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE
をtrueにしてやればいいじゃないのとなり、
それは

SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);

とすることでできた。


gtkmm+SDLをVCでコンパイルするというあまり需要なさそうな構成ですが
修正版のソースコード

#include <gtkmm.h>
#include <gtkmm/socket.h>
#ifdef _MSC_VER
#include <gdk/gdkwin32.h>
#include <glib.h>
#endif
#include <SDL.h>
#ifdef USE_OPENGL
#include <SDL_opengl.h>
#endif

#include <sstream>
#include <iostream>
#include <cassert>

#define WIDTH 320
#define HEIGHT 200

//------------------------------------------------------------//
// use Gtk socket
//
// http://sdl.beuc.net/sdl.wiki/FAQ_GUI
//
// base from
// http://lists.libsdl.org/pipermail/sdl-libsdl.org/2007-October/062896.html
// http://www.devolution.com/pipermail/sdl/2005-August/069884.html
//------------------------------------------------------------//
class GTKSDL : public Gtk::Socket
{
  SDL_Surface *surface_;

public:
  GTKSDL()
  : surface_(NULL)
  {
  }

  ~GTKSDL()
  {
    if (surface_) 
      SDL_FreeSurface(surface_);
    SDL_Quit();
  }

  bool draw()
  {
    if(!surface_){
      if(!initialize_()){
        return true;
      }
    }
    assert(surface_);

    // SDL draw loop
    SDL_Event event;
    while ( SDL_PollEvent( &event ) ) {
      // Handle quit event, not sure if this will ever appear
      if ( event.type == SDL_QUIT ){
        return false;
      }

      // Handle draw rect userevent
      if ( event.type == SDL_USEREVENT && event.user.code == 1 ) {

#ifdef USE_OPENGL
        static float t=0;
        const int N = 500;              // 頂点の数
        const float PI = 3.141593f;     // 円周率

        glClear(GL_COLOR_BUFFER_BIT);

        glBegin(GL_LINE_LOOP);
        glColor3f(1.0f, 1.0f, 1.0f);
        for (int i = 0; i < N; ++i) {
          glVertex3f(300*cos(3*2*PI*i/N + t) + 320,
              220*sin(5*2*PI*i/N - PI/4 + t) + 240,
              0.0);
        }
        glEnd();

        // 更新を画面に反映する
        SDL_GL_SwapBuffers();
        t+=0.01f;
#else
        // draw rect
        SDL_Rect rect;
        rect.x = rand() % WIDTH;
        rect.y = rand() % HEIGHT;
        rect.w = rand() % 100 + 10;
        rect.h = rand() % 100 + 10;
        SDL_FillRect(surface_, &rect, SDL_MapRGB(surface_->format,
              rand()%255, rand()%255, rand()%255 ) );
        SDL_Flip(surface_);
#endif

      }
    }

    // Forzar a que se dibuje un cuadrado nuevo
    event.type = SDL_USEREVENT;
    event.user.code = 1;
    SDL_PushEvent( &event );

    return true;
  }

private:
  // SDL_WINDOWID hack
  //------------------------------------------------------------//
  bool initialize_()
  {
      std::stringstream sdlhack;

#ifdef _MSC_VER
      HWND hwnd=
        static_cast<HWND>(GDK_WINDOW_HWND(get_window()->gobj()));
      sdlhack << "SDL_WINDOWID=" << (long)hwnd << std::ends;
      //SDL_putenv("SDL_VIDEODRIVER=windib");
#else
      sdlhack << "SDL_WINDOWID=" << get_id() << std::ends;
#endif
      char windowid[128];
      sprintf(windowid, "%s", sdlhack.str().c_str());
      SDL_putenv(windowid);

      if (SDL_Init(SDL_INIT_VIDEO)){
        return false;
      }

#ifdef USE_OPENGL
#ifdef _MSC_VER
      // skip Gtk::Socket window procedure     
      SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
#endif
      SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
      surface_ = SDL_SetVideoMode(
          WIDTH, HEIGHT, 0, SDL_OPENGL);
#else
      surface_ = SDL_SetVideoMode(
          WIDTH, HEIGHT, 0, SDL_HWSURFACE|SDL_DOUBLEBUF);
#endif

      if(!surface_){
        return false;
      }

#ifdef USE_OPENGL
      glClearColor(1.0f, 0.5f, 0.0f, 0.0f);
      glOrtho(0.0, WIDTH, HEIGHT, 0.0, -1.0, 1.0);
#endif

      return true;
  }
};

//------------------------------------------------------------//
// TopWindow
//------------------------------------------------------------//
class TopWindow : public Gtk::Window
{
  GTKSDL sdl_;
  Gtk::VBox vbox_;

public:
  TopWindow()
  {
    set_title("Ventana SDL");
    sdl_.set_size_request(WIDTH, HEIGHT);
    set_border_width(6);

    vbox_.pack_start(sdl_, false, false);
    add(vbox_);
    show_all();

    // SDL draw / 5 msec
    Glib::signal_timeout().connect(
        sigc::mem_fun(sdl_ ,&GTKSDL::draw), 5);
  }

};

void siginthandler(int signum)
{
  std::cerr << "SIGINT!\n";
  Gtk::Main::quit();
}

//------------------------------------------------------------//
int main(int argc, char ** argv)
{
#ifndef _MSC_VER
  signal(SIGINT, siginthandler);
#endif

  Gtk::Main kit(argc, argv);
  TopWindow window;
  Gtk::Main::run(window);
  return 0;
}

gentooWindows XPで動作確認。

似たようなところに潜っていた

http://risky-safety.org/zinnia/sdl/sourcetour/no4/index.html

ビルド環境参考

premake.lua

project.name = "solution" -- ソリューション名になる
project.configs = { "Debug", "Release" } -- コンフィグの種類を定義
project.config["Debug"].bindir   = "debug" -- デバッグ実行ファイルの出力先
project.config["Debug"].libdir   = "debug" -- デバッグライブラリの出力先
project.config["Release"].bindir = "release" -- リリース実行ファイルの出力先
project.config["Release"].libdir = "release" -- リリースライブラリの出力先

-- inline package
package = newpackage()
package.name       = "sample" -- パッケージ名
package.kind       = "winexe" -- 種類
package.language   = "c++" -- 言語
package.target     = "sample" -- 出力ファイル(拡張子やlib無し)

package.config["Debug"].defines={"DEBUG"}
package.config["Release"].defines={"NDEBUG"}
package.config["Release"].buildflags={"optimize"}

-- ソースとヘッダ
package.files = {
  matchfiles("*.cpp", "*.h", "*.def"),
}
package.defines={
  "USE_OPENGL"
}

if(windows)then
  package.buildoptions={
    "/wd4250", "/wd4520", "/wd4819"
  }

  package.buildflags={
    "no-main"
  }

  package.includepaths={
    "F:/gtkmm/include/gtkmm-2.4",
    "F:/gtkmm/lib/gtkmm-2.4/include",
    "F:/gtkmm/include/glibmm-2.4",
    "F:/gtkmm/lib/glibmm-2.4/include",
    "F:/gtkmm/include/sigc++-2.0",
    "F:/gtkmm/lib/sigc++-2.0/include",
    "F:/gtkmm/include/gdkmm-2.4",
    "F:/gtkmm/lib/gdkmm-2.4/include",
    "F:/gtkmm/include/pangomm-1.4",
    "F:/gtkmm/include/cairomm-1.0",
    "F:/gtkmm/include/atkmm-1.6",
    "F:/gtkmm/include/giomm-2.4",

    "F:/gtkmm/include/glib-2.0",
    "F:/gtkmm/include/gtk-2.0",
    "F:/gtkmm/include/pango-1.0",
    "F:/gtkmm/include/cairo",
    "F:/gtkmm/include/atk-1.0",
    "F:/gtkmm/lib/glib-2.0/include",
    "F:/gtkmm/lib/gtk-2.0/include",

    "F:/SDL/include",
  }
  package.libpaths={
    "F:/gtkmm/lib",
    "H:/Program Files/Microsoft Platform SDK/Lib",
  }
  package.config["Release"].libpaths={
    "F:/SDL/lib",
  }
  package.config["Debug"].libpaths={
    "F:/SDL/debug",
  }
  package.links={
    "SDL", "SDLmain", "gdk-win32-2.0", "gtk-win32-2.0", "opengl32"
  }
  package.config["Debug"].links={
    "gtkmm-vc90-d-2_4", "sigc-vc90-d-2_0", "glibmm-vc90-d-2_4", 
    "atkmm-vc90-d-1_6", "gdkmm-vc90-d-2_4"
  }
  package.config["Release"].links={
    "gtkmm-vc90-2_4", "sigc-vc90-2_0", "glibmm-vc90-2_4", 
    "atkmm-vc90-1_6", "gdkmm-vc90-2_4"
  }
else
  tinsert(package.buildoptions, {
    "`pkg-config --cflags gtkmm-2.4`",
    "`pkg-config --cflags sdl`"
  })
  tinsert(package.config["Debug"].buildoptions, {
    "-g", "-Wall"
  })
  tinsert(package.linkoptions, {
    "`pkg-config --libs gtkmm-2.4`",
    "`pkg-config --libs sdl`"
  })
  tinsert(package.config["Debug"].linkoptions, {
    "-g", "-Wall"
  })
end