all: move standalone libraries to libs/ subdirectory

The root dir of our repository has grown quite a lot the past few months.

I'd like to make it more clear where the bulk of the engine lives (`src/`) and
also make it more clear which Mach libraries are consumable as standalone projects.

As for the name of this directory, `libs` was my first choice but there's a bit of
a convention of that being external libraries in Zig projects _today_, while these
are libraries maintained as part of Mach in this repository - not external ones.

We will name this directory `libs`, and if we have a need for external libraries
we will use `external` or `deps` for that directory name. I considered other names
such as `components`, `systems`, `modules` (which are bad as they overlap with
major ECS / engine concepts), and it seems likely the official Zig package manager
will break the convention of using a `libs` dir anyway.

Performed via:

```sh
mkdir libs/
git mv freetype libs/
git mv basisu libs/
git mv gamemode libs/
git mv glfw libs/
git mv gpu libs/
git mv gpu-dawn libs/
git mv sysaudio libs/
git mv sysjs libs/
git mv ecs libs/
```

git-subtree-dir: glfw
git-subtree-mainline: 0d5b853443
git-subtree-split: 572d1144f11b353abdb64fff828b25a4f0fbb7ca

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>

git mv ecs libs/

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2022-08-26 13:29:04 -07:00 committed by Stephen Gutekanst
parent 79ec61396f
commit 0645429df9
240 changed files with 6 additions and 6 deletions

223
libs/glfw/src/Cursor.zig Normal file
View file

@ -0,0 +1,223 @@
//! Represents a cursor and provides facilities for setting cursor images.
const std = @import("std");
const testing = std.testing;
const c = @import("c.zig").c;
const Error = @import("errors.zig").Error;
const getError = @import("errors.zig").getError;
const Image = @import("Image.zig");
const internal_debug = @import("internal_debug.zig");
const Cursor = @This();
ptr: *c.GLFWcursor,
/// Standard system cursor shapes.
///
/// These are the standard cursor shapes that can be requested from the platform (window system).
pub const Shape = enum(i32) {
/// The regular arrow cursor shape.
arrow = c.GLFW_ARROW_CURSOR,
/// The text input I-beam cursor shape.
ibeam = c.GLFW_IBEAM_CURSOR,
/// The crosshair cursor shape.
crosshair = c.GLFW_CROSSHAIR_CURSOR,
/// The pointing hand cursor shape.
///
/// NOTE: This supersedes the old `hand` enum.
pointing_hand = c.GLFW_POINTING_HAND_CURSOR,
/// The horizontal resize/move arrow shape.
///
/// The horizontal resize/move arrow shape. This is usually a horizontal double-headed arrow.
//
// NOTE: This supersedes the old `hresize` enum.
resize_ew = c.GLFW_RESIZE_EW_CURSOR,
/// The vertical resize/move arrow shape.
///
/// The vertical resize/move shape. This is usually a vertical double-headed arrow.
///
/// NOTE: This supersedes the old `vresize` enum.
resize_ns = c.GLFW_RESIZE_NS_CURSOR,
/// The top-left to bottom-right diagonal resize/move arrow shape.
///
/// The top-left to bottom-right diagonal resize/move shape. This is usually a diagonal
/// double-headed arrow.
///
/// macos: This shape is provided by a private system API and may fail CursorUnavailable in the
/// future.
///
/// x11: This shape is provided by a newer standard not supported by all cursor themes.
///
/// wayland: This shape is provided by a newer standard not supported by all cursor themes.
resize_nwse = c.GLFW_RESIZE_NWSE_CURSOR,
/// The top-right to bottom-left diagonal resize/move arrow shape.
///
/// The top-right to bottom-left diagonal resize/move shape. This is usually a diagonal
/// double-headed arrow.
///
/// macos: This shape is provided by a private system API and may fail with CursorUnavailable
/// in the future.
///
/// x11: This shape is provided by a newer standard not supported by all cursor themes.
///
/// wayland: This shape is provided by a newer standard not supported by all cursor themes.
resize_nesw = c.GLFW_RESIZE_NESW_CURSOR,
/// The omni-directional resize/move cursor shape.
///
/// The omni-directional resize cursor/move shape. This is usually either a combined horizontal
/// and vertical double-headed arrow or a grabbing hand.
resize_all = c.GLFW_RESIZE_ALL_CURSOR,
/// The operation-not-allowed shape.
///
/// The operation-not-allowed shape. This is usually a circle with a diagonal line through it.
///
/// x11: This shape is provided by a newer standard not supported by all cursor themes.
///
/// wayland: This shape is provided by a newer standard not supported by all cursor themes.
not_allowed = c.GLFW_NOT_ALLOWED_CURSOR,
};
/// Creates a custom cursor.
///
/// Creates a new custom cursor image that can be set for a window with glfw.Cursor.set. The cursor
/// can be destroyed with glfwCursor.destroy. Any remaining cursors are destroyed by glfw.terminate.
///
/// The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight bits per channel with
/// the red channel first. They are arranged canonically as packed sequential rows, starting from
/// the top-left corner.
///
/// The cursor hotspot is specified in pixels, relative to the upper-left corner of the cursor
/// image. Like all other coordinate systems in GLFW, the X-axis points to the right and the Y-axis
/// points down.
///
/// @param[in] image The desired cursor image.
/// @param[in] xhot The desired x-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.
///
/// @pointer_lifetime The specified image data is copied before this function returns.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: cursor_object, glfw.Cursor.destroy, glfw.Cursor.createStandard
pub inline fn create(image: Image, xhot: i32, yhot: i32) error{ PlatformError, InvalidValue }!Cursor {
internal_debug.assertInitialized();
const img = image.toC();
if (c.glfwCreateCursor(&img, @intCast(c_int, xhot), @intCast(c_int, yhot))) |cursor| return Cursor{ .ptr = cursor };
getError() catch |err| return switch (err) {
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.
///
/// Returns a cursor with a standard shape, that can be set for a window with glfw.Window.setCursor.
/// The images for these cursors come from the system cursor theme and their exact appearance will
/// vary between platforms.
///
/// Most of these shapes are guaranteed to exist on every supported platform but a few may not be
/// present. See the table below for details.
///
/// | Cursor shape | Windows | macOS | X11 | Wayland |
/// |------------------|---------|-----------------|-------------------|-------------------|
/// | `.arrow` | Yes | Yes | Yes | Yes |
/// | `.ibeam` | Yes | Yes | Yes | Yes |
/// | `.crosshair` | Yes | Yes | Yes | Yes |
/// | `.pointing_hand` | Yes | Yes | Yes | Yes |
/// | `.resize_ew` | Yes | Yes | Yes | Yes |
/// | `.resize_ns` | Yes | Yes | Yes | Yes |
/// | `.resize_nwse` | Yes | Yes<sup>1</sup> | Maybe<sup>2</sup> | Maybe<sup>2</sup> |
/// | `.resize_nesw` | Yes | Yes<sup>1</sup> | Maybe<sup>2</sup> | Maybe<sup>2</sup> |
/// | `.resize_all` | Yes | Yes | Yes | Yes |
/// | `.not_allowed` | Yes | Yes | Maybe<sup>2</sup> | Maybe<sup>2</sup> |
///
/// 1. This uses a private system API and may fail in the future.
/// 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
///
/// thread_safety: This function must only be called from the main thread.
///
/// see also: cursor_object, glfwCreateCursor
pub inline fn createStandard(shape: Shape) error{ PlatformError, CursorUnavailable }!Cursor {
internal_debug.assertInitialized();
if (c.glfwCreateStandardCursor(@intCast(c_int, @enumToInt(shape)))) |cursor| return Cursor{ .ptr = cursor };
getError() catch |err| return switch (err) {
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.
///
/// This function destroys a cursor previously created with glfw.Cursor.create. Any remaining
/// cursors will be destroyed by glfw.terminate.
///
/// 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.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError.
///
/// @reentrancy This function must not be called from a callback.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: cursor_object, glfw.createCursor
pub inline fn destroy(self: Cursor) void {
internal_debug.assertInitialized();
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" {
const allocator = testing.allocator;
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const image = try Image.init(allocator, 32, 32, 32 * 32 * 4);
defer image.deinit(allocator);
const cursor = glfw.Cursor.create(image, 0, 0) catch |err| {
std.debug.print("failed to create cursor, custom cursors not supported? error={}\n", .{err});
return;
};
cursor.destroy();
}
test "createStandard" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const cursor = glfw.Cursor.createStandard(.ibeam) catch |err| {
std.debug.print("failed to create cursor, custom cursors not supported? error={}\n", .{err});
return;
};
cursor.destroy();
}

View file

@ -0,0 +1,74 @@
//! Gamma ramp for monitors and related functions.
//!
//! It may be .owned (e.g. in the case of a gamma ramp initialized by you for passing into
//! glfw.Monitor.setGammaRamp) or not .owned (e.g. in the case of one gotten via
//! glfw.Monitor.getGammaRamp.) If it is .owned, deinit should be called to free the memory. It is
//! safe to call deinit even if not .owned.
//!
//! see also: monitor_gamma, glfw.Monitor.getGammaRamp
const std = @import("std");
const testing = std.testing;
const mem = std.mem;
const c = @import("c.zig").c;
const GammaRamp = @This();
red: []u16,
green: []u16,
blue: []u16,
owned: bool,
/// Initializes a new owned gamma ramp with the given array size and undefined values.
///
/// see also: glfw.Monitor.getGammaRamp
pub inline fn init(allocator: mem.Allocator, size: usize) !GammaRamp {
const buf = try allocator.alloc(u16, size * 3);
return GammaRamp{
.red = buf[size * 0 .. (size * 0) + size],
.green = buf[size * 1 .. (size * 1) + size],
.blue = buf[size * 2 .. (size * 2) + size],
.owned = true,
};
}
/// Turns a GLFW / C gamma ramp into the nicer Zig type, and sets `.owned = false`.
///
/// The returned memory is valid for as long as the GLFW C memory is valid.
pub inline fn fromC(native: c.GLFWgammaramp) GammaRamp {
return GammaRamp{
.red = native.red[0..native.size],
.green = native.green[0..native.size],
.blue = native.blue[0..native.size],
.owned = false,
};
}
/// Turns the nicer Zig type into a GLFW / C gamma ramp, for passing into GLFW C functions.
///
/// The returned memory is valid for as long as the Zig memory is valid.
pub inline fn toC(self: GammaRamp) c.GLFWgammaramp {
std.debug.assert(self.red.len == self.green.len);
std.debug.assert(self.red.len == self.blue.len);
return c.GLFWgammaramp{
.red = &self.red[0],
.green = &self.green[0],
.blue = &self.blue[0],
.size = @intCast(c_uint, self.red.len),
};
}
/// Deinitializes the memory using the allocator iff `.owned = true`.
pub inline fn deinit(self: GammaRamp, allocator: mem.Allocator) void {
if (self.owned) allocator.free(self.red);
}
test "conversion" {
const allocator = testing.allocator;
const ramp = try GammaRamp.init(allocator, 256);
defer ramp.deinit(allocator);
const glfw = ramp.toC();
_ = GammaRamp.fromC(glfw);
}

82
libs/glfw/src/Image.zig Normal file
View file

@ -0,0 +1,82 @@
//! Image data
//!
//!
//! This describes a single 2D image. See the documentation for each related function what the
//! expected pixel format is.
//!
//! see also: cursor_custom, window_icon
//!
//! It may be .owned (e.g. in the case of an image initialized by you for passing into glfw) or not
//! .owned (e.g. in the case of one gotten via glfw) If it is .owned, deinit should be called to
//! free the memory. It is safe to call deinit even if not .owned.
const std = @import("std");
const testing = std.testing;
const mem = std.mem;
const c = @import("c.zig").c;
const Image = @This();
/// The width of this image, in pixels.
width: u32,
/// The height of this image, in pixels.
height: u32,
/// The pixel data of this image, arranged left-to-right, top-to-bottom.
pixels: []u8,
/// Whether or not the pixels data is owned by you (true) or GLFW (false).
owned: bool,
/// Initializes a new owned image with the given size and pixel_data_len of undefined .pixel values.
pub inline fn init(allocator: mem.Allocator, width: u32, height: u32, pixel_data_len: usize) !Image {
const buf = try allocator.alloc(u8, pixel_data_len);
return Image{
.width = width,
.height = height,
.pixels = buf,
.owned = true,
};
}
/// Turns a GLFW / C image into the nicer Zig type, and sets `.owned = false`.
///
/// The length of pixel data must be supplied, as GLFW's image type does not itself describe the
/// number of bytes required per pixel / the length of the pixel data array.
///
/// The returned memory is valid for as long as the GLFW C memory is valid.
pub inline fn fromC(native: c.GLFWimage, pixel_data_len: usize) Image {
return Image{
.width = @intCast(u32, native.width),
.height = @intCast(u32, native.height),
.pixels = native.pixels[0..pixel_data_len],
.owned = false,
};
}
/// Turns the nicer Zig type into a GLFW / C image, for passing into GLFW C functions.
///
/// The returned memory is valid for as long as the Zig memory is valid.
pub inline fn toC(self: Image) c.GLFWimage {
return c.GLFWimage{
.width = @intCast(c_int, self.width),
.height = @intCast(c_int, self.height),
.pixels = &self.pixels[0],
};
}
/// Deinitializes the memory using the allocator iff `.owned = true`.
pub inline fn deinit(self: Image, allocator: mem.Allocator) void {
if (self.owned) allocator.free(self.pixels);
}
test "conversion" {
const allocator = testing.allocator;
const image = try Image.init(allocator, 256, 256, 256 * 256 * 4);
defer image.deinit(allocator);
const glfw = image.toC();
_ = Image.fromC(glfw, image.width * image.height * 4);
}

667
libs/glfw/src/Joystick.zig Normal file
View file

@ -0,0 +1,667 @@
//! Represents a Joystick or gamepad
//!
//! It can be manually crafted via e.g. `glfw.Joystick{.jid = .one}`, but more
//! typically you'll want to discover the joystick using `glfw.Joystick.setCallback`.
const std = @import("std");
const c = @import("c.zig").c;
const Window = @import("Window.zig");
const Error = @import("errors.zig").Error;
const getError = @import("errors.zig").getError;
const Action = @import("action.zig").Action;
const GamepadAxis = @import("gamepad_axis.zig").GamepadAxis;
const GamepadButton = @import("gamepad_button.zig").GamepadButton;
const Hat = @import("hat.zig").Hat;
const internal_debug = @import("internal_debug.zig");
const Joystick = @This();
/// The GLFW joystick ID.
jid: Id,
/// Joystick IDs.
///
/// See glfw.Joystick.setCallback for how these are used.
pub const Id = enum(c_int) {
one = c.GLFW_JOYSTICK_1,
two = c.GLFW_JOYSTICK_2,
three = c.GLFW_JOYSTICK_3,
four = c.GLFW_JOYSTICK_4,
five = c.GLFW_JOYSTICK_5,
six = c.GLFW_JOYSTICK_6,
seven = c.GLFW_JOYSTICK_7,
eight = c.GLFW_JOYSTICK_8,
nine = c.GLFW_JOYSTICK_9,
ten = c.GLFW_JOYSTICK_10,
eleven = c.GLFW_JOYSTICK_11,
twelve = c.GLFW_JOYSTICK_12,
thirteen = c.GLFW_JOYSTICK_13,
fourteen = c.GLFW_JOYSTICK_14,
fifteen = c.GLFW_JOYSTICK_15,
sixteen = c.GLFW_JOYSTICK_16,
pub const last = @intToEnum(@This(), c.GLFW_JOYSTICK_LAST);
};
/// Gamepad input state
///
/// This describes the input state of a gamepad.
///
/// see also: gamepad, glfwGetGamepadState
const GamepadState = extern struct {
/// The states of each gamepad button (see gamepad_buttons), `glfw.Action.press` or `glfw.Action.release`.
///
/// Use the enumeration helper e.g. `.getButton(.dpad_up)` to access these indices.
buttons: [15]u8,
/// The states of each gamepad axis (see gamepad_axes), in the range -1.0 to 1.0 inclusive.
///
/// Use the enumeration helper e.g. `.getAxis(.left_x)` to access these indices.
axes: [6]f32,
/// Returns the state of the specified gamepad button.
pub fn getButton(self: @This(), which: GamepadButton) Action {
_ = self;
return @intToEnum(Action, self.buttons[@intCast(u32, @enumToInt(which))]);
}
/// Returns the status of the specified gamepad axis, in the range -1.0 to 1.0 inclusive.
pub fn getAxis(self: @This(), which: GamepadAxis) f32 {
_ = self;
return self.axes[@intCast(u32, @enumToInt(which))];
}
};
/// Returns whether the specified joystick is present.
///
/// This function returns whether the specified joystick is present.
///
/// There is no need to call this function before other functions that accept a joystick ID, as
/// they all check for presence before performing any other work.
///
/// @return `true` if the joystick is present, or `false` otherwise.
///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.InvalidEnum and glfw.Error.PlatformError.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: joystick
pub inline fn present(self: Joystick) error{PlatformError}!bool {
internal_debug.assertInitialized();
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;
}
/// Returns the values of all axes of the specified joystick.
///
/// This function returns the values of all axes of the specified joystick. Each element in the
/// array is a value between -1.0 and 1.0.
///
/// If the specified joystick is not present this function will return null but will not generate
/// an error. This can be used instead of first calling glfw.Joystick.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.
///
/// @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.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: joystick_axis
/// Replaces `glfwGetJoystickPos`.
pub inline fn getAxes(self: Joystick) error{PlatformError}!?[]const f32 {
internal_debug.assertInitialized();
var count: c_int = undefined;
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;
return axes[0..@intCast(u32, count)];
}
/// Returns the state of all buttons of the specified joystick.
///
/// This function returns the state of all buttons of the specified joystick. Each element in the
/// array is either `glfw.Action.press` or `glfw.Action.release`.
///
/// For backward compatibility with earlier versions that did not have glfw.Joystick.getHats, the
/// button array also includes all hats, each represented as four buttons. The hats are in the same
/// order as returned by glfw.Joystick.getHats and are in the order _up_, _right_, _down_ and
/// _left_. To disable these extra buttons, set the glfw.joystick_hat_buttons init hint before
/// initialization.
///
/// If the specified joystick is not present this function will return null but will not generate an
/// error. This can be used instead of first calling glfw.Joystick.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.
///
/// @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.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: joystick_button
pub inline fn getButtons(self: Joystick) error{PlatformError}!?[]const u8 {
internal_debug.assertInitialized();
var count: c_int = undefined;
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;
return buttons[0..@intCast(u32, count)];
}
/// Returns the state of all hats of the specified joystick.
///
/// This function returns the state of all hats of the specified joystick. Each element in the array
/// is one of the following values:
///
/// | Name | Value |
/// |---------------------------|---------------------------------------------|
/// | `glfw.RawHats.centered` | 0 |
/// | `glfw.RawHats.up` | 1 |
/// | `glfw.RawHats.right` | 2 |
/// | `glfw.RawHats.down` | 4 |
/// | `glfw.RawHats.left` | 8 |
/// | `glfw.RawHats.right_up` | `glfw.RawHats.right` \| `glfw.RawHats.up` |
/// | `glfw.RawHats.right_down` | `glfw.RawHats.right` \| `glfw.RawHats.down` |
/// | `glfw.RawHats.left_up` | `glfw.RawHats.left` \| `glfw.RawHats.up` |
/// | `glfw.RawHats.left_down` | `glfw.RawHats.left` \| `glfw.RawHats.down` |
///
/// The diagonal directions are bitwise combinations of the primary (up, right, down and left)
/// directions, since the Zig GLFW wrapper returns a packed struct it is trivial to test for these:
///
/// ```
/// if (hats.up and hats.right) {
/// // up-right!
/// }
/// ```
///
/// If the specified joystick is not present this function will return null but will not generate an
/// error. This can be used instead of first calling glfw.Joystick.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.
///
/// @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
/// again for that joystick or the library is terminated.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: joystick_hat
pub inline fn getHats(self: Joystick) error{PlatformError}!?[]const Hat {
internal_debug.assertInitialized();
var count: c_int = undefined;
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;
const slice = hats[0..@intCast(u32, count)];
return @ptrCast(*const []const Hat, &slice).*;
}
/// Returns the name of the specified joystick.
///
/// This function returns the name, encoded as UTF-8, of the specified joystick. The returned string
/// is allocated and freed by GLFW. You should not free it yourself.
///
/// If the specified joystick is not present this function will return null but will not generate an
/// error. This can be used instead of first calling glfw.Joystick.present.
///
/// @return The UTF-8 encoded name of the joystick, or null if the joystick is not present or an
/// error occurred.
///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.InvalidEnum and glfw.Error.PlatformError.
///
/// @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.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: joystick_name
pub inline fn getName(self: Joystick) error{PlatformError}!?[:0]const u8 {
internal_debug.assertInitialized();
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|
std.mem.span(@ptrCast([*:0]const u8, name))
else
null;
}
/// Returns the SDL compatible GUID of the specified joystick.
///
/// This function returns the SDL compatible GUID, as a UTF-8 encoded hexadecimal string, of the
/// specified joystick. The returned string is allocated and freed by GLFW. You should not free it
/// yourself.
///
/// The GUID is what connects a joystick to a gamepad mapping. A connected joystick will always have
/// a GUID even if there is no gamepad mapping assigned to it.
///
/// If the specified joystick is not present this function will return null but will not generate an
/// error. This can be used instead of first calling glfw.Joystick.present.
///
/// The GUID uses the format introduced in SDL 2.0.5. This GUID tries to uniquely identify the make
/// and model of a joystick but does not identify a specific unit, e.g. all wired Xbox 360
/// controllers will have the same GUID on that platform. The GUID for a unit may vary between
/// platforms depending on what hardware information the platform specific APIs provide.
///
/// @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.
///
/// @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.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: gamepad
pub inline fn getGUID(self: Joystick) error{PlatformError}!?[:0]const u8 {
internal_debug.assertInitialized();
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|
std.mem.span(@ptrCast([*:0]const u8, guid))
else
null;
}
/// Sets the user pointer of the specified joystick.
///
/// This function sets the user-defined pointer of the specified joystick. The current value is
/// retained until the joystick is disconnected. The initial value is null.
///
/// This function may be called from the joystick callback, even for a joystick that is being disconnected.
///
/// @thread_safety This function may be called from any thread. Access is not synchronized.
///
/// see also: joystick_userptr, glfw.Joystick.getUserPointer
pub inline fn setUserPointer(self: Joystick, comptime T: type, pointer: *T) void {
internal_debug.assertInitialized();
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.
///
/// This function returns the current value of the user-defined pointer of the specified joystick.
/// The initial value is null.
///
/// This function may be called from the joystick callback, even for a joystick that is being
/// disconnected.
///
/// @thread_safety This function may be called from any thread. Access is not synchronized.
///
/// see also: joystick_userptr, glfw.Joystick.setUserPointer
pub inline fn getUserPointer(self: Joystick, comptime PointerType: type) ?PointerType {
internal_debug.assertInitialized();
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));
return null;
}
/// Describes an event relating to a joystick.
pub const Event = enum(c_int) {
/// The device was connected.
connected = c.GLFW_CONNECTED,
/// The device was disconnected.
disconnected = c.GLFW_DISCONNECTED,
};
/// Sets the joystick configuration callback.
///
/// This function sets the joystick configuration callback, or removes the currently set callback.
/// This is called when a joystick is connected to or disconnected from the system.
///
/// For joystick connection and disconnection events to be delivered on all platforms, you need to
/// call one of the event processing (see events) functions. Joystick disconnection may also be
/// detected and the callback called by joystick functions. The function will then return whatever
/// it returns if the joystick is not present.
///
/// @param[in] callback The new callback, or null to remove the currently set callback.
///
/// @callback_param `jid` The joystick that was connected or disconnected.
/// @callback_param `event` One of `.connected` or `.disconnected`. Future releases may add
/// more events.
///
/// Possible errors include glfw.Error.NotInitialized.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: joystick_event
pub inline fn setCallback(comptime callback: ?fn (joystick: Joystick, event: Event) void) void {
internal_debug.assertInitialized();
if (callback) |user_callback| {
const CWrapper = struct {
pub fn joystickCallbackWrapper(jid: c_int, event: c_int) callconv(.C) void {
@call(.{ .modifier = .always_inline }, user_callback, .{
Joystick{ .jid = @intToEnum(Joystick.Id, jid) },
@intToEnum(Event, event),
});
}
};
if (c.glfwSetJoystickCallback(CWrapper.joystickCallbackWrapper) != null) return;
} else {
if (c.glfwSetJoystickCallback(null) != null) return;
}
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
}
/// Adds the specified SDL_GameControllerDB gamepad mappings.
///
/// This function parses the specified ASCII encoded string and updates the internal list with any
/// gamepad mappings it finds. This string may contain either a single gamepad mapping or many
/// mappings separated by newlines. The parser supports the full format of the `gamecontrollerdb.txt`
/// source file including empty lines and comments.
///
/// See gamepad_mapping for a description of the format.
///
/// If there is already a gamepad mapping for a given GUID in the internal list, it will be
/// replaced by the one passed to this function. If the library is terminated and re-initialized
/// the internal list will revert to the built-in default.
///
/// @param[in] string The string containing the gamepad mappings.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.InvalidValue.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: gamepad, glfw.Joystick.isGamepad, glfwGetGamepadName
///
///
/// @ingroup input
pub inline fn updateGamepadMappings(gamepad_mappings: [*:0]const u8) error{InvalidValue}!void {
internal_debug.assertInitialized();
if (c.glfwUpdateGamepadMappings(gamepad_mappings) == c.GLFW_TRUE) return;
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.
///
/// This function returns whether the specified joystick is both present and has a gamepad mapping.
///
/// If the specified joystick is present but does not have a gamepad mapping this function will
/// return `false` but will not generate an error. Call glfw.Joystick.present to check if a
/// joystick is present regardless of whether it has a mapping.
///
/// @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.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: gamepad, glfw.Joystick.getGamepadState
pub inline fn isGamepad(self: Joystick) bool {
internal_debug.assertInitialized();
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;
}
/// Returns the human-readable gamepad name for the specified joystick.
///
/// This function returns the human-readable name of the gamepad from the gamepad mapping assigned
/// to the specified joystick.
///
/// If the specified joystick is not present or does not have a gamepad mapping this function will
/// return null, not an error. Call glfw.Joystick.present to check whether it is
/// present regardless of whether it has a mapping.
///
/// @return The UTF-8 encoded name of the gamepad, or null if the joystick is not present or does
/// not have a mapping.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.InvalidEnum.
///
/// @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
/// updated or the library is terminated.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: gamepad, glfw.Joystick.isGamepad
pub inline fn getGamepadName(self: Joystick) ?[:0]const u8 {
internal_debug.assertInitialized();
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|
std.mem.span(@ptrCast([*:0]const u8, name))
else
null;
}
/// Retrieves the state of the joystick remapped as a gamepad.
///
/// This function retrieves the state of the joystick remapped to an Xbox-like gamepad.
///
/// If the specified joystick is not present or does not have a gamepad mapping this function will
/// return `false`. Call glfw.joystickPresent to check whether it is present regardless of whether
/// it has a mapping.
///
/// The Guide button may not be available for input as it is often hooked by the system or the
/// Steam client.
///
/// Not all devices have all the buttons or axes provided by GamepadState. Unavailable buttons
/// and axes will always report `glfw.Action.release` and 0.0 respectively.
///
/// @param[in] jid The joystick (see joysticks) to query.
/// @param[out] state The gamepad input state of the joystick.
/// @return the gamepad input state if successful, or null if no joystick is connected or it has no
/// gamepad mapping.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.InvalidEnum.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: gamepad, glfw.UpdateGamepadMappings, glfw.Joystick.isGamepad
pub inline fn getGamepadState(self: Joystick) ?GamepadState {
internal_debug.assertInitialized();
var state: GamepadState = undefined;
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;
}
test "present" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.present() catch |err| std.debug.print("failed to detect joystick, joysticks not supported? error={}\n", .{err});
}
test "getAxes" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.getAxes() catch |err| std.debug.print("failed to get joystick axes, joysticks not supported? error={}\n", .{err});
}
test "getButtons" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.getButtons() catch |err| std.debug.print("failed to get joystick buttons, joysticks not supported? error={}\n", .{err});
}
test "getHats" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.getHats() catch |err| std.debug.print("failed to get joystick hats, joysticks not supported? error={}\n", .{err});
const hats = std.mem.zeroes(Hat);
if (hats.down and hats.up) {
// down-up!
}
}
test "getName" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.getName() catch |err| std.debug.print("failed to get joystick name, joysticks not supported? error={}\n", .{err});
}
test "getGUID" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.getGUID() catch |err| std.debug.print("failed to get joystick GUID, joysticks not supported? error={}\n", .{err});
}
test "setUserPointer_syntax" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one };
// Must be called from joystick callback, we cannot test it.
_ = joystick;
_ = setUserPointer;
}
test "getUserPointer_syntax" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one };
// Must be called from joystick callback, we cannot test it.
_ = joystick;
_ = getUserPointer;
}
test "setCallback" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
glfw.Joystick.setCallback((struct {
pub fn callback(joystick: Joystick, event: Event) void {
_ = joystick;
_ = event;
}
}).callback);
}
test "updateGamepadMappings_syntax" {
// We don't have a gamepad mapping to test with, just confirm the syntax is good.
_ = updateGamepadMappings;
}
test "isGamepad" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.isGamepad();
}
test "getGamepadName" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.getGamepadName();
}
test "getGamepadState" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const joystick = glfw.Joystick{ .jid = .one };
_ = joystick.getGamepadState();
_ = (std.mem.zeroes(GamepadState)).getAxis(.left_x);
_ = (std.mem.zeroes(GamepadState)).getButton(.dpad_up);
}

