GLFW 输入系统实战指南:从事件处理到键盘、鼠标与手柄输入
发布时间:2026/9/14 13:14:32 作者:尧图编辑部 阅读量:1,286

GLFW 输入系统实战指南从事件处理到键盘、鼠标与手柄输入【免费下载链接】glfwA multi-platform library for OpenGL, OpenGL ES, Vulkan, window and input项目地址: https://gitcode.com/GitHub_Trending/gl/glfw本文围绕 GLFW 官方的输入指南docs/input.md展开系统讲解 GLFW 的全部输入能力事件处理三件套轮询/等待/超时等待、键盘按键与字符输入、鼠标位置、光标模式与光标对象、摇杆与游戏手柄含 SDL 兼容映射格式、高精度时间、剪贴板与文件拖放。读完后你可以完整实现一个跨平台应用的输入层并能对照 GLFW 源码src/input.c、src/window.c理解各回调与轮询函数背后的事件管线。输入模型总览回调与轮询并存GLFW 提供多种输入通道其交互方式各不相同只能轮询如高精度时间glfwGetTime只能回调如鼠标滚轮scrolling两者兼备按键、鼠标按钮、光标位置等既可以通过回调接收也可以用glfwGetKey等函数轮询。回调callback比轮询用起来更繁琐但 CPU 开销更低并且能保证你不漏掉任何状态变化。原文档给出了明确建议优先回调轮询作为兜底。所有输入回调都接收一个窗口句柄GLFWwindow*。借助 窗口用户指针glfwSetWindowUserPointer你可以在回调中访问非全局的结构体或对象这是处理多窗口场景的标准做法。想要直观感受各类事件回调的行为可以运行仓库自带的events测试程序源码见 tests/events.c。它注册了 GLFW 支持的全部回调并打印每个事件的所有参数、时间戳与序号信息是调试输入问题最快的工具。事件处理输入到达应用的前提GLFW 必须定期向窗口系统轮询事件这既是为了把输入送达到应用也是为了向窗口系统证明应用没有锁死很多窗口系统会据此判断无响应并允许强制关闭。事件处理通常放在每帧渲染结束、缓冲交换glfwSwapBuffers之后。即使当前没有任何窗口也必须处理事件否则收不到显示器与摇杆的热插拔事件。事件处理相关的全部实现集中在 src/window.c 中四个核心 API 分别位于glfwPollEvents— src/window.cglfwWaitEvents— src/window.cglfwWaitEventsTimeout— src/window.cglfwPostEmptyEvent— src/window.cglfwPollEvents持续渲染的首选glfwPollEvents();它只处理已经接收到的事件处理完立即返回不会阻塞。这是持续渲染场景大多数游戏的最佳选择每帧调一次即可。glfwWaitEvents事件驱动的理想选择glfwWaitEvents();当窗口内容只需要在新输入到达时更新就应该用它。它会让当前线程挂起直到至少收到一个事件然后处理所有已收到的事件节省大量 CPU 周期适合编辑类工具等事件驱动型应用。glfwWaitEventsTimeout带周期任务的等待glfwWaitEventsTimeout(0.7);如果你需要等待事件但又有 UI 元素或其他任务需要周期性刷新例如 HUD、网络心跳用超时版等待线程挂起直到收到至少一个事件或指定秒数如 0.7 秒过去然后处理期间收到的所有事件。glfwPostEmptyEvent从其他线程唤醒等待glfwPostEmptyEvent();如果主线程正睡眠在glfwWaitEvents中你可以从另一个线程向事件队列投递一个空事件把它唤醒。重要陷阱回调不一定只在上述函数中触发不要假设回调只会在这三个事件处理函数的调用中被触发。某些窗口系统要求 GLFW 自己注册回调这些回调可能在很多窗口系统函数调用期间收到事件GLFW 会在返回前把事件转交给你的回调。文档给出的经典例子是 Windows实现glfwSetWindowSize的系统函数会直接向每个窗口的过程回调投递尺寸事件。如果你设置了窗口尺寸回调那么它会在glfwSetWindowSize调用返回之前就先被调用此时拿到新尺寸。这一点在实现按尺寸重建帧缓冲等逻辑时尤其要注意。从源码结构看这一机制对应平台层的_glfwPollEvents*家族Win32 在 src/win32_window.c_glfwPollEventsWin32、Cocoa 在 src/cocoa_window.m_glfwPollEventsCocoa内部基于NSRunLoop、X11/Wayland 在src/x11_window.c/src/wl_window.c、Null 平台在 src/null_window.c。各平台通过函数表如 src/cocoa_init.m 中.pollEvents _glfwPollEventsCocoa统一挂接到window.c的公共入口。键盘输入GLFW 把键盘输入分为两类按键事件key events与真实的物理按键相关用按键 token如GLFW_KEY_E标识字符事件character events与按键产生的文本相关由操作系统的文本输入系统生成。两者不是一一对应一次按键可能产生多个字符如 CtrlC一个字符也可能需要多次按键如死音符。你的键盘布局、输入法与用户的可能完全不同因此做游戏按键用 key 事件做文本输入用 char 事件。按键输入Key input要在物理按键按下、释放或重复时收到通知设置按键回调实现见 src/input.cglfwGetKey位于 src/input.cglfwSetKeyCallback(window, key_callback);回调函数接收键盘按键 token、平台相关的扫描码scancode、按键动作action和修饰位modifier bitsvoid key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { if (key GLFW_KEY_E action GLFW_PRESS) activate_airship(); }action取值之一GLFW_PRESS、GLFW_REPEAT、GLFW_RELEASE。GLFW_PRESS与GLFW_RELEASE事件对每次按键都会成对发出大多数按键在持续按住期间还会发出GLFW_REPEAT事件。几点必须知道的行为细节按键滚落key rollover许多键盘同时能识别的按键数量有上限超限时会丢键。不要依赖GLFW_REPEAT重复事件按用户在键盘设置中的频率发出即使按住多个键也最多只有一个键重复。不要用GLFW_REPEAT来判断当前按住了哪些键或驱动动画。正确做法是基于GLFW_PRESS/GLFW_RELEASE自己维护按键状态或调用glfwGetKey提供基本的缓存按键状态。GLFW_KEY_UNKNOWN按键是既有的 key token 之一若 GLFW 没有对应 token如 E-mail 键、Play 键则为GLFW_KEY_UNKNOWN。扫描码可以持久化每个物理键的 scancode 唯一平台相关但随时间稳定因此可以安全地存盘。可以用glfwGetKeyScancode查询任意受支持 key token 的扫描码const int scancode glfwGetKeyScancode(GLFW_KEY_X); set_key_mapping(scancode, swap_weapons);轮询按键状态每个有 key token 的物理键的最后上报状态都保存在每窗口的状态数组中int state glfwGetKey(window, GLFW_KEY_E); if (state GLFW_PRESS) { activate_airship(); }返回值只有GLFW_PRESS或GLFW_RELEASE。注意glfwGetKey只返回缓存的按键事件状态不会向系统查询物理键的当前状态也不提供任何按键重复信息。GLFW_KEY_LAST保存所有 key token 中的最大值可用来做遍历上界。粘滞按键GLFW_STICKY_KEYS轮询状态有一个固有风险按键在被轮询前就释放了你会错过这次按下。推荐方案是使用按键回调若必须轮询可开启GLFW_STICKY_KEYS输入模式glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);开启后按键的可轮询状态会保持GLFW_PRESS直到你用glfwGetKey轮询了该键。轮询之后如果期间处理过该键的释放事件状态重置为GLFW_RELEASE否则保持GLFW_PRESS。锁定键修饰位GLFW_LOCK_KEY_MODS如果想知道 Caps Lock 和 Num Lock 在输入事件产生时的状态设置GLFW_LOCK_KEY_MODSglfwSetInputMode(window, GLFW_LOCK_KEY_MODS, GLFW_TRUE);开启后所有接收 修饰位 的回调都会带上相应位Caps Lock 开启时置GLFW_MOD_CAPS_LOCKNum Lock 开启时置GLFW_MOD_NUM_LOCK。文本输入Text inputGLFW 支持以 Unicode 码点流 形式的文本输入由操作系统的文本输入系统产生。与按键输入不同文本输入受键盘布局和修饰键影响并支持使用**死音键dead keys**组合字符。收到码点后你可以自行编码为 UTF-8 或其他任意编码。由于unsigned int在 GLFW 支持的所有平台上都是 32 位码点参数可以直接当作本机字节序的 UTF-32 处理。glfwSetCharCallback(window, character_callback);回调函数接收产生普通文本输入的按键事件对应的 Unicode 码点行为与该平台的普通文本框一致void character_callback(GLFWwindow* window, unsigned int codepoint) { }按键名称Key names若要以名称指代按键可以用glfwGetKeyName查询可打印键随键盘布局变化的名称实现见 src/input.cconst char* key_name glfwGetKeyName(GLFW_KEY_W, 0); show_tutorial_hint(Press %s to move forward, key_name);该函数同时接受 key 和 scancode如果指定的 key 是GLFW_KEY_UNKNOWN则使用 scancode否则 scancode 被忽略。这与按键回调的语义一致意味着回调参数可以原封不动地传给这个函数。鼠标输入鼠标输入形式多样移动、按钮按下、滚轮偏移此外光标外观还可以改成自定义图片或系统主题标准形状。光标位置Cursor position要在光标在窗口上移动时收到通知设置光标位置回调glfwSetCursorPosCallback(window, cursor_position_callback);回调接收的光标位置以屏幕坐标系度量但相对于窗口内容区域左上角。在平台支持时会传递完整的亚像素光标位置static void cursor_position_callback(GLFWwindow* window, double xpos, double ypos) { }光标位置同时按窗口保存可以用glfwGetCursorPos轮询double xpos, ypos; glfwGetCursorPos(window, xpos, ypos);光标模式Cursor modeGLFW_CURSOR输入模式提供几种针对特殊鼠标运动输入的光标模式默认为GLFW_CURSOR_NORMAL——使用普通箭头光标或用glfwSetCursor设置的其他光标运动不受限制。GLFW_CURSOR_DISABLED实现无边界鼠标运动的关键。要实现基于鼠标运动的相机控制等需要无限鼠标运动的输入方案glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);这会隐藏光标并将其锁定到指定窗口。GLFW 接管所有光标回中和偏移计算的细节向应用提供一个虚拟光标位置该虚拟位置照常通过光标位置回调和轮询两种方式提供。注意不要试图用 GLFW 的其他特性自行实现这个功能。官方明确声明这种用法不受支持也没有GLFW_CURSOR_DISABLED健壮。GLFW_CURSOR_HIDDEN仅隐藏光标行为正常、运动不受限glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);GLFW_CURSOR_CAPTURED光标可见但被限制在窗口内容区域内除非窗口失去焦点否则无法离开glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_CAPTURED);要从这两种特殊模式退出恢复GLFW_CURSOR_NORMAL即可若光标曾被禁用恢复后它会移回最后可见的位置glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);原始鼠标运动Raw mouse motion光标禁用时如果平台支持可以启用原始不缩放、不加速鼠标运动。原始运动更贴近鼠标在表面上的真实位移不受桌面光标缩放与加速影响——那套处理适合光标而原始运动更适合控制 3D 相机。正因如此原始运动只在光标禁用时提供if (glfwRawMouseMotionSupported()) glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);先用glfwRawMouseMotionSupported检查当前机器是否提供原始运动。该模式默认关闭支持时可按窗口随时开关但只有光标处于禁用状态时才会提供原始数据。光标对象Cursor objectsGLFW 支持创建自定义光标和系统主题光标统一封装为GLFWcursor对象。创建用glfwCreateCursor/glfwCreateStandardCursor实现分别位于 src/input.c 和 src/input.c销毁用glfwDestroyCursor或在glfwTerminate时一并销毁残留对象。自定义光标glfwCreateCursor返回创建的光标对象句柄。例如创建一个 16×16 白色方块光标热区在左上角unsigned char pixels[16 * 16 * 4]; memset(pixels, 0xff, sizeof(pixels)); GLFWimage image; image.width 16; image.height 16; image.pixels pixels; GLFWcursor* cursor glfwCreateCursor(image, 0, 0);创建失败时返回NULL必须检查返回值。图像数据为 32 位小端、未预乘的 RGBA每通道 8 位、红色在前像素按常规逐行排列从左上角开始。标准形状光标可以用glfwCreateStandardCursor从当前系统光标主题创建一个标准形状的光标GLFWcursor* url_cursor glfwCreateStandardCursor(GLFW_POINTING_HAND_CURSOR);这类对象的行为与glfwCreateCursor创建的完全相同只是实际图像由系统光标主题提供。部分形状并非处处可用不可用时返回NULL详见glfwCreateStandardCursor参考文档。光标销毁glfwDestroyCursor(cursor);光标销毁总是成功。若某个窗口当前正使用该光标该窗口会回退到默认光标不影响光标模式。调用glfwTerminate时所有残留光标都会被销毁。光标设置glfwSetCursor(window, cursor);设置后只要系统光标位于窗口内容区域内且光标模式为GLFW_CURSOR_NORMAL就会使用该光标图像。同一光标可设置给任意数量的窗口。传NULL恢复默认光标glfwSetCursor(window, NULL);光标被销毁时所有设置为它的光标也会回退到默认光标同样不影响光标模式。光标进入/离开事件glfwSetCursorEnterCallback(window, cursor_enter_callback);回调接收光标的最新分类void cursor_enter_callback(GLFWwindow* window, int entered) { if (entered) { // 光标进入窗口内容区域 } else { // 光标离开窗口内容区域 } }也可以用GLFW_HOVERED窗口属性查询光标当前是否位于内容区域内if (glfwGetWindowAttrib(window, GLFW_HOVERED)) { highlight_interface(); }鼠标按钮输入glfwSetMouseButtonCallback(window, mouse_button_callback);GLFW_UNLIMITED_MOUSE_BUTTONS要在回调中处理所有鼠标按钮而非只有存在按钮 token的按钮设置该输入模式glfwSetInputMode(window, GLFW_UNLIMITED_MOUSE_BUTTONS, GLFW_TRUE);开启后 GLFW 不再把上报的鼠标按钮限制为有 token 的按钮——这是出于对旧版 GLFW 的兼容性考虑旧版本从不上报GLFW_MOUSE_BUTTON_LAST之上的按钮用户可能依赖这一行为。回调接收鼠标按钮、动作和修饰位void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) { if (button GLFW_MOUSE_BUTTON_RIGHT action GLFW_PRESS) popup_menu(); }button为鼠标按钮 token 之一若设置了GLFW_UNLIMITED_MOUSE_BUTTONS也可以是任意其他正整数。action为GLFW_PRESS或GLFW_RELEASE。每个鼠标按钮 token 的最后上报状态同样保存在每窗口状态数组中用glfwGetMouseButton轮询不受GLFW_UNLIMITED_MOUSE_BUTTONS影响int state glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); if (state GLFW_PRESS) { upgrade_cow(); }返回值为GLFW_PRESS或GLFW_RELEASE且只返回缓存的事件状态不向系统查询真实状态。粘滞鼠标按钮GLFW_STICKY_MOUSE_BUTTONS与按键同理轮询可能错过按下又释放的短按。推荐用回调否则可开启glfwSetInputMode(window, GLFW_STICKY_MOUSE_BUTTONS, GLFW_TRUE);开启后按钮的可轮询状态保持GLFW_PRESS直到你用glfwGetMouseButton轮询。轮询后若期间处理过该按钮的释放事件则重置为GLFW_RELEASE否则保持GLFW_PRESS。GLFW_MOUSE_BUTTON_LAST保存所有鼠标按钮 token 中的最大值。滚轮输入ScrollingglfwSetScrollCallback(window, scroll_callback);无论是鼠标滚轮还是触控板手势滚动都会到达该回调。回调接收二维滚动偏移void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { }普通垂直鼠标滚轮只沿 Y 轴提供偏移。滚动只能回调、不能轮询——上文输入模型总览中只能回调的典型。摇杆输入Joystick摇杆函数暴露已连接的摇杆与手柄两者都称作 joystick最多支持16 个ID 从GLFW_JOYSTICK_1、GLFW_JOYSTICK_2一直到GLFW_JOYSTICK_16即GLFW_JOYSTICK_LAST。用glfwJoystickPresent测试某个摇杆是否存在int present glfwJoystickPresent(GLFW_JOYSTICK_1);每个摇杆拥有零个或多个轴axes、零个或多个按钮buttons、零个或多个帽键hats、一个人类可读名称、一个用户指针、以及一个 SDL 兼容的 GUID。分配规则检测到的摇杆加入数组开头。摇杆一旦被检测就保持其 ID 直到断开或库终止因此随着插拔出现ID 中可能出现空洞。轮询与事件的分工轴/按钮/帽状态在轮询时更新不需要窗口、也不需要处理事件但若要可靠地接收摇杆连接/断开事件到摇杆回调就必须处理事件。想实时查看所有已连接摇杆的全部属性运行 tests/joysticks.c 对应的joysticks测试程序。轴状态int count; const float* axes glfwGetJoystickAxes(GLFW_JOYSTICK_5, count);返回数组中每个元素为 -1.0 到 1.0 之间的值。注意查看参考文档中关于返回数组长度的说明数组仅在本次调用到下一次摇杆函数调用前有效。按钮状态int count; const unsigned char* buttons glfwGetJoystickButtons(GLFW_JOYSTICK_3, count);每个元素为GLFW_PRESS或GLFW_RELEASE。出于向后兼容按钮数组默认还包含所有帽键针对没有glfwGetJoystickHats的旧版本详见glfwGetJoystickButtons参考文档。帽键状态int count; const unsigned char* hats glfwGetJoystickHats(GLFW_JOYSTICK_7, count);每个元素取值如下名称值GLFW_HAT_CENTERED0GLFW_HAT_UP1GLFW_HAT_RIGHT2GLFW_HAT_DOWN4GLFW_HAT_LEFT8GLFW_HAT_RIGHT_UPGLFW_HAT_RIGHT|GLFW_HAT_UPGLFW_HAT_RIGHT_DOWNGLFW_HAT_RIGHT|GLFW_HAT_DOWNGLFW_HAT_LEFT_UPGLFW_HAT_LEFT|GLFW_HAT_UPGLFW_HAT_LEFT_DOWNGLFW_HAT_LEFT|GLFW_HAT_DOWN对角方向是主方向上下左右的按位组合可用按位与单独测试if (hats[2] GLFW_HAT_RIGHT) { // 帽键 2 的状态可能是 right-up、right 或 right-down }同样出于向后兼容所有帽键默认也会包含在按钮数组中。摇杆名称const char* name glfwGetJoystickName(GLFW_JOYSTICK_4);返回人类可读、UTF-8 编码的名称。名称不保证唯一——两个同品牌同型号的摇杆可能同名唯一性的只有摇杆 ID且只在断开前有效。摇杆用户指针每个摇杆都有一个用户指针glfwSetJoystickUserPointer(jid, pointer); glfwGetJoystickUserPointer(jid);可任意用途GLFW 不修改值会保留直到摇杆断开或库终止。初始值为NULL。摇杆连接/断开事件glfwSetJoystickCallback(joystick_callback);回调接收摇杆 ID 与事件类型void joystick_callback(int jid, int event) { if (event GLFW_CONNECTED) { // 摇杆已连接 } else if (event GLFW_DISCONNECTED) { // 摇杆已断开 } }要点要在所有平台上收到连接/断开事件必须调用某个事件处理函数断开也可能由摇杆函数调用时被动检测到并触发回调该函数随后返回已断开摇杆对应的值只有glfwGetJoystickName和glfwGetJoystickUserPointer对已断开的摇杆仍返回有用值且仅限于回调返回之前。手柄输入Gamepad摇杆函数提供的是无标签的轴、按钮和帽键无法指示它们在设备上的位置顺序甚至可能随平台变化。为解决该问题SDL 社区众包维护了SDL_GameControllerDB项目——一个把各种设备映射到类 Xbox 手柄的映射数据库。GLFW 支持该映射格式并在发布时内置了当时可用的映射副本生成产物即 src/mappings.h。映射会在摇杆连接或映射更新时自动分配。基本查询与状态判断摇杆既存在又有手柄映射if (glfwJoystickIsGamepad(GLFW_JOYSTICK_2)) { // 作为手柄使用 }如果只关心手柄输入可直接用它代替glfwJoystickPresent。查询映射提供的人类可读名称可能与摇杆名称相同也可能不同const char* name glfwGetGamepadName(GLFW_JOYSTICK_7);获取手柄状态实现见 src/input.cGLFWgamepadstate state; if (glfwGetGamepadState(GLFW_JOYSTICK_3, state)) { if (state.buttons[GLFW_GAMEPAD_BUTTON_A]) { input_jump(); } input_speed(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER]); }GLFWgamepadstate包含两个数组按钮状态与轴状态。取值与glfwGetJoystickButtons/glfwGetJoystickAxes一致按钮为GLFW_PRESS/GLFW_RELEASE轴为 -1.0 到 1.0含端点。数组大小与下标位置是固定的按钮下标GLFW_GAMEPAD_BUTTON_A、_B、_X、_Y、_LEFT_BUMPER、_RIGHT_BUMPER、_BACK、_START、_GUIDE、_LEFT_THUMB、_RIGHT_THUMB、_DPAD_UP、_DPAD_RIGHT、_DPAD_DOWN、_DPAD_LEFT另有用法等价别名GLFW_GAMEPAD_BUTTON_CROSS、_CIRCLE、_SQUARE、_TRIANGLE分别对应 A、B、X、Y。轴下标GLFW_GAMEPAD_AXIS_LEFT_X、_LEFT_Y、_RIGHT_X、_RIGHT_Y、_LEFT_TRIGGER、_RIGHT_TRIGGER。GLFW_GAMEPAD_BUTTON_LAST与GLFW_GAMEPAD_AXIS_LAST等于各数组的最大可用下标。手柄映射Gamepad mappings运行时可用glfwUpdateGamepadMappings追加新映射实现见 src/input.cconst char* mappings load_file_contents(game/data/gamecontrollerdb.txt); glfwUpdateGamepadMappings(mappings);该函数从单行到未修改的整个gamecontrollerdb.txt文件内容都支持。源码构建时更新内置映射用 CMake 从源码构建 GLFW 时可构建update_mappings目标它会运行 CMake/GenerateMappings.cmake 脚本下载gamecontrollerdb.txt并重新生成 src/mappings.h 头文件。映射格式由 SDL 与 SDL_GameControllerDB 项目定义其文档与代码具有最终解释权每条映射是一个逗号分隔值的单行GUID、名称、布局不以十六进制数字开头的行会被忽略第一个值始终是手柄 GUID32 字符的十六进制字符串通常标识厂商、型号、修订版本和连接类型信息不可得时用手柄名称生成。GLFW 使用 SDL 2.0.5 的 GUID 格式但可转换旧格式第二个值始终是人可读名称其后为字段:值形式的布局描述字段可缺省且顺序任意。按钮字段a、b、x、y、back、start、guide、dpup、dpright、dpdown、dpleft、leftshoulder、rightshoulder、leftstick、rightstick。轴字段leftx、lefty、rightx、righty、lefttrigger、righttrigger。字段取值可以是摇杆按钮bN如b2表示第 3 个按钮、摇杆轴aN如a7、帽键位掩码hN.N如h0.8表示第一个帽键的左方向可设多个位或空。修饰符轴前可加或-范围修饰如a3只取第 4 轴的负半区之外的正半区把输入限制在轴的正/负半区轴或半轴后可加~反转修饰如a2~或-a7~对该手柄轴的值取反。另有特殊的platform字段指定映射生效的平台取值为Windows、Mac OS X、Linux。下面是 GLFW 内置的 Windows 上经 XInput 访问的 Xbox 手柄映射为排版断成了多行实际映射必须是单行78696e7075740100000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0, b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8, rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4, righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,注意GLFW 尚不支持 SDL 新近加入的输出范围与修饰符、-输入修饰符、-、~是支持的如上所述。时间输入GLFW 提供以秒为单位的高精度时间输入double seconds glfwGetTime();返回自glfwInit初始化库以来的秒数。各平台底层计时源通常具有微秒或纳秒级分辨率。可以用glfwSetTime修改基准时间glfwSetTime(4.0);设置为指定秒数后从该点继续计数。还可以访问实现上述函数的原始计时器uint64_t value glfwGetTimerValue();该值以 1/频率 秒为单位。频率取决于操作系统与硬件用glfwGetTimerFrequency单位 Hz查询uint64_t frequency glfwGetTimerFrequency();各平台的原始计时器实现可参见 src/win32_time.c、src/posix_time.c、src/cocoa_time.c 等平台源文件。剪贴板输入与输出若系统剪贴板包含 UTF-8 字符串或可转换为 UTF-8用glfwGetClipboardString获取const char* text glfwGetClipboardString(NULL); if (text) { insert_text(text); }剪贴板为空或内容无法转换时返回NULL。用glfwSetClipboardString把系统剪贴板设置为 UTF-8 字符串glfwSetClipboardString(NULL, A string with words in it);路径拖放输入Path drop要接收拖放到窗口上的文件/目录路径设置文件拖放回调glfwSetDropCallback(window, drop_callback);回调接收以 UTF-8 编码的路径数组void drop_callback(GLFWwindow* window, int count, const char** paths) { int i; for (i 0; i count; i) handle_dropped_file(paths[i]); }数组及其字符串只在文件拖放回调返回前有效——它们可能是为该事件专门生成的。若需保留路径必须深拷贝数组。小结与调试建议事件是输入到达应用的唯一通道每帧glfwPollEvents持续渲染或glfwWaitEvents/glfwWaitEventsTimeout事件驱动并注意回调可能在其他 GLFW 调用内被触发按键与字符是两套输入游戏控制用 key 回调 自维护状态或glfwGetKey缓存文本输入用 char 回调GLFW_REPEAT不可靠、轮询会丢事件必要时开GLFW_STICKY_KEYS/GLFW_STICKY_MOUSE_BUTTONS相机类控制用GLFW_CURSOR_DISABLEDGLFW_RAW_MOUSE_MOTION不要用其他方式自行模拟手柄统一走glfwJoystickIsGamepadglfwGetGamepadState需要新设备时用glfwUpdateGamepadMappings或构建update_mappings目标更新 src/mappings.h调试输入行为首选 tests/events.c全量事件打印与 tests/joysticks.c手柄实时属性延迟类问题还可参考 tests/inputlag.c。【免费下载链接】glfwA multi-platform library for OpenGL, OpenGL ES, Vulkan, window and input项目地址: https://gitcode.com/GitHub_Trending/gl/glfw创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考