GL01-04:GLFW的上下文与事件交互

本文主要说明OpenGL技术中glfw的GL上下文与事件处理:
  1.glfw创建GL上下文;
  2.鼠标事件;
  3.键盘事件;
  4.文件删除事件;
  5.计时操作;


OpenGL上下文创建

关于上下文

上下文的创建与释放

  1. 创建

    • 使用glfwCreateWindow函数创建窗体的时候,就同时创建两两个环境:窗体与OpenGL或者OpenGL ES上下文;
  2. 释放

    • 上下文的释放与窗体与整个应用的释放一起,就是调用glfwDestroyWindow或者glfwTerminate函数的时候;

上下文的设置

  • 上下问的设置使用glfwWindowHint函数设置,并且在调用glfwCreateWindow之前。

两个窗体共享上下文

  • 在使用glfwCreateWindow创建窗体的时候,指定GLFWwindow * share参数即可。

离屏上下文

  • GLFW不支持在没有关联窗口的情况下创建上下文。但是,可以使用GLFW_VISIBLE窗体创建提示创建具有隐藏窗口的上下文。

  • 但是在Mac系统中,窗体创建的时候会自动创建菜单条,可以使用GLFW_COCOA_MENUBAR提示设置隐藏。

没有上下文的窗体

  • 创建窗体的时候,可以不创建上下文,只要使用GLFW_CLIENT_API提示的值为:GLFW_NO_API
  • 没有上下文的窗体不能传递给如下两个函数:
    • glfwMakeContextCurrent
    • glfwSwapBuffers

当前上下文

  1. 在进行OpenGL或OpenGL ES调用之前,需要具有正确类型的当前上下文。

    • 一个上下文一次只能是单个线程的当前上下文,而一个线程一次只能有一个当前上下文。
    • 在线程之间移动上下文时,必须先使旧线程上的上下文变成非当前,然后使其成为新线程上的当前上下文(否则两个线程都是当前上下文,就麻烦了)。
    • 设置当前上下文,使用函数glfwMakeContextCurrent
    • 返回当前上下文,使用函数glfwGetCurrentContext
  2. glfwMakeContextCurrent函数说明

    void glfwMakeContextCurrent (   GLFWwindow *    window  )   

  1. glfwGetCurrentContext函数说明
    
    GLFWwindow* glfwGetCurrentContext   (   void        )   

  1. 下面几个函数调用,必须有当前上下文,否则会产生错误GLFW_NO_CURRENT_CONTEXT
    • glfwSwapInterval
    • glfwExtensionSupported
    • glfwGetProcAddress

关于OpenGL的扩展

  1. 关于扩展

    • OpenGL和OpenGLES的一个好处是它们的可扩展性。硬件供应商可能在其实现中包含扩展,这些扩展在新版本的OpenGL或OpenGL ES规范中包含该功能之前对API进行扩展,并且某些扩展永远不会包含在其中,并且在它们过时之前一直作为扩展保留。
    • OpenGL和Direct3D比较起来,最大的一个长处就是其扩展机制。硬件厂商开发出一个新功能,可以针对新功能开发OpenGL扩展,软件开发人员通过这个扩展就可以使用新的硬件功能。所以虽然显卡的发展速度比OpenGL版本更新速度快得多,但程序员仍然可以通过OpenGL使用最新的硬件功能。而Direct3D则没有扩展机制,硬件的新功能要等到微软发布新版DirectX后才可能支持。
  2. glad扩展

    • https://glad.dav1d.de

在上下文上开始3D绘制

  • 基本步骤是:
    1. 创建上下文(通过创建窗体实现)
    2. 设置当前上下文;
    3. 初始化OpenGL
    4. 开始绘制
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>


int main(int argc, char const *argv[]){
    // 初始化
    glfwInit();
            // 1. 初始化opengl

    // 创建窗体
    GLFWwindow *window = glfwCreateWindow(800, 600, "OpenGL的UI窗体", NULL, NULL); 
    // 1. 第一步:设置当前上下文
    glfwMakeContextCurrent(window);
    // 2. 第二部:初始化OpenGL
    if (glewInit() != GLEW_OK){
        printf("OpenGL初始化失败:glew\n");
        exit(-1);
    }
    while (! glfwWindowShouldClose(window)){
        // 3. 设置清屏颜色
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        // 4. 清屏
        glClear(GL_COLOR_BUFFER_BIT);

        glfwSwapBuffers(window);
        glfwWaitEvents(); 
    }
    // 释放
    glfwTerminate();
    return 0;
}