626
libs/glfw/src/Monitor.zig Normal file
View file

@ -0,0 +1,626 @@
//! Monitor type and related functions
const std = @import("std");
const mem = std.mem;
const testing = std.testing;
const c = @import("c.zig").c;
const Error = @import("errors.zig").Error;
const getError = @import("errors.zig").getError;
const GammaRamp = @import("GammaRamp.zig");
const VideoMode = @import("VideoMode.zig");
const internal_debug = @import("internal_debug.zig");
const Monitor = @This();
handle: *c.GLFWmonitor,
/// A monitor position, in screen coordinates, of the upper left corner of the monitor on the
/// virtual screen.
const Pos = struct {
/// The x coordinate.
x: u32,
/// The y coordinate.
y: u32,
};
/// Returns the position of the monitor's viewport on the virtual screen.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: monitor_properties
pub inline fn getPos(self: Monitor) error{PlatformError}!Pos {
internal_debug.assertInitialized();
var xpos: c_int = 0;
var ypos: c_int = 0;
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) };
}
/// The monitor workarea, in screen coordinates.
///
/// This is the position of the upper-left corner of the work area of the monitor, along with the
/// work area size. The work area is defined as the area of the monitor not occluded by the
/// window system task bar where present. If no task bar exists then the work area is the
/// monitor resolution in screen coordinates.
const Workarea = struct {
x: u32,
y: u32,
width: u32,
height: u32,
};
/// Retrieves the work area of the monitor.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: monitor_workarea
pub inline fn getWorkarea(self: Monitor) error{PlatformError}!Workarea {
internal_debug.assertInitialized();
var xpos: c_int = 0;
var ypos: c_int = 0;
var width: c_int = 0;
var height: c_int = 0;
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) };
}
/// The physical size, in millimetres, of the display area of a monitor.
const PhysicalSize = struct {
width_mm: u32,
height_mm: u32,
};
/// Returns the physical size of the monitor.
///
/// Some platforms do not provide accurate monitor size information, either because the monitor
/// [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data)
/// 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
/// 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.
///
/// see also: monitor_properties
pub inline fn getPhysicalSize(self: Monitor) PhysicalSize {
internal_debug.assertInitialized();
var width_mm: c_int = 0;
var height_mm: c_int = 0;
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) };
}
/// The content scale for a monitor.
///
/// This is the ratio between the current DPI and the platform's default DPI. This is especially
/// important for text and any UI elements. If the pixel dimensions of your UI scaled by this look
/// appropriate on your machine then it should appear at a reasonable size on other machines
/// regardless of their DPI and scaling settings. This relies on the system DPI and scaling
/// settings being somewhat correct.
///
/// The content scale may depend on both the monitor resolution and pixel density and on users
/// settings. It may be very different from the raw DPI calculated from the physical size and
/// current resolution.
const ContentScale = struct {
x_scale: f32,
y_scale: f32,
};
/// Returns the content scale for the monitor.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: monitor_scale, glfw.Window.getContentScale
pub inline fn getContentScale(self: Monitor) error{PlatformError}!ContentScale {
internal_debug.assertInitialized();
var x_scale: f32 = 0;
var y_scale: f32 = 0;
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) };
}
/// Returns the name of the specified monitor.
///
/// This function returns a human-readable name, encoded as UTF-8, of the specified monitor. The
/// name typically reflects the make and model of the monitor and is not guaranteed to be unique
/// 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
/// yourself. It is valid until the specified monitor is disconnected or the library is terminated.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: monitor_properties
pub inline fn getName(self: Monitor) [*:0]const u8 {
internal_debug.assertInitialized();
if (c.glfwGetMonitorName(self.handle)) |name| return @ptrCast([*:0]const u8, name);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetMonitorName` returns `null` only for errors
unreachable;
}
/// Sets the user pointer of the specified monitor.
///
/// This function sets the user-defined pointer of the specified monitor. The current value is
/// retained until the monitor is disconnected.
///
/// This function may be called from the monitor callback, even for a monitor that is being
/// disconnected.
///
/// Possible errors include glfw.Error.NotInitialized.
///
/// @thread_safety This function may be called from any thread. Access is not synchronized.
///
/// see also: monitor_userptr, glfw.Monitor.getUserPointer
pub inline fn setUserPointer(self: Monitor, comptime T: type, ptr: *T) void {
internal_debug.assertInitialized();
c.glfwSetMonitorUserPointer(self.handle, ptr);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
}
/// Returns the user pointer of the specified monitor.
///
/// This function returns the current value of the user-defined pointer of the specified monitor.
///
/// This function may be called from the monitor callback, even for a monitor that is being
/// disconnected.
///
/// Possible errors include glfw.Error.NotInitialized.
///
/// @thread_safety This function may be called from any thread. Access is not synchronized.
///
/// see also: monitor_userptr, glfw.Monitor.setUserPointer
pub inline fn getUserPointer(self: Monitor, comptime T: type) ?*T {
internal_debug.assertInitialized();
const ptr = c.glfwGetMonitorUserPointer(self.handle);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
if (ptr == null) return null;
return @ptrCast(*T, @alignCast(@alignOf(T), ptr.?));
}
/// Returns the available video modes for the specified monitor.
///
/// This function returns an array of all video modes supported by the monitor. The returned slice
/// is sorted in ascending order, first by color bit depth (the sum of all channel depths) and
/// then by resolution area (the product of width and height), then resolution width and finally
/// by refresh rate.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError.
///
/// The returned slice memory is owned by the caller.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: monitor_modes, glfw.Monitor.getVideoMode
pub inline fn getVideoModes(self: Monitor, allocator: mem.Allocator) (mem.Allocator.Error || error{PlatformError})![]VideoMode {
internal_debug.assertInitialized();
var count: c_int = 0;
if (c.glfwGetVideoModes(self.handle, &count)) |modes| {
const slice = try allocator.alloc(VideoMode, @intCast(u32, count));
var i: u32 = 0;
while (i < count) : (i += 1) {
slice[i] = VideoMode{ .handle = @ptrCast([*c]const c.GLFWvidmode, modes)[i] };
}
return slice;
}
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
// `glfwGetVideoModes` returns `null` only for errors
unreachable;
}
/// Returns the current mode of the specified monitor.
///
/// This function returns the current video mode of the specified monitor. If you have created a
/// full screen window for that monitor, the return value will depend on whether that window is
/// iconified.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: monitor_modes, glfw.Monitor.getVideoModes
pub inline fn getVideoMode(self: Monitor) error{PlatformError}!VideoMode {
internal_debug.assertInitialized();
if (c.glfwGetVideoMode(self.handle)) |mode| return VideoMode{ .handle = mode.* };
getError() catch |err| return switch (err) {
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.
///
/// This function generates an appropriately sized gamma ramp from the specified exponent and then
/// calls glfw.Monitor.setGammaRamp with it. The value must be a finite number greater than zero.
///
/// The software controlled gamma ramp is applied _in addition_ to the hardware gamma correction,
/// which today is usually an approximation of sRGB gamma. This means that setting a perfectly
/// linear ramp, or gamma 1.0, will produce the default (usually sRGB-like) behavior.
///
/// 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.
///
/// wayland: Gamma handling is a privileged protocol, this function will thus never be implemented
/// and emits glfw.Error.PlatformError.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: monitor_gamma
pub inline fn setGamma(self: Monitor, gamma: f32) error{PlatformError}!void {
internal_debug.assertInitialized();
std.debug.assert(!std.math.isNan(gamma));
std.debug.assert(gamma >= 0);
std.debug.assert(gamma <= std.math.f32_max);
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.
///
/// This function returns the current gamma ramp of the specified monitor.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError.
///
/// wayland: Gamma handling is a privileged protocol, this function will thus never be implemented
/// and returns glfw.Error.PlatformError.
/// TODO: Is the documentation obsolete? On wayland the error returned is FeatureUnavailable
///
/// The returned gamma ramp is `.owned = true` by GLFW, and is valid until the monitor is
/// disconnected, this function is called again, or `glfw.terminate()` is called.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: monitor_gamma
pub inline fn getGammaRamp(self: Monitor) error{ PlatformError, FeatureUnavailable }!GammaRamp {
internal_debug.assertInitialized();
if (c.glfwGetGammaRamp(self.handle)) |ramp| return GammaRamp.fromC(ramp.*);
getError() catch |err| return switch (err) {
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.
///
/// This function sets the current gamma ramp for the specified monitor. The original gamma ramp
/// for that monitor is saved by GLFW the first time this function is called and is restored by
/// `glfw.terminate()`.
///
/// The software controlled gamma ramp is applied _in addition_ to the hardware gamma correction,
/// which today is usually an approximation of sRGB gamma. This means that setting a perfectly
/// linear ramp, or gamma 1.0, will produce the default (usually sRGB-like) behavior.
///
/// 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.
///
/// 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.
///
/// wayland: Gamma handling is a privileged protocol, this function will thus never be implemented
/// and emits glfw.Error.PlatformError.
///
/// @pointer_lifetime The specified gamma ramp is copied before this function returns.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: monitor_gamma
pub inline fn setGammaRamp(self: Monitor, ramp: GammaRamp) error{PlatformError}!void {
internal_debug.assertInitialized();
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.
///
/// This function returns a slice of all currently connected monitors. The primary monitor is
/// always first. If no monitors were found, this function returns an empty slice.
///
/// The returned slice memory is owned by the caller. The underlying handles are owned by GLFW, and
/// are valid until the monitor configuration changes or `glfw.terminate` is called.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: monitor_monitors, monitor_event, glfw.monitor.getPrimary
pub inline fn getAll(allocator: mem.Allocator) mem.Allocator.Error![]Monitor {
internal_debug.assertInitialized();
var count: c_int = 0;
if (c.glfwGetMonitors(&count)) |monitors| {
const slice = try allocator.alloc(Monitor, @intCast(u32, count));
var i: u32 = 0;
while (i < count) : (i += 1) {
slice[i] = Monitor{ .handle = @ptrCast([*c]const ?*c.GLFWmonitor, monitors)[i].? };
}
return slice;
}
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetMonitors` returning null can be either an error or no monitors
return &[_]Monitor{};
}
/// Returns the primary monitor.
///
/// This function returns the primary monitor. This is usually the monitor where elements like
/// 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.
///
/// see also: monitor_monitors, glfw.monitors.getAll
pub inline fn getPrimary() ?Monitor {
internal_debug.assertInitialized();
if (c.glfwGetPrimaryMonitor()) |handle| return Monitor{ .handle = handle };
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
return null;
}
/// Describes an event relating to a monitor.
pub const Event = enum(c_int) {
/// The device was connected.
connected = c.GLFW_CONNECTED,
/// The device was disconnected.
disconnected = c.GLFW_DISCONNECTED,
};
/// Sets the monitor configuration callback.
///
/// This function sets the monitor configuration callback, or removes the currently set callback.
/// This is called when a monitor is connected to or disconnected from the system. Example:
///
/// ```
/// fn monitorCallback(monitor: glfw.Monitor, event: glfw.Monitor.Event, data: *MyData) void {
/// // data is the pointer you passed into setCallback.
/// // event is one of .connected or .disconnected
/// }
/// ...
/// glfw.Monitor.setCallback(MyData, &myData, monitorCallback)
/// ```
///
/// `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.
///
/// see also: monitor_event
pub inline fn setCallback(comptime callback: ?fn (monitor: Monitor, event: Event) void) void {
internal_debug.assertInitialized();
if (callback) |user_callback| {
const CWrapper = struct {
pub fn monitorCallbackWrapper(monitor: ?*c.GLFWmonitor, event: c_int) callconv(.C) void {
@call(.{ .modifier = .always_inline }, user_callback, .{
Monitor{ .handle = monitor.? },
@intToEnum(Event, event),
});
}
};
if (c.glfwSetMonitorCallback(CWrapper.monitorCallbackWrapper) != null) return;
} else {
if (c.glfwSetMonitorCallback(null) != null) return;
}
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
}
test "getAll" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const allocator = testing.allocator;
const monitors = try getAll(allocator);
defer allocator.free(monitors);
}
test "getPrimary" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
_ = getPrimary();
}
test "getPos" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const monitor = getPrimary();
if (monitor) |m| {
_ = try m.getPos();
}
}
test "getWorkarea" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const monitor = getPrimary();
if (monitor) |m| {
_ = try m.getWorkarea();
}
}
test "getPhysicalSize" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const monitor = getPrimary();
if (monitor) |m| {
_ = m.getPhysicalSize();
}
}
test "getContentScale" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const monitor = getPrimary();
if (monitor) |m| {
_ = try m.getContentScale();
}
}
test "getName" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const monitor = getPrimary();
if (monitor) |m| {
_ = m.getName();
}
}
test "userPointer" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const monitor = getPrimary();
if (monitor) |m| {
var p = m.getUserPointer(u32);
try testing.expect(p == null);
var x: u32 = 5;
m.setUserPointer(u32, &x);
p = m.getUserPointer(u32);
try testing.expectEqual(p.?.*, 5);
}
}
test "setCallback" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
setCallback(struct {
fn callback(monitor: Monitor, event: Event) void {
_ = monitor;
_ = event;
}
}.callback);
}
test "getVideoModes" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const monitor = getPrimary();
if (monitor) |m| {
const allocator = testing.allocator;
const modes = try m.getVideoModes(allocator);
defer allocator.free(modes);
}
}
test "getVideoMode" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const monitor = getPrimary();
if (monitor) |m| {
_ = try m.getVideoMode();
}
}
test "set_getGammaRamp" {
const allocator = testing.allocator;
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const monitor = getPrimary();
if (monitor) |m| {
const ramp = m.getGammaRamp() catch |err| {
std.debug.print("can't get window position, wayland maybe? error={}\n", .{err});
return;
};
// Set it to the exact same value; if we do otherwise an our tests fail it wouldn't call
// terminate and our made-up gamma ramp would get stuck.
try m.setGammaRamp(ramp);
// technically not needed here / noop because GLFW owns this gamma ramp.
defer ramp.deinit(allocator);
}
}

