diff --git a/examples/inputsynth.c b/examples/inputsynth.c
index 7c465733e973abff58600b8b02fe7fc9bfda6ea0..9a9f2b5fcdb824a03158f875ae22885d3bf743f4 100644
--- a/examples/inputsynth.c
+++ b/examples/inputsynth.c
@@ -16,6 +16,7 @@
 
 #include <inputsynth.h>
 #include <unistd.h>
+#include <X11/keysym.h>
 
 #define MSEC_TO_NSEC 1000000
 
@@ -41,6 +42,29 @@ _run_exmaple (InputSynth *input)
   "नीला🙏"
   };
 
+  g_print ("keyboard input in 1 second...\n");
+  _nanosleep (1000 * MSEC_TO_NSEC);
+
+  for (unsigned long i = 0; i < sizeof (strings) / sizeof (strings[0]); i++)
+    {
+      input_synth_characters (input, strings[i]);
+      _nanosleep (100 * MSEC_TO_NSEC);
+
+        for (glong j = 0; j < g_utf8_strlen (strings[i], -1); j++)
+          {
+            input_synth_keyvals (input, (guint[]){ XK_BackSpace }, 1);
+            _nanosleep (100 * MSEC_TO_NSEC);
+          }
+    }
+
+  input_synth_characters (input, "helloo!");
+  _nanosleep (100 * MSEC_TO_NSEC);
+  input_synth_keyvals (input, (guint[]){ XK_Left }, 1);
+  _nanosleep (100 * MSEC_TO_NSEC);
+  input_synth_keyvals (input, (guint[]){ XK_Control_L, XK_h }, 2);
+  _nanosleep (100 * MSEC_TO_NSEC);
+  input_synth_characters (input, ", world");
+
   g_print ("Moving cursor...\n");
   for (int i = 0; i < 500; i += 5)
     {
@@ -75,14 +99,6 @@ _run_exmaple (InputSynth *input)
   _nanosleep (500 * MSEC_TO_NSEC);
   input_synth_click (input, char_x, char_y, 3, FALSE);
 
-  g_print ("keyboard input in 1 second...\n");
-  _nanosleep (1000 * MSEC_TO_NSEC);
-
-  for (unsigned long i = 0; i < sizeof (strings) / sizeof (strings[0]); i++)
-    {
-      input_synth_characters (input, strings[i]);
-      _nanosleep (100 * MSEC_TO_NSEC);
-    }
   return TRUE;
 }
 
diff --git a/src/inputsynth-xdo.c b/src/inputsynth-xdo.c
index 001039802664627a15a7f89b9529c7d3cec6a607..0d4c07d43448283e35c8be3e0c023f55448ad3a1 100644
--- a/src/inputsynth-xdo.c
+++ b/src/inputsynth-xdo.c
@@ -64,6 +64,24 @@ characters_xdo (InputSynth *self_parent, char *characters)
   xdo_enter_text_window (self->xdo, CURRENTWINDOW, characters, 0);
 }
 
+static void
+keyvals_xdo (InputSynth *self_parent, guint *keyvals, guint keyvals_length)
+{
+  InputSynthXdo *self = INPUT_SYNTH_XDO (self_parent);
+
+  g_autoptr(GString) keyvals_str = g_string_new("");
+
+  for (guint i = 0; i < keyvals_length; i++)
+    {
+      if (i > 0)
+        g_string_append (keyvals_str, "+");
+
+      g_string_append_printf (keyvals_str, "0x%x", keyvals[i]);
+    }
+
+  xdo_send_keysequence_window (self->xdo, CURRENTWINDOW, keyvals_str->str, 0);
+}
+
 static void
 input_synth_xdo_finalize (GObject *gobject)
 {
@@ -92,5 +110,6 @@ input_synth_xdo_class_init (InputSynthXdoClass *klass)
   input_synth_class->move_cursor = move_cursor_xdo;
   input_synth_class->character = character_xdo;
   input_synth_class->characters = characters_xdo;
+  input_synth_class->keyvals = keyvals_xdo;
   input_synth_class->get_backend_name = get_backend_name_xdo;
 }
diff --git a/src/inputsynth-xi2.c b/src/inputsynth-xi2.c
index 3913e3519425ab6fb6de07ca662e0d7c6319a4ea..1cd4f679d97b62df3d38f4082c9df2baca8a0f4b 100644
--- a/src/inputsynth-xi2.c
+++ b/src/inputsynth-xi2.c
@@ -448,6 +448,16 @@ characters_xi2 (InputSynth *self_parent, char *characters)
   g_printerr ("xi2 characters input not implemented\n");
 }
 
+static void
+keyvals_xi2 (InputSynth *self_parent, guint *keyvals, guint keyvals_length)
+{
+  InputSynthXi2 *self = INPUT_SYNTH_XI2 (self_parent);
+  (void) self;
+  (void) keyvals;
+  (void) keyvals_length;
+  g_printerr ("xi2 keyvals input not implemented\n");
+}
+
 static GString *
 get_backend_name_xi2 (InputSynth *self_parent)
 {
@@ -469,6 +479,7 @@ input_synth_xi2_class_init (InputSynthXi2Class *klass)
   input_synth_class->move_cursor = move_cursor_xi2;
   input_synth_class->character = character_xi2;
   input_synth_class->characters = characters_xi2;
+  input_synth_class->keyvals = keyvals_xi2;
 
   input_synth_class->get_backend_name = get_backend_name_xi2;
 }
diff --git a/src/inputsynth.c b/src/inputsynth.c
index 977d4728afe63a4175bc554d3c5ab3c9ae6bafe6..c3b20c82b19904f9f7f03d9d4577d649fb538a48 100644
--- a/src/inputsynth.c
+++ b/src/inputsynth.c
@@ -190,6 +190,19 @@ input_synth_characters (InputSynth *self, char *characters)
   klass->characters (self, characters);
 }
 
+/**
+ * input_synth_keysequence:
+ * @self: The #InputSynth
+ * @c: The key sequence to synthesize. Nul-terminated string.
+ */
+void
+input_synth_keyvals (InputSynth *self, guint *keyvals, guint keyvals_length)
+{
+  InputSynthClass *klass = INPUT_SYNTH_GET_CLASS (self);
+  g_return_if_fail (klass->keyvals != NULL);
+  klass->keyvals (self, keyvals, keyvals_length);
+}
+
 /**
  * input_synth_get_backend_name:
  * @self: The #InputSynth
diff --git a/src/inputsynth.h b/src/inputsynth.h
index b6a5e07e2caa23a84f3791aed82987248c60b4d2..14994138b223159234e5e9bfea45d91ea5d3c7be 100644
--- a/src/inputsynth.h
+++ b/src/inputsynth.h
@@ -23,6 +23,7 @@ struct _InputSynthClass
   void (*click) (InputSynth *self, int x, int y, int button, gboolean press);
   void (*character) (InputSynth *self, char character);
   void (*characters) (InputSynth *self, char *characters);
+  void (*keyvals) (InputSynth *self, guint *keyvals, guint keyvals_length);
   GString * (*get_backend_name) (InputSynth *self);
 };
 
@@ -57,6 +58,9 @@ input_synth_character (InputSynth *self, char character);
 void
 input_synth_characters (InputSynth *self, char *characters);
 
+void
+input_synth_keyvals (InputSynth *self, guint *keyval, guint keyval_length);
+
 GString *
 input_synth_get_backend_name (InputSynth *self);