glfw: rework error handling system to prevent footguns

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2022-12-27 04:20:16 -07:00 committed by Stephen Gutekanst
parent a9e9ac0955
commit e9ba368443
12 changed files with 1136 additions and 1531 deletions

View file

@ -106,23 +106,19 @@ pub const Shape = enum(i32) {
/// @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot. /// @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot.
/// @return The handle of the created cursor. /// @return The handle of the created cursor.
/// ///
/// Possible errors include glfw.Error.PlatformError and glfw.Error.InvalidValue
/// null is returned in the event of an error.
///
/// @pointer_lifetime The specified image data is copied before this function returns. /// @pointer_lifetime The specified image data is copied before this function returns.
/// ///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: cursor_object, glfw.Cursor.destroy, glfw.Cursor.createStandard /// see also: cursor_object, glfw.Cursor.destroy, glfw.Cursor.createStandard
pub inline fn create(image: Image, xhot: i32, yhot: i32) error{ PlatformError, InvalidValue }!Cursor { pub inline fn create(image: Image, xhot: i32, yhot: i32) ?Cursor {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const img = image.toC(); const img = image.toC();
if (c.glfwCreateCursor(&img, @intCast(c_int, xhot), @intCast(c_int, yhot))) |cursor| return Cursor{ .ptr = cursor }; if (c.glfwCreateCursor(&img, @intCast(c_int, xhot), @intCast(c_int, yhot))) |cursor| return Cursor{ .ptr = cursor };
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
Error.InvalidValue => |e| e,
else => unreachable,
};
// `glfwCreateCursor` returns `null` only for errors
unreachable;
} }
/// Creates a cursor with a standard shape. /// Creates a cursor with a standard shape.
@ -151,22 +147,16 @@ pub inline fn create(image: Image, xhot: i32, yhot: i32) error{ PlatformError, I
/// 2. This uses a newer standard that not all cursor themes support. /// 2. This uses a newer standard that not all cursor themes support.
/// ///
/// If the requested shape is not available, this function emits a CursorUnavailable error /// If the requested shape is not available, this function emits a CursorUnavailable error
/// Possible errors include glfw.Error.PlatformError and glfw.Error.CursorUnavailable.
/// null is returned in the event of an error.
/// ///
/// thread_safety: This function must only be called from the main thread. /// thread_safety: This function must only be called from the main thread.
/// ///
/// see also: cursor_object, glfwCreateCursor /// see also: cursor_object, glfwCreateCursor
pub inline fn createStandard(shape: Shape) error{ PlatformError, CursorUnavailable }!Cursor { pub inline fn createStandard(shape: Shape) ?Cursor {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (c.glfwCreateStandardCursor(@intCast(c_int, @enumToInt(shape)))) |cursor| return Cursor{ .ptr = cursor }; if (c.glfwCreateStandardCursor(@intCast(c_int, @enumToInt(shape)))) |cursor| return Cursor{ .ptr = cursor };
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.InvalidEnum => unreachable,
Error.CursorUnavailable => |e| e,
Error.PlatformError => |e| e,
else => unreachable,
};
// `glfwCreateStandardCursor` returns `null` only for errors
unreachable;
} }
/// Destroys a cursor. /// Destroys a cursor.
@ -177,7 +167,7 @@ pub inline fn createStandard(shape: Shape) error{ PlatformError, CursorUnavailab
/// If the specified cursor is current for any window, that window will be reverted to the default /// If the specified cursor is current for any window, that window will be reverted to the default
/// cursor. This does not affect the cursor mode. /// cursor. This does not affect the cursor mode.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// ///
/// @reentrancy This function must not be called from a callback. /// @reentrancy This function must not be called from a callback.
/// ///
@ -187,37 +177,35 @@ pub inline fn createStandard(shape: Shape) error{ PlatformError, CursorUnavailab
pub inline fn destroy(self: Cursor) void { pub inline fn destroy(self: Cursor) void {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
c.glfwDestroyCursor(self.ptr); c.glfwDestroyCursor(self.ptr);
getError() catch |err| return switch (err) {
Error.PlatformError => std.log.err("mach/glfw: unable to destroy Cursor: {}\n", .{err}),
else => unreachable,
};
} }
test "create" { test "create" {
const allocator = testing.allocator; const allocator = testing.allocator;
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const image = try Image.init(allocator, 32, 32, 32 * 32 * 4); const image = try Image.init(allocator, 32, 32, 32 * 32 * 4);
defer image.deinit(allocator); defer image.deinit(allocator);
const cursor = glfw.Cursor.create(image, 0, 0) catch |err| { const cursor = glfw.Cursor.create(image, 0, 0);
std.debug.print("failed to create cursor, custom cursors not supported? error={}\n", .{err}); if (cursor) |cur| cur.destroy();
return;
};
cursor.destroy();
} }
test "createStandard" { test "createStandard" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const cursor = glfw.Cursor.createStandard(.ibeam) catch |err| { const cursor = glfw.Cursor.createStandard(.ibeam);
std.debug.print("failed to create cursor, custom cursors not supported? error={}\n", .{err}); if (cursor) |cur| cur.destroy();
return;
};
cursor.destroy();
} }

View file

@ -80,20 +80,14 @@ const GamepadState = extern struct {
/// ///
/// @return `true` if the joystick is present, or `false` otherwise. /// @return `true` if the joystick is present, or `false` otherwise.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.InvalidEnum and glfw.Error.PlatformError. /// Possible errors include glfw.Error.InvalidEnum and glfw.Error.PlatformError.
/// ///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: joystick /// see also: joystick
pub inline fn present(self: Joystick) error{PlatformError}!bool { pub inline fn present(self: Joystick) bool {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const is_present = c.glfwJoystickPresent(@enumToInt(self.jid)); const is_present = c.glfwJoystickPresent(@enumToInt(self.jid));
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.InvalidEnum => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
return is_present == c.GLFW_TRUE; return is_present == c.GLFW_TRUE;
} }
@ -107,7 +101,8 @@ pub inline fn present(self: Joystick) error{PlatformError}!bool {
/// ///
/// @return An array of axis values, or null if the joystick is not present. /// @return An array of axis values, or null if the joystick is not present.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.InvalidEnum and glfw.Error.PlatformError. /// Possible errors include glfw.Error.InvalidEnum and glfw.Error.PlatformError.
/// null is additionally returned in the event of an error.
/// ///
/// @pointer_lifetime The returned array is allocated and freed by GLFW. You should not free it /// @pointer_lifetime The returned array is allocated and freed by GLFW. You should not free it
/// yourself. It is valid until the specified joystick is disconnected or the library is /// yourself. It is valid until the specified joystick is disconnected or the library is
@ -117,16 +112,10 @@ pub inline fn present(self: Joystick) error{PlatformError}!bool {
/// ///
/// see also: joystick_axis /// see also: joystick_axis
/// Replaces `glfwGetJoystickPos`. /// Replaces `glfwGetJoystickPos`.
pub inline fn getAxes(self: Joystick) error{PlatformError}!?[]const f32 { pub inline fn getAxes(self: Joystick) ?[]const f32 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
var count: c_int = undefined; var count: c_int = undefined;
const axes = c.glfwGetJoystickAxes(@enumToInt(self.jid), &count); const axes = c.glfwGetJoystickAxes(@enumToInt(self.jid), &count);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.InvalidEnum => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
if (axes == null) return null; if (axes == null) return null;
return axes[0..@intCast(u32, count)]; return axes[0..@intCast(u32, count)];
} }
@ -147,7 +136,8 @@ pub inline fn getAxes(self: Joystick) error{PlatformError}!?[]const f32 {
/// ///
/// @return An array of button states, or null if the joystick is not present. /// @return An array of button states, or null if the joystick is not present.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.InvalidEnum and glfw.Error.PlatformError. /// Possible errors include glfw.Error.InvalidEnum and glfw.Error.PlatformError.
/// null is additionally returned in the event of an error.
/// ///
/// @pointer_lifetime The returned array is allocated and freed by GLFW. You should not free it /// @pointer_lifetime The returned array is allocated and freed by GLFW. You should not free it
/// yourself. It is valid until the specified joystick is disconnected or the library is terminated. /// yourself. It is valid until the specified joystick is disconnected or the library is terminated.
@ -155,16 +145,10 @@ pub inline fn getAxes(self: Joystick) error{PlatformError}!?[]const f32 {
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: joystick_button /// see also: joystick_button
pub inline fn getButtons(self: Joystick) error{PlatformError}!?[]const u8 { pub inline fn getButtons(self: Joystick) ?[]const u8 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
var count: c_int = undefined; var count: c_int = undefined;
const buttons = c.glfwGetJoystickButtons(@enumToInt(self.jid), &count); const buttons = c.glfwGetJoystickButtons(@enumToInt(self.jid), &count);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.InvalidEnum => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
if (buttons == null) return null; if (buttons == null) return null;
return buttons[0..@intCast(u32, count)]; return buttons[0..@intCast(u32, count)];
} }
@ -200,7 +184,8 @@ pub inline fn getButtons(self: Joystick) error{PlatformError}!?[]const u8 {
/// ///
/// @return An array of hat states, or null if the joystick is not present. /// @return An array of hat states, or null if the joystick is not present.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.InvalidEnum and glfw.Error.PlatformError. /// Possible errors include glfw.Error.InvalidEnum and glfw.Error.PlatformError.
/// null is additionally returned in the event of an error.
/// ///
/// @pointer_lifetime The returned array is allocated and freed by GLFW. You should not free it /// @pointer_lifetime The returned array is allocated and freed by GLFW. You should not free it
/// yourself. It is valid until the specified joystick is disconnected, this function is called /// yourself. It is valid until the specified joystick is disconnected, this function is called
@ -209,16 +194,10 @@ pub inline fn getButtons(self: Joystick) error{PlatformError}!?[]const u8 {
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: joystick_hat /// see also: joystick_hat
pub inline fn getHats(self: Joystick) error{PlatformError}!?[]const Hat { pub inline fn getHats(self: Joystick) ?[]const Hat {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
var count: c_int = undefined; var count: c_int = undefined;
const hats = c.glfwGetJoystickHats(@enumToInt(self.jid), &count); const hats = c.glfwGetJoystickHats(@enumToInt(self.jid), &count);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.InvalidEnum => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
if (hats == null) return null; if (hats == null) return null;
const slice = hats[0..@intCast(u32, count)]; const slice = hats[0..@intCast(u32, count)];
return @ptrCast(*const []const Hat, &slice).*; return @ptrCast(*const []const Hat, &slice).*;
@ -235,7 +214,8 @@ pub inline fn getHats(self: Joystick) error{PlatformError}!?[]const Hat {
/// @return The UTF-8 encoded name of the joystick, or null if the joystick is not present or an /// @return The UTF-8 encoded name of the joystick, or null if the joystick is not present or an
/// error occurred. /// error occurred.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.InvalidEnum and glfw.Error.PlatformError. /// Possible errors include glfw.Error.InvalidEnum and glfw.Error.PlatformError.
/// null is additionally returned in the event of an error.
/// ///
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it /// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
/// yourself. It is valid until the specified joystick is disconnected or the library is terminated. /// yourself. It is valid until the specified joystick is disconnected or the library is terminated.
@ -243,15 +223,9 @@ pub inline fn getHats(self: Joystick) error{PlatformError}!?[]const Hat {
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: joystick_name /// see also: joystick_name
pub inline fn getName(self: Joystick) error{PlatformError}!?[:0]const u8 { pub inline fn getName(self: Joystick) ?[:0]const u8 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const name_opt = c.glfwGetJoystickName(@enumToInt(self.jid)); const name_opt = c.glfwGetJoystickName(@enumToInt(self.jid));
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.InvalidEnum => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
return if (name_opt) |name| return if (name_opt) |name|
std.mem.span(@ptrCast([*:0]const u8, name)) std.mem.span(@ptrCast([*:0]const u8, name))
else else
@ -277,7 +251,8 @@ pub inline fn getName(self: Joystick) error{PlatformError}!?[:0]const u8 {
/// ///
/// @return The UTF-8 encoded GUID of the joystick, or null if the joystick is not present. /// @return The UTF-8 encoded GUID of the joystick, or null if the joystick is not present.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.InvalidEnum and glfw.Error.PlatformError. /// Possible errors include glfw.Error.InvalidEnum and glfw.Error.PlatformError.
/// null is additionally returned in the event of an error.
/// ///
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it /// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
/// yourself. It is valid until the specified joystick is disconnected or the library is terminated. /// yourself. It is valid until the specified joystick is disconnected or the library is terminated.
@ -285,15 +260,9 @@ pub inline fn getName(self: Joystick) error{PlatformError}!?[:0]const u8 {
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: gamepad /// see also: gamepad
pub inline fn getGUID(self: Joystick) error{PlatformError}!?[:0]const u8 { pub inline fn getGUID(self: Joystick) ?[:0]const u8 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const guid_opt = c.glfwGetJoystickGUID(@enumToInt(self.jid)); const guid_opt = c.glfwGetJoystickGUID(@enumToInt(self.jid));
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.InvalidEnum => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
return if (guid_opt) |guid| return if (guid_opt) |guid|
std.mem.span(@ptrCast([*:0]const u8, guid)) std.mem.span(@ptrCast([*:0]const u8, guid))
else else
@ -313,10 +282,6 @@ pub inline fn getGUID(self: Joystick) error{PlatformError}!?[:0]const u8 {
pub inline fn setUserPointer(self: Joystick, comptime T: type, pointer: *T) void { pub inline fn setUserPointer(self: Joystick, comptime T: type, pointer: *T) void {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
c.glfwSetJoystickUserPointer(@enumToInt(self.jid), @ptrCast(*anyopaque, pointer)); c.glfwSetJoystickUserPointer(@enumToInt(self.jid), @ptrCast(*anyopaque, pointer));
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
} }
/// Returns the user pointer of the specified joystick. /// Returns the user pointer of the specified joystick.
@ -333,10 +298,6 @@ pub inline fn setUserPointer(self: Joystick, comptime T: type, pointer: *T) void
pub inline fn getUserPointer(self: Joystick, comptime PointerType: type) ?PointerType { pub inline fn getUserPointer(self: Joystick, comptime PointerType: type) ?PointerType {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const ptr = c.glfwGetJoystickUserPointer(@enumToInt(self.jid)); const ptr = c.glfwGetJoystickUserPointer(@enumToInt(self.jid));
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
if (ptr) |p| return @ptrCast(PointerType, @alignCast(@alignOf(std.meta.Child(PointerType)), p)); if (ptr) |p| return @ptrCast(PointerType, @alignCast(@alignOf(std.meta.Child(PointerType)), p));
return null; return null;
} }
@ -366,8 +327,6 @@ pub const Event = enum(c_int) {
/// @callback_param `event` One of `.connected` or `.disconnected`. Future releases may add /// @callback_param `event` One of `.connected` or `.disconnected`. Future releases may add
/// more events. /// more events.
/// ///
/// Possible errors include glfw.Error.NotInitialized.
///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: joystick_event /// see also: joystick_event
@ -388,11 +347,6 @@ pub inline fn setCallback(comptime callback: ?fn (joystick: Joystick, event: Eve
} else { } else {
if (c.glfwSetJoystickCallback(null) != null) return; if (c.glfwSetJoystickCallback(null) != null) return;
} }
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
} }
/// Adds the specified SDL_GameControllerDB gamepad mappings. /// Adds the specified SDL_GameControllerDB gamepad mappings.
@ -410,7 +364,8 @@ pub inline fn setCallback(comptime callback: ?fn (joystick: Joystick, event: Eve
/// ///
/// @param[in] string The string containing the gamepad mappings. /// @param[in] string The string containing the gamepad mappings.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.InvalidValue. /// Possible errors include glfw.Error.InvalidValue.
/// Returns a boolean indicating success.
/// ///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
@ -418,18 +373,9 @@ pub inline fn setCallback(comptime callback: ?fn (joystick: Joystick, event: Eve
/// ///
/// ///
/// @ingroup input /// @ingroup input
pub inline fn updateGamepadMappings(gamepad_mappings: [*:0]const u8) error{InvalidValue}!void { pub inline fn updateGamepadMappings(gamepad_mappings: [*:0]const u8) bool {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (c.glfwUpdateGamepadMappings(gamepad_mappings) == c.GLFW_TRUE) return; return c.glfwUpdateGamepadMappings(gamepad_mappings) == c.GLFW_TRUE;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
// TODO: Maybe return as 'ParseError' here?
// TODO: Look into upstream proposal for GLFW to publicize
// their Gamepad mappings parsing functions/interface
// for a better error message in debug.
Error.InvalidValue => |e| e,
else => unreachable,
};
} }
/// Returns whether the specified joystick has a gamepad mapping. /// Returns whether the specified joystick has a gamepad mapping.
@ -442,7 +388,8 @@ pub inline fn updateGamepadMappings(gamepad_mappings: [*:0]const u8) error{Inval
/// ///
/// @return `true` if a joystick is both present and has a gamepad mapping, or `false` otherwise. /// @return `true` if a joystick is both present and has a gamepad mapping, or `false` otherwise.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.InvalidEnum. /// Possible errors include glfw.Error.InvalidEnum.
/// Additionally returns false in the event of an error.
/// ///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
@ -450,11 +397,6 @@ pub inline fn updateGamepadMappings(gamepad_mappings: [*:0]const u8) error{Inval
pub inline fn isGamepad(self: Joystick) bool { pub inline fn isGamepad(self: Joystick) bool {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const is_gamepad = c.glfwJoystickIsGamepad(@enumToInt(self.jid)); const is_gamepad = c.glfwJoystickIsGamepad(@enumToInt(self.jid));
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.InvalidEnum => unreachable,
else => unreachable,
};
return is_gamepad == c.GLFW_TRUE; return is_gamepad == c.GLFW_TRUE;
} }
@ -470,7 +412,8 @@ pub inline fn isGamepad(self: Joystick) bool {
/// @return The UTF-8 encoded name of the gamepad, or null if the joystick is not present or does /// @return The UTF-8 encoded name of the gamepad, or null if the joystick is not present or does
/// not have a mapping. /// not have a mapping.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.InvalidEnum. /// Possible errors include glfw.Error.InvalidEnum.
/// Additionally returns null in the event of an error.
/// ///
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it /// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
/// yourself. It is valid until the specified joystick is disconnected, the gamepad mappings are /// yourself. It is valid until the specified joystick is disconnected, the gamepad mappings are
@ -482,11 +425,6 @@ pub inline fn isGamepad(self: Joystick) bool {
pub inline fn getGamepadName(self: Joystick) ?[:0]const u8 { pub inline fn getGamepadName(self: Joystick) ?[:0]const u8 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const name_opt = c.glfwGetGamepadName(@enumToInt(self.jid)); const name_opt = c.glfwGetGamepadName(@enumToInt(self.jid));
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.InvalidValue => unreachable,
else => unreachable,
};
return if (name_opt) |name| return if (name_opt) |name|
std.mem.span(@ptrCast([*:0]const u8, name)) std.mem.span(@ptrCast([*:0]const u8, name))
else else
@ -512,7 +450,8 @@ pub inline fn getGamepadName(self: Joystick) ?[:0]const u8 {
/// @return the gamepad input state if successful, or null if no joystick is connected or it has no /// @return the gamepad input state if successful, or null if no joystick is connected or it has no
/// gamepad mapping. /// gamepad mapping.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.InvalidEnum. /// Possible errors include glfw.Error.InvalidEnum.
/// Returns null in the event of an error.
/// ///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
@ -521,81 +460,101 @@ pub inline fn getGamepadState(self: Joystick) ?GamepadState {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
var state: GamepadState = undefined; var state: GamepadState = undefined;
const success = c.glfwGetGamepadState(@enumToInt(self.jid), @ptrCast(*c.GLFWgamepadstate, &state)); const success = c.glfwGetGamepadState(@enumToInt(self.jid), @ptrCast(*c.GLFWgamepadstate, &state));
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.InvalidEnum => unreachable,
else => unreachable,
};
return if (success == c.GLFW_TRUE) state else null; return if (success == c.GLFW_TRUE) state else null;
} }
test "present" { test "present" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one }; const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.present();
_ = joystick.present() catch |err| std.debug.print("failed to detect joystick, joysticks not supported? error={}\n", .{err});
} }
test "getAxes" { test "getAxes" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one }; const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.getAxes();
_ = joystick.getAxes() catch |err| std.debug.print("failed to get joystick axes, joysticks not supported? error={}\n", .{err});
} }
test "getButtons" { test "getButtons" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one }; const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.getButtons();
_ = joystick.getButtons() catch |err| std.debug.print("failed to get joystick buttons, joysticks not supported? error={}\n", .{err});
} }
test "getHats" { test "getHats" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one }; const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.getHats() catch |err| std.debug.print("failed to get joystick hats, joysticks not supported? error={}\n", .{err}); if (joystick.getHats()) |hats| {
const hats = std.mem.zeroes(Hat); for (hats) |hat| {
if (hats.down and hats.up) { if (hat.down and hat.up) {
// down-up! // down-up!
}
}
} }
} }
test "getName" { test "getName" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one }; const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.getName();
_ = joystick.getName() catch |err| std.debug.print("failed to get joystick name, joysticks not supported? error={}\n", .{err});
} }
test "getGUID" { test "getGUID" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one }; const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.getGUID();
_ = joystick.getGUID() catch |err| std.debug.print("failed to get joystick GUID, joysticks not supported? error={}\n", .{err});
} }
test "setUserPointer_syntax" { test "setUserPointer_syntax" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one }; const joystick = glfw.Joystick{ .jid = .one };
@ -607,7 +566,11 @@ test "setUserPointer_syntax" {
test "getUserPointer_syntax" { test "getUserPointer_syntax" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one }; const joystick = glfw.Joystick{ .jid = .one };
@ -619,7 +582,11 @@ test "getUserPointer_syntax" {
test "setCallback" { test "setCallback" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
glfw.Joystick.setCallback((struct { glfw.Joystick.setCallback((struct {
@ -637,7 +604,11 @@ test "updateGamepadMappings_syntax" {
test "isGamepad" { test "isGamepad" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one }; const joystick = glfw.Joystick{ .jid = .one };
@ -646,7 +617,11 @@ test "isGamepad" {
test "getGamepadName" { test "getGamepadName" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one }; const joystick = glfw.Joystick{ .jid = .one };
@ -655,7 +630,11 @@ test "getGamepadName" {
test "getGamepadState" { test "getGamepadState" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one }; const joystick = glfw.Joystick{ .jid = .one };

View file

@ -27,21 +27,16 @@ const Pos = struct {
/// Returns the position of the monitor's viewport on the virtual screen. /// Returns the position of the monitor's viewport on the virtual screen.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// ///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: monitor_properties /// see also: monitor_properties
pub inline fn getPos(self: Monitor) error{PlatformError}!Pos { pub inline fn getPos(self: Monitor) Pos {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
var xpos: c_int = 0; var xpos: c_int = 0;
var ypos: c_int = 0; var ypos: c_int = 0;
c.glfwGetMonitorPos(self.handle, &xpos, &ypos); c.glfwGetMonitorPos(self.handle, &xpos, &ypos);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
return Pos{ .x = @intCast(u32, xpos), .y = @intCast(u32, ypos) }; return Pos{ .x = @intCast(u32, xpos), .y = @intCast(u32, ypos) };
} }
@ -60,23 +55,19 @@ const Workarea = struct {
/// Retrieves the work area of the monitor. /// Retrieves the work area of the monitor.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// A zero value is returned in the event of an error.
/// ///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: monitor_workarea /// see also: monitor_workarea
pub inline fn getWorkarea(self: Monitor) error{PlatformError}!Workarea { pub inline fn getWorkarea(self: Monitor) Workarea {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
var xpos: c_int = 0; var xpos: c_int = 0;
var ypos: c_int = 0; var ypos: c_int = 0;
var width: c_int = 0; var width: c_int = 0;
var height: c_int = 0; var height: c_int = 0;
c.glfwGetMonitorWorkarea(self.handle, &xpos, &ypos, &width, &height); c.glfwGetMonitorWorkarea(self.handle, &xpos, &ypos, &width, &height);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
return Workarea{ .x = @intCast(u32, xpos), .y = @intCast(u32, ypos), .width = @intCast(u32, width), .height = @intCast(u32, height) }; return Workarea{ .x = @intCast(u32, xpos), .y = @intCast(u32, ypos), .width = @intCast(u32, width), .height = @intCast(u32, height) };
} }
@ -92,9 +83,6 @@ const PhysicalSize = struct {
/// [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data) /// [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data)
/// data is incorrect or because the driver does not report it accurately. /// data is incorrect or because the driver does not report it accurately.
/// ///
/// Possible errors include glfw.Error.NotInitialized.
///
///
/// win32: On Windows 8 and earlier the physical size is calculated from /// win32: On Windows 8 and earlier the physical size is calculated from
/// the current resolution and system DPI instead of querying the monitor EDID data /// the current resolution and system DPI instead of querying the monitor EDID data
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
@ -105,10 +93,6 @@ pub inline fn getPhysicalSize(self: Monitor) PhysicalSize {
var width_mm: c_int = 0; var width_mm: c_int = 0;
var height_mm: c_int = 0; var height_mm: c_int = 0;
c.glfwGetMonitorPhysicalSize(self.handle, &width_mm, &height_mm); c.glfwGetMonitorPhysicalSize(self.handle, &width_mm, &height_mm);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
return PhysicalSize{ .width_mm = @intCast(u32, width_mm), .height_mm = @intCast(u32, height_mm) }; return PhysicalSize{ .width_mm = @intCast(u32, width_mm), .height_mm = @intCast(u32, height_mm) };
} }
@ -130,21 +114,17 @@ const ContentScale = struct {
/// Returns the content scale for the monitor. /// Returns the content scale for the monitor.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// A zero value is returned in the event of an error.
/// ///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: monitor_scale, glfw.Window.getContentScale /// see also: monitor_scale, glfw.Window.getContentScale
pub inline fn getContentScale(self: Monitor) error{PlatformError}!ContentScale { pub inline fn getContentScale(self: Monitor) ContentScale {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
var x_scale: f32 = 0; var x_scale: f32 = 0;
var y_scale: f32 = 0; var y_scale: f32 = 0;
c.glfwGetMonitorContentScale(self.handle, &x_scale, &y_scale); c.glfwGetMonitorContentScale(self.handle, &x_scale, &y_scale);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
return ContentScale{ .x_scale = @floatCast(f32, x_scale), .y_scale = @floatCast(f32, y_scale) }; return ContentScale{ .x_scale = @floatCast(f32, x_scale), .y_scale = @floatCast(f32, y_scale) };
} }
@ -154,8 +134,6 @@ pub inline fn getContentScale(self: Monitor) error{PlatformError}!ContentScale {
/// name typically reflects the make and model of the monitor and is not guaranteed to be unique /// name typically reflects the make and model of the monitor and is not guaranteed to be unique
/// among the connected monitors. /// among the connected monitors.
/// ///
/// Possible errors include glfw.Error.NotInitialized.
///
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it /// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
/// yourself. It is valid until the specified monitor is disconnected or the library is terminated. /// yourself. It is valid until the specified monitor is disconnected or the library is terminated.
/// ///
@ -165,11 +143,8 @@ pub inline fn getContentScale(self: Monitor) error{PlatformError}!ContentScale {
pub inline fn getName(self: Monitor) [*:0]const u8 { pub inline fn getName(self: Monitor) [*:0]const u8 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (c.glfwGetMonitorName(self.handle)) |name| return @ptrCast([*:0]const u8, name); if (c.glfwGetMonitorName(self.handle)) |name| return @ptrCast([*:0]const u8, name);
getError() catch |err| return switch (err) { // `glfwGetMonitorName` returns `null` only for errors, but the only error is unreachable
Error.NotInitialized => unreachable, // (NotInitialized)
else => unreachable,
};
// `glfwGetMonitorName` returns `null` only for errors
unreachable; unreachable;
} }
@ -181,18 +156,12 @@ pub inline fn getName(self: Monitor) [*:0]const u8 {
/// This function may be called from the monitor callback, even for a monitor that is being /// This function may be called from the monitor callback, even for a monitor that is being
/// disconnected. /// disconnected.
/// ///
/// Possible errors include glfw.Error.NotInitialized.
///
/// @thread_safety This function may be called from any thread. Access is not synchronized. /// @thread_safety This function may be called from any thread. Access is not synchronized.
/// ///
/// see also: monitor_userptr, glfw.Monitor.getUserPointer /// see also: monitor_userptr, glfw.Monitor.getUserPointer
pub inline fn setUserPointer(self: Monitor, comptime T: type, ptr: *T) void { pub inline fn setUserPointer(self: Monitor, comptime T: type, ptr: *T) void {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
c.glfwSetMonitorUserPointer(self.handle, ptr); c.glfwSetMonitorUserPointer(self.handle, ptr);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
} }
/// Returns the user pointer of the specified monitor. /// Returns the user pointer of the specified monitor.
@ -202,18 +171,12 @@ pub inline fn setUserPointer(self: Monitor, comptime T: type, ptr: *T) void {
/// This function may be called from the monitor callback, even for a monitor that is being /// This function may be called from the monitor callback, even for a monitor that is being
/// disconnected. /// disconnected.
/// ///
/// Possible errors include glfw.Error.NotInitialized.
///
/// @thread_safety This function may be called from any thread. Access is not synchronized. /// @thread_safety This function may be called from any thread. Access is not synchronized.
/// ///
/// see also: monitor_userptr, glfw.Monitor.setUserPointer /// see also: monitor_userptr, glfw.Monitor.setUserPointer
pub inline fn getUserPointer(self: Monitor, comptime T: type) ?*T { pub inline fn getUserPointer(self: Monitor, comptime T: type) ?*T {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const ptr = c.glfwGetMonitorUserPointer(self.handle); const ptr = c.glfwGetMonitorUserPointer(self.handle);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
if (ptr == null) return null; if (ptr == null) return null;
return @ptrCast(*T, @alignCast(@alignOf(T), ptr.?)); return @ptrCast(*T, @alignCast(@alignOf(T), ptr.?));
} }
@ -225,14 +188,17 @@ pub inline fn getUserPointer(self: Monitor, comptime T: type) ?*T {
/// then by resolution area (the product of width and height), then resolution width and finally /// then by resolution area (the product of width and height), then resolution width and finally
/// by refresh rate. /// by refresh rate.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// Returns null in the event of an error.
/// ///
/// The returned slice memory is owned by the caller. /// The returned slice memory is owned by the caller.
/// ///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: monitor_modes, glfw.Monitor.getVideoMode /// see also: monitor_modes, glfw.Monitor.getVideoMode
pub inline fn getVideoModes(self: Monitor, allocator: mem.Allocator) (mem.Allocator.Error || error{PlatformError})![]VideoMode { //
/// TODO(glfw): rewrite this to not require any allocation.
pub inline fn getVideoModes(self: Monitor, allocator: mem.Allocator) mem.Allocator.Error!?[]VideoMode {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
var count: c_int = 0; var count: c_int = 0;
if (c.glfwGetVideoModes(self.handle, &count)) |modes| { if (c.glfwGetVideoModes(self.handle, &count)) |modes| {
@ -243,13 +209,7 @@ pub inline fn getVideoModes(self: Monitor, allocator: mem.Allocator) (mem.Alloca
} }
return slice; return slice;
} }
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
// `glfwGetVideoModes` returns `null` only for errors
unreachable;
} }
/// Returns the current mode of the specified monitor. /// Returns the current mode of the specified monitor.
@ -258,21 +218,16 @@ pub inline fn getVideoModes(self: Monitor, allocator: mem.Allocator) (mem.Alloca
/// full screen window for that monitor, the return value will depend on whether that window is /// full screen window for that monitor, the return value will depend on whether that window is
/// iconified. /// iconified.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// Additionally returns null in the event of an error.
/// ///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: monitor_modes, glfw.Monitor.getVideoModes /// see also: monitor_modes, glfw.Monitor.getVideoModes
pub inline fn getVideoMode(self: Monitor) error{PlatformError}!VideoMode { pub inline fn getVideoMode(self: Monitor) ?VideoMode {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (c.glfwGetVideoMode(self.handle)) |mode| return VideoMode{ .handle = mode.* }; if (c.glfwGetVideoMode(self.handle)) |mode| return VideoMode{ .handle = mode.* };
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
// `glfwGetVideoMode` returns `null` only for errors
unreachable;
} }
/// Generates a gamma ramp and sets it for the specified monitor. /// Generates a gamma ramp and sets it for the specified monitor.
@ -286,7 +241,7 @@ pub inline fn getVideoMode(self: Monitor) error{PlatformError}!VideoMode {
/// ///
/// For gamma correct rendering with OpenGL or OpenGL ES, see the glfw.srgb_capable hint. /// For gamma correct rendering with OpenGL or OpenGL ES, see the glfw.srgb_capable hint.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.InvalidValue and glfw.Error.PlatformError. /// Possible errors include glfw.Error.InvalidValue and glfw.Error.PlatformError.
/// ///
/// wayland: Gamma handling is a privileged protocol, this function will thus never be implemented /// wayland: Gamma handling is a privileged protocol, this function will thus never be implemented
/// and emits glfw.Error.PlatformError. /// and emits glfw.Error.PlatformError.
@ -294,7 +249,7 @@ pub inline fn getVideoMode(self: Monitor) error{PlatformError}!VideoMode {
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: monitor_gamma /// see also: monitor_gamma
pub inline fn setGamma(self: Monitor, gamma: f32) error{PlatformError}!void { pub inline fn setGamma(self: Monitor, gamma: f32) void {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
std.debug.assert(!std.math.isNan(gamma)); std.debug.assert(!std.math.isNan(gamma));
@ -302,19 +257,14 @@ pub inline fn setGamma(self: Monitor, gamma: f32) error{PlatformError}!void {
std.debug.assert(gamma <= std.math.f32_max); std.debug.assert(gamma <= std.math.f32_max);
c.glfwSetGamma(self.handle, gamma); c.glfwSetGamma(self.handle, gamma);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.InvalidValue => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
} }
/// Returns the current gamma ramp for the specified monitor. /// Returns the current gamma ramp for the specified monitor.
/// ///
/// This function returns the current gamma ramp of the specified monitor. /// This function returns the current gamma ramp of the specified monitor.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// Additionally returns null in the event of an error.
/// ///
/// wayland: Gamma handling is a privileged protocol, this function will thus never be implemented /// wayland: Gamma handling is a privileged protocol, this function will thus never be implemented
/// and returns glfw.Error.PlatformError. /// and returns glfw.Error.PlatformError.
@ -326,16 +276,10 @@ pub inline fn setGamma(self: Monitor, gamma: f32) error{PlatformError}!void {
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: monitor_gamma /// see also: monitor_gamma
pub inline fn getGammaRamp(self: Monitor) error{ PlatformError, FeatureUnavailable }!GammaRamp { pub inline fn getGammaRamp(self: Monitor) ?GammaRamp {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (c.glfwGetGammaRamp(self.handle)) |ramp| return GammaRamp.fromC(ramp.*); if (c.glfwGetGammaRamp(self.handle)) |ramp| return GammaRamp.fromC(ramp.*);
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.PlatformError, Error.FeatureUnavailable => |e| e,
else => unreachable,
};
// `glfwGetGammaRamp` returns `null` only for errors
unreachable;
} }
/// Sets the current gamma ramp for the specified monitor. /// Sets the current gamma ramp for the specified monitor.
@ -350,7 +294,7 @@ pub inline fn getGammaRamp(self: Monitor) error{ PlatformError, FeatureUnavailab
/// ///
/// For gamma correct rendering with OpenGL or OpenGL ES, see the glfw.srgb_capable hint. /// For gamma correct rendering with OpenGL or OpenGL ES, see the glfw.srgb_capable hint.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// ///
/// The size of the specified gamma ramp should match the size of the current ramp for that /// The size of the specified gamma ramp should match the size of the current ramp for that
/// monitor. On win32, the gamma ramp size must be 256. /// monitor. On win32, the gamma ramp size must be 256.
@ -363,14 +307,9 @@ pub inline fn getGammaRamp(self: Monitor) error{ PlatformError, FeatureUnavailab
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: monitor_gamma /// see also: monitor_gamma
pub inline fn setGammaRamp(self: Monitor, ramp: GammaRamp) error{PlatformError}!void { pub inline fn setGammaRamp(self: Monitor, ramp: GammaRamp) void {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
c.glfwSetGammaRamp(self.handle, &ramp.toC()); c.glfwSetGammaRamp(self.handle, &ramp.toC());
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
} }
/// Returns the currently connected monitors. /// Returns the currently connected monitors.
@ -395,11 +334,8 @@ pub inline fn getAll(allocator: mem.Allocator) mem.Allocator.Error![]Monitor {
} }
return slice; return slice;
} }
getError() catch |err| return switch (err) { // `glfwGetMonitors` returning null can be either an error or no monitors, but the only error is
Error.NotInitialized => unreachable, // unreachable (NotInitialized)
else => unreachable,
};
// `glfwGetMonitors` returning null can be either an error or no monitors
return &[_]Monitor{}; return &[_]Monitor{};
} }
@ -408,18 +344,12 @@ pub inline fn getAll(allocator: mem.Allocator) mem.Allocator.Error![]Monitor {
/// This function returns the primary monitor. This is usually the monitor where elements like /// This function returns the primary monitor. This is usually the monitor where elements like
/// the task bar or global menu bar are located. /// the task bar or global menu bar are located.
/// ///
/// Possible errors include glfw.Error.NotInitialized.
///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: monitor_monitors, glfw.monitors.getAll /// see also: monitor_monitors, glfw.monitors.getAll
pub inline fn getPrimary() ?Monitor { pub inline fn getPrimary() ?Monitor {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (c.glfwGetPrimaryMonitor()) |handle| return Monitor{ .handle = handle }; if (c.glfwGetPrimaryMonitor()) |handle| return Monitor{ .handle = handle };
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
return null; return null;
} }
@ -448,8 +378,6 @@ pub const Event = enum(c_int) {
/// ///
/// `event` may be one of .connected or .disconnected. More events may be added in the future. /// `event` may be one of .connected or .disconnected. More events may be added in the future.
/// ///
/// Possible errors include glfw.Error.NotInitialized.
///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: monitor_event /// see also: monitor_event
@ -470,16 +398,15 @@ pub inline fn setCallback(comptime callback: ?fn (monitor: Monitor, event: Event
} else { } else {
if (c.glfwSetMonitorCallback(null) != null) return; if (c.glfwSetMonitorCallback(null) != null) return;
} }
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
} }
test "getAll" { test "getAll" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const allocator = testing.allocator; const allocator = testing.allocator;
@ -489,7 +416,11 @@ test "getAll" {
test "getPrimary" { test "getPrimary" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
_ = getPrimary(); _ = getPrimary();
@ -497,29 +428,41 @@ test "getPrimary" {
test "getPos" { test "getPos" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const monitor = getPrimary(); const monitor = getPrimary();
if (monitor) |m| { if (monitor) |m| {
_ = try m.getPos(); _ = m.getPos();
} }
} }
test "getWorkarea" { test "getWorkarea" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const monitor = getPrimary(); const monitor = getPrimary();
if (monitor) |m| { if (monitor) |m| {
_ = try m.getWorkarea(); _ = m.getWorkarea();
} }
} }
test "getPhysicalSize" { test "getPhysicalSize" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const monitor = getPrimary(); const monitor = getPrimary();
@ -530,18 +473,26 @@ test "getPhysicalSize" {
test "getContentScale" { test "getContentScale" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const monitor = getPrimary(); const monitor = getPrimary();
if (monitor) |m| { if (monitor) |m| {
_ = try m.getContentScale(); _ = m.getContentScale();
} }
} }
test "getName" { test "getName" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const monitor = getPrimary(); const monitor = getPrimary();
@ -552,7 +503,11 @@ test "getName" {
test "userPointer" { test "userPointer" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const monitor = getPrimary(); const monitor = getPrimary();
@ -568,7 +523,11 @@ test "userPointer" {
test "setCallback" { test "setCallback" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
setCallback(struct { setCallback(struct {
@ -581,46 +540,57 @@ test "setCallback" {
test "getVideoModes" { test "getVideoModes" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const monitor = getPrimary(); const monitor = getPrimary();
if (monitor) |m| { if (monitor) |m| {
const allocator = testing.allocator; const allocator = testing.allocator;
const modes = try m.getVideoModes(allocator); const modes_maybe = try m.getVideoModes(allocator);
defer allocator.free(modes); if (modes_maybe) |modes| {
defer allocator.free(modes);
}
} }
} }
test "getVideoMode" { test "getVideoMode" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const monitor = getPrimary(); const monitor = getPrimary();
if (monitor) |m| { if (monitor) |m| {
_ = try m.getVideoMode(); _ = m.getVideoMode();
} }
} }
test "set_getGammaRamp" { test "set_getGammaRamp" {
const allocator = testing.allocator; const allocator = testing.allocator;
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const monitor = getPrimary(); const monitor = getPrimary();
if (monitor) |m| { if (monitor) |m| {
const ramp = m.getGammaRamp() catch |err| { if (m.getGammaRamp()) |ramp| {
std.debug.print("can't get window position, wayland maybe? error={}\n", .{err}); // Set it to the exact same value; if we do otherwise an our tests fail it wouldn't call
return; // terminate and our made-up gamma ramp would get stuck.
}; m.setGammaRamp(ramp);
// Set it to the exact same value; if we do otherwise an our tests fail it wouldn't call // technically not needed here / noop because GLFW owns this gamma ramp.
// terminate and our made-up gamma ramp would get stuck. defer ramp.deinit(allocator);
try m.setGammaRamp(ramp); }
// technically not needed here / noop because GLFW owns this gamma ramp.
defer ramp.deinit(allocator);
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -12,21 +12,16 @@ const internal_debug = @import("internal_debug.zig");
/// ///
/// @param[in] string A UTF-8 encoded string. /// @param[in] string A UTF-8 encoded string.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// ///
/// @pointer_lifetime The specified string is copied before this function returns. /// @pointer_lifetime The specified string is copied before this function returns.
/// ///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: clipboard, glfwGetClipboardString /// see also: clipboard, glfwGetClipboardString
pub inline fn setClipboardString(value: [*:0]const u8) error{PlatformError}!void { pub inline fn setClipboardString(value: [*:0]const u8) void {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
c.glfwSetClipboardString(null, value); c.glfwSetClipboardString(null, value);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
} }
/// Returns the contents of the clipboard as a string. /// Returns the contents of the clipboard as a string.
@ -37,7 +32,8 @@ pub inline fn setClipboardString(value: [*:0]const u8) error{PlatformError}!void
/// ///
/// @return The contents of the clipboard as a UTF-8 encoded string. /// @return The contents of the clipboard as a UTF-8 encoded string.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.FormatUnavailable and glfw.Error.PlatformError. /// Possible errors include glfw.Error.FormatUnavailable and glfw.Error.PlatformError.
/// null is returned in the event of an error.
/// ///
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it /// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
/// yourself. It is valid until the next call to glfw.getClipboardString or glfw.setClipboardString /// yourself. It is valid until the next call to glfw.getClipboardString or glfw.setClipboardString
@ -46,30 +42,32 @@ pub inline fn setClipboardString(value: [*:0]const u8) error{PlatformError}!void
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: clipboard, glfwSetClipboardString /// see also: clipboard, glfwSetClipboardString
pub inline fn getClipboardString() error{ FormatUnavailable, PlatformError }![:0]const u8 { pub inline fn getClipboardString() ?[:0]const u8 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (c.glfwGetClipboardString(null)) |c_str| return std.mem.span(@ptrCast([*:0]const u8, c_str)); if (c.glfwGetClipboardString(null)) |c_str| return std.mem.span(@ptrCast([*:0]const u8, c_str));
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.FormatUnavailable, Error.PlatformError => |e| e,
else => unreachable,
};
// `glfwGetClipboardString` returns `null` only for errors
unreachable;
} }
test "setClipboardString" { test "setClipboardString" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
try glfw.setClipboardString("hello mach"); glfw.setClipboardString("hello mach");
} }
test "getClipboardString" { test "getClipboardString" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
_ = glfw.getClipboardString() catch |err| std.debug.print("can't get clipboard, not supported by OS? error={}\n", .{err}); _ = glfw.getClipboardString();
} }

View file

@ -190,6 +190,13 @@ pub inline fn getError() Error!void {
return convertError(c.glfwGetError(null)); return convertError(c.glfwGetError(null));
} }
/// Returns and clears the last error for the calling thread. If no error is present, this function
/// panics.
pub inline fn mustGetError() Error {
try getError();
@panic("glfw: mustGetError called but no error is present");
}
/// Returns and clears the last error description for the calling thread. /// Returns and clears the last error description for the calling thread.
/// ///
/// This function returns a UTF-8 encoded human-readable description of the last error that occured /// This function returns a UTF-8 encoded human-readable description of the last error that occured

View file

@ -204,7 +204,8 @@ pub const Key = enum(c_int) {
/// @param[in] scancode The scancode of the key to query. /// @param[in] scancode The scancode of the key to query.
/// @return The UTF-8 encoded, layout-specific name of the key, or null. /// @return The UTF-8 encoded, layout-specific name of the key, or null.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// Also returns null in the event of an error.
/// ///
/// The contents of the returned string may change when a keyboard layout change event is received. /// The contents of the returned string may change when a keyboard layout change event is received.
/// ///
@ -214,14 +215,9 @@ pub const Key = enum(c_int) {
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: input_key_name /// see also: input_key_name
pub inline fn getName(self: Key, scancode: i32) error{PlatformError}!?[:0]const u8 { pub inline fn getName(self: Key, scancode: i32) ?[:0]const u8 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const name_opt = cc.glfwGetKeyName(@enumToInt(self), @intCast(c_int, scancode)); const name_opt = cc.glfwGetKeyName(@enumToInt(self), @intCast(c_int, scancode));
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
return if (name_opt) |name| return if (name_opt) |name|
std.mem.span(@ptrCast([*:0]const u8, name)) std.mem.span(@ptrCast([*:0]const u8, name))
else else
@ -237,36 +233,36 @@ pub const Key = enum(c_int) {
/// @param[in] key Any named key (see keys). /// @param[in] key Any named key (see keys).
/// @return The platform-specific scancode for the key. /// @return The platform-specific scancode for the key.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.InvalidEnum and glfw.Error.PlatformError. /// Possible errors include glfw.Error.InvalidEnum and glfw.Error.PlatformError.
/// Additionally returns -1 in the event of an error.
/// ///
/// @thread_safety This function may be called from any thread. /// @thread_safety This function may be called from any thread.
pub inline fn getScancode(self: Key) error{PlatformError}!i32 { pub inline fn getScancode(self: Key) i32 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const scancode = cc.glfwGetKeyScancode(@enumToInt(self)); return cc.glfwGetKeyScancode(@enumToInt(self));
if (scancode != -1) return scancode;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.InvalidEnum => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
// `glfwGetKeyScancode` returns `-1` only for errors
unreachable;
} }
}; };
test "getName" { test "getName" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
_ = glfw.Key.a.getName(0) catch |err| std.debug.print("failed to get key name, not supported? error={}\n", .{err}); _ = glfw.Key.a.getName(0);
} }
test "getScancode" { test "getScancode" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
_ = glfw.Key.a.getScancode() catch |err| std.debug.print("failed to get key scancode, not supported? error={}\n", .{err}); _ = glfw.Key.a.getScancode();
} }

View file

@ -12,7 +12,9 @@ pub const dont_care = c.GLFW_DONT_CARE;
/// may be useful. /// may be useful.
pub const errors = @import("errors.zig"); pub const errors = @import("errors.zig");
const getError = errors.getError; pub const getError = errors.getError;
pub const mustGetError = errors.mustGetError;
pub const getErrorString = errors.getErrorString;
pub const setErrorCallback = errors.setErrorCallback; pub const setErrorCallback = errors.setErrorCallback;
pub const Error = errors.Error; pub const Error = errors.Error;
@ -81,8 +83,11 @@ pub fn assumeInitialized() void {
/// current environment if that category is still "C". This is because the "C" locale breaks /// current environment if that category is still "C". This is because the "C" locale breaks
/// Unicode text input. /// Unicode text input.
/// ///
/// Possible errors include glfw.Error.PlatformUnavailable, glfw.Error.PlatformError.
/// Returns a bool indicating success.
///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
pub inline fn init(hints: InitHints) error{ PlatformUnavailable, PlatformError }!void { pub inline fn init(hints: InitHints) bool {
internal_debug.toggleInitialized(); internal_debug.toggleInitialized();
internal_debug.assertInitialized(); internal_debug.assertInitialized();
errdefer { errdefer {
@ -100,12 +105,7 @@ pub inline fn init(hints: InitHints) error{ PlatformUnavailable, PlatformError }
} }
} }
if (c.glfwInit() == c.GLFW_TRUE) return; return c.glfwInit() == c.GLFW_TRUE;
getError() catch |err| return switch (err) {
Error.PlatformUnavailable => |e| e,
Error.PlatformError => |e| e,
else => unreachable,
};
} }
// TODO: implement custom allocator support // TODO: implement custom allocator support
@ -161,10 +161,6 @@ pub inline fn terminate() void {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
internal_debug.toggleInitialized(); internal_debug.toggleInitialized();
c.glfwTerminate(); c.glfwTerminate();
getError() catch |err| return switch (err) {
Error.PlatformError => std.log.err("mach/glfw: Failed to terminate GLFW: {}", .{err}),
else => unreachable,
};
} }
/// Initialization hints for passing into glfw.init /// Initialization hints for passing into glfw.init
@ -280,11 +276,6 @@ fn initHint(hint: InitHint, value: anytype) void {
.Bool => c.glfwInitHint(@enumToInt(hint), @intCast(c_int, @boolToInt(value))), .Bool => c.glfwInitHint(@enumToInt(hint), @intCast(c_int, @boolToInt(value))),
else => @compileError("expected a int or bool, got " ++ @typeName(@TypeOf(value))), else => @compileError("expected a int or bool, got " ++ @typeName(@TypeOf(value))),
} }
getError() catch |err| return switch (err) {
Error.InvalidEnum => unreachable,
Error.InvalidValue => unreachable,
else => unreachable,
};
} }
/// Returns a string describing the compile-time configuration. /// Returns a string describing the compile-time configuration.
@ -320,12 +311,7 @@ pub inline fn getVersionString() [:0]const u8 {
/// thread_safety: This function may be called from any thread. /// thread_safety: This function may be called from any thread.
pub fn getPlatform() PlatformType { pub fn getPlatform() PlatformType {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const platform = @intToEnum(PlatformType, c.glfwGetPlatform()); return @intToEnum(PlatformType, c.glfwGetPlatform());
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
return platform;
} }
/// Returns whether the library includes support for the specified platform. /// Returns whether the library includes support for the specified platform.
@ -339,12 +325,7 @@ pub fn getPlatform() PlatformType {
/// thread_safety: This function may be called from any thread. /// thread_safety: This function may be called from any thread.
pub fn platformSupported(platform: PlatformType) bool { pub fn platformSupported(platform: PlatformType) bool {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const is_supported = c.glfwPlatformSupported(@enumToInt(platform)); return c.glfwPlatformSupported(@enumToInt(platform)) == c.GLFW_TRUE;
getError() catch |err| return switch (err) {
Error.InvalidEnum => unreachable,
else => unreachable,
};
return is_supported == c.GLFW_TRUE;
} }
/// Processes all pending events. /// Processes all pending events.
@ -365,21 +346,16 @@ pub fn platformSupported(platform: PlatformType) bool {
/// ///
/// Event processing is not required for joystick input to work. /// Event processing is not required for joystick input to work.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// ///
/// @reentrancy This function must not be called from a callback. /// @reentrancy This function must not be called from a callback.
/// ///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: events, glfw.waitEvents, glfw.waitEventsTimeout /// see also: events, glfw.waitEvents, glfw.waitEventsTimeout
pub inline fn pollEvents() error{PlatformError}!void { pub inline fn pollEvents() void {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
c.glfwPollEvents(); c.glfwPollEvents();
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
} }
/// Waits until events are queued and processes them. /// Waits until events are queued and processes them.
@ -405,21 +381,16 @@ pub inline fn pollEvents() error{PlatformError}!void {
/// ///
/// Event processing is not required for joystick input to work. /// Event processing is not required for joystick input to work.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// ///
/// @reentrancy This function must not be called from a callback. /// @reentrancy This function must not be called from a callback.
/// ///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: events, glfw.pollEvents, glfw.waitEventsTimeout /// see also: events, glfw.pollEvents, glfw.waitEventsTimeout
pub inline fn waitEvents() error{PlatformError}!void { pub inline fn waitEvents() void {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
c.glfwWaitEvents(); c.glfwWaitEvents();
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
} }
/// Waits with timeout until events are queued and processes them. /// Waits with timeout until events are queued and processes them.
@ -449,25 +420,19 @@ pub inline fn waitEvents() error{PlatformError}!void {
/// ///
/// @param[in] timeout The maximum amount of time, in seconds, to wait. /// @param[in] timeout The maximum amount of time, in seconds, to wait.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.InvalidValue and glfw.Error.PlatformError. /// Possible errors include glfw.Error.InvalidValue and glfw.Error.PlatformError.
/// ///
/// @reentrancy This function must not be called from a callback. /// @reentrancy This function must not be called from a callback.
/// ///
/// @thread_safety This function must only be called from the main thread. /// @thread_safety This function must only be called from the main thread.
/// ///
/// see also: events, glfw.pollEvents, glfw.waitEvents /// see also: events, glfw.pollEvents, glfw.waitEvents
pub inline fn waitEventsTimeout(timeout: f64) error{PlatformError}!void { pub inline fn waitEventsTimeout(timeout: f64) void {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
std.debug.assert(!std.math.isNan(timeout)); std.debug.assert(!std.math.isNan(timeout));
std.debug.assert(timeout >= 0); std.debug.assert(timeout >= 0);
std.debug.assert(timeout <= std.math.f64_max); std.debug.assert(timeout <= std.math.f64_max);
c.glfwWaitEventsTimeout(timeout); c.glfwWaitEventsTimeout(timeout);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.InvalidValue => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
} }
/// Posts an empty event to the event queue. /// Posts an empty event to the event queue.
@ -475,19 +440,14 @@ pub inline fn waitEventsTimeout(timeout: f64) error{PlatformError}!void {
/// This function posts an empty event from the current thread to the event queue, causing /// This function posts an empty event from the current thread to the event queue, causing
/// glfw.waitEvents or glfw.waitEventsTimeout to return. /// glfw.waitEvents or glfw.waitEventsTimeout to return.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// ///
/// @thread_safety This function may be called from any thread. /// @thread_safety This function may be called from any thread.
/// ///
/// see also: events, glfw.waitEvents, glfw.waitEventsTimeout /// see also: events, glfw.waitEvents, glfw.waitEventsTimeout
pub inline fn postEmptyEvent() error{PlatformError}!void { pub inline fn postEmptyEvent() void {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
c.glfwPostEmptyEvent(); c.glfwPostEmptyEvent();
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
} }
/// Returns whether raw mouse motion is supported. /// Returns whether raw mouse motion is supported.
@ -509,23 +469,20 @@ pub inline fn postEmptyEvent() error{PlatformError}!void {
/// see also: raw_mouse_motion, glfw.setInputMode /// see also: raw_mouse_motion, glfw.setInputMode
pub inline fn rawMouseMotionSupported() bool { pub inline fn rawMouseMotionSupported() bool {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const supported = c.glfwRawMouseMotionSupported(); return c.glfwRawMouseMotionSupported() == c.GLFW_TRUE;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
return supported == c.GLFW_TRUE;
} }
pub fn basicTest() !void { pub fn basicTest() !void {
try init(.{}); defer getError() catch {}; // clear any error we generate
if (!init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{getErrorString()});
std.process.exit(1);
}
defer terminate(); defer terminate();
const window = Window.create(640, 480, "GLFW example", null, null, .{}) catch |err| { const window = Window.create(640, 480, "GLFW example", null, null, .{}) orelse {
// return without fail, because most of our CI environments are headless / we cannot open std.log.err("failed to create window: {?s}", .{getErrorString()});
// windows on them. std.process.exit(0); // note: we don't exit(1) here because our CI can't open windows
std.debug.print("note: failed to create window: {}\n", .{err});
return;
}; };
defer window.destroy(); defer window.destroy();
@ -545,34 +502,54 @@ test "getVersionString" {
} }
test "pollEvents" { test "pollEvents" {
try init(.{ .cocoa_chdir_resources = true }); init(.{ .cocoa_chdir_resources = true });
if (getErrorString()) |err| {
std.log.err("failed to initialize GLFW: {?s}", .{err});
std.process.exit(1);
}
defer terminate(); defer terminate();
} }
test "pollEvents" { test "pollEvents" {
try init(.{}); defer getError() catch {}; // clear any error we generate
if (!init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{getErrorString()});
std.process.exit(1);
}
defer terminate(); defer terminate();
try pollEvents(); pollEvents();
} }
test "waitEventsTimeout" { test "waitEventsTimeout" {
try init(.{}); defer getError() catch {}; // clear any error we generate
if (!init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{getErrorString()});
std.process.exit(1);
}
defer terminate(); defer terminate();
try waitEventsTimeout(0.25); waitEventsTimeout(0.25);
} }
test "postEmptyEvent_and_waitEvents" { test "postEmptyEvent_and_waitEvents" {
try init(.{}); defer getError() catch {}; // clear any error we generate
if (!init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{getErrorString()});
std.process.exit(1);
}
defer terminate(); defer terminate();
try postEmptyEvent(); postEmptyEvent();
try waitEvents(); waitEvents();
} }
test "rawMouseMotionSupported" { test "rawMouseMotionSupported" {
try init(.{}); defer getError() catch {}; // clear any error we generate
if (!init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{getErrorString()});
std.process.exit(1);
}
defer terminate(); defer terminate();
_ = rawMouseMotionSupported(); _ = rawMouseMotionSupported();

View file

@ -61,17 +61,12 @@ pub fn Native(comptime options: BackendOptions) type {
/// return: The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`) of the /// return: The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`) of the
/// specified monitor. /// specified monitor.
/// ///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getWin32Adapter(monitor: Monitor) [*:0]const u8 { pub fn getWin32Adapter(monitor: Monitor) [*:0]const u8 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (native.glfwGetWin32Adapter(@ptrCast(*native.GLFWmonitor, monitor.handle))) |adapter| return adapter; if (native.glfwGetWin32Adapter(@ptrCast(*native.GLFWmonitor, monitor.handle))) |adapter| return adapter;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetWin32Adapter` returns `null` only for errors // `glfwGetWin32Adapter` returns `null` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
@ -80,17 +75,12 @@ pub fn Native(comptime options: BackendOptions) type {
/// return: The UTF-8 encoded display device name (for example `\\.\DISPLAY1\Monitor0`) /// return: The UTF-8 encoded display device name (for example `\\.\DISPLAY1\Monitor0`)
/// of the specified monitor. /// of the specified monitor.
/// ///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getWin32Monitor(monitor: Monitor) [*:0]const u8 { pub fn getWin32Monitor(monitor: Monitor) [*:0]const u8 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (native.glfwGetWin32Monitor(@ptrCast(*native.GLFWmonitor, monitor.handle))) |mon| return mon; if (native.glfwGetWin32Monitor(@ptrCast(*native.GLFWmonitor, monitor.handle))) |mon| return mon;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetWin32Monitor` returns `null` only for errors // `glfwGetWin32Monitor` returns `null` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
@ -104,18 +94,13 @@ pub fn Native(comptime options: BackendOptions) type {
/// ``` /// ```
/// This DC is private and does not need to be released. /// This DC is private and does not need to be released.
/// ///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getWin32Window(window: Window) std.os.windows.HWND { pub fn getWin32Window(window: Window) std.os.windows.HWND {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (native.glfwGetWin32Window(@ptrCast(*native.GLFWwindow, window.handle))) |win| if (native.glfwGetWin32Window(@ptrCast(*native.GLFWwindow, window.handle))) |win|
return @ptrCast(std.os.windows.HWND, win); return @ptrCast(std.os.windows.HWND, win);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetWin32Window` returns `null` only for errors // `glfwGetWin32Window` returns `null` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
@ -129,261 +114,181 @@ pub fn Native(comptime options: BackendOptions) type {
/// ``` /// ```
/// This DC is private and does not need to be released. /// This DC is private and does not need to be released.
/// ///
/// Possible errors include glfw.Error.NotInitalized. /// Possible errors include glfw.Error.NoWindowContext
/// null is returned in the event of an error.
/// ///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getWGLContext(window: Window) error{NoWindowContext}!std.os.windows.HGLRC { pub fn getWGLContext(window: Window) ?std.os.windows.HGLRC {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (native.glfwGetWGLContext(@ptrCast(*native.GLFWwindow, window.handle))) |context| return context; if (native.glfwGetWGLContext(@ptrCast(*native.GLFWwindow, window.handle))) |context| return context;
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.NoWindowContext => |e| e,
else => unreachable,
};
// `glfwGetWGLContext` returns `null` only for errors
unreachable;
} }
/// Returns the `CGDirectDisplayID` of the specified monitor. /// Returns the `CGDirectDisplayID` of the specified monitor.
/// ///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getCocoaMonitor(monitor: Monitor) u32 { pub fn getCocoaMonitor(monitor: Monitor) u32 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const mon = native.glfwGetCocoaMonitor(@ptrCast(*native.GLFWmonitor, monitor.handle)); const mon = native.glfwGetCocoaMonitor(@ptrCast(*native.GLFWmonitor, monitor.handle));
if (mon != native.kCGNullDirectDisplay) return mon; if (mon != native.kCGNullDirectDisplay) return mon;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetCocoaMonitor` returns `kCGNullDirectDisplay` only for errors // `glfwGetCocoaMonitor` returns `kCGNullDirectDisplay` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
/// Returns the `NSWindow` of the specified window. /// Returns the `NSWindow` of the specified window.
/// ///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getCocoaWindow(window: Window) ?*anyopaque { pub fn getCocoaWindow(window: Window) ?*anyopaque {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const win = native.glfwGetCocoaWindow(@ptrCast(*native.GLFWwindow, window.handle)); return native.glfwGetCocoaWindow(@ptrCast(*native.GLFWwindow, window.handle));
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
return win;
} }
/// Returns the `NSWindow` of the specified window. /// Returns the `NSWindow` of the specified window.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.NoWindowContext. /// Possible errors include glfw.Error.NoWindowContext.
/// ///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getNSGLContext(window: Window) error{NoWindowContext}!u32 { pub fn getNSGLContext(window: Window) u32 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const context = native.glfwGetNSGLContext(@ptrCast(*native.GLFWwindow, window.handle)); return native.glfwGetNSGLContext(@ptrCast(*native.GLFWwindow, window.handle));
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.NoWindowContext => |e| e,
else => unreachable,
};
return context;
} }
/// Returns the `Display` used by GLFW. /// Returns the `Display` used by GLFW.
/// ///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getX11Display() *anyopaque { pub fn getX11Display() *anyopaque {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (native.glfwGetX11Display()) |display| return @ptrCast(*anyopaque, display); if (native.glfwGetX11Display()) |display| return @ptrCast(*anyopaque, display);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetX11Display` returns `null` only for errors // `glfwGetX11Display` returns `null` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
/// Returns the `RRCrtc` of the specified monitor. /// Returns the `RRCrtc` of the specified monitor.
/// ///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getX11Adapter(monitor: Monitor) u32 { pub fn getX11Adapter(monitor: Monitor) u32 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const adapter = native.glfwGetX11Adapter(@ptrCast(*native.GLFWMonitor, monitor.handle)); const adapter = native.glfwGetX11Adapter(@ptrCast(*native.GLFWMonitor, monitor.handle));
if (adapter != 0) return adapter; if (adapter != 0) return adapter;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetX11Adapter` returns `0` only for errors // `glfwGetX11Adapter` returns `0` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
/// Returns the `RROutput` of the specified monitor. /// Returns the `RROutput` of the specified monitor.
/// ///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getX11Monitor(monitor: Monitor) u32 { pub fn getX11Monitor(monitor: Monitor) u32 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const mon = native.glfwGetX11Monitor(@ptrCast(*native.GLFWmonitor, monitor.handle)); const mon = native.glfwGetX11Monitor(@ptrCast(*native.GLFWmonitor, monitor.handle));
if (mon != 0) return mon; if (mon != 0) return mon;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetX11Monitor` returns `0` only for errors // `glfwGetX11Monitor` returns `0` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
/// Returns the `Window` of the specified window. /// Returns the `Window` of the specified window.
/// ///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getX11Window(window: Window) u32 { pub fn getX11Window(window: Window) u32 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const win = native.glfwGetX11Window(@ptrCast(*native.GLFWwindow, window.handle)); const win = native.glfwGetX11Window(@ptrCast(*native.GLFWwindow, window.handle));
if (win != 0) return @intCast(u32, win); if (win != 0) return @intCast(u32, win);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetX11Window` returns `0` only for errors // `glfwGetX11Window` returns `0` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
/// Sets the current primary selection to the specified string. /// Sets the current primary selection to the specified string.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// ///
/// The specified string is copied before this function returns. /// The specified string is copied before this function returns.
/// ///
/// thread_safety: This function must only be called from the main thread. /// thread_safety: This function must only be called from the main thread.
pub fn setX11SelectionString(string: [*:0]const u8) error{PlatformError}!void { pub fn setX11SelectionString(string: [*:0]const u8) void {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
native.glfwSetX11SelectionString(string); native.glfwSetX11SelectionString(string);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
} }
/// Returns the contents of the current primary selection as a string. /// Returns the contents of the current primary selection as a string.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError. /// Possible errors include glfw.Error.PlatformError.
/// Returns null in the event of an error.
/// ///
/// The returned string is allocated and freed by GLFW. You should not free it /// The returned string is allocated and freed by GLFW. You should not free it
/// yourself. It is valid until the next call to getX11SelectionString or /// yourself. It is valid until the next call to getX11SelectionString or
/// setX11SelectionString, or until the library is terminated. /// setX11SelectionString, or until the library is terminated.
/// ///
/// thread_safety: This function must only be called from the main thread. /// thread_safety: This function must only be called from the main thread.
pub fn getX11SelectionString() error{FormatUnavailable}![*:0]const u8 { pub fn getX11SelectionString() ?[*:0]const u8 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (native.glfwGetX11SelectionString()) |str| return str; if (native.glfwGetX11SelectionString()) |str| return str;
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.FormatUnavailable => |e| e,
else => unreachable,
};
// `glfwGetX11SelectionString` returns `null` only for errors
unreachable;
} }
/// Returns the `GLXContext` of the specified window. /// Returns the `GLXContext` of the specified window.
/// ///
/// Possible errors include glfw.Error.NoWindowContext and glfw.Error.NotInitialized. /// Possible errors include glfw.Error.NoWindowContext.
/// Returns null in the event of an error.
/// ///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getGLXContext(window: Window) error{NoWindowContext}!*anyopaque { pub fn getGLXContext(window: Window) ?*anyopaque {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (native.glfwGetGLXContext(@ptrCast(*native.GLFWwindow, window.handle))) |context| return @ptrCast(*anyopaque, context); if (native.glfwGetGLXContext(@ptrCast(*native.GLFWwindow, window.handle))) |context| return @ptrCast(*anyopaque, context);
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.NoWindowContext => |e| e,
else => unreachable,
};
// `glfwGetGLXContext` returns `null` only for errors
unreachable;
} }
/// Returns the `GLXWindow` of the specified window. /// Returns the `GLXWindow` of the specified window.
/// ///
/// Possible errors include glfw.Error.NoWindowContext and glfw.Error.NotInitialized. /// Possible errors include glfw.Error.NoWindowContext.
/// Returns null in the event of an error.
/// ///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getGLXWindow(window: Window) error{NoWindowContext}!*anyopaque { pub fn getGLXWindow(window: Window) ?*anyopaque {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const win = native.glfwGetGLXWindow(@ptrCast(*native.GLFWwindow, window.handle)); const win = native.glfwGetGLXWindow(@ptrCast(*native.GLFWwindow, window.handle));
if (win != 0) return @ptrCast(*anyopaque, win); if (win != 0) return @ptrCast(*anyopaque, win);
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.NoWindowContext => |e| e,
else => unreachable,
};
// `glfwGetGLXWindow` returns `0` only for errors
unreachable;
} }
/// Returns the `*wl_display` used by GLFW. /// Returns the `*wl_display` used by GLFW.
/// ///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getWaylandDisplay() *anyopaque { pub fn getWaylandDisplay() *anyopaque {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (native.glfwGetWaylandDisplay()) |display| return @ptrCast(*anyopaque, display); if (native.glfwGetWaylandDisplay()) |display| return @ptrCast(*anyopaque, display);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetWaylandDisplay` returns `null` only for errors // `glfwGetWaylandDisplay` returns `null` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
/// Returns the `*wl_output` of the specified monitor. /// Returns the `*wl_output` of the specified monitor.
/// ///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getWaylandMonitor(monitor: Monitor) *anyopaque { pub fn getWaylandMonitor(monitor: Monitor) *anyopaque {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (native.glfwGetWaylandMonitor(@ptrCast(*native.GLFWmonitor, monitor.handle))) |mon| return @ptrCast(*anyopaque, mon); if (native.glfwGetWaylandMonitor(@ptrCast(*native.GLFWmonitor, monitor.handle))) |mon| return @ptrCast(*anyopaque, mon);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetWaylandMonitor` returns `null` only for errors // `glfwGetWaylandMonitor` returns `null` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
/// Returns the `*wl_surface` of the specified window. /// Returns the `*wl_surface` of the specified window.
/// ///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getWaylandWindow(window: Window) *anyopaque { pub fn getWaylandWindow(window: Window) *anyopaque {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (native.glfwGetWaylandWindow(@ptrCast(*native.GLFWwindow, window.handle))) |win| return @ptrCast(*anyopaque, win); if (native.glfwGetWaylandWindow(@ptrCast(*native.GLFWwindow, window.handle))) |win| return @ptrCast(*anyopaque, win);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetWaylandWindow` returns `null` only for errors // `glfwGetWaylandWindow` returns `null` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
/// Returns the `EGLDisplay` used by GLFW. /// Returns the `EGLDisplay` used by GLFW.
/// ///
/// Possible errors include glfw.Error.NotInitalized.
///
/// remark: Because EGL is initialized on demand, this function will return `EGL_NO_DISPLAY` /// remark: Because EGL is initialized on demand, this function will return `EGL_NO_DISPLAY`
/// until the first context has been created via EGL. /// until the first context has been created via EGL.
/// ///
@ -392,30 +297,22 @@ pub fn Native(comptime options: BackendOptions) type {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const display = native.glfwGetEGLDisplay(); const display = native.glfwGetEGLDisplay();
if (display != native.EGL_NO_DISPLAY) return @ptrCast(*anyopaque, display); if (display != native.EGL_NO_DISPLAY) return @ptrCast(*anyopaque, display);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetEGLDisplay` returns `EGL_NO_DISPLAY` only for errors // `glfwGetEGLDisplay` returns `EGL_NO_DISPLAY` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
/// Returns the `EGLContext` of the specified window. /// Returns the `EGLContext` of the specified window.
/// ///
/// Possible errors include glfw.Error.NotInitalized and glfw.Error.NoWindowContext. /// Possible errors include glfw.Error.NoWindowContext.
/// Returns null in the event of an error.
/// ///
/// thread_safety This function may be called from any thread. Access is not synchronized. /// thread_safety This function may be called from any thread. Access is not synchronized.
pub fn getEGLContext(window: Window) error{NoWindowContext}!*anyopaque { pub fn getEGLContext(window: Window) ?*anyopaque {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const context = native.glfwGetEGLContext(@ptrCast(*native.GLFWwindow, window.handle)); const context = native.glfwGetEGLContext(@ptrCast(*native.GLFWwindow, window.handle));
if (context != native.EGL_NO_CONTEXT) return @ptrCast(*anyopaque, context); if (context != native.EGL_NO_CONTEXT) return @ptrCast(*anyopaque, context);
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.NoWindowContext => |e| e,
else => unreachable,
};
// `glfwGetEGLContext` returns `EGL_NO_CONTEXT` only for errors
unreachable;
} }
/// Returns the `EGLSurface` of the specified window. /// Returns the `EGLSurface` of the specified window.
@ -423,17 +320,11 @@ pub fn Native(comptime options: BackendOptions) type {
/// Possible errors include glfw.Error.NotInitalized and glfw.Error.NoWindowContext. /// Possible errors include glfw.Error.NotInitalized and glfw.Error.NoWindowContext.
/// ///
/// thread_safety This function may be called from any thread. Access is not synchronized. /// thread_safety This function may be called from any thread. Access is not synchronized.
pub fn getEGLSurface(window: Window) error{NoWindowContext}!*anyopaque { pub fn getEGLSurface(window: Window) ?*anyopaque {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const surface = native.glfwGetEGLSurface(@ptrCast(*native.GLFWwindow, window.handle)); const surface = native.glfwGetEGLSurface(@ptrCast(*native.GLFWwindow, window.handle));
if (surface != native.EGL_NO_SURFACE) return @ptrCast(*anyopaque, surface); if (surface != native.EGL_NO_SURFACE) return @ptrCast(*anyopaque, surface);
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.NoWindowContext => |e| e,
else => unreachable,
};
// `glfwGetEGLSurface` returns `EGL_NO_SURFACE` only for errors
unreachable;
} }
pub const OSMesaColorBuffer = struct { pub const OSMesaColorBuffer = struct {
@ -445,11 +336,11 @@ pub fn Native(comptime options: BackendOptions) type {
/// Retrieves the color buffer associated with the specified window. /// Retrieves the color buffer associated with the specified window.
/// ///
/// Possible errors include glfw.Error.NotInitalized, glfw.Error.NoWindowContext /// Possible errors include glfw.Error.NoWindowContext and glfw.Error.PlatformError.
/// and glfw.Error.PlatformError. /// Returns null in the event of an error.
/// ///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getOSMesaColorBuffer(window: Window) error{ PlatformError, NoWindowContext }!OSMesaColorBuffer { pub fn getOSMesaColorBuffer(window: Window) ?OSMesaColorBuffer {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
var buf: OSMesaColorBuffer = undefined; var buf: OSMesaColorBuffer = undefined;
if (native.glfwGetOSMesaColorBuffer( if (native.glfwGetOSMesaColorBuffer(
@ -459,13 +350,7 @@ pub fn Native(comptime options: BackendOptions) type {
&buf.format, &buf.format,
&buf.buffer, &buf.buffer,
) == native.GLFW_TRUE) return buf; ) == native.GLFW_TRUE) return buf;
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.PlatformError, Error.NoWindowContext => |e| e,
else => unreachable,
};
// `glfwGetOSMesaColorBuffer` returns `GLFW_FALSE` only for errors
unreachable;
} }
pub const OSMesaDepthBuffer = struct { pub const OSMesaDepthBuffer = struct {
@ -477,11 +362,11 @@ pub fn Native(comptime options: BackendOptions) type {
/// Retrieves the depth buffer associated with the specified window. /// Retrieves the depth buffer associated with the specified window.
/// ///
/// Possible errors include glfw.Error.NotInitalized, glfw.Error.NoWindowContext /// Possible errors include glfw.Error.NoWindowContext and glfw.Error.PlatformError.
/// and glfw.Error.PlatformError. /// Returns null in the event of an error.
/// ///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getOSMesaDepthBuffer(window: Window) error{ PlatformError, NoWindowContext }!OSMesaDepthBuffer { pub fn getOSMesaDepthBuffer(window: Window) ?OSMesaDepthBuffer {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
var buf: OSMesaDepthBuffer = undefined; var buf: OSMesaDepthBuffer = undefined;
if (native.glfwGetOSMesaDepthBuffer( if (native.glfwGetOSMesaDepthBuffer(
@ -491,30 +376,18 @@ pub fn Native(comptime options: BackendOptions) type {
&buf.bytes_per_value, &buf.bytes_per_value,
&buf.buffer, &buf.buffer,
) == native.GLFW_TRUE) return buf; ) == native.GLFW_TRUE) return buf;
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.PlatformError, Error.NoWindowContext => |e| e,
else => unreachable,
};
// `glfwGetOSMesaDepthBuffer` returns `GLFW_FALSE` only for errors
unreachable;
} }
/// Returns the 'OSMesaContext' of the specified window. /// Returns the 'OSMesaContext' of the specified window.
/// ///
/// Possible errors include glfw.Error.NotInitalized and glfw.Error.NoWindowContext. /// Possible errors include glfw.Error.NoWindowContext.
/// ///
/// thread_safety: This function may be called from any thread. Access is not synchronized. /// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getOSMesaContext(window: Window) error{NoWindowContext}!*anyopaque { pub fn getOSMesaContext(window: Window) ?*anyopaque {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (native.glfwGetOSMesaContext(@ptrCast(*native.GLFWwindow, window.handle))) |context| return @ptrCast(*anyopaque, context); if (native.glfwGetOSMesaContext(@ptrCast(*native.GLFWwindow, window.handle))) |context| return @ptrCast(*anyopaque, context);
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.NoWindowContext => |e| e,
else => unreachable,
};
// `glfwGetOSMesaContext` returns `null` only for errors
unreachable;
} }
}; };
} }

View file

@ -26,19 +26,14 @@ const internal_debug = @import("internal_debug.zig");
/// @param[in] window The window whose context to make current, or null to /// @param[in] window The window whose context to make current, or null to
/// detach the current context. /// detach the current context.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.NoWindowContext and glfw.Error.PlatformError. /// Possible errors include glfw.Error.NoWindowContext and glfw.Error.PlatformError.
/// ///
/// @thread_safety This function may be called from any thread. /// @thread_safety This function may be called from any thread.
/// ///
/// see also: context_current, glfwGetCurrentContext /// see also: context_current, glfwGetCurrentContext
pub inline fn makeContextCurrent(window: ?Window) error{ NoWindowContext, PlatformError }!void { pub inline fn makeContextCurrent(window: ?Window) void {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (window) |w| c.glfwMakeContextCurrent(w.handle) else c.glfwMakeContextCurrent(null); if (window) |w| c.glfwMakeContextCurrent(w.handle) else c.glfwMakeContextCurrent(null);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.NoWindowContext, Error.PlatformError => |e| e,
else => unreachable,
};
} }
/// Returns the window whose context is current on the calling thread. /// Returns the window whose context is current on the calling thread.
@ -48,18 +43,12 @@ pub inline fn makeContextCurrent(window: ?Window) error{ NoWindowContext, Platfo
/// ///
/// Returns he window whose context is current, or null if no window's context is current. /// Returns he window whose context is current, or null if no window's context is current.
/// ///
/// Possible errors include glfw.Error.NotInitialized.
///
/// @thread_safety This function may be called from any thread. /// @thread_safety This function may be called from any thread.
/// ///
/// see also: context_current, glfwMakeContextCurrent /// see also: context_current, glfwMakeContextCurrent
pub inline fn getCurrentContext() ?Window { pub inline fn getCurrentContext() ?Window {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (c.glfwGetCurrentContext()) |handle| return Window.from(handle); if (c.glfwGetCurrentContext()) |handle| return Window.from(handle);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
return null; return null;
} }
@ -83,7 +72,7 @@ pub inline fn getCurrentContext() ?Window {
/// @param[in] interval The minimum number of screen updates to wait for until the buffers are /// @param[in] interval The minimum number of screen updates to wait for until the buffers are
/// swapped by glfw.swapBuffers. /// swapped by glfw.swapBuffers.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.NoCurrentContext and glfw.Error.PlatformError. /// Possible errors include glfw.Error.NoCurrentContext and glfw.Error.PlatformError.
/// ///
/// This function is not called during context creation, leaving the swap interval set to whatever /// This function is not called during context creation, leaving the swap interval set to whatever
/// is the default for that API. This is done because some swap interval extensions used by /// is the default for that API. This is done because some swap interval extensions used by
@ -96,14 +85,9 @@ pub inline fn getCurrentContext() ?Window {
/// @thread_safety This function may be called from any thread. /// @thread_safety This function may be called from any thread.
/// ///
/// see also: buffer_swap, glfwSwapBuffers /// see also: buffer_swap, glfwSwapBuffers
pub inline fn swapInterval(interval: i32) error{ NoCurrentContext, PlatformError }!void { pub inline fn swapInterval(interval: i32) void {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
c.glfwSwapInterval(@intCast(c_int, interval)); c.glfwSwapInterval(@intCast(c_int, interval));
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.NoCurrentContext, Error.PlatformError => |e| e,
else => unreachable,
};
} }
/// Returns whether the specified extension is available. /// Returns whether the specified extension is available.
@ -125,26 +109,19 @@ pub inline fn swapInterval(interval: i32) error{ NoCurrentContext, PlatformError
/// @param[in] extension The ASCII encoded name of the extension. /// @param[in] extension The ASCII encoded name of the extension.
/// @return `true` if the extension is available, or `false` otherwise. /// @return `true` if the extension is available, or `false` otherwise.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.NoCurrentContext, glfw.Error.InvalidValue /// Possible errors include glfw.Error.NoCurrentContext, glfw.Error.InvalidValue
/// and glfw.Error.PlatformError. /// and glfw.Error.PlatformError.
/// ///
/// @thread_safety This function may be called from any thread. /// @thread_safety This function may be called from any thread.
/// ///
/// see also: context_glext, glfw.getProcAddress /// see also: context_glext, glfw.getProcAddress
pub inline fn extensionSupported(extension: [:0]const u8) error{ NoCurrentContext, PlatformError }!bool { pub inline fn extensionSupported(extension: [:0]const u8) bool {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
std.debug.assert(extension.len != 0); std.debug.assert(extension.len != 0);
std.debug.assert(extension[0] != 0); std.debug.assert(extension[0] != 0);
const supported = c.glfwExtensionSupported(extension.ptr); return c.glfwExtensionSupported(extension.ptr) == c.GLFW_TRUE;
getError() catch |err| return switch (err) {
Error.NoCurrentContext, Error.PlatformError => |e| e,
Error.NotInitialized => unreachable,
Error.InvalidValue => unreachable,
else => unreachable,
};
return supported == c.GLFW_TRUE;
} }
const builtin = @import("builtin"); const builtin = @import("builtin");
@ -189,29 +166,34 @@ pub const GLProc = *const fn () callconv(.C) void;
pub fn getProcAddress(proc_name: [*:0]const u8) callconv(.C) ?GLProc { pub fn getProcAddress(proc_name: [*:0]const u8) callconv(.C) ?GLProc {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (c.glfwGetProcAddress(proc_name)) |proc_address| return proc_address; if (c.glfwGetProcAddress(proc_name)) |proc_address| return proc_address;
getError() catch |err| @panic(@errorName(err));
return null; return null;
} }
test "makeContextCurrent" { test "makeContextCurrent" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) catch |err| { const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) orelse {
// return without fail, because most of our CI environments are headless / we cannot open std.log.err("failed to create window: {?s}", .{glfw.getErrorString()});
// windows on them. std.process.exit(1);
std.debug.print("note: failed to create window: {}\n", .{err});
return;
}; };
defer window.destroy(); defer window.destroy();
try glfw.makeContextCurrent(window); glfw.makeContextCurrent(window);
} }
test "getCurrentContext" { test "getCurrentContext" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const current_context = glfw.getCurrentContext(); const current_context = glfw.getCurrentContext();
@ -220,51 +202,57 @@ test "getCurrentContext" {
test "swapInterval" { test "swapInterval" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) catch |err| { const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) orelse {
// return without fail, because most of our CI environments are headless / we cannot open std.log.err("failed to create window: {?s}", .{glfw.getErrorString()});
// windows on them. std.process.exit(1);
std.debug.print("note: failed to create window: {}\n", .{err});
return;
}; };
defer window.destroy(); defer window.destroy();
try glfw.makeContextCurrent(window); glfw.makeContextCurrent(window);
glfw.swapInterval(1) catch |err| std.debug.print("failed to set swap interval, error={}\n", .{err}); glfw.swapInterval(1);
} }
test "getProcAddress" { test "getProcAddress" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) catch |err| { const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) orelse {
// return without fail, because most of our CI environments are headless / we cannot open std.log.err("failed to create window: {?s}", .{glfw.getErrorString()});
// windows on them. std.process.exit(1);
std.debug.print("note: failed to create window: {}\n", .{err});
return;
}; };
defer window.destroy(); defer window.destroy();
try glfw.makeContextCurrent(window); glfw.makeContextCurrent(window);
_ = glfw.getProcAddress("foobar"); _ = glfw.getProcAddress("foobar");
} }
test "extensionSupported" { test "extensionSupported" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) catch |err| { const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) orelse {
// return without fail, because most of our CI environments are headless / we cannot open std.log.err("failed to create window: {?s}", .{glfw.getErrorString()});
// windows on them. std.process.exit(1);
std.debug.print("note: failed to create window: {}\n", .{err});
return;
}; };
defer window.destroy(); defer window.destroy();
try glfw.makeContextCurrent(window); glfw.makeContextCurrent(window);
_ = glfw.extensionSupported("foobar") catch |err| std.debug.print("failed to check if extension supported, error={}\n", .{err}); _ = glfw.extensionSupported("foobar");
} }

View file

@ -31,11 +31,8 @@ pub inline fn getTime() f64 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const time = c.glfwGetTime(); const time = c.glfwGetTime();
if (time != 0) return time; if (time != 0) return time;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetTime` returns `0` only for errors // `glfwGetTime` returns `0` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
@ -49,7 +46,7 @@ pub inline fn getTime() f64 {
/// ///
/// @param[in] time The new value, in seconds. /// @param[in] time The new value, in seconds.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.InvalidValue. /// Possible errors include glfw.Error.InvalidValue.
/// ///
/// The upper limit of GLFW time is calculated as `floor((2^64 - 1) / 10^9)` and is due to /// The upper limit of GLFW time is calculated as `floor((2^64 - 1) / 10^9)` and is due to
/// implementations storing nanoseconds in 64 bits. The limit may be increased in the future. /// implementations storing nanoseconds in 64 bits. The limit may be increased in the future.
@ -70,11 +67,6 @@ pub inline fn setTime(time: f64) void {
}); });
c.glfwSetTime(time); c.glfwSetTime(time);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.InvalidValue => unreachable,
else => unreachable,
};
} }
/// Returns the current value of the raw timer. /// Returns the current value of the raw timer.
@ -91,11 +83,8 @@ pub inline fn getTimerValue() u64 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const value = c.glfwGetTimerValue(); const value = c.glfwGetTimerValue();
if (value != 0) return value; if (value != 0) return value;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetTimerValue` returns `0` only for errors // `glfwGetTimerValue` returns `0` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
@ -112,17 +101,18 @@ pub inline fn getTimerFrequency() u64 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const frequency = c.glfwGetTimerFrequency(); const frequency = c.glfwGetTimerFrequency();
if (frequency != 0) return frequency; if (frequency != 0) return frequency;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetTimerFrequency` returns `0` only for errors // `glfwGetTimerFrequency` returns `0` only for errors
// but the only potential error is unreachable (NotInitialized)
unreachable; unreachable;
} }
test "getTime" { test "getTime" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
_ = getTime(); _ = getTime();
@ -130,7 +120,11 @@ test "getTime" {
test "setTime" { test "setTime" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
_ = glfw.setTime(1234); _ = glfw.setTime(1234);
@ -138,7 +132,11 @@ test "setTime" {
test "getTimerValue" { test "getTimerValue" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
_ = glfw.getTimerValue(); _ = glfw.getTimerValue();
@ -146,7 +144,11 @@ test "getTimerValue" {
test "getTimerFrequency" { test "getTimerFrequency" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
_ = glfw.getTimerFrequency(); _ = glfw.getTimerFrequency();

View file

@ -49,13 +49,10 @@ pub const VKGetInstanceProcAddr = *const fn (vk_instance: c.VkInstance, name: [*
/// ///
/// @return `true` if Vulkan is minimally available, or `false` otherwise. /// @return `true` if Vulkan is minimally available, or `false` otherwise.
/// ///
/// Possible errors include glfw.Error.NotInitialized.
///
/// @thread_safety This function may be called from any thread. /// @thread_safety This function may be called from any thread.
pub inline fn vulkanSupported() bool { pub inline fn vulkanSupported() bool {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const supported = c.glfwVulkanSupported(); const supported = c.glfwVulkanSupported();
getError() catch unreachable; // Only error 'GLFW_NOT_INITIALIZED' is impossible
return supported == c.GLFW_TRUE; return supported == c.GLFW_TRUE;
} }
@ -73,7 +70,8 @@ pub inline fn vulkanSupported() bool {
/// If Vulkan is available but no set of extensions allowing window surface creation was found, /// If Vulkan is available but no set of extensions allowing window surface creation was found,
/// this function returns null. You may still use Vulkan for off-screen rendering and compute work. /// this function returns null. You may still use Vulkan for off-screen rendering and compute work.
/// ///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.APIUnavailable. /// Possible errors include glfw.Error.APIUnavailable.
/// Returns null in the event of an error.
/// ///
/// Additional extensions may be required by future versions of GLFW. You should check if any /// Additional extensions may be required by future versions of GLFW. You should check if any
/// extensions you wish to enable are already in the returned array, as it is an error to specify /// extensions you wish to enable are already in the returned array, as it is an error to specify
@ -85,17 +83,11 @@ pub inline fn vulkanSupported() bool {
/// @thread_safety This function may be called from any thread. /// @thread_safety This function may be called from any thread.
/// ///
/// see also: vulkan_ext, glfwCreateWindowSurface /// see also: vulkan_ext, glfwCreateWindowSurface
pub inline fn getRequiredInstanceExtensions() error{APIUnavailable}![][*:0]const u8 { pub inline fn getRequiredInstanceExtensions() ?[][*:0]const u8 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
var count: u32 = 0; var count: u32 = 0;
if (c.glfwGetRequiredInstanceExtensions(&count)) |extensions| return @ptrCast([*][*:0]const u8, extensions)[0..count]; if (c.glfwGetRequiredInstanceExtensions(&count)) |extensions| return @ptrCast([*][*:0]const u8, extensions)[0..count];
getError() catch |err| return switch (err) { return null;
Error.NotInitialized => unreachable,
Error.APIUnavailable => |e| e,
else => unreachable,
};
// `glfwGetRequiredInstanceExtensions` returns `null` only for errors
unreachable;
} }
/// Vulkan API function pointer type. /// Vulkan API function pointer type.
@ -139,7 +131,6 @@ pub const VKProc = *const fn () callconv(.C) void;
pub fn getInstanceProcAddress(vk_instance: ?*anyopaque, proc_name: [*:0]const u8) callconv(.C) ?VKProc { pub fn getInstanceProcAddress(vk_instance: ?*anyopaque, proc_name: [*:0]const u8) callconv(.C) ?VKProc {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
if (c.glfwGetInstanceProcAddress(if (vk_instance) |v| @ptrCast(c.VkInstance, v) else null, proc_name)) |proc_address| return proc_address; if (c.glfwGetInstanceProcAddress(if (vk_instance) |v| @ptrCast(c.VkInstance, v) else null, proc_name)) |proc_address| return proc_address;
getError() catch |err| @panic(@errorName(err));
return null; return null;
} }
@ -159,7 +150,8 @@ pub fn getInstanceProcAddress(vk_instance: ?*anyopaque, proc_name: [*:0]const u8
/// @param[in] queuefamily The index of the queue family to query. /// @param[in] queuefamily The index of the queue family to query.
/// @return `true` if the queue family supports presentation, or `false` otherwise. /// @return `true` if the queue family supports presentation, or `false` otherwise.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.APIUnavailable and glfw.Error.PlatformError. /// Possible errors include glfw.Error.APIUnavailable and glfw.Error.PlatformError.
/// Returns false in the event of an error.
/// ///
/// macos: This function currently always returns `true`, as the `VK_MVK_macos_surface` and /// macos: This function currently always returns `true`, as the `VK_MVK_macos_surface` and
/// 'VK_EXT_metal_surface' extension does not provide a `vkGetPhysicalDevice*PresentationSupport` type function. /// 'VK_EXT_metal_surface' extension does not provide a `vkGetPhysicalDevice*PresentationSupport` type function.
@ -172,19 +164,13 @@ pub inline fn getPhysicalDevicePresentationSupport(
vk_instance: *anyopaque, vk_instance: *anyopaque,
vk_physical_device: *anyopaque, vk_physical_device: *anyopaque,
queue_family: u32, queue_family: u32,
) error{ APIUnavailable, PlatformError }!bool { ) bool {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
const v = c.glfwGetPhysicalDevicePresentationSupport( return c.glfwGetPhysicalDevicePresentationSupport(
@ptrCast(c.VkInstance, vk_instance), @ptrCast(c.VkInstance, vk_instance),
@ptrCast(c.VkPhysicalDevice, vk_physical_device), @ptrCast(c.VkPhysicalDevice, vk_physical_device),
queue_family, queue_family,
); ) == c.GLFW_TRUE;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.APIUnavailable, Error.PlatformError => |e| e,
else => unreachable,
};
return v == c.GLFW_TRUE;
} }
/// Creates a Vulkan surface for the specified window. /// Creates a Vulkan surface for the specified window.
@ -217,7 +203,8 @@ pub inline fn getPhysicalDevicePresentationSupport(
/// @return `VkResult` type, `VK_SUCCESS` if successful, or a Vulkan error code if an /// @return `VkResult` type, `VK_SUCCESS` if successful, or a Vulkan error code if an
/// error occurred. /// error occurred.
/// ///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.APIUnavailable, glfw.Error.PlatformError and glfw.Error.InvalidValue /// Possible errors include glfw.Error.APIUnavailable, glfw.Error.PlatformError and glfw.Error.InvalidValue
/// Returns a bool indicating success.
/// ///
/// If an error occurs before the creation call is made, GLFW returns the Vulkan error code most /// If an error occurs before the creation call is made, GLFW returns the Vulkan error code most
/// appropriate for the error. Appropriate use of glfw.vulkanSupported and glfw.getRequiredInstanceExtensions /// appropriate for the error. Appropriate use of glfw.vulkanSupported and glfw.getRequiredInstanceExtensions
@ -241,7 +228,7 @@ pub inline fn getPhysicalDevicePresentationSupport(
/// Vulkan objects, see the Vulkan specification. /// Vulkan objects, see the Vulkan specification.
/// ///
/// see also: vulkan_surface, glfw.getRequiredInstanceExtensions /// see also: vulkan_surface, glfw.getRequiredInstanceExtensions
pub inline fn createWindowSurface(vk_instance: anytype, window: Window, vk_allocation_callbacks: anytype, vk_surface_khr: anytype) error{ APIUnavailable, PlatformError }!i32 { pub inline fn createWindowSurface(vk_instance: anytype, window: Window, vk_allocation_callbacks: anytype, vk_surface_khr: anytype) i32 {
internal_debug.assertInitialized(); internal_debug.assertInitialized();
// zig-vulkan uses enums to represent opaque pointers: // zig-vulkan uses enums to represent opaque pointers:
// pub const Instance = enum(usize) { null_handle = 0, _ }; // pub const Instance = enum(usize) { null_handle = 0, _ };
@ -250,26 +237,21 @@ pub inline fn createWindowSurface(vk_instance: anytype, window: Window, vk_alloc
else => @ptrCast(c.VkInstance, vk_instance), else => @ptrCast(c.VkInstance, vk_instance),
}; };
const v = c.glfwCreateWindowSurface( return c.glfwCreateWindowSurface(
instance, instance,
window.handle, window.handle,
if (vk_allocation_callbacks == null) null else @ptrCast(*const c.VkAllocationCallbacks, @alignCast(@alignOf(c.VkAllocationCallbacks), vk_allocation_callbacks)), if (vk_allocation_callbacks == null) null else @ptrCast(*const c.VkAllocationCallbacks, @alignCast(@alignOf(c.VkAllocationCallbacks), vk_allocation_callbacks)),
@ptrCast(*c.VkSurfaceKHR, @alignCast(@alignOf(c.VkSurfaceKHR), vk_surface_khr)), @ptrCast(*c.VkSurfaceKHR, @alignCast(@alignOf(c.VkSurfaceKHR), vk_surface_khr)),
); ) == c.VK_SUCCESS;
if (v == c.VK_SUCCESS) return v;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.InvalidValue => @panic("Attempted to use window with client api to create vulkan surface."),
Error.APIUnavailable, Error.PlatformError => |e| e,
else => unreachable,
};
// `glfwCreateWindowSurface` returns `!VK_SUCCESS` only for errors
unreachable;
} }
test "vulkanSupported" { test "vulkanSupported" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
_ = glfw.vulkanSupported(); _ = glfw.vulkanSupported();
@ -277,15 +259,23 @@ test "vulkanSupported" {
test "getRequiredInstanceExtensions" { test "getRequiredInstanceExtensions" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
_ = glfw.getRequiredInstanceExtensions() catch |err| std.debug.print("failed to get vulkan instance extensions, error={}\n", .{err}); _ = glfw.getRequiredInstanceExtensions();
} }
test "getInstanceProcAddress" { test "getInstanceProcAddress" {
const glfw = @import("main.zig"); const glfw = @import("main.zig");
try glfw.init(.{}); defer glfw.getError() catch {}; // clear any error we generate
if (!glfw.init(.{})) {
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
std.process.exit(1);
}
defer glfw.terminate(); defer glfw.terminate();
// syntax check only, we don't have a real vulkan instance and so this function would panic. // syntax check only, we don't have a real vulkan instance and so this function would panic.