View file

@ -0,0 +1,50 @@
//! Monitor video modes and related functions
//!
//! see also: glfw.Monitor.getVideoMode
const std = @import("std");
const c = @import("c.zig").c;
const VideoMode = @This();
handle: c.GLFWvidmode,
/// Returns the width of the video mode, in screen coordinates.
pub inline fn getWidth(self: VideoMode) u32 {
return @intCast(u32, self.handle.width);
}
/// Returns the height of the video mode, in screen coordinates.
pub inline fn getHeight(self: VideoMode) u32 {
return @intCast(u32, self.handle.height);
}
/// Returns the bit depth of the red channel of the video mode.
pub inline fn getRedBits(self: VideoMode) u32 {
return @intCast(u32, self.handle.redBits);
}
/// Returns the bit depth of the green channel of the video mode.
pub inline fn getGreenBits(self: VideoMode) u32 {
return @intCast(u32, self.handle.greenBits);
}
/// Returns the bit depth of the blue channel of the video mode.
pub inline fn getBlueBits(self: VideoMode) u32 {
return @intCast(u32, self.handle.blueBits);
}
/// Returns the refresh rate of the video mode, in Hz.
pub inline fn getRefreshRate(self: VideoMode) u32 {
return @intCast(u32, self.handle.refreshRate);
}
test "getters" {
const x = std.mem.zeroes(VideoMode);
_ = x.getWidth();
_ = x.getHeight();
_ = x.getRedBits();
_ = x.getGreenBits();
_ = x.getBlueBits();
_ = x.getRefreshRate();
}

3684
libs/glfw/src/Window.zig Normal file

File diff suppressed because it is too large Load diff

13
libs/glfw/src/action.zig Normal file
View file

@ -0,0 +1,13 @@
const c = @import("c.zig").c;
/// Key and button actions
pub const Action = enum(c_int) {
/// The key or mouse button was released.
release = c.GLFW_RELEASE,
/// The key or mouse button was pressed.
press = c.GLFW_PRESS,
/// The key was held down until it repeated.
repeat = c.GLFW_REPEAT,
};

143
libs/glfw/src/allocator.zig Normal file
View file

@ -0,0 +1,143 @@
// TODO: implement custom allocator support
// /*! @brief
// *
// * @sa @ref init_allocator
// * @sa @ref glfwInitAllocator
// *
// * @since Added in version 3.4.
// *
// * @ingroup init
// */
// typedef struct GLFWallocator
// {
// GLFWallocatefun allocate;
// GLFWreallocatefun reallocate;
// GLFWdeallocatefun deallocate;
// void* user;
// } GLFWallocator;
// /*! @brief The function pointer type for memory allocation callbacks.
// *
// * This is the function pointer type for memory allocation callbacks. A memory
// * allocation callback function has the following signature:
// * @code
// * void* function_name(size_t size, void* user)
// * @endcode
// *
// * This function must return either a memory block at least `size` bytes long,
// * or `NULL` if allocation failed. Note that not all parts of GLFW handle allocation
// * failures gracefully yet.
// *
// * This function may be called during @ref glfwInit but before the library is
// * flagged as initialized, as well as during @ref glfwTerminate after the
// * library is no longer flagged as initialized.
// *
// * Any memory allocated by this function will be deallocated during library
// * termination or earlier.
// *
// * The size will always be greater than zero. Allocations of size zero are filtered out
// * before reaching the custom allocator.
// *
// * @param[in] size The minimum size, in bytes, of the memory block.
// * @param[in] user The user-defined pointer from the allocator.
// * @return The address of the newly allocated memory block, or `NULL` if an
// * error occurred.
// *
// * @pointer_lifetime The returned memory block must be valid at least until it
// * is deallocated.
// *
// * @reentrancy This function should not call any GLFW function.
// *
// * @thread_safety This function may be called from any thread that calls GLFW functions.
// *
// * @sa @ref init_allocator
// * @sa @ref GLFWallocator
// *
// * @since Added in version 3.4.
// *
// * @ingroup init
// */
// typedef void* (* GLFWallocatefun)(size_t size, void* user);
// /*! @brief The function pointer type for memory reallocation callbacks.
// *
// * This is the function pointer type for memory reallocation callbacks.
// * A memory reallocation callback function has the following signature:
// * @code
// * void* function_name(void* block, size_t size, void* user)
// * @endcode
// *
// * This function must return a memory block at least `size` bytes long, or
// * `NULL` if allocation failed. Note that not all parts of GLFW handle allocation
// * failures gracefully yet.
// *
// * This function may be called during @ref glfwInit but before the library is
// * flagged as initialized, as well as during @ref glfwTerminate after the
// * library is no longer flagged as initialized.
// *
// * Any memory allocated by this function will be deallocated during library
// * termination or earlier.
// *
// * The block address will never be `NULL` and the size will always be greater than zero.
// * Reallocations of a block to size zero are converted into deallocations. Reallocations
// * of `NULL` to a non-zero size are converted into regular allocations.
// *
// * @param[in] block The address of the memory block to reallocate.
// * @param[in] size The new minimum size, in bytes, of the memory block.
// * @param[in] user The user-defined pointer from the allocator.
// * @return The address of the newly allocated or resized memory block, or
// * `NULL` if an error occurred.
// *
// * @pointer_lifetime The returned memory block must be valid at least until it
// * is deallocated.
// *
// * @reentrancy This function should not call any GLFW function.
// *
// * @thread_safety This function may be called from any thread that calls GLFW functions.
// *
// * @sa @ref init_allocator
// * @sa @ref GLFWallocator
// *
// * @since Added in version 3.4.
// *
// * @ingroup init
// */
// typedef void* (* GLFWreallocatefun)(void* block, size_t size, void* user);
// /*! @brief The function pointer type for memory deallocation callbacks.
// *
// * This is the function pointer type for memory deallocation callbacks.
// * A memory deallocation callback function has the following signature:
// * @code
// * void function_name(void* block, void* user)
// * @endcode
// *
// * This function may deallocate the specified memory block. This memory block
// * will have been allocated with the same allocator.
// *
// * This function may be called during @ref glfwInit but before the library is
// * flagged as initialized, as well as during @ref glfwTerminate after the
// * library is no longer flagged as initialized.
// *
// * The block address will never be `NULL`. Deallocations of `NULL` are filtered out
// * before reaching the custom allocator.
// *
// * @param[in] block The address of the memory block to deallocate.
// * @param[in] user The user-defined pointer from the allocator.
// *
// * @pointer_lifetime The specified memory block will not be accessed by GLFW
// * after this function is called.
// *
// * @reentrancy This function should not call any GLFW function.
// *
// * @thread_safety This function may be called from any thread that calls GLFW functions.
// *
// * @sa @ref init_allocator
// * @sa @ref GLFWallocator
// *
// * @since Added in version 3.4.
// *
// * @ingroup init
// */
// typedef void (* GLFWdeallocatefun)(void* block, void* user);

11
libs/glfw/src/c.zig Normal file
View file

@ -0,0 +1,11 @@
pub const c = if (@import("builtin").zig_backend == .stage1)
@cImport({
@cDefine("GLFW_INCLUDE_VULKAN", "1");
@cInclude("GLFW/glfw3.h");
})
else
// TODO(self-hosted): HACK: workaround https://github.com/ziglang/zig/issues/12483
//
// Extracted from a build using stage1 from zig-cache/ (`cimport.zig`)
// Then find+replace `= ?fn` -> `= ?*const fn`
@import("cimport2.zig");

65914
libs/glfw/src/cimport1.zig Normal file

File diff suppressed because it is too large Load diff

15045
libs/glfw/src/cimport2.zig Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,75 @@
const std = @import("std");
const c = @import("c.zig").c;
const Error = @import("errors.zig").Error;
const getError = @import("errors.zig").getError;
const internal_debug = @import("internal_debug.zig");
/// Sets the clipboard to the specified string.
///
/// This function sets the system clipboard to the specified, UTF-8 encoded string.
///
/// @param[in] string A UTF-8 encoded string.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError.
///
/// @pointer_lifetime The specified string is copied before this function returns.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: clipboard, glfwGetClipboardString
pub inline fn setClipboardString(value: [*:0]const u8) error{PlatformError}!void {
internal_debug.assertInitialized();
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.
///
/// This function returns the contents of the system clipboard, if it contains or is convertible to
/// a UTF-8 encoded string. If the clipboard is empty or if its contents cannot be converted,
/// glfw.Error.FormatUnavailable is returned.
///
/// @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.
///
/// @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
/// or until the library is terminated.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: clipboard, glfwSetClipboardString
pub inline fn getClipboardString() error{ FormatUnavailable, PlatformError }![:0]const u8 {
internal_debug.assertInitialized();
if (c.glfwGetClipboardString(null)) |c_str| return std.mem.span(@ptrCast([*:0]const u8, c_str));
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.FormatUnavailable, Error.PlatformError => |e| e,
else => unreachable,
};
// `glfwGetClipboardString` returns `null` only for errors
unreachable;
}
test "setClipboardString" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
try glfw.setClipboardString("hello mach");
}
test "getClipboardString" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
_ = glfw.getClipboardString() catch |err| std.debug.print("can't get clipboard, not supported by OS? error={}\n", .{err});
}

