From 0aa400383aa7d24f484efa5b45254b8d068f70fb Mon Sep 17 00:00:00 2001 From: Kinzie Date: Sun, 14 Apr 2024 11:16:21 +0100 Subject: [PATCH] moved ini to a lib, fixed alternative langs --- build.zig | 11 +- build.zig.zon | 8 +- res/lang/cat.ini | 1 - res/lang/cs.ini | 1 - res/lang/de.ini | 1 - res/lang/en.ini | 1 - res/lang/es.ini | 1 - res/lang/fr.ini | 3 +- res/lang/it.ini | 1 - res/lang/pl.ini | 1 - res/lang/pt.ini | 1 - res/lang/pt_BR.ini | 1 - res/lang/ro.ini | 1 - res/lang/ru.ini | 1 - res/lang/sr.ini | 1 - res/lang/sv.ini | 1 - res/lang/tr.ini | 1 - res/lang/uk.ini | 1 - src/config/Config.zig | 4 +- src/config/ini.zig | 194 --------------------------------- src/interop.zig | 33 ++---- src/main.zig | 22 ++-- src/tui/TerminalBuffer.zig | 8 +- src/tui/components/Desktop.zig | 7 +- src/tui/utils.zig | 12 +- 25 files changed, 61 insertions(+), 256 deletions(-) mode change 100755 => 100644 res/lang/cat.ini delete mode 100644 src/config/ini.zig diff --git a/build.zig b/build.zig index f5e8b1c..97df282 100644 --- a/build.zig +++ b/build.zig @@ -1,10 +1,15 @@ const std = @import("std"); +const ly_version = std.SemanticVersion{ .major = 1, .minor = 0, .patch = 0 }; + pub fn build(b: *std.Build) void { const data_directory = b.option([]const u8, "data_directory", "Specify a default data directory (default is /etc/ly)"); const build_options = b.addOptions(); build_options.addOption([]const u8, "data_directory", data_directory orelse "/etc/ly"); + var version_str = b.fmt("{d}.{d}.{d}", .{ ly_version.major, ly_version.minor, ly_version.patch }); + + build_options.addOption([]const u8, "version", version_str); const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); @@ -29,10 +34,10 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); - exe.addOptions("build_options", build_options); + const zigini = b.dependency("zigini", .{ .target = target, .optimize = optimize }); + exe.addModule("zigini", zigini.module("zigini")); - const ini = b.dependency("ini", .{}); - exe.addModule("ini", ini.module("ini")); + exe.addOptions("build_options", build_options); const clap = b.dependency("clap", .{ .target = target, .optimize = optimize }); exe.addModule("clap", clap.module("clap")); diff --git a/build.zig.zon b/build.zig.zon index 420de3a..d1e55cf 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -2,13 +2,13 @@ .name = "ly", .version = "1.0.0", .dependencies = .{ - .ini = .{ - .url = "https://github.com/ziglibs/ini/archive/2b11e8fef86d0eefb225156e695be1c1d5c35cbc.tar.gz", - .hash = "1220ed24f7dda09121a175601ddd5c86c1cc937d061a97d38aef049ee4af76e2f594", - }, .clap = .{ .url = "https://github.com/Hejsil/zig-clap/archive/f49b94700e0761b7514abdca0e4f0e7f3f938a93.tar.gz", .hash = "1220f48518ce22882e102255ed3bcdb7aeeb4891f50b2cdd3bd74b5b2e24d3149ba2", }, + .zigini = .{ + .url = "https://github.com/Kawaii-Ash/zigini/archive/91f47e46591982fc559afa3248749c1d29a0fa2a.tar.gz", + .hash = "12209908f2773f730fbca024c80dc7f48dce15a6527b2387f3768968f5bae0d3931e", + }, }, } diff --git a/res/lang/cat.ini b/res/lang/cat.ini old mode 100755 new mode 100644 index 56bd49a..380d8d1 --- a/res/lang/cat.ini +++ b/res/lang/cat.ini @@ -1,4 +1,3 @@ -[ly] capslock = Bloq Majús err_alloc = falla d'assignació de memòria err_bounds = índex fora de límit diff --git a/res/lang/cs.ini b/res/lang/cs.ini index 701e0b0..6ab669e 100644 --- a/res/lang/cs.ini +++ b/res/lang/cs.ini @@ -1,4 +1,3 @@ -[ly] capslock = capslock err_alloc = alokace paměti selhala err_bounds = index je mimo hranice pole diff --git a/res/lang/de.ini b/res/lang/de.ini index f062273..e18b9ba 100644 --- a/res/lang/de.ini +++ b/res/lang/de.ini @@ -1,4 +1,3 @@ -[ly] capslock = Feststelltaste err_alloc = Speicherzuweisung fehlgeschlagen err_bounds = Listenindex ist außerhalb des Bereichs diff --git a/res/lang/en.ini b/res/lang/en.ini index 0d7d73d..71776e5 100644 --- a/res/lang/en.ini +++ b/res/lang/en.ini @@ -1,4 +1,3 @@ -[ly] capslock = capslock err_alloc = failed memory allocation err_bounds = out-of-bounds index diff --git a/res/lang/es.ini b/res/lang/es.ini index 1f963d1..fabba15 100644 --- a/res/lang/es.ini +++ b/res/lang/es.ini @@ -1,4 +1,3 @@ -[ly] capslock = Bloq Mayús err_alloc = asignación de memoria fallida err_bounds = índice fuera de límites diff --git a/res/lang/fr.ini b/res/lang/fr.ini index daaa27c..ab1cbc2 100644 --- a/res/lang/fr.ini +++ b/res/lang/fr.ini @@ -1,5 +1,4 @@ -[ly] -capslock = verr.maj +capslock = verr.maj err_alloc = échec d'allocation mémoire err_bounds = indice hors-limite err_chdir = échec de l'ouverture du répertoire home diff --git a/res/lang/it.ini b/res/lang/it.ini index 84a3dea..13eb147 100644 --- a/res/lang/it.ini +++ b/res/lang/it.ini @@ -1,4 +1,3 @@ -[ly] capslock = capslock err_alloc = impossibile allocare memoria err_bounds = indice fuori limite diff --git a/res/lang/pl.ini b/res/lang/pl.ini index 460ddef..f37c4a8 100644 --- a/res/lang/pl.ini +++ b/res/lang/pl.ini @@ -1,4 +1,3 @@ -[ly] capslock = capslock err_alloc = nieudana alokacja pamięci err_bounds = indeks poza granicami diff --git a/res/lang/pt.ini b/res/lang/pt.ini index a982eb9..ad2c397 100644 --- a/res/lang/pt.ini +++ b/res/lang/pt.ini @@ -1,4 +1,3 @@ -[ly] capslock = capslock err_alloc = erro na atribuição de memória err_bounds = índice fora de limites diff --git a/res/lang/pt_BR.ini b/res/lang/pt_BR.ini index 68424a7..017129a 100644 --- a/res/lang/pt_BR.ini +++ b/res/lang/pt_BR.ini @@ -1,4 +1,3 @@ -[ly] capslock = caixa alta err_alloc = alocação de memória malsucedida err_bounds = índice fora de limites diff --git a/res/lang/ro.ini b/res/lang/ro.ini index 8c754d6..884e9da 100644 --- a/res/lang/ro.ini +++ b/res/lang/ro.ini @@ -1,4 +1,3 @@ -[ly] capslock = capslock diff --git a/res/lang/ru.ini b/res/lang/ru.ini index 9f0b6fc..356ce5f 100644 --- a/res/lang/ru.ini +++ b/res/lang/ru.ini @@ -1,4 +1,3 @@ -[ly] capslock = capslock err_alloc = не удалось выделить память err_bounds = за пределами индекса diff --git a/res/lang/sr.ini b/res/lang/sr.ini index 59fae5a..2f685e3 100644 --- a/res/lang/sr.ini +++ b/res/lang/sr.ini @@ -1,4 +1,3 @@ -[ly] capslock = capslock err_alloc = neuspijesna alokacija memorije err_bounds = izvan granica indeksa diff --git a/res/lang/sv.ini b/res/lang/sv.ini index 2f4505a..2e40a05 100644 --- a/res/lang/sv.ini +++ b/res/lang/sv.ini @@ -1,4 +1,3 @@ -[ly] capslock = capslock err_alloc = misslyckad minnesallokering err_bounds = utanför banan index diff --git a/res/lang/tr.ini b/res/lang/tr.ini index 885a6db..9bef617 100644 --- a/res/lang/tr.ini +++ b/res/lang/tr.ini @@ -1,4 +1,3 @@ -[ly] capslock = capslock err_alloc = basarisiz bellek ayirma err_bounds = sinirlarin disinda dizin diff --git a/res/lang/uk.ini b/res/lang/uk.ini index bf52fb6..5f5b113 100644 --- a/res/lang/uk.ini +++ b/res/lang/uk.ini @@ -1,4 +1,3 @@ -[ly] capslock = capslock err_alloc = невдале виділення пам'яті err_bounds = поза межами індексу diff --git a/src/config/Config.zig b/src/config/Config.zig index 0790219..84230d8 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -13,8 +13,8 @@ bigclock: bool = false, blank_box: bool = true, border_fg: u8 = 8, clear_password: bool = false, -clock: ?[]const u8 = null, -console_dev: []const u8 = "/dev/console", +clock: ?[:0]const u8 = null, +console_dev: [:0]const u8 = "/dev/console", default_input: Input = .login, fg: u8 = 8, hide_borders: bool = false, diff --git a/src/config/ini.zig b/src/config/ini.zig deleted file mode 100644 index dffbc55..0000000 --- a/src/config/ini.zig +++ /dev/null @@ -1,194 +0,0 @@ -const std = @import("std"); -const ini = @import("ini"); - -const Allocator = std.mem.Allocator; - -const trueOrFalse = std.ComptimeStringMap(bool, .{ .{ "true", true }, .{ "false", false }, .{ "1", true }, .{ "0", false } }); - -pub fn Ini(comptime T: type) type { - return struct { - const Self = @This(); - - data: T, - allocator: std.mem.Allocator, - list: std.ArrayList([]u8), - - pub fn init(allocator: std.mem.Allocator) Self { - return .{ - .data = T{}, - .allocator = allocator, - .list = std.ArrayList([]u8).init(allocator), - }; - } - - pub fn deinit(self: *Self) void { - for (self.list.items) |item| { - self.allocator.free(item); - } - self.list.deinit(); - } - - pub fn readToStruct(self: *Self, path: []const u8) !T { - const file = try std.fs.cwd().openFile(path, .{}); - defer file.close(); - var parser = ini.parse(self.allocator, file.reader()); - defer parser.deinit(); - - var ns: []u8 = &.{}; - defer self.allocator.free(ns); - - while (try parser.next()) |record| { - switch (record) { - .section => |heading| { - ns = try self.allocator.realloc(ns, heading.len); - @memcpy(ns, heading); - std.mem.replaceScalar(u8, ns, ' ', '_'); - }, - .property => |kv| { - inline for (std.meta.fields(T)) |field| { - const field_info = @typeInfo(field.type); - if (field_info == .Struct or (field_info == .Optional and @typeInfo(field_info.Optional.child) == .Struct)) { - if (ns.len != 0 and std.mem.eql(u8, field.name, ns)) { - inline for (std.meta.fields(@TypeOf(@field(self.data, field.name)))) |inner_field| { - if (std.mem.eql(u8, inner_field.name, kv.key)) { - @field(@field(self.data, field.name), inner_field.name) = try self.convert(inner_field.type, kv.value); - } - } - } - } else if (ns.len == 0 and std.mem.eql(u8, field.name, kv.key)) { - @field(self.data, field.name) = try self.convert(field.type, kv.value); - } - } - }, - .enumeration => {}, - } - } - - return self.data; - } - - fn convert(self: *Self, comptime T1: type, val: []const u8) !T1 { - return switch (@typeInfo(T1)) { - .Int, .ComptimeInt => try std.fmt.parseInt(T1, val, 0), - .Float, .ComptimeFloat => try std.fmt.parseFloat(T1, val), - .Bool => trueOrFalse.get(val).?, - .Enum => std.meta.stringToEnum(T1, val).?, - .Optional => |opt| { - if (val.len == 0 or std.mem.eql(u8, val, "null")) return null; - return try self.convert(opt.child, val); - }, - else => { - const a_val = try self.allocator.alloc(u8, val.len); - @memcpy(a_val, val); - try self.list.append(a_val); - return @as(T1, a_val); - }, - }; - } - }; -} - -fn writeProperty(writer: anytype, field_name: []const u8, val: anytype) !void { - switch (@typeInfo(@TypeOf(val))) { - .Bool => { - try writer.print("{s}={d}\n", .{ field_name, @intFromBool(val) }); - }, - .Int, .ComptimeInt, .Float, .ComptimeFloat => { - try writer.print("{s}={d}\n", .{ field_name, val }); - }, - .Enum => { - try writer.print("{s}={s}\n", .{ field_name, @tagName(val) }); - }, - else => { - try writer.print("{s}={s}\n", .{ field_name, val }); - }, - } -} - -fn isDefaultValue(field: anytype, field_value: field.type) bool { - if (field.default_value) |default_value_ao| { - const def_val: *align(field.alignment) const anyopaque = @alignCast(default_value_ao); - const default_value = @as(*const field.type, @ptrCast(def_val)).*; - const field_t_info = @typeInfo(field.type); - if (field_t_info == .Optional) { - if (default_value != null) { - if (field_value != null) { - if (field_t_info == .Pointer) { - return std.mem.eql(field_t_info.Pointer.child, default_value.?, field_value.?); - } else { - return default_value.? == field_value.?; - } - } - return false; - } - return field_value == null; - } - - if (field_t_info == .Pointer) { - return std.mem.eql(field_t_info.Pointer.child, default_value, field_value); - } else { - return default_value == field_value; - } - } - - return false; -} - -pub fn writeFromStruct(data: anytype, writer: anytype, ns: ?[]const u8) !void { - if (@typeInfo(@TypeOf(data)) != .Struct) @compileError("writeFromStruct() requires a struct"); - - var should_write_ns = ns != null; - - inline for (std.meta.fields(@TypeOf(data))) |field| { - switch (@typeInfo(field.type)) { - .Struct => continue, - .Optional => |opt| { - if (@typeInfo(opt.child) != .Struct) { - const val = @field(data, field.name); - if (val) |field_val| { - if (!isDefaultValue(field, @field(data, field.name))) { - if (should_write_ns) { - try writer.print("[{s}]\n", .{ns.?}); - should_write_ns = false; - } - try writeProperty(writer, field.name, field_val); - } - } else if (!isDefaultValue(field, @field(data, field.name))) { - if (should_write_ns) { - try writer.print("[{s}]\n", .{ns.?}); - should_write_ns = false; - } - try writeProperty(writer, field.name, ""); - } - } else continue; - }, - else => { - if (!isDefaultValue(field, @field(data, field.name))) { - if (should_write_ns) { - try writer.print("[{s}]\n", .{ns.?}); - should_write_ns = false; - } - try writeProperty(writer, field.name, @field(data, field.name)); - } - }, - } - } - - if (ns == null) { - inline for (std.meta.fields(@TypeOf(data))) |field| { - switch (@typeInfo(field.type)) { - .Struct => { - try writeFromStruct(@field(data, field.name), writer, field.name); - }, - .Optional => |opt| { - if (@typeInfo(opt.child) == .Struct) { - if (@field(data, field.name)) |inner_data| { - try writeFromStruct(inner_data, writer, field.name); - } - } else continue; - }, - else => continue, - } - } - } -} diff --git a/src/interop.zig b/src/interop.zig index c1b8c22..64f7b83 100644 --- a/src/interop.zig +++ b/src/interop.zig @@ -18,10 +18,6 @@ pub const xcb = @cImport({ @cInclude("xcb/xcb.h"); }); -pub const c_size = u64; -pub const c_uid = u32; -pub const c_gid = u32; -pub const c_time = c_long; pub const tm = extern struct { tm_sec: c_int, tm_min: c_int, @@ -37,8 +33,8 @@ pub const passwd = extern struct { pw_name: [*:0]u8, pw_passwd: [*:0]u8, - pw_uid: c_uid, - pw_gid: c_gid, + pw_uid: u32, + pw_gid: u32, pw_gecos: [*:0]u8, pw_dir: [*:0]u8, pw_shell: [*:0]u8, @@ -68,20 +64,20 @@ pub const O_RDWR: c_uint = 0x02; pub extern "c" fn fileno(stream: *std.c.FILE) c_int; pub extern "c" fn sysconf(name: c_int) c_long; -pub extern "c" fn time(second: ?*c_time) c_time; -pub extern "c" fn localtime(timer: *const c_time) *tm; -pub extern "c" fn strftime(str: [*:0]u8, maxsize: c_size, format: [*:0]const u8, timeptr: *const tm) c_size; +pub extern "c" fn time(second: ?*c_long) c_long; +pub extern "c" fn localtime(timer: *const c_long) *tm; +pub extern "c" fn strftime(str: [*:0]u8, maxsize: u64, format: [*:0]const u8, timeptr: *const tm) u64; pub extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int; pub extern "c" fn getenv(name: [*:0]const u8) [*:0]u8; pub extern "c" fn putenv(name: [*:0]u8) c_int; pub extern "c" fn clearenv() c_int; -pub extern "c" fn getuid() c_uid; +pub extern "c" fn getuid() u32; pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd; pub extern "c" fn endpwent() void; pub extern "c" fn setusershell() void; pub extern "c" fn getusershell() [*:0]u8; pub extern "c" fn endusershell() void; -pub extern "c" fn initgroups(user: [*:0]const u8, group: c_gid) c_int; +pub extern "c" fn initgroups(user: [*:0]const u8, group: u32) c_int; pub extern "c" fn chdir(path: [*:0]const u8) c_int; pub extern "c" fn execl(path: [*:0]const u8, arg: [*:0]const u8, ...) c_int; @@ -93,6 +89,7 @@ pub fn getHostName(allocator: Allocator) !struct { const hostname_max_length: u64 = if (hostname_sysconf < 0) @intCast(_POSIX_HOST_NAME_MAX) else @intCast(hostname_sysconf); const buffer = try allocator.alloc(u8, hostname_max_length); + errdefer allocator.free(buffer); const error_code = std.c.gethostname(buffer.ptr, hostname_max_length); if (error_code < 0) return error.CannotGetHostName; @@ -111,27 +108,21 @@ pub fn getHostName(allocator: Allocator) !struct { }; } -pub fn timeAsString(allocator: Allocator, format: []const u8, max_length: u64) ![:0]u8 { +pub fn timeAsString(allocator: Allocator, format: [:0]const u8, max_length: u64) ![:0]u8 { const timer = time(null); const tm_info = localtime(&timer); const buffer = try allocator.allocSentinel(u8, max_length, 0); - const format_z = try allocator.dupeZ(u8, format); - defer allocator.free(format_z); - - if (strftime(buffer, max_length, format_z, tm_info) < 0) return error.CannotGetFormattedTime; + if (strftime(buffer, max_length, format, tm_info) < 0) return error.CannotGetFormattedTime; return buffer; } -pub fn getLockState(allocator: Allocator, console_dev: []const u8) !struct { +pub fn getLockState(console_dev: [:0]const u8) !struct { numlock: bool, capslock: bool, } { - const console_dev_z = try allocator.dupeZ(u8, console_dev); - defer allocator.free(console_dev_z); - - const fd = std.c.open(console_dev_z, O_RDONLY); + const fd = std.c.open(console_dev, O_RDONLY); if (fd < 0) return error.CannotOpenConsoleDev; var numlock = false; diff --git a/src/main.zig b/src/main.zig index 6b1d0d1..e85f655 100644 --- a/src/main.zig +++ b/src/main.zig @@ -11,17 +11,16 @@ const TerminalBuffer = @import("tui/TerminalBuffer.zig"); const Desktop = @import("tui/components/Desktop.zig"); const Text = @import("tui/components/Text.zig"); const Config = @import("config/Config.zig"); -const ini = @import("config/ini.zig"); +const ini = @import("zigini"); const Lang = @import("config/Lang.zig"); const Save = @import("config/Save.zig"); const ViMode = @import("enums.zig").ViMode; const SharedError = @import("SharedError.zig"); +const utils = @import("tui/utils.zig"); const Ini = ini.Ini; const termbox = interop.termbox; -const LY_VERSION = "1.0.0"; - pub fn signalHandler(i: c_int) callconv(.C) void { termbox.tb_shutdown(); std.c.exit(i); @@ -59,7 +58,7 @@ pub fn main() !void { std.os.exit(0); } if (res.args.version != 0) { - _ = try stderr.write("Ly version " ++ LY_VERSION ++ "\n"); + _ = try stderr.write("Ly version " ++ build_options.version ++ "\n"); std.os.exit(0); } @@ -352,8 +351,11 @@ pub fn main() !void { buffer.drawLabel(lang.password, label_x, label_y + 6); if (info_line.len > 0) { - const x = buffer.box_x + ((buffer.box_width - info_line.len) / 2); - buffer.drawLabel(info_line, x, label_y); + const info_line_width = try utils.strWidth(info_line); + if (buffer.box_width > info_line_width) { + const x = buffer.box_x + ((buffer.box_width - info_line_width) / 2); + buffer.drawLabel(info_line, x, label_y); + } } if (!config.hide_key_hints) { @@ -364,14 +366,16 @@ pub fn main() !void { buffer.drawLabel(" ", length - 1, 0); buffer.drawLabel(lang.shutdown, length, 0); - length += lang.shutdown.len + 1; + const shutdown_len = try utils.strWidth(lang.shutdown); + length += shutdown_len + 1; buffer.drawLabel(config.restart_key, length, 0); length += config.restart_key.len + 1; buffer.drawLabel(" ", length - 1, 0); buffer.drawLabel(lang.restart, length, 0); - length += lang.restart.len + 1; + const restart_len = try utils.strWidth(lang.restart); + length += restart_len + 1; if (config.sleep_cmd != null) { buffer.drawLabel(config.sleep_key, length, 0); @@ -388,7 +392,7 @@ pub fn main() !void { } draw_lock_state: { - const lock_state = interop.getLockState(allocator, config.console_dev) catch |err| { + const lock_state = interop.getLockState(config.console_dev) catch |err| { if (err == error.CannotOpenConsoleDev) { info_line = lang.err_console_dev; } else { diff --git a/src/tui/TerminalBuffer.zig b/src/tui/TerminalBuffer.zig index 51920e2..fd3d11f 100644 --- a/src/tui/TerminalBuffer.zig +++ b/src/tui/TerminalBuffer.zig @@ -161,7 +161,13 @@ pub fn calculateComponentCoordinates(self: TerminalBuffer) struct { pub fn drawLabel(self: TerminalBuffer, text: []const u8, x: u64, y: u64) void { const yc: c_int = @intCast(y); - for (0..text.len) |xx| termbox.tb_change_cell(@intCast(x + xx), yc, text[xx], self.fg, self.bg); + const utf8view = std.unicode.Utf8View.init(text) catch return; + var utf8 = utf8view.iterator(); + + var i = x; + while (utf8.nextCodepoint()) |codepoint| : (i += 1) { + termbox.tb_change_cell(@intCast(i), yc, codepoint, self.fg, self.bg); + } } pub fn drawCharMultiple(self: TerminalBuffer, char: u8, x: u64, y: u64, length: u64) void { diff --git a/src/tui/components/Desktop.zig b/src/tui/components/Desktop.zig index 2e340de..259a865 100644 --- a/src/tui/components/Desktop.zig +++ b/src/tui/components/Desktop.zig @@ -1,9 +1,8 @@ const std = @import("std"); -const ini = @import("ini"); const enums = @import("../../enums.zig"); const interop = @import("../../interop.zig"); const TerminalBuffer = @import("../TerminalBuffer.zig"); -const Ini = @import("../../config/ini.zig").Ini; +const Ini = @import("zigini").Ini; const Allocator = std.mem.Allocator; const EnvironmentList = std.ArrayList(Environment); @@ -74,7 +73,7 @@ pub fn addEnvironment(self: *Desktop, name: []const u8, cmd: []const u8, display .specifier = switch (display_server) { .wayland => "wayland", .x11 => "x11", - else => "other", + else => "", }, .display_server = display_server, }); @@ -91,7 +90,7 @@ pub fn addEnvironmentWithIni(self: *Desktop, entry_ini: Ini(Entry), name: []cons .specifier = switch (display_server) { .wayland => "wayland", .x11 => "x11", - else => "other", + else => "", }, .display_server = display_server, }); diff --git a/src/tui/utils.zig b/src/tui/utils.zig index cb2a7ca..37fe2d0 100644 --- a/src/tui/utils.zig +++ b/src/tui/utils.zig @@ -3,10 +3,20 @@ const interop = @import("../interop.zig"); const termbox = interop.termbox; -pub inline fn initCell(ch: u32, fg: u32, bg: u32) termbox.tb_cell { +pub fn initCell(ch: u32, fg: u32, bg: u32) termbox.tb_cell { return .{ .ch = ch, .fg = fg, .bg = bg, }; } + +// Every codepoint is assumed to have a width of 1. +// Since ly should be running in a tty, this should be fine. +pub fn strWidth(str: []const u8) !u8 { + const utf8view = try std.unicode.Utf8View.init(str); + var utf8 = utf8view.iterator(); + var i: u8 = 0; + while (utf8.nextCodepoint()) |_| i += 1; + return i; +}