// 编译命令:g++ -omain  trangle.cpp  -lglfw -lglew -framework opengl

  • 运行效果:
    • 窗体上的清屏绘制

键盘事件

键盘事件实现

  • 键盘事件使用回调函数实现:glfwSetKeyCallback
  1. 函数说明:
GLFWkeyfun glfwSetKeyCallback   (   
    GLFWwindow *    window,
    GLFWkeyfun  cbfun 
)   
  1. 回调函数GLFWkeyfun原型说明
typedef void(* GLFWkeyfun) (GLFWwindow *, int, int, int, int)
  • 参数说明:
    • window :发生事件的窗体
    • key :激发的键值
    • scancode :键值的系统扫描码
    • action:动作GLFW_PRESS, GLFW_RELEASE or GLFW_REPEAT.
    • mods: 辅助键ALT,CTRL,SHIFT,META等
  1. 事件实现代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>

void cb_key(GLFWwindow * win, int key, int code, int action, int mods){
    printf("key:%d, code:%d, action:%d, mods:%d\n", key, code, action, mods);
}

int main(int argc, char const *argv[]){
    // 初始化
    glfwInit();
            // 1. 初始化opengl

    // 创建窗体
    GLFWwindow *window = glfwCreateWindow(800, 600, "OpenGL的UI窗体", NULL, NULL); 
    // 处理事件
    glfwSetKeyCallback(window, cb_key);
    glfwMakeContextCurrent(window);
    if (glewInit() != GLEW_OK){
        printf("OpenGL初始化失败:glew\n");
        exit(-1);
    }
    while (! glfwWindowShouldClose(window)){
        // 3. 设置清屏颜色
        glClearColor(0.0f, 0.6f, 0.6f, 1.0f);
        // 4. 清屏
        glClear(GL_COLOR_BUFFER_BIT);

        glfwSwapBuffers(window);
        glfwWaitEvents(); 
    }
    // 释放
    glfwTerminate();
    return 0;
}

// 编译命令:g++ -omain  gl02_key_event.cpp  -lglfw -lglew -framework opengl

