glfw: add system for nice Zig callbacks, add Window.setPosCallback
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
c7161afec3
commit
0ed547af0e
2 changed files with 98 additions and 41 deletions
137
src/Window.zig
137
src/Window.zig
|
@ -14,6 +14,31 @@ const Window = @This();
|
||||||
|
|
||||||
handle: *c.GLFWwindow,
|
handle: *c.GLFWwindow,
|
||||||
|
|
||||||
|
/// Returns a Zig GLFW window from an underlying C GLFW window handle.
|
||||||
|
///
|
||||||
|
/// Note that the Zig GLFW library stores a custom user pointer in order to make callbacks nicer,
|
||||||
|
/// see glfw.Window.InternalUserPointer.
|
||||||
|
pub inline fn from(handle: *c.GLFWwindow) Error!Window {
|
||||||
|
const ptr = c.glfwGetWindowUserPointer(handle);
|
||||||
|
if (ptr == null) {
|
||||||
|
const internal = try std.heap.c_allocator.create(InternalUserPointer);
|
||||||
|
c.glfwSetWindowUserPointer(handle, @ptrCast(*c_void, internal));
|
||||||
|
try getError();
|
||||||
|
}
|
||||||
|
return Window{ .handle = handle };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The actual type which is stored by the Zig GLFW library in glfwSetWindowUserPointer.
|
||||||
|
///
|
||||||
|
/// This is used to internally carry function callbacks with nicer Zig interfaces.
|
||||||
|
pub const InternalUserPointer = struct {
|
||||||
|
/// The actual user pointer that the user of the library wished to set via setUserPointer.
|
||||||
|
user_pointer: ?*c_void,
|
||||||
|
|
||||||
|
// Callbacks to be invoked by wrapper functions.
|
||||||
|
setPosCallback: ?fn (window: Window, xpos: isize, ypos: isize) void,
|
||||||
|
};
|
||||||
|
|
||||||
/// Resets all window hints to their default values.
|
/// Resets all window hints to their default values.
|
||||||
///
|
///
|
||||||
/// This function resets all window hints to their default values.
|
/// This function resets all window hints to their default values.
|
||||||
|
@ -212,7 +237,7 @@ pub inline fn create(width: usize, height: usize, title: [*c]const u8, monitor:
|
||||||
if (share) |w| w.handle else null,
|
if (share) |w| w.handle else null,
|
||||||
);
|
);
|
||||||
try getError();
|
try getError();
|
||||||
return Window{ .handle = handle.? };
|
return from(handle.?);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destroys the specified window and its context.
|
/// Destroys the specified window and its context.
|
||||||
|
@ -232,7 +257,9 @@ pub inline fn create(width: usize, height: usize, title: [*c]const u8, monitor:
|
||||||
///
|
///
|
||||||
/// see also: window_creation, glfw.Window.create
|
/// see also: window_creation, glfw.Window.create
|
||||||
pub inline fn destroy(self: Window) void {
|
pub inline fn destroy(self: Window) void {
|
||||||
|
const internal = self.getInternal();
|
||||||
c.glfwDestroyWindow(self.handle);
|
c.glfwDestroyWindow(self.handle);
|
||||||
|
std.heap.c_allocator.destroy(internal);
|
||||||
|
|
||||||
// Technically, glfwDestroyWindow could produce errors including glfw.Error.NotInitialized and
|
// Technically, glfwDestroyWindow could produce errors including glfw.Error.NotInitialized and
|
||||||
// glfw.Error.PlatformError. But how would anybody handle them? By creating a new window to
|
// glfw.Error.PlatformError. But how would anybody handle them? By creating a new window to
|
||||||
|
@ -1060,6 +1087,12 @@ pub inline fn setAttrib(self: Window, attrib: isize, value: bool) Error!void {
|
||||||
try getError();
|
try getError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub inline fn getInternal(self: Window) *InternalUserPointer {
|
||||||
|
const ptr = c.glfwGetWindowUserPointer(self.handle);
|
||||||
|
if (ptr) |p| return @ptrCast(*InternalUserPointer, @alignCast(@alignOf(*InternalUserPointer), p));
|
||||||
|
@panic("expected GLFW window user pointer to be *glfw.Window.InternalUserPointer, found null");
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the user pointer of the specified window.
|
/// Sets the user pointer of the specified window.
|
||||||
///
|
///
|
||||||
/// This function sets the user-defined pointer of the specified window. The current value is
|
/// This function sets the user-defined pointer of the specified window. The current value is
|
||||||
|
@ -1069,12 +1102,8 @@ pub inline fn setAttrib(self: Window, attrib: isize, value: bool) Error!void {
|
||||||
///
|
///
|
||||||
/// see also: window_userptr, glfw.Window.getUserPointer
|
/// see also: window_userptr, glfw.Window.getUserPointer
|
||||||
pub inline fn setUserPointer(self: Window, Type: anytype, pointer: Type) void {
|
pub inline fn setUserPointer(self: Window, Type: anytype, pointer: Type) void {
|
||||||
c.glfwSetWindowUserPointer(self.handle, @ptrCast(*c_void, pointer));
|
var internal = self.getInternal();
|
||||||
|
internal.user_pointer = @ptrCast(*c_void, pointer);
|
||||||
// The only error this could return would be glfw.Error.NotInitialized, which should
|
|
||||||
// definitely have occurred before calls to this. Returning an error here makes the API
|
|
||||||
// awkward to use, so we discard it instead.
|
|
||||||
getError() catch {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the user pointer of the specified window.
|
/// Returns the user pointer of the specified window.
|
||||||
|
@ -1086,43 +1115,49 @@ pub inline fn setUserPointer(self: Window, Type: anytype, pointer: Type) void {
|
||||||
///
|
///
|
||||||
/// see also: window_userptr, glfw.Window.setUserPointer
|
/// see also: window_userptr, glfw.Window.setUserPointer
|
||||||
pub inline fn getUserPointer(self: Window, Type: anytype) ?Type {
|
pub inline fn getUserPointer(self: Window, Type: anytype) ?Type {
|
||||||
const ptr = c.glfwGetWindowUserPointer(self.handle);
|
var internal = self.getInternal();
|
||||||
if (ptr) |p| return @ptrCast(Type, @alignCast(@alignOf(Type), p));
|
if (internal.user_pointer) |p| return @ptrCast(Type, @alignCast(@alignOf(Type), p));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(window):
|
fn setPosCallbackWrapper(handle: ?*c.GLFWwindow, xpos: c_int, ypos: c_int) callconv(.C) void {
|
||||||
|
const window = from(handle.?) catch unreachable;
|
||||||
|
const internal = window.getInternal();
|
||||||
|
internal.setPosCallback.?(window, @intCast(isize, xpos), @intCast(isize, ypos));
|
||||||
|
}
|
||||||
|
|
||||||
// /// Sets the position callback for the specified window.
|
/// Sets the position callback for the specified window.
|
||||||
// ///
|
///
|
||||||
// /// This function sets the position callback of the specified window, which is
|
/// This function sets the position callback of the specified window, which is called when the
|
||||||
// /// called when the window is moved. The callback is provided with the
|
/// window is moved. The callback is provided with the position, in screen coordinates, of the
|
||||||
// /// position, in screen coordinates, of the upper-left corner of the content
|
/// upper-left corner of the content area of the window.
|
||||||
// /// area of the window.
|
///
|
||||||
// ///
|
/// @param[in] callback The new callback, or null to remove the currently set callback.
|
||||||
// /// @param[in] window The window whose callback to set.
|
///
|
||||||
// /// @param[in] callback The new callback, or null to remove the currently set
|
/// @callback_param `window` the window that moved.
|
||||||
// /// callback.
|
/// @callback_param `xpos` the new x-coordinate, in screen coordinates, of the upper-left corner of
|
||||||
// /// @return The previously set callback, or null if no callback was set or the
|
/// the content area of the window.
|
||||||
// /// library had not been [initialized](@ref intro_init).
|
/// @callback_param `ypos` the new y-coordinate, in screen coordinates, of the upper-left corner of
|
||||||
// ///
|
/// the content area of the window.
|
||||||
// /// @callback_signature
|
///
|
||||||
// /// @code
|
/// wayland: This callback will never be called, as there is no way for an application to know its
|
||||||
// /// void function_name(GLFWwindow* window, int xpos, int ypos)
|
/// global position.
|
||||||
// /// @endcode
|
///
|
||||||
// /// For more information about the callback parameters, see the
|
/// @thread_safety This function must only be called from the main thread.
|
||||||
// /// [function pointer type](@ref GLFWwindowposfun).
|
///
|
||||||
// ///
|
/// see also: window_pos
|
||||||
// /// Possible errors include glfw.Error.NotInitialized.
|
pub inline fn setPosCallback(self: Window, callback: ?fn (window: Window, xpos: isize, ypos: isize) void) void {
|
||||||
// ///
|
var internal = self.getInternal();
|
||||||
// /// wayland: This callback will never be called, as there is no way for
|
internal.setPosCallback = callback;
|
||||||
// /// an application to know its global position.
|
_ = c.glfwSetWindowPosCallback(self.handle, if (callback != null) setPosCallbackWrapper else null);
|
||||||
// ///
|
|
||||||
// /// @thread_safety This function must only be called from the main thread.
|
// The only error this could return would be glfw.Error.NotInitialized, which should
|
||||||
// ///
|
// definitely have occurred before calls to this. Returning an error here makes the API
|
||||||
// /// see also: window_pos
|
// awkward to use, so we discard it instead.
|
||||||
// ///
|
getError() catch {};
|
||||||
// GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun callback);
|
}
|
||||||
|
|
||||||
|
// TODO(window):
|
||||||
|
|
||||||
// /// Sets the size callback for the specified window.
|
// /// Sets the size callback for the specified window.
|
||||||
// ///
|
// ///
|
||||||
|
@ -1899,3 +1934,25 @@ test "getUserPointer" {
|
||||||
const got = window.getUserPointer(*T);
|
const got = window.getUserPointer(*T);
|
||||||
std.debug.assert(&my_value == got);
|
std.debug.assert(&my_value == got);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "setPosCallback" {
|
||||||
|
const glfw = @import("main.zig");
|
||||||
|
try glfw.init();
|
||||||
|
defer glfw.terminate();
|
||||||
|
|
||||||
|
const window = glfw.Window.create(640, 480, "Hello, Zig!", null, null) catch |err| {
|
||||||
|
// return without fail, because most of our CI environments are headless / we cannot open
|
||||||
|
// windows on them.
|
||||||
|
std.debug.print("note: failed to create window: {}\n", .{err});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
defer window.destroy();
|
||||||
|
|
||||||
|
window.setPosCallback((struct {
|
||||||
|
fn callback(_window: Window, xpos: isize, ypos: isize) void {
|
||||||
|
_ = _window;
|
||||||
|
_ = xpos;
|
||||||
|
_ = ypos;
|
||||||
|
}
|
||||||
|
}).callback);
|
||||||
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ pub inline fn makeContextCurrent(window: ?Window) Error!void {
|
||||||
pub inline fn getCurrentContext() Error!?Window {
|
pub inline fn getCurrentContext() Error!?Window {
|
||||||
const handle = c.glfwGetCurrentContext();
|
const handle = c.glfwGetCurrentContext();
|
||||||
try getError();
|
try getError();
|
||||||
if (handle) |h| return Window{ .handle = h };
|
if (handle) |h| return try Window.from(h);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue