Commit e5a3acd6bd0349c585ba7c0e9865cefb4772c00c

Guldoman 2022-05-05T04:01:05

ime: fcitx: Retrieve cursor position and selection Also, if `SDL_HINT_IME_SUPPORT_EXTENDED_TEXT` is enabled, make use of `SDL_TEXTEDITING_EXT` by sending the full preedit string.

diff --git a/src/core/linux/SDL_fcitx.c b/src/core/linux/SDL_fcitx.c
index bab5e88..b7a0cfa 100644
--- a/src/core/linux/SDL_fcitx.c
+++ b/src/core/linux/SDL_fcitx.c
@@ -85,14 +85,22 @@ GetAppName()
 }
 
 static size_t
-Fcitx_GetPreeditString(SDL_DBusContext *dbus, DBusMessage *msg, char **ret) {
+Fcitx_GetPreeditString(SDL_DBusContext *dbus,
+        DBusMessage *msg,
+        char **ret,
+        Sint32 *start_pos,
+        Sint32 *end_pos)
+{
     char *text = NULL, *subtext;
     size_t text_bytes = 0;
     DBusMessageIter iter, array, sub;
+    Sint32 p_start_pos = -1;
+    Sint32 p_end_pos = -1;
 
     dbus->message_iter_init(msg, &iter);
     /* Message type is a(si)i, we only need string part */
     if (dbus->message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
+        size_t pos = 0;
         /* First pass: calculate string length */
         dbus->message_iter_recurse(&iter, &array);
         while (dbus->message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
@@ -103,7 +111,29 @@ Fcitx_GetPreeditString(SDL_DBusContext *dbus, DBusMessage *msg, char **ret) {
                     text_bytes += SDL_strlen(subtext);
                 }
             }
+            dbus->message_iter_next(&sub);
+            if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_INT32 && p_end_pos == -1) {
+                /* Type is a bit field defined as follows:                */
+                /* bit 3: Underline, bit 4: HighLight, bit 5: DontCommit, */
+                /* bit 6: Bold,      bit 7: Strike,    bit 8: Italic      */
+                Sint32 type;
+                dbus->message_iter_get_basic(&sub, &type);
+                /* We only consider highlight */
+                if (type & (1 << 4)) {
+                    if (p_start_pos == -1) {
+                        p_start_pos = pos;
+                    }
+                } else if (p_start_pos != -1 && p_end_pos == -1) {
+                    p_end_pos = pos;
+                }
+            }
             dbus->message_iter_next(&array);
+            if (subtext && *subtext) {
+                pos += SDL_utf8strlen(subtext);
+            }
+        }
+        if (p_start_pos != -1 && p_end_pos == -1) {
+            p_end_pos = pos;
         }
         if (text_bytes) {
             text = SDL_malloc(text_bytes + 1);
@@ -129,10 +159,32 @@ Fcitx_GetPreeditString(SDL_DBusContext *dbus, DBusMessage *msg, char **ret) {
             text_bytes = 0;
         }
     }
-    *ret= text;
+
+    *ret = text;
+    *start_pos = p_start_pos;
+    *end_pos = p_end_pos;
     return text_bytes;
 }
 
+static Sint32
+Fcitx_GetPreeditCursorByte(SDL_DBusContext *dbus, DBusMessage *msg)
+{
+    Sint32 byte = -1;
+    DBusMessageIter iter;
+
+    dbus->message_iter_init(msg, &iter);
+
+    dbus->message_iter_next(&iter);
+
+    if (dbus->message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
+        return -1;
+    }
+
+    dbus->message_iter_get_basic(&iter, &byte);
+
+    return byte;
+}
+
 static DBusHandlerResult
 DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)
 {
@@ -162,20 +214,28 @@ DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)
 
     if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "UpdateFormattedPreedit")) {
         char *text = NULL;
-        size_t text_bytes = Fcitx_GetPreeditString(dbus, msg, &text);
+        Sint32 start_pos, end_pos;
+        size_t text_bytes = Fcitx_GetPreeditString(dbus, msg, &text, &start_pos, &end_pos);
         if (text_bytes) {
-            char buf[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
-            size_t i = 0;
-            size_t cursor = 0;
-
-            while (i < text_bytes) {
-                const size_t sz = SDL_utf8strlcpy(buf, text + i, sizeof(buf));
-                const size_t chars = SDL_utf8strlen(buf);
-
-                SDL_SendEditingText(buf, cursor, chars);
-
-                i += sz;
-                cursor += chars;
+            if (SDL_GetHintBoolean(SDL_HINT_IME_SUPPORT_EXTENDED_TEXT, SDL_FALSE)) {
+                if (start_pos == -1) {
+                    Sint32 byte_pos = Fcitx_GetPreeditCursorByte(dbus, msg);
+                    start_pos = byte_pos >= 0 ? SDL_utf8strnlen(text, byte_pos) : -1;
+                }
+                SDL_SendEditingText(text, start_pos, end_pos >= 0 ? end_pos - start_pos : -1);
+            } else {
+                char buf[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
+                size_t i = 0;
+                size_t cursor = 0;
+                while (i < text_bytes) {
+                    const size_t sz = SDL_utf8strlcpy(buf, text + i, sizeof(buf));
+                    const size_t chars = SDL_utf8strlen(buf);
+
+                    SDL_SendEditingText(buf, cursor, chars);
+
+                    i += sz;
+                    cursor += chars;
+                }
             }
             SDL_free(text);
         } else {