键值定义


    #define     GLFW_KEY_UNKNOWN   -1
    #define     GLFW_KEY_SPACE   32
    #define     GLFW_KEY_APOSTROPHE   39 /* ' */
    #define     GLFW_KEY_COMMA   44 /* , */
    #define     GLFW_KEY_MINUS   45 /* - */
    #define     GLFW_KEY_PERIOD   46 /* . */
    #define     GLFW_KEY_SLASH   47 /* / */
    #define     GLFW_KEY_0   48
    #define     GLFW_KEY_1   49
    #define     GLFW_KEY_2   50
    #define     GLFW_KEY_3   51
    #define     GLFW_KEY_4   52
    #define     GLFW_KEY_5   53
    #define     GLFW_KEY_6   54
    #define     GLFW_KEY_7   55
    #define     GLFW_KEY_8   56
    #define     GLFW_KEY_9   57
    #define     GLFW_KEY_SEMICOLON   59 /* ; */
    #define     GLFW_KEY_EQUAL   61 /* = */
    #define     GLFW_KEY_A   65
    #define     GLFW_KEY_B   66
    #define     GLFW_KEY_C   67
    #define     GLFW_KEY_D   68
    #define     GLFW_KEY_E   69
    #define     GLFW_KEY_F   70
    #define     GLFW_KEY_G   71
    #define     GLFW_KEY_H   72
    #define     GLFW_KEY_I   73
    #define     GLFW_KEY_J   74
    #define     GLFW_KEY_K   75
    #define     GLFW_KEY_L   76
    #define     GLFW_KEY_M   77
    #define     GLFW_KEY_N   78
    #define     GLFW_KEY_O   79
    #define     GLFW_KEY_P   80
    #define     GLFW_KEY_Q   81
    #define     GLFW_KEY_R   82
    #define     GLFW_KEY_S   83
    #define     GLFW_KEY_T   84
    #define     GLFW_KEY_U   85 
    #define     GLFW_KEY_V   86
    #define     GLFW_KEY_W   87
    #define     GLFW_KEY_X   88
    #define     GLFW_KEY_Y   89
    #define     GLFW_KEY_Z   90
    #define     GLFW_KEY_LEFT_BRACKET   91 /* [ */
    #define     GLFW_KEY_BACKSLASH   92 /* \ */
    #define     GLFW_KEY_RIGHT_BRACKET   93 /* ] */
    #define     GLFW_KEY_GRAVE_ACCENT   96 /* ` */
    #define     GLFW_KEY_WORLD_1   161 /* non-US #1 */
    #define     GLFW_KEY_WORLD_2   162 /* non-US #2 */
    #define     GLFW_KEY_ESCAPE   256
    #define     GLFW_KEY_ENTER   257
    #define     GLFW_KEY_TAB   258
    #define     GLFW_KEY_BACKSPACE   259
    #define     GLFW_KEY_INSERT   260
    #define     GLFW_KEY_DELETE   261
    #define     GLFW_KEY_RIGHT   262
    #define     GLFW_KEY_LEFT   263
    #define     GLFW_KEY_DOWN   264
    #define     GLFW_KEY_UP   265
    #define     GLFW_KEY_PAGE_UP   266
    #define     GLFW_KEY_PAGE_DOWN   267
    #define     GLFW_KEY_HOME   268
    #define     GLFW_KEY_END   269
    #define     GLFW_KEY_CAPS_LOCK   280
    #define     GLFW_KEY_SCROLL_LOCK   281
    #define     GLFW_KEY_NUM_LOCK   282
    #define     GLFW_KEY_PRINT_SCREEN   283
    #define     GLFW_KEY_PAUSE   284
    #define     GLFW_KEY_F1   290
    #define     GLFW_KEY_F2   291
    #define     GLFW_KEY_F3   292
    #define     GLFW_KEY_F4   293
    #define     GLFW_KEY_F5   294
    #define     GLFW_KEY_F6   295
    #define     GLFW_KEY_F7   296
    #define     GLFW_KEY_F8   297
    #define     GLFW_KEY_F9   298
    #define     GLFW_KEY_F10   299
    #define     GLFW_KEY_F11   300
    #define     GLFW_KEY_F12   301
    #define     GLFW_KEY_F13   302
    #define     GLFW_KEY_F14   303
    #define     GLFW_KEY_F15   304
    #define     GLFW_KEY_F16   305
    #define     GLFW_KEY_F17   306
    #define     GLFW_KEY_F18   307
    #define     GLFW_KEY_F19   308
    #define     GLFW_KEY_F20   309
    #define     GLFW_KEY_F21   310
    #define     GLFW_KEY_F22   311
    #define     GLFW_KEY_F23   312
    #define     GLFW_KEY_F24   313
    #define     GLFW_KEY_F25   314
    #define     GLFW_KEY_KP_0   320
    #define     GLFW_KEY_KP_1   321
    #define     GLFW_KEY_KP_2   322
    #define     GLFW_KEY_KP_3   323
    #define     GLFW_KEY_KP_4   324
    #define     GLFW_KEY_KP_5   325
    #define     GLFW_KEY_KP_6   326
    #define     GLFW_KEY_KP_7   327
    #define     GLFW_KEY_KP_8   328
    #define     GLFW_KEY_KP_9   329
    #define     GLFW_KEY_KP_DECIMAL   330
    #define     GLFW_KEY_KP_DIVIDE   331
    #define     GLFW_KEY_KP_MULTIPLY   332
    #define     GLFW_KEY_KP_SUBTRACT   333
    #define     GLFW_KEY_KP_ADD   334
    #define     GLFW_KEY_KP_ENTER   335
    #define     GLFW_KEY_KP_EQUAL   336
    #define     GLFW_KEY_LEFT_SHIFT   340
    #define     GLFW_KEY_LEFT_CONTROL   341
    #define     GLFW_KEY_LEFT_ALT   342
    #define     GLFW_KEY_LEFT_SUPER   343
    #define     GLFW_KEY_RIGHT_SHIFT   344
    #define     GLFW_KEY_RIGHT_CONTROL   345
    #define     GLFW_KEY_RIGHT_ALT   346
    #define     GLFW_KEY_RIGHT_SUPER   347
    #define     GLFW_KEY_MENU   348
    #define     GLFW_KEY_LAST   GLFW_KEY_MENU

