commit 469d81cb10e549582439680862b72e95bc981dbf
Author: Peng Wu <pwu@redhat.com>
Date:   Wed Mar 25 15:42:44 2020 +0800

    wayland/text-input: Support ibus update_preedit_string_with_mode feature

diff --git a/clutter/clutter/clutter-input-focus-private.h b/clutter/clutter/clutter-input-focus-private.h
index 563e58978..825fa63a0 100644
--- a/clutter/clutter/clutter-input-focus-private.h
+++ b/clutter/clutter/clutter-input-focus-private.h
@@ -36,5 +36,9 @@ void clutter_input_focus_request_surrounding (ClutterInputFocus *focus);
 void clutter_input_focus_set_preedit_text (ClutterInputFocus *focus,
                                            const gchar       *preedit,
                                            guint              cursor);
+void clutter_input_focus_set_preedit_text_with_mode (ClutterInputFocus *focus,
+                                                     const gchar       *preedit,
+                                                     guint              cursor,
+                                                     guint              mode);
 
 #endif /* __CLUTTER_INPUT_FOCUS_PRIVATE_H__ */
diff --git a/clutter/clutter/clutter-input-focus.c b/clutter/clutter/clutter-input-focus.c
index d54494b0f..45fa0d493 100644
--- a/clutter/clutter/clutter-input-focus.c
+++ b/clutter/clutter/clutter-input-focus.c
@@ -146,6 +146,20 @@ clutter_input_focus_set_content_purpose (ClutterInputFocus          *focus,
   clutter_input_method_set_content_purpose (priv->im, purpose);
 }
 
+void
+clutter_input_focus_set_client_commit_preedit (ClutterInputFocus          *focus,
+                                               guint                       client_commit_preedit)
+{
+  ClutterInputFocusPrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus));
+  g_return_if_fail (clutter_input_focus_is_focused (focus));
+
+  priv = clutter_input_focus_get_instance_private (focus);
+
+  clutter_input_method_set_content_purpose (priv->im, client_commit_preedit);
+}
+
 gboolean
 clutter_input_focus_filter_key_event (ClutterInputFocus     *focus,
                                       const ClutterKeyEvent *key)
@@ -242,3 +256,14 @@ clutter_input_focus_set_preedit_text (ClutterInputFocus *focus,
 
   CLUTTER_INPUT_FOCUS_GET_CLASS (focus)->set_preedit_text (focus, preedit, cursor);
 }
+
+void
+clutter_input_focus_set_preedit_text_with_mode (ClutterInputFocus *focus,
+                                                const gchar       *preedit,
+                                                guint              cursor,
+                                                guint              mode)
+{
+  g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus));
+
+  CLUTTER_INPUT_FOCUS_GET_CLASS (focus)->set_preedit_text_with_mode (focus, preedit, cursor, mode);
+}
diff --git a/clutter/clutter/clutter-input-focus.h b/clutter/clutter/clutter-input-focus.h
index 3f971fec4..1d7d9f253 100644
--- a/clutter/clutter/clutter-input-focus.h
+++ b/clutter/clutter/clutter-input-focus.h
@@ -49,6 +49,10 @@ struct _ClutterInputFocusClass
   void (* set_preedit_text) (ClutterInputFocus *focus,
                              const gchar       *preedit,
                              guint              cursor);
+  void (* set_preedit_text_with_mode) (ClutterInputFocus *focus,
+                                       const gchar       *preedit,
+                                       guint              cursor,
+                                       guint              mode);
 };
 
 CLUTTER_EXPORT
@@ -72,6 +76,9 @@ CLUTTER_EXPORT
 void clutter_input_focus_set_content_purpose (ClutterInputFocus          *focus,
                                               ClutterInputContentPurpose  purpose);
 CLUTTER_EXPORT
+void clutter_input_focus_set_client_commit_preedit (ClutterInputFocus          *focus,
+                                                    guint                       client_commit_preedit);
+CLUTTER_EXPORT
 gboolean clutter_input_focus_filter_key_event (ClutterInputFocus     *focus,
                                                const ClutterKeyEvent *key);
 CLUTTER_EXPORT
