今天不知神马原因,一下子突发奇想,试想了一下GTK与GLUT是否可以相互协作,经过初步测试,这还算是可行的。在以下demo中,我们可以点击由GTK创建的按钮然后弹出用GLUT创建的窗口。由于GTK与GLUT是两个完全独立的库,两者均含有各自的消息循环(也就是通常所说的runloop),因此当我们点击按钮弹出GLUT窗口的时候,其实相应的都是GLUT的消息循环,GTK此时处于冻结状态。只有当我们将GLUT窗口关闭之后,GTK窗口才会被再次激活,侦听消息。
跟以往一样,这里先给出本次demo所需的shell文件内容,便于编译构建。
clang main.c -std=gnu11 -fblocks -lBlocksRuntime -lglut -lGL -I/usr/include/glib-2.0/ -I/usr/include/atk-1.0/ -I/usr/include/gdk-pixbuf-2.0/ -I/usr/include/cairo/ -I/usr/include/pango-1.0/ -I/usr/lib/x86_64-linux-gnu/glib-2.0/include/ -I/usr/include/gtk-3.0/ -L/usr/lib/x86_64-linux-gnu/ -lgtk-3 -lgobject-2.0 -lpangocairo-1.0 -lgio-2.0 -latk-1.0 -lgdk-3 -lglib-2.0 -o gtk-glut
同样,如果各位使用的是GCC编译器,那么将上述的clang变为gcc,同时删除 -fblocks -lBlocksRuntime
这两个命令行选项。
下面则是C源代码。
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdnoreturn.h>
#include <stdatomic.h>
#include <string.h>
#include <assert.h>
#include <gtk/gtk.h>
#include <glib/gprintf.h>
#include <GL/freeglut.h>
#include <GL/gl.h>
#ifndef var
#define var __auto_type
#endif
static const GLfloat sRectVertices[] = {
// top left
-0.4f, 0.4f,
// bottom left
-0.4f, -0.4f,
// top right
0.4f, 0.4f,
// bottom right
0.4f, -0.4f
};
static const GLfloat sTriangleVertices[] = {
// top center
0.0f, 0.4f,
// bottom left
-0.4f, -0.4f,
// bottom right
0.4f, -0.4f
};
static const GLfloat sColors[] = {
// red
1.0f, 0.0f, 0.0f, 1.0f,
// green
0.0f, 1.0f, 0.0f, 1.0f,
// blue
0.0f, 0.0f, 1.0f, 1.0f,
// white
1.0f, 1.0f, 1.0f, 1.0f
};
static int sRotAngle = 0;
static void TimerHandler(int value);
static void RenderHandler(void)
{
// 以50FPS的帧率进行刷新
glutTimerFunc(20, TimerHandler, 0);
glClear(GL_COLOR_BUFFER_BIT);
// Draw rectangle
glVertexPointer(2, GL_FLOAT, 0, sRectVertices);
glColorPointer(4, GL_FLOAT, 0, sColors);
glLoadIdentity();
glTranslatef(-0.5f, 0.0f, -2.0f);
glRotatef(sRotAngle, 0.0f, 0.0f, 1.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Draw triangle
glVertexPointer(2, GL_FLOAT, 0, sTriangleVertices);
glColorPointer(4, GL_FLOAT, 0, sColors);
glLoadIdentity();
glTranslatef(0.5f, 0.0f, -2.1f);
glRotatef(-sRotAngle, 0.0f, 0.0f, 1.0f);
glDrawArrays(GL_TRIANGLES, 0, 3);
glFlush();
glutSwapBuffers();
if(++sRotAngle == 360)
sRotAngle = 0;
}
static void TimerHandler(int value)
{
RenderHandler();
}
static int sGLUTWindow;
static int sStoredArgC;
static char **sStoredArgv;
static void GLUTWindowCloseHandler(void)
{
glutDestroyWindow(sGLUTWindow);
glutLeaveMainLoop();
}
static void ShowOpenGLWindow(GtkWidget *widget, gpointer window)
{
if(window != NULL)
g_printf("Button clicked\n");
// 每次打开GLUT窗口前必须先调用一次glutInit函数
glutInit(&sStoredArgC, sStoredArgv);
// 当用户点击GLUT窗口时,应用不直接退出
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
glutCloseFunc(GLUTWindowCloseHandler);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_MULTISAMPLE);
glutInitWindowSize(480, 480);
glutInitWindowPosition(200, 100);
sGLUTWindow = glutCreateWindow("OpenGL GLUT Window");
glutSetOption(GLUT_MULTISAMPLE, 4);
glutDisplayFunc(RenderHandler);
var vendor = (const char*)glGetString(GL_VENDOR);
var renderer = (const char*)glGetString(GL_RENDERER);
var version = (const char*)glGetString(GL_VERSION);
g_printf("The vendor is: %s\n", vendor);
g_printf("The renderer is: %s\n", renderer);
g_printf("The GL version is: %s\n", version);
glViewport(0, 0, 480, 480);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glShadeModel(GL_SMOOTH);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glEnable(GL_MULTISAMPLE_ARB);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 3.0f);
glMatrixMode(GL_MODELVIEW);
glutMainLoop();
g_printf("GLUT runloop has quit!\n");
}
int main(int argc, char *argv[])
{
// 保存argc与argv
sStoredArgC = argc;
sStoredArgv = argv;
gtk_init(&argc, &argv);
var window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GTK-GLUT");
gtk_window_set_default_size(GTK_WINDOW(window), 480, 320);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
var button = gtk_button_new_with_label("Show GLUT");
gtk_widget_set_halign(button, GTK_ALIGN_START);
gtk_widget_set_valign(button, GTK_ALIGN_START);
gtk_widget_set_tooltip_text(button, "This is a button widget");
g_signal_connect(button, "clicked", G_CALLBACK(ShowOpenGLWindow), window);
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show_all(window);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
static_assert(sizeof(int) == 4, "Not compatible architecture!");
puts("Will enter GTK runloop...");
gtk_main();
puts("Program terminated!");
}
我们编译构建完此程序之后,直接在命令行输入gtk-glut这个app即可运行。我们每次点击按钮均能弹出GLUT窗口,点击关闭GLUT窗口可再次对GTK窗口中的按钮进行点击。