274
libs/glfw/src/errors.zig Normal file
View file

@ -0,0 +1,274 @@
//! Errors
const testing = @import("std").testing;
const mem = @import("std").mem;
const c = @import("c.zig").c;
/// Errors that GLFW can produce.
pub const Error = error{
/// GLFW has not been initialized.
///
/// This occurs if a GLFW function was called that must not be called unless the library is
/// initialized.
NotInitialized,
/// No context is current for this thread.
///
/// This occurs if a GLFW function was called that needs and operates on the current OpenGL or
/// OpenGL ES context but no context is current on the calling thread. One such function is
/// glfw.SwapInterval.
NoCurrentContext,
/// One of the arguments to the function was an invalid enum value.
///
/// One of the arguments to the function was an invalid enum value, for example requesting
/// glfw.red_bits with glfw.getWindowAttrib.
InvalidEnum,
/// One of the arguments to the function was an invalid value.
///
/// One of the arguments to the function was an invalid value, for example requesting a
/// non-existent OpenGL or OpenGL ES version like 2.7.
///
/// Requesting a valid but unavailable OpenGL or OpenGL ES version will instead result in a
/// glfw.Error.VersionUnavailable error.
InvalidValue,
/// A memory allocation failed.
OutOfMemory,
/// GLFW could not find support for the requested API on the system.
///
/// The installed graphics driver does not support the requested API, or does not support it
/// via the chosen context creation API. Below are a few examples.
///
/// Some pre-installed Windows graphics drivers do not support OpenGL. AMD only supports
/// OpenGL ES via EGL, while Nvidia and Intel only support it via a WGL or GLX extension. macOS
/// does not provide OpenGL ES at all. The Mesa EGL, OpenGL and OpenGL ES libraries do not
/// interface with the Nvidia binary driver. Older graphics drivers do not support Vulkan.
APIUnavailable,
/// The requested OpenGL or OpenGL ES version (including any requested context or framebuffer
/// hints) is not available on this machine.
///
/// The machine does not support your requirements. If your application is sufficiently
/// flexible, downgrade your requirements and try again. Otherwise, inform the user that their
/// machine does not match your requirements.
///
/// Future invalid OpenGL and OpenGL ES versions, for example OpenGL 4.8 if 5.0 comes out
/// before the 4.x series gets that far, also fail with this error and not glfw.Error.InvalidValue,
/// because GLFW cannot know what future versions will exist.
VersionUnavailable,
/// A platform-specific error occurred that does not match any of the more specific categories.
///
/// A bug or configuration error in GLFW, the underlying operating system or its drivers, or a
/// lack of required resources. Report the issue to our [issue tracker](https://github.com/glfw/glfw/issues).
PlatformError,
/// The requested format is not supported or available.
///
/// If emitted during window creation, the requested pixel format is not supported.
///
/// If emitted when querying the clipboard, the contents of the clipboard could not be
/// converted to the requested format.
///
/// If emitted during window creation, one or more hard constraints did not match any of the
/// available pixel formats. If your application is sufficiently flexible, downgrade your
/// requirements and try again. Otherwise, inform the user that their machine does not match
/// your requirements.
///
/// If emitted when querying the clipboard, ignore the error or report it to the user, as
/// appropriate.
FormatUnavailable,
/// The specified window does not have an OpenGL or OpenGL ES context.
///
/// A window that does not have an OpenGL or OpenGL ES context was passed to a function that
/// requires it to have one.
NoWindowContext,
/// The specified cursor shape is not available.
///
/// The specified standard cursor shape is not available, either because the
/// current platform cursor theme does not provide it or because it is not
/// available on the platform.
///
/// analysis: Platform or system settings limitation. Pick another standard cursor shape or
/// create a custom cursor.
CursorUnavailable,
/// The requested feature is not provided by the platform.
///
/// The requested feature is not provided by the platform, so GLFW is unable to
/// implement it. The documentation for each function notes if it could emit
/// this error.
///
/// analysis: Platform or platform version limitation. The error can be ignored
/// unless the feature is critical to the application.
///
/// A function call that emits this error has no effect other than the error and
/// updating any existing out parameters.
///
FeatureUnavailable,
/// The requested feature is not implemented for the platform.
///
/// The requested feature has not yet been implemented in GLFW for this platform.
///
/// analysis: An incomplete implementation of GLFW for this platform, hopefully
/// fixed in a future release. The error can be ignored unless the feature is
/// critical to the application.
///
/// A function call that emits this error has no effect other than the error and
/// updating any existing out parameters.
///
FeatureUnimplemented,
/// Platform unavailable or no matching platform was found.
///
/// If emitted during initialization, no matching platform was found. If glfw.InitHint.platform
/// is set to `.any_platform`, GLFW could not detect any of the platforms supported by this
/// library binary, except for the Null platform. If set to a specific platform, it is either
/// not supported by this library binary or GLFW was not able to detect it.
///
/// If emitted by a native access function, GLFW was initialized for a different platform
/// than the function is for.
///
/// analysis: Failure to detect any platform usually only happens on non-macOS Unix
/// systems, either when no window system is running or the program was run from
/// a terminal that does not have the necessary environment variables. Fall back to
/// a different platform if possible or notify the user that no usable platform was
/// detected.
///
/// Failure to detect a specific platform may have the same cause as above or be because
/// support for that platform was not compiled in. Call glfw.platformSupported to
/// check whether a specific platform is supported by a library binary.
///
PlatformUnavailable,
};
fn convertError(e: c_int) Error!void {
return switch (e) {
c.GLFW_NO_ERROR => {},
c.GLFW_NOT_INITIALIZED => Error.NotInitialized,
c.GLFW_NO_CURRENT_CONTEXT => Error.NoCurrentContext,
c.GLFW_INVALID_ENUM => Error.InvalidEnum,
c.GLFW_INVALID_VALUE => Error.InvalidValue,
c.GLFW_OUT_OF_MEMORY => Error.OutOfMemory,
c.GLFW_API_UNAVAILABLE => Error.APIUnavailable,
c.GLFW_VERSION_UNAVAILABLE => Error.VersionUnavailable,
c.GLFW_PLATFORM_ERROR => Error.PlatformError,
c.GLFW_FORMAT_UNAVAILABLE => Error.FormatUnavailable,
c.GLFW_NO_WINDOW_CONTEXT => Error.NoWindowContext,
c.GLFW_CURSOR_UNAVAILABLE => Error.CursorUnavailable,
c.GLFW_FEATURE_UNAVAILABLE => Error.FeatureUnavailable,
c.GLFW_FEATURE_UNIMPLEMENTED => Error.FeatureUnimplemented,
c.GLFW_PLATFORM_UNAVAILABLE => Error.PlatformUnavailable,
else => unreachable,
};
}
/// Returns and clears the last error for the calling thread.
///
/// This function returns and clears the error code of the last error that occurred on the calling
/// thread, and optionally a UTF-8 encoded human-readable description of it. If no error has
/// occurred since the last call, it returns GLFW_NO_ERROR (zero) and the description pointer is
/// set to `NULL`.
///
/// * @param[in] description Where to store the error description pointer, or `NULL`.
/// @return The last error code for the calling thread, or @ref GLFW_NO_ERROR (zero).
///
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
/// yourself. It is guaranteed to be valid only until the next error occurs or the library is
/// terminated.
///
/// @remark This function may be called before @ref glfwInit.
///
/// @thread_safety This function may be called from any thread.
pub inline fn getError() Error!void {
return convertError(c.glfwGetError(null));
}
/// 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
/// on the calling thread. If no error has occurred since the last call, it returns null.
///
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
/// yourself. It is guaranteed to be valid only until the next error occurs or the library is
/// terminated.
///
/// @remark This function may be called before @ref glfwInit.
///
/// @thread_safety This function may be called from any thread.
pub inline fn getErrorString() ?[]const u8 {
var desc: [*c]const u8 = null;
const error_code = c.glfwGetError(&desc);
convertError(error_code) catch {
return mem.sliceTo(desc, 0);
};
return null;
}
/// Sets the error callback.
///
/// This function sets the error callback, which is called with an error code
/// and a human-readable description each time a GLFW error occurs.
///
/// The error code is set before the callback is called. Calling @ref
/// glfwGetError from the error callback will return the same value as the error
/// code argument.
///
/// The error callback is called on the thread where the error occurred. If you
/// are using GLFW from multiple threads, your error callback needs to be
/// written accordingly.
///
/// Because the description string may have been generated specifically for that
/// error, it is not guaranteed to be valid after the callback has returned. If
/// you wish to use it after the callback returns, you need to make a copy.
///
/// Once set, the error callback remains set even after the library has been
/// terminated.
///
/// @param[in] callback The new callback, or `NULL` to remove the currently set
/// callback.
///
/// @callback_param `error_code` An error code. Future releases may add more error codes.
/// @callback_param `description` A UTF-8 encoded string describing the error.
///
/// @errors None.
///
/// @remark This function may be called before @ref glfwInit.
///
/// @thread_safety This function must only be called from the main thread.
pub fn setErrorCallback(comptime callback: ?fn (error_code: Error, description: [:0]const u8) void) void {
if (callback) |user_callback| {
const CWrapper = struct {
pub fn errorCallbackWrapper(err_int: c_int, c_description: [*c]const u8) callconv(.C) void {
if (convertError(err_int)) |_| {
// This means the error was `GLFW_NO_ERROR`
return;
} else |err| {
user_callback(err, mem.sliceTo(c_description, 0));
}
}
};
_ = c.glfwSetErrorCallback(CWrapper.errorCallbackWrapper);
return;
}
_ = c.glfwSetErrorCallback(null);
}
test "errorCallback" {
const TestStruct = struct {
pub fn callback(_: Error, _: [:0]const u8) void {}
};
setErrorCallback(TestStruct.callback);
}
test "error string" {
try testing.expect(getErrorString() == null);
}

View file

@ -0,0 +1,16 @@
const c = @import("c.zig").c;
/// Gamepad axes.
///
/// See glfw.getGamepadState for how these are used.
pub const GamepadAxis = enum(c_int) {
left_x = c.GLFW_GAMEPAD_AXIS_LEFT_X,
left_y = c.GLFW_GAMEPAD_AXIS_LEFT_Y,
right_x = c.GLFW_GAMEPAD_AXIS_RIGHT_X,
right_y = c.GLFW_GAMEPAD_AXIS_RIGHT_Y,
left_trigger = c.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER,
right_trigger = c.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER,
};
/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden.
pub const last = GamepadAxis.right_trigger;

View file

@ -0,0 +1,37 @@
const c = @import("c.zig").c;
/// Gamepad buttons.
///
/// See glfw.getGamepadState for how these are used.
pub const GamepadButton = enum(c_int) {
a = c.GLFW_GAMEPAD_BUTTON_A,
b = c.GLFW_GAMEPAD_BUTTON_B,
x = c.GLFW_GAMEPAD_BUTTON_X,
y = c.GLFW_GAMEPAD_BUTTON_Y,
left_bumper = c.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER,
right_bumper = c.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER,
back = c.GLFW_GAMEPAD_BUTTON_BACK,
start = c.GLFW_GAMEPAD_BUTTON_START,
guide = c.GLFW_GAMEPAD_BUTTON_GUIDE,
left_thumb = c.GLFW_GAMEPAD_BUTTON_LEFT_THUMB,
right_thumb = c.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB,
dpad_up = c.GLFW_GAMEPAD_BUTTON_DPAD_UP,
dpad_right = c.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT,
dpad_down = c.GLFW_GAMEPAD_BUTTON_DPAD_DOWN,
dpad_left = c.GLFW_GAMEPAD_BUTTON_DPAD_LEFT,
};
/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden.
pub const last = GamepadButton.dpad_left;
/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden.
pub const cross = GamepadButton.a;
/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden.
pub const circle = GamepadButton.b;
/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden.
pub const square = GamepadButton.x;
/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden.
pub const triangle = GamepadButton.y;

100
libs/glfw/src/hat.zig Normal file
View file

@ -0,0 +1,100 @@
const c = @import("c.zig").c;
// must be in sync with GLFW C constants in hat state group, search for "@defgroup hat_state Joystick hat states"
/// A bitmask of all Joystick hat states
///
/// See glfw.Joystick.getHats for how these are used.
pub const Hat = packed struct {
up: bool = false,
right: bool = false,
down: bool = false,
left: bool = false,
_reserved: u4 = 0,
pub inline fn centered(self: Hat) bool {
return self.up == false and self.right == false and self.down == false and self.left == false;
}
inline fn verifyIntType(comptime IntType: type) void {
comptime {
switch (@typeInfo(IntType)) {
.Int => {},
else => @compileError("Int was not of int type"),
}
}
}
pub inline fn toInt(self: Hat, comptime IntType: type) IntType {
verifyIntType(IntType);
return @intCast(IntType, @bitCast(u8, self));
}
pub inline fn fromInt(flags: anytype) Hat {
verifyIntType(@TypeOf(flags));
return @bitCast(Hat, @intCast(u8, flags));
}
};
/// Holds all GLFW hat values in their raw form.
pub const RawHat = struct {
pub const centered = c.GLFW_HAT_CENTERED;
pub const up = c.GLFW_HAT_UP;
pub const right = c.GLFW_HAT_RIGHT;
pub const down = c.GLFW_HAT_DOWN;
pub const left = c.GLFW_HAT_LEFT;
pub const right_up = right | up;
pub const right_down = right | down;
pub const left_up = left | up;
pub const left_down = left | down;
};
test "from int, single" {
const std = @import("std");
try std.testing.expectEqual(Hat{
.up = true,
.right = false,
.down = false,
.left = false,
._reserved = 0,
}, Hat.fromInt(RawHat.up));
}
test "from int, multi" {
const std = @import("std");
try std.testing.expectEqual(Hat{
.up = true,
.right = false,
.down = true,
.left = true,
._reserved = 0,
}, Hat.fromInt(RawHat.up | RawHat.down | RawHat.left));
}
test "to int, single" {
const std = @import("std");
var v = Hat{
.up = true,
.right = false,
.down = false,
.left = false,
._reserved = 0,
};
try std.testing.expectEqual(v.toInt(c_int), RawHat.up);
}
test "to int, multi" {
const std = @import("std");
var v = Hat{
.up = true,
.right = false,
.down = true,
.left = true,
._reserved = 0,
};
try std.testing.expectEqual(v.toInt(c_int), RawHat.up | RawHat.down | RawHat.left);
}

View file

@ -0,0 +1,14 @@
const std = @import("std");
const zig_builtin = @import("builtin");
const debug_mode = (zig_builtin.mode == .Debug);
var glfw_initialized = if (debug_mode) false else @as(void, {});
pub inline fn toggleInitialized() void {
if (debug_mode) glfw_initialized = !glfw_initialized;
}
pub inline fn assertInitialized() void {
if (debug_mode) std.debug.assert(glfw_initialized);
}
pub inline fn assumeInitialized() void {
glfw_initialized = true;
}

273
libs/glfw/src/key.zig Normal file
View file

