summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-21 15:50:37 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-21 15:50:37 +0200
commit69777086ffad917b7a48d57df9dccf4e21a0a364 (patch)
tree91a0d4a2e02dd589e9d5066248a031776892d772
parentdb03b0b2875e6981fdb86c5c3c23d1a59280f222 (diff)
downloadbokf-69777086ffad917b7a48d57df9dccf4e21a0a364.tar.gz
bokf-69777086ffad917b7a48d57df9dccf4e21a0a364.zip
tui: level-2 modifyOtherKeys, Kitty keyboard protocol and ^Enter/F9 hintsv0.1.59
-rw-r--r--clients/bokftui.c1
-rw-r--r--clients/screens_bokslut.c2
-rw-r--r--clients/screens_ib.c2
-rw-r--r--clients/screens_invoices.c2
-rw-r--r--clients/screens_payroll.c5
-rw-r--r--clients/screens_templates.c2
-rw-r--r--clients/screens_vouchers.c2
-rw-r--r--clients/tui.c35
-rw-r--r--docs/TUI-GUIDELINES.md2
-rwxr-xr-xscripts/tui-golden.py60
10 files changed, 99 insertions, 14 deletions
diff --git a/clients/bokftui.c b/clients/bokftui.c
index 2dc23ba..69d6690 100644
--- a/clients/bokftui.c
+++ b/clients/bokftui.c
@@ -346,6 +346,7 @@ static void usage(FILE *f)
static void reset_modify_keys(void)
{
+ putp("\033[<u");
putp("\033[>4m");
fflush(stdout);
}
diff --git a/clients/screens_bokslut.c b/clients/screens_bokslut.c
index df47196..3f69982 100644
--- a/clients/screens_bokslut.c
+++ b/clients/screens_bokslut.c
@@ -242,7 +242,7 @@ void bokslut_screen(struct app *a)
ff[7].cap = sizeof rate;
ff[7].kind = TUI_F_TEXT;
tui_form_hint("upp/ned/Tab = flytta Enter = ändra/utför F5 = visa"
- " bokslutsplan ^Enter = bokför planen (frågar"
+ " bokslutsplan ^Enter/F9 = bokför planen (frågar"
" först) Esc/q = tillbaka ^C = avsluta");
int r = tui_form_run_actions("Bokslut", ff, nf + 2, can_edit, acts, 5,
NULL, &sel);
diff --git a/clients/screens_ib.c b/clients/screens_ib.c
index ca35562..8950667 100644
--- a/clients/screens_ib.c
+++ b/clients/screens_ib.c
@@ -272,7 +272,7 @@ static int ib_form(struct app *a, char **old_acc, int64_t *old_amt, int nold)
for (;;) {
int rr = tui_rt_run("Ingående balans", &ib.rt,
"Tab = byta fält F5 = validera ^X = rensa rad"
- " ^Enter = spara Esc = avbryt");
+ " ^Enter/F9 = spara Esc = avbryt");
if (rr == -1)
return 0;
char msg[256];
diff --git a/clients/screens_invoices.c b/clients/screens_invoices.c
index b8cc8dd..ca59eef 100644
--- a/clients/screens_invoices.c
+++ b/clients/screens_invoices.c
@@ -1092,7 +1092,7 @@ static int64_t invoices_new(struct app *a)
tui_rt_set_fields(&f.rt, ff, 7);
const char *hint =
"Enter = välj/ändra Tab = byta fält F5 = förhandsvisa"
- " ^Enter = utfärda Esc = avbryt";
+ " ^Enter/F9 = utfärda Esc = avbryt";
int64_t out = 0;
for (;;) {
int rr = tui_rt_run("Ny faktura", &f.rt, hint);
diff --git a/clients/screens_payroll.c b/clients/screens_payroll.c
index 9bfaf38..23d765f 100644
--- a/clients/screens_payroll.c
+++ b/clients/screens_payroll.c
@@ -1101,7 +1101,8 @@ static int64_t payroll_run_screen(struct app *a, int64_t id)
{ status, -1, NULL },
{ totals, -1, NULL },
{ "Förhandsvisa (F5)", 1, NULL },
- { "Bokför körning (^Enter)", post_reason ? 0 : 1, post_reason },
+ { "Bokför körning (^Enter/F9)", post_reason ? 0 : 1,
+ post_reason },
{ "Lönebesked (PDF)", payslip_reason ? 0 : 1, payslip_reason },
{ "AGI-underlag", agi_reason ? 0 : 1, agi_reason },
{ "Betala skatt & avgifter", pay_reason ? 0 : 1, pay_reason },
@@ -1123,7 +1124,7 @@ static int64_t payroll_run_screen(struct app *a, int64_t id)
snprintf(title, sizeof title, "Ny lönekörning");
int editable = can_write && !posted;
tui_form_hint("upp/ned/Tab = flytta Enter = ändra/utför"
- " F5 = förhandsvisa ^Enter = bokför"
+ " F5 = förhandsvisa ^Enter/F9 = bokför"
" Esc/q = tillbaka ^C = avsluta");
int ret = tui_form_run_actions(title, ff, 2, editable, acts, 7, NULL,
&focus);
diff --git a/clients/screens_templates.c b/clients/screens_templates.c
index b2cd7ab..5777742 100644
--- a/clients/screens_templates.c
+++ b/clients/screens_templates.c
@@ -234,7 +234,7 @@ static int template_form(struct app *a, const char *load_name)
tui_rt_set_fields(&tf.rt, ff, 3);
const char *title = load_name ? "Redigera mall" : "Ny mall";
const char *hint = "Enter = ändra fält Tab = byta fält F5 = validera"
- " ^X = rensa rad ^Enter = spara Esc = avbryt";
+ " ^X = rensa rad ^Enter/F9 = spara Esc = avbryt";
for (;;) {
int rr = tui_rt_run(title, &tf.rt, hint);
if (rr == -1)
diff --git a/clients/screens_vouchers.c b/clients/screens_vouchers.c
index fedffe9..5e1b006 100644
--- a/clients/screens_vouchers.c
+++ b/clients/screens_vouchers.c
@@ -836,7 +836,7 @@ int64_t vouchers_new_prefill(struct app *a, const struct voucher_prefill *p)
tui_rt_normalize(&vf.rt);
const char *hint = "Enter = ändra fält Tab = byta fält F4 = mall"
" ^F = bifoga fil F5 = validera ^X = rensa rad"
- " ^Enter = bokför Esc = avbryt";
+ " ^Enter/F9 = bokför Esc = avbryt";
for (;;) {
int rr = tui_rt_run("Nytt verifikat", &vf.rt, hint);
if (rr == -1)
diff --git a/clients/tui.c b/clients/tui.c
index 031fad0..30761a8 100644
--- a/clients/tui.c
+++ b/clients/tui.c
@@ -159,9 +159,34 @@ int tui_sticky_split(char **lines, int n, char **head, int *nhead)
void tui_keys_setup(void)
{
- /* ^Enter has no control code; enable xterm modifyOtherKeys and bind the
- sequence it produces. Terminals without it ignore the request. */
+ /* ^Enter has no control code; enable xterm modifyOtherKeys level 2 (level
+ 1 leaves control+Enter alone) and the Kitty keyboard protocol, then bind
+ the sequences they produce. Terminals with neither keep F9 working. */
+ putp("\033[>4;2m");
+ putp("\033[>1u");
+ fflush(stdout);
define_key("\033[27;5;13~", TUI_KEY_CTRL_ENTER);
+ define_key("\033[13;5u", TUI_KEY_CTRL_ENTER);
+ define_key("\033[27;2;9~", KEY_BTAB);
+ define_key("\033[9;2u", KEY_BTAB);
+ define_key("\033[27u", 27);
+ define_key("\033[27;1u", 27);
+ define_key("\033[27;5;97~", 1);
+ define_key("\033[97;5u", 1);
+ define_key("\033[27;5;99~", 3);
+ define_key("\033[99;5u", 3);
+ define_key("\033[27;5;101~", 5);
+ define_key("\033[101;5u", 5);
+ define_key("\033[27;5;102~", 6);
+ define_key("\033[102;5u", 6);
+ define_key("\033[27;5;110~", 14);
+ define_key("\033[110;5u", 14);
+ define_key("\033[27;5;114~", 18);
+ define_key("\033[114;5u", 18);
+ define_key("\033[27;5;117~", 21);
+ define_key("\033[117;5u", 21);
+ define_key("\033[27;5;120~", 24);
+ define_key("\033[120;5u", 24);
/* Home/End arrive as CSI or SS3 depending on the terminal */
define_key("\033[1~", KEY_HOME);
define_key("\033OH", KEY_HOME);
@@ -184,8 +209,6 @@ void tui_keys_setup(void)
init_pair(TUI_CP_RULE, COLOR_BLUE, -1);
init_pair(TUI_CP_STATUS, COLOR_CYAN, -1);
}
- putp("\033[>4;1m");
- fflush(stdout);
}
@@ -1592,7 +1615,7 @@ static int form_run(const char *title, struct tui_form_field *f, int nf,
h = na > 0
? (can_edit
? "upp/ned/Tab = flytta Enter = ändra/utför"
- " F5 = uppdatera ^Enter = spara"
+ " F5 = uppdatera ^Enter/F9 = spara"
" Esc/q = tillbaka ^C = avsluta"
: "upp/ned/Tab = flytta Enter = utför"
" F5 = uppdatera Esc/q = tillbaka"
@@ -1600,7 +1623,7 @@ static int form_run(const char *title, struct tui_form_field *f, int nf,
" (endast behöriga kan ändra)")
: (can_edit
? "upp/ned/Home/End Enter = ändra"
- " F5 = uppdatera ^Enter = spara"
+ " F5 = uppdatera ^Enter/F9 = spara"
" Esc/q = tillbaka ^C = avsluta"
: "upp/ned/Home/End F5 = uppdatera"
" Esc/q = tillbaka ^C = avsluta"
diff --git a/docs/TUI-GUIDELINES.md b/docs/TUI-GUIDELINES.md
index 94c3b5c..190a5c5 100644
--- a/docs/TUI-GUIDELINES.md
+++ b/docs/TUI-GUIDELINES.md
@@ -33,7 +33,7 @@ there.
| `Ctrl+F` | Attach a file via the file browser (voucher form and voucher detail) |
| `k` | Underlag: link the highlighted attachment to a voucher picked from a list |
| `Ctrl+X` | Clear the current row — only inside row editors (never "new") |
-| `Ctrl+Enter` | Save/post the current form. Enabled via xterm `modifyOtherKeys`; terminals that cannot send it keep `F9` working |
+| `Ctrl+Enter` | Save/post the current form. Enabled via xterm `modifyOtherKeys` level 2 or the Kitty keyboard protocol; terminals that send neither keep `F9` working, and the hints show `^Enter/F9` |
Every screen prints its keys in the footer via `hints()`. If a key exists, the
footer shows it; if the footer shows it, the key works. Control keys are
diff --git a/scripts/tui-golden.py b/scripts/tui-golden.py
index 9df5ed4..f5d5b7f 100755
--- a/scripts/tui-golden.py
+++ b/scripts/tui-golden.py
@@ -56,7 +56,9 @@ KEYS = {
"ctrlc": "\x03",
"ctrln": "\x0e",
"ctrlenter": "\x1b[27;5;13~",
+ "ctrlenter-kitty": "\x1b[13;5u",
"f5": "\x1b[15~",
+ "f9": "\x1b[20~",
}
# {org_name} {org_nr} {fy_label} {fy_start} {fy_end} are substituted at run
@@ -234,6 +236,58 @@ SCENARIOS = [
"expect": ["Anställda", "Testanställd", "Ny anställd"],
},
{
+ "name": "employee-new-f9",
+ "screen": "employees",
+ "steps": [
+ {
+ "keys": ["ctrln"],
+ "expect": ["Ny anställd", "Personnummer"],
+ },
+ {
+ "keys": ["enter", "Testperson", "enter"],
+ "expect": ["Ny anställd", "Testperson"],
+ },
+ {
+ "keys": ["down", "enter", "19900101-1234", "enter"],
+ "expect": ["19900101-1234"],
+ },
+ {
+ "keys": ["f9"],
+ "expect": ["Anställd skapad."],
+ },
+ {
+ "keys": ["enter"],
+ "expect": ["Anställda", "Testperson"],
+ },
+ ],
+ },
+ {
+ "name": "employee-new-kitty",
+ "screen": "employees",
+ "steps": [
+ {
+ "keys": ["ctrln"],
+ "expect": ["Ny anställd", "Personnummer"],
+ },
+ {
+ "keys": ["enter", "Kitty Person", "enter"],
+ "expect": ["Kitty Person"],
+ },
+ {
+ "keys": ["down", "enter", "19900202-5678", "enter"],
+ "expect": ["19900202-5678"],
+ },
+ {
+ "keys": ["ctrlenter-kitty"],
+ "expect": ["Anställd skapad."],
+ },
+ {
+ "keys": ["enter"],
+ "expect": ["Anställda", "Kitty Person"],
+ },
+ ],
+ },
+ {
"name": "company",
"screen": "company",
"expect": ["Bolaget", "Företagsuppgifter", "Fakturauppgifter",
@@ -947,6 +1001,12 @@ def main(argv):
report_failure(name, missing, "", before)
failures += 1
continue
+ if (b"\x1b[>4;2m" not in app.raw or
+ b"\x1b[>1u" not in app.raw):
+ report_failure(name, ["keyboard protocol enable "
+ "sequence"], "", before)
+ failures += 1
+ continue
current = before
for step in sc.get("steps", []):
keys = resolve_keys(step["keys"])