local ffi = require 'ffi' local C = ffi.os == 'Windows' and ffi.load('glfw3') or ffi.C ffi.cdef [[ enum { GLFW_CURSOR = 0x00033001, GLFW_CURSOR_NORMAL = 0x00034001, GLFW_CURSOR_HIDDEN = 0x00034002, GLFW_CURSOR_DISABLED = 0x00034003, GLFW_ARROW_CURSOR = 0x00036001, GLFW_IBEAM_CURSOR = 0x00036002, GLFW_CROSSHAIR_CURSOR = 0x00036003, GLFW_HAND_CURSOR = 0x00036004, GLFW_HRESIZE_CURSOR = 0x00036005, GLFW_VRESIZE_CURSOR = 0x00036006 }; typedef struct { int width; int height; unsigned char* pixels; } GLFWimage; typedef struct GLFWcursor GLFWcursor; typedef struct GLFWwindow GLFWwindow; typedef void(*GLFWmousebuttonfun)(GLFWwindow*, int, int, int); typedef void(*GLFWcursorposfun)(GLFWwindow*, double, double); typedef void(*GLFWscrollfun)(GLFWwindow*, double, double); GLFWwindow* os_get_glfw_window(void); void glfwGetInputMode(GLFWwindow* window, int mode); void glfwSetInputMode(GLFWwindow* window, int mode, int value); void glfwGetCursorPos(GLFWwindow* window, double* x, double* y); void glfwSetCursorPos(GLFWwindow* window, double x, double y); GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot); GLFWcursor* glfwCreateStandardCursor(int kind); void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); int glfwGetMouseButton(GLFWwindow* window, int button); void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback); GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback); GLFWcursorposfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback); ]] local window = ffi.C.os_get_glfw_window() local mouse = {} -- LÖVR uses framebuffer scale for everything, but glfw uses window scale for events. -- It is necessary to convert between the two at all boundaries. function mouse.getScale() local x, _ = ffi.new('int[1]'), ffi.new('int[1]') C.glfwGetWindowSize(window, x, _) return lovr.system.getWindowWidth() / x[0] end function mouse.getX() local x = ffi.new('double[1]') C.glfwGetCursorPos(window, x, nil) return x[0] * mouse.getScale() end function mouse.getY() local y = ffi.new('double[1]') C.glfwGetCursorPos(window, nil, y) return y[0] * mouse.getScale() end function mouse.getPosition() local x, y = ffi.new('double[1]'), ffi.new('double[1]') local scale = mouse.getScale() C.glfwGetCursorPos(window, x, y) return x[0] * scale, y[0] * scale end function mouse.setX(x) local y = mouse.getY() local scale = mouse.getScale() C.glfwSetCursorPos(window, x / scale, y / scale) end function mouse.setY(y) local x = mouse.getX() local scale = mouse.getScale() C.glfwSetCursorPos(window, x / scale, y / scale) end function mouse.setPosition(x, y) local scale = mouse.getScale() C.glfwSetCursorPos(window, x / scale, y / scale) end function mouse.isDown(button, ...) if not button then return false end return C.glfwGetMouseButton(window, button - 1) > 0 or mouse.isDown(...) end function mouse.getRelativeMode() return C.glfwGetInputMode(window, C.GLFW_CURSOR) == C.GLFW_CURSOR_DISABLED end function mouse.setRelativeMode(enable) C.glfwSetInputMode(window, C.GLFW_CURSOR, enable and C.GLFW_CURSOR_DISABLED or C.GLFW_CURSOR_NORMAL) end function mouse.newCursor(source, hotx, hoty) if type(source) == 'string' or tostring(source) == 'Blob' then source = lovr.data.newImage(source, false) else assert(tostring(source) == 'Image', 'Bad argument #1 to newCursor (Image expected)') end local image = ffi.new('GLFWimage', source:getWidth(), source:getHeight(), source:getPointer()) return C.glfwCreateCursor(image, hotx or 0, hoty or 0) end function mouse.getSystemCursor(kind) local kinds = { arrow = C.GLFW_ARROW_CURSOR, ibeam = C.GLFW_IBEAM_CURSOR, crosshair = C.GLFW_CROSSHAIR_CURSOR, hand = C.GLFW_HAND_CURSOR, sizewe = C.GLFW_HRESIZE_CURSOR, sizens = C.GLFW_VRESIZE_CURSOR } assert(kinds[kind], string.format('Unknown cursor %q', tostring(kind))) return C.glfwCreateStandardCursor(kinds[kind]) end function mouse.setCursor(cursor) C.glfwSetCursor(window, cursor) end C.glfwSetMouseButtonCallback(window, function(target, button, action, mods) if target == window then local x, y = mouse.getPosition() lovr.event.push(action > 0 and 'mousepressed' or 'mousereleased', x, y, button + 1, false) end end) local px, py = mouse.getPosition() C.glfwSetCursorPosCallback(window, function(target, x, y) if target == window then local scale = mouse.getScale() x = x * scale y = y * scale lovr.event.push('mousemoved', x, y, x - px, y - py, false) px, py = x, y end end) C.glfwSetScrollCallback(window, function(target, x, y) if target == window then local scale = mouse.getScale() lovr.event.push('wheelmoved', x * scale, y * scale) end end) return mouse