@ -0,0 +1,273 @@
//! Keyboard key IDs.
//!
//! See glfw.setKeyCallback for how these are used.
//!
//! These key codes are inspired by the _USB HID Usage Tables v1.12_ (p. 53-60), but re-arranged to
//! map to 7-bit ASCII for printable keys (function keys are put in the 256+ range).
//!
//! The naming of the key codes follow these rules:
//!
//! - The US keyboard layout is used
//! - Names of printable alphanumeric characters are used (e.g. "a", "r", "three", etc.)
//! - For non-alphanumeric characters, Unicode:ish names are used (e.g. "comma", "left_bracket",
//! etc.). Note that some names do not correspond to the Unicode standard (usually for brevity)
//! - Keys that lack a clear US mapping are named "world_x"
//! - For non-printable keys, custom names are used (e.g. "F4", "backspace", etc.)
const std = @import("std");
const cc = @import("c.zig").c;
const Error = @import("errors.zig").Error;
const getError = @import("errors.zig").getError;
const internal_debug = @import("internal_debug.zig");
/// enum containing all glfw keys
pub const Key = enum(c_int) {
/// The unknown key
unknown = cc.GLFW_KEY_UNKNOWN,
/// Printable keys
space = cc.GLFW_KEY_SPACE,
apostrophe = cc.GLFW_KEY_APOSTROPHE,
comma = cc.GLFW_KEY_COMMA,
minus = cc.GLFW_KEY_MINUS,
period = cc.GLFW_KEY_PERIOD,
slash = cc.GLFW_KEY_SLASH,
zero = cc.GLFW_KEY_0,
one = cc.GLFW_KEY_1,
two = cc.GLFW_KEY_2,
three = cc.GLFW_KEY_3,
four = cc.GLFW_KEY_4,
five = cc.GLFW_KEY_5,
six = cc.GLFW_KEY_6,
seven = cc.GLFW_KEY_7,
eight = cc.GLFW_KEY_8,
nine = cc.GLFW_KEY_9,
semicolon = cc.GLFW_KEY_SEMICOLON,
equal = cc.GLFW_KEY_EQUAL,
a = cc.GLFW_KEY_A,
b = cc.GLFW_KEY_B,
c = cc.GLFW_KEY_C,
d = cc.GLFW_KEY_D,
e = cc.GLFW_KEY_E,
f = cc.GLFW_KEY_F,
g = cc.GLFW_KEY_G,
h = cc.GLFW_KEY_H,
i = cc.GLFW_KEY_I,
j = cc.GLFW_KEY_J,
k = cc.GLFW_KEY_K,
l = cc.GLFW_KEY_L,
m = cc.GLFW_KEY_M,
n = cc.GLFW_KEY_N,
o = cc.GLFW_KEY_O,
p = cc.GLFW_KEY_P,
q = cc.GLFW_KEY_Q,
r = cc.GLFW_KEY_R,
s = cc.GLFW_KEY_S,
t = cc.GLFW_KEY_T,
u = cc.GLFW_KEY_U,
v = cc.GLFW_KEY_V,
w = cc.GLFW_KEY_W,
x = cc.GLFW_KEY_X,
y = cc.GLFW_KEY_Y,
z = cc.GLFW_KEY_Z,
left_bracket = cc.GLFW_KEY_LEFT_BRACKET,
backslash = cc.GLFW_KEY_BACKSLASH,
right_bracket = cc.GLFW_KEY_RIGHT_BRACKET,
grave_accent = cc.GLFW_KEY_GRAVE_ACCENT,
world_1 = cc.GLFW_KEY_WORLD_1, // non-US #1
world_2 = cc.GLFW_KEY_WORLD_2, // non-US #2
// Function keys
escape = cc.GLFW_KEY_ESCAPE,
enter = cc.GLFW_KEY_ENTER,
tab = cc.GLFW_KEY_TAB,
backspace = cc.GLFW_KEY_BACKSPACE,
insert = cc.GLFW_KEY_INSERT,
delete = cc.GLFW_KEY_DELETE,
right = cc.GLFW_KEY_RIGHT,
left = cc.GLFW_KEY_LEFT,
down = cc.GLFW_KEY_DOWN,
up = cc.GLFW_KEY_UP,
page_up = cc.GLFW_KEY_PAGE_UP,
page_down = cc.GLFW_KEY_PAGE_DOWN,
home = cc.GLFW_KEY_HOME,
end = cc.GLFW_KEY_END,
caps_lock = cc.GLFW_KEY_CAPS_LOCK,
scroll_lock = cc.GLFW_KEY_SCROLL_LOCK,
num_lock = cc.GLFW_KEY_NUM_LOCK,
print_screen = cc.GLFW_KEY_PRINT_SCREEN,
pause = cc.GLFW_KEY_PAUSE,
F1 = cc.GLFW_KEY_F1,
F2 = cc.GLFW_KEY_F2,
F3 = cc.GLFW_KEY_F3,
F4 = cc.GLFW_KEY_F4,
F5 = cc.GLFW_KEY_F5,
F6 = cc.GLFW_KEY_F6,
F7 = cc.GLFW_KEY_F7,
F8 = cc.GLFW_KEY_F8,
F9 = cc.GLFW_KEY_F9,
F10 = cc.GLFW_KEY_F10,
F11 = cc.GLFW_KEY_F11,
F12 = cc.GLFW_KEY_F12,
F13 = cc.GLFW_KEY_F13,
F14 = cc.GLFW_KEY_F14,
F15 = cc.GLFW_KEY_F15,
F16 = cc.GLFW_KEY_F16,
F17 = cc.GLFW_KEY_F17,
F18 = cc.GLFW_KEY_F18,
F19 = cc.GLFW_KEY_F19,
F20 = cc.GLFW_KEY_F20,
F21 = cc.GLFW_KEY_F21,
F22 = cc.GLFW_KEY_F22,
F23 = cc.GLFW_KEY_F23,
F24 = cc.GLFW_KEY_F24,
F25 = cc.GLFW_KEY_F25,
kp_0 = cc.GLFW_KEY_KP_0,
kp_1 = cc.GLFW_KEY_KP_1,
kp_2 = cc.GLFW_KEY_KP_2,
kp_3 = cc.GLFW_KEY_KP_3,
kp_4 = cc.GLFW_KEY_KP_4,
kp_5 = cc.GLFW_KEY_KP_5,
kp_6 = cc.GLFW_KEY_KP_6,
kp_7 = cc.GLFW_KEY_KP_7,
kp_8 = cc.GLFW_KEY_KP_8,
kp_9 = cc.GLFW_KEY_KP_9,
kp_decimal = cc.GLFW_KEY_KP_DECIMAL,
kp_divide = cc.GLFW_KEY_KP_DIVIDE,
kp_multiply = cc.GLFW_KEY_KP_MULTIPLY,
kp_subtract = cc.GLFW_KEY_KP_SUBTRACT,
kp_add = cc.GLFW_KEY_KP_ADD,
kp_enter = cc.GLFW_KEY_KP_ENTER,
kp_equal = cc.GLFW_KEY_KP_EQUAL,
left_shift = cc.GLFW_KEY_LEFT_SHIFT,
left_control = cc.GLFW_KEY_LEFT_CONTROL,
left_alt = cc.GLFW_KEY_LEFT_ALT,
left_super = cc.GLFW_KEY_LEFT_SUPER,
right_shift = cc.GLFW_KEY_RIGHT_SHIFT,
right_control = cc.GLFW_KEY_RIGHT_CONTROL,
right_alt = cc.GLFW_KEY_RIGHT_ALT,
right_super = cc.GLFW_KEY_RIGHT_SUPER,
menu = cc.GLFW_KEY_MENU,
pub inline fn last() Key {
return @intToEnum(Key, cc.GLFW_KEY_LAST);
}
/// Returns the layout-specific name of the specified printable key.
///
/// This function returns the name of the specified printable key, encoded as UTF-8. This is
/// typically the character that key would produce without any modifier keys, intended for
/// displaying key bindings to the user. For dead keys, it is typically the diacritic it would add
/// to a character.
///
/// __Do not use this function__ for text input (see input_char). You will break text input for many
/// languages even if it happens to work for yours.
///
/// If the key is `glfw.key.unknown`, the scancode is used to identify the key, otherwise the
/// scancode is ignored. If you specify a non-printable key, or `glfw.key.unknown` and a scancode
/// that maps to a non-printable key, this function returns null but does not emit an error.
///
/// This behavior allows you to always pass in the arguments in the key callback (see input_key)
/// without modification.
///
/// The printable keys are:
///
/// - `glfw.Key.apostrophe`
/// - `glfw.Key.comma`
/// - `glfw.Key.minus`
/// - `glfw.Key.period`
/// - `glfw.Key.slash`
/// - `glfw.Key.semicolon`
/// - `glfw.Key.equal`
/// - `glfw.Key.left_bracket`
/// - `glfw.Key.right_bracket`
/// - `glfw.Key.backslash`
/// - `glfw.Key.world_1`
/// - `glfw.Key.world_2`
/// - `glfw.Key.0` to `glfw.key.9`
/// - `glfw.Key.a` to `glfw.key.z`
/// - `glfw.Key.kp_0` to `glfw.key.kp_9`
/// - `glfw.Key.kp_decimal`
/// - `glfw.Key.kp_divide`
/// - `glfw.Key.kp_multiply`
/// - `glfw.Key.kp_subtract`
/// - `glfw.Key.kp_add`
/// - `glfw.Key.kp_equal`
///
/// Names for printable keys depend on keyboard layout, while names for non-printable keys are the
/// same across layouts but depend on the application language and should be localized along with
/// other user interface text.
///
/// @param[in] key The key to query, or `glfw.key.unknown`.
/// @param[in] scancode The scancode of the key to query.
/// @return The UTF-8 encoded, layout-specific name of the key, or null.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError.
///
/// The contents of the returned string may change when a keyboard layout change event is received.
///
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
/// yourself. It is valid until the library is terminated.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: input_key_name
pub inline fn getName(self: Key, scancode: i32) error{PlatformError}!?[:0]const u8 {
internal_debug.assertInitialized();
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|
std.mem.span(@ptrCast([*:0]const u8, name))
else
null;
}
/// Returns the platform-specific scancode of the specified key.
///
/// This function returns the platform-specific scancode of the specified key.
///
/// If the key is `glfw.key.UNKNOWN` or does not exist on the keyboard this method will return `-1`.
///
/// @param[in] key Any named key (see keys).
/// @return The platform-specific scancode for the key.
///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.InvalidEnum and glfw.Error.PlatformError.
///
/// @thread_safety This function may be called from any thread.
pub inline fn getScancode(self: Key) error{PlatformError}!i32 {
internal_debug.assertInitialized();
const scancode = 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;
}
};
// TODO: https://github.com/hexops/mach/issues/375
// test "getName" {
// const glfw = @import("main.zig");
// try glfw.init(.{});
// defer glfw.terminate();
// _ = glfw.Key.a.getName(0) catch |err| std.debug.print("failed to get key name, not supported? error={}\n", .{err});
// }
test "getScancode" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
_ = glfw.Key.a.getScancode() catch |err| std.debug.print("failed to get key scancode, not supported? error={}\n", .{err});
}

580
libs/glfw/src/main.zig Normal file
View file

