From 2b46a8179653fa694acf000ee772000ca254c60d Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Thu, 5 Feb 2026 15:01:45 +0100 Subject: [PATCH] Add basic KMSCON support (closes #886) It's not perfect yet, but at least it works! Signed-off-by: AnErrupTion --- build.zig | 3 +++ res/config.ini | 5 +++-- res/ly-kmsconvt@.service | 17 +++++++++++++++++ src/auth.zig | 12 +++++++++--- src/main.zig | 4 ++++ 5 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 res/ly-kmsconvt@.service diff --git a/build.zig b/build.zig index d59d0d1..1688603 100644 --- a/build.zig +++ b/build.zig @@ -269,6 +269,9 @@ fn install_service(allocator: std.mem.Allocator, patch_map: PatchMap) !void { const patched_service = try patchFile(allocator, "res/ly@.service", patch_map); try installText(patched_service, service_dir, service_path, "ly@.service", .{ .mode = 0o644 }); + + const patched_kmsconvt_service = try patchFile(allocator, "res/ly-kmsconvt@.service", patch_map); + try installText(patched_kmsconvt_service, service_dir, service_path, "ly-kmsconvt@.service", .{ .mode = 0o644 }); }, .openrc => { const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" }); diff --git a/res/config.ini b/res/config.ini index f2793d4..5a10e35 100644 --- a/res/config.ini +++ b/res/config.ini @@ -316,8 +316,9 @@ service_name = ly # Session log file path # This will contain stdout and stderr of Wayland sessions # By default it's saved in the user's home directory -# Important: due to technical limitations, X11 and shell sessions aren't supported, which -# means you won't get any logs from those sessions. +# Important: due to technical limitations, X11, shell sessions as well as +# launching session via KMSCON aren't supported, which means you won't get any +# logs from those sessions. # If null, no session log will be created session_log = .local/state/ly-session.log diff --git a/res/ly-kmsconvt@.service b/res/ly-kmsconvt@.service new file mode 100644 index 0000000..e6212c0 --- /dev/null +++ b/res/ly-kmsconvt@.service @@ -0,0 +1,17 @@ +[Unit] +Description=TUI display manager using KMSCON +After=systemd-user-sessions.service plymouth-quit-wait.service +After=kmsconvt@%i.service +Conflicts=kmsconvt@%i.service + +[Service] +ExecStart=$PREFIX_DIRECTORY/bin/kmscon --vt=%I --seats=seat0 --login -- $PREFIX_DIRECTORY/bin/ly --use-kmscon-vt +StandardInput=tty +UtmpIdentifier=%I +TTYPath=/dev/%I +TTYReset=yes +TTYVHangup=yes +TTYVTDisallocate=yes + +[Install] +WantedBy=multi-user.target diff --git a/src/auth.zig b/src/auth.zig index 9e90021..ddf34f9 100644 --- a/src/auth.zig +++ b/src/auth.zig @@ -22,6 +22,7 @@ pub const AuthOptions = struct { x_cmd: []const u8, x_vt: ?u8, session_pid: std.posix.pid_t, + use_kmscon_vt: bool, }; var xorg_pid: std.posix.pid_t = 0; @@ -475,7 +476,7 @@ fn executeX11Cmd(log_file: *LogFile, allocator: std.mem.Allocator, shell: []cons xorg_pid = try std.posix.fork(); if (xorg_pid == 0) { var cmd_buffer: [1024]u8 = undefined; - const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", desktop_cmd }) catch std.process.exit(1); + const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s} {s}", .{ if (options.use_kmscon_vt) "kmscon-launch-gui" else "", options.setup_cmd, options.login_cmd orelse "", desktop_cmd }) catch std.process.exit(1); try log_file.info("auth/x11", "executing: {s} -c {s}", .{ shell, cmd_str }); const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str }; @@ -508,7 +509,12 @@ fn executeCmd(global_log_file: *LogFile, allocator: std.mem.Allocator, shell: [] try global_log_file.info("auth/sys", "launching wayland/shell/custom session", .{}); var maybe_log_file: ?std.fs.File = null; - if (!is_terminal) { + if (!is_terminal) redirect_streams: { + if (options.use_kmscon_vt) { + try global_log_file.err("auth/sys", "cannot redirect stdio & stderr with kmscon", .{}); + break :redirect_streams; + } + // For custom desktop entries, the "Terminal" value here determines if // we redirect standard output & error or not. That is, we redirect only // if it's equal to false (so if it's not running in a TTY). @@ -523,7 +529,7 @@ fn executeCmd(global_log_file: *LogFile, allocator: std.mem.Allocator, shell: [] defer allocator.free(shell_z); var cmd_buffer: [1024]u8 = undefined; - const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", exec_cmd orelse shell }); + const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s} {s}", .{ if (!is_terminal and options.use_kmscon_vt) "kmscon-launch-gui" else "", options.setup_cmd, options.login_cmd orelse "", exec_cmd orelse shell }); try global_log_file.info("auth/sys", "executing: {s} -c {s}", .{ shell, cmd_str }); const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str }; diff --git a/src/main.zig b/src/main.zig index 9bbddfb..f2e2fcd 100644 --- a/src/main.zig +++ b/src/main.zig @@ -126,6 +126,7 @@ pub fn main() !void { \\-h, --help Shows all commands. \\-v, --version Shows the version of Ly. \\-c, --config Overrides the default configuration path. Example: --config /usr/share/ly + \\--use-kmscon-vt Use KMSCON instead of kernel VT ); var diag = clap.Diagnostic{}; @@ -141,6 +142,7 @@ pub fn main() !void { var old_save_parser: ?IniParser(OldSave) = null; defer if (old_save_parser) |*str| str.deinit(); + var use_kmscon_vt = false; var start_cmd_exit_code: u8 = 0; var saved_users = SavedUsers.init(); @@ -161,6 +163,7 @@ pub fn main() !void { std.process.exit(0); } if (res.args.config) |path| config_parent_path = path; + if (res.args.@"use-kmscon-vt" != 0) use_kmscon_vt = true; } // Load configuration file @@ -845,6 +848,7 @@ pub fn main() !void { .x_cmd = config.x_cmd, .x_vt = config.x_vt, .session_pid = session_pid, + .use_kmscon_vt = use_kmscon_vt, }; // Signal action to give up control on the TTY