修饰键定义

#define     GLFW_MOD_SHIFT   0x0001
#define     GLFW_MOD_CONTROL   0x0002
#define     GLFW_MOD_ALT   0x0004
#define     GLFW_MOD_SUPER   0x0008
#define     GLFW_MOD_CAPS_LOCK   0x0010
#define     GLFW_MOD_NUM_LOCK   0x0020

键值转换为扫描码

  • 使用函数
  1. glfwGetKeyScancode函数说明
int glfwGetKeyScancode  (   int     key )   

获取键值的状态

  • 获取某个键在制定窗体最后的状态,用来判定一个键是否按下,还是释放:glfwGetKey
  1. glfwGetKey函数说明

int glfwGetKey  (   
    GLFWwindow *    window,
    int     key 
)   
  1. GLFW_STICKY_KEYS粘附键设置

    • 如果模式是GLFW_STICKY_KEYS,则该值必须为GLFW_TRUE才能启用粘滞键,或者GLFW_FALSE才能禁用该模式。

    • 如果启用了粘滞键,按键将确保下次调用glfwGetKey时返回GLFW_PRESS,即使在调用之前已释放该键。当您只关心按键是否已按下,而不关心按键的时间或顺序时,这一点非常有用。

    • 设置输入模式使用:glfwSetInputMode

  2. glfwSetInputMode函数说明:


void glfwSetInputMode   (   
    GLFWwindow *    window,
    int     mode,
    int     value )     

  • 其中的mode定义只能是如下值:
    • GLFW_CURSOR:这个只能设置为:GLFW_CURSOR_NORMAL,GLFW_CURSOR_HIDDEN,GLFW_CURSOR_DISABLED。
    • GLFW_STICKY_KEYS,
    • GLFW_STICKY_MOUSE_BUTTONS,
    • GLFW_LOCK_KEY_MODS,
    • GLFW_RAW_MOUSE_MOTION

文件输入

  • 使用glfwSetCharCallback函数实现文本输入,既输入的是文本,包含unicode字符(一般是UTF-8编码)。
  1. glfwSetCharCallback函数说明:
GLFWcharfun glfwSetCharCallback (   
    GLFWwindow *    window,
    GLFWcharfun     cbfun 
)   
  1. 回调函数GLFWcharfun原型说明

typedef void(* GLFWcharfun) (GLFWwindow *, unsigned int)

  1. 文本输入的例子代码
    • 包含UTF-8字符的输出;
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <sstream>
using namespace std;

// 把codeppint转换为native字符。
void utf8chr(int cp, char c[5]){  //一个unicode最多占5个字节。
    // char c[5]={ 0x00,0x00,0x00,0x00,0x00 };
    if (cp<=0x7F) {   // 127的常规字符
        c[0] = cp;    
    }
    else if(cp<=0x7FF) {   
        c[0] = (cp>>6)+192; 
        c[1] = (cp&63)+128; 
    }
    else if(0xd800<=cp && cp<=0xdfff) {
        // 无效utf-8的codepoint区
    } //invalid block of utf8
    else if(cp<=0xFFFF) { 
        c[0] = (cp>>12)+224; 
        c[1]= ((cp>>6)&63)+128; 
        c[2]=(cp&63)+128; }
    else if(cp<=0x10FFFF) { 
        c[0] = (cp>>18)+240; 
        c[1] = ((cp>>12)&63)+128; 
        c[2] = ((cp>>6)&63)+128; 
        c[3]=(cp&63)+128; 
    }
}

void cb_char(GLFWwindow *win, unsigned int ch){
    char c[5]={ 0x00,0x00,0x00,0x00,0x00 };  // 一个unicode字符最多4字节,包含一个空终止符。
    utf8chr(ch, c);
    printf("输入的字符:%s\n", c);
}


