summaryrefslogtreecommitdiff
path: root/src/input.c
diff options
context:
space:
mode:
authorPhil Jones <philj56@gmail.com>2022-10-21 23:37:24 +0100
committerPhil Jones <philj56@gmail.com>2022-10-21 23:37:24 +0100
commitd1b94b4487c72b13320a281ad6a2fc291e4c79c7 (patch)
treed4b7ce738f9ad14dd55a1054a39901bf835db2a5 /src/input.c
parent7bb70c7407f652c584837d651fb9bcdb6b66ff9f (diff)
Initial input handling cleanup.
Also changed keyboard shortcuts to use physical keycodes, rather than XKB keysyms. This means shortcuts don't shift when keyboard layout is changed.
Diffstat (limited to 'src/input.c')
-rw-r--r--src/input.c240
1 files changed, 240 insertions, 0 deletions
diff --git a/src/input.c b/src/input.c
new file mode 100644
index 0000000..a343aef
--- /dev/null
+++ b/src/input.c
@@ -0,0 +1,240 @@
+#include <linux/input-event-codes.h>
+#include <wctype.h>
+#include "input.h"
+#include "nelem.h"
+#include "tofi.h"
+#include "utf8.h"
+
+
+static void add_character(struct tofi *tofi, xkb_keycode_t keycode);
+static void delete_character(struct tofi *tofi);
+static void delete_word(struct tofi *tofi);
+static void clear_input(struct tofi *tofi);
+static void select_previous_result(struct tofi *tofi);
+static void select_next_result(struct tofi *tofi);
+static void reset_selection(struct tofi *tofi);
+static void refresh_results(struct tofi *tofi);
+
+void input_handle_keypress(struct tofi *tofi, xkb_keycode_t keycode)
+{
+ if (tofi->xkb_state == NULL) {
+ return;
+ }
+
+ /*
+ * Use physical key code for shortcuts, ignoring layout changes.
+ * Linux keycodes are 8 less than XKB keycodes.
+ */
+ const uint32_t key = keycode - 8;
+
+ xkb_keysym_t sym = xkb_state_key_get_one_sym(tofi->xkb_state, keycode);
+
+ uint32_t ch = xkb_state_key_get_utf32(
+ tofi->xkb_state,
+ keycode);
+ if (utf8_isprint(ch)) {
+ add_character(tofi, keycode);
+ } else if (sym == XKB_KEY_BackSpace) {
+ delete_character(tofi);
+ } else if (key == KEY_W
+ && xkb_state_mod_name_is_active(
+ tofi->xkb_state,
+ XKB_MOD_NAME_CTRL,
+ XKB_STATE_MODS_EFFECTIVE))
+ {
+ delete_word(tofi);
+ } else if (key == KEY_U
+ && xkb_state_mod_name_is_active(
+ tofi->xkb_state,
+ XKB_MOD_NAME_CTRL,
+ XKB_STATE_MODS_EFFECTIVE)
+ )
+ {
+ clear_input(tofi);
+ } else if (sym == XKB_KEY_Up || sym == XKB_KEY_Left || sym == XKB_KEY_ISO_Left_Tab
+ || (key == KEY_K
+ && xkb_state_mod_name_is_active(
+ tofi->xkb_state,
+ XKB_MOD_NAME_CTRL,
+ XKB_STATE_MODS_EFFECTIVE)
+ )
+ ) {
+ select_previous_result(tofi);
+ } else if (sym == XKB_KEY_Down || sym == XKB_KEY_Right || sym == XKB_KEY_Tab
+ || (key == KEY_J
+ && xkb_state_mod_name_is_active(
+ tofi->xkb_state,
+ XKB_MOD_NAME_CTRL,
+ XKB_STATE_MODS_EFFECTIVE)
+ )
+ ) {
+ select_next_result(tofi);
+ } else if (sym == XKB_KEY_Home) {
+ reset_selection(tofi);
+ } else if (sym == XKB_KEY_Escape
+ || (key == KEY_C
+ && xkb_state_mod_name_is_active(
+ tofi->xkb_state,
+ XKB_MOD_NAME_CTRL,
+ XKB_STATE_MODS_EFFECTIVE)
+ )
+ )
+ {
+ tofi->closed = true;
+ return;
+ } else if (sym == XKB_KEY_Return || sym == XKB_KEY_KP_Enter) {
+ tofi->submit = true;
+ return;
+ }
+
+ entry_update(&tofi->window.entry);
+ tofi->window.surface.redraw = true;
+}
+
+void reset_selection(struct tofi *tofi) {
+ struct entry *entry = &tofi->window.entry;
+ entry->selection = 0;
+ entry->first_result = 0;
+}
+
+void add_character(struct tofi *tofi, xkb_keycode_t keycode)
+{
+ struct entry *entry = &tofi->window.entry;
+
+ if (entry->input_length >= N_ELEM(entry->input) - 1) {
+ /* No more room for input */
+ return;
+ }
+
+ char buf[5]; /* 4 UTF-8 bytes plus null terminator. */
+ int len = xkb_state_key_get_utf8(
+ tofi->xkb_state,
+ keycode,
+ buf,
+ sizeof(buf));
+ wchar_t ch;
+ mbtowc(&ch, buf, sizeof(buf));
+ entry->input[entry->input_length] = ch;
+ entry->input_length++;
+ entry->input[entry->input_length] = L'\0';
+ memcpy(&entry->input_mb[entry->input_mb_length],
+ buf,
+ N_ELEM(buf));
+ entry->input_mb_length += len;
+ if (entry->drun) {
+ struct string_vec results = desktop_vec_filter(&entry->apps, entry->input_mb, tofi->fuzzy_match);
+ string_vec_destroy(&entry->results);
+ entry->results = results;
+ } else {
+ struct string_vec tmp = entry->results;
+ entry->results = string_vec_filter(&entry->results, entry->input_mb, tofi->fuzzy_match);
+ string_vec_destroy(&tmp);
+ }
+
+ reset_selection(tofi);
+}
+
+void refresh_results(struct tofi *tofi)
+{
+ struct entry *entry = &tofi->window.entry;
+
+ const wchar_t *src = entry->input;
+ size_t siz = wcsrtombs(
+ entry->input_mb,
+ &src,
+ N_ELEM(entry->input_mb),
+ NULL);
+ entry->input_mb_length = siz;
+ string_vec_destroy(&entry->results);
+ if (entry->drun) {
+ entry->results = desktop_vec_filter(&entry->apps, entry->input_mb, tofi->fuzzy_match);
+ } else {
+ entry->results = string_vec_filter(&entry->commands, entry->input_mb, tofi->fuzzy_match);
+ }
+
+ reset_selection(tofi);
+}
+
+void delete_character(struct tofi *tofi)
+{
+ struct entry *entry = &tofi->window.entry;
+
+ if (entry->input_length == 0) {
+ /* No input to delete. */
+ return;
+ }
+
+ entry->input_length--;
+ entry->input[entry->input_length] = L'\0';
+
+ refresh_results(tofi);
+}
+
+void delete_word(struct tofi *tofi)
+{
+ struct entry *entry = &tofi->window.entry;
+
+ if (entry->input_length == 0) {
+ /* No input to delete. */
+ return;
+ }
+
+ while (entry->input_length > 0 && iswspace(entry->input[entry->input_length - 1])) {
+ entry->input_length--;
+ }
+ while (entry->input_length > 0 && !iswspace(entry->input[entry->input_length - 1])) {
+ entry->input_length--;
+ }
+ entry->input[entry->input_length] = L'\0';
+
+ refresh_results(tofi);
+}
+
+void clear_input(struct tofi *tofi)
+{
+ struct entry *entry = &tofi->window.entry;
+
+ entry->input_length = 0;
+ entry->input[0] = L'\0';
+
+ refresh_results(tofi);
+}
+
+void select_previous_result(struct tofi *tofi)
+{
+ struct entry *entry = &tofi->window.entry;
+
+ if (entry->selection > 0) {
+ entry->selection--;
+ return;
+ }
+
+ uint32_t nsel = MAX(MIN(entry->num_results_drawn, entry->results.count), 1);
+
+ if (entry->first_result > nsel) {
+ entry->first_result -= entry->last_num_results_drawn;
+ entry->selection = entry->last_num_results_drawn - 1;
+ } else if (entry->first_result > 0) {
+ entry->selection = entry->first_result - 1;
+ entry->first_result = 0;
+ }
+}
+
+void select_next_result(struct tofi *tofi)
+{
+ struct entry *entry = &tofi->window.entry;
+
+ uint32_t nsel = MAX(MIN(entry->num_results_drawn, entry->results.count), 1);
+
+ entry->selection++;
+ if (entry->selection >= nsel) {
+ entry->selection -= nsel;
+ if (entry->results.count > 0) {
+ entry->first_result += nsel;
+ entry->first_result %= entry->results.count;
+ } else {
+ entry->first_result = 0;
+ }
+ entry->last_num_results_drawn = entry->num_results_drawn;
+ }
+}