diff --git a/clutter/clutter/clutter-input-method-private.h b/clutter/clutter/clutter-input-method-private.h
index e8ab2cabf..6b380e34a 100644
--- a/clutter/clutter/clutter-input-method-private.h
+++ b/clutter/clutter/clutter-input-method-private.h
@@ -36,6 +36,8 @@ void clutter_input_method_set_content_hints   (ClutterInputMethod           *met
                                                ClutterInputContentHintFlags  hints);
 void clutter_input_method_set_content_purpose (ClutterInputMethod         *method,
                                                ClutterInputContentPurpose  purpose);
+void clutter_input_method_set_client_commit_preedit (ClutterInputMethod         *method,
+                                                     gboolean                    client_commit_preedit);
 void clutter_input_method_set_can_show_preedit (ClutterInputMethod *method,
                                                 gboolean            can_show_preedit);
 gboolean clutter_input_method_filter_key_event (ClutterInputMethod    *method,
diff --git a/clutter/clutter/clutter-input-method.c b/clutter/clutter/clutter-input-method.c
index 344e19297..a5e6960b4 100644
--- a/clutter/clutter/clutter-input-method.c
+++ b/clutter/clutter/clutter-input-method.c
@@ -34,6 +34,7 @@ struct _ClutterInputMethodPrivate
   ClutterInputFocus *focus;
   ClutterInputContentHintFlags content_hints;
   ClutterInputContentPurpose content_purpose;
+  gboolean client_commit_preedit;
   gboolean can_show_preedit;
 };
 
@@ -52,6 +53,7 @@ enum
   PROP_0,
   PROP_CONTENT_HINTS,
   PROP_CONTENT_PURPOSE,
+  PROP_CLIENT_COMMIT_PREEDIT,
   PROP_CAN_SHOW_PREEDIT,
   N_PROPS
 };
@@ -84,6 +86,18 @@ set_content_purpose (ClutterInputMethod         *im,
                                                                content_purpose);
 }
 
+static void
+set_client_commit_preedit (ClutterInputMethod *im,
+                           gboolean            client_commit_preedit)
+{
+  ClutterInputMethodPrivate *priv;
+
+  priv = clutter_input_method_get_instance_private (im);
+  priv->client_commit_preedit = client_commit_preedit;
+  CLUTTER_INPUT_METHOD_GET_CLASS (im)->update_client_commit_preedit (im,
+                                                                     client_commit_preedit);
+}
+
 static void
 set_can_show_preedit (ClutterInputMethod *im,
                       gboolean            can_show_preedit)
