mirror of
https://github.com/fairyglade/ly.git
synced 2025-12-20 19:24:53 +00:00
Add generic cyclable label & base session component off it
Signed-off-by: AnErrupTion <anerruption@disroot.org>
This commit is contained in:
@@ -4,7 +4,7 @@ const builtin = @import("builtin");
|
|||||||
const enums = @import("enums.zig");
|
const enums = @import("enums.zig");
|
||||||
const interop = @import("interop.zig");
|
const interop = @import("interop.zig");
|
||||||
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
|
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
|
||||||
const Desktop = @import("tui/components/Desktop.zig");
|
const Session = @import("tui/components/Session.zig");
|
||||||
const Text = @import("tui/components/Text.zig");
|
const Text = @import("tui/components/Text.zig");
|
||||||
const Config = @import("config/Config.zig");
|
const Config = @import("config/Config.zig");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
@@ -22,7 +22,7 @@ pub fn sessionSignalHandler(i: c_int) callconv(.C) void {
|
|||||||
if (child_pid > 0) _ = std.c.kill(child_pid, i);
|
if (child_pid > 0) _ = std.c.kill(child_pid, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authenticate(config: Config, current_environment: Desktop.Environment, login: [:0]const u8, password: [:0]const u8) !void {
|
pub fn authenticate(config: Config, current_environment: Session.Environment, login: [:0]const u8, password: [:0]const u8) !void {
|
||||||
var tty_buffer: [3]u8 = undefined;
|
var tty_buffer: [3]u8 = undefined;
|
||||||
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty});
|
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty});
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ fn startSession(
|
|||||||
config: Config,
|
config: Config,
|
||||||
pwd: *std.c.passwd,
|
pwd: *std.c.passwd,
|
||||||
handle: ?*interop.pam.pam_handle,
|
handle: ?*interop.pam.pam_handle,
|
||||||
current_environment: Desktop.Environment,
|
current_environment: Session.Environment,
|
||||||
) !void {
|
) !void {
|
||||||
const status = interop.initgroups(pwd.pw_name.?, pwd.pw_gid);
|
const status = interop.initgroups(pwd.pw_name.?, pwd.pw_gid);
|
||||||
if (status != 0) return error.GroupInitializationFailed;
|
if (status != 0) return error.GroupInitializationFailed;
|
||||||
|
|||||||
32
src/main.zig
32
src/main.zig
@@ -9,7 +9,7 @@ const interop = @import("interop.zig");
|
|||||||
const Doom = @import("animations/Doom.zig");
|
const Doom = @import("animations/Doom.zig");
|
||||||
const Matrix = @import("animations/Matrix.zig");
|
const Matrix = @import("animations/Matrix.zig");
|
||||||
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
|
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
|
||||||
const Desktop = @import("tui/components/Desktop.zig");
|
const Session = @import("tui/components/Session.zig");
|
||||||
const Text = @import("tui/components/Text.zig");
|
const Text = @import("tui/components/Text.zig");
|
||||||
const InfoLine = @import("tui/components/InfoLine.zig");
|
const InfoLine = @import("tui/components/InfoLine.zig");
|
||||||
const Config = @import("config/Config.zig");
|
const Config = @import("config/Config.zig");
|
||||||
@@ -235,23 +235,23 @@ pub fn main() !void {
|
|||||||
var buffer = TerminalBuffer.init(config, labels_max_length, random);
|
var buffer = TerminalBuffer.init(config, labels_max_length, random);
|
||||||
|
|
||||||
// Initialize components
|
// Initialize components
|
||||||
var desktop = try Desktop.init(allocator, &buffer, config.max_desktop_len, lang);
|
var session = try Session.init(allocator, &buffer, config.max_desktop_len, lang);
|
||||||
defer desktop.deinit();
|
defer session.deinit();
|
||||||
|
|
||||||
desktop.addEnvironment(.{ .Name = lang.shell }, "", .shell) catch {
|
session.addEnvironment(.{ .Name = lang.shell }, "", .shell) catch {
|
||||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (build_options.enable_x11_support) {
|
if (build_options.enable_x11_support) {
|
||||||
if (config.xinitrc) |xinitrc| {
|
if (config.xinitrc) |xinitrc| {
|
||||||
desktop.addEnvironment(.{ .Name = lang.xinitrc, .Exec = xinitrc }, "", .xinitrc) catch {
|
session.addEnvironment(.{ .Name = lang.xinitrc, .Exec = xinitrc }, "", .xinitrc) catch {
|
||||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try desktop.crawl(config.waylandsessions, .wayland);
|
try session.crawl(config.waylandsessions, .wayland);
|
||||||
if (build_options.enable_x11_support) try desktop.crawl(config.xsessions, .x11);
|
if (build_options.enable_x11_support) try session.crawl(config.xsessions, .x11);
|
||||||
|
|
||||||
var login = try Text.init(allocator, &buffer, config.max_login_len, false, null);
|
var login = try Text.init(allocator, &buffer, config.max_login_len, false, null);
|
||||||
defer login.deinit();
|
defer login.deinit();
|
||||||
@@ -272,7 +272,7 @@ pub fn main() !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (save.session_index) |session_index| {
|
if (save.session_index) |session_index| {
|
||||||
if (session_index < desktop.environments.items.len) desktop.current = session_index;
|
if (session_index < session.label.list.items.len) session.label.current = session_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,12 +281,12 @@ pub fn main() !void {
|
|||||||
buffer.drawBoxCenter(!config.hide_borders, config.blank_box);
|
buffer.drawBoxCenter(!config.hide_borders, config.blank_box);
|
||||||
|
|
||||||
const coordinates = buffer.calculateComponentCoordinates();
|
const coordinates = buffer.calculateComponentCoordinates();
|
||||||
desktop.position(coordinates.x, coordinates.y + 2, coordinates.visible_length);
|
session.label.position(coordinates.x, coordinates.y + 2, coordinates.visible_length);
|
||||||
login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length);
|
login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length);
|
||||||
password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length);
|
password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length);
|
||||||
|
|
||||||
switch (active_input) {
|
switch (active_input) {
|
||||||
.session => desktop.handle(null, insert_mode),
|
.session => session.label.handle(null, insert_mode),
|
||||||
.login => login.handle(null, insert_mode) catch {
|
.login => login.handle(null, insert_mode) catch {
|
||||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||||
},
|
},
|
||||||
@@ -404,7 +404,7 @@ pub fn main() !void {
|
|||||||
|
|
||||||
if (resolution_changed) {
|
if (resolution_changed) {
|
||||||
const coordinates = buffer.calculateComponentCoordinates();
|
const coordinates = buffer.calculateComponentCoordinates();
|
||||||
desktop.position(coordinates.x, coordinates.y + 2, coordinates.visible_length);
|
session.label.position(coordinates.x, coordinates.y + 2, coordinates.visible_length);
|
||||||
login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length);
|
login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length);
|
||||||
password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length);
|
password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length);
|
||||||
|
|
||||||
@@ -412,7 +412,7 @@ pub fn main() !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (active_input) {
|
switch (active_input) {
|
||||||
.session => desktop.handle(null, insert_mode),
|
.session => session.label.handle(null, insert_mode),
|
||||||
.login => login.handle(null, insert_mode) catch {
|
.login => login.handle(null, insert_mode) catch {
|
||||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||||
},
|
},
|
||||||
@@ -506,7 +506,7 @@ pub fn main() !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
desktop.draw();
|
session.label.draw();
|
||||||
login.draw();
|
login.draw();
|
||||||
password.draw();
|
password.draw();
|
||||||
} else {
|
} else {
|
||||||
@@ -631,7 +631,7 @@ pub fn main() !void {
|
|||||||
|
|
||||||
const save_data = Save{
|
const save_data = Save{
|
||||||
.user = login.text.items,
|
.user = login.text.items,
|
||||||
.session_index = desktop.current,
|
.session_index = session.label.current,
|
||||||
};
|
};
|
||||||
ini.writeFromStruct(save_data, file.writer(), null, true, .{}) catch break :save_last_settings;
|
ini.writeFromStruct(save_data, file.writer(), null, true, .{}) catch break :save_last_settings;
|
||||||
}
|
}
|
||||||
@@ -652,7 +652,7 @@ pub fn main() !void {
|
|||||||
|
|
||||||
session_pid = try std.posix.fork();
|
session_pid = try std.posix.fork();
|
||||||
if (session_pid == 0) {
|
if (session_pid == 0) {
|
||||||
const current_environment = desktop.environments.items[desktop.current];
|
const current_environment = session.label.list.items[session.label.current];
|
||||||
auth.authenticate(config, current_environment, login_text, password_text) catch |err| {
|
auth.authenticate(config, current_environment, login_text, password_text) catch |err| {
|
||||||
shared_err.writeError(err);
|
shared_err.writeError(err);
|
||||||
std.process.exit(1);
|
std.process.exit(1);
|
||||||
@@ -715,7 +715,7 @@ pub fn main() !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (active_input) {
|
switch (active_input) {
|
||||||
.session => desktop.handle(&event, insert_mode),
|
.session => session.label.handle(&event, insert_mode),
|
||||||
.login => login.handle(&event, insert_mode) catch {
|
.login => login.handle(&event, insert_mode) catch {
|
||||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,223 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const enums = @import("../../enums.zig");
|
|
||||||
const interop = @import("../../interop.zig");
|
|
||||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
|
||||||
const Ini = @import("zigini").Ini;
|
|
||||||
const Lang = @import("../../config/Lang.zig");
|
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const EnvironmentList = std.ArrayList(Environment);
|
|
||||||
|
|
||||||
const DisplayServer = enums.DisplayServer;
|
|
||||||
|
|
||||||
const termbox = interop.termbox;
|
|
||||||
|
|
||||||
const Desktop = @This();
|
|
||||||
|
|
||||||
pub const Environment = struct {
|
|
||||||
entry_ini: ?Ini(Entry) = null,
|
|
||||||
name: [:0]const u8 = "",
|
|
||||||
xdg_session_desktop: [:0]const u8 = "",
|
|
||||||
xdg_desktop_names: ?[:0]const u8 = "",
|
|
||||||
cmd: []const u8 = "",
|
|
||||||
specifier: []const u8 = "",
|
|
||||||
display_server: DisplayServer = .wayland,
|
|
||||||
};
|
|
||||||
|
|
||||||
const DesktopEntry = struct {
|
|
||||||
Exec: []const u8 = "",
|
|
||||||
Name: [:0]const u8 = "",
|
|
||||||
DesktopNames: ?[]const u8 = null,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Entry = struct { @"Desktop Entry": DesktopEntry = DesktopEntry{} };
|
|
||||||
|
|
||||||
allocator: Allocator,
|
|
||||||
buffer: *TerminalBuffer,
|
|
||||||
environments: EnvironmentList,
|
|
||||||
current: usize,
|
|
||||||
visible_length: usize,
|
|
||||||
x: usize,
|
|
||||||
y: usize,
|
|
||||||
lang: Lang,
|
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: usize, lang: Lang) !Desktop {
|
|
||||||
return .{
|
|
||||||
.allocator = allocator,
|
|
||||||
.buffer = buffer,
|
|
||||||
.environments = try EnvironmentList.initCapacity(allocator, max_length),
|
|
||||||
.current = 0,
|
|
||||||
.visible_length = 0,
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
.lang = lang,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: Desktop) void {
|
|
||||||
for (self.environments.items) |*environment| {
|
|
||||||
if (environment.entry_ini) |*entry_ini| entry_ini.deinit();
|
|
||||||
if (environment.xdg_desktop_names) |desktop_name| self.allocator.free(desktop_name);
|
|
||||||
self.allocator.free(environment.xdg_session_desktop);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.environments.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn position(self: *Desktop, x: usize, y: usize, visible_length: usize) void {
|
|
||||||
self.x = x;
|
|
||||||
self.y = y;
|
|
||||||
self.visible_length = visible_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn addEnvironment(self: *Desktop, entry: DesktopEntry, xdg_session_desktop: []const u8, display_server: DisplayServer) !void {
|
|
||||||
var xdg_desktop_names: ?[:0]const u8 = null;
|
|
||||||
if (entry.DesktopNames) |desktop_names| {
|
|
||||||
const desktop_names_z = try self.allocator.dupeZ(u8, desktop_names);
|
|
||||||
for (desktop_names_z) |*c| {
|
|
||||||
if (c.* == ';') c.* = ':';
|
|
||||||
}
|
|
||||||
xdg_desktop_names = desktop_names_z;
|
|
||||||
}
|
|
||||||
|
|
||||||
errdefer {
|
|
||||||
if (xdg_desktop_names) |desktop_names| self.allocator.free(desktop_names);
|
|
||||||
}
|
|
||||||
|
|
||||||
const session_desktop = try self.allocator.dupeZ(u8, xdg_session_desktop);
|
|
||||||
errdefer self.allocator.free(session_desktop);
|
|
||||||
|
|
||||||
try self.environments.append(.{
|
|
||||||
.entry_ini = null,
|
|
||||||
.name = entry.Name,
|
|
||||||
.xdg_session_desktop = session_desktop,
|
|
||||||
.xdg_desktop_names = xdg_desktop_names,
|
|
||||||
.cmd = entry.Exec,
|
|
||||||
.specifier = switch (display_server) {
|
|
||||||
.wayland => self.lang.wayland,
|
|
||||||
.x11 => self.lang.x11,
|
|
||||||
else => self.lang.other,
|
|
||||||
},
|
|
||||||
.display_server = display_server,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.current = self.environments.items.len - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn addEnvironmentWithIni(self: *Desktop, entry_ini: Ini(Entry), xdg_session_desktop: []const u8, display_server: DisplayServer) !void {
|
|
||||||
const entry = entry_ini.data.@"Desktop Entry";
|
|
||||||
var xdg_desktop_names: ?[:0]const u8 = null;
|
|
||||||
if (entry.DesktopNames) |desktop_names| {
|
|
||||||
const desktop_names_z = try self.allocator.dupeZ(u8, desktop_names);
|
|
||||||
for (desktop_names_z) |*c| {
|
|
||||||
if (c.* == ';') c.* = ':';
|
|
||||||
}
|
|
||||||
xdg_desktop_names = desktop_names_z;
|
|
||||||
}
|
|
||||||
|
|
||||||
errdefer {
|
|
||||||
if (xdg_desktop_names) |desktop_names| self.allocator.free(desktop_names);
|
|
||||||
}
|
|
||||||
|
|
||||||
const session_desktop = try self.allocator.dupeZ(u8, xdg_session_desktop);
|
|
||||||
errdefer self.allocator.free(session_desktop);
|
|
||||||
|
|
||||||
try self.environments.append(.{
|
|
||||||
.entry_ini = entry_ini,
|
|
||||||
.name = entry.Name,
|
|
||||||
.xdg_session_desktop = session_desktop,
|
|
||||||
.xdg_desktop_names = xdg_desktop_names,
|
|
||||||
.cmd = entry.Exec,
|
|
||||||
.specifier = switch (display_server) {
|
|
||||||
.wayland => self.lang.wayland,
|
|
||||||
.x11 => self.lang.x11,
|
|
||||||
else => self.lang.other,
|
|
||||||
},
|
|
||||||
.display_server = display_server,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.current = self.environments.items.len - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn crawl(self: *Desktop, path: []const u8, display_server: DisplayServer) !void {
|
|
||||||
var iterable_directory = std.fs.openDirAbsolute(path, .{ .iterate = true }) catch return;
|
|
||||||
defer iterable_directory.close();
|
|
||||||
|
|
||||||
var iterator = iterable_directory.iterate();
|
|
||||||
while (try iterator.next()) |item| {
|
|
||||||
if (!std.mem.eql(u8, std.fs.path.extension(item.name), ".desktop")) continue;
|
|
||||||
|
|
||||||
const entry_path = try std.fmt.allocPrint(self.allocator, "{s}/{s}", .{ path, item.name });
|
|
||||||
defer self.allocator.free(entry_path);
|
|
||||||
var entry_ini = Ini(Entry).init(self.allocator);
|
|
||||||
_ = try entry_ini.readFileToStruct(entry_path, "#", null);
|
|
||||||
errdefer entry_ini.deinit();
|
|
||||||
|
|
||||||
var xdg_session_desktop: []const u8 = undefined;
|
|
||||||
const maybe_desktop_names = entry_ini.data.@"Desktop Entry".DesktopNames;
|
|
||||||
if (maybe_desktop_names) |desktop_names| {
|
|
||||||
xdg_session_desktop = std.mem.sliceTo(desktop_names, ';');
|
|
||||||
} else {
|
|
||||||
// if DesktopNames is empty, we'll take the name of the session file
|
|
||||||
xdg_session_desktop = std.fs.path.stem(item.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.addEnvironmentWithIni(entry_ini, xdg_session_desktop, display_server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle(self: *Desktop, maybe_event: ?*termbox.tb_event, insert_mode: bool) void {
|
|
||||||
if (maybe_event) |event| blk: {
|
|
||||||
if (event.type != termbox.TB_EVENT_KEY) break :blk;
|
|
||||||
|
|
||||||
switch (event.key) {
|
|
||||||
termbox.TB_KEY_ARROW_LEFT, termbox.TB_KEY_CTRL_H => self.goLeft(),
|
|
||||||
termbox.TB_KEY_ARROW_RIGHT, termbox.TB_KEY_CTRL_L => self.goRight(),
|
|
||||||
else => {
|
|
||||||
if (!insert_mode) {
|
|
||||||
switch (event.ch) {
|
|
||||||
'h' => self.goLeft(),
|
|
||||||
'l' => self.goRight(),
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = termbox.tb_set_cursor(@intCast(self.x + 2), @intCast(self.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw(self: Desktop) void {
|
|
||||||
const environment = self.environments.items[self.current];
|
|
||||||
|
|
||||||
const length = @min(environment.name.len, self.visible_length - 3);
|
|
||||||
if (length == 0) return;
|
|
||||||
|
|
||||||
const x = self.buffer.box_x + self.buffer.margin_box_h;
|
|
||||||
const y = self.buffer.box_y + self.buffer.margin_box_v + 2;
|
|
||||||
self.buffer.drawLabel(environment.specifier, x, y);
|
|
||||||
|
|
||||||
_ = termbox.tb_set_cell(@intCast(self.x), @intCast(self.y), '<', self.buffer.fg, self.buffer.bg);
|
|
||||||
_ = termbox.tb_set_cell(@intCast(self.x + self.visible_length - 1), @intCast(self.y), '>', self.buffer.fg, self.buffer.bg);
|
|
||||||
|
|
||||||
self.buffer.drawLabel(environment.name, self.x + 2, self.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goLeft(self: *Desktop) void {
|
|
||||||
if (self.current == 0) {
|
|
||||||
self.current = self.environments.items.len - 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.current -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goRight(self: *Desktop) void {
|
|
||||||
if (self.current == self.environments.items.len - 1) {
|
|
||||||
self.current = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.current += 1;
|
|
||||||
}
|
|
||||||
153
src/tui/components/Session.zig
Normal file
153
src/tui/components/Session.zig
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||||
|
const enums = @import("../../enums.zig");
|
||||||
|
const generic = @import("generic.zig");
|
||||||
|
const Ini = @import("zigini").Ini;
|
||||||
|
const Lang = @import("../../config/Lang.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const DisplayServer = enums.DisplayServer;
|
||||||
|
|
||||||
|
const EnvironmentLabel = generic.CyclableLabel(Environment);
|
||||||
|
|
||||||
|
const Session = @This();
|
||||||
|
|
||||||
|
pub const Environment = struct {
|
||||||
|
entry_ini: ?Ini(Entry) = null,
|
||||||
|
name: [:0]const u8 = "",
|
||||||
|
xdg_session_desktop: [:0]const u8 = "",
|
||||||
|
xdg_desktop_names: ?[:0]const u8 = "",
|
||||||
|
cmd: []const u8 = "",
|
||||||
|
specifier: []const u8 = "",
|
||||||
|
display_server: DisplayServer = .wayland,
|
||||||
|
};
|
||||||
|
|
||||||
|
const DesktopEntry = struct {
|
||||||
|
Exec: []const u8 = "",
|
||||||
|
Name: [:0]const u8 = "",
|
||||||
|
DesktopNames: ?[]const u8 = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Entry = struct { @"Desktop Entry": DesktopEntry = .{} };
|
||||||
|
|
||||||
|
label: EnvironmentLabel,
|
||||||
|
lang: Lang,
|
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: usize, lang: Lang) !Session {
|
||||||
|
return .{
|
||||||
|
.label = try EnvironmentLabel.init(allocator, buffer, max_length, drawItem),
|
||||||
|
.lang = lang,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: Session) void {
|
||||||
|
for (self.label.list.items) |*environment| {
|
||||||
|
if (environment.entry_ini) |*entry_ini| entry_ini.deinit();
|
||||||
|
if (environment.xdg_desktop_names) |desktop_name| self.label.allocator.free(desktop_name);
|
||||||
|
self.label.allocator.free(environment.xdg_session_desktop);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.label.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addEnvironment(self: *Session, entry: DesktopEntry, xdg_session_desktop: []const u8, display_server: DisplayServer) !void {
|
||||||
|
var xdg_desktop_names: ?[:0]const u8 = null;
|
||||||
|
if (entry.DesktopNames) |desktop_names| {
|
||||||
|
const desktop_names_z = try self.label.allocator.dupeZ(u8, desktop_names);
|
||||||
|
for (desktop_names_z) |*c| {
|
||||||
|
if (c.* == ';') c.* = ':';
|
||||||
|
}
|
||||||
|
xdg_desktop_names = desktop_names_z;
|
||||||
|
}
|
||||||
|
|
||||||
|
errdefer {
|
||||||
|
if (xdg_desktop_names) |desktop_names| self.label.allocator.free(desktop_names);
|
||||||
|
}
|
||||||
|
|
||||||
|
const session_desktop = try self.label.allocator.dupeZ(u8, xdg_session_desktop);
|
||||||
|
errdefer self.label.allocator.free(session_desktop);
|
||||||
|
|
||||||
|
try self.label.addItem(.{
|
||||||
|
.entry_ini = null,
|
||||||
|
.name = entry.Name,
|
||||||
|
.xdg_session_desktop = session_desktop,
|
||||||
|
.xdg_desktop_names = xdg_desktop_names,
|
||||||
|
.cmd = entry.Exec,
|
||||||
|
.specifier = switch (display_server) {
|
||||||
|
.wayland => self.lang.wayland,
|
||||||
|
.x11 => self.lang.x11,
|
||||||
|
else => self.lang.other,
|
||||||
|
},
|
||||||
|
.display_server = display_server,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addEnvironmentWithIni(self: *Session, entry_ini: Ini(Entry), xdg_session_desktop: []const u8, display_server: DisplayServer) !void {
|
||||||
|
const entry = entry_ini.data.@"Desktop Entry";
|
||||||
|
var xdg_desktop_names: ?[:0]const u8 = null;
|
||||||
|
if (entry.DesktopNames) |desktop_names| {
|
||||||
|
const desktop_names_z = try self.label.allocator.dupeZ(u8, desktop_names);
|
||||||
|
for (desktop_names_z) |*c| {
|
||||||
|
if (c.* == ';') c.* = ':';
|
||||||
|
}
|
||||||
|
xdg_desktop_names = desktop_names_z;
|
||||||
|
}
|
||||||
|
|
||||||
|
errdefer {
|
||||||
|
if (xdg_desktop_names) |desktop_names| self.label.allocator.free(desktop_names);
|
||||||
|
}
|
||||||
|
|
||||||
|
const session_desktop = try self.label.allocator.dupeZ(u8, xdg_session_desktop);
|
||||||
|
errdefer self.label.allocator.free(session_desktop);
|
||||||
|
|
||||||
|
try self.label.addItem(.{
|
||||||
|
.entry_ini = entry_ini,
|
||||||
|
.name = entry.Name,
|
||||||
|
.xdg_session_desktop = session_desktop,
|
||||||
|
.xdg_desktop_names = xdg_desktop_names,
|
||||||
|
.cmd = entry.Exec,
|
||||||
|
.specifier = switch (display_server) {
|
||||||
|
.wayland => self.lang.wayland,
|
||||||
|
.x11 => self.lang.x11,
|
||||||
|
else => self.lang.other,
|
||||||
|
},
|
||||||
|
.display_server = display_server,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn crawl(self: *Session, path: []const u8, display_server: DisplayServer) !void {
|
||||||
|
var iterable_directory = std.fs.openDirAbsolute(path, .{ .iterate = true }) catch return;
|
||||||
|
defer iterable_directory.close();
|
||||||
|
|
||||||
|
var iterator = iterable_directory.iterate();
|
||||||
|
while (try iterator.next()) |item| {
|
||||||
|
if (!std.mem.eql(u8, std.fs.path.extension(item.name), ".desktop")) continue;
|
||||||
|
|
||||||
|
const entry_path = try std.fmt.allocPrint(self.label.allocator, "{s}/{s}", .{ path, item.name });
|
||||||
|
defer self.label.allocator.free(entry_path);
|
||||||
|
var entry_ini = Ini(Entry).init(self.label.allocator);
|
||||||
|
_ = try entry_ini.readFileToStruct(entry_path, "#", null);
|
||||||
|
errdefer entry_ini.deinit();
|
||||||
|
|
||||||
|
var xdg_session_desktop: []const u8 = undefined;
|
||||||
|
const maybe_desktop_names = entry_ini.data.@"Desktop Entry".DesktopNames;
|
||||||
|
if (maybe_desktop_names) |desktop_names| {
|
||||||
|
xdg_session_desktop = std.mem.sliceTo(desktop_names, ';');
|
||||||
|
} else {
|
||||||
|
// if DesktopNames is empty, we'll take the name of the session file
|
||||||
|
xdg_session_desktop = std.fs.path.stem(item.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.addEnvironmentWithIni(entry_ini, xdg_session_desktop, display_server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drawItem(label: EnvironmentLabel, environment: Environment, x: usize, y: usize) bool {
|
||||||
|
const length = @min(environment.name.len, label.visible_length - 3);
|
||||||
|
if (length == 0) return false;
|
||||||
|
|
||||||
|
label.buffer.drawLabel(environment.specifier, x, y);
|
||||||
|
label.buffer.drawLabel(environment.name, label.x + 2, label.y);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
105
src/tui/components/generic.zig
Normal file
105
src/tui/components/generic.zig
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const enums = @import("../../enums.zig");
|
||||||
|
const interop = @import("../../interop.zig");
|
||||||
|
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||||
|
|
||||||
|
pub fn CyclableLabel(comptime ItemType: type) type {
|
||||||
|
return struct {
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const ItemList = std.ArrayList(ItemType);
|
||||||
|
const DrawItemFn = *const fn (Self, ItemType, usize, usize) bool;
|
||||||
|
|
||||||
|
const termbox = interop.termbox;
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
allocator: Allocator,
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
list: ItemList,
|
||||||
|
current: usize,
|
||||||
|
visible_length: usize,
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
draw_item_fn: DrawItemFn,
|
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: usize, draw_item_fn: DrawItemFn) !Self {
|
||||||
|
return .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.buffer = buffer,
|
||||||
|
.list = try ItemList.initCapacity(allocator, max_length),
|
||||||
|
.current = 0,
|
||||||
|
.visible_length = 0,
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.draw_item_fn = draw_item_fn,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: Self) void {
|
||||||
|
self.list.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn position(self: *Self, x: usize, y: usize, visible_length: usize) void {
|
||||||
|
self.x = x;
|
||||||
|
self.y = y;
|
||||||
|
self.visible_length = visible_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addItem(self: *Self, item: ItemType) !void {
|
||||||
|
try self.list.append(item);
|
||||||
|
self.current = self.list.items.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(self: *Self, maybe_event: ?*termbox.tb_event, insert_mode: bool) void {
|
||||||
|
if (maybe_event) |event| blk: {
|
||||||
|
if (event.type != termbox.TB_EVENT_KEY) break :blk;
|
||||||
|
|
||||||
|
switch (event.key) {
|
||||||
|
termbox.TB_KEY_ARROW_LEFT, termbox.TB_KEY_CTRL_H => self.goLeft(),
|
||||||
|
termbox.TB_KEY_ARROW_RIGHT, termbox.TB_KEY_CTRL_L => self.goRight(),
|
||||||
|
else => {
|
||||||
|
if (!insert_mode) {
|
||||||
|
switch (event.ch) {
|
||||||
|
'h' => self.goLeft(),
|
||||||
|
'l' => self.goRight(),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = termbox.tb_set_cursor(@intCast(self.x + 2), @intCast(self.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(self: Self) void {
|
||||||
|
const current_item = self.list.items[self.current];
|
||||||
|
const x = self.buffer.box_x + self.buffer.margin_box_h;
|
||||||
|
const y = self.buffer.box_y + self.buffer.margin_box_v + 2;
|
||||||
|
|
||||||
|
const continue_drawing = @call(.auto, self.draw_item_fn, .{ self, current_item, x, y });
|
||||||
|
if (!continue_drawing) return;
|
||||||
|
|
||||||
|
_ = termbox.tb_set_cell(@intCast(self.x), @intCast(self.y), '<', self.buffer.fg, self.buffer.bg);
|
||||||
|
_ = termbox.tb_set_cell(@intCast(self.x + self.visible_length - 1), @intCast(self.y), '>', self.buffer.fg, self.buffer.bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goLeft(self: *Self) void {
|
||||||
|
if (self.current == 0) {
|
||||||
|
self.current = self.list.items.len - 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goRight(self: *Self) void {
|
||||||
|
if (self.current == self.list.items.len - 1) {
|
||||||
|
self.current = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user