mirror of
https://github.com/fairyglade/ly.git
synced 2026-03-25 09:46:06 +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)
|
# Command executed when pressing hibernate key (can be null)
|
||||||
hibernate_cmd = null
|
hibernate_cmd = null
|
||||||
|
|
||||||
# Specifies the key used for hibernate (F1-F12)
|
# Specifies the key used for hibernate
|
||||||
hibernate_key = F4
|
hibernate_key = F4
|
||||||
|
|
||||||
# Remove main box borders
|
# 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
|
# Command executed when pressing restart_key
|
||||||
restart_cmd = /sbin/shutdown -r now
|
restart_cmd = /sbin/shutdown -r now
|
||||||
|
|
||||||
# Specifies the key used for restart (F1-F12)
|
# Specifies the key used for restart
|
||||||
restart_key = F2
|
restart_key = F2
|
||||||
|
|
||||||
# Save the current desktop and login as defaults, and load them on startup
|
# 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
|
# Command executed when pressing shutdown_key
|
||||||
shutdown_cmd = /sbin/shutdown $PLATFORM_SHUTDOWN_ARG now
|
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
|
shutdown_key = F1
|
||||||
|
|
||||||
# Command executed when pressing sleep key (can be null)
|
# Command executed when pressing sleep key (can be null)
|
||||||
sleep_cmd = null
|
sleep_cmd = null
|
||||||
|
|
||||||
# Specifies the key used for sleep (F1-F12)
|
# Specifies the key used for sleep
|
||||||
sleep_key = F3
|
sleep_key = F3
|
||||||
|
|
||||||
# Command executed when starting Ly (before the TTY is taken control of)
|
# Command executed when starting Ly (before the TTY is taken control of)
|
||||||
|
|||||||
556
src/main.zig
556
src/main.zig
@@ -1,4 +1,5 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
const StringList = std.ArrayListUnmanaged([]const u8);
|
const StringList = std.ArrayListUnmanaged([]const u8);
|
||||||
const temporary_allocator = std.heap.page_allocator;
|
const temporary_allocator = std.heap.page_allocator;
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
@@ -66,8 +67,13 @@ fn ttyControlTransferSignalHandler(_: c_int) callconv(.c) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const UiState = struct {
|
const UiState = struct {
|
||||||
|
allocator: Allocator,
|
||||||
auth_fails: u64,
|
auth_fails: u64,
|
||||||
|
run: bool,
|
||||||
update: bool,
|
update: bool,
|
||||||
|
is_autologin: bool,
|
||||||
|
use_kmscon_vt: bool,
|
||||||
|
active_tty: u8,
|
||||||
buffer: *TerminalBuffer,
|
buffer: *TerminalBuffer,
|
||||||
labels_max_length: usize,
|
labels_max_length: usize,
|
||||||
animation_timed_out: bool,
|
animation_timed_out: bool,
|
||||||
@@ -91,6 +97,7 @@ const UiState = struct {
|
|||||||
info_line: *InfoLine,
|
info_line: *InfoLine,
|
||||||
animate: bool,
|
animate: bool,
|
||||||
session: *Session,
|
session: *Session,
|
||||||
|
saved_users: SavedUsers,
|
||||||
login: *UserList,
|
login: *UserList,
|
||||||
password: *Text,
|
password: *Text,
|
||||||
active_input: enums.Input,
|
active_input: enums.Input,
|
||||||
@@ -99,15 +106,18 @@ const UiState = struct {
|
|||||||
config: Config,
|
config: Config,
|
||||||
lang: Lang,
|
lang: Lang,
|
||||||
log_file: *LogFile,
|
log_file: *LogFile,
|
||||||
|
save_path: []const u8,
|
||||||
|
old_save_path: ?[]const u8,
|
||||||
battery_buf: [16:0]u8,
|
battery_buf: [16:0]u8,
|
||||||
bigclock_format_buf: [16:0]u8,
|
bigclock_format_buf: [16:0]u8,
|
||||||
clock_buf: [64:0]u8,
|
clock_buf: [64:0]u8,
|
||||||
bigclock_buf: [32:0]u8,
|
bigclock_buf: [32:0]u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var shutdown = false;
|
||||||
|
var restart = false;
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var shutdown = false;
|
|
||||||
var restart = false;
|
|
||||||
var shutdown_cmd: []const u8 = undefined;
|
var shutdown_cmd: []const u8 = undefined;
|
||||||
var restart_cmd: []const u8 = undefined;
|
var restart_cmd: []const u8 = undefined;
|
||||||
var commands_allocated = false;
|
var commands_allocated = false;
|
||||||
@@ -324,10 +334,15 @@ pub fn main() !void {
|
|||||||
.full_color = config.full_color,
|
.full_color = config.full_color,
|
||||||
.is_tty = true,
|
.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 {
|
defer {
|
||||||
log_file.info("tui", "shutting down terminal buffer", .{}) catch {};
|
log_file.info("tui", "shutting down terminal buffer", .{}) catch {};
|
||||||
TerminalBuffer.shutdownStatic();
|
buffer.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
const act = std.posix.Sigaction{
|
const act = std.posix.Sigaction{
|
||||||
@@ -707,10 +722,28 @@ pub fn main() !void {
|
|||||||
is_autologin = true;
|
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 animation: ?Animation = null;
|
||||||
var state = UiState{
|
var state = UiState{
|
||||||
|
.allocator = allocator,
|
||||||
.auth_fails = 0,
|
.auth_fails = 0,
|
||||||
|
.run = true,
|
||||||
.update = true,
|
.update = true,
|
||||||
|
.is_autologin = is_autologin,
|
||||||
|
.use_kmscon_vt = use_kmscon_vt,
|
||||||
|
.active_tty = active_tty,
|
||||||
.buffer = &buffer,
|
.buffer = &buffer,
|
||||||
.labels_max_length = labels_max_length,
|
.labels_max_length = labels_max_length,
|
||||||
.animation_timed_out = false,
|
.animation_timed_out = false,
|
||||||
@@ -734,6 +767,7 @@ pub fn main() !void {
|
|||||||
.info_line = &info_line,
|
.info_line = &info_line,
|
||||||
.animate = config.animation != .none,
|
.animate = config.animation != .none,
|
||||||
.session = &session,
|
.session = &session,
|
||||||
|
.saved_users = saved_users,
|
||||||
.login = &login,
|
.login = &login,
|
||||||
.password = &password,
|
.password = &password,
|
||||||
.active_input = config.default_input,
|
.active_input = config.default_input,
|
||||||
@@ -745,6 +779,8 @@ pub fn main() !void {
|
|||||||
.config = config,
|
.config = config,
|
||||||
.lang = lang,
|
.lang = lang,
|
||||||
.log_file = &log_file,
|
.log_file = &log_file,
|
||||||
|
.save_path = save_path,
|
||||||
|
.old_save_path = if (old_save_parser != null) old_save_path else null,
|
||||||
.battery_buf = undefined,
|
.battery_buf = undefined,
|
||||||
.bigclock_format_buf = undefined,
|
.bigclock_format_buf = undefined,
|
||||||
.clock_buf = undefined,
|
.clock_buf = undefined,
|
||||||
@@ -775,7 +811,7 @@ pub fn main() !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position components
|
// Position components and place cursor accordingly
|
||||||
try updateComponents(&state);
|
try updateComponents(&state);
|
||||||
positionComponents(&state);
|
positionComponents(&state);
|
||||||
|
|
||||||
@@ -815,31 +851,37 @@ pub fn main() !void {
|
|||||||
}
|
}
|
||||||
defer if (animation) |*a| a.deinit();
|
defer if (animation) |*a| a.deinit();
|
||||||
|
|
||||||
const shutdown_key = try std.fmt.parseInt(u8, config.shutdown_key[1..], 10);
|
try buffer.registerKeybind("Esc", &disableInsertMode);
|
||||||
const restart_key = try std.fmt.parseInt(u8, config.restart_key[1..], 10);
|
try buffer.registerKeybind("I", &enableInsertMode);
|
||||||
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);
|
try buffer.registerKeybind("Ctrl+C", &quit);
|
||||||
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("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 event: termbox.tb_event = undefined;
|
||||||
var run = true;
|
|
||||||
var inactivity_time_start = try interop.getTimeOfDay();
|
var inactivity_time_start = try interop.getTimeOfDay();
|
||||||
var inactivity_cmd_ran = false;
|
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| {
|
if (config.initial_info_text) |text| {
|
||||||
try info_line.addMessage(text, config.bg, config.fg);
|
try info_line.addMessage(text, config.bg, config.fg);
|
||||||
} else get_host_name: {
|
} else get_host_name: {
|
||||||
@@ -853,7 +895,7 @@ pub fn main() !void {
|
|||||||
try info_line.addMessage(hostname, config.bg, config.fg);
|
try info_line.addMessage(hostname, config.bg, config.fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (run) {
|
while (state.run) {
|
||||||
if (state.update) {
|
if (state.update) {
|
||||||
try updateComponents(&state);
|
try updateComponents(&state);
|
||||||
|
|
||||||
@@ -935,6 +977,9 @@ pub fn main() !void {
|
|||||||
if (event_error < 0) continue;
|
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) {
|
if (event.type == termbox.TB_EVENT_RESIZE) {
|
||||||
state.buffer.width = TerminalBuffer.getWidthStatic();
|
state.buffer.width = TerminalBuffer.getWidthStatic();
|
||||||
state.buffer.height = TerminalBuffer.getHeightStatic();
|
state.buffer.height = TerminalBuffer.getHeightStatic();
|
||||||
@@ -952,120 +997,181 @@ pub fn main() !void {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input of some kind was detected, so reset the inactivity timer
|
const passthrough_event = try buffer.handleKeybind(
|
||||||
inactivity_time_start = try interop.getTimeOfDay();
|
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) {
|
state.update = true;
|
||||||
termbox.TB_KEY_ESC => {
|
}
|
||||||
if (config.vi_mode and state.insert_mode) {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.insert_mode = false;
|
||||||
state.update = true;
|
state.update = true;
|
||||||
}
|
}
|
||||||
},
|
return false;
|
||||||
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;
|
|
||||||
|
|
||||||
handle_sleep_cmd: {
|
fn enableInsertMode(ptr: *anyopaque) !bool {
|
||||||
const process_result = sleep.spawnAndWait() catch {
|
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||||
break :handle_sleep_cmd;
|
if (state.insert_mode) return true;
|
||||||
};
|
|
||||||
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;
|
|
||||||
|
|
||||||
handle_hibernate_cmd: {
|
state.insert_mode = true;
|
||||||
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.update = true;
|
state.update = true;
|
||||||
},
|
return false;
|
||||||
termbox.TB_KEY_CTRL_K, termbox.TB_KEY_ARROW_UP => {
|
}
|
||||||
|
|
||||||
|
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.active_input.move(true, false);
|
||||||
state.update = true;
|
state.update = true;
|
||||||
},
|
return false;
|
||||||
termbox.TB_KEY_CTRL_J, termbox.TB_KEY_ARROW_DOWN => {
|
}
|
||||||
|
|
||||||
|
fn viMoseCursorUp(ptr: *anyopaque) !bool {
|
||||||
|
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||||
|
if (state.insert_mode) return true;
|
||||||
|
|
||||||
state.active_input.move(false, false);
|
state.active_input.move(false, false);
|
||||||
state.update = true;
|
state.update = true;
|
||||||
},
|
return false;
|
||||||
termbox.TB_KEY_TAB => {
|
}
|
||||||
|
|
||||||
|
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.active_input.move(false, true);
|
||||||
state.update = true;
|
state.update = true;
|
||||||
},
|
return false;
|
||||||
termbox.TB_KEY_BACK_TAB => {
|
}
|
||||||
|
|
||||||
|
fn wrapCursorReverse(ptr: *anyopaque) !bool {
|
||||||
|
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
state.active_input.move(true, true);
|
state.active_input.move(true, true);
|
||||||
state.update = true;
|
state.update = true;
|
||||||
},
|
return false;
|
||||||
termbox.TB_KEY_ENTER => authenticate: {
|
}
|
||||||
try log_file.info("auth", "starting authentication", .{});
|
|
||||||
|
|
||||||
if (!config.allow_empty_password and password.text.items.len == 0) {
|
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
|
// Let's not log this message for security reasons
|
||||||
try info_line.addMessage(lang.err_empty_password, config.error_bg, config.error_fg);
|
try state.info_line.addMessage(
|
||||||
info_line.clearRendered(allocator) catch |err| {
|
state.lang.err_empty_password,
|
||||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
state.config.error_bg,
|
||||||
try log_file.err("tui", "failed to clear info line: {s}", .{@errorName(err)});
|
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();
|
_ = TerminalBuffer.presentBufferStatic();
|
||||||
break :authenticate;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try info_line.addMessage(lang.authenticating, config.bg, config.fg);
|
try state.info_line.addMessage(
|
||||||
info_line.clearRendered(allocator) catch |err| {
|
state.lang.authenticating,
|
||||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
state.config.bg,
|
||||||
try log_file.err("tui", "failed to clear info line: {s}", .{@errorName(err)});
|
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)},
|
||||||
|
);
|
||||||
};
|
};
|
||||||
info_line.label.draw();
|
state.info_line.label.draw();
|
||||||
_ = TerminalBuffer.presentBufferStatic();
|
_ = 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
|
// It isn't worth cluttering the code with precise error
|
||||||
// handling, so let's just report a generic error message,
|
// handling, so let's just report a generic error message,
|
||||||
// that should be good enough for debugging anyway.
|
// 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| {
|
var file = std.fs.cwd().createFile(state.save_path, .{}) catch |err| {
|
||||||
log_file.err("sys", "failed to create save file: {s}", .{@errorName(err)}) catch break :save_last_settings;
|
state.log_file.err(
|
||||||
|
"sys",
|
||||||
|
"failed to create save file: {s}",
|
||||||
|
.{@errorName(err)},
|
||||||
|
) catch break :save_last_settings;
|
||||||
break :save_last_settings;
|
break :save_last_settings;
|
||||||
};
|
};
|
||||||
defer file.close();
|
defer file.close();
|
||||||
@@ -1074,8 +1180,8 @@ pub fn main() !void {
|
|||||||
var file_writer = file.writer(&file_buffer);
|
var file_writer = file.writer(&file_buffer);
|
||||||
var writer = &file_writer.interface;
|
var writer = &file_writer.interface;
|
||||||
|
|
||||||
try writer.print("{d}\n", .{login.label.current});
|
try writer.print("{d}\n", .{state.login.label.current});
|
||||||
for (saved_users.user_list.items) |user| {
|
for (state.saved_users.user_list.items) |user| {
|
||||||
try writer.print("{s}:{d}\n", .{ user.username, user.session_index });
|
try writer.print("{s}:{d}\n", .{ user.username, user.session_index });
|
||||||
}
|
}
|
||||||
try writer.flush();
|
try writer.flush();
|
||||||
@@ -1083,8 +1189,8 @@ pub fn main() !void {
|
|||||||
// Delete previous save file if it exists
|
// Delete previous save file if it exists
|
||||||
if (migrator.maybe_save_file) |path| {
|
if (migrator.maybe_save_file) |path| {
|
||||||
std.fs.cwd().deleteFile(path) catch {};
|
std.fs.cwd().deleteFile(path) catch {};
|
||||||
} else if (old_save_parser != null) {
|
} else if (state.old_save_path) |path| {
|
||||||
std.fs.cwd().deleteFile(old_save_path) catch {};
|
std.fs.cwd().deleteFile(path) catch {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1092,28 +1198,28 @@ pub fn main() !void {
|
|||||||
defer shared_err.deinit();
|
defer shared_err.deinit();
|
||||||
|
|
||||||
{
|
{
|
||||||
log_file.deinit();
|
state.log_file.deinit();
|
||||||
|
|
||||||
session_pid = try std.posix.fork();
|
session_pid = try std.posix.fork();
|
||||||
if (session_pid == 0) {
|
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
|
// 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 service_name = if (state.is_autologin) state.config.auto_login_service else state.config.service_name;
|
||||||
const password_text = if (is_autologin) "" else password.text.items;
|
const password_text = if (state.is_autologin) "" else state.password.text.items;
|
||||||
|
|
||||||
const auth_options = auth.AuthOptions{
|
const auth_options = auth.AuthOptions{
|
||||||
.tty = active_tty,
|
.tty = state.active_tty,
|
||||||
.service_name = service_name,
|
.service_name = service_name,
|
||||||
.path = config.path,
|
.path = state.config.path,
|
||||||
.session_log = config.session_log,
|
.session_log = state.config.session_log,
|
||||||
.xauth_cmd = config.xauth_cmd,
|
.xauth_cmd = state.config.xauth_cmd,
|
||||||
.setup_cmd = config.setup_cmd,
|
.setup_cmd = state.config.setup_cmd,
|
||||||
.login_cmd = config.login_cmd,
|
.login_cmd = state.config.login_cmd,
|
||||||
.x_cmd = config.x_cmd,
|
.x_cmd = state.config.x_cmd,
|
||||||
.x_vt = config.x_vt,
|
.x_vt = state.config.x_vt,
|
||||||
.session_pid = session_pid,
|
.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
|
// 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);
|
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);
|
shared_err.writeError(err);
|
||||||
|
|
||||||
log_file.deinit();
|
state.log_file.deinit();
|
||||||
std.process.exit(1);
|
std.process.exit(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
log_file.deinit();
|
state.log_file.deinit();
|
||||||
std.process.exit(0);
|
std.process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1143,33 +1256,45 @@ pub fn main() !void {
|
|||||||
std.Thread.sleep(std.time.ns_per_s * 1);
|
std.Thread.sleep(std.time.ns_per_s * 1);
|
||||||
session_pid = -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();
|
const auth_err = shared_err.readError();
|
||||||
if (auth_err) |err| {
|
if (auth_err) |err| {
|
||||||
state.auth_fails += 1;
|
state.auth_fails += 1;
|
||||||
state.active_input = .password;
|
state.active_input = .password;
|
||||||
|
|
||||||
try info_line.addMessage(getAuthErrorMsg(err, lang), config.error_bg, config.error_fg);
|
try state.info_line.addMessage(
|
||||||
try log_file.err("auth", "failed to authenticate: {s}", .{@errorName(err)});
|
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 {
|
} else {
|
||||||
if (config.logout_cmd) |logout_cmd| {
|
if (state.config.logout_cmd) |logout_cmd| {
|
||||||
var logout_process = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", logout_cmd }, allocator);
|
var logout_process = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", logout_cmd }, state.allocator);
|
||||||
_ = logout_process.spawnAndWait() catch .{};
|
_ = logout_process.spawnAndWait() catch .{};
|
||||||
}
|
}
|
||||||
|
|
||||||
password.clear();
|
state.password.clear();
|
||||||
is_autologin = false;
|
state.is_autologin = false;
|
||||||
try info_line.addMessage(lang.logout, config.bg, config.fg);
|
try state.info_line.addMessage(
|
||||||
try log_file.info("auth", "logged out", .{});
|
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);
|
try TerminalBuffer.clearScreenStatic(true);
|
||||||
state.update = true;
|
state.update = true;
|
||||||
}
|
}
|
||||||
@@ -1177,42 +1302,109 @@ pub fn main() !void {
|
|||||||
// Restore the cursor
|
// Restore the cursor
|
||||||
TerminalBuffer.setCursorStatic(0, 0);
|
TerminalBuffer.setCursorStatic(0, 0);
|
||||||
_ = TerminalBuffer.presentBufferStatic();
|
_ = TerminalBuffer.presentBufferStatic();
|
||||||
},
|
return false;
|
||||||
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 => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (state.active_input) {
|
fn shutdownCmd(ptr: *anyopaque) !bool {
|
||||||
.info_line => info_line.label.handle(&event, state.insert_mode),
|
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||||
.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);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
state.update = true;
|
shutdown = true;
|
||||||
},
|
state.run = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
fn updateComponents(state: *UiState) !void {
|
||||||
@@ -1559,7 +1751,7 @@ fn findSessionByName(session: *Session, name: []const u8) ?usize {
|
|||||||
return null;
|
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: {
|
const uid_range = interop.getUserIdRange(allocator, login_defs_path) catch |err| no_uid_range: {
|
||||||
uid_range_error.* = err;
|
uid_range_error.* = err;
|
||||||
break :no_uid_range UidRange{
|
break :no_uid_range UidRange{
|
||||||
@@ -1606,19 +1798,15 @@ fn getAllUsernames(allocator: std.mem.Allocator, login_defs_path: []const u8, ui
|
|||||||
return usernames;
|
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);
|
var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", cmd }, allocator);
|
||||||
brightness.stdout_behavior = .Ignore;
|
brightness.stdout_behavior = .Ignore;
|
||||||
brightness.stderr_behavior = .Ignore;
|
brightness.stderr_behavior = .Ignore;
|
||||||
|
|
||||||
handle_brightness_cmd: {
|
const process_result = brightness.spawnAndWait() catch return;
|
||||||
const process_result = brightness.spawnAndWait() catch {
|
|
||||||
break :handle_brightness_cmd;
|
|
||||||
};
|
|
||||||
if (process_result.Exited != 0) {
|
if (process_result.Exited != 0) {
|
||||||
return error.BrightnessChangeFailed;
|
return error.BrightnessChangeFailed;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getBatteryPercentage(battery_id: []const u8) !u8 {
|
fn getBatteryPercentage(battery_id: []const u8) !u8 {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
const Random = std.Random;
|
const Random = std.Random;
|
||||||
|
|
||||||
const ly_core = @import("ly-core");
|
const ly_core = @import("ly-core");
|
||||||
@@ -7,10 +8,14 @@ const LogFile = ly_core.LogFile;
|
|||||||
pub const termbox = @import("termbox2");
|
pub const termbox = @import("termbox2");
|
||||||
|
|
||||||
const Cell = @import("Cell.zig");
|
const Cell = @import("Cell.zig");
|
||||||
|
const keyboard = @import("keyboard.zig");
|
||||||
const Position = @import("Position.zig");
|
const Position = @import("Position.zig");
|
||||||
|
|
||||||
const TerminalBuffer = @This();
|
const TerminalBuffer = @This();
|
||||||
|
|
||||||
|
const KeybindCallbackFn = *const fn (*anyopaque) anyerror!bool;
|
||||||
|
const KeybindMap = std.AutoHashMap(keyboard.Key, KeybindCallbackFn);
|
||||||
|
|
||||||
pub const InitOptions = struct {
|
pub const InitOptions = struct {
|
||||||
fg: u32,
|
fg: u32,
|
||||||
bg: u32,
|
bg: u32,
|
||||||
@@ -59,6 +64,7 @@ pub const Color = struct {
|
|||||||
|
|
||||||
pub const START_POSITION = Position.init(0, 0);
|
pub const START_POSITION = Position.init(0, 0);
|
||||||
|
|
||||||
|
log_file: *LogFile,
|
||||||
random: Random,
|
random: Random,
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
@@ -78,8 +84,9 @@ box_chars: struct {
|
|||||||
blank_cell: Cell,
|
blank_cell: Cell,
|
||||||
full_color: bool,
|
full_color: bool,
|
||||||
termios: ?std.posix.termios,
|
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
|
// Initialize termbox
|
||||||
_ = termbox.tb_init();
|
_ = 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 });
|
try log_file.info("tui", "screen resolution is {d}x{d}", .{ width, height });
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
|
.log_file = log_file,
|
||||||
.random = random,
|
.random = random,
|
||||||
.width = width,
|
.width = width,
|
||||||
.height = height,
|
.height = height,
|
||||||
@@ -130,9 +138,15 @@ pub fn init(options: InitOptions, log_file: *LogFile, random: Random) !TerminalB
|
|||||||
.full_color = options.full_color,
|
.full_color = options.full_color,
|
||||||
// Needed to reclaim the TTY after giving up its control
|
// Needed to reclaim the TTY after giving up its control
|
||||||
.termios = try std.posix.tcgetattr(std.posix.STDIN_FILENO),
|
.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 {
|
pub fn getWidthStatic() usize {
|
||||||
return @intCast(termbox.tb_width());
|
return @intCast(termbox.tb_width());
|
||||||
}
|
}
|
||||||
@@ -205,6 +219,57 @@ pub fn cascade(self: TerminalBuffer) bool {
|
|||||||
return changed;
|
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(
|
pub fn drawText(
|
||||||
text: []const u8,
|
text: []const u8,
|
||||||
x: usize,
|
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