@ -0,0 +1,580 @@
const std = @import("std");
const testing = std.testing;
const c = @import("c.zig").c;
const key = @import("key.zig");
/// Possible value for various window hints, etc.
pub const dont_care = c.GLFW_DONT_CARE;
const errors = @import("errors.zig");
const getError = errors.getError;
pub const setErrorCallback = errors.setErrorCallback;
pub const Error = errors.Error;
pub const Action = @import("action.zig").Action;
pub const GamepadAxis = @import("gamepad_axis.zig").GamepadAxis;
pub const GamepadButton = @import("gamepad_button.zig").GamepadButton;
pub const gamepad_axis = @import("gamepad_axis.zig");
pub const gamepad_button = @import("gamepad_button.zig");
pub const GammaRamp = @import("GammaRamp.zig");
pub const Image = @import("Image.zig");
pub const Joystick = @import("Joystick.zig");
pub const Monitor = @import("Monitor.zig");
pub const mouse_button = @import("mouse_button.zig");
pub const MouseButton = mouse_button.MouseButton;
pub const version = @import("version.zig");
pub const VideoMode = @import("VideoMode.zig");
pub const Window = @import("Window.zig");
pub const Cursor = @import("Cursor.zig");
pub const Native = @import("native.zig").Native;
pub const BackendOptions = @import("native.zig").BackendOptions;
pub const Key = key.Key;
pub usingnamespace @import("clipboard.zig");
pub usingnamespace @import("opengl.zig");
pub usingnamespace @import("vulkan.zig");
pub usingnamespace @import("time.zig");
pub usingnamespace @import("hat.zig");
pub usingnamespace @import("mod.zig");
const internal_debug = @import("internal_debug.zig");
/// If GLFW was already initialized in your program, e.g. you are embedding Zig code into an existing
/// program that has already called glfwInit via the C API for you - then you need to tell mach/glfw
/// that it has in fact been initialized already, otherwise when you call other methods mach/glfw
/// would panic thinking glfw.init has not been called yet.
pub fn assumeInitialized() void {
internal_debug.assumeInitialized();
}
/// Initializes the GLFW library.
///
/// This function initializes the GLFW library. Before most GLFW functions can be used, GLFW must
/// be initialized, and before an application terminates GLFW should be terminated in order to free
/// any resources allocated during or after initialization.
///
/// If this function fails, it calls glfw.Terminate before returning. If it succeeds, you should
/// call glfw.Terminate before the application exits.
///
/// Additional calls to this function after successful initialization but before termination will
/// return immediately with no error.
///
/// The glfw.InitHint.platform init hint controls which platforms are considered during
/// initialization. This also depends on which platforms the library was compiled to support.
///
/// macos: This function will change the current directory of the application to the
/// `Contents/Resources` subdirectory of the application's bundle, if present. This can be disabled
/// with `glfw.InitHint.cocoa_chdir_resources`.
///
/// macos: This function will create the main menu and dock icon for the application. If GLFW finds
/// a `MainMenu.nib` it is loaded and assumed to contain a menu bar. Otherwise a minimal menu bar is
/// created manually with common commands like Hide, Quit and About. The About entry opens a minimal
/// about dialog with information from the application's bundle. The menu bar and dock icon can be
/// disabled entirely with `glfw.InitHint.cocoa_menubar`.
///
/// x11: This function will set the `LC_CTYPE` category of the application locale according to the
/// current environment if that category is still "C". This is because the "C" locale breaks
/// Unicode text input.
///
/// @thread_safety This function must only be called from the main thread.
pub inline fn init(hints: InitHints) error{ PlatformUnavailable, PlatformError }!void {
internal_debug.toggleInitialized();
internal_debug.assertInitialized();
errdefer {
internal_debug.assertInitialized();
internal_debug.toggleInitialized();
}
inline for (comptime std.meta.fieldNames(InitHints)) |field_name| {
const init_hint = @field(InitHint, field_name);
const init_value = @field(hints, field_name);
if (@TypeOf(init_value) == PlatformType) {
initHint(init_hint, @enumToInt(init_value));
} else {
initHint(init_hint, init_value);
}
}
if (c.glfwInit() == c.GLFW_TRUE) return;
getError() catch |err| return switch (err) {
Error.PlatformUnavailable => |e| e,
Error.PlatformError => |e| e,
else => unreachable,
};
}
// TODO: implement custom allocator support
//
// /*! @brief Sets the init allocator to the desired value.
// *
// * To use the default allocator, call this function with a `NULL` argument.
// *
// * If you specify an allocator struct, every member must be a valid function
// * pointer. If any member is `NULL`, this function emits @ref
// * GLFW_INVALID_VALUE and the init allocator is unchanged.
// *
// * @param[in] allocator The allocator to use at the next initialization, or
// * `NULL` to use the default one.
// *
// * @errors Possible errors include @ref GLFW_INVALID_VALUE.
// *
// * @pointer_lifetime The specified allocator is copied before this function
// * returns.
// *
// * @thread_safety This function must only be called from the main thread.
// *
// * @sa @ref init_allocator
// * @sa @ref glfwInit
// *
// * @since Added in version 3.4.
// *
// * @ingroup init
// */
// GLFWAPI void glfwInitAllocator(const GLFWallocator* allocator);
/// Terminates the GLFW library.
///
/// This function destroys all remaining windows and cursors, restores any modified gamma ramps
/// and frees any other allocated resources. Once this function is called, you must again call
/// glfw.init successfully before you will be able to use most GLFW functions.
///
/// If GLFW has been successfully initialized, this function should be called before the
/// application exits. If initialization fails, there is no need to call this function, as it is
/// called by glfw.init before it returns failure.
///
/// This function has no effect if GLFW is not initialized.
///
/// Possible errors include glfw.Error.PlatformError.
///
/// warning: The contexts of any remaining windows must not be current on any other thread when
/// this function is called.
///
/// reentrancy: This function must not be called from a callback.
///
/// thread_safety: This function must only be called from the main thread.
pub inline fn terminate() void {
internal_debug.assertInitialized();
internal_debug.toggleInitialized();
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
pub const InitHints = struct {
/// Specifies whether to also expose joystick hats as buttons, for compatibility with earlier
/// versions of GLFW that did not have glfwGetJoystickHats.
joystick_hat_buttons: bool = true,
/// macOS specific init hint. Ignored on other platforms.
///
/// Specifies whether to set the current directory to the application to the Contents/Resources
/// subdirectory of the application's bundle, if present.
cocoa_chdir_resources: bool = true,
/// macOS specific init hint. Ignored on other platforms.
///
/// specifies whether to create a basic menu bar, either from a nib or manually, when the first
/// window is created, which is when AppKit is initialized.
cocoa_menubar: bool = true,
/// Platform selection init hint.
///
/// Possible values are `PlatformType` enums.
platform: PlatformType = .any,
};
/// Initialization hints for passing into glfw.initHint
const InitHint = enum(c_int) {
/// Specifies whether to also expose joystick hats as buttons, for compatibility with earlier
/// versions of GLFW that did not have glfwGetJoystickHats.
///
/// Possible values are `true` and `false`.
joystick_hat_buttons = c.GLFW_JOYSTICK_HAT_BUTTONS,
/// ANGLE rendering backend init hint.
///
/// Possible values are `AnglePlatformType` enums.
angle_platform_type = c.GLFW_ANGLE_PLATFORM_TYPE,
/// Platform selection init hint.
///
/// Possible values are `PlatformType` enums.
platform = c.GLFW_PLATFORM,
/// macOS specific init hint. Ignored on other platforms.
///
/// Specifies whether to set the current directory to the application to the Contents/Resources
/// subdirectory of the application's bundle, if present.
///
/// Possible values are `true` and `false`.
cocoa_chdir_resources = c.GLFW_COCOA_CHDIR_RESOURCES,
/// macOS specific init hint. Ignored on other platforms.
///
/// specifies whether to create a basic menu bar, either from a nib or manually, when the first
/// window is created, which is when AppKit is initialized.
///
/// Possible values are `true` and `false`.
cocoa_menubar = c.GLFW_COCOA_MENUBAR,
/// X11 specific init hint.
x11_xcb_vulkan_surface = c.GLFW_X11_XCB_VULKAN_SURFACE,
};
/// Angle platform type hints for glfw.InitHint.angle_platform_type
pub const AnglePlatformType = enum(c_int) {
none = c.GLFW_ANGLE_PLATFORM_TYPE_NONE,
opengl = c.GLFW_ANGLE_PLATFORM_TYPE_OPENGL,
opengles = c.GLFW_ANGLE_PLATFORM_TYPE_OPENGLES,
d3d9 = c.GLFW_ANGLE_PLATFORM_TYPE_D3D9,
d3d11 = c.GLFW_ANGLE_PLATFORM_TYPE_D3D11,
vulkan = c.GLFW_ANGLE_PLATFORM_TYPE_VULKAN,
metal = c.GLFW_ANGLE_PLATFORM_TYPE_METAL,
};
/// Platform type hints for glfw.InitHint.platform
pub const PlatformType = enum(c_int) {
/// Enables automatic platform detection.
/// Will default to X11 on wayland.
any = c.GLFW_ANY_PLATFORM,
win32 = c.GLFW_PLATFORM_WIN32,
cocoa = c.GLFW_PLATFORM_COCOA,
wayland = c.GLFW_PLATFORM_WAYLAND,
x11 = c.GLFW_PLATFORM_X11,
nul = c.GLFW_PLATFORM_NULL,
};
/// Sets the specified init hint to the desired value.
///
/// This function sets hints for the next initialization of GLFW.
///
/// The values you set hints to are never reset by GLFW, but they only take effect during
/// initialization. Once GLFW has been initialized, any values you set will be ignored until the
/// library is terminated and initialized again.
///
/// Some hints are platform specific. These may be set on any platform but they will only affect
/// their specific platform. Other platforms will ignore them. Setting these hints requires no
/// platform specific headers or functions.
///
/// @param hint: The init hint to set.
/// @param value: The new value of the init hint.
///
/// Possible errors include glfw.Error.InvalidEnum and glfw.Error.InvalidValue.
///
/// @remarks This function may be called before glfw.init.
///
/// @thread_safety This function must only be called from the main thread.
fn initHint(hint: InitHint, value: anytype) void {
switch (@typeInfo(@TypeOf(value))) {
.Int, .ComptimeInt => {
c.glfwInitHint(@enumToInt(hint), @intCast(c_int, value));
},
.Bool => c.glfwInitHint(@enumToInt(hint), @intCast(c_int, @boolToInt(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.
///
/// This function returns the compile-time generated version string of the GLFW library binary. It
/// describes the version, platform, compiler and any platform or operating system specific
/// compile-time options. It should not be confused with the OpenGL or OpenGL ES version string,
/// queried with `glGetString`.
///
/// __Do not use the version string__ to parse the GLFW library version. Use the glfw.version
/// constants instead.
///
/// __Do not use the version string__ to parse what platforms are supported. The
/// `glfw.platformSupported` function lets you query platform support.
///
/// returns: The ASCII encoded GLFW version string.
///
/// remark: This function may be called before @ref glfw.Init.
///
/// pointer_lifetime: The returned string is static and compile-time generated.
///
/// thread_safety: This function may be called from any thread.
pub inline fn getVersionString() [:0]const u8 {
return std.mem.span(@ptrCast([*:0]const u8, c.glfwGetVersionString()));
}
/// Returns the currently selected platform.
///
/// This function returns the platform that was selected during initialization. The returned value
/// will be one of `glfw.PlatformType.win32`, `glfw.PlatformType.cocoa`,
/// `glfw.PlatformType.wayland`, `glfw.PlatformType.x11` or `glfw.PlatformType.nul`.
///
/// thread_safety: This function may be called from any thread.
pub fn getPlatform() PlatformType {
internal_debug.assertInitialized();
const platform = @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.
///
/// This function returns whether the library was compiled with support for the specified platform.
/// The platform must be one of `glfw.PlatformType.win32`, `glfw.PlatformType.cocoa`,
/// `glfw.PlatformType.wayland`, `glfw.PlatformType.x11` or `glfw.PlatformType.nul`.
///
/// remark: This function may be called before glfw.Init.
///
/// thread_safety: This function may be called from any thread.
pub fn platformSupported(platform: PlatformType) bool {
internal_debug.assertInitialized();
const is_supported = c.glfwPlatformSupported(@enumToInt(platform));
getError() catch |err| return switch (err) {
Error.InvalidEnum => unreachable,
else => unreachable,
};
return is_supported == c.GLFW_TRUE;
}
/// Processes all pending events.
///
/// This function processes only those events that are already in the event queue and then returns
/// immediately. Processing events will cause the window and input callbacks associated with those
/// events to be called.
///
/// On some platforms, a window move, resize or menu operation will cause event processing to
/// block. This is due to how event processing is designed on those platforms. You can use the
/// window refresh callback (see window_refresh) to redraw the contents of your window when
/// necessary during such operations.
///
/// Do not assume that callbacks you set will _only_ be called in response to event processing
/// functions like this one. While it is necessary to poll for events, window systems that require
/// GLFW to register callbacks of its own can pass events to GLFW in response to many window system
/// function calls. GLFW will pass those events on to the application callbacks before returning.
///
/// Event processing is not required for joystick input to work.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError.
///
/// @reentrancy This function must not be called from a callback.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: events, glfw.waitEvents, glfw.waitEventsTimeout
pub inline fn pollEvents() error{PlatformError}!void {
internal_debug.assertInitialized();
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.
///
/// This function puts the calling thread to sleep until at least one event is available in the
/// event queue. Once one or more events are available, it behaves exactly like glfw.pollEvents,
/// i.e. the events in the queue are processed and the function then returns immediately.
/// Processing events will cause the window and input callbacks associated with those events to be
/// called.
///
/// Since not all events are associated with callbacks, this function may return without a callback
/// having been called even if you are monitoring all callbacks.
///
/// On some platforms, a window move, resize or menu operation will cause event processing to
/// block. This is due to how event processing is designed on those platforms. You can use the
/// window refresh callback (see window_refresh) to redraw the contents of your window when
/// necessary during such operations.
///
/// Do not assume that callbacks you set will _only_ be called in response to event processing
/// functions like this one. While it is necessary to poll for events, window systems that require
/// GLFW to register callbacks of its own can pass events to GLFW in response to many window system
/// function calls. GLFW will pass those events on to the application callbacks before returning.
///
/// Event processing is not required for joystick input to work.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError.
///
/// @reentrancy This function must not be called from a callback.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: events, glfw.pollEvents, glfw.waitEventsTimeout
pub inline fn waitEvents() error{PlatformError}!void {
internal_debug.assertInitialized();
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.
///
/// This function puts the calling thread to sleep until at least one event is available in the
/// event queue, or until the specified timeout is reached. If one or more events are available, it
/// behaves exactly like glfw.pollEvents, i.e. the events in the queue are processed and the
/// function then returns immediately. Processing events will cause the window and input callbacks
/// associated with those events to be called.
///
/// The timeout value must be a positive finite number.
///
/// Since not all events are associated with callbacks, this function may return without a callback
/// having been called even if you are monitoring all callbacks.
///
/// On some platforms, a window move, resize or menu operation will cause event processing to
/// block. This is due to how event processing is designed on those platforms. You can use the
/// window refresh callback (see window_refresh) to redraw the contents of your window when
/// necessary during such operations.
///
/// Do not assume that callbacks you set will _only_ be called in response to event processing
/// functions like this one. While it is necessary to poll for events, window systems that require
/// GLFW to register callbacks of its own can pass events to GLFW in response to many window system
/// function calls. GLFW will pass those events on to the application callbacks before returning.
///
/// Event processing is not required for joystick input to work.
///
/// @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.
///
/// @reentrancy This function must not be called from a callback.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: events, glfw.pollEvents, glfw.waitEvents
pub inline fn waitEventsTimeout(timeout: f64) error{PlatformError}!void {
internal_debug.assertInitialized();
std.debug.assert(!std.math.isNan(timeout));
std.debug.assert(timeout >= 0);
std.debug.assert(timeout <= std.math.f64_max);
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.
///
/// This function posts an empty event from the current thread to the event queue, causing
/// glfw.waitEvents or glfw.waitEventsTimeout to return.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError.
///
/// @thread_safety This function may be called from any thread.
///
/// see also: events, glfw.waitEvents, glfw.waitEventsTimeout
pub inline fn postEmptyEvent() error{PlatformError}!void {
internal_debug.assertInitialized();
c.glfwPostEmptyEvent();
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.PlatformError => |e| e,
else => unreachable,
};
}
/// Returns whether raw mouse motion is supported.
///
/// This function returns whether raw mouse motion is supported on the current system. This status
/// does not change after GLFW has been initialized so you only need to check this once. If you
/// attempt to enable raw motion on a system that does not support it, glfw.Error.PlatformError will
/// be emitted.
///
/// Raw mouse motion is closer to the actual motion of the mouse across a surface. It is not
/// affected by the scaling and acceleration applied to the motion of the desktop cursor. That
/// processing is suitable for a cursor while raw motion is better for controlling for example a 3D
/// camera. Because of this, raw mouse motion is only provided when the cursor is disabled.
///
/// @return `true` if raw mouse motion is supported on the current machine, or `false` otherwise.
///
/// @thread_safety This function must only be called from the main thread.
///
/// see also: raw_mouse_motion, glfw.setInputMode
pub inline fn rawMouseMotionSupported() bool {
internal_debug.assertInitialized();
const supported = c.glfwRawMouseMotionSupported();
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
return supported == c.GLFW_TRUE;
}
pub fn basicTest() !void {
try init(.{});
defer terminate();
const window = Window.create(640, 480, "GLFW example", null, null, .{}) catch |err| {
// return without fail, because most of our CI environments are headless / we cannot open
// windows on them.
std.debug.print("note: failed to create window: {}\n", .{err});
return;
};
defer window.destroy();
var start = std.time.milliTimestamp();
while (std.time.milliTimestamp() < start + 1000 and !window.shouldClose()) {
c.glfwPollEvents();
}
}
test {
std.testing.refAllDeclsRecursive(@This());
}
test "getVersionString" {
std.debug.print("\nGLFW version v{}.{}.{}\n", .{ version.major, version.minor, version.revision });
std.debug.print("\nstring: {s}\n", .{getVersionString()});
}
test "pollEvents" {
try init(.{ .cocoa_chdir_resources = true });
defer terminate();
}
test "pollEvents" {
try init(.{});
defer terminate();
try pollEvents();
}
test "waitEventsTimeout" {
try init(.{});
defer terminate();
try waitEventsTimeout(0.25);
}
test "postEmptyEvent_and_waitEvents" {
try init(.{});
defer terminate();
try postEmptyEvent();
try waitEvents();
}
test "rawMouseMotionSupported" {
try init(.{});
defer terminate();
_ = rawMouseMotionSupported();
}
test "basic" {
try basicTest();
}

167
libs/glfw/src/mod.zig Normal file
View file

@ -0,0 +1,167 @@
//! Modifier key flags
//!
//! See glfw.setKeyCallback for how these are used.
const c = @import("c.zig").c;
// must be in sync with GLFW C constants in modifier group, search for "@defgroup mods Modifier key flags"
/// A bitmask of all key modifiers
pub const Mods = packed struct {
shift: bool = false,
control: bool = false,
alt: bool = false,
super: bool = false,
caps_lock: bool = false,
num_lock: bool = false,
_reserved: u2 = 0,
inline fn verifyIntType(comptime IntType: type) void {
comptime {
switch (@typeInfo(IntType)) {
.Int => {},
else => @compileError("Int was not of int type"),
}
}
}
pub inline fn toInt(self: Mods, comptime IntType: type) IntType {
verifyIntType(IntType);
return @intCast(IntType, @bitCast(u8, self));
}
pub inline fn fromInt(flags: anytype) Mods {
verifyIntType(@TypeOf(flags));
return @bitCast(Mods, @intCast(u8, flags));
}
};
/// Holds all GLFW mod values in their raw form.
pub const RawMods = struct {
/// If this bit is set one or more Shift keys were held down.
pub const shift = c.GLFW_MOD_SHIFT;
/// If this bit is set one or more Control keys were held down.
pub const control = c.GLFW_MOD_CONTROL;
/// If this bit is set one or more Alt keys were held down.
pub const alt = c.GLFW_MOD_ALT;
/// If this bit is set one or more Super keys were held down.
pub const super = c.GLFW_MOD_SUPER;
/// If this bit is set the Caps Lock key is enabled and the glfw.lock_key_mods input mode is set.
pub const caps_lock = c.GLFW_MOD_CAPS_LOCK;
/// If this bit is set the Num Lock key is enabled and the glfw.lock_key_mods input mode is set.
pub const num_lock = c.GLFW_MOD_NUM_LOCK;
};
test "shift int to bitmask" {
const std = @import("std");
const int_mod = RawMods.shift;
const mod = Mods.fromInt(int_mod);
try std.testing.expect(mod.shift == true);
try std.testing.expect(mod.control == false);
try std.testing.expect(mod.alt == false);
try std.testing.expect(mod.super == false);
try std.testing.expect(mod.caps_lock == false);
try std.testing.expect(mod.num_lock == false);
}
test "shift int and alt to bitmask" {
const std = @import("std");
const int_mod = RawMods.shift | RawMods.alt;
const mod = Mods.fromInt(int_mod);
try std.testing.expect(mod.shift == true);
try std.testing.expect(mod.control == false);
try std.testing.expect(mod.alt == true);
try std.testing.expect(mod.super == false);
try std.testing.expect(mod.caps_lock == false);
try std.testing.expect(mod.num_lock == false);
}
test "super int to bitmask" {
const std = @import("std");
const int_mod = RawMods.super;
const mod = Mods.fromInt(int_mod);
try std.testing.expect(mod.shift == false);
try std.testing.expect(mod.control == false);
try std.testing.expect(mod.alt == false);
try std.testing.expect(mod.super == true);
try std.testing.expect(mod.caps_lock == false);
try std.testing.expect(mod.num_lock == false);
}
test "num lock int to bitmask" {
const std = @import("std");
const int_mod = RawMods.num_lock;
const mod = Mods.fromInt(int_mod);
try std.testing.expect(mod.shift == false);
try std.testing.expect(mod.control == false);
try std.testing.expect(mod.alt == false);
try std.testing.expect(mod.super == false);
try std.testing.expect(mod.caps_lock == false);
try std.testing.expect(mod.num_lock == true);
}
test "all int to bitmask" {
const std = @import("std");
const int_mod = RawMods.shift | RawMods.control |
RawMods.alt | RawMods.super |
RawMods.caps_lock | RawMods.num_lock;
const mod = Mods.fromInt(int_mod);
try std.testing.expect(mod.shift == true);
try std.testing.expect(mod.control == true);
try std.testing.expect(mod.alt == true);
try std.testing.expect(mod.super == true);
try std.testing.expect(mod.caps_lock == true);
try std.testing.expect(mod.num_lock == true);
}
test "shift bitmask to int" {
const std = @import("std");
const mod = Mods{ .shift = true };
const int_mod = mod.toInt(c_int);
try std.testing.expectEqual(int_mod, RawMods.shift);
}
test "shift and alt bitmask to int" {
const std = @import("std");
const mod = Mods{ .shift = true, .alt = true };
const int_mod = mod.toInt(c_int);
try std.testing.expectEqual(int_mod, RawMods.shift | RawMods.alt);
}
test "all bitmask to int" {
const std = @import("std");
const mod = Mods{
.shift = true,
.control = true,
.alt = true,
.super = true,
.caps_lock = true,
.num_lock = true,
};
const int_mod = mod.toInt(c_int);
const expected = RawMods.shift | RawMods.control |
RawMods.alt | RawMods.super |
RawMods.caps_lock | RawMods.num_lock;
try std.testing.expectEqual(int_mod, expected);
}

View file

@ -0,0 +1,23 @@
const c = @import("c.zig").c;
/// Mouse button IDs.
///
/// See glfw.setMouseButtonCallback for how these are used.
pub const MouseButton = enum(c_int) {
// We use left/right/middle aliases here because those are more common and we cannot have
// duplicate values in a Zig enum.
left = c.GLFW_MOUSE_BUTTON_1,
right = c.GLFW_MOUSE_BUTTON_2,
middle = c.GLFW_MOUSE_BUTTON_3,
four = c.GLFW_MOUSE_BUTTON_4,
five = c.GLFW_MOUSE_BUTTON_5,
six = c.GLFW_MOUSE_BUTTON_6,
seven = c.GLFW_MOUSE_BUTTON_7,
eight = c.GLFW_MOUSE_BUTTON_8,
};
/// Not in the MouseButton enumeration as it is a duplicate value which is forbidden.
pub const last = MouseButton.eight;
pub const one = MouseButton.left;
pub const two = MouseButton.right;
pub const three = MouseButton.middle;

528
libs/glfw/src/native.zig Normal file
View file

@ -0,0 +1,528 @@
//! Native access functions
const std = @import("std");
const Window = @import("Window.zig");
const Monitor = @import("Monitor.zig");
const Error = @import("errors.zig").Error;
const getError = @import("errors.zig").getError;
const internal_debug = @import("internal_debug.zig");
pub const BackendOptions = struct {
win32: bool = false,
wgl: bool = false,
cocoa: bool = false,
nsgl: bool = false,
x11: bool = false,
glx: bool = false,
wayland: bool = false,
egl: bool = false,
osmesa: bool = false,
};
/// This function returns a type which allows provides an interface to access
/// the native handles based on backends selected.
///
/// The available window API options are:
/// * win32
/// * cocoa
/// * x11
/// * wayland
///
/// The available context API options are:
///
/// * wgl
/// * nsgl
/// * glx
/// * egl
/// * osmesa
///
/// The chosen backends must match those the library was compiled for. Failure to do so
/// will cause a link-time error.
pub fn Native(comptime options: BackendOptions) type {
const native = if (@import("builtin").zig_backend == .stage1)
@cImport({
@cDefine("GLFW_INCLUDE_VULKAN", "1");
@cInclude("GLFW/glfw3.h");
if (options.win32) @cDefine("GLFW_EXPOSE_NATIVE_WIN32", "1");
if (options.wgl) @cDefine("GLFW_EXPOSE_NATIVE_WGL", "1");
if (options.cocoa) @cDefine("GLFW_EXPOSE_NATIVE_COCOA", "1");
if (options.nsgl) @cDefine("GLFW_EXPOSE_NATIVE_NGSL", "1");
if (options.x11) @cDefine("GLFW_EXPOSE_NATIVE_X11", "1");
if (options.glx) @cDefine("GLFW_EXPOSE_NATIVE_GLX", "1");
if (options.wayland) @cDefine("GLFW_EXPOSE_NATIVE_WAYLAND", "1");
if (options.egl) @cDefine("GLFW_EXPOSE_NATIVE_EGL", "1");
if (options.osmesa) @cDefine("GLFW_EXPOSE_NATIVE_OSMESA", "1");
@cInclude("GLFW/glfw3native.h");
})
else
// TODO(self-hosted): HACK: workaround https://github.com/ziglang/zig/issues/12483
//
// Extracted from a build using stage1 from zig-cache/ (`cimport.zig`)
// Then find+replace `= ?fn` -> `= ?*const fn`
@import("cimport1.zig");
return struct {
/// Returns the adapter device name of the specified monitor.
///
/// return: The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`) of the
/// specified monitor.
///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getWin32Adapter(monitor: Monitor) [*:0]const u8 {
internal_debug.assertInitialized();
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
unreachable;
}
/// Returns the display device name of the specified monitor.
///
/// return: The UTF-8 encoded display device name (for example `\\.\DISPLAY1\Monitor0`)
/// of the specified monitor.
///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getWin32Monitor(monitor: Monitor) [*:0]const u8 {
internal_debug.assertInitialized();
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
unreachable;
}
/// Returns the `HWND` of the specified window.
///
/// The `HDC` associated with the window can be queried with the
/// [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc)
/// function.
/// ```
/// const dc = std.os.windows.user32.GetDC(native.getWin32Window(window));
/// ```
/// 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.
pub fn getWin32Window(window: Window) std.os.windows.HWND {
internal_debug.assertInitialized();
if (native.glfwGetWin32Window(@ptrCast(*native.GLFWwindow, window.handle))) |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
unreachable;
}
/// Returns the `HGLRC` of the specified window.
///
/// The `HDC` associated with the window can be queried with the
/// [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc)
/// function.
/// ```
/// const dc = std.os.windows.user32.GetDC(native.getWin32Window(window));
/// ```
/// 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.
pub fn getWGLContext(window: Window) error{NoWindowContext}!std.os.windows.HGLRC {
internal_debug.assertInitialized();
if (native.glfwGetWGLContext(@ptrCast(*native.GLFWwindow, window.handle))) |context| return context;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.NoWindowContext => |e| e,
else => unreachable,
};
// `glfwGetWGLContext` returns `null` only for errors
unreachable;
}
/// 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.
pub fn getCocoaMonitor(monitor: Monitor) u32 {
internal_debug.assertInitialized();
const mon = native.glfwGetCocoaMonitor(@ptrCast(*native.GLFWmonitor, monitor.handle));
if (mon != native.kCGNullDirectDisplay) return mon;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetCocoaMonitor` returns `kCGNullDirectDisplay` only for errors
unreachable;
}
/// 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.
pub fn getCocoaWindow(window: Window) ?*anyopaque {
internal_debug.assertInitialized();
const win = 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.
///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.NoWindowContext.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getNSGLContext(window: Window) error{NoWindowContext}!u32 {
internal_debug.assertInitialized();
const context = 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.
///
/// Possible errors include glfw.Error.NotInitalized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getX11Display() *anyopaque {
internal_debug.assertInitialized();
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
unreachable;
}
/// 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.
pub fn getX11Adapter(monitor: Monitor) u32 {
internal_debug.assertInitialized();
const adapter = native.glfwGetX11Adapter(@ptrCast(*native.GLFWMonitor, monitor.handle));
if (adapter != 0) return adapter;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetX11Adapter` returns `0` only for errors
unreachable;
}
/// 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.
pub fn getX11Monitor(monitor: Monitor) u32 {
internal_debug.assertInitialized();
const mon = native.glfwGetX11Monitor(@ptrCast(*native.GLFWmonitor, monitor.handle));
if (mon != 0) return mon;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetX11Monitor` returns `0` only for errors
unreachable;
}
/// 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.
pub fn getX11Window(window: Window) u32 {
internal_debug.assertInitialized();
const win = native.glfwGetX11Window(@ptrCast(*native.GLFWwindow, window.handle));
if (win != 0) return @intCast(u32, win);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetX11Window` returns `0` only for errors
unreachable;
}
/// Sets the current primary selection to the specified string.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError.
///
/// The specified string is copied before this function returns.
///
/// thread_safety: This function must only be called from the main thread.
pub fn setX11SelectionString(string: [*:0]const u8) error{PlatformError}!void {
internal_debug.assertInitialized();
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.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.PlatformError.
///
/// 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
/// setX11SelectionString, or until the library is terminated.
///
/// thread_safety: This function must only be called from the main thread.
pub fn getX11SelectionString() error{FormatUnavailable}![*:0]const u8 {
internal_debug.assertInitialized();
if (native.glfwGetX11SelectionString()) |str| return str;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.FormatUnavailable => |e| e,
else => unreachable,
};
// `glfwGetX11SelectionString` returns `null` only for errors
unreachable;
}
/// Returns the `GLXContext` of the specified window.
///
/// Possible errors include glfw.Error.NoWindowContext and glfw.Error.NotInitialized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getGLXContext(window: Window) error{NoWindowContext}!*anyopaque {
internal_debug.assertInitialized();
if (native.glfwGetGLXContext(@ptrCast(*native.GLFWwindow, window.handle))) |context| return @ptrCast(*anyopaque, context);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.NoWindowContext => |e| e,
else => unreachable,
};
// `glfwGetGLXContext` returns `null` only for errors
unreachable;
}
/// Returns the `GLXWindow` of the specified window.
///
/// Possible errors include glfw.Error.NoWindowContext and glfw.Error.NotInitialized.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getGLXWindow(window: Window) error{NoWindowContext}!*anyopaque {
internal_debug.assertInitialized();
const win = native.glfwGetGLXWindow(@ptrCast(*native.GLFWwindow, window.handle));
if (win != 0) return @ptrCast(*anyopaque, win);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.NoWindowContext => |e| e,
else => unreachable,
};
// `glfwGetGLXWindow` returns `0` only for errors
unreachable;
}
/// 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.
pub fn getWaylandDisplay() *anyopaque {
internal_debug.assertInitialized();
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
unreachable;
}
/// 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.
pub fn getWaylandMonitor(monitor: Monitor) *anyopaque {
internal_debug.assertInitialized();
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
unreachable;
}
/// 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.
pub fn getWaylandWindow(window: Window) *anyopaque {
internal_debug.assertInitialized();
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
unreachable;
}
/// 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`
/// until the first context has been created via EGL.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getEGLDisplay() *anyopaque {
internal_debug.assertInitialized();
const display = native.glfwGetEGLDisplay();
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
unreachable;
}
/// Returns the `EGLContext` of the specified window.
///
/// Possible errors include glfw.Error.NotInitalized and glfw.Error.NoWindowContext.
///
/// thread_safety This function may be called from any thread. Access is not synchronized.
pub fn getEGLContext(window: Window) error{NoWindowContext}!*anyopaque {
internal_debug.assertInitialized();
const context = native.glfwGetEGLContext(@ptrCast(*native.GLFWwindow, window.handle));
if (context != native.EGL_NO_CONTEXT) return @ptrCast(*anyopaque, context);
getError() catch |err| return switch (err) {
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.
///
/// Possible errors include glfw.Error.NotInitalized and glfw.Error.NoWindowContext.
///
/// thread_safety This function may be called from any thread. Access is not synchronized.
pub fn getEGLSurface(window: Window) error{NoWindowContext}!*anyopaque {
internal_debug.assertInitialized();
const surface = native.glfwGetEGLSurface(@ptrCast(*native.GLFWwindow, window.handle));
if (surface != native.EGL_NO_SURFACE) return @ptrCast(*anyopaque, surface);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.NoWindowContext => |e| e,
else => unreachable,
};
// `glfwGetEGLSurface` returns `EGL_NO_SURFACE` only for errors
unreachable;
}
pub const OSMesaColorBuffer = struct {
width: c_int,
height: c_int,
format: c_int,
buffer: *anyopaque,
};
/// Retrieves the color buffer associated with the specified window.
///
/// Possible errors include glfw.Error.NotInitalized, glfw.Error.NoWindowContext
/// and glfw.Error.PlatformError.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getOSMesaColorBuffer(window: Window) error{ PlatformError, NoWindowContext }!OSMesaColorBuffer {
internal_debug.assertInitialized();
var buf: OSMesaColorBuffer = undefined;
if (native.glfwGetOSMesaColorBuffer(
@ptrCast(*native.GLFWwindow, window.handle),
&buf.width,
&buf.height,
&buf.format,
&buf.buffer,
) == native.GLFW_TRUE) return buf;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.PlatformError, Error.NoWindowContext => |e| e,
else => unreachable,
};
// `glfwGetOSMesaColorBuffer` returns `GLFW_FALSE` only for errors
unreachable;
}
pub const OSMesaDepthBuffer = struct {
width: c_int,
height: c_int,
bytes_per_value: c_int,
buffer: *anyopaque,
};
/// Retrieves the depth buffer associated with the specified window.
///
/// Possible errors include glfw.Error.NotInitalized, glfw.Error.NoWindowContext
/// and glfw.Error.PlatformError.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getOSMesaDepthBuffer(window: Window) error{ PlatformError, NoWindowContext }!OSMesaDepthBuffer {
internal_debug.assertInitialized();
var buf: OSMesaDepthBuffer = undefined;
if (native.glfwGetOSMesaDepthBuffer(
@ptrCast(*native.GLFWwindow, window.handle),
&buf.width,
&buf.height,
&buf.bytes_per_value,
&buf.buffer,
) == native.GLFW_TRUE) return buf;
getError() catch |err| return switch (err) {
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.
///
/// Possible errors include glfw.Error.NotInitalized and glfw.Error.NoWindowContext.
///
/// thread_safety: This function may be called from any thread. Access is not synchronized.
pub fn getOSMesaContext(window: Window) error{NoWindowContext}!*anyopaque {
internal_debug.assertInitialized();
if (native.glfwGetOSMesaContext(@ptrCast(*native.GLFWwindow, window.handle))) |context| return @ptrCast(*anyopaque, context);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.NoWindowContext => |e| e,
else => unreachable,
};
// `glfwGetOSMesaContext` returns `null` only for errors
unreachable;
}
};
}

