mirror of
https://github.com/fairyglade/ly.git
synced 2026-05-06 15:20:36 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8ae126623 | ||
|
|
4db9295102 | ||
|
|
864f5f2892 | ||
|
|
c50af66407 | ||
|
|
fdf241bed5 | ||
|
|
79eebd8ee0 | ||
|
|
3869bfd2f9 | ||
|
|
5905e054c5 |
@@ -23,7 +23,7 @@ comptime {
|
||||
}
|
||||
}
|
||||
|
||||
const ly_version = std.SemanticVersion{ .major = 1, .minor = 4, .patch = 0 };
|
||||
const ly_version = std.SemanticVersion{ .major = 1, .minor = 5, .patch = 0 };
|
||||
|
||||
var dest_directory: []const u8 = undefined;
|
||||
var config_directory: []const u8 = undefined;
|
||||
@@ -72,7 +72,11 @@ pub fn build(b: *std.Build) !void {
|
||||
.use_llvm = true,
|
||||
});
|
||||
|
||||
const ly_ui = b.dependency("ly_ui", .{ .target = target, .optimize = optimize });
|
||||
const ly_ui = b.dependency("ly_ui", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.enable_x11_support = enable_x11_support,
|
||||
});
|
||||
exe.root_module.addImport("ly-ui", ly_ui.module("ly-ui"));
|
||||
|
||||
exe.root_module.addOptions("build_options", build_options);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.{
|
||||
.name = .ly,
|
||||
.version = "1.4.0",
|
||||
.version = "1.5.0",
|
||||
.fingerprint = 0xa148ffcc5dc2cb59,
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
|
||||
@@ -4,6 +4,7 @@ const Translator = @import("translate_c").Translator;
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const enable_x11_support = b.option(bool, "enable_x11_support", "Enable X11 support") orelse true;
|
||||
const mod = b.addModule("ly-core", .{
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
.target = target,
|
||||
@@ -20,7 +21,9 @@ pub fn build(b: *std.Build) void {
|
||||
|
||||
addCImport(b, mod, translate_c, target, optimize, "pam", "#include <security/pam_appl.h>");
|
||||
addCImport(b, mod, translate_c, target, optimize, "utmp", "#include <utmpx.h>");
|
||||
if (enable_x11_support) {
|
||||
addCImport(b, mod, translate_c, target, optimize, "xcb", "#include <xcb/xcb.h>");
|
||||
}
|
||||
if (target.result.os.tag == .freebsd) {
|
||||
addCImport(b, mod, translate_c, target, optimize, "pwd",
|
||||
\\#include <pwd.h>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.{
|
||||
.name = .ly_core,
|
||||
.version = "1.0.0",
|
||||
.version = "1.1.0",
|
||||
.fingerprint = 0xddda7afda795472,
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
|
||||
@@ -3,50 +3,85 @@ const interop = @import("interop.zig");
|
||||
|
||||
const LogFile = @This();
|
||||
|
||||
path: []const u8,
|
||||
maybe_path: ?[]const u8,
|
||||
could_open_log_file: bool = undefined,
|
||||
file: std.Io.File = undefined,
|
||||
maybe_file: ?std.Io.File = null,
|
||||
buffer: []u8,
|
||||
file_writer: std.Io.File.Writer = undefined,
|
||||
maybe_file_writer: ?std.Io.File.Writer = null,
|
||||
|
||||
pub fn init(io: std.Io, path: ?[]const u8, buffer: []u8) !LogFile {
|
||||
var log_file = LogFile{
|
||||
.maybe_path = path,
|
||||
.buffer = buffer,
|
||||
};
|
||||
|
||||
if (path) |p| {
|
||||
log_file.could_open_log_file = try openLogFile(io, p, &log_file);
|
||||
} else {
|
||||
std.posix.system.openlog("ly", 0, 0);
|
||||
log_file.could_open_log_file = true;
|
||||
}
|
||||
|
||||
pub fn init(io: std.Io, path: []const u8, buffer: []u8) !LogFile {
|
||||
var log_file = LogFile{ .path = path, .buffer = buffer };
|
||||
log_file.could_open_log_file = try openLogFile(io, path, &log_file);
|
||||
return log_file;
|
||||
}
|
||||
|
||||
pub fn reinit(self: *LogFile, io: std.Io) !void {
|
||||
self.could_open_log_file = try openLogFile(io, self.path, self);
|
||||
if (self.maybe_path) |path| {
|
||||
self.could_open_log_file = try openLogFile(io, path, self);
|
||||
} else {
|
||||
std.posix.system.openlog("ly", 0, 0);
|
||||
self.could_open_log_file = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *LogFile, io: std.Io) void {
|
||||
self.file.close(io);
|
||||
if (self.maybe_file) |file| {
|
||||
file.close(io);
|
||||
} else {
|
||||
std.posix.system.closelog();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn info(self: *LogFile, io: std.Io, category: []const u8, comptime message: []const u8, args: anytype) !void {
|
||||
if (self.maybe_file_writer) |*writer| {
|
||||
var buffer: [128:0]u8 = undefined;
|
||||
const time = interop.timeAsString(io, &buffer, "%Y-%m-%d %H:%M:%S");
|
||||
|
||||
try self.file_writer.interface.print("{s} [info/{s}] ", .{ time, category });
|
||||
try self.file_writer.interface.print(message, args);
|
||||
try self.file_writer.interface.writeByte('\n');
|
||||
try self.file_writer.interface.flush();
|
||||
try writer.interface.print("{s} [info/{s}] ", .{ time, category });
|
||||
try writer.interface.print(message, args);
|
||||
try writer.interface.writeByte('\n');
|
||||
try writer.interface.flush();
|
||||
} else {
|
||||
var buffer: [1024]u8 = undefined;
|
||||
const slice = try std.fmt.bufPrint(&buffer, message, args);
|
||||
const msg = try std.fmt.bufPrintZ(buffer[slice.len..], "[info/{s}] {s}", .{ category, slice });
|
||||
|
||||
std.posix.system.syslog(std.posix.LOG.INFO, msg.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn err(self: *LogFile, io: std.Io, category: []const u8, comptime message: []const u8, args: anytype) !void {
|
||||
if (self.maybe_file_writer) |*writer| {
|
||||
var buffer: [128:0]u8 = undefined;
|
||||
const time = interop.timeAsString(io, &buffer, "%Y-%m-%d %H:%M:%S");
|
||||
|
||||
try self.file_writer.interface.print("{s} [err/{s}] ", .{ time, category });
|
||||
try self.file_writer.interface.print(message, args);
|
||||
try self.file_writer.interface.writeByte('\n');
|
||||
try self.file_writer.interface.flush();
|
||||
try writer.interface.print("{s} [err/{s}] ", .{ time, category });
|
||||
try writer.interface.print(message, args);
|
||||
try writer.interface.writeByte('\n');
|
||||
try writer.interface.flush();
|
||||
} else {
|
||||
var buffer: [1024]u8 = undefined;
|
||||
const slice = try std.fmt.bufPrint(&buffer, message, args);
|
||||
const msg = try std.fmt.bufPrintZ(buffer[slice.len..], "[info/{s}] {s}", .{ category, slice });
|
||||
|
||||
std.posix.system.syslog(std.posix.LOG.ERR, msg.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
fn openLogFile(io: std.Io, path: []const u8, log_file: *LogFile) !bool {
|
||||
var could_open_log_file = true;
|
||||
open_log_file: {
|
||||
log_file.file = std.Io.Dir.cwd().openFile(io, path, .{ .mode = .write_only }) catch std.Io.Dir.cwd().createFile(io, path, .{ .permissions = .fromMode(0o666) }) catch {
|
||||
log_file.maybe_file = std.Io.Dir.cwd().openFile(io, path, .{ .mode = .write_only }) catch std.Io.Dir.cwd().createFile(io, path, .{ .permissions = .fromMode(0o666) }) catch {
|
||||
// If we could neither open an existing log file nor create a new
|
||||
// one, abort.
|
||||
could_open_log_file = false;
|
||||
@@ -55,17 +90,17 @@ fn openLogFile(io: std.Io, path: []const u8, log_file: *LogFile) !bool {
|
||||
}
|
||||
|
||||
if (!could_open_log_file) {
|
||||
log_file.file = try std.Io.Dir.openFileAbsolute(io, "/dev/null", .{ .mode = .write_only });
|
||||
log_file.maybe_file = try std.Io.Dir.openFileAbsolute(io, "/dev/null", .{ .mode = .write_only });
|
||||
}
|
||||
|
||||
var log_file_writer = log_file.file.writer(io, log_file.buffer);
|
||||
var log_file_writer = log_file.maybe_file.?.writer(io, log_file.buffer);
|
||||
|
||||
// Seek to the end of the log file
|
||||
if (could_open_log_file) {
|
||||
const stat = try log_file.file.stat(io);
|
||||
const stat = try log_file.maybe_file.?.stat(io);
|
||||
try log_file_writer.seekTo(stat.size);
|
||||
}
|
||||
|
||||
log_file.file_writer = log_file_writer;
|
||||
log_file.maybe_file_writer = log_file_writer;
|
||||
return could_open_log_file;
|
||||
}
|
||||
|
||||
@@ -4,13 +4,18 @@ const Translator = @import("translate_c").Translator;
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const enable_x11_support = b.option(bool, "enable_x11_support", "Enable X11 support") orelse true;
|
||||
const mod = b.addModule("ly-ui", .{
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const ly_core = b.dependency("ly_core", .{ .target = target, .optimize = optimize });
|
||||
const ly_core = b.dependency("ly_core", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.enable_x11_support = enable_x11_support,
|
||||
});
|
||||
mod.addImport("ly-core", ly_core.module("ly-core"));
|
||||
|
||||
const termbox_dep = b.dependency("termbox2", .{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.{
|
||||
.name = .ly_ui,
|
||||
.version = "1.0.0",
|
||||
.version = "1.1.0",
|
||||
.fingerprint = 0x8d11bf85a74ec803,
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
|
||||
@@ -226,6 +226,12 @@ You can, of course, still select the init system of your choice when using this
|
||||
|
||||
You can find all the configuration in `/etc/ly/config.ini`. The file is fully commented, and includes the default values.
|
||||
|
||||
You may also check the validity of your configuration file (i.e. if there are any errors in it) with the following command:
|
||||
|
||||
```
|
||||
$ ly --validate-config /etc/ly/config.ini
|
||||
```
|
||||
|
||||
## Controls
|
||||
|
||||
Use the Up/Down arrow keys to change the current field, and the Left/Right arrow keys to scroll through the different fields (whether it be the info line, the desktop environment, or the username). The info line is where messages and errors are displayed.
|
||||
|
||||
@@ -97,6 +97,14 @@ blank_box = true
|
||||
# Border foreground color id
|
||||
border_fg = 0x00FFFFFF
|
||||
|
||||
# Relative horizontal position from the end of the screen
|
||||
# default: 0.5
|
||||
box_position_h = 0.5
|
||||
|
||||
# Relative vertical position from the bottom of the screen
|
||||
# default: 0.4
|
||||
box_position_v = 0.4
|
||||
|
||||
# Title to show at the top of the main box
|
||||
# If set to null, none will be shown
|
||||
box_title = null
|
||||
@@ -291,6 +299,7 @@ login_defs_path = /etc/login.defs
|
||||
logout_cmd = null
|
||||
|
||||
# General log file path
|
||||
# If null, syslog will be used instead
|
||||
ly_log = /var/log/ly.log
|
||||
|
||||
# Main box horizontal margin
|
||||
|
||||
@@ -62,7 +62,7 @@ const Frame = struct {
|
||||
};
|
||||
|
||||
// https://github.com/cmang/durdraw/blob/0.29.0/durformat.md
|
||||
const DurFormat = struct {
|
||||
const DurFormatRaw = struct {
|
||||
allocator: Allocator,
|
||||
formatVersion: ?i64 = null,
|
||||
colorFormat: ?[]const u8 = null,
|
||||
@@ -72,38 +72,48 @@ const DurFormat = struct {
|
||||
lines: ?i64 = null,
|
||||
frames: std.ArrayList(Frame) = undefined,
|
||||
|
||||
pub fn valid(self: *DurFormat) bool {
|
||||
if (self.formatVersion != null and
|
||||
self.colorFormat != null and
|
||||
self.encoding != null and
|
||||
self.framerate != null and
|
||||
self.columns != null and
|
||||
self.lines != null and
|
||||
self.frames.items.len >= 1)
|
||||
{
|
||||
// Validate data and return a valid DurFormat
|
||||
// Consumes `self`, making it unusable after
|
||||
pub fn validate(self: *DurFormatRaw) !DurFormat {
|
||||
// v8 may have breaking changes like changing the colormap xy direction
|
||||
// (https://github.com/cmang/durdraw/issues/24)
|
||||
if (self.formatVersion.? != 7) return false;
|
||||
const format_version = self.formatVersion orelse return error.MissingFieldVersion;
|
||||
if (format_version != 7) return error.UnsupportedVersion;
|
||||
|
||||
const color_format_str = self.colorFormat orelse return error.MissingFieldColorFormat;
|
||||
// Code currently only supports 16 and 256 color format only
|
||||
if (!(eql(u8, "16", self.colorFormat.?) or eql(u8, "256", self.colorFormat.?)))
|
||||
return false;
|
||||
const color_format: DurColorFormat =
|
||||
if (eql(u8, color_format_str, "16")) .@"16" else if (eql(u8, color_format_str, "256")) .@"256" else return error.UnsupportedColorFormat;
|
||||
|
||||
const encoding_str = self.encoding orelse return error.MissingFieldEncoding;
|
||||
// Code currently supports only utf-8 encoding
|
||||
if (!eql(u8, self.encoding.?, "utf-8")) return false;
|
||||
const encoding: DurEncoding = if (eql(u8, encoding_str, "utf-8")) .utf_8 else return error.UnsupportedEncoding;
|
||||
|
||||
if (self.framerate == null) return error.MissingFieldFramerate;
|
||||
if (self.framerate.? <= 0) return error.InvalidFramerate;
|
||||
const framerate: f64 = self.framerate.?;
|
||||
|
||||
// Sanity check on file
|
||||
if (self.columns.? <= 0) return false;
|
||||
if (self.lines.? <= 0) return false;
|
||||
if (self.framerate.? < 0) return false;
|
||||
if (self.columns == null or self.lines == null) return error.MissingDimensions;
|
||||
const columns = std.math.cast(u32, self.columns.?) orelse return error.InvalidColumnCount;
|
||||
const lines = std.math.cast(u32, self.lines.?) orelse return error.InvalidLineCount;
|
||||
|
||||
return true;
|
||||
if (self.frames.items.len == 0) return error.NoFrames;
|
||||
const frames = self.frames;
|
||||
|
||||
return .{
|
||||
.allocator = self.allocator,
|
||||
.formatVersion = format_version,
|
||||
.colorFormat = color_format,
|
||||
.encoding = encoding,
|
||||
.framerate = framerate,
|
||||
.columns = columns,
|
||||
.lines = lines,
|
||||
.frames = frames,
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fn parse_dur_from_json(self: *DurFormat, allocator: Allocator, dur_json_root: Json.Value) !void {
|
||||
fn parse_dur_from_json(self: *DurFormatRaw, allocator: Allocator, dur_json_root: Json.Value) !void {
|
||||
var dur_movie = if (dur_json_root.object.get("DurMovie")) |dm| dm.object else return error.NotValidFile;
|
||||
|
||||
// Depending on the version, a dur file can have different json object names (ie: columns vs sizeX)
|
||||
@@ -150,7 +160,7 @@ const DurFormat = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_from_file(self: *DurFormat, allocator: Allocator, io: std.Io, file_path: []const u8) !void {
|
||||
pub fn create_from_file(self: *DurFormatRaw, allocator: Allocator, io: std.Io, file_path: []const u8) !void {
|
||||
const file_decompressed = try read_decompress_file(allocator, io, file_path);
|
||||
defer allocator.free(file_decompressed);
|
||||
|
||||
@@ -158,20 +168,36 @@ const DurFormat = struct {
|
||||
defer parsed.deinit();
|
||||
|
||||
try parse_dur_from_json(self, allocator, parsed.value);
|
||||
|
||||
if (!self.valid()) {
|
||||
return error.NotValidFile;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(allocator: Allocator) DurFormat {
|
||||
pub fn init(allocator: Allocator) DurFormatRaw {
|
||||
return .{ .allocator = allocator };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *DurFormat) void {
|
||||
pub fn deinit(self: *DurFormatRaw) void {
|
||||
if (self.colorFormat) |str| self.allocator.free(str);
|
||||
if (self.encoding) |str| self.allocator.free(str);
|
||||
}
|
||||
};
|
||||
|
||||
const DurColorFormat = enum {
|
||||
@"16",
|
||||
@"256",
|
||||
};
|
||||
|
||||
const DurEncoding = enum { utf_8 };
|
||||
|
||||
const DurFormat = struct {
|
||||
allocator: Allocator,
|
||||
formatVersion: i64,
|
||||
colorFormat: DurColorFormat,
|
||||
encoding: DurEncoding,
|
||||
framerate: f64,
|
||||
columns: u32,
|
||||
lines: u32,
|
||||
frames: std.ArrayList(Frame),
|
||||
|
||||
pub fn deinit(self: *DurFormat) void {
|
||||
for (self.frames.items) |frame| {
|
||||
frame.deinit(self.allocator);
|
||||
}
|
||||
@@ -324,16 +350,16 @@ offset_alignment: DurOffsetAlignment,
|
||||
offset: IVec2,
|
||||
|
||||
// if the user has an even number of columns or rows, we will default to the left or higher position (e.g. 4 columns center = .x..)
|
||||
fn center(v: u32) i64 {
|
||||
return @intCast((v / 2) + (v % 2));
|
||||
fn center(v: i64) i64 {
|
||||
return @intCast(@divTrunc(v, 2) + @mod(v, 2));
|
||||
}
|
||||
|
||||
fn calc_start_position(terminal_buffer: *TerminalBuffer, dur_movie: *DurFormat, offset_alignment: DurOffsetAlignment, offset: IVec2) IVec2 {
|
||||
const buf_width: u32 = @intCast(terminal_buffer.width);
|
||||
const buf_height: u32 = @intCast(terminal_buffer.height);
|
||||
|
||||
var movie_width: u32 = @intCast(dur_movie.columns.?);
|
||||
var movie_height: u32 = @intCast(dur_movie.lines.?);
|
||||
var movie_width: u32 = dur_movie.columns;
|
||||
var movie_height: u32 = dur_movie.lines;
|
||||
|
||||
if (movie_width > buf_width) movie_width = buf_width;
|
||||
if (movie_height > buf_height) movie_height = buf_height;
|
||||
@@ -357,8 +383,8 @@ fn calc_frame_size(terminal_buffer: *TerminalBuffer, dur_movie: *DurFormat) UVec
|
||||
const buf_width: u32 = @intCast(terminal_buffer.width);
|
||||
const buf_height: u32 = @intCast(terminal_buffer.height);
|
||||
|
||||
const movie_width: u32 = @intCast(dur_movie.columns.?);
|
||||
const movie_height: u32 = @intCast(dur_movie.lines.?);
|
||||
const movie_width: u32 = dur_movie.columns;
|
||||
const movie_height: u32 = dur_movie.lines;
|
||||
|
||||
// Draw only the needed amount if movie smaller than screen. If movie is bigger, we will just draw entire screen
|
||||
const frame_width = if (movie_width < buf_width) movie_width else buf_width;
|
||||
@@ -381,9 +407,10 @@ pub fn init(
|
||||
timeout_sec: u12,
|
||||
frame_delay: u16,
|
||||
) !DurFile {
|
||||
var dur_movie: DurFormat = .init(allocator);
|
||||
var dur_movie_raw: DurFormatRaw = .init(allocator);
|
||||
defer dur_movie_raw.deinit();
|
||||
|
||||
dur_movie.create_from_file(allocator, io, file_path) catch |err| switch (err) {
|
||||
dur_movie_raw.create_from_file(allocator, io, file_path) catch |err| switch (err) {
|
||||
error.FileNotFound => {
|
||||
try log_file.err(io, "tui", "dur_file was not found at: {s}", .{file_path});
|
||||
return err;
|
||||
@@ -395,11 +422,62 @@ pub fn init(
|
||||
else => return err,
|
||||
};
|
||||
|
||||
var dur_movie = dur_movie_raw.validate() catch |err| switch (err) {
|
||||
error.MissingFieldVersion => {
|
||||
try log_file.err(io, "tui", "dur_file loaded was invalid: missing field formatVersion!", .{});
|
||||
return err;
|
||||
},
|
||||
error.UnsupportedVersion => {
|
||||
try log_file.err(io, "tui", "dur_file loaded was invalid: unsupported version ({d})!", .{dur_movie_raw.formatVersion.?});
|
||||
return err;
|
||||
},
|
||||
error.MissingFieldColorFormat => {
|
||||
try log_file.err(io, "tui", "dur_file loaded was invalid: missing field colorFormat!", .{});
|
||||
return err;
|
||||
},
|
||||
error.UnsupportedColorFormat => {
|
||||
try log_file.err(io, "tui", "dur_file loaded was invalid: unsupported colorFormat ({s})!", .{dur_movie_raw.colorFormat.?});
|
||||
return err;
|
||||
},
|
||||
error.MissingFieldEncoding => {
|
||||
try log_file.err(io, "tui", "dur_file loaded was invalid: missing field encoding!", .{});
|
||||
return err;
|
||||
},
|
||||
error.UnsupportedEncoding => {
|
||||
try log_file.err(io, "tui", "dur_file loaded was invalid: unsupported encoding ({s})!", .{dur_movie_raw.encoding.?});
|
||||
return err;
|
||||
},
|
||||
error.MissingFieldFramerate => {
|
||||
try log_file.err(io, "tui", "dur_file loaded was invalid: missing field framerate!", .{});
|
||||
return err;
|
||||
},
|
||||
error.InvalidFramerate => {
|
||||
try log_file.err(io, "tui", "dur_file loaded was invalid: negative framerate value found!", .{});
|
||||
return err;
|
||||
},
|
||||
error.MissingDimensions => {
|
||||
try log_file.err(io, "tui", "dur_file loaded was invalid: missing field(s) lines and/or columns!", .{});
|
||||
return err;
|
||||
},
|
||||
error.InvalidColumnCount => {
|
||||
try log_file.err(io, "tui", "dur_file loaded was invalid: columns value falls outside of supported range ({d})!", .{dur_movie_raw.columns.?});
|
||||
return err;
|
||||
},
|
||||
error.InvalidLineCount => {
|
||||
try log_file.err(io, "tui", "dur_file loaded was invalid: lines value falls outside of supported range ({d})!", .{dur_movie_raw.lines.?});
|
||||
return err;
|
||||
},
|
||||
error.NoFrames => {
|
||||
try log_file.err(io, "tui", "dur_file loaded was invalid: animation has no frames!", .{});
|
||||
return err;
|
||||
},
|
||||
};
|
||||
|
||||
// 4 bit mode with 256 color is unsupported
|
||||
if (!full_color and eql(u8, dur_movie.colorFormat.?, "256")) {
|
||||
if (!full_color and dur_movie.colorFormat == .@"256") {
|
||||
try log_file.err(io, "tui", "dur_file can not be 256 color encoded when not using full_color option!", .{});
|
||||
dur_movie.deinit();
|
||||
return error.InvalidColorFormat;
|
||||
return error.NotFullColor;
|
||||
}
|
||||
|
||||
const offset: IVec2 = .{ x_offset, y_offset };
|
||||
@@ -408,7 +486,7 @@ pub fn init(
|
||||
const frame_size = calc_frame_size(terminal_buffer, &dur_movie);
|
||||
|
||||
// Convert dur fps to frames per ms
|
||||
const frame_time: u32 = @trunc(1000 / dur_movie.framerate.?);
|
||||
const frame_time: u32 = @trunc(1000 / dur_movie.framerate);
|
||||
|
||||
return .{
|
||||
.instance = null,
|
||||
@@ -426,7 +504,7 @@ pub fn init(
|
||||
.frame_delay = frame_delay,
|
||||
.dur_movie = dur_movie,
|
||||
.frame_time = frame_time,
|
||||
.is_color_format_16 = eql(u8, dur_movie.colorFormat.?, "16"),
|
||||
.is_color_format_16 = dur_movie.colorFormat == .@"16",
|
||||
.offset_alignment = offset_alignment,
|
||||
.offset = offset,
|
||||
};
|
||||
|
||||
@@ -417,19 +417,27 @@ fn xauth(log_file: *LogFile, allocator: std.mem.Allocator, io: std.Io, display_n
|
||||
|
||||
const magic_cookie = mcookie(io);
|
||||
|
||||
log_file.deinit(io);
|
||||
|
||||
const pid = std.posix.system.fork();
|
||||
if (pid == 0) {
|
||||
try log_file.reinit(io);
|
||||
|
||||
var cmd_buffer: [1024]u8 = undefined;
|
||||
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . {s}", .{ options.xauth_cmd, display_name, magic_cookie }) catch std.process.exit(1);
|
||||
|
||||
try log_file.info(io, "auth/x11", "executing: {s} -c {s}", .{ shell, cmd_str });
|
||||
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
|
||||
_ = std.posix.system.execve(shell, &args, std.c.environ);
|
||||
|
||||
log_file.deinit(io);
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
var status: c_int = undefined;
|
||||
const result = std.posix.system.waitpid(pid, &status, 0);
|
||||
|
||||
try log_file.reinit(io);
|
||||
if (interop.isError(result) or status != 0) {
|
||||
try log_file.err(
|
||||
io,
|
||||
|
||||
@@ -23,6 +23,8 @@ bigclock_12hr: bool = false,
|
||||
bigclock_seconds: bool = false,
|
||||
blank_box: bool = true,
|
||||
border_fg: u32 = 0x00FFFFFF,
|
||||
box_position_h: f32 = 0.5,
|
||||
box_position_v: f32 = 0.4,
|
||||
box_title: ?[]const u8 = null,
|
||||
brightness_down_cmd: [:0]const u8 = build_options.prefix_directory ++ "/bin/brightnessctl -q -n s 10%-",
|
||||
brightness_down_key: ?[]const u8 = "F5",
|
||||
@@ -72,7 +74,7 @@ lang: []const u8 = "en",
|
||||
login_cmd: ?[]const u8 = null,
|
||||
login_defs_path: []const u8 = "/etc/login.defs",
|
||||
logout_cmd: ?[]const u8 = null,
|
||||
ly_log: []const u8 = "/var/log/ly.log",
|
||||
ly_log: ?[]const u8 = "/var/log/ly.log",
|
||||
margin_box_h: u8 = 2,
|
||||
margin_box_v: u8 = 1,
|
||||
numlock: bool = false,
|
||||
|
||||
77
src/main.zig
77
src/main.zig
@@ -146,12 +146,10 @@ pub fn main(init: std.process.Init) !void {
|
||||
// If we can't shutdown or restart due to an error, we print it to standard error. If that fails, just bail out
|
||||
if (shutdown) {
|
||||
const shutdown_error = std.process.replace(state.io, .{ .argv = &[_][]const u8{ "/bin/sh", "-c", shutdown_cmd } });
|
||||
stderr.print("error: couldn't shutdown: {s}\n", .{@errorName(shutdown_error)}) catch std.process.exit(1);
|
||||
stderr.flush() catch std.process.exit(1);
|
||||
std.log.err("couldn't shutdown: {s}", .{@errorName(shutdown_error)});
|
||||
} else if (restart) {
|
||||
const restart_error = std.process.replace(state.io, .{ .argv = &[_][]const u8{ "/bin/sh", "-c", restart_cmd } });
|
||||
stderr.print("error: couldn't restart: {s}\n", .{@errorName(restart_error)}) catch std.process.exit(1);
|
||||
stderr.flush() catch std.process.exit(1);
|
||||
std.log.err("couldn't restart: {s}", .{@errorName(restart_error)});
|
||||
} else {
|
||||
// The user has quit Ly using Ctrl+C
|
||||
if (commands_allocated) {
|
||||
@@ -172,7 +170,8 @@ pub fn main(init: std.process.Init) !void {
|
||||
\\-h, --help Shows all commands.
|
||||
\\-v, --version Shows the version of Ly.
|
||||
\\-c, --config <str> Overrides the default configuration path. Example: --config /usr/share/ly
|
||||
\\--use-kmscon-vt Use KMSCON instead of kernel VT
|
||||
\\--use-kmscon-vt Uses KMSCON instead of the kernel VT.
|
||||
\\--validate-config <str> Validates the given configuration file.
|
||||
);
|
||||
|
||||
var diag = clap.Diagnostic{};
|
||||
@@ -200,17 +199,39 @@ pub fn main(init: std.process.Init) !void {
|
||||
if (res.args.help != 0) {
|
||||
try clap.help(stderr, clap.Help, ¶ms, .{});
|
||||
|
||||
_ = try stderr.write("Note: if you want to configure Ly, please check the config file, which is located at " ++ build_options.config_directory ++ "/ly/config.ini.\n");
|
||||
try stderr.flush();
|
||||
std.log.info("note: if you want to configure Ly, please check the config file, which is located at " ++ build_options.config_directory ++ "/ly/config.ini.", .{});
|
||||
std.process.exit(0);
|
||||
}
|
||||
if (res.args.version != 0) {
|
||||
_ = try stderr.write("Ly version " ++ build_options.version ++ "\n");
|
||||
try stderr.flush();
|
||||
std.log.info("ly version " ++ build_options.version, .{});
|
||||
std.process.exit(0);
|
||||
}
|
||||
if (res.args.config) |path| config_parent_path = path;
|
||||
if (res.args.@"use-kmscon-vt" != 0) state.use_kmscon_vt = true;
|
||||
if (res.args.@"validate-config") |path| {
|
||||
var parser = try IniParser(Config).init(
|
||||
state.allocator,
|
||||
state.io,
|
||||
path,
|
||||
migrator.configFieldHandler,
|
||||
);
|
||||
defer parser.deinit();
|
||||
|
||||
for (parser.errors.items) |err| {
|
||||
std.log.err(
|
||||
"failed to convert value '{s}' of option '{s}' to type '{s}': {s}",
|
||||
.{ err.value, err.key, err.type_name, err.error_name },
|
||||
);
|
||||
}
|
||||
|
||||
if (parser.maybe_load_error) |err| {
|
||||
std.log.err("failed to load config file: {s}", .{@errorName(err)});
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
std.log.info("no errors detected!", .{});
|
||||
std.process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Load configuration file
|
||||
@@ -2025,22 +2046,36 @@ fn positionWidgets(ptr: *anyopaque) !void {
|
||||
.childrenPosition()
|
||||
.removeX(TerminalBuffer.strWidth(state.lang.numlock) + TerminalBuffer.strWidth(state.lang.capslock) + 1));
|
||||
|
||||
state.box.positionXY(TerminalBuffer.START_POSITION
|
||||
.addX((state.buffer.width - @min(state.buffer.width - 2, state.box.width)) / 2)
|
||||
.addY((state.buffer.height - @min(state.buffer.height - 2, state.box.height)) / 2));
|
||||
var bb_height = state.box.height;
|
||||
var bb_width = state.box.width;
|
||||
const clock_text_len = TerminalBuffer.strWidth(state.bigclock_label.text) * (BigLabel.CHAR_WIDTH + 1);
|
||||
|
||||
if (state.config.bigclock != .none) {
|
||||
const half_width = state.buffer.width / 2;
|
||||
const half_label_width = (TerminalBuffer.strWidth(state.bigclock_label.text) * (BigLabel.CHAR_WIDTH + 1)) / 2;
|
||||
const half_height = (if (state.buffer.height > state.box.height) state.buffer.height - state.box.height else state.buffer.height) / 2;
|
||||
|
||||
state.bigclock_label.positionXY(TerminalBuffer.START_POSITION
|
||||
.addX(half_width)
|
||||
.removeXIf(half_label_width, half_width > half_label_width)
|
||||
.addY(half_height)
|
||||
.removeYIf(BigLabel.CHAR_HEIGHT + 2, half_height > BigLabel.CHAR_HEIGHT + 2));
|
||||
bb_height += BigLabel.CHAR_HEIGHT + 2;
|
||||
bb_width = @max(bb_width, clock_text_len);
|
||||
}
|
||||
|
||||
const max_v_position: f32 = @floatFromInt(state.buffer.height - bb_height - 1);
|
||||
const max_h_position: f32 = @floatFromInt(state.buffer.width - bb_width - 1);
|
||||
|
||||
bb_height = @min(bb_height, state.buffer.height - 2);
|
||||
bb_width = @min(bb_width, state.buffer.width - 2);
|
||||
|
||||
const v_space: f32 = @floatFromInt(state.buffer.height - bb_height);
|
||||
const v_position: usize = @intFromFloat(std.math.clamp(v_space * state.config.box_position_v, 1.0, max_v_position));
|
||||
const h_space: f32 = @floatFromInt(state.buffer.width - bb_width);
|
||||
const h_position: usize = @intFromFloat(std.math.clamp(h_space * state.config.box_position_h, 1.0, max_h_position));
|
||||
|
||||
if (state.config.bigclock != .none) {
|
||||
state.bigclock_label.positionXY(TerminalBuffer.START_POSITION
|
||||
.addX(h_position + (bb_width - clock_text_len) / 2)
|
||||
.addY(v_position));
|
||||
}
|
||||
|
||||
state.box.positionXY(TerminalBuffer.START_POSITION
|
||||
.addX(h_position + (bb_width - state.box.width) / 2)
|
||||
.addY(v_position + (bb_height - state.box.height)));
|
||||
|
||||
state.info_line.label.positionY(state.box
|
||||
.childrenPosition());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user