int main(int argc, char const *argv[]){
    // 初始化
    glfwInit();
            // 1. 初始化opengl

    // 创建窗体
    GLFWwindow *window = glfwCreateWindow(800, 600, "OpenGL的UI窗体", NULL, NULL); 
    glfwSetCharCallback (window, cb_char);  
    glfwMakeContextCurrent(window);
    if (glewInit() != GLEW_OK){
        printf("OpenGL初始化失败:glew\n");
        exit(-1);
    }
    while (! glfwWindowShouldClose(window)){


        // 3. 设置清屏颜色
        glClearColor(0.0f, 0.6f, 0.6f, 1.0f);
        // 4. 清屏
        glClear(GL_COLOR_BUFFER_BIT);

        glfwSwapBuffers(window);
        glfwWaitEvents(); 
    }
    // 释放
    glfwTerminate();
    return 0;
}

// 编译命令:g++ -omain  gl04_char.cpp  -lglfw -lglew -framework opengl

  • 其中utf8chr函数是实现把codepoint转换为本地字符串。
    • codepoint转换为本地字符串最多4字节就是int32整数,当然使用5的长度是保证最后那个NULL字符。

键的名字

  • 上面对codepint解码,如果不了解codeoint与unicode,不了解utf-8的编码规则,一般不太容易理解。
  • GLFW提供函数处理:glfwGetKeyName
  1. glfwGetKeyName函数说明
const char* glfwGetKeyName  (   
    int     key,
    int     scancode 
)       
  • 如果key是GLFW_KEY_UNKNOWN,则对scancode解释。对key解释,则scancode为0。
  1. glfwGetKeyName函数使用例子
// glfwSetKeyCallback(window, cb_key);
void cb_key(GLFWwindow * win, int key, int code, int action, int mods){
    const char *str_ch = glfwGetKeyName(key, code);
    printf("glfwGetKeyName:%s\n", str_ch);

}

鼠标事件

光标位置

  • 鼠标的光标位置通过回调传递,回调函数通过函数glfwSetCursorPosCallback设置;
  • 也可以使用函数glfwGetCursorPos直接获取鼠标位置。
  1. glfwSetCursorPosCallback函数说明

GLFWcursorposfun glfwSetCursorPosCallback   (   
    GLFWwindow *    window,
    GLFWcursorposfun    cbfun 
)   
  1. 回调函数GLFWcursorposfun的原型说明

typedef void(* GLFWcursorposfun) (GLFWwindow *, double, double)

  1. glfwGetCursorPos函数说明
void glfwGetCursorPos   (   
    GLFWwindow *    window,
    double *    xpos,
    double *    ypos 
)   

光标模式

  • glfwSetInputMode函数可以控制鼠标光标模式

void glfwSetInputMode   (   
    GLFWwindow *    window,
    int     mode,
    int     value 
)   
  • 其中mode设置为光标的模式,光标模式包含3个值
    • GLFW_CURSOR,其值为:
      • GLFW_CURSOR_NORMAL:正常
      • GLFW_CURSOR_HIDDEN:隐藏
      • GLFW_CURSOR_DISABLED:隐藏和抓取光标,提供虚拟和无限的光标移动。

鼠标原生动作

  • 禁用光标时,可以启用原生(未缩放和未加速)鼠标运动(如果可用)。

  • 原生鼠标运动更接近鼠标在曲面上的实际运动。它不受应用于桌面光标运动的缩放和加速的影响。该处理适用于光标,而原生运动更适合控制例如3D相机。因此,只有在禁用光标时才提供原生鼠标运动。

  • 调用glfwRawMouseMotionSupported函数检查当前机器是否提供原始运动,并将GLFW_RAW_MOUSE_MOTION输入模式设置为启用。

  • 默认情况下禁用。

光标位置与模式的使用例子

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>

void cb_pos(GLFWwindow *win, double x, double y){
    char title[50];
    bzero(title, sizeof(title));
    sprintf(title, "坐标:(%8.2f,%8.2f)", x, y);
    glfwSetWindowTitle(win, title);

    double xpos, ypos;
    glfwGetCursorPos(win, &xpos, &ypos);   //与上面传递的位置完全一样。
    printf("坐标:(%8.2f,%8.2f)\n", xpos, ypos);
}


