diff --git a/src/config/Config.zig b/src/config/Config.zig index fb87d9a81d..92ac60af5f 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -3189,6 +3189,86 @@ fn expandPaths(self: *Config, base: []const u8) !void { } } +/// Expand a relative path to an absolute path. This function is used by +/// the RepeatablePath and SinglePath to expand the paths they store. +fn expandPath( + alloc: Allocator, + base: []const u8, + path: []const u8, + diags: *cli.DiagnosticList, +) ![]const u8 { + assert(std.fs.path.isAbsolute(base)); + var dir = try std.fs.cwd().openDir(base, .{}); + defer dir.close(); + + // If it is already absolute we can just return it + if (path.len == 0 or std.fs.path.isAbsolute(path)) return path; + + // If it isn't absolute, we need to make it absolute relative + // to the base. + var buf: [std.fs.max_path_bytes]u8 = undefined; + + // Check if the path starts with a tilde and expand it to the + // home directory on Linux/macOS. We explicitly look for "~/" + // because we don't support alternate users such as "~alice/" + if (std.mem.startsWith(u8, path, "~/")) expand: { + // Windows isn't supported yet + if (comptime builtin.os.tag == .windows) break :expand; + + const expanded: []const u8 = internal_os.expandHome( + path, + &buf, + ) catch |err| { + try diags.append(alloc, .{ + .message = try std.fmt.allocPrintZ( + alloc, + "error expanding home directory for path {s}: {}", + .{ path, err }, + ), + }); + + // We can't expand this path so return an empty string + return ""; + }; + + log.debug( + "expanding file path from home directory: path={s}", + .{expanded}, + ); + + return expanded; + } + + const abs = dir.realpath(path, &buf) catch |err| abs: { + if (err == error.FileNotFound) { + // The file doesn't exist. Try to resolve the relative path + // another way. + const resolved = try std.fs.path.resolve(alloc, &.{ base, path }); + defer alloc.free(resolved); + @memcpy(buf[0..resolved.len], resolved); + break :abs buf[0..resolved.len]; + } + + try diags.append(alloc, .{ + .message = try std.fmt.allocPrintZ( + alloc, + "error resolving file path {s}: {}", + .{ path, err }, + ), + }); + + // We can't expand this path so return an empty string + return ""; + }; + + log.debug( + "expanding file path relative={s} abs={s}", + .{ path, abs }, + ); + + return abs; +} + fn loadTheme(self: *Config, theme: Theme) !void { // Load the correct theme depending on the conditional state. // Dark/light themes were programmed prior to conditional configuration @@ -4288,52 +4368,22 @@ pub const SinglePath = struct { try formatter.formatEntry([]const u8, value); } + /// Expand all the paths relative to the base directory. pub fn expand( self: *Self, alloc: Allocator, base: []const u8, diags: *cli.DiagnosticList, ) !void { - assert(std.fs.path.isAbsolute(base)); - var dir = try std.fs.cwd().openDir(base, .{}); - defer dir.close(); - - // If it is already absolute we can ignore it. + // Try expanding path relative to the base. const path = self.value orelse return; - if (std.fs.path.isAbsolute(path)) return; - - // If it isn't absolute, we need to make it absolute relative - // to the base. - var buf: [std.fs.max_path_bytes]u8 = undefined; - const abs = dir.realpath(path, &buf) catch |err| abs: { - if (err == error.FileNotFound) { - // The file doesn't exist. Try to resolve the relative path - // another way. - const resolved = try std.fs.path.resolve(alloc, &.{ base, path }); - defer alloc.free(resolved); - @memcpy(buf[0..resolved.len], resolved); - break :abs buf[0..resolved.len]; - } - - try diags.append(alloc, .{ - .message = try std.fmt.allocPrintZ( - alloc, - "error resolving file path {s}: {}", - .{ path, err }, - ), - }); + const abs = try expandPath(alloc, base, path, diags); + if (abs.len == 0) { // Blank this path so that we don't attempt to resolve it again self.value = null; - return; - }; - - log.debug( - "expanding file path relative={s} abs={s}", - .{ path, abs }, - ); - + } self.value = try alloc.dupeZ(u8, abs); } }; @@ -4569,88 +4619,20 @@ pub const RepeatablePath = struct { base: []const u8, diags: *cli.DiagnosticList, ) !void { - assert(std.fs.path.isAbsolute(base)); - var dir = try std.fs.cwd().openDir(base, .{}); - defer dir.close(); - for (0..self.value.items.len) |i| { const path = switch (self.value.items[i]) { .optional, .required => |path| path, }; - // If it is already absolute we can ignore it. - if (path.len == 0 or std.fs.path.isAbsolute(path)) continue; - - // If it isn't absolute, we need to make it absolute relative - // to the base. - var buf: [std.fs.max_path_bytes]u8 = undefined; - - // Check if the path starts with a tilde and expand it to the - // home directory on Linux/macOS. We explicitly look for "~/" - // because we don't support alternate users such as "~alice/" - if (std.mem.startsWith(u8, path, "~/")) expand: { - // Windows isn't supported yet - if (comptime builtin.os.tag == .windows) break :expand; - - const expanded: []const u8 = internal_os.expandHome( - path, - &buf, - ) catch |err| { - try diags.append(alloc, .{ - .message = try std.fmt.allocPrintZ( - alloc, - "error expanding home directory for path {s}: {}", - .{ path, err }, - ), - }); - - // Blank this path so that we don't attempt to resolve it - // again - self.value.items[i] = .{ .required = "" }; - - continue; - }; - - log.debug( - "expanding file path from home directory: path={s}", - .{expanded}, - ); - - switch (self.value.items[i]) { - .optional, .required => |*p| p.* = try alloc.dupeZ(u8, expanded), - } - - continue; - } - - const abs = dir.realpath(path, &buf) catch |err| abs: { - if (err == error.FileNotFound) { - // The file doesn't exist. Try to resolve the relative path - // another way. - const resolved = try std.fs.path.resolve(alloc, &.{ base, path }); - defer alloc.free(resolved); - @memcpy(buf[0..resolved.len], resolved); - break :abs buf[0..resolved.len]; - } - - try diags.append(alloc, .{ - .message = try std.fmt.allocPrintZ( - alloc, - "error resolving file path {s}: {}", - .{ path, err }, - ), - }); + // Try expanding path relative to the base. + const abs = try expandPath(alloc, base, path, diags); + if (abs.len == 0) { // Blank this path so that we don't attempt to resolve it again self.value.items[i] = .{ .required = "" }; continue; - }; - - log.debug( - "expanding file path relative={s} abs={s}", - .{ path, abs }, - ); + } switch (self.value.items[i]) { .optional, .required => |*p| p.* = try alloc.dupeZ(u8, abs), @@ -5705,6 +5687,10 @@ pub const GraphemeWidthMethod = enum { }; /// See background-image-mode +/// +/// This enum is used to set the background image mode. The values +/// for each mode should be kept in sync with the values in the +/// vertex shader used to render the background image (bgimage) pub const BackgroundImageMode = enum(u8) { zoomed = 0, stretched = 1, diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 8c12c79f7f..8e92df4121 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -1254,7 +1254,22 @@ pub fn prepBackgroundImage(self: *OpenGL) !void { defer self.alloc.free(file_content); // Decode the png (currently, we only support png) - const decoded_image = try wuffs.png.decode(self.alloc, file_content); + const decoded_image: wuffs.ImageData = blk: { + // Extract the file extension + const ext = std.fs.path.extension(path); + const ext_lower = try std.ascii.allocLowerString(self.alloc, ext); + defer self.alloc.free(ext_lower); + + // Match based on extension + if (std.mem.eql(u8, ext_lower, ".png")) { + break :blk try wuffs.png.decode(self.alloc, file_content); + } else if (std.mem.eql(u8, ext_lower, ".jpg") or std.mem.eql(u8, ext_lower, ".jpeg")) { + break :blk try wuffs.jpeg.decode(self.alloc, file_content); + } else { + log.warn("unsupported image format: {s}", .{ext}); + return error.InvalidData; + } + }; defer self.alloc.free(decoded_image.data); // Copy the data into the pending state diff --git a/src/renderer/shaders/bgimage.v.glsl b/src/renderer/shaders/bgimage.v.glsl index 8fe9bb28b6..1d0b5fe760 100644 --- a/src/renderer/shaders/bgimage.v.glsl +++ b/src/renderer/shaders/bgimage.v.glsl @@ -1,5 +1,8 @@ #version 330 core +// These are the possible modes that "mode" can be set to. +// +// NOTE: this must be kept in sync with the BackgroundImageMode const uint MODE_ZOOMED = 0u; const uint MODE_STRETCHED = 1u; const uint MODE_TILED = 2u;