const c = @import("c.zig");
const std = @import("std");
const cfg = @import("config.zig");
const gl = @import("gl.zig");
pub const Output = struct {
update: *const fn (*Output, c.GLuint) bool,
destroy: *const fn (*Output) void,
fn init(update: *const fn (*Output, c.GLuint) bool, destroy: *const fn (*Output) void) Output {
return Output{
.update = update,
.destroy = destroy,
};
}
pub fn create(
allocator: std.mem.Allocator,
config: cfg.OutputConfig,
constants: *gl.Constants,
) *Output {
return switch (config.type) {
.window => WindowOutput.create(allocator, config, constants),
};
}
};
pub const WindowOutput = struct {
output: Output,
window: *c.GLFWwindow,
constants: *gl.Constants,
resized: bool = false,
pub fn create(
allocator: std.mem.Allocator,
config: cfg.OutputConfig,
constants: *gl.Constants,
) *Output {
const self = allocator.create(WindowOutput) catch unreachable;
c.glfwDefaultWindowHints();
self.* = WindowOutput{
.constants = constants,
.output = Output.init(update, destroy),
.window = c.glfwCreateWindow(
config.width,
config.height,
"glsl-view output",
null,
constants.main_window,
) orelse {
std.debug.panic("unable to create output window\n", .{});
},
};
c.glfwSetWindowUserPointer(self.window, @as(*anyopaque, @ptrCast(self)));
_ = c.glfwSetKeyCallback(self.window, keyCallback);
_ = c.glfwSetFramebufferSizeCallback(self.window, sizeCallback);
c.glfwMakeContextCurrent(self.window);
constants.normalized_quad.bind(0);
return &self.output;
}
fn update(output: *Output, texture_id: c.GLuint) bool {
const self = @fieldParentPtr(WindowOutput, "output", output);
if (c.glfwWindowShouldClose(self.window) == c.GL_TRUE)
return true;
c.glfwMakeContextCurrent(self.window);
c.glClear(c.GL_COLOR_BUFFER_BIT);
if (self.resized) {
var width: c_int = undefined;
var height: c_int = undefined;
var scaled_width: c_int = undefined;
var scaled_height: c_int = undefined;
c.glfwGetFramebufferSize(self.window, &width, &height);
const window_aspect = @as(f32, @floatFromInt(width)) / @as(f32, @floatFromInt(height));
if (window_aspect >= self.constants.aspect) {
scaled_height = height;
scaled_width = @as(c_int, @intFromFloat(@as(f32, @floatFromInt(height)) * self.constants.aspect));
} else {
scaled_width = width;
scaled_height = @as(c_int, @intFromFloat(@as(f32, @floatFromInt(width)) / self.constants.aspect));
}
c.glViewport(
@divFloor(width - scaled_width, 2),
@divFloor(height - scaled_height, 2),
scaled_width,
scaled_height,
);
}
c.glBindTexture(c.GL_TEXTURE_2D, texture_id);
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
self.constants.texture_shader.bind();
self.constants.normalized_quad.draw();
c.glfwSwapBuffers(self.window);
return false;
}
fn destroy(output: *Output) void {
const self = @fieldParentPtr(WindowOutput, "output", output);
c.glfwDestroyWindow(self.window);
}
fn keyCallback(
win: ?*c.GLFWwindow,
key: c_int,
scancode: c_int,
action: c_int,
mods: c_int,
) callconv(.C) void {
if (action != c.GLFW_PRESS) return;
const userptr = c.glfwGetWindowUserPointer(win).?;
const self = @as(*WindowOutput, @ptrCast(@alignCast(userptr)));
_ = self;
_ = scancode;
_ = mods;
switch (key) {
// c.GLFW_KEY_F => // toggle fullscreen
// c.GLFW_KEY_LEFT => // cycle through monitors
// c.GLFW_KEY_RIGHT => // cycle through monitors
else => {},
}
}
fn sizeCallback(win: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void {
const userptr = c.glfwGetWindowUserPointer(win).?;
const self = @as(*WindowOutput, @ptrCast(@alignCast(userptr)));
self.resized = true;
_ = width;
_ = height;
}
};