diff --git a/meson_options.txt b/meson.options similarity index 100% rename from meson_options.txt rename to meson.options diff --git a/run.in b/run.in index 856d33cd7f0d817aa6b219bf25fb99dfbb973e42..341ed86c266488a146d12a82c7952bc7278f10af 100755 --- a/run.in +++ b/run.in @@ -1,15 +1,36 @@ -#!/bin/sh +#!/bin/bash set -e +handle_done() { + echo "Caught SIGINT" + kill -TERM $child +} + +trap handle_done SIGTERM + ABS_BUILDDIR='@ABS_BUILDDIR@' -ABS_SRCDIR='@ABS_SRCDIR@' export GSETTINGS_SCHEMA_DIR="${ABS_BUILDDIR}/data" +STATE=$(gsettings get org.gnome.desktop.a11y.applications screen-keyboard-enabled) +gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true + if [ "${POS_GDB}" = 1 ]; then echo "Running phosh-osk-stevia under gdb" WRAPPER="gdb --args" fi +set +e set -x -exec ${WRAPPER} "${ABS_BUILDDIR}/src/phosh-osk-stevia" "$@" +${WRAPPER} "${ABS_BUILDDIR}/src/phosh-osk-stevia" "$@" +child=$! +set +x +ret=$? +set -e + +if [ "${STATE}" == false ]; then + echo "Disabling OSK" + gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled "${STATE}" +fi + +exit "$ret" diff --git a/src/pos-completer.c b/src/pos-completer.c index e1824c6dcaa47d547a865ed85fdc0fe93cb1c19d..cad47aeb3fd60340a2ce94d5144070ed4c2b1d2d 100644 --- a/src/pos-completer.c +++ b/src/pos-completer.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2023 The Phosh Developers + * Copyright (C) 2023-2024 The Phosh Developers + * 2025 Phosh.mobi e.V. * * SPDX-License-Identifier: GPL-3.0-or-later * @@ -408,9 +409,8 @@ pos_completer_add_preedit (PosCompleter *self, GString *preedit, const char *sym } /* Return/Enter is special, see above. */ - if (g_strcmp0 (symbol, "KEY_ENTER") == 0) { + if (g_strcmp0 (symbol, "KEY_ENTER") == 0) return TRUE; - } /* Ignore all other special keys */ if (g_str_has_prefix (symbol, "KEY_")) @@ -506,9 +506,9 @@ pos_completer_symbol_is_word_separator (const char *symbol, gboolean *is_ws) * @new_text:(out): The new text with the last word removed * @word: The last word of text * - * Scans `text` from the end returns the last word. If `text` ends with - * whitespace the last word is considered empty and `new_text` and `word` - * remain unchanged. + * Scans `text` from the end and returns the last word. If `text` ends + * with whitespace the last word is considered empty and `new_text` + * and `word` remain unchanged. * * Returns: %TRUE `new_text` and `word` were filled. */ @@ -527,18 +527,18 @@ pos_completer_grab_last_word (const char *text, char **new_text, char **word) /* text ends with whitespace */ len = g_utf8_strlen (text, -1); - symbol = g_utf8_substring (text, len-1, len); + symbol = g_utf8_substring (text, len - 1, len); if (pos_completer_symbol_is_word_separator (symbol, NULL)) return FALSE; /* Get last word in text */ for (glong start = len - 1; start >= 0; start--) { g_free (symbol); - symbol = g_utf8_substring (text, start, start+1); + symbol = g_utf8_substring (text, start, start + 1); if (pos_completer_symbol_is_word_separator (symbol, NULL)) { - *word = g_strdup (g_utf8_offset_to_pointer (text, start+1)); - *new_text = g_utf8_substring (text, 0, start+1); + *word = g_strdup (g_utf8_offset_to_pointer (text, start + 1)); + *new_text = g_utf8_substring (text, 0, start + 1); return TRUE; } } @@ -550,6 +550,62 @@ pos_completer_grab_last_word (const char *text, char **new_text, char **word) return TRUE; } +/** + * pos_completer_find_prev_word_break: + * @text: the text to find the word break in + * + * Scans `text` from the end and returns the last word break in bytes + * counted from the end of the string. The word break is the position + * of the last two words separated by `completion_end_symbols`. + * + * If the string has trailing whitespace the amount of whitespace at the + * end is returned instead. + * + * Returns: The position of the last word break or `-1` if none was found. + */ +long +pos_completer_find_prev_word_break (const char *text) +{ + char *addr; + long len, pos, byte_len; + g_autofree char *symbol = NULL; + + /* Nothing to parse */ + if (STR_IS_NULL_OR_EMPTY (text)) + return -1; + + len = g_utf8_strlen (text, -1); + if (!len) + return -1; + + /* Check trailing whitespace */ + for (pos = len - 1; pos >= 0; pos--) { + g_free (symbol); + symbol = g_utf8_substring (text, pos, pos + 1); + + if (!pos_completer_symbol_is_word_separator (symbol, NULL)) + break; + } + + if (pos != len - 1) + goto out; + + for (; pos >= 0; pos--) { + g_free (symbol); + symbol = g_utf8_substring (text, pos, pos + 1); + + g_debug ("%s", symbol); + + if (pos_completer_symbol_is_word_separator (symbol, NULL)) + break; + } + + out: + byte_len = strlen (text); + addr = g_utf8_offset_to_pointer (text, pos); + return &(text[byte_len]) - addr - 1; +} + /** * pos_completer_capitalize_by_template: * @template: String with some characters in upper case @@ -562,7 +618,7 @@ pos_completer_grab_last_word (const char *text, char **new_text, char **word) * Returns: (transfer full): copy of completions with changed capitalization */ GStrv -pos_completer_capitalize_by_template (const char *template, const GStrv completions) +pos_completer_capitalize_by_template (const char *template, const GStrv completions) { gboolean has_caps; glong templ_len; diff --git a/src/pos-completer.h b/src/pos-completer.h index cc956976e4e929ee49c87d5c7fe00d3e5dcb0f85..5312df3e158361af2c99c27922b9f447c65b3cdc 100644 --- a/src/pos-completer.h +++ b/src/pos-completer.h @@ -81,4 +81,6 @@ void pos_completer_learn_accepted (PosCompleter *self, const char *wor GStrv pos_completer_capitalize_by_template (const char *template, const GStrv completions); +glong pos_completer_find_prev_word_break (const char *text); + G_END_DECLS diff --git a/src/pos-enums.h b/src/pos-enums.h index c754d5c7f163c2db3065c9972df6ec614a98437a..39ca2804e8720f07090882fec1ec541486dea3b7 100644 --- a/src/pos-enums.h +++ b/src/pos-enums.h @@ -108,4 +108,16 @@ typedef enum { POS_INPUT_METHOD_HINT_MULTILINE, } PosInputMethodHint; +/** + * PosBackspaceMode: + * @POS_BACKSPACE_MODE_CHAR: Delete on character + * @POS_BACKSPACE_MODE_WORD: Delete one wordx + * + * How much a press of backspace should remove + */ +typedef enum { + POS_BACKSPACE_MODE_CHAR = 1, + POS_BACKSPACE_MODE_WORD = 2, +} PosBackspaceMode; + G_END_DECLS diff --git a/src/pos-input-surface.c b/src/pos-input-surface.c index 65da1e010de38b95d8f4e1ca6aafb722f6438b98..25c51243bd536c625bd345a4b8d4ab0c432aa47c 100644 --- a/src/pos-input-surface.c +++ b/src/pos-input-surface.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2021 Purism SPC * 2022-2024 The Phosh Developers + * 2025 Phosh.mobi e.V. * * SPDX-License-Identifier: GPL-3.0-or-later * @@ -27,7 +28,6 @@ #include "pos-shortcuts-bar.h" #include "pos-style-manager.h" #include "pos-vk-driver.h" -#include "pos-virtual-keyboard.h" #include "pos-vk-driver.h" #include "util.h" @@ -41,6 +41,15 @@ #include +#define KEY_PRESS_EVENT "key-pressed" +#define BUTTON_PRESS_EVENT "button-pressed" + +#define MIN_Y_VELOCITY 1000 + +#define BS_KEY_REPEAT_DELAY 700 +#define BS_KEY_REPEAT_INTERVAL_CHAR 50 +#define BS_KEY_REPEAT_INTERVAL_WORD 150 + /** * POS_INPUT_SURFACE_IS_LANG_LAYOUT: * @layout: The layout to check @@ -150,9 +159,16 @@ struct _PosInputSurface { /* emission hook for clicks */ gulong clicked_id; + + /* Backspace handling */ + guint bs_repeat_id; + PosBackspaceMode bs_mode; + char *surround_before; }; +static void pos_input_surface_submit_symbol (PosInputSurface *self, const char *symbol); + static void pos_input_surface_action_group_iface_init (GActionGroupInterface *iface); static void pos_input_surface_action_map_iface_init (GActionMapInterface *iface); @@ -161,7 +177,128 @@ G_DEFINE_TYPE_WITH_CODE (PosInputSurface, pos_input_surface, PHOSH_TYPE_LAYER_SU G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_MAP, pos_input_surface_action_map_iface_init) ) -#define MIN_Y_VELOCITY 1000 + +static void +pos_input_surface_trigger_feedback (PosInputSurface *self, const char *event_name) +{ + g_autoptr (LfbEvent) event = NULL; + + g_assert (POS_IS_INPUT_SURFACE (self)); + + event = lfb_event_new (event_name); + lfb_event_set_important (event, TRUE); + lfb_event_trigger_feedback_async (event, NULL, NULL, NULL); +} + + +static void +pos_input_surface_set_backspace_mode (PosInputSurface *self, PosBackspaceMode mode) +{ + if (self->bs_mode == mode) + return; + + self->bs_mode = mode; + g_debug ("Backspace mode: %d", self->bs_mode); +} + +/** + * pos_input_surface_delete_last_word: + * @self: The input surface + * + * Delete the last word from the current surrounding text. + * + * Returns: `TRUE` if anything was deleted, otherwise `FALSE` + */ +static gboolean +pos_input_surface_delete_last_word (PosInputSurface *self) +{ + long len = 0; + + if (self->surround_before) + len = pos_completer_find_prev_word_break (self->surround_before); + + /* We didn't find anything to delete or it's just a single char */ + if (len <= 1) + return FALSE; + + g_debug ("Deleting last word of length %ld", len); + pos_input_method_delete_surrounding_text (self->input_method, MAX (1, len), 0, TRUE); + return TRUE; +} + + +static gboolean +on_bs_key_repeat (gpointer data) +{ + PosInputSurface *self = data; + + pos_input_surface_trigger_feedback (self, KEY_PRESS_EVENT); + pos_input_surface_submit_symbol (self, "KEY_BACKSPACE"); + + return G_SOURCE_CONTINUE; +} + + +static void +on_bs_long_press_timeout (gpointer data) +{ + PosInputSurface *self = data; + guint interval; + + if (pos_input_method_get_active (self->input_method)) { + interval = BS_KEY_REPEAT_INTERVAL_WORD; + pos_input_surface_set_backspace_mode (self, POS_BACKSPACE_MODE_WORD); + } else { + /* When no input method is active we delete individual chars which should + * happen faster than deleting whole words */ + interval = BS_KEY_REPEAT_INTERVAL_CHAR; + } + + self->bs_repeat_id = g_timeout_add (interval, on_bs_key_repeat, self); + g_source_set_name_by_id (self->bs_repeat_id, "[pos-bs-key-repeat]"); + } + + +static gboolean +pos_input_surface_set_backspace_pressed (PosInputSurface *self, const char *symbol) +{ + gboolean pressed; + + pressed = symbol && g_str_equal (symbol, "KEY_BACKSPACE"); + if (!pressed) { + pos_input_surface_set_backspace_mode (self, POS_BACKSPACE_MODE_CHAR); + g_clear_handle_id (&self->bs_repeat_id, g_source_remove); + return FALSE; + } + + if (!self->bs_repeat_id) { + self->bs_repeat_id = g_timeout_add_once (BS_KEY_REPEAT_DELAY, + on_bs_long_press_timeout, + self); + g_source_set_name_by_id (self->bs_repeat_id, "[pos-bs-long-press-timeout]"); + } + + return TRUE; +} + + +static void +pos_input_surface_handle_backsapce (PosInputSurface *self) +{ + gboolean handled = FALSE; + + g_assert (pos_input_method_get_active (self->input_method)); + + if (self->bs_mode == POS_BACKSPACE_MODE_WORD) + handled = pos_input_surface_delete_last_word (self); + + if (handled) + return; + + pos_vk_driver_key_down (self->keyboard_driver, "KEY_BACKSPACE", POS_KEYCODE_MODIFIER_NONE); + pos_vk_driver_key_up (self->keyboard_driver, "KEY_BACKSPACE"); +} + static void on_swipe (GtkGestureSwipe *swipe, double velocity_x, double velocity_y, gpointer data) @@ -236,31 +373,6 @@ on_num_shortcuts_changed (PosInputSurface *self) } -static void -pos_input_surface_notify_key_press (PosInputSurface *self) -{ - g_autoptr (LfbEvent) event = NULL; - - g_assert (POS_IS_INPUT_SURFACE (self)); - - event = lfb_event_new ("key-pressed"); - lfb_event_set_important (event, TRUE); - lfb_event_trigger_feedback_async (event, NULL, NULL, NULL); -} - - -static void -pos_input_surface_notify_button_press (PosInputSurface *self) -{ - g_autoptr (LfbEvent) event = NULL; - - g_assert (POS_IS_INPUT_SURFACE (self)); - - event = lfb_event_new ("button-pressed"); - lfb_event_trigger_feedback_async (event, NULL, NULL, NULL); -} - - static gboolean on_click_hook (GSignalInvocationHint *ihint, guint n_param_values, @@ -269,7 +381,7 @@ on_click_hook (GSignalInvocationHint *ihint, { PosInputSurface *self = POS_INPUT_SURFACE (user_data); - pos_input_surface_notify_button_press (self); + pos_input_surface_trigger_feedback (self, BUTTON_PRESS_EVENT); return TRUE; } @@ -388,19 +500,48 @@ on_osk_key_down (PosInputSurface *self, const char *symbol, GtkWidget *osk_widge g_return_if_fail (POS_IS_INPUT_SURFACE (self)); g_return_if_fail (POS_IS_OSK_WIDGET (osk_widget)); - pos_input_surface_notify_key_press (self); + pos_input_surface_trigger_feedback (self, KEY_PRESS_EVENT); + + pos_input_surface_set_backspace_pressed (self, symbol); +} + + +static void +on_osk_key_up (PosInputSurface *self) +{ + g_assert (POS_IS_INPUT_SURFACE (self)); + + pos_input_surface_set_backspace_pressed (self, NULL); +} + + +static void +on_osk_key_cancelled (PosInputSurface *self) +{ + g_assert (POS_IS_INPUT_SURFACE (self)); + + pos_input_surface_set_backspace_pressed (self, NULL); } static void on_osk_key_symbol (PosInputSurface *self, const char *symbol) { - gboolean handled; + g_assert (POS_IS_INPUT_SURFACE (self)); - g_return_if_fail (POS_IS_INPUT_SURFACE (self)); + pos_input_surface_submit_symbol (self, symbol); +} + + +static void +pos_input_surface_submit_symbol (PosInputSurface *self, const char *symbol) +{ + gboolean handled, is_bs; g_debug ("Key: '%s' symbol", symbol); + is_bs = pos_input_surface_set_backspace_pressed (self, symbol); + /* Latched modifiers, send as virtual-keyboard */ if (self->latched_modifiers) { PosKeycodeModifier modifier; @@ -424,7 +565,9 @@ on_osk_key_symbol (PosInputSurface *self, const char *symbol) return; } - if (g_str_has_prefix (symbol, "KEY_")) { + if (is_bs) { + pos_input_surface_handle_backsapce (self); + } else if (g_str_has_prefix (symbol, "KEY_")) { pos_vk_driver_key_down (self->keyboard_driver, symbol, POS_KEYCODE_MODIFIER_NONE); pos_vk_driver_key_up (self->keyboard_driver, symbol); } else { @@ -590,7 +733,7 @@ on_emoji_picked (PosInputSurface *self, const char *emoji, PosEmojiPicker *emoji send_emoji_via_vk (self, emoji); } - pos_input_surface_notify_key_press (self); + pos_input_surface_trigger_feedback (self, KEY_PRESS_EVENT); } @@ -604,8 +747,8 @@ on_emoji_picker_done (PosInputSurface *self) static void on_emoji_picker_delete_last (PosInputSurface *self) { - g_debug ("%s", __func__); - on_osk_key_symbol (self, "KEY_BACKSPACE"); + g_debug ("Deleting last emoji"); + pos_input_surface_submit_symbol (self, "KEY_BACKSPACE"); } /* Keypads */ @@ -624,7 +767,7 @@ on_keypad_symbol_pressed (PosInputSurface *self, const char *symbol, PosKeypad * pos_vk_driver_key_up (self->keyboard_driver, symbol); } - pos_input_surface_notify_key_press (self); + pos_input_surface_trigger_feedback (self, KEY_PRESS_EVENT); } @@ -1281,7 +1424,6 @@ on_im_surrounding_text_changed (PosInputSurface *self, GParamSpec *pspec, PosInp { const char *text; guint anchor, cursor; - g_autofree char *before = NULL; g_autofree char *after = NULL; g_assert (POS_IS_INPUT_SURFACE (self)); @@ -1292,12 +1434,15 @@ on_im_surrounding_text_changed (PosInputSurface *self, GParamSpec *pspec, PosInp if (!pos_input_surface_is_completion_mode (self)) return; - before = g_strndup (text, cursor); + g_free (self->surround_before); + self->surround_before = g_strndup (text, cursor); if (text) after = g_strdup (&(text[cursor])); - pos_completer_set_surrounding_text (POS_COMPLETER (self->completer), before, after); + pos_completer_set_surrounding_text (POS_COMPLETER (self->completer), + self->surround_before, + after); } @@ -1399,6 +1544,7 @@ pos_input_surface_finalize (GObject *object) self->clicked_id = 0; g_clear_handle_id (&self->animation.id, g_source_remove); + g_clear_handle_id (&self->bs_repeat_id, g_source_remove); g_clear_object (&self->logind_session); g_clear_object (&self->keyboard_driver); @@ -1413,6 +1559,7 @@ pos_input_surface_finalize (GObject *object) g_clear_object (&self->swipe_down); g_clear_object (&self->style_manager); g_clear_pointer (&self->osks, g_hash_table_destroy); + g_clear_pointer (&self->surround_before, g_free); G_OBJECT_CLASS (pos_input_surface_parent_class)->finalize (object); } @@ -1602,7 +1749,9 @@ pos_input_surface_class_init (PosInputSurfaceClass *klass) gtk_widget_class_bind_template_callback (widget_class, on_latched_modifiers_changed); gtk_widget_class_bind_template_callback (widget_class, on_num_shortcuts_changed); + gtk_widget_class_bind_template_callback (widget_class, on_osk_key_cancelled); gtk_widget_class_bind_template_callback (widget_class, on_osk_key_down); + gtk_widget_class_bind_template_callback (widget_class, on_osk_key_up); gtk_widget_class_bind_template_callback (widget_class, on_osk_key_symbol); gtk_widget_class_bind_template_callback (widget_class, on_osk_mode_changed); gtk_widget_class_bind_template_callback (widget_class, on_osk_popover_shown); @@ -1786,8 +1935,10 @@ insert_osk (PosInputSurface *self, g_debug ("Adding osk for layout '%s'", name); gtk_widget_set_visible (GTK_WIDGET (osk_widget), TRUE); g_object_connect (osk_widget, + "swapped-signal::key-cancelled", G_CALLBACK (on_osk_key_cancelled), self, "swapped-signal::key-down", G_CALLBACK (on_osk_key_down), self, "swapped-signal::key-symbol", G_CALLBACK (on_osk_key_symbol), self, + "swapped-signal::key-up", G_CALLBACK (on_osk_key_up), self, "swapped-signal::notify::mode", G_CALLBACK (on_osk_mode_changed), self, "swapped-signal::popover-shown", G_CALLBACK (on_osk_popover_shown), self, "swapped-signal::popover-hidden", G_CALLBACK (on_osk_popover_hidden), self, @@ -1981,6 +2132,7 @@ pos_input_surface_init (PosInputSurface *self) gtk_widget_init_template (GTK_WIDGET (self)); + self->bs_mode = POS_BACKSPACE_MODE_CHAR; self->style_manager = pos_style_manager_new (); self->action_map = g_simple_action_group_new (); g_action_map_add_action_entries (G_ACTION_MAP (self->action_map), diff --git a/src/pos-osk-widget.c b/src/pos-osk-widget.c index 90e088dd575e1f2c74d951ac42775c05c4f08499..597c53ca47da681d71c0c7e1943028d976ce64d0 100644 --- a/src/pos-osk-widget.c +++ b/src/pos-osk-widget.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2022 The Phosh Developers + * Copyright (C) 2022-2024 The Phosh Developers + * 2025 Phosh.mobi e.V. * * SPDX-License-Identifier: GPL-3.0-or-later * @@ -32,9 +33,6 @@ #define MINIMUM_WIDTH 360 -#define KEY_REPEAT_DELAY 700 -#define KEY_REPEAT_INTERVAL 50 - enum { OSK_KEY_DOWN, OSK_KEY_UP, @@ -843,33 +841,6 @@ pos_osk_widget_locate_key (PosOskWidget *self, double x, double y) } -static gboolean -on_key_repeat (gpointer data) -{ - PosOskWidget *self = POS_OSK_WIDGET (data); - - g_return_val_if_fail (self->current, G_SOURCE_REMOVE); - - g_signal_emit (self, signals[OSK_KEY_DOWN], 0, pos_osk_key_get_symbol (self->current)); - g_signal_emit (self, signals[OSK_KEY_UP], 0, pos_osk_key_get_symbol (self->current)); - g_signal_emit (self, signals[OSK_KEY_SYMBOL], 0, pos_osk_key_get_symbol (self->current)); - - return G_SOURCE_CONTINUE; -} - - -static gboolean -on_repeat_timeout (gpointer data) -{ - PosOskWidget *self = POS_OSK_WIDGET (data); - - self->repeat_id = g_timeout_add (KEY_REPEAT_INTERVAL, on_key_repeat, self); - g_source_set_name_by_id (self->repeat_id, "[pos-key-repeat]"); - - return G_SOURCE_REMOVE; -} - - static void key_repeat_cancel (PosOskWidget *self) { @@ -919,11 +890,6 @@ pos_osk_widget_key_press (PosOskWidget *self, double x, double y) } pos_osk_widget_key_press_action (self, key); - if (pos_osk_key_get_use (key) == POS_OSK_KEY_USE_DELETE) { - self->repeat_id = g_timeout_add (KEY_REPEAT_DELAY, on_repeat_timeout, self); - g_source_set_name_by_id (self->repeat_id, "[pos-key-repeat-timeout]"); - } - return GDK_EVENT_PROPAGATE; } @@ -986,8 +952,8 @@ pos_osk_widget_key_release_action (PosOskWidget *self, PosOskKey *key) G_GNUC_FALLTHROUGH; case POS_OSK_KEY_USE_DELETE: pos_osk_widget_set_key_pressed (self, self->current, FALSE); - g_signal_emit (self, signals[OSK_KEY_UP], 0, pos_osk_key_get_symbol (key)); g_signal_emit (self, signals[OSK_KEY_SYMBOL], 0, pos_osk_key_get_symbol (key)); + g_signal_emit (self, signals[OSK_KEY_UP], 0, pos_osk_key_get_symbol (key)); switch_layer (self, key); break; @@ -1142,6 +1108,7 @@ on_symbol_selected (PosOskWidget *self, const char *symbol) g_signal_emit (self, signals[OSK_KEY_DOWN], 0, symbol); g_signal_emit (self, signals[OSK_KEY_SYMBOL], 0, symbol); + g_signal_emit (self, signals[OSK_KEY_UP], 0, symbol); g_clear_pointer (&self->char_popup, phosh_cp_widget_destroy); } diff --git a/src/ui/input-surface.ui b/src/ui/input-surface.ui index 88535a02f8b1a4a0b61c1cd95380776bc614fc2a..9680f38860d25e74d7d81cd56c928c43b5985a95 100644 --- a/src/ui/input-surface.ui +++ b/src/ui/input-surface.ui @@ -39,8 +39,10 @@ 1 + + diff --git a/tests/meson.build b/tests/meson.build index 2b8729e7683f580e672d342cc41f3362726f1449..45761470c57aeb0ad9ba7c4ab245531861bbf8e1 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -20,7 +20,7 @@ test_env.set('ASAN_OPTIONS', 'fast_unwind_on_malloc=0') #test_env.set('ASAN_OPTIONS', 'fast_unwind_on_malloc=0:disable_coredump=0:unmap_shadow_on_exit=1:abort_on_error=1') tests = [ - 'grab-word', + 'completer', 'completer-hunspell', 'load-layouts', 'osk-widget', diff --git a/tests/test-grab-word.c b/tests/test-completer.c similarity index 56% rename from tests/test-grab-word.c rename to tests/test-completer.c index 9b40c917ec3e6f02473ad4a2524cb1cfbbc732a0..300b2b7a6e59876c0d15207ae40e63774da78a35 100644 --- a/tests/test-grab-word.c +++ b/tests/test-completer.c @@ -1,8 +1,10 @@ /* * Copyright © 2020 Lugsole - * Copyright (C) 2021 Purism SPC + * 2021 Purism SPC * * SPDX-License-Identifier: GPL-3.0-or-later + * + * Author: Guido Günther */ #include "pos-completer-priv.h" @@ -10,7 +12,7 @@ #include static void -test_grab_last_word(void) +test_grab_last_word (void) { char *new_before = NULL; char *word = NULL; @@ -40,12 +42,31 @@ test_grab_last_word(void) } +static void +test_find_prev_word_break (void) +{ + g_assert_cmpint (pos_completer_find_prev_word_break ("ends with word"), ==, 4); + g_assert_cmpint (pos_completer_find_prev_word_break ("ends with w😀rd"), ==, 7); + g_assert_cmpint (pos_completer_find_prev_word_break ("e😀😀s with w😀rd"), ==, 7); + g_assert_cmpint (pos_completer_find_prev_word_break ("justoneword"), ==, 11); + + g_assert_cmpint (pos_completer_find_prev_word_break (NULL), ==, -1); + g_assert_cmpint (pos_completer_find_prev_word_break (""), ==, -1); + + g_assert_cmpint (pos_completer_find_prev_word_break (" "), ==, 1); + g_assert_cmpint (pos_completer_find_prev_word_break (" \n\t"), ==, 3); + + g_assert_cmpint (pos_completer_find_prev_word_break ("a \t "), ==, 3); +} + + int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/pos/completer/grab_last_word", test_grab_last_word); + g_test_add_func ("/pos/completer/find_prev_word_break", test_find_prev_word_break); return g_test_run (); }