@@ -110,6 +124,10 @@ clutter_input_method_set_property (GObject      *object,
       set_content_purpose (CLUTTER_INPUT_METHOD (object),
                            g_value_get_enum (value));
       break;
+    case PROP_CLIENT_COMMIT_PREEDIT:
+      set_client_commit_preedit (CLUTTER_INPUT_METHOD (object),
+                                 g_value_get_boolean (value));
+      break;
     case PROP_CAN_SHOW_PREEDIT:
       set_can_show_preedit (CLUTTER_INPUT_METHOD (object),
                             g_value_get_boolean (value));
@@ -140,6 +158,9 @@ clutter_input_method_get_property (GObject    *object,
     case PROP_CONTENT_PURPOSE:
       g_value_set_enum (value, priv->content_purpose);
       break;
+    case PROP_CLIENT_COMMIT_PREEDIT:
+      g_value_set_boolean (value, priv->client_commit_preedit);
+      break;
     case PROP_CAN_SHOW_PREEDIT:
       g_value_set_boolean (value, priv->can_show_preedit);
       break;
@@ -203,6 +224,13 @@ clutter_input_method_class_init (ClutterInputMethodClass *klass)
                        CLUTTER_TYPE_INPUT_CONTENT_PURPOSE, 0,
                        G_PARAM_READWRITE |
                        G_PARAM_STATIC_STRINGS);
+  pspecs[PROP_CLIENT_COMMIT_PREEDIT] =
+    g_param_spec_boolean ("client-commit-preedit",
+                          P_("Client commit preedit"),
+                          P_("Client commit preedit"),
+                          FALSE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_STATIC_STRINGS);
   pspecs[PROP_CAN_SHOW_PREEDIT] =
     g_param_spec_boolean ("can-show-preedit",
                           P_("Can show preedit"),
@@ -338,6 +366,30 @@ clutter_input_method_set_preedit_text (ClutterInputMethod *im,
     clutter_input_focus_set_preedit_text (priv->focus, preedit, cursor);
 }
 
+/**
+ * clutter_input_method_set_preedit_text_with_mode:
+ * @im: a #ClutterInputMethod
+ * @preedit: (nullable): the preedit text, or %NULL
+ * @cursor: the cursor
+ * @mode: the commit mode
+ *
+ * Sets the preedit text with commit mode on the current input focus.
+ **/
+void
+clutter_input_method_set_preedit_text_with_mode (ClutterInputMethod *im,
+                                                 const gchar        *preedit,
+                                                 guint               cursor,
+                                                 guint               mode)
+{
+  ClutterInputMethodPrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im));
+
+  priv = clutter_input_method_get_instance_private (im);
+  if (priv->focus)
+    clutter_input_focus_set_preedit_text_with_mode (priv->focus, preedit, cursor, mode);
+}
+
 void
 clutter_input_method_notify_key_event (ClutterInputMethod *im,
                                        const ClutterEvent *event,
@@ -417,6 +469,15 @@ clutter_input_method_set_content_purpose (ClutterInputMethod         *im,
   g_object_set (G_OBJECT (im), "content-purpose", purpose, NULL);
 }
 
+void
+clutter_input_method_set_client_commit_preedit (ClutterInputMethod         *im,
+                                                gboolean                    client_commit_preedit)
+{
+  g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im));
+
+  g_object_set (G_OBJECT (im), "client-commit-preedit", client_commit_preedit, NULL);
+}
+
 void
 clutter_input_method_set_can_show_preedit (ClutterInputMethod *im,
                                            gboolean            can_show_preedit)
diff --git a/clutter/clutter/clutter-input-method.h b/clutter/clutter/clutter-input-method.h
index ba1bc223a..4b41213fb 100644
--- a/clutter/clutter/clutter-input-method.h
+++ b/clutter/clutter/clutter-input-method.h
@@ -52,6 +52,8 @@ struct _ClutterInputMethodClass
                                  ClutterInputContentHintFlags  hint);
   void (* update_content_purpose) (ClutterInputMethod         *im,
                                    ClutterInputContentPurpose  purpose);
+  void (* update_client_commit_preedit) (ClutterInputMethod         *im,
+                                         gboolean                    client_commit_preedit);
 
   gboolean (* filter_key_event) (ClutterInputMethod *im,
                                  const ClutterEvent *key);
@@ -78,6 +80,12 @@ void clutter_input_method_set_preedit_text (ClutterInputMethod *im,
                                             const gchar        *preedit,
                                             guint               cursor);
 
+CLUTTER_EXPORT
+void clutter_input_method_set_preedit_text_with_mode (ClutterInputMethod *im,
+                                                      const gchar        *preedit,
+                                                      guint               cursor,
+                                                      guint               mode);
+
 CLUTTER_EXPORT
 void clutter_input_method_notify_key_event (ClutterInputMethod *im,
                                             const ClutterEvent *event,
diff --git a/src/wayland/meta-wayland-text-input-legacy.c b/src/wayland/meta-wayland-text-input-legacy.c
index d4caa9aad..636f9bf42 100644
--- a/src/wayland/meta-wayland-text-input-legacy.c
+++ b/src/wayland/meta-wayland-text-input-legacy.c
@@ -34,10 +34,12 @@
 
 typedef enum
 {
-  META_WAYLAND_PENDING_STATE_NONE             = 0,
-  META_WAYLAND_PENDING_STATE_INPUT_RECT       = 1 << 0,
-  META_WAYLAND_PENDING_STATE_CONTENT_TYPE     = 1 << 1,
-  META_WAYLAND_PENDING_STATE_SURROUNDING_TEXT = 1 << 2,
+  META_WAYLAND_PENDING_STATE_NONE                     = 0,
+  META_WAYLAND_PENDING_STATE_INPUT_RECT               = 1 << 0,
+  META_WAYLAND_PENDING_STATE_CONTENT_TYPE             = 1 << 1,
+  META_WAYLAND_PENDING_STATE_SURROUNDING_TEXT         = 1 << 2,
+  META_WAYLAND_PENDING_STATE_RESET                    = 1 << 3,
+  META_WAYLAND_PENDING_STATE_CLIENT_COMMIT_PREEDIT    = 1 << 4,
 } MetaWaylandTextInputPendingState;
 
 typedef struct _MetaWaylandGtkTextInput MetaWaylandGtkTextInput;
@@ -66,6 +68,7 @@ struct _MetaWaylandGtkTextInput
 
   uint32_t content_type_hint;
   uint32_t content_type_purpose;
+  uint32_t client_commit_preedit;
 };
 
 struct _MetaWaylandGtkTextInputFocus