270
libs/glfw/src/opengl.zig Normal file
View file

@ -0,0 +1,270 @@
const std = @import("std");
const c = @import("c.zig").c;
const Window = @import("Window.zig");
const Error = @import("errors.zig").Error;
const getError = @import("errors.zig").getError;
const internal_debug = @import("internal_debug.zig");
/// Makes the context of the specified window current for the calling thread.
///
/// This function makes the OpenGL or OpenGL ES context of the specified window current on the
/// calling thread. A context must only be made current on a single thread at a time and each
/// thread can have only a single current context at a time.
///
/// When moving a context between threads, you must make it non-current on the old thread before
/// making it current on the new one.
///
/// By default, making a context non-current implicitly forces a pipeline flush. On machines that
/// support `GL_KHR_context_flush_control`, you can control whether a context performs this flush
/// by setting the glfw.context_release_behavior hint.
///
/// The specified window must have an OpenGL or OpenGL ES context. Specifying a window without a
/// context will generate Error.NoWindowContext.
///
/// @param[in] window The window whose context to make current, or null to
/// detach the current context.
///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.NoWindowContext and glfw.Error.PlatformError.
///
/// @thread_safety This function may be called from any thread.
///
/// see also: context_current, glfwGetCurrentContext
pub inline fn makeContextCurrent(window: ?Window) error{ NoWindowContext, PlatformError }!void {
internal_debug.assertInitialized();
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.
///
/// This function returns the window whose OpenGL or OpenGL ES context is current on the calling
/// thread.
///
/// 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.
///
/// see also: context_current, glfwMakeContextCurrent
pub inline fn getCurrentContext() ?Window {
internal_debug.assertInitialized();
if (c.glfwGetCurrentContext()) |handle| return Window.from(handle);
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
return null;
}
/// Sets the swap interval for the current context.
///
/// This function sets the swap interval for the current OpenGL or OpenGL ES context, i.e. the
/// number of screen updates to wait from the time glfw.SwapBuffers was called before swapping the
/// buffers and returning. This is sometimes called _vertical synchronization_, _vertical retrace
/// synchronization_ or just _vsync_.
///
/// A context that supports either of the `WGL_EXT_swap_control_tear` and `GLX_EXT_swap_control_tear`
/// extensions also accepts _negative_ swap intervals, which allows the driver to swap immediately
/// even if a frame arrives a little bit late. You can check for these extensions with glfw.extensionSupported.
///
/// A context must be current on the calling thread. Calling this function without a current context
/// will cause Error.NoCurrentContext.
///
/// This function does not apply to Vulkan. If you are rendering with Vulkan, see the present mode
/// of your swapchain instead.
///
/// @param[in] interval The minimum number of screen updates to wait for until the buffers are
/// swapped by glfw.swapBuffers.
///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.NoCurrentContext and glfw.Error.PlatformError.
///
/// 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
/// GLFW do not allow the swap interval to be reset to zero once it has been set to a non-zero
/// value.
///
/// Some GPU drivers do not honor the requested swap interval, either because of a user setting
/// that overrides the application's request or due to bugs in the driver.
///
/// @thread_safety This function may be called from any thread.
///
/// see also: buffer_swap, glfwSwapBuffers
pub inline fn swapInterval(interval: i32) error{ NoCurrentContext, PlatformError }!void {
internal_debug.assertInitialized();
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.
///
/// This function returns whether the specified API extension (see context_glext) is supported by
/// the current OpenGL or OpenGL ES context. It searches both for client API extension and context
/// creation API extensions.
///
/// A context must be current on the calling thread. Calling this function without a current
/// context will cause Error.NoCurrentContext.
///
/// As this functions retrieves and searches one or more extension strings each call, it is
/// recommended that you cache its results if it is going to be used frequently. The extension
/// strings will not change during the lifetime of a context, so there is no danger in doing this.
///
/// This function does not apply to Vulkan. If you are using Vulkan, see glfw.getRequiredInstanceExtensions,
/// `vkEnumerateInstanceExtensionProperties` and `vkEnumerateDeviceExtensionProperties` instead.
///
/// @param[in] extension The ASCII encoded name of the extension.
/// @return `true` if the extension is available, or `false` otherwise.
///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.NoCurrentContext, glfw.Error.InvalidValue
/// and glfw.Error.PlatformError.
///
/// @thread_safety This function may be called from any thread.
///
/// see also: context_glext, glfw.getProcAddress
pub inline fn extensionSupported(extension: [:0]const u8) error{ NoCurrentContext, PlatformError }!bool {
internal_debug.assertInitialized();
std.debug.assert(extension.len != 0);
std.debug.assert(extension[0] != 0);
const supported = c.glfwExtensionSupported(extension.ptr);
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");
/// Client API function pointer type.
///
/// Generic function pointer used for returning client API function pointers.
///
/// see also: context_glext, glfwGetProcAddress
pub const GLProc = if (builtin.zig_backend == .stage1 or builtin.zig_backend == .other) fn () callconv(.C) void else *const fn () callconv(.C) void;
/// Returns the address of the specified function for the current context.
///
/// This function returns the address of the specified OpenGL or OpenGL ES core or extension
/// function (see context_glext), if it is supported by the current context.
///
/// A context must be current on the calling thread. Calling this function without a current
/// context will cause Error.NoCurrentContext.
///
/// This function does not apply to Vulkan. If you are rendering with Vulkan, see glfw.getInstanceProcAddress,
/// `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` instead.
///
/// @param[in] procname The ASCII encoded name of the function.
/// @return The address of the function, or null if an error occurred.
///
/// To maintain ABI compatability with the C glfwGetProcAddress, as it is commonly passed into
/// libraries expecting that exact ABI, this function does not return an error. Instead, if
/// glfw.Error.NotInitialized, glfw.Error.NoCurrentContext, or glfw.Error.PlatformError would
/// occur this function will panic. You should ensure a valid OpenGL context exists and the
/// GLFW is initialized before calling this function.
///
/// The address of a given function is not guaranteed to be the same between contexts.
///
/// This function may return a non-null address despite the associated version or extension
/// not being available. Always check the context version or extension string first.
///
/// @pointer_lifetime The returned function pointer is valid until the context is destroyed or the
/// library is terminated.
///
/// @thread_safety This function may be called from any thread.
///
/// see also: context_glext, glfwExtensionSupported
pub fn getProcAddress(proc_name: [*:0]const u8) callconv(.C) ?GLProc {
internal_debug.assertInitialized();
if (c.glfwGetProcAddress(proc_name)) |proc_address| return proc_address;
getError() catch |err| @panic(@errorName(err));
return null;
}
test "makeContextCurrent" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) catch |err| {
// return without fail, because most of our CI environments are headless / we cannot open
// windows on them.
std.debug.print("note: failed to create window: {}\n", .{err});
return;
};
defer window.destroy();
try glfw.makeContextCurrent(window);
}
test "getCurrentContext" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const current_context = glfw.getCurrentContext();
std.debug.assert(current_context == null);
}
test "swapInterval" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) catch |err| {
// return without fail, because most of our CI environments are headless / we cannot open
// windows on them.
std.debug.print("note: failed to create window: {}\n", .{err});
return;
};
defer window.destroy();
try glfw.makeContextCurrent(window);
glfw.swapInterval(1) catch |err| std.debug.print("failed to set swap interval, error={}\n", .{err});
}
test "getProcAddress" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) catch |err| {
// return without fail, because most of our CI environments are headless / we cannot open
// windows on them.
std.debug.print("note: failed to create window: {}\n", .{err});
return;
};
defer window.destroy();
try glfw.makeContextCurrent(window);
_ = glfw.getProcAddress("foobar");
}
test "extensionSupported" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
const window = Window.create(640, 480, "Hello, Zig!", null, null, .{}) catch |err| {
// return without fail, because most of our CI environments are headless / we cannot open
// windows on them.
std.debug.print("note: failed to create window: {}\n", .{err});
return;
};
defer window.destroy();
try glfw.makeContextCurrent(window);
_ = glfw.extensionSupported("foobar") catch |err| std.debug.print("failed to check if extension supported, error={}\n", .{err});
}

