mirror of
https://github.com/fairyglade/ly.git
synced 2026-03-21 22:43:38 +00:00
Support more configurable keybindings (closes #679)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
This commit is contained in:
@@ -233,7 +233,7 @@ gameoflife_initial_density = 0.4
|
||||
# Command executed when pressing hibernate key (can be null)
|
||||
hibernate_cmd = null
|
||||
|
||||
# Specifies the key used for hibernate (F1-F12)
|
||||
# Specifies the key used for hibernate
|
||||
hibernate_key = F4
|
||||
|
||||
# Remove main box borders
|
||||
@@ -304,7 +304,7 @@ path = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
# Command executed when pressing restart_key
|
||||
restart_cmd = /sbin/shutdown -r now
|
||||
|
||||
# Specifies the key used for restart (F1-F12)
|
||||
# Specifies the key used for restart
|
||||
restart_key = F2
|
||||
|
||||
# Save the current desktop and login as defaults, and load them on startup
|
||||
@@ -328,13 +328,13 @@ setup_cmd = $CONFIG_DIRECTORY/ly/setup.sh
|
||||
# Command executed when pressing shutdown_key
|
||||
shutdown_cmd = /sbin/shutdown $PLATFORM_SHUTDOWN_ARG now
|
||||
|
||||
# Specifies the key used for shutdown (F1-F12)
|
||||
# Specifies the key used for shutdown
|
||||
shutdown_key = F1
|
||||
|
||||
# Command executed when pressing sleep key (can be null)
|
||||
sleep_cmd = null
|
||||
|
||||
# Specifies the key used for sleep (F1-F12)
|
||||
# Specifies the key used for sleep
|
||||
sleep_key = F3
|
||||
|
||||
# Command executed when starting Ly (before the TTY is taken control of)
|
||||
|
||||
570
src/main.zig
570
src/main.zig
@@ -1,4 +1,5 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const StringList = std.ArrayListUnmanaged([]const u8);
|
||||
const temporary_allocator = std.heap.page_allocator;
|
||||
const builtin = @import("builtin");
|
||||
@@ -66,8 +67,13 @@ fn ttyControlTransferSignalHandler(_: c_int) callconv(.c) void {
|
||||
}
|
||||
|
||||
const UiState = struct {
|
||||
allocator: Allocator,
|
||||
auth_fails: u64,
|
||||
run: bool,
|
||||
update: bool,
|
||||
is_autologin: bool,
|
||||
use_kmscon_vt: bool,
|
||||
active_tty: u8,
|
||||
buffer: *TerminalBuffer,
|
||||
labels_max_length: usize,
|
||||
animation_timed_out: bool,
|
||||
@@ -91,6 +97,7 @@ const UiState = struct {
|
||||
info_line: *InfoLine,
|
||||
animate: bool,
|
||||
session: *Session,
|
||||
saved_users: SavedUsers,
|
||||
login: *UserList,
|
||||
password: *Text,
|
||||
active_input: enums.Input,
|
||||
@@ -99,15 +106,18 @@ const UiState = struct {
|
||||
config: Config,
|
||||
lang: Lang,
|
||||
log_file: *LogFile,
|
||||
save_path: []const u8,
|
||||
old_save_path: ?[]const u8,
|
||||
battery_buf: [16:0]u8,
|
||||
bigclock_format_buf: [16:0]u8,
|
||||
clock_buf: [64:0]u8,
|
||||
bigclock_buf: [32:0]u8,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
var shutdown = false;
|
||||
var restart = false;
|
||||
|
||||
pub fn main() !void {
|
||||
var shutdown_cmd: []const u8 = undefined;
|
||||
var restart_cmd: []const u8 = undefined;
|
||||
var commands_allocated = false;
|
||||
@@ -324,10 +334,15 @@ pub fn main() !void {
|
||||
.full_color = config.full_color,
|
||||
.is_tty = true,
|
||||
};
|
||||
var buffer = try TerminalBuffer.init(buffer_options, &log_file, random);
|
||||
var buffer = try TerminalBuffer.init(
|
||||
allocator,
|
||||
buffer_options,
|
||||
&log_file,
|
||||
random,
|
||||
);
|
||||
defer {
|
||||
log_file.info("tui", "shutting down terminal buffer", .{}) catch {};
|
||||
TerminalBuffer.shutdownStatic();
|
||||
buffer.deinit();
|
||||
}
|
||||
|
||||
const act = std.posix.Sigaction{
|
||||
@@ -707,10 +722,28 @@ pub fn main() !void {
|
||||
is_autologin = true;
|
||||
}
|
||||
|
||||
// Switch to selected TTY
|
||||
const active_tty = interop.getActiveTty(allocator, use_kmscon_vt) catch |err| no_tty_found: {
|
||||
try info_line.addMessage(lang.err_get_active_tty, config.error_bg, config.error_fg);
|
||||
try log_file.err("sys", "failed to get active tty: {s}", .{@errorName(err)});
|
||||
break :no_tty_found build_options.fallback_tty;
|
||||
};
|
||||
if (!use_kmscon_vt) {
|
||||
interop.switchTty(active_tty) catch |err| {
|
||||
try info_line.addMessage(lang.err_switch_tty, config.error_bg, config.error_fg);
|
||||
try log_file.err("sys", "failed to switch to tty {d}: {s}", .{ active_tty, @errorName(err) });
|
||||
};
|
||||
}
|
||||
|
||||
var animation: ?Animation = null;
|
||||
var state = UiState{
|
||||
.allocator = allocator,
|
||||
.auth_fails = 0,
|
||||
.run = true,
|
||||
.update = true,
|
||||
.is_autologin = is_autologin,
|
||||
.use_kmscon_vt = use_kmscon_vt,
|
||||
.active_tty = active_tty,
|
||||
.buffer = &buffer,
|
||||
.labels_max_length = labels_max_length,
|
||||
.animation_timed_out = false,
|
||||
@@ -734,6 +767,7 @@ pub fn main() !void {
|
||||
.info_line = &info_line,
|
||||
.animate = config.animation != .none,
|
||||
.session = &session,
|
||||
.saved_users = saved_users,
|
||||
.login = &login,
|
||||
.password = &password,
|
||||
.active_input = config.default_input,
|
||||
@@ -745,6 +779,8 @@ pub fn main() !void {
|
||||
.config = config,
|
||||
.lang = lang,
|
||||
.log_file = &log_file,
|
||||
.save_path = save_path,
|
||||
.old_save_path = if (old_save_parser != null) old_save_path else null,
|
||||
.battery_buf = undefined,
|
||||
.bigclock_format_buf = undefined,
|
||||
.clock_buf = undefined,
|
||||
@@ -775,7 +811,7 @@ pub fn main() !void {
|
||||
}
|
||||
}
|
||||
|
||||
// Position components
|
||||
// Position components and place cursor accordingly
|
||||
try updateComponents(&state);
|
||||
positionComponents(&state);
|
||||
|
||||
@@ -815,31 +851,37 @@ pub fn main() !void {
|
||||
}
|
||||
defer if (animation) |*a| a.deinit();
|
||||
|
||||
const shutdown_key = try std.fmt.parseInt(u8, config.shutdown_key[1..], 10);
|
||||
const restart_key = try std.fmt.parseInt(u8, config.restart_key[1..], 10);
|
||||
const sleep_key = try std.fmt.parseInt(u8, config.sleep_key[1..], 10);
|
||||
const hibernate_key = try std.fmt.parseInt(u8, config.hibernate_key[1..], 10);
|
||||
const brightness_down_key = if (config.brightness_down_key) |key| try std.fmt.parseInt(u8, key[1..], 10) else null;
|
||||
const brightness_up_key = if (config.brightness_up_key) |key| try std.fmt.parseInt(u8, key[1..], 10) else null;
|
||||
try buffer.registerKeybind("Esc", &disableInsertMode);
|
||||
try buffer.registerKeybind("I", &enableInsertMode);
|
||||
|
||||
try buffer.registerKeybind("Ctrl+C", &quit);
|
||||
|
||||
try buffer.registerKeybind("Ctrl+U", &clearPassword);
|
||||
|
||||
try buffer.registerKeybind("Ctrl+K", &moveCursorUp);
|
||||
try buffer.registerKeybind("Up", &moveCursorUp);
|
||||
try buffer.registerKeybind("J", &viMoseCursorUp);
|
||||
|
||||
try buffer.registerKeybind("Ctrl+J", &moveCursorDown);
|
||||
try buffer.registerKeybind("Down", &moveCursorDown);
|
||||
try buffer.registerKeybind("K", &viMoveCursorDown);
|
||||
|
||||
try buffer.registerKeybind("Tab", &wrapCursor);
|
||||
try buffer.registerKeybind("Shift+Tab", &wrapCursorReverse);
|
||||
|
||||
try buffer.registerKeybind("Enter", &authenticate);
|
||||
|
||||
try buffer.registerKeybind(config.shutdown_key, &shutdownCmd);
|
||||
try buffer.registerKeybind(config.restart_key, &restartCmd);
|
||||
if (config.sleep_cmd != null) try buffer.registerKeybind(config.sleep_key, &sleepCmd);
|
||||
if (config.hibernate_cmd != null) try buffer.registerKeybind(config.hibernate_key, &hibernateCmd);
|
||||
if (config.brightness_down_key) |key| try buffer.registerKeybind(key, &decreaseBrightnessCmd);
|
||||
if (config.brightness_up_key) |key| try buffer.registerKeybind(key, &increaseBrightnessCmd);
|
||||
|
||||
var event: termbox.tb_event = undefined;
|
||||
var run = true;
|
||||
var inactivity_time_start = try interop.getTimeOfDay();
|
||||
var inactivity_cmd_ran = false;
|
||||
|
||||
// Switch to selected TTY
|
||||
const active_tty = interop.getActiveTty(allocator, use_kmscon_vt) catch |err| no_tty_found: {
|
||||
try info_line.addMessage(lang.err_get_active_tty, config.error_bg, config.error_fg);
|
||||
try log_file.err("sys", "failed to get active tty: {s}", .{@errorName(err)});
|
||||
break :no_tty_found build_options.fallback_tty;
|
||||
};
|
||||
if (!use_kmscon_vt) {
|
||||
interop.switchTty(active_tty) catch |err| {
|
||||
try info_line.addMessage(lang.err_switch_tty, config.error_bg, config.error_fg);
|
||||
try log_file.err("sys", "failed to switch to tty {d}: {s}", .{ active_tty, @errorName(err) });
|
||||
};
|
||||
}
|
||||
|
||||
if (config.initial_info_text) |text| {
|
||||
try info_line.addMessage(text, config.bg, config.fg);
|
||||
} else get_host_name: {
|
||||
@@ -853,7 +895,7 @@ pub fn main() !void {
|
||||
try info_line.addMessage(hostname, config.bg, config.fg);
|
||||
}
|
||||
|
||||
while (run) {
|
||||
while (state.run) {
|
||||
if (state.update) {
|
||||
try updateComponents(&state);
|
||||
|
||||
@@ -935,6 +977,9 @@ pub fn main() !void {
|
||||
if (event_error < 0) continue;
|
||||
}
|
||||
|
||||
// Input of some kind was detected, so reset the inactivity timer
|
||||
inactivity_time_start = try interop.getTimeOfDay();
|
||||
|
||||
if (event.type == termbox.TB_EVENT_RESIZE) {
|
||||
state.buffer.width = TerminalBuffer.getWidthStatic();
|
||||
state.buffer.height = TerminalBuffer.getHeightStatic();
|
||||
@@ -952,120 +997,181 @@ pub fn main() !void {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Input of some kind was detected, so reset the inactivity timer
|
||||
inactivity_time_start = try interop.getTimeOfDay();
|
||||
const passthrough_event = try buffer.handleKeybind(
|
||||
allocator,
|
||||
event,
|
||||
&state,
|
||||
);
|
||||
if (passthrough_event) {
|
||||
switch (state.active_input) {
|
||||
.info_line => info_line.label.handle(&event, state.insert_mode),
|
||||
.session => session.label.handle(&event, state.insert_mode),
|
||||
.login => login.label.handle(&event, state.insert_mode),
|
||||
.password => password.handle(&event, state.insert_mode) catch {
|
||||
try info_line.addMessage(
|
||||
lang.err_alloc,
|
||||
config.error_bg,
|
||||
config.error_fg,
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
termbox.TB_KEY_ESC => {
|
||||
if (config.vi_mode and state.insert_mode) {
|
||||
state.update = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn disableInsertMode(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (state.config.vi_mode and state.insert_mode) {
|
||||
state.insert_mode = false;
|
||||
state.update = true;
|
||||
}
|
||||
},
|
||||
termbox.TB_KEY_F12...termbox.TB_KEY_F1 => {
|
||||
const pressed_key = 0xFFFF - event.key + 1;
|
||||
if (pressed_key == shutdown_key) {
|
||||
shutdown = true;
|
||||
run = false;
|
||||
} else if (pressed_key == restart_key) {
|
||||
restart = true;
|
||||
run = false;
|
||||
} else if (pressed_key == sleep_key) {
|
||||
if (config.sleep_cmd) |sleep_cmd| {
|
||||
var sleep = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", sleep_cmd }, allocator);
|
||||
sleep.stdout_behavior = .Ignore;
|
||||
sleep.stderr_behavior = .Ignore;
|
||||
return false;
|
||||
}
|
||||
|
||||
handle_sleep_cmd: {
|
||||
const process_result = sleep.spawnAndWait() catch {
|
||||
break :handle_sleep_cmd;
|
||||
};
|
||||
if (process_result.Exited != 0) {
|
||||
try info_line.addMessage(lang.err_sleep, config.error_bg, config.error_fg);
|
||||
try log_file.err("sys", "failed to execute sleep command: exit code {d}", .{process_result.Exited});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (pressed_key == hibernate_key) {
|
||||
if (config.hibernate_cmd) |hibernate_cmd| {
|
||||
var hibernate = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", hibernate_cmd }, allocator);
|
||||
hibernate.stdout_behavior = .Ignore;
|
||||
hibernate.stderr_behavior = .Ignore;
|
||||
fn enableInsertMode(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
if (state.insert_mode) return true;
|
||||
|
||||
handle_hibernate_cmd: {
|
||||
const process_result = hibernate.spawnAndWait() catch {
|
||||
break :handle_hibernate_cmd;
|
||||
};
|
||||
if (process_result.Exited != 0) {
|
||||
try info_line.addMessage(lang.err_hibernate, config.error_bg, config.error_fg);
|
||||
try log_file.err("sys", "failed to execute hibernate command: exit code {d}", .{process_result.Exited});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (brightness_down_key != null and pressed_key == brightness_down_key.?) {
|
||||
adjustBrightness(allocator, config.brightness_down_cmd) catch |err| {
|
||||
try info_line.addMessage(lang.err_brightness_change, config.error_bg, config.error_fg);
|
||||
try log_file.err("sys", "failed to change brightness: {s}", .{@errorName(err)});
|
||||
};
|
||||
} else if (brightness_up_key != null and pressed_key == brightness_up_key.?) {
|
||||
adjustBrightness(allocator, config.brightness_up_cmd) catch |err| {
|
||||
try info_line.addMessage(lang.err_brightness_change, config.error_bg, config.error_fg);
|
||||
try log_file.err("sys", "failed to change brightness: {s}", .{@errorName(err)});
|
||||
};
|
||||
}
|
||||
},
|
||||
termbox.TB_KEY_CTRL_C => run = false,
|
||||
termbox.TB_KEY_CTRL_U => if (state.active_input == .password) {
|
||||
password.clear();
|
||||
state.insert_mode = true;
|
||||
state.update = true;
|
||||
},
|
||||
termbox.TB_KEY_CTRL_K, termbox.TB_KEY_ARROW_UP => {
|
||||
return false;
|
||||
}
|
||||
|
||||
fn clearPassword(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (state.active_input == .password) {
|
||||
state.password.clear();
|
||||
state.update = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn moveCursorUp(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
state.active_input.move(true, false);
|
||||
state.update = true;
|
||||
},
|
||||
termbox.TB_KEY_CTRL_J, termbox.TB_KEY_ARROW_DOWN => {
|
||||
state.active_input.move(false, false);
|
||||
state.update = true;
|
||||
},
|
||||
termbox.TB_KEY_TAB => {
|
||||
state.active_input.move(false, true);
|
||||
state.update = true;
|
||||
},
|
||||
termbox.TB_KEY_BACK_TAB => {
|
||||
state.active_input.move(true, true);
|
||||
state.update = true;
|
||||
},
|
||||
termbox.TB_KEY_ENTER => authenticate: {
|
||||
try log_file.info("auth", "starting authentication", .{});
|
||||
|
||||
if (!config.allow_empty_password and password.text.items.len == 0) {
|
||||
// Let's not log this message for security reasons
|
||||
try info_line.addMessage(lang.err_empty_password, config.error_bg, config.error_fg);
|
||||
info_line.clearRendered(allocator) catch |err| {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
try log_file.err("tui", "failed to clear info line: {s}", .{@errorName(err)});
|
||||
};
|
||||
info_line.label.draw();
|
||||
_ = TerminalBuffer.presentBufferStatic();
|
||||
break :authenticate;
|
||||
return false;
|
||||
}
|
||||
|
||||
try info_line.addMessage(lang.authenticating, config.bg, config.fg);
|
||||
info_line.clearRendered(allocator) catch |err| {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
try log_file.err("tui", "failed to clear info line: {s}", .{@errorName(err)});
|
||||
fn viMoseCursorUp(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
if (state.insert_mode) return true;
|
||||
|
||||
state.active_input.move(false, false);
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn moveCursorDown(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
state.active_input.move(false, false);
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn viMoveCursorDown(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
if (state.insert_mode) return true;
|
||||
|
||||
state.active_input.move(true, false);
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn wrapCursor(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
state.active_input.move(false, true);
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn wrapCursorReverse(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
state.active_input.move(true, true);
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn quit(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
state.run = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn authenticate(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
try state.log_file.info("auth", "starting authentication", .{});
|
||||
|
||||
if (!state.config.allow_empty_password and state.password.text.items.len == 0) {
|
||||
// Let's not log this message for security reasons
|
||||
try state.info_line.addMessage(
|
||||
state.lang.err_empty_password,
|
||||
state.config.error_bg,
|
||||
state.config.error_fg,
|
||||
);
|
||||
state.info_line.clearRendered(state.allocator) catch |err| {
|
||||
try state.info_line.addMessage(
|
||||
state.lang.err_alloc,
|
||||
state.config.error_bg,
|
||||
state.config.error_fg,
|
||||
);
|
||||
try state.log_file.err(
|
||||
"tui",
|
||||
"failed to clear info line: {s}",
|
||||
.{@errorName(err)},
|
||||
);
|
||||
};
|
||||
info_line.label.draw();
|
||||
state.info_line.label.draw();
|
||||
_ = TerminalBuffer.presentBufferStatic();
|
||||
return false;
|
||||
}
|
||||
|
||||
try state.info_line.addMessage(
|
||||
state.lang.authenticating,
|
||||
state.config.bg,
|
||||
state.config.fg,
|
||||
);
|
||||
state.info_line.clearRendered(state.allocator) catch |err| {
|
||||
try state.info_line.addMessage(
|
||||
state.lang.err_alloc,
|
||||
state.config.error_bg,
|
||||
state.config.error_fg,
|
||||
);
|
||||
try state.log_file.err(
|
||||
"tui",
|
||||
"failed to clear info line: {s}",
|
||||
.{@errorName(err)},
|
||||
);
|
||||
};
|
||||
state.info_line.label.draw();
|
||||
_ = TerminalBuffer.presentBufferStatic();
|
||||
|
||||
if (config.save) save_last_settings: {
|
||||
if (state.config.save) save_last_settings: {
|
||||
// It isn't worth cluttering the code with precise error
|
||||
// handling, so let's just report a generic error message,
|
||||
// that should be good enough for debugging anyway.
|
||||
errdefer log_file.err("conf", "failed to save current user data", .{}) catch {};
|
||||
errdefer state.log_file.err(
|
||||
"conf",
|
||||
"failed to save current user data",
|
||||
.{},
|
||||
) catch {};
|
||||
|
||||
var file = std.fs.cwd().createFile(save_path, .{}) catch |err| {
|
||||
log_file.err("sys", "failed to create save file: {s}", .{@errorName(err)}) catch break :save_last_settings;
|
||||
var file = std.fs.cwd().createFile(state.save_path, .{}) catch |err| {
|
||||
state.log_file.err(
|
||||
"sys",
|
||||
"failed to create save file: {s}",
|
||||
.{@errorName(err)},
|
||||
) catch break :save_last_settings;
|
||||
break :save_last_settings;
|
||||
};
|
||||
defer file.close();
|
||||
@@ -1074,8 +1180,8 @@ pub fn main() !void {
|
||||
var file_writer = file.writer(&file_buffer);
|
||||
var writer = &file_writer.interface;
|
||||
|
||||
try writer.print("{d}\n", .{login.label.current});
|
||||
for (saved_users.user_list.items) |user| {
|
||||
try writer.print("{d}\n", .{state.login.label.current});
|
||||
for (state.saved_users.user_list.items) |user| {
|
||||
try writer.print("{s}:{d}\n", .{ user.username, user.session_index });
|
||||
}
|
||||
try writer.flush();
|
||||
@@ -1083,8 +1189,8 @@ pub fn main() !void {
|
||||
// Delete previous save file if it exists
|
||||
if (migrator.maybe_save_file) |path| {
|
||||
std.fs.cwd().deleteFile(path) catch {};
|
||||
} else if (old_save_parser != null) {
|
||||
std.fs.cwd().deleteFile(old_save_path) catch {};
|
||||
} else if (state.old_save_path) |path| {
|
||||
std.fs.cwd().deleteFile(path) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1092,28 +1198,28 @@ pub fn main() !void {
|
||||
defer shared_err.deinit();
|
||||
|
||||
{
|
||||
log_file.deinit();
|
||||
state.log_file.deinit();
|
||||
|
||||
session_pid = try std.posix.fork();
|
||||
if (session_pid == 0) {
|
||||
const current_environment = session.label.list.items[session.label.current].environment;
|
||||
const current_environment = state.session.label.list.items[state.session.label.current].environment;
|
||||
|
||||
// Use auto_login_service for autologin, otherwise use configured service
|
||||
const service_name = if (is_autologin) config.auto_login_service else config.service_name;
|
||||
const password_text = if (is_autologin) "" else password.text.items;
|
||||
const service_name = if (state.is_autologin) state.config.auto_login_service else state.config.service_name;
|
||||
const password_text = if (state.is_autologin) "" else state.password.text.items;
|
||||
|
||||
const auth_options = auth.AuthOptions{
|
||||
.tty = active_tty,
|
||||
.tty = state.active_tty,
|
||||
.service_name = service_name,
|
||||
.path = config.path,
|
||||
.session_log = config.session_log,
|
||||
.xauth_cmd = config.xauth_cmd,
|
||||
.setup_cmd = config.setup_cmd,
|
||||
.login_cmd = config.login_cmd,
|
||||
.x_cmd = config.x_cmd,
|
||||
.x_vt = config.x_vt,
|
||||
.path = state.config.path,
|
||||
.session_log = state.config.session_log,
|
||||
.xauth_cmd = state.config.xauth_cmd,
|
||||
.setup_cmd = state.config.setup_cmd,
|
||||
.login_cmd = state.config.login_cmd,
|
||||
.x_cmd = state.config.x_cmd,
|
||||
.x_vt = state.config.x_vt,
|
||||
.session_pid = session_pid,
|
||||
.use_kmscon_vt = use_kmscon_vt,
|
||||
.use_kmscon_vt = state.use_kmscon_vt,
|
||||
};
|
||||
|
||||
// Signal action to give up control on the TTY
|
||||
@@ -1124,16 +1230,23 @@ pub fn main() !void {
|
||||
};
|
||||
std.posix.sigaction(std.posix.SIG.CHLD, &tty_control_transfer_act, null);
|
||||
|
||||
try log_file.reinit();
|
||||
try state.log_file.reinit();
|
||||
|
||||
auth.authenticate(allocator, &log_file, auth_options, current_environment, login.getCurrentUsername(), password_text) catch |err| {
|
||||
auth.authenticate(
|
||||
state.allocator,
|
||||
state.log_file,
|
||||
auth_options,
|
||||
current_environment,
|
||||
state.login.getCurrentUsername(),
|
||||
password_text,
|
||||
) catch |err| {
|
||||
shared_err.writeError(err);
|
||||
|
||||
log_file.deinit();
|
||||
state.log_file.deinit();
|
||||
std.process.exit(1);
|
||||
};
|
||||
|
||||
log_file.deinit();
|
||||
state.log_file.deinit();
|
||||
std.process.exit(0);
|
||||
}
|
||||
|
||||
@@ -1143,33 +1256,45 @@ pub fn main() !void {
|
||||
std.Thread.sleep(std.time.ns_per_s * 1);
|
||||
session_pid = -1;
|
||||
|
||||
try log_file.reinit();
|
||||
try state.log_file.reinit();
|
||||
}
|
||||
|
||||
try buffer.reclaim();
|
||||
try state.buffer.reclaim();
|
||||
|
||||
const auth_err = shared_err.readError();
|
||||
if (auth_err) |err| {
|
||||
state.auth_fails += 1;
|
||||
state.active_input = .password;
|
||||
|
||||
try info_line.addMessage(getAuthErrorMsg(err, lang), config.error_bg, config.error_fg);
|
||||
try log_file.err("auth", "failed to authenticate: {s}", .{@errorName(err)});
|
||||
try state.info_line.addMessage(
|
||||
getAuthErrorMsg(err, state.lang),
|
||||
state.config.error_bg,
|
||||
state.config.error_fg,
|
||||
);
|
||||
try state.log_file.err(
|
||||
"auth",
|
||||
"failed to authenticate: {s}",
|
||||
.{@errorName(err)},
|
||||
);
|
||||
|
||||
if (config.clear_password or err != error.PamAuthError) password.clear();
|
||||
if (state.config.clear_password or err != error.PamAuthError) state.password.clear();
|
||||
} else {
|
||||
if (config.logout_cmd) |logout_cmd| {
|
||||
var logout_process = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", logout_cmd }, allocator);
|
||||
if (state.config.logout_cmd) |logout_cmd| {
|
||||
var logout_process = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", logout_cmd }, state.allocator);
|
||||
_ = logout_process.spawnAndWait() catch .{};
|
||||
}
|
||||
|
||||
password.clear();
|
||||
is_autologin = false;
|
||||
try info_line.addMessage(lang.logout, config.bg, config.fg);
|
||||
try log_file.info("auth", "logged out", .{});
|
||||
state.password.clear();
|
||||
state.is_autologin = false;
|
||||
try state.info_line.addMessage(
|
||||
state.lang.logout,
|
||||
state.config.bg,
|
||||
state.config.fg,
|
||||
);
|
||||
try state.log_file.info("auth", "logged out", .{});
|
||||
}
|
||||
|
||||
if (config.auth_fails == 0 or state.auth_fails < config.auth_fails) {
|
||||
if (state.config.auth_fails == 0 or state.auth_fails < state.config.auth_fails) {
|
||||
try TerminalBuffer.clearScreenStatic(true);
|
||||
state.update = true;
|
||||
}
|
||||
@@ -1177,42 +1302,109 @@ pub fn main() !void {
|
||||
// Restore the cursor
|
||||
TerminalBuffer.setCursorStatic(0, 0);
|
||||
_ = TerminalBuffer.presentBufferStatic();
|
||||
},
|
||||
else => {
|
||||
if (!state.insert_mode) {
|
||||
switch (event.ch) {
|
||||
'k' => {
|
||||
state.active_input.move(true, false);
|
||||
state.update = true;
|
||||
continue;
|
||||
},
|
||||
'j' => {
|
||||
state.active_input.move(false, false);
|
||||
state.update = true;
|
||||
continue;
|
||||
},
|
||||
'i' => {
|
||||
state.insert_mode = true;
|
||||
state.update = true;
|
||||
continue;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (state.active_input) {
|
||||
.info_line => info_line.label.handle(&event, state.insert_mode),
|
||||
.session => session.label.handle(&event, state.insert_mode),
|
||||
.login => login.label.handle(&event, state.insert_mode),
|
||||
.password => password.handle(&event, state.insert_mode) catch {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
},
|
||||
fn shutdownCmd(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
shutdown = true;
|
||||
state.run = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
state.update = true;
|
||||
},
|
||||
fn restartCmd(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
restart = true;
|
||||
state.run = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn sleepCmd(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (state.config.sleep_cmd) |sleep_cmd| {
|
||||
var sleep = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", sleep_cmd }, state.allocator);
|
||||
sleep.stdout_behavior = .Ignore;
|
||||
sleep.stderr_behavior = .Ignore;
|
||||
|
||||
const process_result = sleep.spawnAndWait() catch return false;
|
||||
if (process_result.Exited != 0) {
|
||||
try state.info_line.addMessage(
|
||||
state.lang.err_sleep,
|
||||
state.config.error_bg,
|
||||
state.config.error_fg,
|
||||
);
|
||||
try state.log_file.err(
|
||||
"sys",
|
||||
"failed to execute sleep command: exit code {d}",
|
||||
.{process_result.Exited},
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn hibernateCmd(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (state.config.hibernate_cmd) |hibernate_cmd| {
|
||||
var hibernate = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", hibernate_cmd }, state.allocator);
|
||||
hibernate.stdout_behavior = .Ignore;
|
||||
hibernate.stderr_behavior = .Ignore;
|
||||
|
||||
const process_result = hibernate.spawnAndWait() catch return false;
|
||||
if (process_result.Exited != 0) {
|
||||
try state.info_line.addMessage(
|
||||
state.lang.err_hibernate,
|
||||
state.config.error_bg,
|
||||
state.config.error_fg,
|
||||
);
|
||||
try state.log_file.err(
|
||||
"sys",
|
||||
"failed to execute hibernate command: exit code {d}",
|
||||
.{process_result.Exited},
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn decreaseBrightnessCmd(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
adjustBrightness(state.allocator, state.config.brightness_down_cmd) catch |err| {
|
||||
try state.info_line.addMessage(
|
||||
state.lang.err_brightness_change,
|
||||
state.config.error_bg,
|
||||
state.config.error_fg,
|
||||
);
|
||||
try state.log_file.err(
|
||||
"sys",
|
||||
"failed to decrease brightness: {s}",
|
||||
.{@errorName(err)},
|
||||
);
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
fn increaseBrightnessCmd(ptr: *anyopaque) !bool {
|
||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||
|
||||
adjustBrightness(state.allocator, state.config.brightness_up_cmd) catch |err| {
|
||||
try state.info_line.addMessage(
|
||||
state.lang.err_brightness_change,
|
||||
state.config.error_bg,
|
||||
state.config.error_fg,
|
||||
);
|
||||
try state.log_file.err(
|
||||
"sys",
|
||||
"failed to increase brightness: {s}",
|
||||
.{@errorName(err)},
|
||||
);
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
fn updateComponents(state: *UiState) !void {
|
||||
@@ -1559,7 +1751,7 @@ fn findSessionByName(session: *Session, name: []const u8) ?usize {
|
||||
return null;
|
||||
}
|
||||
|
||||
fn getAllUsernames(allocator: std.mem.Allocator, login_defs_path: []const u8, uid_range_error: *?anyerror) !StringList {
|
||||
fn getAllUsernames(allocator: Allocator, login_defs_path: []const u8, uid_range_error: *?anyerror) !StringList {
|
||||
const uid_range = interop.getUserIdRange(allocator, login_defs_path) catch |err| no_uid_range: {
|
||||
uid_range_error.* = err;
|
||||
break :no_uid_range UidRange{
|
||||
@@ -1606,20 +1798,16 @@ fn getAllUsernames(allocator: std.mem.Allocator, login_defs_path: []const u8, ui
|
||||
return usernames;
|
||||
}
|
||||
|
||||
fn adjustBrightness(allocator: std.mem.Allocator, cmd: []const u8) !void {
|
||||
fn adjustBrightness(allocator: Allocator, cmd: []const u8) !void {
|
||||
var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", cmd }, allocator);
|
||||
brightness.stdout_behavior = .Ignore;
|
||||
brightness.stderr_behavior = .Ignore;
|
||||
|
||||
handle_brightness_cmd: {
|
||||
const process_result = brightness.spawnAndWait() catch {
|
||||
break :handle_brightness_cmd;
|
||||
};
|
||||
const process_result = brightness.spawnAndWait() catch return;
|
||||
if (process_result.Exited != 0) {
|
||||
return error.BrightnessChangeFailed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn getBatteryPercentage(battery_id: []const u8) !u8 {
|
||||
const path = try std.fmt.allocPrint(temporary_allocator, "/sys/class/power_supply/{s}/capacity", .{battery_id});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Random = std.Random;
|
||||
|
||||
const ly_core = @import("ly-core");
|
||||
@@ -7,10 +8,14 @@ const LogFile = ly_core.LogFile;
|
||||
pub const termbox = @import("termbox2");
|
||||
|
||||
const Cell = @import("Cell.zig");
|
||||
const keyboard = @import("keyboard.zig");
|
||||
const Position = @import("Position.zig");
|
||||
|
||||
const TerminalBuffer = @This();
|
||||
|
||||
const KeybindCallbackFn = *const fn (*anyopaque) anyerror!bool;
|
||||
const KeybindMap = std.AutoHashMap(keyboard.Key, KeybindCallbackFn);
|
||||
|
||||
pub const InitOptions = struct {
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
@@ -59,6 +64,7 @@ pub const Color = struct {
|
||||
|
||||
pub const START_POSITION = Position.init(0, 0);
|
||||
|
||||
log_file: *LogFile,
|
||||
random: Random,
|
||||
width: usize,
|
||||
height: usize,
|
||||
@@ -78,8 +84,9 @@ box_chars: struct {
|
||||
blank_cell: Cell,
|
||||
full_color: bool,
|
||||
termios: ?std.posix.termios,
|
||||
keybinds: KeybindMap,
|
||||
|
||||
pub fn init(options: InitOptions, log_file: *LogFile, random: Random) !TerminalBuffer {
|
||||
pub fn init(allocator: Allocator, options: InitOptions, log_file: *LogFile, random: Random) !TerminalBuffer {
|
||||
// Initialize termbox
|
||||
_ = termbox.tb_init();
|
||||
|
||||
@@ -101,6 +108,7 @@ pub fn init(options: InitOptions, log_file: *LogFile, random: Random) !TerminalB
|
||||
try log_file.info("tui", "screen resolution is {d}x{d}", .{ width, height });
|
||||
|
||||
return .{
|
||||
.log_file = log_file,
|
||||
.random = random,
|
||||
.width = width,
|
||||
.height = height,
|
||||
@@ -130,9 +138,15 @@ pub fn init(options: InitOptions, log_file: *LogFile, random: Random) !TerminalB
|
||||
.full_color = options.full_color,
|
||||
// Needed to reclaim the TTY after giving up its control
|
||||
.termios = try std.posix.tcgetattr(std.posix.STDIN_FILENO),
|
||||
.keybinds = KeybindMap.init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *TerminalBuffer) void {
|
||||
self.keybinds.deinit();
|
||||
TerminalBuffer.shutdownStatic();
|
||||
}
|
||||
|
||||
pub fn getWidthStatic() usize {
|
||||
return @intCast(termbox.tb_width());
|
||||
}
|
||||
@@ -205,6 +219,57 @@ pub fn cascade(self: TerminalBuffer) bool {
|
||||
return changed;
|
||||
}
|
||||
|
||||
pub fn registerKeybind(self: *TerminalBuffer, keybind: []const u8, callback: KeybindCallbackFn) !void {
|
||||
var key = std.mem.zeroes(keyboard.Key);
|
||||
var iterator = std.mem.splitScalar(u8, keybind, '+');
|
||||
|
||||
while (iterator.next()) |item| {
|
||||
var found = false;
|
||||
|
||||
inline for (std.meta.fields(keyboard.Key)) |field| {
|
||||
if (std.ascii.eqlIgnoreCase(field.name, item)) {
|
||||
@field(key, field.name) = true;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
try self.log_file.err(
|
||||
"tui",
|
||||
"failed to parse key {s} of keybind {s}",
|
||||
.{ item, keybind },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.keybinds.put(key, callback) catch |err| {
|
||||
try self.log_file.err(
|
||||
"tui",
|
||||
"failed to register keybind {s}: {s}",
|
||||
.{ keybind, @errorName(err) },
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
pub fn handleKeybind(
|
||||
self: *TerminalBuffer,
|
||||
allocator: Allocator,
|
||||
tb_event: termbox.tb_event,
|
||||
context: *anyopaque,
|
||||
) !bool {
|
||||
var keys = try keyboard.getKeyList(allocator, tb_event);
|
||||
defer keys.deinit(allocator);
|
||||
|
||||
for (keys.items) |key| {
|
||||
if (self.keybinds.get(key)) |callback| {
|
||||
return @call(.auto, callback, .{context});
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn drawText(
|
||||
text: []const u8,
|
||||
x: usize,
|
||||
|
||||
735
src/tui/keyboard.zig
Normal file
735
src/tui/keyboard.zig
Normal file
@@ -0,0 +1,735 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const KeyList = std.ArrayList(Key);
|
||||
|
||||
const TerminalBuffer = @import("TerminalBuffer.zig");
|
||||
const termbox = TerminalBuffer.termbox;
|
||||
|
||||
pub const Key = packed struct {
|
||||
ctrl: bool,
|
||||
shift: bool,
|
||||
alt: bool,
|
||||
|
||||
f1: bool,
|
||||
f2: bool,
|
||||
f3: bool,
|
||||
f4: bool,
|
||||
f5: bool,
|
||||
f6: bool,
|
||||
f7: bool,
|
||||
f8: bool,
|
||||
f9: bool,
|
||||
f10: bool,
|
||||
f11: bool,
|
||||
f12: bool,
|
||||
|
||||
insert: bool,
|
||||
delete: bool,
|
||||
home: bool,
|
||||
end: bool,
|
||||
pageup: bool,
|
||||
pagedown: bool,
|
||||
up: bool,
|
||||
down: bool,
|
||||
left: bool,
|
||||
right: bool,
|
||||
tab: bool,
|
||||
backspace: bool,
|
||||
enter: bool,
|
||||
space: bool,
|
||||
|
||||
@"!": bool,
|
||||
@"`": bool,
|
||||
esc: bool,
|
||||
@"[": bool,
|
||||
@"\\": bool,
|
||||
@"]": bool,
|
||||
@"/": bool,
|
||||
_: bool,
|
||||
@"'": bool,
|
||||
@"\"": bool,
|
||||
@",": bool,
|
||||
@"-": bool,
|
||||
@".": bool,
|
||||
@"#": bool,
|
||||
@"$": bool,
|
||||
@"%": bool,
|
||||
@"&": bool,
|
||||
@"*": bool,
|
||||
@"(": bool,
|
||||
@")": bool,
|
||||
@"+": bool,
|
||||
@"=": bool,
|
||||
@":": bool,
|
||||
@";": bool,
|
||||
@"<": bool,
|
||||
@">": bool,
|
||||
@"?": bool,
|
||||
@"@": bool,
|
||||
@"^": bool,
|
||||
@"~": bool,
|
||||
@"{": bool,
|
||||
@"}": bool,
|
||||
@"|": bool,
|
||||
|
||||
@"0": bool,
|
||||
@"1": bool,
|
||||
@"2": bool,
|
||||
@"3": bool,
|
||||
@"4": bool,
|
||||
@"5": bool,
|
||||
@"6": bool,
|
||||
@"7": bool,
|
||||
@"8": bool,
|
||||
@"9": bool,
|
||||
|
||||
a: bool,
|
||||
b: bool,
|
||||
c: bool,
|
||||
d: bool,
|
||||
e: bool,
|
||||
f: bool,
|
||||
g: bool,
|
||||
h: bool,
|
||||
i: bool,
|
||||
j: bool,
|
||||
k: bool,
|
||||
l: bool,
|
||||
m: bool,
|
||||
n: bool,
|
||||
o: bool,
|
||||
p: bool,
|
||||
q: bool,
|
||||
r: bool,
|
||||
s: bool,
|
||||
t: bool,
|
||||
u: bool,
|
||||
v: bool,
|
||||
w: bool,
|
||||
x: bool,
|
||||
y: bool,
|
||||
z: bool,
|
||||
};
|
||||
|
||||
pub fn getKeyList(allocator: Allocator, tb_event: termbox.tb_event) !KeyList {
|
||||
var keys: KeyList = .empty;
|
||||
var key = std.mem.zeroes(Key);
|
||||
|
||||
if (tb_event.mod & termbox.TB_MOD_CTRL != 0) key.ctrl = true;
|
||||
if (tb_event.mod & termbox.TB_MOD_SHIFT != 0) key.shift = true;
|
||||
if (tb_event.mod & termbox.TB_MOD_ALT != 0) key.alt = true;
|
||||
|
||||
if (tb_event.key == termbox.TB_KEY_BACK_TAB) {
|
||||
key.shift = true;
|
||||
key.tab = true;
|
||||
} else if (tb_event.key > termbox.TB_KEY_BACK_TAB) {
|
||||
const code = 0xFFFF - tb_event.key;
|
||||
|
||||
switch (code) {
|
||||
0 => key.f1 = true,
|
||||
1 => key.f2 = true,
|
||||
2 => key.f3 = true,
|
||||
3 => key.f4 = true,
|
||||
4 => key.f5 = true,
|
||||
5 => key.f6 = true,
|
||||
6 => key.f7 = true,
|
||||
7 => key.f8 = true,
|
||||
8 => key.f9 = true,
|
||||
9 => key.f10 = true,
|
||||
10 => key.f11 = true,
|
||||
11 => key.f12 = true,
|
||||
12 => key.insert = true,
|
||||
13 => key.delete = true,
|
||||
14 => key.home = true,
|
||||
15 => key.end = true,
|
||||
16 => key.pageup = true,
|
||||
17 => key.pagedown = true,
|
||||
18 => key.up = true,
|
||||
19 => key.down = true,
|
||||
20 => key.left = true,
|
||||
21 => key.right = true,
|
||||
else => {},
|
||||
}
|
||||
} else if (tb_event.ch < 128) {
|
||||
const code = if (tb_event.ch == 0 and tb_event.key < 128) tb_event.key else tb_event.ch;
|
||||
|
||||
switch (code) {
|
||||
0 => {
|
||||
key.ctrl = true;
|
||||
key.@"2" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"`" = true;
|
||||
},
|
||||
1 => {
|
||||
key.ctrl = true;
|
||||
key.a = true;
|
||||
},
|
||||
2 => {
|
||||
key.ctrl = true;
|
||||
key.b = true;
|
||||
},
|
||||
3 => {
|
||||
key.ctrl = true;
|
||||
key.c = true;
|
||||
},
|
||||
4 => {
|
||||
key.ctrl = true;
|
||||
key.d = true;
|
||||
},
|
||||
5 => {
|
||||
key.ctrl = true;
|
||||
key.e = true;
|
||||
},
|
||||
6 => {
|
||||
key.ctrl = true;
|
||||
key.f = true;
|
||||
},
|
||||
7 => {
|
||||
key.ctrl = true;
|
||||
key.g = true;
|
||||
},
|
||||
8 => {
|
||||
key.ctrl = true;
|
||||
key.h = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.backspace = true;
|
||||
},
|
||||
9 => {
|
||||
key.ctrl = true;
|
||||
key.i = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.tab = true;
|
||||
},
|
||||
10 => {
|
||||
key.ctrl = true;
|
||||
key.j = true;
|
||||
},
|
||||
11 => {
|
||||
key.ctrl = true;
|
||||
key.k = true;
|
||||
},
|
||||
12 => {
|
||||
key.ctrl = true;
|
||||
key.l = true;
|
||||
},
|
||||
13 => {
|
||||
key.ctrl = true;
|
||||
key.m = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.enter = true;
|
||||
},
|
||||
14 => {
|
||||
key.ctrl = true;
|
||||
key.n = true;
|
||||
},
|
||||
15 => {
|
||||
key.ctrl = true;
|
||||
key.o = true;
|
||||
},
|
||||
16 => {
|
||||
key.ctrl = true;
|
||||
key.p = true;
|
||||
},
|
||||
17 => {
|
||||
key.ctrl = true;
|
||||
key.q = true;
|
||||
},
|
||||
18 => {
|
||||
key.ctrl = true;
|
||||
key.r = true;
|
||||
},
|
||||
19 => {
|
||||
key.ctrl = true;
|
||||
key.s = true;
|
||||
},
|
||||
20 => {
|
||||
key.ctrl = true;
|
||||
key.t = true;
|
||||
},
|
||||
21 => {
|
||||
key.ctrl = true;
|
||||
key.u = true;
|
||||
},
|
||||
22 => {
|
||||
key.ctrl = true;
|
||||
key.v = true;
|
||||
},
|
||||
23 => {
|
||||
key.ctrl = true;
|
||||
key.w = true;
|
||||
},
|
||||
24 => {
|
||||
key.ctrl = true;
|
||||
key.x = true;
|
||||
},
|
||||
25 => {
|
||||
key.ctrl = true;
|
||||
key.y = true;
|
||||
},
|
||||
26 => {
|
||||
key.ctrl = true;
|
||||
key.z = true;
|
||||
},
|
||||
27 => {
|
||||
key.ctrl = true;
|
||||
key.@"3" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.esc = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"[" = true;
|
||||
},
|
||||
28 => {
|
||||
key.ctrl = true;
|
||||
key.@"4" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"\\" = true;
|
||||
},
|
||||
29 => {
|
||||
key.ctrl = true;
|
||||
key.@"5" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"]" = true;
|
||||
},
|
||||
30 => {
|
||||
key.ctrl = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"6" = true;
|
||||
},
|
||||
31 => {
|
||||
key.ctrl = true;
|
||||
key.@"7" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"/" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key._ = true;
|
||||
},
|
||||
32 => {
|
||||
key.space = true;
|
||||
},
|
||||
33 => {
|
||||
key.shift = true;
|
||||
key.@"1" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"!" = true;
|
||||
},
|
||||
34 => {
|
||||
key.shift = true;
|
||||
key.@"2" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"\"" = true;
|
||||
},
|
||||
35 => {
|
||||
key.shift = true;
|
||||
key.@"3" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"#" = true;
|
||||
},
|
||||
36 => {
|
||||
key.shift = true;
|
||||
key.@"4" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"$" = true;
|
||||
},
|
||||
37 => {
|
||||
key.shift = true;
|
||||
key.@"5" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"%" = true;
|
||||
},
|
||||
38 => {
|
||||
key.shift = true;
|
||||
key.@"6" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"&" = true;
|
||||
},
|
||||
39 => {
|
||||
key.@"'" = true;
|
||||
},
|
||||
40 => {
|
||||
key.shift = true;
|
||||
key.@"9" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"(" = true;
|
||||
},
|
||||
41 => {
|
||||
key.shift = true;
|
||||
key.@"0" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@")" = true;
|
||||
},
|
||||
42 => {
|
||||
key.shift = true;
|
||||
key.@"8" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"*" = true;
|
||||
},
|
||||
43 => {
|
||||
key.shift = true;
|
||||
key.@"7" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"+" = true;
|
||||
},
|
||||
44 => {
|
||||
key.@"," = true;
|
||||
},
|
||||
45 => {
|
||||
key.@"-" = true;
|
||||
},
|
||||
46 => {
|
||||
key.@"." = true;
|
||||
},
|
||||
47 => {
|
||||
key.@"/" = true;
|
||||
},
|
||||
48 => {
|
||||
key.@"0" = true;
|
||||
},
|
||||
49 => {
|
||||
key.@"1" = true;
|
||||
},
|
||||
50 => {
|
||||
key.@"2" = true;
|
||||
},
|
||||
51 => {
|
||||
key.@"3" = true;
|
||||
},
|
||||
52 => {
|
||||
key.@"4" = true;
|
||||
},
|
||||
53 => {
|
||||
key.@"5" = true;
|
||||
},
|
||||
54 => {
|
||||
key.@"6" = true;
|
||||
},
|
||||
55 => {
|
||||
key.@"7" = true;
|
||||
},
|
||||
56 => {
|
||||
key.@"8" = true;
|
||||
},
|
||||
57 => {
|
||||
key.@"9" = true;
|
||||
},
|
||||
58 => {
|
||||
key.shift = true;
|
||||
key.@":" = true;
|
||||
},
|
||||
59 => {
|
||||
key.@";" = true;
|
||||
},
|
||||
60 => {
|
||||
key.shift = true;
|
||||
key.@"<" = true;
|
||||
},
|
||||
61 => {
|
||||
key.@"=" = true;
|
||||
},
|
||||
62 => {
|
||||
key.shift = true;
|
||||
key.@">" = true;
|
||||
},
|
||||
63 => {
|
||||
key.shift = true;
|
||||
key.@"?" = true;
|
||||
},
|
||||
64 => {
|
||||
key.shift = true;
|
||||
key.@"2" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"@" = true;
|
||||
},
|
||||
65 => {
|
||||
key.shift = true;
|
||||
key.a = true;
|
||||
},
|
||||
66 => {
|
||||
key.shift = true;
|
||||
key.b = true;
|
||||
},
|
||||
67 => {
|
||||
key.shift = true;
|
||||
key.c = true;
|
||||
},
|
||||
68 => {
|
||||
key.shift = true;
|
||||
key.d = true;
|
||||
},
|
||||
69 => {
|
||||
key.shift = true;
|
||||
key.e = true;
|
||||
},
|
||||
70 => {
|
||||
key.shift = true;
|
||||
key.f = true;
|
||||
},
|
||||
71 => {
|
||||
key.shift = true;
|
||||
key.g = true;
|
||||
},
|
||||
72 => {
|
||||
key.shift = true;
|
||||
key.h = true;
|
||||
},
|
||||
73 => {
|
||||
key.shift = true;
|
||||
key.i = true;
|
||||
},
|
||||
74 => {
|
||||
key.shift = true;
|
||||
key.j = true;
|
||||
},
|
||||
75 => {
|
||||
key.shift = true;
|
||||
key.k = true;
|
||||
},
|
||||
76 => {
|
||||
key.shift = true;
|
||||
key.l = true;
|
||||
},
|
||||
77 => {
|
||||
key.shift = true;
|
||||
key.m = true;
|
||||
},
|
||||
78 => {
|
||||
key.shift = true;
|
||||
key.n = true;
|
||||
},
|
||||
79 => {
|
||||
key.shift = true;
|
||||
key.o = true;
|
||||
},
|
||||
80 => {
|
||||
key.shift = true;
|
||||
key.p = true;
|
||||
},
|
||||
81 => {
|
||||
key.shift = true;
|
||||
key.q = true;
|
||||
},
|
||||
82 => {
|
||||
key.shift = true;
|
||||
key.r = true;
|
||||
},
|
||||
83 => {
|
||||
key.shift = true;
|
||||
key.s = true;
|
||||
},
|
||||
84 => {
|
||||
key.shift = true;
|
||||
key.t = true;
|
||||
},
|
||||
85 => {
|
||||
key.shift = true;
|
||||
key.u = true;
|
||||
},
|
||||
86 => {
|
||||
key.shift = true;
|
||||
key.v = true;
|
||||
},
|
||||
87 => {
|
||||
key.shift = true;
|
||||
key.w = true;
|
||||
},
|
||||
88 => {
|
||||
key.shift = true;
|
||||
key.x = true;
|
||||
},
|
||||
89 => {
|
||||
key.shift = true;
|
||||
key.y = true;
|
||||
},
|
||||
90 => {
|
||||
key.shift = true;
|
||||
key.z = true;
|
||||
},
|
||||
91 => {
|
||||
key.@"[" = true;
|
||||
},
|
||||
92 => {
|
||||
key.@"\\" = true;
|
||||
},
|
||||
93 => {
|
||||
key.@"]" = true;
|
||||
},
|
||||
94 => {
|
||||
key.shift = true;
|
||||
key.@"6" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"^" = true;
|
||||
},
|
||||
95 => {
|
||||
key.shift = true;
|
||||
key.@"-" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key._ = true;
|
||||
},
|
||||
96 => {
|
||||
key.@"`" = true;
|
||||
},
|
||||
97 => {
|
||||
key.a = true;
|
||||
},
|
||||
98 => {
|
||||
key.b = true;
|
||||
},
|
||||
99 => {
|
||||
key.c = true;
|
||||
},
|
||||
100 => {
|
||||
key.d = true;
|
||||
},
|
||||
101 => {
|
||||
key.e = true;
|
||||
},
|
||||
102 => {
|
||||
key.f = true;
|
||||
},
|
||||
103 => {
|
||||
key.g = true;
|
||||
},
|
||||
104 => {
|
||||
key.h = true;
|
||||
},
|
||||
105 => {
|
||||
key.i = true;
|
||||
},
|
||||
106 => {
|
||||
key.j = true;
|
||||
},
|
||||
107 => {
|
||||
key.k = true;
|
||||
},
|
||||
108 => {
|
||||
key.l = true;
|
||||
},
|
||||
109 => {
|
||||
key.m = true;
|
||||
},
|
||||
110 => {
|
||||
key.n = true;
|
||||
},
|
||||
111 => {
|
||||
key.o = true;
|
||||
},
|
||||
112 => {
|
||||
key.p = true;
|
||||
},
|
||||
113 => {
|
||||
key.q = true;
|
||||
},
|
||||
114 => {
|
||||
key.r = true;
|
||||
},
|
||||
115 => {
|
||||
key.s = true;
|
||||
},
|
||||
116 => {
|
||||
key.t = true;
|
||||
},
|
||||
117 => {
|
||||
key.u = true;
|
||||
},
|
||||
118 => {
|
||||
key.v = true;
|
||||
},
|
||||
119 => {
|
||||
key.w = true;
|
||||
},
|
||||
120 => {
|
||||
key.x = true;
|
||||
},
|
||||
121 => {
|
||||
key.y = true;
|
||||
},
|
||||
122 => {
|
||||
key.z = true;
|
||||
},
|
||||
123 => {
|
||||
key.shift = true;
|
||||
key.@"{" = true;
|
||||
},
|
||||
124 => {
|
||||
key.shift = true;
|
||||
key.@"\\" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"|" = true;
|
||||
},
|
||||
125 => {
|
||||
key.shift = true;
|
||||
key.@"}" = true;
|
||||
},
|
||||
126 => {
|
||||
key.shift = true;
|
||||
key.@"`" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"~" = true;
|
||||
},
|
||||
127 => {
|
||||
key.ctrl = true;
|
||||
key.@"8" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.backspace = true;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
try keys.append(allocator, key);
|
||||
|
||||
return keys;
|
||||
}
|
||||
Reference in New Issue
Block a user