@@ -140,6 +143,23 @@ meta_wayland_text_input_focus_set_preedit_text (ClutterInputFocus *focus,
     }
 }
 
+static void
+meta_wayland_text_input_focus_set_preedit_text_with_mode (ClutterInputFocus *focus,
+                                                          const gchar       *text,
+                                                          guint              cursor,
+                                                          guint              mode)
+{
+  MetaWaylandGtkTextInput *text_input;
+  struct wl_resource *resource;
+
+  text_input = META_WAYLAND_GTK_TEXT_INPUT_FOCUS (focus)->text_input;
+
+  wl_resource_for_each (resource, &text_input->focus_resource_list)
+    {
+      gtk_text_input_send_preedit_string_with_mode (resource, text, cursor, mode);
+    }
+}
+
 static void
 meta_wayland_gtk_text_input_focus_class_init (MetaWaylandGtkTextInputFocusClass *klass)
 {
@@ -149,6 +169,7 @@ meta_wayland_gtk_text_input_focus_class_init (MetaWaylandGtkTextInputFocusClass
   focus_class->delete_surrounding = meta_wayland_text_input_focus_delete_surrounding;
   focus_class->commit_text = meta_wayland_text_input_focus_commit_text;
   focus_class->set_preedit_text = meta_wayland_text_input_focus_set_preedit_text;
+  focus_class->set_preedit_text_with_mode = meta_wayland_text_input_focus_set_preedit_text_with_mode;
 }
 
 static void
@@ -428,6 +449,20 @@ text_input_set_content_type (struct wl_client   *client,
   text_input->pending_state |= META_WAYLAND_PENDING_STATE_CONTENT_TYPE;
 }
 
+static void
+text_input_set_client_commit_preedit (struct wl_client   *client,
+                                      struct wl_resource *resource,
+                                      uint32_t            client_commit_preedit)
+{
+  MetaWaylandGtkTextInput *text_input = wl_resource_get_user_data (resource);
+
+  if (!text_input->surface)
+    return;
+
+  text_input->client_commit_preedit = client_commit_preedit;
+  text_input->pending_state |= META_WAYLAND_PENDING_STATE_CLIENT_COMMIT_PREEDIT;
+}
+
 static void
 text_input_set_cursor_rectangle (struct wl_client   *client,
                                  struct wl_resource *resource,
@@ -492,17 +527,42 @@ text_input_commit_state (struct wl_client   *client,
                                                &cursor_rect);
     }
 
+  if (text_input->pending_state & META_WAYLAND_PENDING_STATE_RESET)
+    {
+        clutter_input_focus_reset (text_input->input_focus);
+    }
+
+  if (text_input->pending_state & META_WAYLAND_PENDING_STATE_CLIENT_COMMIT_PREEDIT)
+    {
+      clutter_input_focus_set_client_commit_preedit (text_input->input_focus,
+                                                     text_input->client_commit_preedit);
+    }
+
   text_input->pending_state = META_WAYLAND_PENDING_STATE_NONE;
 }
 
+static void
+text_input_reset (struct wl_client   *client,
+                  struct wl_resource *resource)
+{
+  MetaWaylandGtkTextInput *text_input = wl_resource_get_user_data (resource);
+
+  if (!text_input->surface)
+    return;
+
+  text_input->pending_state |= META_WAYLAND_PENDING_STATE_RESET;
+}
+
 static struct gtk_text_input_interface meta_text_input_interface = {
   text_input_destroy,
   text_input_enable,
   text_input_disable,
   text_input_set_surrounding_text,
   text_input_set_content_type,
+  text_input_set_client_commit_preedit,
   text_input_set_cursor_rectangle,
   text_input_commit_state,
+  text_input_reset,
 };
 
 MetaWaylandGtkTextInput *
