Refactor: Move the Window struct out of main.zig into its own file.

This commit is contained in:
IntegratedQuantum 2024-04-23 16:30:30 +02:00
parent 032acc1600
commit 6da2971528
6 changed files with 438 additions and 430 deletions

366
src/graphics/Window.zig Normal file
View File

@ -0,0 +1,366 @@
const std = @import("std");
const main = @import("root");
const vec = main.vec;
const Vec2f = vec.Vec2f;
pub const c = @cImport ({
@cInclude("glad/glad.h");
@cInclude("GLFW/glfw3.h");
});
var isFullscreen: bool = false;
pub var width: u31 = 1280;
pub var height: u31 = 720;
pub var window: *c.GLFWwindow = undefined;
pub var grabbed: bool = false;
pub var scrollOffset: f32 = 0;
pub const Key = struct {
name: []const u8,
pressed: bool = false,
key: c_int = c.GLFW_KEY_UNKNOWN,
mouseButton: c_int = -1,
scancode: c_int = 0,
releaseAction: ?*const fn() void = null,
pressAction: ?*const fn() void = null,
repeatAction: ?*const fn(Modifiers) void = null,
pub const Modifiers = packed struct(u6) {
shift: bool = false,
control: bool = false,
alt: bool = false,
super: bool = false,
capsLock: bool = false,
numLock: bool = false,
};
pub fn getName(self: Key) []const u8 {
if(self.mouseButton == -1) {
const cName = c.glfwGetKeyName(self.key, self.scancode);
if(cName != null) return std.mem.span(cName);
return switch(self.key) {
c.GLFW_KEY_SPACE => "Space",
c.GLFW_KEY_GRAVE_ACCENT => "Grave Accent",
c.GLFW_KEY_ESCAPE => "Escape",
c.GLFW_KEY_ENTER => "Enter",
c.GLFW_KEY_TAB => "Tab",
c.GLFW_KEY_BACKSPACE => "Backspace",
c.GLFW_KEY_INSERT => "Insert",
c.GLFW_KEY_DELETE => "Delete",
c.GLFW_KEY_RIGHT => "Right",
c.GLFW_KEY_LEFT => "Left",
c.GLFW_KEY_DOWN => "Down",
c.GLFW_KEY_UP => "Up",
c.GLFW_KEY_PAGE_UP => "Page Up",
c.GLFW_KEY_PAGE_DOWN => "Page Down",
c.GLFW_KEY_HOME => "Home",
c.GLFW_KEY_END => "End",
c.GLFW_KEY_CAPS_LOCK => "Caps Lock",
c.GLFW_KEY_SCROLL_LOCK => "Scroll Lock",
c.GLFW_KEY_NUM_LOCK => "Num Lock",
c.GLFW_KEY_PRINT_SCREEN => "Print Screen",
c.GLFW_KEY_PAUSE => "Pause",
c.GLFW_KEY_F1 => "F1",
c.GLFW_KEY_F2 => "F2",
c.GLFW_KEY_F3 => "F3",
c.GLFW_KEY_F4 => "F4",
c.GLFW_KEY_F5 => "F5",
c.GLFW_KEY_F6 => "F6",
c.GLFW_KEY_F7 => "F7",
c.GLFW_KEY_F8 => "F8",
c.GLFW_KEY_F9 => "F9",
c.GLFW_KEY_F10 => "F10",
c.GLFW_KEY_F11 => "F11",
c.GLFW_KEY_F12 => "F12",
c.GLFW_KEY_F13 => "F13",
c.GLFW_KEY_F14 => "F14",
c.GLFW_KEY_F15 => "F15",
c.GLFW_KEY_F16 => "F16",
c.GLFW_KEY_F17 => "F17",
c.GLFW_KEY_F18 => "F18",
c.GLFW_KEY_F19 => "F19",
c.GLFW_KEY_F20 => "F20",
c.GLFW_KEY_F21 => "F21",
c.GLFW_KEY_F22 => "F22",
c.GLFW_KEY_F23 => "F23",
c.GLFW_KEY_F24 => "F24",
c.GLFW_KEY_F25 => "F25",
c.GLFW_KEY_KP_ENTER => "Keypad Enter",
c.GLFW_KEY_LEFT_SHIFT => "Left Shift",
c.GLFW_KEY_LEFT_CONTROL => "Left Control",
c.GLFW_KEY_LEFT_ALT => "Left Alt",
c.GLFW_KEY_LEFT_SUPER => "Left Super",
c.GLFW_KEY_RIGHT_SHIFT => "Right Shift",
c.GLFW_KEY_RIGHT_CONTROL => "Right Control",
c.GLFW_KEY_RIGHT_ALT => "Right Alt",
c.GLFW_KEY_RIGHT_SUPER => "Right Super",
c.GLFW_KEY_MENU => "Menu",
else => "Unknown Key",
};
} else {
return switch(self.mouseButton) {
c.GLFW_MOUSE_BUTTON_LEFT => "Left Button",
c.GLFW_MOUSE_BUTTON_MIDDLE => "Middle Button",
c.GLFW_MOUSE_BUTTON_RIGHT => "Right Button",
else => "Other Mouse Button",
};
}
}
};
pub const GLFWCallbacks = struct {
fn errorCallback(errorCode: c_int, description: [*c]const u8) callconv(.C) void {
std.log.err("GLFW Error({}): {s}", .{errorCode, description});
}
fn keyCallback(_: ?*c.GLFWwindow, glfw_key: c_int, scancode: c_int, action: c_int, _mods: c_int) callconv(.C) void {
const mods: Key.Modifiers = @bitCast(@as(u6, @intCast(_mods)));
if(action == c.GLFW_PRESS) {
for(&main.KeyBoard.keys) |*key| {
if(glfw_key == key.key) {
if(glfw_key != c.GLFW_KEY_UNKNOWN or scancode == key.scancode) {
key.pressed = true;
if(key.pressAction) |pressAction| {
pressAction();
}
if(key.repeatAction) |repeatAction| {
repeatAction(mods);
}
}
}
}
if(nextKeypressListener) |listener| {
listener(glfw_key, -1, scancode);
nextKeypressListener = null;
}
} else if(action == c.GLFW_RELEASE) {
for(&main.KeyBoard.keys) |*key| {
if(glfw_key == key.key) {
if(glfw_key != c.GLFW_KEY_UNKNOWN or scancode == key.scancode) {
key.pressed = false;
if(key.releaseAction) |releaseAction| {
releaseAction();
}
}
}
}
} else if(action == c.GLFW_REPEAT) {
for(&main.KeyBoard.keys) |*key| {
if(glfw_key == key.key) {
if(glfw_key != c.GLFW_KEY_UNKNOWN or scancode == key.scancode) {
if(key.repeatAction) |repeatAction| {
repeatAction(mods);
}
}
}
}
}
}
fn charCallback(_: ?*c.GLFWwindow, codepoint: c_uint) callconv(.C) void {
if(!grabbed) {
main.gui.textCallbacks.char(@intCast(codepoint));
}
}
pub fn framebufferSize(_: ?*c.GLFWwindow, newWidth: c_int, newHeight: c_int) callconv(.C) void {
std.log.info("Framebuffer: {}, {}", .{newWidth, newHeight});
width = @intCast(newWidth);
height = @intCast(newHeight);
main.renderer.updateViewport(width, height, main.settings.fov);
main.gui.updateGuiScale();
main.gui.updateWindowPositions();
}
// Mouse deltas are averaged over multiple frames using a circular buffer:
const deltasLen: u2 = 3;
var deltas: [deltasLen]Vec2f = [_]Vec2f{Vec2f{0, 0}} ** 3;
var deltaBufferPosition: u2 = 0;
var currentPos: Vec2f = Vec2f{0, 0};
var ignoreDataAfterRecentGrab: bool = true;
fn cursorPosition(_: ?*c.GLFWwindow, x: f64, y: f64) callconv(.C) void {
const newPos = Vec2f {
@floatCast(x),
@floatCast(y),
};
if(grabbed and !ignoreDataAfterRecentGrab) {
deltas[deltaBufferPosition] += (newPos - currentPos)*@as(Vec2f, @splat(main.settings.mouseSensitivity));
var averagedDelta: Vec2f = Vec2f{0, 0};
for(deltas) |delta| {
averagedDelta += delta;
}
averagedDelta /= @splat(deltasLen);
main.game.camera.moveRotation(averagedDelta[0]*0.0089, averagedDelta[1]*0.0089);
deltaBufferPosition = (deltaBufferPosition + 1)%deltasLen;
deltas[deltaBufferPosition] = Vec2f{0, 0};
}
ignoreDataAfterRecentGrab = false;
currentPos = newPos;
}
fn mouseButton(_: ?*c.GLFWwindow, button: c_int, action: c_int, mods: c_int) callconv(.C) void {
_ = mods;
if(action == c.GLFW_PRESS) {
for(&main.KeyBoard.keys) |*key| {
if(button == key.mouseButton) {
key.pressed = true;
if(key.pressAction) |pressAction| {
pressAction();
}
}
}
if(nextKeypressListener) |listener| {
listener(c.GLFW_KEY_UNKNOWN, button, 0);
nextKeypressListener = null;
}
} else if(action == c.GLFW_RELEASE) {
for(&main.KeyBoard.keys) |*key| {
if(button == key.mouseButton) {
key.pressed = false;
if(key.releaseAction) |releaseAction| {
releaseAction();
}
}
}
}
}
fn scroll(_ : ?*c.GLFWwindow, xOffset: f64, yOffset: f64) callconv(.C) void {
_ = xOffset;
scrollOffset += @floatCast(yOffset);
}
fn glDebugOutput(source: c_uint, typ: c_uint, _: c_uint, severity: c_uint, length: c_int, message: [*c]const u8, _: ?*const anyopaque) callconv(.C) void {
const sourceString: []const u8 = switch (source) {
c.GL_DEBUG_SOURCE_API => "API",
c.GL_DEBUG_SOURCE_APPLICATION => "Application",
c.GL_DEBUG_SOURCE_OTHER => "Other",
c.GL_DEBUG_SOURCE_SHADER_COMPILER => "Shader Compiler",
c.GL_DEBUG_SOURCE_THIRD_PARTY => "Third Party",
c.GL_DEBUG_SOURCE_WINDOW_SYSTEM => "Window System",
else => "Unknown",
};
const typeString: []const u8 = switch (typ) {
c.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR => "deprecated behavior",
c.GL_DEBUG_TYPE_ERROR => "error",
c.GL_DEBUG_TYPE_MARKER => "marker",
c.GL_DEBUG_TYPE_OTHER => "other",
c.GL_DEBUG_TYPE_PERFORMANCE => "performance",
c.GL_DEBUG_TYPE_POP_GROUP => "pop group",
c.GL_DEBUG_TYPE_PORTABILITY => "portability",
c.GL_DEBUG_TYPE_PUSH_GROUP => "push group",
c.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR => "undefined behavior",
else => "unknown",
};
switch (severity) {
c.GL_DEBUG_SEVERITY_HIGH => {
std.log.err("OpenGL {s} {s}: {s}", .{sourceString, typeString, message[0..@intCast(length)]});
},
else => {
std.log.warn("OpenGL {s} {s}: {s}", .{sourceString, typeString, message[0..@intCast(length)]});
},
}
}
};
var nextKeypressListener: ?*const fn(c_int, c_int, c_int) void = null;
pub fn setNextKeypressListener(listener: ?*const fn(c_int, c_int, c_int) void) !void {
if(nextKeypressListener != null) return error.AlreadyUsed;
nextKeypressListener = listener;
}
pub fn setMouseGrabbed(grab: bool) void {
if(grabbed != grab) {
if(grab) {
c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_DISABLED);
if (c.glfwRawMouseMotionSupported() != 0)
c.glfwSetInputMode(window, c.GLFW_RAW_MOUSE_MOTION, c.GLFW_TRUE);
GLFWCallbacks.ignoreDataAfterRecentGrab = true;
} else {
c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_NORMAL);
}
grabbed = grab;
}
}
pub fn getMousePosition() Vec2f {
return GLFWCallbacks.currentPos;
}
pub fn getWindowSize() Vec2f {
return Vec2f{@floatFromInt(width), @floatFromInt(height)};
}
pub fn reloadSettings() void {
c.glfwSwapInterval(@intFromBool(main.settings.vsync));
}
pub fn getClipboardString() []const u8 {
return std.mem.span(c.glfwGetClipboardString(window));
}
pub fn setClipboardString(string: []const u8) void {
const nullTerminatedString = main.stackAllocator.dupeZ(u8, string);
defer main.stackAllocator.free(nullTerminatedString);
c.glfwSetClipboardString(window, nullTerminatedString.ptr);
}
pub fn init() void {
_ = c.glfwSetErrorCallback(GLFWCallbacks.errorCallback);
if(c.glfwInit() == 0) {
@panic("Failed to initialize GLFW");
}
c.glfwWindowHint(c.GLFW_OPENGL_DEBUG_CONTEXT, 1);
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 4);
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 6);
window = c.glfwCreateWindow(width, height, "Cubyz", null, null) orelse @panic("Failed to create GLFW window");
_ = c.glfwSetKeyCallback(window, GLFWCallbacks.keyCallback);
_ = c.glfwSetCharCallback(window, GLFWCallbacks.charCallback);
_ = c.glfwSetFramebufferSizeCallback(window, GLFWCallbacks.framebufferSize);
_ = c.glfwSetCursorPosCallback(window, GLFWCallbacks.cursorPosition);
_ = c.glfwSetMouseButtonCallback(window, GLFWCallbacks.mouseButton);
_ = c.glfwSetScrollCallback(window, GLFWCallbacks.scroll);
c.glfwMakeContextCurrent(window);
if(c.gladLoadGL() == 0) {
@panic("Failed to load OpenGL functions from GLAD");
}
reloadSettings();
c.glEnable(c.GL_DEBUG_OUTPUT);
c.glEnable(c.GL_DEBUG_OUTPUT_SYNCHRONOUS);
c.glDebugMessageCallback(GLFWCallbacks.glDebugOutput, null);
c.glDebugMessageControl(c.GL_DONT_CARE, c.GL_DONT_CARE, c.GL_DONT_CARE, 0, null, c.GL_TRUE);
}
pub fn deinit() void {
c.glfwDestroyWindow(window);
c.glfwTerminate();
}
pub fn handleEvents() void {
scrollOffset = 0;
c.glfwPollEvents();
}
var oldX: c_int = 0;
var oldY: c_int = 0;
var oldWidth: c_int = 0;
var oldHeight: c_int = 0;
pub fn toggleFullscreen() void {
isFullscreen = !isFullscreen;
if (isFullscreen) {
c.glfwGetWindowPos(window, &oldX, &oldY);
c.glfwGetWindowSize(window, &oldWidth, &oldHeight);
const monitor = c.glfwGetPrimaryMonitor();
if(monitor == null) {
isFullscreen = false;
return;
}
const vidMode = c.glfwGetVideoMode(monitor).?;
c.glfwSetWindowMonitor(window, monitor, 0, 0, vidMode[0].width, vidMode[0].height, c.GLFW_DONT_CARE);
} else {
c.glfwSetWindowMonitor(window, null, oldX, oldY, oldWidth, oldHeight, c.GLFW_DONT_CARE);
c.glfwSetWindowAttrib(window, c.GLFW_DECORATED, c.GLFW_TRUE);
}
}

