const std = @import("std"); const c = @import("c.zig").c; pub const Config = struct { // camera settings min_scale: f32 = 0.5, scroll_speed: f32 = 1.5, drag_friction: f32 = 6.0, scale_friction: f32 = 4.0, velocity_threshold: f32 = 15.0, scale_change_threshold: f32 = 0.5, // flashlight settings initial_radius: f32 = 200.0, initial_delta_radius: f32 = 250.0, radius_damping: f32 = 10.0, fade_speed: f32 = 6.0, max_shadow_opacity: f32 = 0.8, radius_change_threshold: f32 = 1.0, feather: f32 = 0.0, // opengl settings texture_filter: i32 = 0, // key bindings (KeySym = c_ulong on x86_64 linux) key_escape: c_ulong = c.XK_Escape, key_flashlight: c_ulong = c.XK_f, key_reset: c_ulong = c.XK_0, key_mirror: c_ulong = c.XK_m, key_zoom_in: c_ulong = c.XK_equal, key_zoom_out: c_ulong = c.XK_minus, modifier_flashlight: c_uint = c.ControlMask, button_drag: c_uint = c.Button1, button_zoom_in: c_uint = c.Button4, button_zoom_out: c_uint = c.Button5, pub fn default() Config { return .{}; } pub fn applyValue(self: *Config, key: []const u8, value: f32) void { if (std.mem.eql(u8, key, "min_scale")) self.min_scale = value; if (std.mem.eql(u8, key, "scroll_speed")) self.scroll_speed = value; if (std.mem.eql(u8, key, "drag_friction")) self.drag_friction = value; if (std.mem.eql(u8, key, "scale_friction")) self.scale_friction = value; if (std.mem.eql(u8, key, "velocity_threshold")) self.velocity_threshold = value; if (std.mem.eql(u8, key, "scale_change_threshold")) self.scale_change_threshold = value; if (std.mem.eql(u8, key, "initial_radius")) self.initial_radius = value; if (std.mem.eql(u8, key, "initial_delta_radius")) self.initial_delta_radius = value; if (std.mem.eql(u8, key, "radius_damping")) self.radius_damping = value; if (std.mem.eql(u8, key, "fade_speed")) self.fade_speed = value; if (std.mem.eql(u8, key, "max_shadow_opacity")) self.max_shadow_opacity = value; if (std.mem.eql(u8, key, "radius_change_threshold")) self.radius_change_threshold = value; if (std.mem.eql(u8, key, "feather")) self.feather = value; if (std.mem.eql(u8, key, "texture_filter")) self.texture_filter = @intFromFloat(value); } pub fn loadFromFile(self: *Config, path: []const u8) void { var path_buf: [1024]u8 = undefined; @memcpy(path_buf[0..@min(path.len, path_buf.len - 1)], path); path_buf[@min(path.len, path_buf.len - 1)] = 0; const path_z: [*:0]const u8 = @ptrCast(&path_buf); const f = c.fopen(path_z, "r") orelse return; defer _ = c.fclose(f); var buf: [4096]u8 = undefined; while (c.fgets(&buf, buf.len, f) != null) { const raw = buf[0 .. std.mem.indexOfScalar(u8, &buf, 0) orelse buf.len]; const line = std.mem.trimStart(u8, raw, " \t"); if (line.len == 0 or line[0] == '#') continue; var parts = std.mem.splitScalar(u8, line, '='); const key = std.mem.trimEnd(u8, parts.first(), " \t"); const val_str = if (parts.next()) |v| std.mem.trimStart(u8, v, " \t") else continue; const value = std.fmt.parseFloat(f32, val_str) catch continue; self.applyValue(key, value); } } pub fn writeDefault(self: Config, path: []const u8) !void { var path_buf: [1024]u8 = undefined; @memcpy(path_buf[0..@min(path.len, path_buf.len - 1)], path); path_buf[@min(path.len, path_buf.len - 1)] = 0; const path_z: [*:0]const u8 = @ptrCast(&path_buf); const f = c.fopen(path_z, "w") orelse { std.debug.print("error: could not write config to {s}\n", .{path}); return error.ConfigWriteFailed; }; defer _ = c.fclose(f); _ = c.fprintf(f, "min_scale = %.1f\n", self.min_scale); _ = c.fprintf(f, "scroll_speed = %.1f\n", self.scroll_speed); _ = c.fprintf(f, "drag_friction = %.1f\n", self.drag_friction); _ = c.fprintf(f, "scale_friction = %.1f\n", self.scale_friction); _ = c.fprintf(f, "velocity_threshold = %.1f\n", self.velocity_threshold); _ = c.fprintf(f, "scale_change_threshold = %.2f\n", self.scale_change_threshold); _ = c.fprintf(f, "initial_radius = %.1f\n", self.initial_radius); _ = c.fprintf(f, "initial_delta_radius = %.1f\n", self.initial_delta_radius); _ = c.fprintf(f, "radius_damping = %.1f\n", self.radius_damping); _ = c.fprintf(f, "fade_speed = %.1f\n", self.fade_speed); _ = c.fprintf(f, "max_shadow_opacity = %.2f\n", self.max_shadow_opacity); _ = c.fprintf(f, "radius_change_threshold = %.2f\n", self.radius_change_threshold); _ = c.fprintf(f, "feather = %.2f\n", self.feather); _ = c.fprintf(f, "texture_filter = %d\n", self.texture_filter); } pub fn defaultConfigPath(alloc: std.mem.Allocator) ?[]const u8 { const home = std.c.getenv("HOME") orelse return null; const home_slice = std.mem.sliceTo(home, 0); const path = std.fs.path.join(alloc, &.{ home_slice, ".config", "boomer", "config" }) catch return null; return path; } pub fn mkdirP(path: []const u8) void { var buf: [1024]u8 = undefined; const len = @min(path.len, buf.len - 1); @memcpy(buf[0..len], path[0..len]); buf[len] = 0; for (1..len) |i| { if (buf[i] == '/') { buf[i] = 0; _ = std.c.mkdir((buf[0..i :0]).ptr, 0o755); buf[i] = '/'; } } _ = std.c.mkdir((buf[0..len :0]).ptr, 0o755); } };