6 Commits

Author SHA1 Message Date
9cfe00c197 fix: pass clang-tidy 2026-01-16 01:03:32 +01:00
5749263605 bodge: shut linter 2026-01-16 00:51:05 +01:00
e5c3c619c3 Merge branch 'master' into support-xorg 2026-01-16 00:44:58 +01:00
Martin Bogdanov
4eca2b056f docs,makefile: put conventional systemd service path (#94)
`/etc/systemd` -> `/usr/local/lib/systemd`
2026-01-15 23:52:46 +01:00
creations
a7a1f42f0a feat: add support for fido yubikeys (#89)
* add support for fido keybind

* add to themes

* fix clang format

* Update ui.c

* docs: add misc stuff about the yubikey

---------

Co-authored-by: javalsai <jvssxxi@gmail.com>
2025-12-13 21:37:58 +01:00
dariuskl
feeba5c41b feat: don't trunc long hostnames if there is space (#87)
Instead of restricting the length of the hostname field to the size of
the column, the hostname is allowed to utilize all space that is not
taken up by the time string.

Co-authored-by: Darius Kellermann <kellermann@pm.me>
2025-11-16 17:29:59 +01:00
16 changed files with 195 additions and 51 deletions

View File

@@ -58,7 +58,7 @@ install: lidm
uninstall: uninstall:
rm -rf ${DESTDIR}${PREFIX}/bin/lidm ${DESTDIR}/etc/lidm.ini rm -rf ${DESTDIR}${PREFIX}/bin/lidm ${DESTDIR}/etc/lidm.ini
rm -rf ${DESTDIR}/usr/share/man/man{1/lidm.1,5/lidm-config.5}.gz rm -rf ${DESTDIR}/usr/share/man/man{1/lidm.1,5/lidm-config.5}.gz
rm -rf ${DESTDIR}/etc/systemd/system/lidm.service ${DESTDIR}/etc/dinit.d/lidm ${DESTDIR}/etc/runit/sv/lidm rm -rf ${DESTDIR}/usr/local/lib/systemd/system/lidm.service ${DESTDIR}/etc/dinit.d/lidm ${DESTDIR}/etc/runit/sv/lidm
install-service: install-service:
@if command -v systemctl &> /dev/null; then \ @if command -v systemctl &> /dev/null; then \
@@ -88,7 +88,7 @@ install-service:
fi fi
install-service-systemd: install-service-systemd:
install -m644 ./assets/services/systemd.service ${DESTDIR}/etc/systemd/system/lidm.service install -m644 ./assets/services/systemd.service ${DESTDIR}/usr/local/lib/systemd/system/lidm.service
@printf '\033[1m%s\033[0m\n\n' " don't forget to run 'systemctl enable lidm'" @printf '\033[1m%s\033[0m\n\n' " don't forget to run 'systemctl enable lidm'"
install-service-dinit: install-service-dinit:
install -m644 ./assets/services/dinit ${DESTDIR}/etc/dinit.d/lidm install -m644 ./assets/services/dinit ${DESTDIR}/etc/dinit.d/lidm

View File

@@ -27,10 +27,11 @@ kmscon -l --vt /dev/tty7 --font-name "Cascadia Code" -- /usr/bin/lidm
## Features ## Features
- Simple as C, you only need a C compiler and standard unix libraries to build this. - Simple as C, meant to depend only on standard libc and basic unix system headers.
- Fully customizable, from strings, including action keys, to colors (I hope you know ansi escape codes) - Fully customizable: ALL strings, colors (with its ANSI keys) and most behavior.
- Automatically detects xorg and wayland sessions, plus allowing to launch the default user shell (if enabled in config) - Xorg[\*](https://github.com/javalsai/lidm/issues/79) and wayland sessions, while supporting the default user shell (if enabled in config)
- Starts with many init systems (systemd, dinit, runit, openrc and s6). - Init agnostinc (systemd, dinit, runit, openrc and s6).
- Supports [fido yubikeys](./docs/yubikey.md) (via pam_u2f).
# Table of Contents # Table of Contents

View File

@@ -69,13 +69,13 @@ Characters for the corners of the box (ctl = corner top left, cbr = corner botto
.SS functions .SS functions
All these are of type \fBKEY\fP. All these are of type \fBKEY\fP.
.TP .TP
\fBpoweroff, reboot, refresh\fP \fBpoweroff, reboot, fido, refresh\fP
Function key to use for such action. Function key to use for such action.
.SS strings .SS strings
Display strings to use for some elements. Display strings to use for some elements.
.TP .TP
\fBf_poweroff, f_reboot, f_refresh\fP \fBf_poweroff, f_reboot, f_fido, f_refresh\fP
Text displayed to name such functions at the bottom of the screen. Text displayed to name such functions at the bottom of the screen.
.TP .TP
\fBe_user, e_passwd\fP \fBe_user, e_passwd\fP

View File

@@ -13,7 +13,7 @@ The manual steps for installation are:
## Systemd ## Systemd
- Copy `systemd.service` to `/etc/systemd/system/lidm.service` - Copy `systemd.service` to `/usr/local/lib/systemd/system/lidm.service` (if the directory doesn't exist, create it first)
- To enable it you can run `systemctl enable lidm` - To enable it you can run `systemctl enable lidm`
## Dinit ## Dinit

30
docs/yubikey.md Normal file
View File

@@ -0,0 +1,30 @@
# Yubikeys
Quick reference explaining how yubikeys work for now.
# Enable
Yubikeys are disabled by default, to enable them activate a keybinding for it (`[functions] fido`) in the config file.
Note that pressing this configured keybinding has no difference from trying to log in with an empty password, there's virtually no difference.
`pam_u2f` must be configured with a registered key (`pamu2fcfg`).
# Extra
All my yubikey knowledge comes from the [pr that implemented this](https://github.com/javalsai/lidm/pull/89), please refer to it for extra details. Contributions to this documentation are welcome (explaining more in detail, potential issues... really anything that improves this).
Allegedly this pam module configuration should work:
```pam
#%PAM-1.0
auth sufficient pam_u2f.so cue
auth requisite pam_nologin.so
auth include system-local-login
account include system-local-login
session include system-local-login
password include system-local-login
```
Also, I recommend giving the [arch wiki](https://wiki.archlinux.org/title/YubiKey) a read anyways.

View File

@@ -92,6 +92,7 @@ BUILD(chars, CHARS, TABLE_CHARS);
#define TABLE_FUNCTIONS(F, name) \ #define TABLE_FUNCTIONS(F, name) \
F(enum Keys, poweroff, KEY, F1, name) \ F(enum Keys, poweroff, KEY, F1, name) \
F(enum Keys, reboot, KEY, F2, name) \ F(enum Keys, reboot, KEY, F2, name) \
F(enum Keys, fido, KEY, NONE, name) \
F(enum Keys, refresh, KEY, F5, name) F(enum Keys, refresh, KEY, F5, name)
BUILD(functions, FUNCTIONS, TABLE_FUNCTIONS); BUILD(functions, FUNCTIONS, TABLE_FUNCTIONS);
@@ -99,6 +100,7 @@ BUILD(functions, FUNCTIONS, TABLE_FUNCTIONS);
#define TABLE_STRINGS(F, name) \ #define TABLE_STRINGS(F, name) \
F(char* NNULLABLE, f_poweroff, STRING, "poweroff", name) \ F(char* NNULLABLE, f_poweroff, STRING, "poweroff", name) \
F(char* NNULLABLE, f_reboot, STRING, "reboot", name) \ F(char* NNULLABLE, f_reboot, STRING, "reboot", name) \
F(char* NNULLABLE, f_fido, STRING, "fido", name) \
F(char* NNULLABLE, f_refresh, STRING, "refresh", name) \ F(char* NNULLABLE, f_refresh, STRING, "refresh", name) \
F(char* NNULLABLE, e_user, STRING, "user", name) \ F(char* NNULLABLE, e_user, STRING, "user", name) \
F(char* NNULLABLE, e_passwd, STRING, "password", name) \ F(char* NNULLABLE, e_passwd, STRING, "password", name) \

View File

@@ -1,10 +1,14 @@
#ifndef DESKTOP_EXEC_H_ // TODO: rewrite properly
#define DESKTOP_EXEC_H_ // NOLINTBEGIN(clang-diagnostic-nullability-completeness)
#include "macros.h" #ifndef DESKTOP_EXEC_H_
#define DESKTOP_EXEC_H_
#include "macros.h"
int execvpe_desktop(char** args, char* NNULLABLE* NNULLABLE envlist); int execvpe_desktop(char** args, char* NNULLABLE* NNULLABLE envlist);
int parse_exec_string(const char* exec_s, int* arg_count, char*** args); int parse_exec_string(const char* exec_s, int* arg_count, char*** args);
void free_parsed_args(int arg_count, char** args); void free_parsed_args(int arg_count, char** args);
#endif #endif
// NOLINTEND(clang-diagnostic-nullability-completeness)

View File

@@ -32,9 +32,11 @@ enum Keys {
END, END,
PAGE_UP, PAGE_UP,
PAGE_DOWN, PAGE_DOWN,
NONE,
}; };
static const char* const KEY_NAMES[] = { static const char* const KEY_NAMES[] = {
[NONE] = "NONE",
[ESC] = "ESC", [ESC] = "ESC",
[F1] = "F1", [F1] = "F1",
[F2] = "F2", [F2] = "F2",

View File

@@ -7,25 +7,25 @@
#endif #endif
// Do we just replace the compiler with clang?? // Do we just replace the compiler with clang??
#if defined(__clang__) #ifdef __clang__
#define NULLABLE _Nullable #define NULLABLE _Nullable
#else #else
#define NULLABLE #define NULLABLE
#endif #endif
#if defined(__clang__) #ifdef __clang__
#define NNULLABLE _Nonnull #define NNULLABLE _Nonnull
#else #else
#define NNULLABLE #define NNULLABLE
#endif #endif
#if defined(__clang__) #ifdef __clang__
#define UNULLABLE _Null_unspecified #define UNULLABLE _Null_unspecified
#else #else
#define UNULLABLE #define UNULLABLE
#endif #endif
#if defined(__clang__) #ifdef __clang__
#define COMPILER_VERSION __VERSION__ #define COMPILER_VERSION __VERSION__
#elif defined(__GNUC__) #elif defined(__GNUC__)
#define xstr(s) str(s) #define xstr(s) str(s)

View File

@@ -46,6 +46,8 @@ void setup(struct config* config);
int load(struct Vector* users, struct Vector* sessions); int load(struct Vector* users, struct Vector* sessions);
void print_err(const char* /*msg*/); void print_err(const char* /*msg*/);
void print_errno(const char* /*descr*/); void print_errno(const char* /*descr*/);
void print_pam_msg(const char* msg, int msg_style);
void clear_pam_msg(void);
void ui_update_field(enum Input focused_input); void ui_update_field(enum Input focused_input);
void ui_update_ffield(); void ui_update_ffield();

View File

@@ -1,4 +1,4 @@
// TODO: handle `fork() == -1`s // TODO: handle `fork() == -1`// TODO: handle `fork() == -1`s
#include <errno.h> #include <errno.h>
#include <grp.h> #include <grp.h>
@@ -240,9 +240,10 @@ bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
pam_handle_t* pamh = get_pamh(user, passwd); pam_handle_t* pamh = get_pamh(user, passwd);
if (pamh == NULL) { if (pamh == NULL) {
print_err("error on pam authentication"); print_pam_msg("authentication failed", PAM_ERROR_MSG);
return false; return false;
} }
clear_pam_msg();
struct pamh_getenv_status env_ret = struct pamh_getenv_status env_ret =
pamh_get_complete_env(pamh, pw, session.type); pamh_get_complete_env(pamh, pw, session.type);

View File

@@ -1,3 +1,5 @@
// TODO: rewrite properly
// NOLINTBEGIN(clang-diagnostic-nullability-completeness)
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -16,7 +18,9 @@
// returns NULL on any error // returns NULL on any error
// otherwise it returns the absolute path of the program that MUST BE FREED // otherwise it returns the absolute path of the program that MUST BE FREED
static char* NULLABLE search_path(const char* NNULLABLE for_binary) { static char* NULLABLE search_path(const char* NNULLABLE for_binary) {
char* path = strdup(getenv("PATH")); char* path_env = getenv("PATH");
if (!path_env) return NULL;
char* path = strdup(path_env);
if (!path) return NULL; if (!path) return NULL;
char* tok = strtok(path, ":"); char* tok = strtok(path, ":");
@@ -40,6 +44,7 @@ static char* NULLABLE search_path(const char* NNULLABLE for_binary) {
tok = strtok(NULL, ":"); tok = strtok(NULL, ":");
} }
free(path);
return NULL; return NULL;
} }
@@ -133,6 +138,7 @@ int push_arg(struct ctx* st) {
3 = syntax 3 = syntax
Important: call free_parsed_args afterwards to free the passed ***args Important: call free_parsed_args afterwards to free the passed ***args
*/ */
// NOLINTBEGIN(readability-function-cognitive-complexity) // NOLINTBEGIN(readability-function-cognitive-complexity)
int parse_exec_string(const char* exec_s, int* arg_count, char*** args) { int parse_exec_string(const char* exec_s, int* arg_count, char*** args) {
if (!exec_s || !args || !arg_count) return 1; if (!exec_s || !args || !arg_count) return 1;
@@ -226,3 +232,4 @@ syntax_err:
return 3; return 3;
} }
// NOLINTEND(readability-function-cognitive-complexity) // NOLINTEND(readability-function-cognitive-complexity)
// NOLINTEND(clang-diagnostic-nullability-completeness)

View File

@@ -22,6 +22,10 @@ int read_launch_state(struct LaunchState* NNULLABLE state) {
size_t num = 0; size_t num = 0;
if (getline(&state->username, &num, state_fd) < 0) goto fail; if (getline(&state->username, &num, state_fd) < 0) goto fail;
// not sure I can actually prove it but ughh, getline < 0 will ensure there's
// something in the string and then I think strcspn is bounded to the final
// null byte, so it shouldn't go over
// NOLINTNEXTLINE(clang-analyzer-security.ArrayBound)
state->username[strcspn(state->username, "\n")] = 0; state->username[strcspn(state->username, "\n")] = 0;
num = 0; num = 0;

View File

@@ -9,6 +9,7 @@
#include "macros.h" #include "macros.h"
#include "pam.h" #include "pam.h"
#include "sessions.h" #include "sessions.h"
#include "ui.h"
struct envpair { struct envpair {
const char* NNULLABLE name; const char* NNULLABLE name;
@@ -113,19 +114,45 @@ struct pamh_getenv_status pamh_get_complete_env(pam_handle_t* handle,
/////////////// ///////////////
struct pam_conv_data {
char* password;
void (*display_pam_msg)(const char* msg, int msg_style);
};
int pam_conversation(int num_msg, const struct pam_message** msg, int pam_conversation(int num_msg, const struct pam_message** msg,
struct pam_response** resp, void* appdata_ptr) { struct pam_response** resp, void* appdata_ptr) {
struct pam_response* reply = malloc(sizeof(struct pam_response) * num_msg); struct pam_response* reply = malloc(sizeof(struct pam_response) * num_msg);
if (!reply) { if (!reply) {
return PAM_BUF_ERR; return PAM_BUF_ERR;
} }
struct pam_conv_data* conv_data = (struct pam_conv_data*)appdata_ptr;
for (int i = 0; i < num_msg; i++) { for (int i = 0; i < num_msg; i++) {
reply[i].resp = NULL; reply[i].resp = NULL;
reply[i].resp_retcode = 0; reply[i].resp_retcode = 0;
if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ||
msg[i]->msg_style == PAM_PROMPT_ECHO_ON) { switch (msg[i]->msg_style) {
char* input = (char*)appdata_ptr; case PAM_PROMPT_ECHO_OFF:
reply[i].resp = strdup(input); case PAM_PROMPT_ECHO_ON:
reply[i].resp = strdup(conv_data->password);
if (!reply[i].resp) {
for (int j = 0; j < i; j++)
free(reply[j].resp);
free(reply);
return PAM_BUF_ERR;
}
break;
case PAM_TEXT_INFO:
case PAM_ERROR_MSG:
if (conv_data->display_pam_msg && msg[i]->msg) {
conv_data->display_pam_msg(msg[i]->msg, msg[i]->msg_style);
}
break;
default:
break;
} }
} }
*resp = reply; *resp = reply;
@@ -144,7 +171,9 @@ int pam_conversation(int num_msg, const struct pam_message** msg,
} }
pam_handle_t* get_pamh(char* user, char* passwd) { pam_handle_t* get_pamh(char* user, char* passwd) {
pam_handle_t* pamh = NULL; pam_handle_t* pamh = NULL;
struct pam_conv pamc = {pam_conversation, (void*)passwd}; struct pam_conv_data conv_data = {.password = passwd,
.display_pam_msg = print_pam_msg};
struct pam_conv pamc = {pam_conversation, (void*)&conv_data};
int ret; int ret;
char* pam_service_override = getenv("LIDM_PAM_SERVICE"); char* pam_service_override = getenv("LIDM_PAM_SERVICE");

114
src/ui.c
View File

@@ -5,6 +5,7 @@
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <pwd.h> #include <pwd.h>
#include <security/pam_appl.h>
#include <signal.h> #include <signal.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
@@ -111,8 +112,8 @@ static char* fmt_time(const char* fmt) {
} }
} }
char* trunc_gethostname(const size_t MAXLEN, const char* const ELLIPSIS) { char* trunc_gethostname(size_t maxlen, const char* const ELLIPSIS) {
if (utf8len(ELLIPSIS) > MAXLEN) return NULL; if (utf8len(ELLIPSIS) > maxlen) return NULL;
size_t alloc_size = HOST_NAME_MAX + strlen(ELLIPSIS) + 1; size_t alloc_size = HOST_NAME_MAX + strlen(ELLIPSIS) + 1;
char* buf = malloc(alloc_size); char* buf = malloc(alloc_size);
if (!buf) return NULL; if (!buf) return NULL;
@@ -122,8 +123,8 @@ char* trunc_gethostname(const size_t MAXLEN, const char* const ELLIPSIS) {
return NULL; return NULL;
} }
if (utf8len(buf) > MAXLEN) { if (utf8len(buf) > maxlen) {
size_t end = utf8trunc(buf, MAXLEN - utf8len(ELLIPSIS)); size_t end = utf8trunc(buf, maxlen - utf8len(ELLIPSIS));
strcpy(&buf[end], ELLIPSIS); strcpy(&buf[end], ELLIPSIS);
} }
return buf; return buf;
@@ -192,8 +193,8 @@ void scratch_print_ui() {
.y = ((window.ws_row - BOX_HEIGHT) / 2), // leave more space under .y = ((window.ws_row - BOX_HEIGHT) / 2), // leave more space under
}; };
if (window.ws_row < BOX_HEIGHT + INNER_BOX_OUT_MARGIN * 2 || if (window.ws_row < BOX_HEIGHT + (INNER_BOX_OUT_MARGIN * 2) ||
window.ws_col < BOX_WIDTH + INNER_BOX_OUT_MARGIN * 2) { window.ws_col < BOX_WIDTH + (INNER_BOX_OUT_MARGIN * 2)) {
printf("\033[2J\033[H"); // Clear screen printf("\033[2J\033[H"); // Clear screen
printf("\x1b[1;31mScreen too small\x1b[0m\n"); printf("\x1b[1;31mScreen too small\x1b[0m\n");
printf("\x1b[%s;%sm\x1b[2J", g_config->colors.bg, g_config->colors.fg); printf("\x1b[%s;%sm\x1b[2J", g_config->colors.bg, g_config->colors.fg);
@@ -298,6 +299,21 @@ int load(struct Vector* users, struct Vector* sessions) {
restore_all(); restore_all();
reboot(RB_POWER_OFF); reboot(RB_POWER_OFF);
exit(0); exit(0);
} else if (g_config->functions.fido != NONE &&
ansi_key == g_config->functions.fido) {
bool successful_write = write_launch_state((struct LaunchState){
.username = st_user().username,
.session_opt =
st_session(g_config->behavior.include_defshell).name,
});
if (!successful_write) log_puts("[E] failed to write launch state");
if (!launch(st_user().username, "",
st_session(g_config->behavior.include_defshell),
&restore_all, g_config)) {
print_passwd(utf8len(of_passwd.efield.content), true);
ui_update_cursor_focus();
}
} else if (ansi_key == A_UP || ansi_key == A_DOWN) { } else if (ansi_key == A_UP || ansi_key == A_DOWN) {
st_ch_focus(ansi_key == A_DOWN ? 1 : -1); st_ch_focus(ansi_key == A_DOWN ? 1 : -1);
} else if (ansi_key == A_RIGHT || ansi_key == A_LEFT) { } else if (ansi_key == A_RIGHT || ansi_key == A_LEFT) {
@@ -344,25 +360,40 @@ u_char get_render_pos_offset(struct opts_field* self, u_char maxlen) {
return pos - ofield_display_cursor_col(self, maxlen); return pos - ofield_display_cursor_col(self, maxlen);
} }
#define HOSTNAME_SIZE (VALUES_COL - VALUES_SEPR - BOX_HMARGIN - 1)
void print_head() { void print_head() {
// hostname doesn't just change on runtime char* fmtd_time = fmt_time(g_config->behavior.timefmt);
static char* hostname = NULL; size_t len_tm = utf8len(fmtd_time);
if (!hostname)
hostname = trunc_gethostname(HOSTNAME_SIZE, g_config->strings.ellipsis); // calculate the space available for the host name
if (!hostname) hostname = "unknown"; ssize_t hostname_size = BOX_WIDTH - (BOX_HMARGIN * 2) - len_tm - VALUES_SEPR;
if (hostname_size < 0) hostname_size = 0;
// hostname doesn't just change on runtime,
// but the length of the time string might
static char* NULLABLE hostname = NULL;
static ssize_t hostname_calcd_size;
// save the truncated hostname and the length it truncated to,
// if said length changes recalculate this (and free previous str)
if (!hostname || hostname_calcd_size != hostname_size) {
if (hostname) free(hostname);
hostname = trunc_gethostname(hostname_size, g_config->strings.ellipsis);
hostname_calcd_size = hostname_size;
}
clean_line(box_start, HEAD_ROW); clean_line(box_start, HEAD_ROW);
// put hostname // put hostname
printf("\x1b[%dG\x1b[%sm%s\x1b[%sm", if (hostname_size)
box_start.x + VALUES_COL - VALUES_SEPR - (uint)utf8len(hostname), printf("\x1b[%dG\x1b[%sm%s\x1b[%sm", box_start.x + 1 + BOX_HMARGIN,
g_config->colors.e_hostname, hostname, g_config->colors.fg); g_config->colors.e_hostname, hostname ? hostname : "unknown",
g_config->colors.fg);
// put date // put date
char* fmtd_time = fmt_time(g_config->behavior.timefmt);
printf("\x1b[%dG\x1b[%sm%s\x1b[%sm", printf("\x1b[%dG\x1b[%sm%s\x1b[%sm",
box_start.x + BOX_WIDTH - 1 - BOX_HMARGIN - (uint)utf8len(fmtd_time), box_start.x + BOX_WIDTH - 1 - BOX_HMARGIN - (uint)len_tm,
g_config->colors.e_date, fmtd_time, g_config->colors.fg); g_config->colors.e_date, fmtd_time, g_config->colors.fg);
free(fmtd_time); free(fmtd_time);
} }
@@ -497,6 +528,8 @@ static void print_box() {
} }
static void print_footer() { static void print_footer() {
bool fido_enabled = g_config->functions.fido != NONE;
size_t bsize = utf8len(g_config->strings.f_poweroff) + size_t bsize = utf8len(g_config->strings.f_poweroff) +
utf8len(KEY_NAMES[g_config->functions.poweroff]) + utf8len(KEY_NAMES[g_config->functions.poweroff]) +
utf8len(g_config->strings.f_reboot) + utf8len(g_config->strings.f_reboot) +
@@ -504,19 +537,31 @@ static void print_footer() {
utf8len(g_config->strings.f_refresh) + utf8len(g_config->strings.f_refresh) +
utf8len(KEY_NAMES[g_config->functions.refresh]); utf8len(KEY_NAMES[g_config->functions.refresh]);
bsize += 2 * 2 + // 2 wide separators between 3 items bsize += (2 * 2) + (3 * 1);
3 * 1; // 3 thin separators inside every item
if (fido_enabled) {
bsize += utf8len(g_config->strings.f_fido) +
utf8len(KEY_NAMES[g_config->functions.fido]) + 2 + 1;
}
uint row = window.ws_row - 1; uint row = window.ws_row - 1;
uint col = window.ws_col - 2 - bsize; uint col = window.ws_col - 2 - bsize;
printf(
"\x1b[%3$d;%4$dH%8$s \x1b[%1$sm%5$s\x1b[%2$sm %9$s " printf("\x1b[%d;%dH%s \x1b[%sm%s\x1b[%sm %s \x1b[%sm%s\x1b[%sm ", row, col,
"\x1b[%1$sm%6$s\x1b[%2$sm %10$s \x1b[%1$sm%7$s\x1b[%2$sm", g_config->strings.f_poweroff, g_config->colors.e_key,
g_config->colors.e_key, g_config->colors.fg, row, col, KEY_NAMES[g_config->functions.poweroff], g_config->colors.fg,
KEY_NAMES[g_config->functions.poweroff], g_config->strings.f_reboot, g_config->colors.e_key,
KEY_NAMES[g_config->functions.reboot], KEY_NAMES[g_config->functions.reboot], g_config->colors.fg);
KEY_NAMES[g_config->functions.refresh], g_config->strings.f_poweroff,
g_config->strings.f_reboot, g_config->strings.f_refresh); if (fido_enabled) {
printf("%s \x1b[%sm%s\x1b[%sm ", g_config->strings.f_fido,
g_config->colors.e_key, KEY_NAMES[g_config->functions.fido],
g_config->colors.fg);
}
printf("%s \x1b[%sm%s\x1b[%sm", g_config->strings.f_refresh,
g_config->colors.e_key, KEY_NAMES[g_config->functions.refresh],
g_config->colors.fg);
} }
void print_err(const char* msg) { void print_err(const char* msg) {
@@ -524,6 +569,21 @@ void print_err(const char* msg) {
msg, errno, strerror(errno)); msg, errno, strerror(errno));
} }
void print_pam_msg(const char* msg, int msg_style) {
uint row = box_start.y + BOX_HEIGHT + 1;
const char* color =
(msg_style == PAM_ERROR_MSG) ? g_config->colors.err : g_config->colors.fg;
printf("\x1b[%d;%dH\x1b[K\x1b[%sm%.*s\x1b[%sm", row, box_start.x, color,
BOX_WIDTH, msg, g_config->colors.fg);
(void)fflush(stdout);
}
void clear_pam_msg(void) {
uint row = box_start.y + BOX_HEIGHT + 1;
printf("\x1b[%d;%dH\x1b[K", row, box_start.x);
(void)fflush(stdout);
}
void print_errno(const char* descr) { void print_errno(const char* descr) {
if (descr == NULL) if (descr == NULL)
(void)fprintf(stderr, "\x1b[%d;%dH\x1b[%smunknown error(%d): %s", (void)fprintf(stderr, "\x1b[%d;%dH\x1b[%smunknown error(%d): %s",

View File

@@ -25,11 +25,13 @@
[functions] [functions]
# poweroff = F1 # poweroff = F1
# reboot = F2 # reboot = F2
# fido = NONE
# refresh = F5 # refresh = F5
[strings] [strings]
# f_poweroff = "poweroff" # f_poweroff = "poweroff"
# f_reboot = "reboot" # f_reboot = "reboot"
# f_fido = "fido"
# f_refresh = "refresh" # f_refresh = "refresh"
# e_user = "user" # e_user = "user"
# e_passwd = "password" # e_passwd = "password"