int main(int argc, char const *argv[]){
    // 初始化
    glfwInit();
            // 1. 初始化opengl

    // 创建窗体
    GLFWwindow *window = glfwCreateWindow(800, 600, "OpenGL的UI窗体", NULL, NULL); 
    // 鼠标光标模式
    // glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
    if (glfwRawMouseMotionSupported()){
        printf("原生鼠标模式\n");
        glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
        
    }else{
        printf("原生不支持鼠标模式\n");
    }
    // 处理事件
    glfwSetCursorPosCallback(window, cb_pos);
    glfwMakeContextCurrent(window); 
    if (glewInit() != GLEW_OK){
        printf("OpenGL初始化失败:glew\n");
        exit(-1);
    }
    while (! glfwWindowShouldClose(window)){
        // 3. 设置清屏颜色
        glClearColor(0.0f, 0.6f, 0.6f, 1.0f);
        // 4. 清屏
        glClear(GL_COLOR_BUFFER_BIT);

        glfwSwapBuffers(window);
        glfwWaitEvents(); 
    }
    // 释放
    glfwTerminate();
    return 0;
}

// 编译命令:g++ -omain  gl06_cursor.cpp  -lglfw -lglew -framework opengl

光标对象

创建光标

  • 光标对象是一个结构体
    • typedef struct GLFWcursor GLFWcursor

创建标准光标

  • 创建标准光标使用glfwCreateStandardCursor函数
  1. glfwCreateStandardCursor函数说明

    GLFWcursor* glfwCreateStandardCursor(int  shape)    

  • 创建好的光标使用下面的设置光标函数设置即可使用。
  1. 标准光标类型
    #define     GLFW_ARROW_CURSOR   0x00036001
    #define     GLFW_IBEAM_CURSOR   0x00036002
    #define     GLFW_CROSSHAIR_CURSOR   0x00036003
    #define     GLFW_HAND_CURSOR   0x00036004
    #define     GLFW_HRESIZE_CURSOR   0x00036005
    #define     GLFW_VRESIZE_CURSOR   0x00036006       

创建定制光标

  • 自定义光标是使用glfwCreateCursor函数创建的,它返回创建的光标对象的句柄。
  1. glfwCreateCursor函数说明

GLFWcursor* glfwCreateCursor    (   
    const GLFWimage *   image,
    int     xhot,
    int     yhot    //光标的参照点(热点hotspot)
)   

释放光标

  • 使用glfwDestroyCursor函数释放光标

    void glfwDestroyCursor  (   GLFWcursor *    cursor  )   

设置窗体光标

  • 当创建好光标后,就可以设置光标来使用,该函数为glfwSetCursor
  1. glfwSetCursor函数说明

void glfwSetCursor  (   
    GLFWwindow *    window,
    GLFWcursor *    cursor 
)   

使用光标对象的例子

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>



int main(int argc, char const *argv[]){
    // 初始化
    glfwInit();
            // 1. 初始化opengl

    // 创建窗体
    GLFWwindow *window = glfwCreateWindow(800, 600, "OpenGL的UI窗体", NULL, NULL); 
    // 创建光标对象
    GLFWcursor  *cursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR);

    // 创建一个定制光标
    unsigned char pixels[16 * 16 * 4];  // 光标的图像数据
    memset(pixels, 0xff, sizeof(pixels));  // 图像结构体
    GLFWimage image;
    image.width = 16;
    image.height = 16;
    image.pixels = pixels;
    GLFWcursor* c_cursor = glfwCreateCursor(&image, 0, 0);  // 使用图像创建光标

    // 设置光标
    // glfwSetCursor(window, cursor);
    glfwSetCursor(window, c_cursor);
    // 释放光标
    // glfwDestroyCursor(cursor);     // 释放光标,光标恢复成缺省光标
    glfwMakeContextCurrent(window); 
    if (glewInit() != GLEW_OK){
        printf("OpenGL初始化失败:glew\n");
        exit(-1);
    }
    while (! glfwWindowShouldClose(window)){
        // 3. 设置清屏颜色
        glClearColor(0.0f, 0.6f, 0.6f, 1.0f);
        // 4. 清屏
        glClear(GL_COLOR_BUFFER_BIT);

        glfwSwapBuffers(window);
        glfwWaitEvents(); 
    }
    // 释放
    glfwTerminate();
    return 0;
}

