// Main function // Copyright (C) 2021-2023, 2025 Nguyễn Gia Phong // // This file is part of Black Shades. // // Black Shades is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Black Shades is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Black Shades. If not, see . const DefaultPrng = std.Random.DefaultPrng; const allocator = std.heap.c_allocator; const std = @import("std"); const al = @import("zeal"); const folders = @import("known-folders"); const gf = @import("gfz"); const gl = @import("zgl"); const Decals = @import("Decals.zig"); const Scores = misc.Scores; const Textures = @import("Textures.zig"); const c = @import("cimport.zig"); const configuration = @import("config.zig"); const misc = @import("misc.zig"); const loadScores = misc.loadScores; const saveScores = misc.saveScores; var game: *c.Game = undefined; var prng: DefaultPrng = undefined; comptime { _ = @import("geom.zig"); _ = @import("model.zig"); } // export functions in C ABI /// Return a floating point value evenly distributed in the range [0, 1). export fn randFloat() f32 { return prng.random().float(f32); } /// Return a random integer i where at_least <= i <= at_most. /// The results of this function may be biased. export fn randInt(at_least: i32, at_most: i32) i32 { return prng.random().intRangeAtMostBiased(i32, at_least, at_most); } /// Return a random integer i where 0 <= i < less_than. /// The results of this function may be biased. export fn randUint(less_than: u32) u32 { return prng.random().uintLessThanBiased(u32, less_than); } fn resizeWindow(window: gf.Window, width: c_int, height: c_int) void { _ = window; c.resizeWindow(game, width, height); } fn handleKey(window: gf.Window, key: gf.Key, scancode: c_int, action: gf.KeyAction, mods: gf.Mods) void { _ = window; _ = scancode; c.handleKey(game, @intFromEnum(key), @intFromEnum(action), mods.toInt()); } fn look(window: gf.Window, xpos: f64, ypos: f64) void { _ = window; c.look(game, xpos, ypos); } fn click(window: gf.Window, button: gf.MouseButton, action: gf.MouseButton.Action, mods: gf.Mods) void { _ = window; c.click(game, @intFromEnum(button), @intFromEnum(action), mods.toInt()); } pub fn main() !void { const user_config = (try folders.getPath(allocator, .local_configuration)).?; defer allocator.free(user_config); const config = try configuration.parse(user_config); defer config.deinit(); try gf.init(); defer gf.deinit() catch unreachable; const window = try gf.Window.create(config.width, config.height, "Black Shades", .{}, .{}); try window.makeCurrent(); prng = DefaultPrng.init(@bitCast(try gf.getTime())); const textures = try Textures.init(); defer textures.deinit(); var decals = Decals.init(allocator, &textures); defer decals.deinit(); const user_data = (try folders.getPath(allocator, .data)).?; defer allocator.free(user_data); game = c.makeGame(&decals, @bitCast(config), @bitCast(try loadScores(user_data))).?; defer saveScores(user_data, @bitCast(c.getScores(game))) catch unreachable; try window.setSizeCallback(resizeWindow); try gf.swapInterval(@intFromBool(config.vsync)); c.initGl(game); if (try gf.rawMouseMotionSupported()) try window.setInputMode(.raw_mouse_motion, true); try window.setCursorPosCallback(look); try window.setInputMode(.sticky_mouse_buttons, true); try window.setMouseButtonCallback(click); try window.setInputMode(.sticky_keys, true); try window.setKeyCallback(handleKey); const device = try al.Device.init(null); defer device.deinit() catch unreachable; const context = try al.Context.init(device, .{}); defer context.deinit() catch unreachable; try context.makeCurrent(); c.initGame(game); defer c.closeGame(game); while (!try window.shouldClose()) { c.eventLoop(game); decals.update(); decals.draw(); try window.swapBuffers(); try gf.pollEvents(); } }