View file

@ -0,0 +1,14 @@
// General sources
#include "monitor.c"
#include "init.c"
#include "vulkan.c"
#include "input.c"
#include "osmesa_context.c"
#include "egl_context.c"
#include "context.c"
#include "window.c"
#include "platform.c"
#include "null_init.c"
#include "null_monitor.c"
#include "null_window.c"
#include "null_joystick.c"

View file

@ -0,0 +1,7 @@
// General Linux-like sources
#include "posix_time.c"
#include "posix_thread.c"
#include "linux_joystick.c"
#include "xkb_unicode.c"
#include "posix_module.c"
#include "posix_poll.c"

View file

@ -0,0 +1,4 @@
// General Linux-like sources
#include "wl_monitor.c"
#include "wl_window.c"
#include "wl_init.c"

View file

@ -0,0 +1,5 @@
// General Linux-like sources
#include "x11_init.c"
#include "x11_window.c"
#include "x11_monitor.c"
#include "glx_context.c"

View file

@ -0,0 +1,4 @@
// MacOS-specific sources
#include "cocoa_time.c"
#include "posix_thread.c"
#include "posix_module.c"

View file

@ -0,0 +1,6 @@
// MacOS-specific sources
#include "cocoa_joystick.m"
#include "cocoa_init.m"
#include "cocoa_window.m"
#include "cocoa_monitor.m"
#include "nsgl_context.m"

View file

@ -0,0 +1,9 @@
// Windows-specific sources
#include "win32_thread.c"
#include "wgl_context.c"
#include "win32_init.c"
#include "win32_monitor.c"
#include "win32_time.c"
#include "win32_joystick.c"
#include "win32_window.c"
#include "win32_module.c"

153
libs/glfw/src/time.zig Normal file
View file

@ -0,0 +1,153 @@
const std = @import("std");
const c = @import("c.zig").c;
const Error = @import("errors.zig").Error;
const getError = @import("errors.zig").getError;
const internal_debug = @import("internal_debug.zig");
/// Returns the GLFW time.
///
/// This function returns the current GLFW time, in seconds. Unless the time
/// has been set using @ref glfwSetTime it measures time elapsed since GLFW was
/// initialized.
///
/// This function and @ref glfwSetTime are helper functions on top of glfw.getTimerFrequency
/// and glfw.getTimerValue.
///
/// The resolution of the timer is system dependent, but is usually on the order
/// of a few micro- or nanoseconds. It uses the highest-resolution monotonic
/// time source on each supported operating system.
///
/// @return The current time, in seconds, or zero if an
/// error occurred.
///
/// @thread_safety This function may be called from any thread. Reading and
/// writing of the internal base time is not atomic, so it needs to be
/// externally synchronized with calls to @ref glfwSetTime.
///
/// see also: time
pub inline fn getTime() f64 {
internal_debug.assertInitialized();
const time = c.glfwGetTime();
if (time != 0) return time;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetTime` returns `0` only for errors
unreachable;
}
/// Sets the GLFW time.
///
/// This function sets the current GLFW time, in seconds. The value must be a positive finite
/// number less than or equal to 18446744073.0, which is approximately 584.5 years.
///
/// This function and @ref glfwGetTime are helper functions on top of glfw.getTimerFrequency and
/// glfw.getTimerValue.
///
/// @param[in] time The new value, in seconds.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.InvalidValue.
///
/// 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.
///
/// @thread_safety This function may be called from any thread. Reading and writing of the internal
/// base time is not atomic, so it needs to be externally synchronized with calls to glfw.getTime.
///
/// see also: time
pub inline fn setTime(time: f64) void {
internal_debug.assertInitialized();
std.debug.assert(!std.math.isNan(time));
std.debug.assert(time >= 0);
// assert time is lteq to largest number of seconds representable by u64 with nanosecond precision
std.debug.assert(time <= max_time: {
const @"2^64" = std.math.maxInt(u64);
break :max_time @divTrunc(@"2^64", std.time.ns_per_s);
});
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.
///
/// This function returns the current value of the raw timer, measured in `1/frequency` seconds. To
/// get the frequency, call glfw.getTimerFrequency.
///
/// @return The value of the timer, or zero if an error occurred.
///
/// @thread_safety This function may be called from any thread.
///
/// see also: time, glfw.getTimerFrequency
pub inline fn getTimerValue() u64 {
internal_debug.assertInitialized();
const value = c.glfwGetTimerValue();
if (value != 0) return value;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetTimerValue` returns `0` only for errors
unreachable;
}
/// Returns the frequency, in Hz, of the raw timer.
///
/// This function returns the frequency, in Hz, of the raw timer.
///
/// @return The frequency of the timer, in Hz, or zero if an error occurred.
///
/// @thread_safety This function may be called from any thread.
///
/// see also: time, glfw.getTimerValue
pub inline fn getTimerFrequency() u64 {
internal_debug.assertInitialized();
const frequency = c.glfwGetTimerFrequency();
if (frequency != 0) return frequency;
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
else => unreachable,
};
// `glfwGetTimerFrequency` returns `0` only for errors
unreachable;
}
test "getTime" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
_ = getTime();
}
test "setTime" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
_ = glfw.setTime(1234);
}
test "getTimerValue" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
_ = glfw.getTimerValue();
}
test "getTimerFrequency" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
_ = glfw.getTimerFrequency();
}

18
libs/glfw/src/version.zig Normal file
View file

@ -0,0 +1,18 @@
//! GLFW version info
const c = @import("c.zig").c;
/// The major version number of the GLFW library.
///
/// This is incremented when the API is changed in non-compatible ways.
pub const major = c.GLFW_VERSION_MAJOR;
/// The minor version number of the GLFW library.
///
/// This is incremented when features are added to the API but it remains backward-compatible.
pub const minor = c.GLFW_VERSION_MINOR;
/// The revision number of the GLFW library.
///
/// This is incremented when a bug fix release is made that does not contain any API changes.
pub const revision = c.GLFW_VERSION_REVISION;

307
libs/glfw/src/vulkan.zig Normal file
View file

@ -0,0 +1,307 @@
const std = @import("std");
const c = @import("c.zig").c;
const Error = @import("errors.zig").Error;
const getError = @import("errors.zig").getError;
const Window = @import("Window.zig");
const internal_debug = @import("internal_debug.zig");
/// Sets the desired Vulkan `vkGetInstanceProcAddr` function.
///
/// This function sets the `vkGetInstanceProcAddr` function that GLFW will use for all
/// Vulkan related entry point queries.
///
/// This feature is mostly useful on macOS, if your copy of the Vulkan loader is in
/// a location where GLFW cannot find it through dynamic loading, or if you are still
/// using the static library version of the loader.
///
/// If set to `NULL`, GLFW will try to load the Vulkan loader dynamically by its standard
/// name and get this function from there. This is the default behavior.
///
/// The standard name of the loader is `vulkan-1.dll` on Windows, `libvulkan.so.1` on
/// Linux and other Unix-like systems and `libvulkan.1.dylib` on macOS. If your code is
/// also loading it via these names then you probably don't need to use this function.
///
/// The function address you set is never reset by GLFW, but it only takes effect during
/// initialization. Once GLFW has been initialized, any updates will be ignored until the
/// library is terminated and initialized again.
///
/// remark: This function may be called before glfw.Init.
///
/// thread_safety: This function must only be called from the main thread.
pub fn initVulkanLoader(loader_function: ?VKGetInstanceProcAddr) void {
c.glfwInitVulkanLoader(loader_function orelse null);
}
pub const VKGetInstanceProcAddr = if (@import("builtin").zig_backend == .stage1)
fn (vk_instance: c.VkInstance, name: [*c]const u8) callconv(.C) ?VKProc
else
*const fn (vk_instance: c.VkInstance, name: [*c]const u8) callconv(.C) ?VKProc;
/// Returns whether the Vulkan loader and an ICD have been found.
///
/// This function returns whether the Vulkan loader and any minimally functional ICD have been
/// found.
///
/// The availability of a Vulkan loader and even an ICD does not by itself guarantee that surface
/// creation or even instance creation is possible. Call glfw.getRequiredInstanceExtensions
/// to check whether the extensions necessary for Vulkan surface creation are available and
/// glfw.getPhysicalDevicePresentationSupport to check whether a queue family of a physical device
/// supports image presentation.
///
/// @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.
pub inline fn vulkanSupported() bool {
internal_debug.assertInitialized();
const supported = c.glfwVulkanSupported();
getError() catch unreachable; // Only error 'GLFW_NOT_INITIALIZED' is impossible
return supported == c.GLFW_TRUE;
}
/// Returns the Vulkan instance extensions required by GLFW.
///
/// This function returns an array of names of Vulkan instance extensions required by GLFW for
/// creating Vulkan surfaces for GLFW windows. If successful, the list will always contain
/// `VK_KHR_surface`, so if you don't require any additional extensions you can pass this list
/// directly to the `VkInstanceCreateInfo` struct.
///
/// If Vulkan is not available on the machine, this function returns null and generates a
/// glfw.Error.APIUnavailable error. Call glfw.vulkanSupported to check whether Vulkan is at least
/// minimally available.
///
/// 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.
///
/// Possible errors include glfw.Error.NotInitialized and glfw.Error.APIUnavailable.
///
/// 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
/// an extension more than once in the `VkInstanceCreateInfo` struct.
///
/// @pointer_lifetime The returned array is allocated and freed by GLFW. You should not free it
/// yourself. It is guaranteed to be valid only until the library is terminated.
///
/// @thread_safety This function may be called from any thread.
///
/// see also: vulkan_ext, glfwCreateWindowSurface
pub inline fn getRequiredInstanceExtensions() error{APIUnavailable}![][*:0]const u8 {
internal_debug.assertInitialized();
var count: u32 = 0;
if (c.glfwGetRequiredInstanceExtensions(&count)) |extensions| return @ptrCast([*][*:0]const u8, extensions)[0..count];
getError() catch |err| return switch (err) {
Error.NotInitialized => unreachable,
Error.APIUnavailable => |e| e,
else => unreachable,
};
// `glfwGetRequiredInstanceExtensions` returns `null` only for errors
unreachable;
}
/// Vulkan API function pointer type.
///
/// Generic function pointer used for returning Vulkan API function pointers.
///
/// see also: vulkan_proc, glfw.getInstanceProcAddress
pub const VKProc = if (@import("builtin").zig_backend == .stage1)
fn () callconv(.C) void
else
*const fn () callconv(.C) void;
/// Returns the address of the specified Vulkan instance function.
///
/// This function returns the address of the specified Vulkan core or extension function for the
/// specified instance. If instance is set to null it can return any function exported from the
/// Vulkan loader, including at least the following functions:
///
/// - `vkEnumerateInstanceExtensionProperties`
/// - `vkEnumerateInstanceLayerProperties`
/// - `vkCreateInstance`
/// - `vkGetInstanceProcAddr`
///
/// If Vulkan is not available on the machine, this function returns null and generates a
/// glfw.Error.APIUnavailable error. Call glfw.vulkanSupported to check whether Vulkan is at least
/// minimally available.
///
/// This function is equivalent to calling `vkGetInstanceProcAddr` with a platform-specific query
/// of the Vulkan loader as a fallback.
///
/// @param[in] instance The Vulkan instance to query, or null to retrieve functions related to
/// instance creation.
/// @param[in] procname The ASCII encoded name of the function.
/// @return The address of the function, or null if an error occurred.
///
/// To maintain ABI compatability with the C glfwGetInstanceProcAddress, as it is commonly passed
/// into libraries expecting that exact ABI, this function does not return an error. Instead, if
/// glfw.Error.NotInitialized or glfw.Error.APIUnavailable would occur this function will panic.
/// You may check glfw.vulkanSupported prior to invoking this function.
///
/// @pointer_lifetime The returned function pointer is valid until the library is terminated.
///
/// @thread_safety This function may be called from any thread.
pub fn getInstanceProcAddress(vk_instance: ?*anyopaque, proc_name: [*:0]const u8) callconv(.C) ?VKProc {
internal_debug.assertInitialized();
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;
}
/// Returns whether the specified queue family can present images.
///
/// This function returns whether the specified queue family of the specified physical device
/// supports presentation to the platform GLFW was built for.
///
/// If Vulkan or the required window surface creation instance extensions are not available on the
/// machine, or if the specified instance was not created with the required extensions, this
/// function returns `GLFW_FALSE` and generates a glfw.Error.APIUnavailable error. Call
/// glfw.vulkanSupported to check whether Vulkan is at least minimally available and
/// glfw.getRequiredInstanceExtensions to check what instance extensions are required.
///
/// @param[in] instance The instance that the physical device belongs to.
/// @param[in] device The physical device that the queue family belongs to.
/// @param[in] queuefamily The index of the queue family to query.
/// @return `true` if the queue family supports presentation, or `false` otherwise.
///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.APIUnavailable and glfw.Error.PlatformError.
///
/// 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.
///
/// @thread_safety This function may be called from any thread. For synchronization details of
/// Vulkan objects, see the Vulkan specification.
///
/// see also: vulkan_present
pub inline fn getPhysicalDevicePresentationSupport(
vk_instance: *anyopaque,
vk_physical_device: *anyopaque,
queue_family: u32,
) error{ APIUnavailable, PlatformError }!bool {
internal_debug.assertInitialized();
const v = c.glfwGetPhysicalDevicePresentationSupport(
@ptrCast(c.VkInstance, vk_instance),
@ptrCast(c.VkPhysicalDevice, vk_physical_device),
queue_family,
);
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.
///
/// This function creates a Vulkan surface for the specified window.
///
/// If the Vulkan loader or at least one minimally functional ICD were not found, this function
/// returns `VK_ERROR_INITIALIZATION_FAILED` and generates a glfw.Error.APIUnavailable error. Call
/// glfw.vulkanSupported to check whether Vulkan is at least minimally available.
///
/// If the required window surface creation instance extensions are not available or if the
/// specified instance was not created with these extensions enabled, this function returns `VK_ERROR_EXTENSION_NOT_PRESENT`
/// and generates a glfw.Error.APIUnavailable error. Call glfw.getRequiredInstanceExtensions to
/// check what instance extensions are required.
///
/// The window surface cannot be shared with another API so the window must have been created with
/// the client api hint set to `GLFW_NO_API` otherwise it generates a glfw.Error.InvalidValue error
/// and returns `VK_ERROR_NATIVE_WINDOW_IN_USE_KHR`.
///
/// The window surface must be destroyed before the specified Vulkan instance. It is the
/// responsibility of the caller to destroy the window surface. GLFW does not destroy it for you.
/// Call `vkDestroySurfaceKHR` to destroy the surface.
///
/// @param[in] vk_instance The Vulkan instance to create the surface in.
/// @param[in] window The window to create the surface for.
/// @param[in] vk_allocation_callbacks The allocator to use, or null to use the default
/// allocator.
/// @param[out] surface Where to store the handle of the surface. This is set
/// to `VK_NULL_HANDLE` if an error occurred.
/// @return `VkResult` type, `VK_SUCCESS` if successful, or a Vulkan error code if an
/// error occurred.
///
/// Possible errors include glfw.Error.NotInitialized, glfw.Error.APIUnavailable, glfw.Error.PlatformError and glfw.Error.InvalidValue
///
/// 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
/// should eliminate almost all occurrences of these errors.
///
/// macos: GLFW prefers the `VK_EXT_metal_surface` extension, with the `VK_MVK_macos_surface`
/// extension as a fallback. The name of the selected extension, if any, is included in the array
/// returned by glfw.getRequiredInstanceExtensions.
///
/// macos: This function currently only supports the `VK_MVK_macos_surface` extension from MoltenVK.
///
/// macos: This function creates and sets a `CAMetalLayer` instance for the window content view,
/// which is required for MoltenVK to function.
///
/// x11: By default GLFW prefers the `VK_KHR_xcb_surface` extension, with the `VK_KHR_xlib_surface`
/// extension as a fallback. You can make `VK_KHR_xlib_surface` the preferred extension by setting
/// glfw.InitHints.x11_xcb_vulkan_surface. The name of the selected extension, if any, is included
/// in the array returned by glfw.getRequiredInstanceExtensions.
///
/// @thread_safety This function may be called from any thread. For synchronization details of
/// Vulkan objects, see the Vulkan specification.
///
/// 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 {
internal_debug.assertInitialized();
// zig-vulkan uses enums to represent opaque pointers:
// pub const Instance = enum(usize) { null_handle = 0, _ };
const instance: c.VkInstance = switch (@typeInfo(@TypeOf(vk_instance))) {
.Enum => @intToPtr(c.VkInstance, @enumToInt(vk_instance)),
else => @ptrCast(c.VkInstance, vk_instance),
};
const v = c.glfwCreateWindowSurface(
instance,
window.handle,
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)),
);
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" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
_ = glfw.vulkanSupported();
}
test "getRequiredInstanceExtensions" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
_ = glfw.getRequiredInstanceExtensions() catch |err| std.debug.print("failed to get vulkan instance extensions, error={}\n", .{err});
}
test "getInstanceProcAddress" {
const glfw = @import("main.zig");
try glfw.init(.{});
defer glfw.terminate();
// syntax check only, we don't have a real vulkan instance and so this function would panic.
_ = glfw.getInstanceProcAddress;
}
test "syntax" {
// Best we can do for these two functions in terms of testing in lieu of an actual Vulkan
// context.
_ = getPhysicalDevicePresentationSupport;
_ = createWindowSurface;
_ = initVulkanLoader;
}