// 编译命令:g++ -omain  gl07_cursor_object.cpp  -lglfw -lglew -framework opengl

光标进出窗体事件

  • 光标进出状态通过两种方式:
    • 回调的事件方式:glfwSetCursorEnterCallback
    • 查询获取方式:glfwGetWindowAttrib + GLFW_HOVERED;
  1. glfwSetCursorEnterCallback函数说明

GLFWcursorenterfun glfwSetCursorEnterCallback   (   
    GLFWwindow *    window,
    GLFWcursorenterfun  cbfun 
)   
  1. 回调函数GLFWcursorenterfun原型说明

typedef void(* GLFWcursorenterfun) (GLFWwindow *, int)
    
    // 第二个参数是GLFW_TRUE:进,否则GLFW_FALSE:出

鼠标按钮输入

鼠标按钮事件处理

  • 这是通过回调函数的方式实现:glfwSetMouseButtonCallback
  1. glfwSetMouseButtonCallback函数说明
GLFWmousebuttonfun glfwSetMouseButtonCallback   (   
    GLFWwindow *    window,
    GLFWmousebuttonfun  cbfun 

  1. GLFWmousebuttonfun回调函数原型说明
typedef void(* GLFWmousebuttonfun) (GLFWwindow *, int, int, int)
        // 第二个参数:button
        // 第三个参数:action:GLFW_PRESS 或者 GLFW_RELEASE
        // 第四个参数:mods:辅助键状态

  1. 鼠标按钮定义
    #define     GLFW_MOUSE_BUTTON_1   0

    #define     GLFW_MOUSE_BUTTON_2   1

    #define     GLFW_MOUSE_BUTTON_3   2

    #define     GLFW_MOUSE_BUTTON_4   3

    #define     GLFW_MOUSE_BUTTON_5   4

    #define     GLFW_MOUSE_BUTTON_6   5

    #define     GLFW_MOUSE_BUTTON_7   6

    #define     GLFW_MOUSE_BUTTON_8   7

    #define     GLFW_MOUSE_BUTTON_LAST   GLFW_MOUSE_BUTTON_8

    #define     GLFW_MOUSE_BUTTON_LEFT   GLFW_MOUSE_BUTTON_1

    #define     GLFW_MOUSE_BUTTON_RIGHT   GLFW_MOUSE_BUTTON_2

    #define     GLFW_MOUSE_BUTTON_MIDDLE   GLFW_MOUSE_BUTTON_3
 

鼠标按钮的轮询

  • 就是鼠标输入的另外一种方式:轮询,不是听过事件;而是通过函数调用来查询状态。
    • 查询函数是:glfwGetMouseButton
  1. glfwGetMouseButton函数说明
int glfwGetMouseButton  (   
    GLFWwindow *    window,
    int     button 
)   
  • 返回值是:

    • GLFW_PRESS :按下过;
    • GLFW_RELEASE:没有按下过;
  • 参数button就是需要对按下判定的按钮。

  1. 为了防止在轮询的时候,按下动作已经完成,这样轮询容易错失那次鼠标按钮操作,解决这个问题与键盘的处理一样,通过设置输入模式来解决。
    • glfwSetInputMode(window, GLFW_STICKY_MOUSE_BUTTONS, GLFW_TRUE);

鼠标滚轮输入

  • 鼠标滚轮事件也是回调函数的方式来实现输入与处理。
    • 函数是:glfwSetScrollCallback
  1. glfwSetScrollCallback 函数说明
GLFWscrollfun glfwSetScrollCallback (   
    GLFWwindow *    window,
    GLFWscrollfun   cbfun 
)   
  1. 回调函数GLFWscrollfun原型说明
    typedef void(* GLFWscrollfun) (GLFWwindow *, double, double)

鼠标进出、滚轮与按钮事件的例子

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>

// 鼠标进出回调触发函数
void cursor_enter_callback(GLFWwindow *win, int enter_or_leave){
    if (enter_or_leave){
        printf("鼠标进入!\n");
    }
    else{
        printf("鼠标出去!\n");
    }
}

// 鼠标按钮
void mouse_button_callback(GLFWwindow *win, int button, int action, int mods){
    printf("鼠标按钮:button=%d,action=%d,mods=%d\n", button, action, mods);
}

// 鼠标滚轮
void scroll_callback(GLFWwindow *win, double xoffset, double yoffset){
    printf("鼠标滚轮:xoffset=%8.2f,yoffset=%8.2f\n", xoffset, yoffset);
}


int main(int argc, char const *argv[]){
    // 初始化
    glfwInit();
            // 1. 初始化opengl

    // 创建窗体
    GLFWwindow *window = glfwCreateWindow(800, 600, "OpenGL的UI窗体", NULL, NULL); 

    // 鼠标的粘附输入模式
    glfwSetInputMode(window, GLFW_STICKY_MOUSE_BUTTONS, GLFW_TRUE);

    // 鼠标进出判定 
    glfwSetCursorEnterCallback(window, cursor_enter_callback);

    // 鼠标按钮操作判定
    glfwSetMouseButtonCallback(window, mouse_button_callback);

    // 鼠标滚轮
    glfwSetScrollCallback(window, scroll_callback);
    
    glfwMakeContextCurrent(window); 
    if (glewInit() != GLEW_OK){
        printf("OpenGL初始化失败:glew\n");
        exit(-1);
    }
    while (! glfwWindowShouldClose(window)){

        // move轮询
        if (glfwGetWindowAttrib(window, GLFW_HOVERED)){
            printf("轮询:进入\n");
        }
        else{
            printf("轮询:出去\n");
        }

        //按键轮询
        int state = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT);
        if(state == GLFW_PRESS){
            printf("左键按下!\n");
        }

        // 3. 设置清屏颜色
        glClearColor(0.0f, 0.6f, 0.6f, 1.0f);
        // 4. 清屏
        glClear(GL_COLOR_BUFFER_BIT);

        glfwSwapBuffers(window);
        glfwWaitEvents(); 
    }
    // 释放
    glfwTerminate();
    return 0;
}