diff --git a/src/wayland/meta-wayland-text-input.c b/src/wayland/meta-wayland-text-input.c
index d63f489bd..1cfd0bf25 100644
--- a/src/wayland/meta-wayland-text-input.c
+++ b/src/wayland/meta-wayland-text-input.c
@@ -35,12 +35,14 @@
 
 typedef enum
 {
-  META_WAYLAND_PENDING_STATE_NONE             = 0,
-  META_WAYLAND_PENDING_STATE_INPUT_RECT       = 1 << 0,
-  META_WAYLAND_PENDING_STATE_CONTENT_TYPE     = 1 << 1,
-  META_WAYLAND_PENDING_STATE_SURROUNDING_TEXT = 1 << 2,
-  META_WAYLAND_PENDING_STATE_CHANGE_CAUSE     = 1 << 3,
-  META_WAYLAND_PENDING_STATE_ENABLED          = 1 << 4,
+  META_WAYLAND_PENDING_STATE_NONE                     = 0,
+  META_WAYLAND_PENDING_STATE_INPUT_RECT               = 1 << 0,
+  META_WAYLAND_PENDING_STATE_CONTENT_TYPE             = 1 << 1,
+  META_WAYLAND_PENDING_STATE_SURROUNDING_TEXT         = 1 << 2,
+  META_WAYLAND_PENDING_STATE_CHANGE_CAUSE             = 1 << 3,
+  META_WAYLAND_PENDING_STATE_ENABLED                  = 1 << 4,
+  META_WAYLAND_PENDING_STATE_RESET                    = 1 << 5,
+  META_WAYLAND_PENDING_STATE_CLIENT_COMMIT_PREEDIT    = 1 << 6,
 } MetaWaylandTextInputPendingState;
 
 typedef struct _MetaWaylandTextInput MetaWaylandTextInput;
@@ -72,6 +74,7 @@ struct _MetaWaylandTextInput
   uint32_t content_type_purpose;
   uint32_t text_change_cause;
   gboolean enabled;
+  uint32_t client_commit_preedit;
 
   guint done_idle_id;
 };
@@ -218,6 +221,25 @@ meta_wayland_text_input_focus_set_preedit_text (ClutterInputFocus *focus,
   meta_wayland_text_input_focus_defer_done (focus);
 }
 
+static void
+meta_wayland_text_input_focus_set_preedit_text_with_mode (ClutterInputFocus *focus,
+                                                          const gchar       *text,
+                                                          guint              cursor,
+                                                          guint              mode)
+{
+  MetaWaylandTextInput *text_input;
+  struct wl_resource *resource;
+
+  text_input = META_WAYLAND_TEXT_INPUT_FOCUS (focus)->text_input;
+
+  wl_resource_for_each (resource, &text_input->focus_resource_list)
+    {
+      zwp_text_input_v3_send_preedit_string_with_mode (resource, text, cursor, cursor, mode);
+    }
+
+  meta_wayland_text_input_focus_defer_done (focus);
+}
+
 static void
 meta_wayland_text_input_focus_class_init (MetaWaylandTextInputFocusClass *klass)
 {
@@ -227,6 +249,7 @@ meta_wayland_text_input_focus_class_init (MetaWaylandTextInputFocusClass *klass)
   focus_class->delete_surrounding = meta_wayland_text_input_focus_delete_surrounding;
   focus_class->commit_text = meta_wayland_text_input_focus_commit_text;
   focus_class->set_preedit_text = meta_wayland_text_input_focus_set_preedit_text;
+  focus_class->set_preedit_text_with_mode = meta_wayland_text_input_focus_set_preedit_text_with_mode;
 }
 
 static void
@@ -485,6 +508,20 @@ text_input_set_content_type (struct wl_client   *client,
   text_input->pending_state |= META_WAYLAND_PENDING_STATE_CONTENT_TYPE;
 }
 
