mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
commons
This commit is contained in:

committed by
rohitb

parent
1ff9162598
commit
320dc35d0a
@ -30,8 +30,10 @@ pub const RepeatableFontVariation = Config.RepeatableFontVariation;
|
|||||||
pub const RepeatableString = Config.RepeatableString;
|
pub const RepeatableString = Config.RepeatableString;
|
||||||
pub const RepeatableStringMap = @import("config/RepeatableStringMap.zig");
|
pub const RepeatableStringMap = @import("config/RepeatableStringMap.zig");
|
||||||
pub const RepeatablePath = Config.RepeatablePath;
|
pub const RepeatablePath = Config.RepeatablePath;
|
||||||
|
pub const SinglePath = Config.SinglePath;
|
||||||
pub const ShellIntegrationFeatures = Config.ShellIntegrationFeatures;
|
pub const ShellIntegrationFeatures = Config.ShellIntegrationFeatures;
|
||||||
pub const WindowPaddingColor = Config.WindowPaddingColor;
|
pub const WindowPaddingColor = Config.WindowPaddingColor;
|
||||||
|
pub const BackgroundImageMode = Config.BackgroundImageMode;
|
||||||
|
|
||||||
// Alternate APIs
|
// Alternate APIs
|
||||||
pub const CAPI = @import("config/CAPI.zig");
|
pub const CAPI = @import("config/CAPI.zig");
|
||||||
|
@ -459,6 +459,32 @@ background: Color = .{ .r = 0x28, .g = 0x2C, .b = 0x34 },
|
|||||||
/// Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color.
|
/// Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color.
|
||||||
foreground: Color = .{ .r = 0xFF, .g = 0xFF, .b = 0xFF },
|
foreground: Color = .{ .r = 0xFF, .g = 0xFF, .b = 0xFF },
|
||||||
|
|
||||||
|
/// Background image for the window.
|
||||||
|
@"background-image": SinglePath = .{},
|
||||||
|
|
||||||
|
/// Background image opacity
|
||||||
|
@"background-image-opacity": f32 = 1.0,
|
||||||
|
|
||||||
|
/// Background image mode to use.
|
||||||
|
///
|
||||||
|
/// Valid values are:
|
||||||
|
///
|
||||||
|
/// * `zoomed` - Image is scaled to fit the window, preserving aspect ratio.
|
||||||
|
/// * `scaled` - Image is scaled to fill the window, not preserving aspect ratio.
|
||||||
|
/// * `tiled` - Image is repeated horizontally and vertically to fill the window.
|
||||||
|
/// * `centered` - Image is centered in the window and displayed 1-to-1 pixel
|
||||||
|
/// scale, preserving both the aspect ratio and the image size.
|
||||||
|
/// * `upper-left` - Image is anchored to the upper left corner of the window,
|
||||||
|
/// preserving the aspect ratio.
|
||||||
|
/// * `upper-right` - Image is anchored to the upper right corner of the window,
|
||||||
|
/// preserving the aspect ratio.
|
||||||
|
/// * `lower-left` - Image is anchored to the lower left corner of the window,
|
||||||
|
/// preserving the aspect ratio.
|
||||||
|
/// * `lower-right` - Image is anchored to the lower right corner of the window,
|
||||||
|
/// preserving the aspect ratio.
|
||||||
|
///
|
||||||
|
@"background-image-mode": BackgroundImageMode = .zoomed,
|
||||||
|
|
||||||
/// The foreground and background color for selection. If this is not set, then
|
/// The foreground and background color for selection. If this is not set, then
|
||||||
/// the selection color is just the inverted window background and foreground
|
/// the selection color is just the inverted window background and foreground
|
||||||
/// (note: not to be confused with the cell bg/fg).
|
/// (note: not to be confused with the cell bg/fg).
|
||||||
@ -2975,20 +3001,97 @@ fn expandPaths(self: *Config, base: []const u8) !void {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Expand all of our paths
|
// Expand all of our paths
|
||||||
inline for (@typeInfo(Config).@"struct".fields) |field| {
|
inline for (@typeInfo(Config).Struct.fields) |field| {
|
||||||
switch (field.type) {
|
if (field.type == RepeatablePath) {
|
||||||
RepeatablePath, Path => {
|
try @field(self, field.name).expand(
|
||||||
try @field(self, field.name).expand(
|
arena_alloc,
|
||||||
arena_alloc,
|
base,
|
||||||
base,
|
&self._diagnostics,
|
||||||
&self._diagnostics,
|
);
|
||||||
);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
fn loadTheme(self: *Config, theme: Theme) !void {
|
||||||
// Load the correct theme depending on the conditional state.
|
// Load the correct theme depending on the conditional state.
|
||||||
// Dark/light themes were programmed prior to conditional configuration
|
// Dark/light themes were programmed prior to conditional configuration
|
||||||
@ -4167,6 +4270,63 @@ pub const Palette = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// SinglePath is a path to a single file. When loading the configuration
|
||||||
|
/// file, always the last one will be kept and be automatically expanded
|
||||||
|
/// relative to the path of the config file.
|
||||||
|
pub const SinglePath = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
/// The actual value that is updated as we parse.
|
||||||
|
value: ?[]const u8 = null,
|
||||||
|
|
||||||
|
/// Parse a single path.
|
||||||
|
pub fn parseCLI(self: *Self, alloc: Allocator, input: ?[]const u8) !void {
|
||||||
|
const value = input orelse return error.ValueRequired;
|
||||||
|
// If the value is empty, we set the value to null
|
||||||
|
if (value.len == 0) {
|
||||||
|
self.value = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const copy = try alloc.dupe(u8, value);
|
||||||
|
self.value = copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deep copy of the struct. Required by Config.
|
||||||
|
pub fn clone(self: Self, alloc: Allocator) Allocator.Error!Self {
|
||||||
|
const value = self.value orelse return .{};
|
||||||
|
|
||||||
|
const copy_path = try alloc.dupe(u8, value);
|
||||||
|
return .{
|
||||||
|
.value = copy_path,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used by Formatter
|
||||||
|
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||||
|
const value = self.value orelse return;
|
||||||
|
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 {
|
||||||
|
// Try expanding path relative to the base.
|
||||||
|
const path = self.value orelse return;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
self.value = try alloc.dupeZ(u8, abs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// RepeatableString is a string value that can be repeated to accumulate
|
/// RepeatableString is a string value that can be repeated to accumulate
|
||||||
/// a list of strings. This isn't called "StringList" because I find that
|
/// a list of strings. This isn't called "StringList" because I find that
|
||||||
/// sometimes leads to confusion that it _accepts_ a list such as
|
/// sometimes leads to confusion that it _accepts_ a list such as
|
||||||
@ -4323,6 +4483,272 @@ pub const RepeatableString = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// RepeatablePath is like repeatable string but represents a path value.
|
||||||
|
/// The difference is that when loading the configuration any values for
|
||||||
|
/// this will be automatically expanded relative to the path of the config
|
||||||
|
/// file.
|
||||||
|
pub const RepeatablePath = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
const Path = union(enum) {
|
||||||
|
/// No error if the file does not exist.
|
||||||
|
optional: [:0]const u8,
|
||||||
|
|
||||||
|
/// The file is required to exist.
|
||||||
|
required: [:0]const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
value: std.ArrayListUnmanaged(Path) = .{},
|
||||||
|
|
||||||
|
pub fn parseCLI(self: *Self, alloc: Allocator, input: ?[]const u8) !void {
|
||||||
|
const value, const optional = if (input) |value| blk: {
|
||||||
|
if (value.len == 0) {
|
||||||
|
self.value.clearRetainingCapacity();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break :blk if (value[0] == '?')
|
||||||
|
.{ value[1..], true }
|
||||||
|
else if (value.len >= 2 and value[0] == '"' and value[value.len - 1] == '"')
|
||||||
|
.{ value[1 .. value.len - 1], false }
|
||||||
|
else
|
||||||
|
.{ value, false };
|
||||||
|
} else return error.ValueRequired;
|
||||||
|
|
||||||
|
if (value.len == 0) {
|
||||||
|
// This handles the case of zero length paths after removing any ?
|
||||||
|
// prefixes or surrounding quotes. In this case, we don't reset the
|
||||||
|
// list.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item: Path = if (optional)
|
||||||
|
.{ .optional = try alloc.dupeZ(u8, value) }
|
||||||
|
else
|
||||||
|
.{ .required = try alloc.dupeZ(u8, value) };
|
||||||
|
|
||||||
|
try self.value.append(alloc, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deep copy of the struct. Required by Config.
|
||||||
|
pub fn clone(self: *const Self, alloc: Allocator) Allocator.Error!Self {
|
||||||
|
const value = try self.value.clone(alloc);
|
||||||
|
for (value.items) |*item| {
|
||||||
|
switch (item.*) {
|
||||||
|
.optional, .required => |*path| path.* = try alloc.dupeZ(u8, path.*),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.value = value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare if two of our value are requal. Required by Config.
|
||||||
|
pub fn equal(self: Self, other: Self) bool {
|
||||||
|
if (self.value.items.len != other.value.items.len) return false;
|
||||||
|
for (self.value.items, other.value.items) |a, b| {
|
||||||
|
if (!std.meta.eql(a, b)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used by Formatter
|
||||||
|
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||||
|
if (self.value.items.len == 0) {
|
||||||
|
try formatter.formatEntry(void, {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf: [std.fs.max_path_bytes + 1]u8 = undefined;
|
||||||
|
for (self.value.items) |item| {
|
||||||
|
const value = switch (item) {
|
||||||
|
.optional => |path| std.fmt.bufPrint(
|
||||||
|
&buf,
|
||||||
|
"?{s}",
|
||||||
|
.{path},
|
||||||
|
) catch |err| switch (err) {
|
||||||
|
// Required for builds on Linux where NoSpaceLeft
|
||||||
|
// isn't an allowed error for fmt.
|
||||||
|
error.NoSpaceLeft => return error.OutOfMemory,
|
||||||
|
},
|
||||||
|
.required => |path| path,
|
||||||
|
};
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
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 },
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parseCLI" {
|
||||||
|
const testing = std.testing;
|
||||||
|
var arena = ArenaAllocator.init(testing.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
|
var list: Self = .{};
|
||||||
|
try list.parseCLI(alloc, "config.1");
|
||||||
|
try list.parseCLI(alloc, "?config.2");
|
||||||
|
try list.parseCLI(alloc, "\"?config.3\"");
|
||||||
|
|
||||||
|
// Zero-length values, ignored
|
||||||
|
try list.parseCLI(alloc, "?");
|
||||||
|
try list.parseCLI(alloc, "\"\"");
|
||||||
|
|
||||||
|
try testing.expectEqual(@as(usize, 3), list.value.items.len);
|
||||||
|
|
||||||
|
const Tag = std.meta.Tag(Path);
|
||||||
|
try testing.expectEqual(Tag.required, @as(Tag, list.value.items[0]));
|
||||||
|
try testing.expectEqualStrings("config.1", list.value.items[0].required);
|
||||||
|
|
||||||
|
try testing.expectEqual(Tag.optional, @as(Tag, list.value.items[1]));
|
||||||
|
try testing.expectEqualStrings("config.2", list.value.items[1].optional);
|
||||||
|
|
||||||
|
try testing.expectEqual(Tag.required, @as(Tag, list.value.items[2]));
|
||||||
|
try testing.expectEqualStrings("?config.3", list.value.items[2].required);
|
||||||
|
|
||||||
|
try list.parseCLI(alloc, "");
|
||||||
|
try testing.expectEqual(@as(usize, 0), list.value.items.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "formatConfig empty" {
|
||||||
|
const testing = std.testing;
|
||||||
|
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||||
|
defer buf.deinit();
|
||||||
|
|
||||||
|
var list: Self = .{};
|
||||||
|
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||||
|
try std.testing.expectEqualSlices(u8, "a = \n", buf.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "formatConfig single item" {
|
||||||
|
const testing = std.testing;
|
||||||
|
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||||
|
defer buf.deinit();
|
||||||
|
|
||||||
|
var arena = ArenaAllocator.init(testing.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
|
var list: Self = .{};
|
||||||
|
try list.parseCLI(alloc, "A");
|
||||||
|
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||||
|
try std.testing.expectEqualSlices(u8, "a = A\n", buf.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "formatConfig multiple items" {
|
||||||
|
const testing = std.testing;
|
||||||
|
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||||
|
defer buf.deinit();
|
||||||
|
|
||||||
|
var arena = ArenaAllocator.init(testing.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
|
var list: Self = .{};
|
||||||
|
try list.parseCLI(alloc, "A");
|
||||||
|
try list.parseCLI(alloc, "?B");
|
||||||
|
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||||
|
try std.testing.expectEqualSlices(u8, "a = A\na = ?B\n", buf.items);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// FontVariation is a repeatable configuration value that sets a single
|
/// FontVariation is a repeatable configuration value that sets a single
|
||||||
/// font variation value. Font variations are configurations for what
|
/// font variation value. Font variations are configurations for what
|
||||||
/// are often called "variable fonts." The font files usually end in
|
/// are often called "variable fonts." The font files usually end in
|
||||||
@ -6199,6 +6625,23 @@ pub const AlphaBlending = enum {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// See background-image-mode
|
||||||
|
///
|
||||||
|
/// This enum is used to set the background image mode. The shader expects
|
||||||
|
/// a `uint`, so we use `u8` here. 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,
|
||||||
|
tiled = 2,
|
||||||
|
centered = 3,
|
||||||
|
upper_left = 4,
|
||||||
|
upper_right = 5,
|
||||||
|
lower_left = 6,
|
||||||
|
lower_right = 7,
|
||||||
|
};
|
||||||
|
|
||||||
/// See freetype-load-flag
|
/// See freetype-load-flag
|
||||||
pub const FreetypeLoadFlags = packed struct {
|
pub const FreetypeLoadFlags = packed struct {
|
||||||
// The defaults here at the time of writing this match the defaults
|
// The defaults here at the time of writing this match the defaults
|
||||||
|
Reference in New Issue
Block a user