View File

@ -145,7 +145,7 @@ fn reloadText(self: *TextInput) void {
self.textSize = self.textBuffer.calculateLineBreaks(fontSize, self.maxWidth - 2*border - scrollBarWidth);
}
fn moveCursorLeft(self: *TextInput, mods: main.Key.Modifiers) void {
fn moveCursorLeft(self: *TextInput, mods: main.Window.Key.Modifiers) void {
if(mods.control) {
const text = self.currentString.items;
if(self.cursor.? == 0) return;
@ -169,7 +169,7 @@ fn moveCursorLeft(self: *TextInput, mods: main.Key.Modifiers) void {
}
}
pub fn left(self: *TextInput, mods: main.Key.Modifiers) void {
pub fn left(self: *TextInput, mods: main.Window.Key.Modifiers) void {
if(self.cursor) |*cursor| {
if(mods.shift) {
if(self.selectionStart == null) {
@ -191,7 +191,7 @@ pub fn left(self: *TextInput, mods: main.Key.Modifiers) void {
}
}
fn moveCursorRight(self: *TextInput, mods: main.Key.Modifiers) void {
fn moveCursorRight(self: *TextInput, mods: main.Window.Key.Modifiers) void {
if(self.cursor.? < self.currentString.items.len) {
if(mods.control) {
const text = self.currentString.items;
@ -211,7 +211,7 @@ fn moveCursorRight(self: *TextInput, mods: main.Key.Modifiers) void {
}
}
pub fn right(self: *TextInput, mods: main.Key.Modifiers) void {
pub fn right(self: *TextInput, mods: main.Window.Key.Modifiers) void {
if(self.cursor) |*cursor| {
if(mods.shift) {
if(self.selectionStart == null) {
@ -237,7 +237,7 @@ fn moveCursorVertically(self: *TextInput, relativeLines: f32) void {
self.cursor = self.textBuffer.mousePosToIndex(self.textBuffer.indexToCursorPos(self.cursor.?) + Vec2f{0, 16*relativeLines}, self.currentString.items.len);
}
pub fn down(self: *TextInput, mods: main.Key.Modifiers) void {
pub fn down(self: *TextInput, mods: main.Window.Key.Modifiers) void {
if(self.cursor) |*cursor| {
if(mods.shift) {
if(self.selectionStart == null) {
@ -259,7 +259,7 @@ pub fn down(self: *TextInput, mods: main.Key.Modifiers) void {
}
}
pub fn up(self: *TextInput, mods: main.Key.Modifiers) void {
pub fn up(self: *TextInput, mods: main.Window.Key.Modifiers) void {
if(self.cursor) |*cursor| {
if(mods.shift) {
if(self.selectionStart == null) {
@ -281,7 +281,7 @@ pub fn up(self: *TextInput, mods: main.Key.Modifiers) void {
}
}
fn moveCursorToStart(self: *TextInput, mods: main.Key.Modifiers) void {
fn moveCursorToStart(self: *TextInput, mods: main.Window.Key.Modifiers) void {
if(mods.control) {
self.cursor.? = 0;
} else {
@ -289,7 +289,7 @@ fn moveCursorToStart(self: *TextInput, mods: main.Key.Modifiers) void {
}
}
pub fn gotoStart(self: *TextInput, mods: main.Key.Modifiers) void {
pub fn gotoStart(self: *TextInput, mods: main.Window.Key.Modifiers) void {
if(self.cursor) |*cursor| {
if(mods.shift) {
if(self.selectionStart == null) {
@ -311,7 +311,7 @@ pub fn gotoStart(self: *TextInput, mods: main.Key.Modifiers) void {
}
}
fn moveCursorToEnd(self: *TextInput, mods: main.Key.Modifiers) void {
fn moveCursorToEnd(self: *TextInput, mods: main.Window.Key.Modifiers) void {
if(mods.control) {
self.cursor.? = @intCast(self.currentString.items.len);
} else {
@ -319,7 +319,7 @@ fn moveCursorToEnd(self: *TextInput, mods: main.Key.Modifiers) void {
}
}
pub fn gotoEnd(self: *TextInput, mods: main.Key.Modifiers) void {
pub fn gotoEnd(self: *TextInput, mods: main.Window.Key.Modifiers) void {
if(self.cursor) |*cursor| {
if(mods.shift) {
if(self.selectionStart == null) {
@ -353,7 +353,7 @@ fn deleteSelection(self: *TextInput) void {
}
}
pub fn deleteLeft(self: *TextInput, _: main.Key.Modifiers) void {
pub fn deleteLeft(self: *TextInput, _: main.Window.Key.Modifiers) void {
if(self.cursor == null) return;
if(self.selectionStart == null) {
self.selectionStart = self.cursor;
@ -364,7 +364,7 @@ pub fn deleteLeft(self: *TextInput, _: main.Key.Modifiers) void {
self.ensureCursorVisibility();
}
pub fn deleteRight(self: *TextInput, _: main.Key.Modifiers) void {
pub fn deleteRight(self: *TextInput, _: main.Window.Key.Modifiers) void {
if(self.cursor == null) return;
if(self.selectionStart == null) {
self.selectionStart = self.cursor;
@ -387,7 +387,7 @@ pub fn inputCharacter(self: *TextInput, character: u21) void {
}
}
pub fn copy(self: *TextInput, mods: main.Key.Modifiers) void {
pub fn copy(self: *TextInput, mods: main.Window.Key.Modifiers) void {
if(mods.control) {
if(self.cursor) |cursor| {
if(self.selectionStart) |selectionStart| {
@ -400,7 +400,7 @@ pub fn copy(self: *TextInput, mods: main.Key.Modifiers) void {
}
}
pub fn paste(self: *TextInput, mods: main.Key.Modifiers) void {
pub fn paste(self: *TextInput, mods: main.Window.Key.Modifiers) void {
if(mods.control) {
const string = main.Window.getClipboardString();
self.deleteSelection();
@ -411,7 +411,7 @@ pub fn paste(self: *TextInput, mods: main.Key.Modifiers) void {
}
}
pub fn cut(self: *TextInput, mods: main.Key.Modifiers) void {
pub fn cut(self: *TextInput, mods: main.Window.Key.Modifiers) void {
if(mods.control) {
self.copy(mods);
self.deleteSelection();
@ -420,7 +420,7 @@ pub fn cut(self: *TextInput, mods: main.Key.Modifiers) void {
}
}
pub fn newline(self: *TextInput, mods: main.Key.Modifiers) void {
pub fn newline(self: *TextInput, mods: main.Window.Key.Modifiers) void {
if(!mods.shift and self.onNewline.callback != null) {
self.onNewline.run();
return;

View File

@ -372,62 +372,62 @@ pub const textCallbacks = struct {
current.inputCharacter(codepoint);
}
}
pub fn left(mods: main.Key.Modifiers) void {
pub fn left(mods: main.Window.Key.Modifiers) void {
if(selectedTextInput) |current| {
current.left(mods);
}
}
pub fn right(mods: main.Key.Modifiers) void {
pub fn right(mods: main.Window.Key.Modifiers) void {
if(selectedTextInput) |current| {
current.right(mods);
}
}
pub fn down(mods: main.Key.Modifiers) void {
pub fn down(mods: main.Window.Key.Modifiers) void {
if(selectedTextInput) |current| {
current.down(mods);
}
}
pub fn up(mods: main.Key.Modifiers) void {
pub fn up(mods: main.Window.Key.Modifiers) void {
if(selectedTextInput) |current| {
current.up(mods);
}
}
pub fn gotoStart(mods: main.Key.Modifiers) void {
pub fn gotoStart(mods: main.Window.Key.Modifiers) void {
if(selectedTextInput) |current| {
current.gotoStart(mods);
}
}
pub fn gotoEnd(mods: main.Key.Modifiers) void {
pub fn gotoEnd(mods: main.Window.Key.Modifiers) void {
if(selectedTextInput) |current| {
current.gotoEnd(mods);
}
}
pub fn deleteLeft(mods: main.Key.Modifiers) void {
pub fn deleteLeft(mods: main.Window.Key.Modifiers) void {
if(selectedTextInput) |current| {
current.deleteLeft(mods);
}
}
pub fn deleteRight(mods: main.Key.Modifiers) void {
pub fn deleteRight(mods: main.Window.Key.Modifiers) void {
if(selectedTextInput) |current| {
current.deleteRight(mods);
}
}
pub fn copy(mods: main.Key.Modifiers) void {
pub fn copy(mods: main.Window.Key.Modifiers) void {
if(selectedTextInput) |current| {
current.copy(mods);
}
}
pub fn paste(mods: main.Key.Modifiers) void {
pub fn paste(mods: main.Window.Key.Modifiers) void {
if(selectedTextInput) |current| {
current.paste(mods);
}
}
pub fn cut(mods: main.Key.Modifiers) void {
pub fn cut(mods: main.Window.Key.Modifiers) void {
if(selectedTextInput) |current| {
current.cut(mods);
}
}
pub fn newline(mods: main.Key.Modifiers) void {
pub fn newline(mods: main.Window.Key.Modifiers) void {
if(selectedTextInput) |current| {
current.newline(mods);
}

View File

@ -16,11 +16,11 @@ pub var window = GuiWindow {
};
const padding: f32 = 8;
var selectedKey: ?*main.Key = null;
var selectedKey: ?*main.Window.Key = null;
var needsUpdate: bool = false;
fn function(keyPtr: usize) void {
main.setNextKeypressListener(&keypressListener) catch return;
main.Window.setNextKeypressListener(&keypressListener) catch return;
selectedKey = @ptrFromInt(keyPtr);
needsUpdate = true;
}

View File

@ -1,7 +1,7 @@
const std = @import("std");
const main = @import("root");
const c = main.c;
const c = main.Window.c;
const graphics = main.graphics;
const draw = graphics.draw;
const Texture = graphics.Texture;

View File

@ -23,6 +23,8 @@ pub const settings = @import("settings.zig");
pub const utils = @import("utils.zig");
pub const vec = @import("vec.zig");
pub const Window = @import("graphics/Window.zig");
pub const List = @import("utils/list.zig").List;
pub const ListUnmanaged = @import("utils/list.zig").ListUnmanaged;
@ -31,11 +33,6 @@ const file_monitor = utils.file_monitor;
const Vec2f = vec.Vec2f;
const Vec3d = vec.Vec3d;
pub const c = @cImport ({
@cInclude("glad/glad.h");
@cInclude("GLFW/glfw3.h");
});
pub threadlocal var stackAllocator: utils.NeverFailingAllocator = undefined;
pub threadlocal var seed: u64 = undefined;
var global_gpa = std.heap.GeneralPurposeAllocator(.{.thread_safe=true}){};
@ -223,106 +220,6 @@ fn logToStdErr(comptime format: []const u8, args: anytype) void {
nosuspend std.io.getStdErr().writeAll(string) catch {};
}
pub const Key = struct {
name: []const u8,
pressed: bool = false,
key: c_int = c.GLFW_KEY_UNKNOWN,
mouseButton: c_int = -1,
scancode: c_int = 0,
releaseAction: ?*const fn() void = null,
pressAction: ?*const fn() void = null,
repeatAction: ?*const fn(Modifiers) void = null,
pub const Modifiers = packed struct(u6) {
shift: bool = false,
control: bool = false,
alt: bool = false,
super: bool = false,
capsLock: bool = false,
numLock: bool = false,
};
pub fn getName(self: Key) []const u8 {
if(self.mouseButton == -1) {
const cName = c.glfwGetKeyName(self.key, self.scancode);
if(cName != null) return std.mem.span(cName);
return switch(self.key) {
c.GLFW_KEY_SPACE => "Space",
c.GLFW_KEY_GRAVE_ACCENT => "Grave Accent",
c.GLFW_KEY_ESCAPE => "Escape",
c.GLFW_KEY_ENTER => "Enter",
c.GLFW_KEY_TAB => "Tab",
c.GLFW_KEY_BACKSPACE => "Backspace",
c.GLFW_KEY_INSERT => "Insert",
c.GLFW_KEY_DELETE => "Delete",
c.GLFW_KEY_RIGHT => "Right",
c.GLFW_KEY_LEFT => "Left",
c.GLFW_KEY_DOWN => "Down",
c.GLFW_KEY_UP => "Up",
c.GLFW_KEY_PAGE_UP => "Page Up",
c.GLFW_KEY_PAGE_DOWN => "Page Down",
c.GLFW_KEY_HOME => "Home",
c.GLFW_KEY_END => "End",
c.GLFW_KEY_CAPS_LOCK => "Caps Lock",
c.GLFW_KEY_SCROLL_LOCK => "Scroll Lock",
c.GLFW_KEY_NUM_LOCK => "Num Lock",
c.GLFW_KEY_PRINT_SCREEN => "Print Screen",
c.GLFW_KEY_PAUSE => "Pause",
c.GLFW_KEY_F1 => "F1",
c.GLFW_KEY_F2 => "F2",
c.GLFW_KEY_F3 => "F3",
c.GLFW_KEY_F4 => "F4",
c.GLFW_KEY_F5 => "F5",
c.GLFW_KEY_F6 => "F6",
c.GLFW_KEY_F7 => "F7",
c.GLFW_KEY_F8 => "F8",
c.GLFW_KEY_F9 => "F9",
c.GLFW_KEY_F10 => "F10",
c.GLFW_KEY_F11 => "F11",
c.GLFW_KEY_F12 => "F12",
c.GLFW_KEY_F13 => "F13",
c.GLFW_KEY_F14 => "F14",
c.GLFW_KEY_F15 => "F15",
c.GLFW_KEY_F16 => "F16",
c.GLFW_KEY_F17 => "F17",
c.GLFW_KEY_F18 => "F18",
c.GLFW_KEY_F19 => "F19",
c.GLFW_KEY_F20 => "F20",
c.GLFW_KEY_F21 => "F21",
c.GLFW_KEY_F22 => "F22",
c.GLFW_KEY_F23 => "F23",
c.GLFW_KEY_F24 => "F24",
c.GLFW_KEY_F25 => "F25",
c.GLFW_KEY_KP_ENTER => "Keypad Enter",
c.GLFW_KEY_LEFT_SHIFT => "Left Shift",
c.GLFW_KEY_LEFT_CONTROL => "Left Control",
c.GLFW_KEY_LEFT_ALT => "Left Alt",
c.GLFW_KEY_LEFT_SUPER => "Left Super",
c.GLFW_KEY_RIGHT_SHIFT => "Right Shift",
c.GLFW_KEY_RIGHT_CONTROL => "Right Control",
c.GLFW_KEY_RIGHT_ALT => "Right Alt",
c.GLFW_KEY_RIGHT_SUPER => "Right Super",
c.GLFW_KEY_MENU => "Menu",
else => "Unknown Key",
};
} else {
return switch(self.mouseButton) {
c.GLFW_MOUSE_BUTTON_LEFT => "Left Button",
c.GLFW_MOUSE_BUTTON_MIDDLE => "Middle Button",
c.GLFW_MOUSE_BUTTON_RIGHT => "Right Button",
else => "Other Mouse Button",
};
}
}
};
var nextKeypressListener: ?*const fn(c_int, c_int, c_int) void = null;
pub fn setNextKeypressListener(listener: ?*const fn(c_int, c_int, c_int) void) !void {
if(nextKeypressListener != null) return error.AlreadyUsed;
nextKeypressListener = listener;
}
fn escape() void {
if(game.world == null) return;
gui.toggleGameMenu();
@ -365,315 +262,58 @@ fn toggleNetworkDebugOverlay() void {
}
pub const KeyBoard = struct {
pub var keys = [_]Key {
const c = Window.c;
pub var keys = [_]Window.Key {
// Gameplay:
Key{.name = "forward", .key = c.GLFW_KEY_W},
Key{.name = "left", .key = c.GLFW_KEY_A},
Key{.name = "backward", .key = c.GLFW_KEY_S},
Key{.name = "right", .key = c.GLFW_KEY_D},
Key{.name = "sprint", .key = c.GLFW_KEY_LEFT_CONTROL},
Key{.name = "jump", .key = c.GLFW_KEY_SPACE},
Key{.name = "fall", .key = c.GLFW_KEY_LEFT_SHIFT},
Key{.name = "fullscreen", .key = c.GLFW_KEY_F11, .releaseAction = &Window.toggleFullscreen},
Key{.name = "placeBlock", .mouseButton = c.GLFW_MOUSE_BUTTON_RIGHT, .pressAction = &game.Player.placeBlock}, // TODO: Add GLFW_REPEAT behavior to mouse buttons.
Key{.name = "breakBlock", .mouseButton = c.GLFW_MOUSE_BUTTON_LEFT, .pressAction = &game.Player.breakBlock}, // TODO: Add GLFW_REPEAT behavior to mouse buttons.
.{.name = "forward", .key = c.GLFW_KEY_W},
.{.name = "left", .key = c.GLFW_KEY_A},
.{.name = "backward", .key = c.GLFW_KEY_S},
.{.name = "right", .key = c.GLFW_KEY_D},
.{.name = "sprint", .key = c.GLFW_KEY_LEFT_CONTROL},
.{.name = "jump", .key = c.GLFW_KEY_SPACE},
.{.name = "fall", .key = c.GLFW_KEY_LEFT_SHIFT},
.{.name = "fullscreen", .key = c.GLFW_KEY_F11, .releaseAction = &Window.toggleFullscreen},
.{.name = "placeBlock", .mouseButton = c.GLFW_MOUSE_BUTTON_RIGHT, .pressAction = &game.Player.placeBlock}, // TODO: Add GLFW_REPEAT behavior to mouse buttons.
.{.name = "breakBlock", .mouseButton = c.GLFW_MOUSE_BUTTON_LEFT, .pressAction = &game.Player.breakBlock}, // TODO: Add GLFW_REPEAT behavior to mouse buttons.
Key{.name = "takeBackgroundImage", .key = c.GLFW_KEY_PRINT_SCREEN, .releaseAction = &takeBackgroundImageFn},
.{.name = "takeBackgroundImage", .key = c.GLFW_KEY_PRINT_SCREEN, .releaseAction = &takeBackgroundImageFn},
// Gui:
Key{.name = "escape", .key = c.GLFW_KEY_ESCAPE, .releaseAction = &escape},
Key{.name = "openInventory", .key = c.GLFW_KEY_I, .releaseAction = &openInventory},
Key{.name = "openWorkbench", .key = c.GLFW_KEY_K, .releaseAction = &openWorkbench}, // TODO: Remove
Key{.name = "openCreativeInventory(aka cheat inventory)", .key = c.GLFW_KEY_C, .releaseAction = &openCreativeInventory},
Key{.name = "mainGuiButton", .mouseButton = c.GLFW_MOUSE_BUTTON_LEFT, .pressAction = &gui.mainButtonPressed, .releaseAction = &gui.mainButtonReleased},
Key{.name = "secondaryGuiButton", .mouseButton = c.GLFW_MOUSE_BUTTON_RIGHT, .pressAction = &gui.secondaryButtonPressed, .releaseAction = &gui.secondaryButtonReleased},
.{.name = "escape", .key = c.GLFW_KEY_ESCAPE, .releaseAction = &escape},
.{.name = "openInventory", .key = c.GLFW_KEY_I, .releaseAction = &openInventory},
.{.name = "openWorkbench", .key = c.GLFW_KEY_K, .releaseAction = &openWorkbench}, // TODO: Remove
.{.name = "openCreativeInventory(aka cheat inventory)", .key = c.GLFW_KEY_C, .releaseAction = &openCreativeInventory},
.{.name = "mainGuiButton", .mouseButton = c.GLFW_MOUSE_BUTTON_LEFT, .pressAction = &gui.mainButtonPressed, .releaseAction = &gui.mainButtonReleased},
.{.name = "secondaryGuiButton", .mouseButton = c.GLFW_MOUSE_BUTTON_RIGHT, .pressAction = &gui.secondaryButtonPressed, .releaseAction = &gui.secondaryButtonReleased},
// text:
Key{.name = "textCursorLeft", .key = c.GLFW_KEY_LEFT, .repeatAction = &gui.textCallbacks.left},
Key{.name = "textCursorRight", .key = c.GLFW_KEY_RIGHT, .repeatAction = &gui.textCallbacks.right},
Key{.name = "textCursorDown", .key = c.GLFW_KEY_DOWN, .repeatAction = &gui.textCallbacks.down},
Key{.name = "textCursorUp", .key = c.GLFW_KEY_UP, .repeatAction = &gui.textCallbacks.up},
Key{.name = "textGotoStart", .key = c.GLFW_KEY_HOME, .repeatAction = &gui.textCallbacks.gotoStart},
Key{.name = "textGotoEnd", .key = c.GLFW_KEY_END, .repeatAction = &gui.textCallbacks.gotoEnd},
Key{.name = "textDeleteLeft", .key = c.GLFW_KEY_BACKSPACE, .repeatAction = &gui.textCallbacks.deleteLeft},
Key{.name = "textDeleteRight", .key = c.GLFW_KEY_DELETE, .repeatAction = &gui.textCallbacks.deleteRight},
Key{.name = "textCopy", .key = c.GLFW_KEY_C, .repeatAction = &gui.textCallbacks.copy},
Key{.name = "textPaste", .key = c.GLFW_KEY_V, .repeatAction = &gui.textCallbacks.paste},
Key{.name = "textCut", .key = c.GLFW_KEY_X, .repeatAction = &gui.textCallbacks.cut},
Key{.name = "textNewline", .key = c.GLFW_KEY_ENTER, .repeatAction = &gui.textCallbacks.newline},
.{.name = "textCursorLeft", .key = c.GLFW_KEY_LEFT, .repeatAction = &gui.textCallbacks.left},
.{.name = "textCursorRight", .key = c.GLFW_KEY_RIGHT, .repeatAction = &gui.textCallbacks.right},
.{.name = "textCursorDown", .key = c.GLFW_KEY_DOWN, .repeatAction = &gui.textCallbacks.down},
.{.name = "textCursorUp", .key = c.GLFW_KEY_UP, .repeatAction = &gui.textCallbacks.up},
.{.name = "textGotoStart", .key = c.GLFW_KEY_HOME, .repeatAction = &gui.textCallbacks.gotoStart},
.{.name = "textGotoEnd", .key = c.GLFW_KEY_END, .repeatAction = &gui.textCallbacks.gotoEnd},
.{.name = "textDeleteLeft", .key = c.GLFW_KEY_BACKSPACE, .repeatAction = &gui.textCallbacks.deleteLeft},
.{.name = "textDeleteRight", .key = c.GLFW_KEY_DELETE, .repeatAction = &gui.textCallbacks.deleteRight},
.{.name = "textCopy", .key = c.GLFW_KEY_C, .repeatAction = &gui.textCallbacks.copy},
.{.name = "textPaste", .key = c.GLFW_KEY_V, .repeatAction = &gui.textCallbacks.paste},
.{.name = "textCut", .key = c.GLFW_KEY_X, .repeatAction = &gui.textCallbacks.cut},
.{.name = "textNewline", .key = c.GLFW_KEY_ENTER, .repeatAction = &gui.textCallbacks.newline},
// debug:
Key{.name = "debugOverlay", .key = c.GLFW_KEY_F3, .releaseAction = &toggleDebugOverlay},
Key{.name = "performanceOverlay", .key = c.GLFW_KEY_F4, .releaseAction = &togglePerformanceOverlay},
Key{.name = "gpuPerformanceOverlay", .key = c.GLFW_KEY_F5, .releaseAction = &toggleGPUPerformanceOverlay},
Key{.name = "networkDebugOverlay", .key = c.GLFW_KEY_F6, .releaseAction = &toggleNetworkDebugOverlay},
.{.name = "debugOverlay", .key = c.GLFW_KEY_F3, .releaseAction = &toggleDebugOverlay},
.{.name = "performanceOverlay", .key = c.GLFW_KEY_F4, .releaseAction = &togglePerformanceOverlay},
.{.name = "gpuPerformanceOverlay", .key = c.GLFW_KEY_F5, .releaseAction = &toggleGPUPerformanceOverlay},
.{.name = "networkDebugOverlay", .key = c.GLFW_KEY_F6, .releaseAction = &toggleNetworkDebugOverlay},
};
pub fn key(name: []const u8) *const Key { // TODO: Maybe I should use a hashmap here?
pub fn key(name: []const u8) *const Window.Key { // TODO: Maybe I should use a hashmap here?
for(&keys) |*_key| {
if(std.mem.eql(u8, name, _key.name)) {
return _key;
}
}
std.log.err("Couldn't find keyboard key with name {s}", .{name});
return &Key{.name = ""};
}
};
pub const Window = struct {
var isFullscreen: bool = false;
pub var width: u31 = 1280;
pub var height: u31 = 720;
var window: *c.GLFWwindow = undefined;
pub var grabbed: bool = false;
pub var scrollOffset: f32 = 0;
const GLFWCallbacks = struct {
fn errorCallback(errorCode: c_int, description: [*c]const u8) callconv(.C) void {
std.log.err("GLFW Error({}): {s}", .{errorCode, description});
}
fn keyCallback(_: ?*c.GLFWwindow, glfw_key: c_int, scancode: c_int, action: c_int, _mods: c_int) callconv(.C) void {
const mods: Key.Modifiers = @bitCast(@as(u6, @intCast(_mods)));
if(action == c.GLFW_PRESS) {
for(&KeyBoard.keys) |*key| {
if(glfw_key == key.key) {
if(glfw_key != c.GLFW_KEY_UNKNOWN or scancode == key.scancode) {
key.pressed = true;
if(key.pressAction) |pressAction| {
pressAction();
}
if(key.repeatAction) |repeatAction| {
repeatAction(mods);
}
}
}
}
if(nextKeypressListener) |listener| {
listener(glfw_key, -1, scancode);
nextKeypressListener = null;
}
} else if(action == c.GLFW_RELEASE) {
for(&KeyBoard.keys) |*key| {
if(glfw_key == key.key) {
if(glfw_key != c.GLFW_KEY_UNKNOWN or scancode == key.scancode) {
key.pressed = false;
if(key.releaseAction) |releaseAction| {
releaseAction();
}
}
}
}
} else if(action == c.GLFW_REPEAT) {
for(&KeyBoard.keys) |*key| {
if(glfw_key == key.key) {
if(glfw_key != c.GLFW_KEY_UNKNOWN or scancode == key.scancode) {
if(key.repeatAction) |repeatAction| {
repeatAction(mods);
}
}
}
}
}
}
fn charCallback(_: ?*c.GLFWwindow, codepoint: c_uint) callconv(.C) void {
if(!grabbed) {
gui.textCallbacks.char(@intCast(codepoint));
}
}
fn framebufferSize(_: ?*c.GLFWwindow, newWidth: c_int, newHeight: c_int) callconv(.C) void {
std.log.info("Framebuffer: {}, {}", .{newWidth, newHeight});
width = @intCast(newWidth);
height = @intCast(newHeight);
renderer.updateViewport(width, height, settings.fov);
gui.updateGuiScale();
gui.updateWindowPositions();
}
// Mouse deltas are averaged over multiple frames using a circular buffer:
const deltasLen: u2 = 3;
var deltas: [deltasLen]Vec2f = [_]Vec2f{Vec2f{0, 0}} ** 3;
var deltaBufferPosition: u2 = 0;
var currentPos: Vec2f = Vec2f{0, 0};
var ignoreDataAfterRecentGrab: bool = true;
fn cursorPosition(_: ?*c.GLFWwindow, x: f64, y: f64) callconv(.C) void {
const newPos = Vec2f {
@floatCast(x),
@floatCast(y),
};
if(grabbed and !ignoreDataAfterRecentGrab) {
deltas[deltaBufferPosition] += (newPos - currentPos)*@as(Vec2f, @splat(settings.mouseSensitivity));
var averagedDelta: Vec2f = Vec2f{0, 0};
for(deltas) |delta| {
averagedDelta += delta;
}
averagedDelta /= @splat(deltasLen);
game.camera.moveRotation(averagedDelta[0]*0.0089, averagedDelta[1]*0.0089);
deltaBufferPosition = (deltaBufferPosition + 1)%deltasLen;
deltas[deltaBufferPosition] = Vec2f{0, 0};
}
ignoreDataAfterRecentGrab = false;
currentPos = newPos;
}
fn mouseButton(_: ?*c.GLFWwindow, button: c_int, action: c_int, mods: c_int) callconv(.C) void {
_ = mods;
if(action == c.GLFW_PRESS) {
for(&KeyBoard.keys) |*key| {
if(button == key.mouseButton) {
key.pressed = true;
if(key.pressAction) |pressAction| {
pressAction();
}
}
}
if(nextKeypressListener) |listener| {
listener(c.GLFW_KEY_UNKNOWN, button, 0);
nextKeypressListener = null;
}
} else if(action == c.GLFW_RELEASE) {
for(&KeyBoard.keys) |*key| {
if(button == key.mouseButton) {
key.pressed = false;
if(key.releaseAction) |releaseAction| {
releaseAction();
}
}
}
}
}
fn scroll(_ : ?*c.GLFWwindow, xOffset: f64, yOffset: f64) callconv(.C) void {
_ = xOffset;
scrollOffset += @floatCast(yOffset);
}
fn glDebugOutput(source: c_uint, typ: c_uint, _: c_uint, severity: c_uint, length: c_int, message: [*c]const u8, _: ?*const anyopaque) callconv(.C) void {
const sourceString: []const u8 = switch (source) {
c.GL_DEBUG_SOURCE_API => "API",
c.GL_DEBUG_SOURCE_APPLICATION => "Application",
c.GL_DEBUG_SOURCE_OTHER => "Other",
c.GL_DEBUG_SOURCE_SHADER_COMPILER => "Shader Compiler",
c.GL_DEBUG_SOURCE_THIRD_PARTY => "Third Party",
c.GL_DEBUG_SOURCE_WINDOW_SYSTEM => "Window System",
else => "Unknown",
};
const typeString: []const u8 = switch (typ) {
c.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR => "deprecated behavior",
c.GL_DEBUG_TYPE_ERROR => "error",
c.GL_DEBUG_TYPE_MARKER => "marker",
c.GL_DEBUG_TYPE_OTHER => "other",
c.GL_DEBUG_TYPE_PERFORMANCE => "performance",
c.GL_DEBUG_TYPE_POP_GROUP => "pop group",
c.GL_DEBUG_TYPE_PORTABILITY => "portability",
c.GL_DEBUG_TYPE_PUSH_GROUP => "push group",
c.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR => "undefined behavior",
else => "unknown",
};
switch (severity) {
c.GL_DEBUG_SEVERITY_HIGH => {
std.log.err("OpenGL {s} {s}: {s}", .{sourceString, typeString, message[0..@intCast(length)]});
},
else => {
std.log.warn("OpenGL {s} {s}: {s}", .{sourceString, typeString, message[0..@intCast(length)]});
},
}
}
};
pub fn setMouseGrabbed(grab: bool) void {
if(grabbed != grab) {
if(grab) {
c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_DISABLED);
if (c.glfwRawMouseMotionSupported() != 0)
c.glfwSetInputMode(window, c.GLFW_RAW_MOUSE_MOTION, c.GLFW_TRUE);
GLFWCallbacks.ignoreDataAfterRecentGrab = true;
} else {
c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_NORMAL);
}
grabbed = grab;
}
}
pub fn getMousePosition() Vec2f {
return GLFWCallbacks.currentPos;
}
pub fn getWindowSize() Vec2f {
return Vec2f{@floatFromInt(width), @floatFromInt(height)};
}
pub fn reloadSettings() void {
c.glfwSwapInterval(@intFromBool(settings.vsync));
}
pub fn getClipboardString() []const u8 {
return std.mem.span(c.glfwGetClipboardString(window));
}
pub fn setClipboardString(string: []const u8) void {
const nullTerminatedString = stackAllocator.dupeZ(u8, string);
defer stackAllocator.free(nullTerminatedString);
c.glfwSetClipboardString(window, nullTerminatedString.ptr);
}
fn init() void {
_ = c.glfwSetErrorCallback(GLFWCallbacks.errorCallback);
if(c.glfwInit() == 0) {
@panic("Failed to initialize GLFW");
}
c.glfwWindowHint(c.GLFW_OPENGL_DEBUG_CONTEXT, 1);
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 4);
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 6);
window = c.glfwCreateWindow(width, height, "Cubyz", null, null) orelse @panic("Failed to create GLFW window");
_ = c.glfwSetKeyCallback(window, GLFWCallbacks.keyCallback);
_ = c.glfwSetCharCallback(window, GLFWCallbacks.charCallback);
_ = c.glfwSetFramebufferSizeCallback(window, GLFWCallbacks.framebufferSize);
_ = c.glfwSetCursorPosCallback(window, GLFWCallbacks.cursorPosition);
_ = c.glfwSetMouseButtonCallback(window, GLFWCallbacks.mouseButton);
_ = c.glfwSetScrollCallback(window, GLFWCallbacks.scroll);
c.glfwMakeContextCurrent(window);
if(c.gladLoadGL() == 0) {
@panic("Failed to load OpenGL functions from GLAD");
}
reloadSettings();
c.glEnable(c.GL_DEBUG_OUTPUT);
c.glEnable(c.GL_DEBUG_OUTPUT_SYNCHRONOUS);
c.glDebugMessageCallback(GLFWCallbacks.glDebugOutput, null);
c.glDebugMessageControl(c.GL_DONT_CARE, c.GL_DONT_CARE, c.GL_DONT_CARE, 0, null, c.GL_TRUE);
}
fn deinit() void {
c.glfwDestroyWindow(window);
c.glfwTerminate();
}
fn handleEvents() void {
scrollOffset = 0;
c.glfwPollEvents();
}
var oldX: c_int = 0;
var oldY: c_int = 0;
var oldWidth: c_int = 0;
var oldHeight: c_int = 0;
pub fn toggleFullscreen() void {
isFullscreen = !isFullscreen;
if (isFullscreen) {
c.glfwGetWindowPos(window, &oldX, &oldY);
c.glfwGetWindowSize(window, &oldWidth, &oldHeight);
const monitor = c.glfwGetPrimaryMonitor();
if(monitor == null) {
isFullscreen = false;
return;
}
const vidMode = c.glfwGetVideoMode(monitor).?;
c.glfwSetWindowMonitor(window, monitor, 0, 0, vidMode[0].width, vidMode[0].height, c.GLFW_DONT_CARE);
} else {
c.glfwSetWindowMonitor(window, null, oldX, oldY, oldWidth, oldHeight, c.GLFW_DONT_CARE);
c.glfwSetWindowAttrib(window, c.GLFW_DECORATED, c.GLFW_TRUE);
}
return &.{.name = ""};
}
};
@ -750,6 +390,8 @@ pub fn main() void {
server.terrain.initGenerators();
defer server.terrain.deinitGenerators();
const c = Window.c;
c.glCullFace(c.GL_BACK);
c.glEnable(c.GL_BLEND);
c.glEnable(c.GL_DEPTH_CLAMP);