glut的回调函数

Callback Function

从行为上概括,回调函数就是用函数指针去调用函数。这样起到的作用与直接调用函数相似,但是灵活性大大增加。

平时函数的参数是变量的值或变量的指针,调用者就只能传递这个有限的信息;而如果函数的参数可以是其他函数的指针,调用者就向被调用者传递了一个动作。

举个我们最熟悉的例子,C语言的stdlib.h中的qsort函数的原型是

1
void qsort (void* base, size_t num, size_t width, int(__cdecl* compare)(const void*, const void*));

在我们调用qsort的时候,不仅可以传递数组的基地址、数组的元素个数、每个元素占用的字节数这些基本信息,还可以传递“比较”这个动作。

库函数编写者的角度看更为明显,我需要编写一个函数foo实现功能A,但是功能A的实现过程中有部分动作是不确定的,由foo的调用者决定,就用函数指针做参数来实现。

总结:实体A提供函数a的指针,调用函数B,B实现的时候就**回调(Callback)**了函数a

意义

从面向对象的角度看,如果是为了实现上述的灵活性,应该尽量避免使用回调函数,去利用面向对象的特性。

现在我们利用回调是因为可以异步实现,有利有弊。由于本人水平有限,更多的例子可以参考知乎问题 回调函数(callback)是什么?

Callback Function in GLUT

Program Format

初学openGL,照着例程的格式写出了这样的程序

1
2
3
4
5
6
7
8
9
10
11
12
void graph (void) {
glBegin(...);
... // points
glEnd();
glFlush();
}
int main () {
... // initial
... // create window
glutDisplayFunc(graph);
glutMainLoop();
}

显然,最后的 glutDisplayFunc()glutMainLoop()是将画好的图形显示出来的关键。

Process of Execution

这里的glutDisplayFunc()属于回调函数,而glutMainLoop()的作用是进入一个死循环,不断检测程序之前注册的那些回调函数是否应该被调用,类似**轮询(polling)**机制。

对于glutDisplayFunc()来说,为了防止画面不停绘制占用CPU资源,只有在以下情况发生时才会再次执行

  • 窗口大小改变
  • 某个回调函数调用了glutPostRedisplay()

而其他回调函数则会在其对应的时候被触发并执行

Sample

先给出glut中那些 回调函数的原型

glutKeyboardFunc()

Prototype

1
void glutKeyboardFunc (void (*func)(unsigned char key, int x, int y));

Called

当按下的是字母键等有对应的ASCII码的键时被调用

Parameter

key:

按下的键

x, y:

鼠标的位置,注意原点(0, 0)为window的左上角

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// control the square with 'w', 'a', 's', 'd'
void keyboard (char key, int curr_x, int curr_y)
{
if (key == 'w') {
y -= 10;
}
else if (key == 'a') {
x -= 10;
}
else if (key == 's') {
y += 10;
}
else if (key == 'd') {
x += 10;
}
glutPostRedisplay();
}

glutSpecialFunc()

Prototype

1
void glutSpecialFunc (void (*func)(int key, int x, int y));

Called

当按下方向键等没有对应的ASCII码的键时被调用

Parameter

类似glutSpecialFunc()

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// control the square with 'up', 'down', 'left', 'right'
void keyboard_special (int key, int curr_x, int curr_y)
{
if (key == GLUT_KEY_UP) {
y -= 10;
}
else if (key == GLUT_KEY_LEFT) {
x -= 10;
}
else if (key == GLUT_KEY_DOWN) {
y += 10;
}
else if (key == GLUT_KEY_RIGHT) {
x += 10;
}
glutPostRedisplay();
}

glutMouseFunc()

Prototype

1
void glutMouseFunc (void (*func)(int button, int state, int x, int y));

Called

当鼠标左键/中键/右键被按下/释放时被调用

Parameter

button:

被按下/释放的是哪个键

state:

该键是被按下还是释放

x, y:

鼠标的位置,注意原点(0, 0)为window的左上角

Code

1
2
3
4
5
6
7
8
9
10
11
// make the square big by click left mouse
void mouse (int button, int state, int curr_x, int curr_y)
{
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
size += 4;
}
if (size >= 30) {
size = 5;
}
glutPostRedisplay();
}

glutMotionFunc(), glutPassiveMotionFunc()

Prototype

1
2
void glutMotionFunc (void (*func)(int x, int y));
void glutPassiveMotionFunc (void (*func)(int x, int y));

Called

当鼠标在被按下的时候移动时,glutMotionFunc()被调用;

当鼠标在松开的时候移动时,glutPassiveMotionFunc()被调用;

Parameter

x, y:

鼠标的位置,注意原点(0, 0)为window的左上角

Code

1
2
3
4
5
6
7
// drag the square by keep pressing the left mouse
void motion (int curr_x, int curr_y)
{
x = curr_x;
y = curr_y;
glutPostRedisplay();
}

glutTimerFunc()

Prototype

1
void glutTimerFunc (unsigned int msecs, void (*func)(int value), int value);

Called

直接被调用,在使用时一般在被调用的函数最后进行递归调用,继续累计时间

这个函数不是在运行到它时立即执行,所以不算死循环

常用于render

glutTimerFunc()中设置了glutPostRedisplay()后其他回调函数中可以不用再调用

Parameter

msecs:

时间间隔

value:

函数标记

Code

1
2
3
4
5
6
7
// act with interval
void timer (int value)
{
y += 1;
glutPostRedisplay();
glutTimerFunc(30, timer, 0);
}

glutIdleFunc()

Prototype

1
void glutIdleFunc (void (*func)(void));

Called

空闲时调用

应尽量减少空闲回调函数中的计算和渲染量,不然会影响其他部分的性能

Code

1
2
3
4
5
6
7
8
9
# include <windows.h> // Sleep() in windows

// act with interval
void idle ()
{
x += 2;
Sleep(20);
glutPostRedisplay();
}

Public Part

这段代码是上文各回调函数定义的调用部分

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
# include <gl\glut.h>

GLint windowSize = 500;
GLint x = 250, y = 250, size = 5;

int main (int argc, char * argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB);
glutInitWindowPosition(300, 100);
glutInitWindowSize(windowSize, windowSize);
glutCreateWindow("I'm a window");
glClearColor(1.0, 1.0, 1.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
gluOrtho2D(0, 500, 0, 500);

glutKeyboardFunc(keyboard);
glutSpecialFunc(keyboard_special);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutIdleFunc(idle);
glutDisplayFunc(graph);
glutTimerFunc(20, timer, 0);
glutMainLoop();
}

Complete Program

完整代码


几个有用的链接

按函数介绍GLUT(官方文档)

按功能介绍GLUT

按功能介绍GLUT_个人翻译版