diff --git a/CHANGELOG.md b/CHANGELOG.md
index 468f897d..365eb7f5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -58,6 +58,9 @@
* Blinking text now uses the foreground color, but dimmed down in its
off state, instead of the background color.
+* Num Lock by default overrides the keypad mode. See
+ **foot.ini**(5)::KEYPAD, or [README.md](README.md#keypad) for
+ details (https://codeberg.org/dnkl/foot/issues/194).
### Deprecated
diff --git a/README.md b/README.md
index bcbddecd..8bf12bf8 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,7 @@ The fast, lightweight and minimalistic Wayland terminal emulator.
1. [Server (daemon) mode](#server-daemon-mode)
1. [Alt/meta](#alt-meta)
1. [Backspace](#backspace)
+1. [Keypad](#keypad)
1. [DPI and font size](#dpi-and-font-size)
1. [Supported OSCs](#supported-oscs)
1. [Programmatically checking if running in foot](#programmatically-checking-if-running-in-foot)
@@ -270,6 +271,25 @@ Finally, pressing alt will prefix the transmitted byte with
ESC.
+## KEYPAD
+
+By default, Num Lock overrides the run-time configuration
+keypad mode; when active, the keypad is always considered to be in
+_numerical_ mode. This corresponds to XTerm's `numLock` option set to
+`true`.
+
+In this mode, the keypad keys always sends either numbers (Num
+Lock is **active**) or cursor movement keys (Up,
+Down, Left, Right, Page
+Up, Page Down etc).
+
+This can be disabled programmatically with `\E[?1035l` (and enabled
+again with `\E[?1035h`).
+
+When disabled, the keypad sends custom escape sequences instead of
+numbers, when in _application_ mode.
+
+
## DPI and font size
Font sizes are apparently a complex thing. Many applications use a
diff --git a/csi.c b/csi.c
index c1982275..80f2bfd0 100644
--- a/csi.c
+++ b/csi.c
@@ -470,6 +470,12 @@ decset_decrst(struct terminal *term, unsigned param, bool enable)
term->meta.eight_bit = enable;
break;
+ case 1035:
+ /* numLock */
+ LOG_DBG("%s Num Lock modifier", enable ? "enabling" : "disabling");
+ term->num_lock_modifier = enable;
+ break;
+
case 1036:
/* metaSendsEscape */
LOG_DBG("%s meta-sends-escape", enable ? "enabling" : "disabling");
@@ -592,6 +598,7 @@ xtsave(struct terminal *term, unsigned param)
case 1007: term->xtsave.alt_scrolling = term->alt_scrolling; break;
case 1015: term->xtsave.mouse_urxvt = term->mouse_reporting == MOUSE_URXVT; break;
case 1034: term->xtsave.meta_eight_bit = term->meta.eight_bit; break;
+ case 1035: term->xtsave.num_lock_modifier = term->num_lock_modifier; break;
case 1036: term->xtsave.meta_esc_prefix = term->meta.esc_prefix; break;
case 1042: term->xtsave.bell_is_urgent = term->bell_is_urgent; break;
case 1049: term->xtsave.alt_screen = term->grid == &term->alt; break;
@@ -624,6 +631,7 @@ xtrestore(struct terminal *term, unsigned param)
case 1007: enable = term->xtsave.alt_scrolling; break;
case 1015: enable = term->xtsave.mouse_urxvt; break;
case 1034: enable = term->xtsave.meta_eight_bit; break;
+ case 1035: enable = term->xtsave.num_lock_modifier; break;
case 1036: enable = term->xtsave.meta_esc_prefix; break;
case 1042: enable = term->xtsave.bell_is_urgent; break;
case 1049: enable = term->xtsave.alt_screen; break;
diff --git a/doc/foot.1.scd b/doc/foot.1.scd
index e8460cc7..e3be3013 100644
--- a/doc/foot.1.scd
+++ b/doc/foot.1.scd
@@ -267,6 +267,23 @@ described above *cannot* be changed.
Finally, pressing *alt* will prefix the transmitted byte with ESC.
+# KEYPAD
+
+By default, *Num Lock* overrides the run-time configuration keypad
+mode; when active, the keypad is always considered to be in
+_numerical_ mode. This corresponds to XTerm's *numLock* option set to
+*true*.
+
+In this mode, the keypad keys always sends either numbers (Num Lock is
+active) or cursor movement keys (up, down, left, right, page up, page
+down etc).
+
+This can be disabled programmatically with *\E[?1035l* (and enabled
+again with *\E[?1035h*).
+
+When disabled, the keypad sends custom escape sequences instead of
+numbers, when in _application_ mode.
+
# CONFIGURATION
See *foot.ini*(5)
diff --git a/foot.ini b/foot.ini
index 8f8d4493..b22ed049 100644
--- a/foot.ini
+++ b/foot.ini
@@ -84,7 +84,6 @@
# pipe-scrollback=[sh -c "xurls | bemenu | xargs -r firefox"] none
# pipe-selected=[xargs -r firefox] none
-
[search-bindings]
# cancel=Control+g Escape
# commit=Return
diff --git a/input.c b/input.c
index 5d21a3bc..d455a473 100644
--- a/input.c
+++ b/input.c
@@ -526,10 +526,11 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
if (seat->kbd.xkb_keymap != NULL) {
seat->kbd.xkb_state = xkb_state_new(seat->kbd.xkb_keymap);
- seat->kbd.mod_shift = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Shift");
- seat->kbd.mod_alt = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod1") ;
- seat->kbd.mod_ctrl = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Control");
- seat->kbd.mod_meta = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod4");
+ seat->kbd.mod_shift = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_MOD_NAME_SHIFT);
+ seat->kbd.mod_alt = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_MOD_NAME_ALT) ;
+ seat->kbd.mod_ctrl = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_MOD_NAME_CTRL);
+ seat->kbd.mod_meta = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_MOD_NAME_LOGO);
+ seat->kbd.mod_num = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_MOD_NAME_NUM);
seat->kbd.key_arrow_up = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "UP");
seat->kbd.key_arrow_down = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "DOWN");
@@ -632,6 +633,7 @@ keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
seat->kbd.alt = false;
seat->kbd.ctrl = false;
seat->kbd.meta = false;
+ seat->kbd.num = false;
if (seat->kbd.xkb_compose_state != NULL)
xkb_compose_state_reset(seat->kbd.xkb_compose_state);
@@ -739,7 +741,8 @@ keymap_data_for_sym(xkb_keysym_t sym, size_t *count)
}
static const struct key_data *
-keymap_lookup(struct terminal *term, xkb_keysym_t sym, enum modifier mods)
+keymap_lookup(struct seat *seat, struct terminal *term,
+ xkb_keysym_t sym, enum modifier mods)
{
size_t count;
const struct key_data *info = keymap_data_for_sym(sym, &count);
@@ -747,16 +750,22 @@ keymap_lookup(struct terminal *term, xkb_keysym_t sym, enum modifier mods)
if (info == NULL)
return NULL;
+ const enum cursor_keys cursor_keys_mode = term->cursor_keys_mode;
+ const enum keypad_keys keypad_keys_mode
+ = (term->num_lock_modifier && seat->kbd.num
+ ? KEYPAD_NUMERICAL
+ : term->keypad_keys_mode);
+
for (size_t j = 0; j < count; j++) {
if (info[j].modifiers != MOD_ANY && info[j].modifiers != mods)
continue;
if (info[j].cursor_keys_mode != CURSOR_KEYS_DONTCARE &&
- info[j].cursor_keys_mode != term->cursor_keys_mode)
+ info[j].cursor_keys_mode != cursor_keys_mode)
continue;
if (info[j].keypad_keys_mode != KEYPAD_DONTCARE &&
- info[j].keypad_keys_mode != term->keypad_keys_mode)
+ info[j].keypad_keys_mode != keypad_keys_mode)
continue;
return &info[j];
@@ -882,7 +891,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
keymap_mods |= seat->kbd.ctrl ? MOD_CTRL : MOD_NONE;
keymap_mods |= seat->kbd.meta ? MOD_META : MOD_NONE;
- const struct key_data *keymap = keymap_lookup(term, sym, keymap_mods);
+ const struct key_data *keymap = keymap_lookup(seat, term, sym, keymap_mods);
if (keymap != NULL) {
term_to_slave(term, keymap->seq, strlen(keymap->seq));
@@ -1047,6 +1056,8 @@ keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
seat->kbd.xkb_state, seat->kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED);
seat->kbd.meta = xkb_state_mod_index_is_active(
seat->kbd.xkb_state, seat->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED);
+ seat->kbd.num = xkb_state_mod_index_is_active(
+ seat->kbd.xkb_state, seat->kbd.mod_num, XKB_STATE_MODS_LOCKED);
}
if (seat->kbd_focus && seat->kbd_focus->active_surface == TERM_SURF_GRID)
diff --git a/terminal.c b/terminal.c
index b9e4528a..d117bf9c 100644
--- a/terminal.c
+++ b/terminal.c
@@ -1047,6 +1047,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
.esc_prefix = true,
.eight_bit = true,
},
+ .num_lock_modifier = true,
.bell_is_urgent = conf->bell_is_urgent,
.tab_stops = tll_init(),
.wl = wayl,
diff --git a/terminal.h b/terminal.h
index 6a8e340b..5c7a9501 100644
--- a/terminal.h
+++ b/terminal.h
@@ -252,6 +252,7 @@ struct terminal {
bool eight_bit;
} meta;
+ bool num_lock_modifier;
bool bell_is_urgent;
/* Saved DECSET modes - we save the SET state */
@@ -275,6 +276,7 @@ struct terminal {
uint32_t mouse_urxvt:1;
uint32_t meta_eight_bit:1;
uint32_t meta_esc_prefix:1;
+ uint32_t num_lock_modifier:1;
uint32_t bell_is_urgent:1;
uint32_t alt_screen:1;
} xtsave;
diff --git a/wayland.h b/wayland.h
index ac6dd9a7..7bf5eb80 100644
--- a/wayland.h
+++ b/wayland.h
@@ -163,6 +163,7 @@ struct seat {
xkb_mod_index_t mod_alt;
xkb_mod_index_t mod_ctrl;
xkb_mod_index_t mod_meta;
+ xkb_mod_index_t mod_num;
xkb_keycode_t key_arrow_up;
xkb_keycode_t key_arrow_down;
@@ -172,6 +173,7 @@ struct seat {
bool alt;
bool ctrl;
bool meta;
+ bool num;
struct {
tll(struct key_binding_normal) key;