+static void
+text_input_set_client_commit_preedit (struct wl_client   *client,
+                                      struct wl_resource *resource,
+                                      uint32_t            client_commit_preedit)
+{
+  MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource);
+
+  if (!text_input->surface)
+    return;
+
+  text_input->client_commit_preedit = client_commit_preedit;
+  text_input->pending_state |= META_WAYLAND_PENDING_STATE_CLIENT_COMMIT_PREEDIT;
+}
+
 static void
 text_input_set_cursor_rectangle (struct wl_client   *client,
                                  struct wl_resource *resource,
@@ -590,12 +627,35 @@ text_input_commit_state (struct wl_client   *client,
                                                &cursor_rect);
     }
 
+  if (text_input->pending_state & META_WAYLAND_PENDING_STATE_RESET)
+    {
+        clutter_input_focus_reset (text_input->input_focus);
+    }
+
+  if (text_input->pending_state & META_WAYLAND_PENDING_STATE_CLIENT_COMMIT_PREEDIT)
+    {
+      clutter_input_focus_set_client_commit_preedit (text_input->input_focus,
+                                                     text_input->client_commit_preedit);
+    }
+
   meta_wayland_text_input_reset (text_input);
 
   if (enable_panel)
     clutter_input_focus_set_input_panel_state (focus, CLUTTER_INPUT_PANEL_STATE_ON);
 }
 
+static void
+text_input_reset (struct wl_client   *client,
+                  struct wl_resource *resource)
+{
+  MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource);
+
+  if (!text_input->surface)
+    return;
+
+  text_input->pending_state |= META_WAYLAND_PENDING_STATE_RESET;
+}
+
 static struct zwp_text_input_v3_interface meta_text_input_interface = {
   text_input_destroy,
   text_input_enable,
@@ -603,8 +663,10 @@ static struct zwp_text_input_v3_interface meta_text_input_interface = {
   text_input_set_surrounding_text,
   text_input_set_text_change_cause,
   text_input_set_content_type,
+  text_input_set_client_commit_preedit,
   text_input_set_cursor_rectangle,
   text_input_commit_state,
+  text_input_reset,
 };
 
 MetaWaylandTextInput *
diff --git a/src/wayland/protocol/gtk-text-input.xml b/src/wayland/protocol/gtk-text-input.xml
index a134a19f6..4c9353889 100644
--- a/src/wayland/protocol/gtk-text-input.xml
+++ b/src/wayland/protocol/gtk-text-input.xml
@@ -185,6 +185,21 @@
       <arg name="purpose" type="uint" enum="content_purpose"/>
     </request>
 
+    <enum name="client_commit_preedit">
+      <description summary="client commit preedit">
+	Whether the client commit preedit text or not.
+      </description>
+      <entry name="false" value="0" summary="default input, input method commits the preedit when received the reset signal."/>
+      <entry name="true" value="1" summary="the client commit the preedit text already."/>
+    </enum>
+
+    <request name="set_client_commit_preedit">
+      <description summary="set client commit preedit capability">
+       Sets the client commit preedit capability.
+      </description>
+      <arg name="client_commit" type="uint" enum="client_commit_preedit"/>
+    </request>
+
     <request name="set_cursor_rectangle">
       <description summary="set cursor position">
        Sets the cursor outline as a x, y, width, height rectangle in surface
@@ -214,6 +229,12 @@
       </description>
     </request>
 
+    <request name="reset">
+      <description summary="reset state">
+       Reset the input method state.
+      </description>
+    </request>
+
     <event name="enter">
       <description summary="enter event">
        Notification that this seat's text-input focus is on a certain surface.
@@ -251,6 +272,17 @@
       <arg name="cursor" type="uint"/>
     </event>
 
+    <event name="preedit_string_with_mode">
+      <description summary="pre-edit-with-mode">
+       Notify when a new composing text (pre-edit) should be set around the
+       current cursor position. Any previously set composing text should
+       be removed.
+      </description>
+      <arg name="text" type="string" allow-null="true"/>
+      <arg name="cursor" type="uint"/>
+      <arg name="mode" type="uint"/>
+    </event>
+
     <event name="commit_string">
       <description summary="text commit">
        Notify when text should be inserted into the editor widget. The text to
