aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2024-09-04 18:56:09 +0200
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2024-09-09 18:22:21 +0200
commit0e6e0590272c8ba2303af3682d29209f439177d9 (patch)
tree62fd2b4e02043e10cb52b7bcc581283e0fb2a5df /src
parent893a0a6ad85411a57c1225af03260b34561377c7 (diff)
downloadsciteco-0e6e0590272c8ba2303af3682d29209f439177d9.tar.gz
Gtk: ignore the keyboard layout whereever possible (refs #5)
* Eg. when typing with a Russian layout, CTRL+I will always insert ^I. * Works with all of the start-state command Ex, Fx, ^x commands and string building constructs. This is exactly where process_edit_cmd_cb() case folds case-insensitive characters. The corresponding state therefore sets an is_case_insensitive flag now. * Does not yet work with anything embedded into Q-Register specifications. This could only be realized with a new state callback (is_case_insensitive()?) that chains to the Q-Register and string building states recursively. * Also it doesn't work with Ё on my Russian phonetic layout, probably because the ANSI caret on that same key is considered dead and not returned by gdk_keyval_to_unicode(). Perhaps we should directly wheck the keyval values? * Whenever a non-ANSI key is pressed in an allowed state, we try to check all other keyvals that could be produced by the same hardware keycode, ie. we check all groups (keyboard layouts).
Diffstat (limited to 'src')
-rw-r--r--src/interface-gtk/interface.c100
-rw-r--r--src/parser.h12
2 files changed, 89 insertions, 23 deletions
diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c
index ed027a2..9b2560d 100644
--- a/src/interface-gtk/interface.c
+++ b/src/interface-gtk/interface.c
@@ -881,6 +881,45 @@ teco_interface_cmdline_commit_cb(GtkIMContext *context, gchar *str, gpointer use
gtk_main_quit();
}
+/**
+ * Try to find an ANSI (latin) key for a given keypress.
+ *
+ * If the given key press does not generate a key from the ANSI
+ * range, it tries to find one in another group.
+ *
+ * @param event Key event to look up. In case of success,
+ * this event structure might also be written to.
+ * @return The codepoint of the ANSI version or 0 if there is
+ * no fitting ANSI/latin key.
+ */
+static gchar
+teco_interface_get_ansi_key(GdkEventKey *event)
+{
+ gunichar cp = gdk_keyval_to_unicode(event->keyval);
+ if (cp && cp < 0x80)
+ return cp;
+
+ GdkKeymap *map = gdk_keymap_get_for_display(gdk_window_get_display(event->window));
+ g_autofree GdkKeymapKey *keys = NULL;
+ g_autofree guint *keyvals = NULL;
+ gint n_entries = 0;
+
+ gdk_keymap_get_entries_for_keycode(map, event->hardware_keycode,
+ &keys, &keyvals, &n_entries);
+ for (gint i = 0; i < n_entries; i++) {
+ g_assert(keys[i].keycode == event->hardware_keycode);
+ cp = gdk_keyval_to_unicode(keyvals[i]);
+ if (cp && cp < 0x80 &&
+ gdk_keyval_is_upper(keyvals[i]) == gdk_keyval_is_upper(event->keyval)) {
+ event->keyval = keyvals[i];
+ event->group = keys[i].group;
+ return cp;
+ }
+ }
+
+ return 0;
+}
+
static gboolean
teco_interface_handle_key_press(GdkEventKey *event, GError **error)
{
@@ -947,29 +986,46 @@ teco_interface_handle_key_press(GdkEventKey *event, GError **error)
/*
* Control keys and keys with printable representation
*/
- default: {
- gunichar u = gdk_keyval_to_unicode(event->keyval);
-
- if (u && u < 0x80 && (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) == GDK_CONTROL_MASK) {
- /*
- * NOTE: Alt-Gr key-combinations are sometimes reported as
- * Ctrl+Alt, so we filter those out.
- */
- if (!teco_cmdline_keypress_c(TECO_CTL_KEY(g_ascii_toupper(u)), error))
- return FALSE;
- } else {
- /*
- * This is necessary to handle dead keys and in the future
- * for inputting Asian languages.
- *
- * FIXME: We do not yet support preediting.
- * It would be easier to forward the event to the Scintilla
- * widget and use its existing IM support.
- * But this breaks the event freezing and results in flickering.
- */
- gtk_im_context_filter_keypress(teco_interface.input_method, event);
+ default:
+ /*
+ * NOTE: Alt-Gr key-combinations are sometimes reported as
+ * Ctrl+Alt, so we filter those out.
+ */
+ if ((event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) == GDK_CONTROL_MASK) {
+ gchar c = teco_interface_get_ansi_key(event);
+ if (c) {
+ if (!teco_cmdline_keypress_c(TECO_CTL_KEY(g_ascii_toupper(c)), error))
+ return FALSE;
+ break;
+ }
}
- }
+
+ /*
+ * If the current state is case-insensitive, it is a command name -
+ * which consists only of ANSI letters - we try to
+ * accept non-ANSI letters as well.
+ * This means, you don't have to change keyboard layouts
+ * so often.
+ * FIXME: This could be made to work with string-building constructs
+ * within Q-Register specs as well.
+ * Unfortunately, Q-Reg specs and string building can be nested
+ * indefinitely.
+ * This would effectively require a new is_case_sensitive_cb().
+ */
+ if (teco_cmdline.machine.parent.current->is_case_insensitive ||
+ teco_cmdline.machine.expectstring.machine.parent.current->is_case_insensitive)
+ teco_interface_get_ansi_key(event);
+
+ /*
+ * This is necessary to handle dead keys and in the future
+ * for inputting Asian languages.
+ *
+ * FIXME: We do not yet support preediting.
+ * It would be easier to forward the event to the Scintilla
+ * widget and use its existing IM support.
+ * But this breaks the event freezing and results in flickering.
+ */
+ gtk_im_context_filter_keypress(teco_interface.input_method, event);
}
teco_interface_refresh(teco_interface_current_view != last_view);
diff --git a/src/parser.h b/src/parser.h
index ba6054f..09ec483 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -189,6 +189,15 @@ struct teco_state_t {
*/
bool is_start : 1;
/**
+ * Whether this state accepts case insensitive characters,
+ * ie. is part of a command name, that can be case folded.
+ * This is also used to determine which state accepts only
+ * ANSI characters.
+ * @fixme But it should be callback to detect all
+ * string building constructs nested in Q-Reg specs.
+ */
+ bool is_case_insensitive : 1;
+ /**
* Function key macro mask.
* This is not a bitmask since it is compared with values set
* from TECO, so the bitorder needs to be defined.
@@ -252,13 +261,14 @@ gboolean teco_state_caseinsensitive_process_edit_cmd(teco_machine_t *ctx, teco_m
* @implements TECO_DEFINE_STATE
* @ingroup states
*
- * Base class of states with case-insenstive input.
+ * Base class of states with case-insensitive input.
*
* This is meant for states accepting command characters
* that can possibly be case-folded.
*/
#define TECO_DEFINE_STATE_CASEINSENSITIVE(NAME, ...) \
TECO_DEFINE_STATE(NAME, \
+ .is_case_insensitive = TRUE, \
.process_edit_cmd_cb = teco_state_caseinsensitive_process_edit_cmd, \
##__VA_ARGS__ \
)