forked from mirrors/zig-glsl-view
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
6.6 KiB
Zig
177 lines
6.6 KiB
Zig
const c = @import("c.zig");
|
|
const std = @import("std");
|
|
const cfg = @import("config.zig");
|
|
const gl = @import("gl.zig");
|
|
|
|
pub const Output = struct {
|
|
config: cfg.OutputConfig,
|
|
update: fn (*Output, c.GLuint) bool,
|
|
destroy: fn (*Output) void,
|
|
|
|
fn init(config: cfg.OutputConfig, update: fn (*Output, c.GLuint) bool, destroy: fn (*Output) void) Output {
|
|
return Output{
|
|
.config = config,
|
|
.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 {
|
|
constants: *gl.Constants,
|
|
output: Output,
|
|
window: ?*c.GLFWwindow = null,
|
|
|
|
monitor_idx: i8 = 1,
|
|
|
|
pub fn create(allocator: std.mem.Allocator, config: cfg.OutputConfig, constants: *gl.Constants) *Output {
|
|
const self = allocator.create(WindowOutput) catch unreachable;
|
|
|
|
self.* = WindowOutput{
|
|
.constants = constants,
|
|
.output = Output.init(config, update, destroy),
|
|
};
|
|
|
|
self.createWindow(config, null);
|
|
|
|
return &self.output;
|
|
}
|
|
|
|
fn createWindow(self: *WindowOutput, config: cfg.OutputConfig, defaultMonitor: ?*c.GLFWmonitor) void {
|
|
if (self.window != null) {
|
|
c.glfwDestroyWindow(self.window);
|
|
self.window = null;
|
|
}
|
|
|
|
c.glfwDefaultWindowHints();
|
|
c.glfwWindowHint(c.GLFW_FOCUSED, c.GLFW_FALSE); // not working on Gnome..
|
|
c.glfwWindowHint(c.GLFW_FOCUS_ON_SHOW, c.GLFW_FALSE);
|
|
|
|
if (config.fullscreen) {
|
|
const monitor = defaultMonitor orelse if (config.monitor.len != 0) mon: {
|
|
break :mon monitorByName(config.monitor) orelse {
|
|
std.debug.panic("specified monitor '{s}' not found\n", .{config.monitor});
|
|
};
|
|
} else secondaryMonitor() orelse std.debug.panic("no monitors found\n", .{});
|
|
|
|
// match current videomode for "windowed fullscreen"
|
|
const mode = c.glfwGetVideoMode(monitor);
|
|
c.glfwWindowHint(c.GLFW_RED_BITS, mode.*.redBits);
|
|
c.glfwWindowHint(c.GLFW_GREEN_BITS, mode.*.greenBits);
|
|
c.glfwWindowHint(c.GLFW_BLUE_BITS, mode.*.blueBits);
|
|
c.glfwWindowHint(c.GLFW_REFRESH_RATE, mode.*.refreshRate);
|
|
|
|
c.glfwWindowHint(c.GLFW_AUTO_ICONIFY, c.GLFW_FALSE);
|
|
c.glfwWindowHint(c.GLFW_CENTER_CURSOR, c.GLFW_FALSE);
|
|
if (config.transparent)
|
|
c.glfwWindowHint(c.GLFW_TRANSPARENT_FRAMEBUFFER, c.GLFW_TRUE);
|
|
|
|
self.window = c.glfwCreateWindow(mode.*.width, mode.*.height, "glsl-view output", monitor, self.constants.main_window) orelse {
|
|
std.debug.panic("unable to create output window\n", .{});
|
|
};
|
|
} else {
|
|
self.window = c.glfwCreateWindow(config.width, config.height, "glsl-view output", null, self.constants.main_window) orelse {
|
|
std.debug.panic("unable to create output window\n", .{});
|
|
};
|
|
}
|
|
|
|
_ = c.glfwSetWindowUserPointer(self.window, @ptrCast(*anyopaque, self));
|
|
_ = c.glfwSetKeyCallback(self.window, keyCallback);
|
|
c.glfwMakeContextCurrent(self.window);
|
|
self.constants.normalized_quad.bind(0);
|
|
}
|
|
|
|
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);
|
|
|
|
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 toggleFullscreen(self: *WindowOutput) void {
|
|
self.output.config.fullscreen = !self.output.config.fullscreen;
|
|
self.createWindow(self.output.config, null);
|
|
}
|
|
|
|
fn changeMonitor(self: *WindowOutput, dir: i8) void {
|
|
if (!self.output.config.fullscreen) return;
|
|
var monitor_count: c_int = 0;
|
|
const monitors = c.glfwGetMonitors(&monitor_count);
|
|
self.monitor_idx = @intCast(i8, @mod(self.monitor_idx + dir, monitor_count));
|
|
self.createWindow(self.output.config, monitors[@intCast(u8, self.monitor_idx)]);
|
|
}
|
|
|
|
fn keyCallback(win: ?*c.GLFWwindow, key: c_int, _: c_int, action: c_int, _: 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_ESCAPE => c.glfwSetWindowShouldClose(win, 1),
|
|
c.GLFW_KEY_F => self.toggleFullscreen(),
|
|
c.GLFW_KEY_LEFT => self.changeMonitor(-1),
|
|
c.GLFW_KEY_RIGHT => self.changeMonitor(1),
|
|
else => {},
|
|
}
|
|
}
|
|
};
|
|
|
|
fn monitorByName(name: []const u8) ?*c.GLFWmonitor {
|
|
var monitor_count: c_int = 0;
|
|
const monitors = c.glfwGetMonitors(&monitor_count);
|
|
for (monitors[0..@intCast(usize, monitor_count)]) |monitor| {
|
|
const cname = c.glfwGetMonitorName(monitor);
|
|
if (std.mem.eql(u8, name, cname[0..c.strlen(cname) :0])) return monitor;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
fn secondaryMonitor() ?*c.GLFWmonitor {
|
|
var monitor_count: c_int = 0;
|
|
const monitors = c.glfwGetMonitors(&monitor_count);
|
|
return switch (monitor_count) {
|
|
0 => null,
|
|
1 => monitors[0],
|
|
else => monitors[1],
|
|
};
|
|
}
|