gtkと合わせて使う場合のコード

前々日に書いたんですが、OpenGLgtkを合わせて使うやりかたには随分なやみました。
結局Widgetの子ウィンドウとしてOpenGL描画領域用ウィンドウを作る方法をとったんですが…

調査中に作ったテストコードを載せておこうと思います。
(ひょっとしたら突っ込みが入るかもしれませんが…)

OpenGL用の描画領域が緑色になっていて、リサイズしたり、Exposeイベント発生時も再描画は出来るはずです。

//=============================================================================
//	プリプロセッサ
//=============================================================================
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <GL/gl.h>
#include <GL/glx.h>


//=============================================================================
//    グローバルパラメータ
//=============================================================================
Display*    g_display; // OpenGL用描画ウィンドウ作成時に使ったディスプレイデータ
Window        g_windowid; // OpenGL用描画ウィンドウのID
GLXContext    g_context; // OpenGLのコンテキスト
int G_WIDTH = 100;
int G_HEIGHT = 100;

//=============================================================================
//    関数宣言
//=============================================================================
gint cb_expose(GtkWidget* widget,
               GdkEventExpose* event,
               gpointer data);

void cb_size_allocate(GtkWidget           *widget,
                      GtkAllocation       *allocation);

gboolean cb_button_press(GtkWidget *widget,
                         GdkEvent *event,
                         gpointer data);

void initOpenGL(Display* display, Window windowid);


//=============================================================================
//    関数定義
//=============================================================================
/**
 * エントリーポイント
 */
int main(int argc, char *argv[]){
  // gtkの初期化
  gtk_init (&argc, &argv);
  gtk_set_locale();

  // ルートウィンドウの作成
  GtkWidget* rootwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_resize(GTK_WINDOW(rootwindow), 300, 300);


  // メニューバーの作成
  GtkWidget* menubar = gtk_menu_bar_new();
  GtkWidget* item = gtk_menu_item_new_with_label("File");

  // メニューバーにFileという名前のアイテムを加える
  gtk_container_add(GTK_CONTAINER(menubar), item);

  // メニューの作成
  GtkWidget* menu = gtk_menu_new();
  GtkWidget* subitem = gtk_menu_item_new_with_label("Open file");
  
  // メニューにOpen fileというアイテムを加える
  gtk_container_add(GTK_CONTAINER(menu), subitem);

  // メニューをメニューバーのアイテムに関連付ける
  gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);

 
  // 垂直パッキングボックスの作成
  GtkWidget* vbox = gtk_vbox_new(FALSE, 2);

  // 垂直パッキングボックスをルートウィンドウに加える
  gtk_container_add(GTK_CONTAINER(rootwindow), vbox);

  // メニューバーを垂直パッキングボックスに加える
  gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);


  // OpenGL描画領域の親ウィンドウとなるWidgetを作成
  GtkWidget* drawarea = gtk_drawing_area_new();
  gtk_widget_set_size_request(drawarea,
                              300,
                              300);

  // OpenGL描画領域の親ウィンドウとなるWidgetを垂直パッキングボックスに加える
  gtk_box_pack_start(GTK_BOX(vbox), drawarea, TRUE, TRUE, 0);
    
  // OpenGL描画領域の親ウィンドウとなるWidgetが受け取るシグナルを増やす
  // この処理は当然g_signal_connected関数の呼び出しより前に
  gtk_widget_add_events (drawarea, GDK_BUTTON_PRESS_MASK );

  // 全てのウィンドウを一旦描画
  gtk_widget_show_all(rootwindow);

  // OpenGLの描画
  initOpenGL(GDK_WINDOW_XDISPLAY(drawarea->window),
             GDK_WINDOW_XWINDOW(drawarea->window));
 
  // ルートウィンドウの破棄イベントに対してコールバック設定
  g_signal_connect_after(G_OBJECT(rootwindow),
                         "destroy",
                         G_CALLBACK(gtk_main_quit),
                         NULL);
 
  // ルートウィンドウの描画イベントに対してコールバック設定
  g_signal_connect_after(G_OBJECT(rootwindow),
                         "expose_event",
                         G_CALLBACK(cb_expose),
                         NULL);

  // OpenGL描画領域の親ウィンドウとなるWidgetのマウスボタン押しイベントに対してコールバック設定
  g_signal_connect_after(G_OBJECT(drawarea),
                         "button_press_event",
                         G_CALLBACK(cb_button_press),
                         NULL);

  // OpenGL描画領域の親ウィンドウとなるWidgetのリサイズイベントに対してコールバック設定
  g_signal_connect_after(G_OBJECT(drawarea),
                         "size_allocate",
                         G_CALLBACK(cb_size_allocate),
                         NULL);

  // gtkのメイン呼び出し
  gtk_main();
}


