aboutsummaryrefslogtreecommitdiffstats
path: root/src/output.zig
blob: 770c0ba16d0d8894a3d3e4e42df9bdf1b31bb100 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
const c = @import("c.zig");
const std = @import("std");
const cfg = @import("config.zig");
const gl = @import("gl.zig");

pub const Output = struct {
    update: fn (*Output, c.GLuint) bool,
    destroy: fn (*Output) void,

    fn init(update: fn (*Output, c.GLuint) bool, destroy: 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),
            else => unreachable,
        };
    }
};

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, @ptrCast(*c_void, 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 = @intToFloat(f32, width) / @intToFloat(f32, height);
            if (window_aspect >= self.constants.aspect) {
                scaled_height = height;
                scaled_width = @floatToInt(c_int, @intToFloat(f32, height) * self.constants.aspect);
            } else {
                scaled_width = width;
                scaled_height = @floatToInt(c_int, @intToFloat(f32, 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 self = @ptrCast(*WindowOutput, @alignCast(@alignOf(WindowOutput), c.glfwGetWindowUserPointer(win).?));

        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 self = @ptrCast(*WindowOutput, @alignCast(@alignOf(WindowOutput), c.glfwGetWindowUserPointer(win).?));
        self.resized = true;
    }
};