// 编译命令:g++ -omain  gl08_button_scroll_move.cpp  -lglfw -lglew -framework opengl

文件删除事件

  • 一旦用户删除文件,可以通过函数glfwSetDropCallback处理;
  1. glfwSetDropCallback函数说明
GLFWdropfun glfwSetDropCallback (   
    GLFWwindow *    window,
    GLFWdropfun     cbfun 
)   
  1. 回调函数GLFWdropfun原型说明

typedef void(* GLFWdropfun) (GLFWwindow *, int, const char **)
        // 第二个参数:count:删除文件个数
        // 第三个参数:paths:返回删除的文件列表

时间计时

  • GLFW提供四个与时间有关的函数:
    • glfwGetTime:获取从glfwInit初始化以来的时间,单位是秒;
    • glfwSetTime:修改从glfwInit初始化以来的时间,单位是秒;
    • glfwGetTimerValue:获取真实的定时器的时间值,单位是\dfrac{1}{frequency}秒;
    • glfwGetTimerFrequency:获取时间频率;
  • 注意:
    • 四个函数比较简单,这里不详细说明,下面是使用例子。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>

int main(int argc, char const *argv[]){
    // 初始化
    glfwInit();
            // 1. 初始化opengl

    // 创建窗体
    GLFWwindow *window = glfwCreateWindow(800, 600, "OpenGL的UI窗体", NULL, NULL); 
    
    glfwMakeContextCurrent(window); 
    if (glewInit() != GLEW_OK){
        printf("OpenGL初始化失败:glew\n");
        exit(-1);
    }
    while (! glfwWindowShouldClose(window)){
        double seconds = glfwGetTime();
        uint64_t value = glfwGetTimerValue();
        uint64_t freqency = glfwGetTimerFrequency();
        printf("相对时间:%8.2f,真实时间:%lld,频率:%lld\n", seconds, value, freqency);

        glfwSetTime(0.0);

        // 3. 设置清屏颜色
        glClearColor(0.0f, 0.6f, 0.6f, 1.0f);
        // 4. 清屏
        glClear(GL_COLOR_BUFFER_BIT);

        glfwSwapBuffers(window);
        glfwWaitEvents(); 
    }
    // 释放
    glfwTerminate();
    return 0;
}

// 编译命令:g++ -omain  gl09_timer.cpp  -lglfw -lglew -framework opengl


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容