/**
 * OpenGLの初期化
 * \note
 * OpenGL描画領域をdisplayに作る、その際windowidの子ウィンドウとする
 * @param display ディスプレイ情報を持つデータ
 * @param windowid 親ウィンドウのID
 * @version 1.0
 */
void initOpenGL(Display* display, Window windowid){
  // OpenGLの設定パラメータ
  int attributeList[] = {GLX_DOUBLEBUFFER,
                         GLX_RGBA,
                         GLX_RED_SIZE, 8,
                         GLX_GREEN_SIZE, 8,
                         GLX_BLUE_SIZE, 8,
                         GLX_ALPHA_SIZE, 8,
                         GLX_DOUBLEBUFFER,
                         GLX_DEPTH_SIZE, 16,
                         None};
  
  // 指定されたdisplayにOpenGL用のXVisualInfoを作成
  XVisualInfo* vi = glXChooseVisual(display,
                                    DefaultScreen(display),
                                    attributeList);

  // OpenGL用のカラーマップを作成(親ウィンドウのIDを渡す)
  XSetWindowAttributes attribute;
  attribute.colormap
    = XCreateColormap( display,
                       windowid,
                       vi->visual,
                       AllocNone);
  attribute.background_pixel = 0;
  attribute.border_pixel = 0;

  // ここは本来何も指定せず、全てのイベントを親ウィンドウで受け取るべきか?
  attribute.event_mask = StructureNotifyMask | ExposureMask;

  unsigned int mask = CWBackPixel|CWBorderPixel|CWColormap|CWEventMask;

  // OpenGL描画領域用ウィンドウ作成
  g_windowid = XCreateWindow(display,
                             windowid,
                             0,
                             0,
                             G_WIDTH,
                             G_HEIGHT,
                             0,
                             vi->depth,
                             InputOutput,
                             vi->visual,
                             mask,
                             &(attribute));

  // OpenGL描画領域用ウィンドウを子ウィンドウとしてマッピング
  XMapSubwindows(display, windowid);

  // OpenGLのコンテキスト作成
  g_context = glXCreateContext(display,
                               vi,
                               None,
                               True);
  
  g_display = display;

  
  glXMakeCurrent(g_display, g_windowid, g_context);

}

/**
 * 描画イベントです
 */
gint cb_expose(GtkWidget* widget,
               GdkEventExpose* event,
               gpointer data)
{
  glXMakeCurrent(g_display, g_windowid, g_context);
  glViewport( 0,
              0,
              G_WIDTH,
              G_HEIGHT);

  // テスト用に緑色で塗りつぶします
  glClearColor(0, 1, 0, 0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

  glFlush();
  glXSwapBuffers(g_display, g_windowid);


  return 0;
}

/**
 * OpenGL描画領域がリサイズされている時に呼び出されるはずです
 */
void cb_size_allocate(GtkWidget           *widget,
                      GtkAllocation       *allocation)
{
  cout << "size" << endl;
 
  XResizeWindow(g_display,
                g_windowid,
                widget->allocation.width,
                widget->allocation.height);
  G_WIDTH = widget->allocation.width;
  G_HEIGHT = widget->allocation.height;

}

/**
 * マウスボタンが押された時のイベント
 * \note
 * OpenGL描画領域内でのイベントしかキャッチしない事を確認するために作りました。
 */
gboolean cb_button_press(GtkWidget *widget,
                         GdkEvent *event,
                         gpointer data)
{
  cout << "button" << endl;
}