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; pub const Error = @import("errors.zig").Error; const getError = @import("errors.zig").getError; 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 version = @import("version.zig"); pub const VideoMode = @import("VideoMode.zig"); pub const Window = @import("Window.zig"); pub const Cursor = @import("Cursor.zig"); 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"); /// 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. /// /// 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 the glfw.COCOA_CHDIR_RESOURCES init hint. /// /// 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{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); initHint(init_hint, init_value); } _ = c.glfwInit(); getError() catch |err| return switch (err) { Error.PlatformError => @errSetCast(error{PlatformError}, err), else => unreachable, }; } /// 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. /// // TODO: Should this remark be removed? Or should we allow this function to be called before init? /// remark: This function may be called before glfw.init. /// /// 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, }; /// 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, /// 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, }; /// 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 => { std.debug.assert(value == c.GLFW_TRUE or value == c.GLFW_FALSE); 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-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. /// /// @return The ASCII encoded GLFW version string. /// /// @errors None. /// /// @remark This function may be called before @ref glfwInit. /// /// @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 { const result = std.mem.span(c.glfwGetVersionString()); getError() catch |err| switch (err) { else => unreachable, }; return result; } /// 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 => @errSetCast(error{PlatformError}, err), 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 => @errSetCast(error{PlatformError}, err), 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 => @errSetCast(error{PlatformError}, err), 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 => @errSetCast(error{PlatformError}, err), 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 "getVersionString" { // Reference these so the tests in these files get pulled in / ran. _ = Monitor; _ = GammaRamp; _ = Image; _ = key; _ = Joystick; _ = VideoMode; _ = Window; _ = Cursor; 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(); }