diff --git a/data/keyboards/de.yaml b/data/keyboards/de.yaml
index 66566289609a1fd4074bfe01b6bcb8e6546bb111..06687ebfc09b3588115ac22aea86d0f5a87b4025 100644
--- a/data/keyboards/de.yaml
+++ b/data/keyboards/de.yaml
@@ -45,7 +45,7 @@ buttons:
     BackSpace:
         outline: "altline"
         icon: "edit-clear-symbolic"
-        keysym: "BackSpace"
+        action: "erase"
     preferences:
         action: "show_prefs"
         outline: "special"
diff --git a/data/keyboards/de_wide.yaml b/data/keyboards/de_wide.yaml
index 1f6385573cb6de8f4048c58e2a3cabdf0ab7aa11..dc5dec0f482df8455623814c38c23c1d6a071f0d 100644
--- a/data/keyboards/de_wide.yaml
+++ b/data/keyboards/de_wide.yaml
@@ -45,7 +45,7 @@ buttons:
     BackSpace:
         outline: "altline"
         icon: "edit-clear-symbolic"
-        keysym: "BackSpace"
+        action: "erase"
     preferences:
         action: "show_prefs"
         outline: "special"
diff --git a/data/keyboards/el.yaml b/data/keyboards/el.yaml
index f24b207a72decce25510735c67a4eeb98816007a..6279f75be87873d8bde0712194fa2d2e43844f4e 100644
--- a/data/keyboards/el.yaml
+++ b/data/keyboards/el.yaml
@@ -46,7 +46,7 @@ buttons:
     BackSpace:
         outline: "altline"
         icon: "edit-clear-symbolic"
-        keysym: "BackSpace"
+        action: "erase"
     preferences:
         action: "show_prefs"
         outline: "altline"
diff --git a/data/keyboards/es.yaml b/data/keyboards/es.yaml
index bc7ba73f74a402d7f055eccc07d1d203bac7764f..7343e7b349a11b6022d7c0b183a8445c757c9601 100644
--- a/data/keyboards/es.yaml
+++ b/data/keyboards/es.yaml
@@ -44,7 +44,7 @@ buttons:
     BackSpace:
         outline: "altline"
         icon: "edit-clear-symbolic"
-        keysym: "BackSpace"
+        action: "erase"
     preferences:
         action: "show_prefs"
         outline: "default"
diff --git a/data/keyboards/fi.yaml b/data/keyboards/fi.yaml
index ec972f47eeb4401cee7e585e8cc537af78efe830..25493d705ce7ae3cdafd57124b98363ca1767ca3 100644
--- a/data/keyboards/fi.yaml
+++ b/data/keyboards/fi.yaml
@@ -39,7 +39,7 @@ buttons:
     BackSpace:
         outline: "altline"
         icon: "edit-clear-symbolic"
-        keysym: "BackSpace"
+        action: "erase"
     preferences:
         action: "show_prefs"
         outline: "altline"
diff --git a/data/keyboards/it.yaml b/data/keyboards/it.yaml
index 7a05ed461d9cc2cbe60f14958905bff7027ed1eb..089304722d54cd2cb403a9ece840e0685fd9ac3c 100644
--- a/data/keyboards/it.yaml
+++ b/data/keyboards/it.yaml
@@ -46,7 +46,7 @@ buttons:
     BackSpace:
         outline: "altline"
         icon: "edit-clear-symbolic"
-        keysym: "BackSpace"
+        action: "erase"
     preferences:
         action: "show_prefs"
         outline: "default"
diff --git a/data/keyboards/jp+kana.yaml b/data/keyboards/jp+kana.yaml
index 076a7db1c0aea08b5fc82073c38264841f574d25..b129d3d745b2e1180aee24abf5df4eba90f52c7b 100644
--- a/data/keyboards/jp+kana.yaml
+++ b/data/keyboards/jp+kana.yaml
@@ -195,7 +195,7 @@ buttons:
     BackSpace:
         outline: "wide"
         icon: "edit-clear-symbolic"
-        keysym: "BackSpace"
+        action: erase
     Return:
         outline: "wide"
         icon: "key-enter"
diff --git a/data/keyboards/jp+kana_wide.yaml b/data/keyboards/jp+kana_wide.yaml
index 684cedfc3f790d30352ab1c274c944c1f35bdd71..8f0c536e5381aafce58de34714ef460e03b6c847 100644
--- a/data/keyboards/jp+kana_wide.yaml
+++ b/data/keyboards/jp+kana_wide.yaml
@@ -195,7 +195,7 @@ buttons:
     BackSpace:
         outline: "wide"
         icon: "edit-clear-symbolic"
-        keysym: "BackSpace"
+        action: erase
     Return:
         outline: "wide"
         icon: "key-enter"
diff --git a/data/keyboards/no.yaml b/data/keyboards/no.yaml
index 27ff21ff2b8745ea4102172196ff77dc015bdac2..44de74cc3ee6f04f3e1160901ca8756c6ba2e0af 100644
--- a/data/keyboards/no.yaml
+++ b/data/keyboards/no.yaml
@@ -39,7 +39,7 @@ buttons:
     BackSpace:
         outline: "altline"
         icon: "edit-clear-symbolic"
-        keysym: "BackSpace"
+        action: erase
     preferences:
         action: "show_prefs"
         outline: "altline"
diff --git a/data/keyboards/number.yaml b/data/keyboards/number.yaml
index f180a2623a0df031149abcd37459ab857836d4c0..8b42ebb2fa2bc97c975d1505153c0ba02087b71e 100644
--- a/data/keyboards/number.yaml
+++ b/data/keyboards/number.yaml
@@ -16,7 +16,7 @@ buttons:
     BackSpace:
         outline: "altline"
         icon: "edit-clear-symbolic"
-        keysym: "BackSpace"
+        action: erase
     space:
         outline: spaceline
         text: " "
diff --git a/data/keyboards/se.yaml b/data/keyboards/se.yaml
index 0a0a127fd359b99d70400fc35b973de4b372a7b2..b51f49adafb989ebd7678df2051b042155c3c648 100644
--- a/data/keyboards/se.yaml
+++ b/data/keyboards/se.yaml
@@ -39,7 +39,7 @@ buttons:
     BackSpace:
         outline: "altline"
         icon: "edit-clear-symbolic"
-        keysym: "BackSpace"
+        action: erase
     preferences:
         action: "show_prefs"
         outline: "altline"
diff --git a/data/keyboards/terminal.yaml b/data/keyboards/terminal.yaml
index 6fb4854466e8a099c0a12497de031bfca7dec718..0e232fa78db853c7f8066ec7986b488c816cdce8 100644
--- a/data/keyboards/terminal.yaml
+++ b/data/keyboards/terminal.yaml
@@ -45,7 +45,7 @@ buttons:
     BackSpace:
         outline: "altline"
         icon: "edit-clear-symbolic"
-        keysym: "BackSpace"
+        action: erase
     preferences:
         action: "show_prefs"
         outline: "special"
diff --git a/data/keyboards/us.yaml b/data/keyboards/us.yaml
index d150c48099eee8a949b3e1c7deaa8a35fd280b22..7a7f4dd6d7a414a0000d293c7847980476db8de2 100644
--- a/data/keyboards/us.yaml
+++ b/data/keyboards/us.yaml
@@ -39,9 +39,9 @@ buttons:
     BackSpace:
         outline: "altline"
         icon: "edit-clear-symbolic"
-        keysym: "BackSpace"
+        action: erase
     preferences:
-        action: "show_prefs"
+        action: show_prefs
         outline: "special"
         icon: "keyboard-mode-symbolic"
     show_numbers:
diff --git a/data/keyboards/us_wide.yaml b/data/keyboards/us_wide.yaml
index 654db8f881e5be7fb024661ea30633eb010cf4f7..cf349c00c01f0890e9d071a66cc5a33f88cf4915 100644
--- a/data/keyboards/us_wide.yaml
+++ b/data/keyboards/us_wide.yaml
@@ -39,7 +39,7 @@ buttons:
     BackSpace:
         outline: "altline"
         icon: "edit-clear-symbolic"
-        keysym: "BackSpace"
+        action: "erase"
     preferences:
         action: "show_prefs"
         outline: "special"
diff --git a/src/action.rs b/src/action.rs
index 97e32dc7456c32ae80e97550346fc21d83f823a8..98bdcd8355bf3852966dfded08d84d4cdeb973ea 100644
--- a/src/action.rs
+++ b/src/action.rs
@@ -31,10 +31,13 @@ pub enum Action {
     SetModifier(Modifier),
     /// Submit some text
     Submit {
-        /// Text to submit with input-method
+        /// Text to submit with input-method.
+        /// If None, then keys are to be submitted instead.
         text: Option<CString>,
         /// The key events this symbol submits when submitting text is not possible
         keys: Vec<KeySym>,
     },
+    /// Erase a position behind the cursor
+    Erase,
     ShowPreferences,
 }
diff --git a/src/data.rs b/src/data.rs
index 5b2c4c9d466d013cca0914d0968a19e813c04960..8484c7b41027ae3c4c7804312a3ee026cc9c2654 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -15,6 +15,7 @@ use std::vec::Vec;
 
 use xkbcommon::xkb;
 
+use ::action;
 use ::keyboard::{
     KeyState, PressType,
     generate_keymap, generate_keycodes, FormattingError
@@ -264,6 +265,9 @@ enum Action {
     SetView(String),
     #[serde(rename="show_prefs")]
     ShowPrefs,
+    /// Remove last character
+    #[serde(rename="erase")]
+    Erase,
 }
 
 #[derive(Debug, Clone, Deserialize, PartialEq)]
@@ -386,6 +390,10 @@ impl Layout {
                             )
                     }).collect()
                 },
+                action::Action::Erase => vec![
+                    *keymap.get("BackSpace")
+                        .expect(&format!("BackSpace missing from keymap")),
+                ],
                 _ => Vec::new(),
             };
             (
@@ -558,6 +566,7 @@ fn create_action<H: logging::Handler>(
         SubmitData::Action(
             Action::ShowPrefs
         ) => ::action::Action::ShowPreferences,
+        SubmitData::Action(Action::Erase) => action::Action::Erase,
         SubmitData::Keysym(keysym) => ::action::Action::Submit {
             text: None,
             keys: vec!(::action::KeySym(
@@ -589,7 +598,7 @@ fn create_action<H: logging::Handler>(
                     false => format!("U{:04X}", codepoint as u32),
                 })
             }).collect(),
-        }
+        },
     }
 }
 
diff --git a/src/imservice.c b/src/imservice.c
index 2a6184c8989b829a5cdc851717d032f7aed19af5..b6b04784e51e23e3603f65bc246e0a204171dc3e 100644
--- a/src/imservice.c
+++ b/src/imservice.c
@@ -49,6 +49,23 @@ void imservice_connect_listeners(struct zwp_input_method_v2 *im, struct imservic
     zwp_input_method_v2_add_listener(im, &input_method_listener, imservice);
 }
 
+void
+eek_input_method_commit_string(struct zwp_input_method_v2 *zwp_input_method_v2, const char *text)
+{
+    zwp_input_method_v2_commit_string(zwp_input_method_v2, text);
+}
+
+void
+eek_input_method_delete_surrounding_text(struct zwp_input_method_v2 *zwp_input_method_v2, uint32_t before_length, uint32_t after_length) {
+    zwp_input_method_v2_delete_surrounding_text(zwp_input_method_v2, before_length, after_length);
+};
+
+void
+eek_input_method_commit(struct zwp_input_method_v2 *zwp_input_method_v2, uint32_t serial)
+{
+    zwp_input_method_v2_commit(zwp_input_method_v2, serial);
+}
+
 /// Declared explicitly because _destroy is inline,
 /// making it unavailable in Rust
 void imservice_destroy_im(struct zwp_input_method_v2 *im) {
diff --git a/src/imservice.rs b/src/imservice.rs
index 4695defdd65f26e249ddd02f5f86d24ef137769c..a4546d94c28a57b877c1fcea7af29d2a64878672 100644
--- a/src/imservice.rs
+++ b/src/imservice.rs
@@ -1,3 +1,8 @@
+/*! Manages zwp_input_method_v2 protocol.
+ * 
+ * Library module.
+ */
+
 use std::boxed::Box;
 use std::ffi::CString;
 use std::fmt;
@@ -32,6 +37,9 @@ pub mod c {
         fn imservice_destroy_im(im: *mut c::InputMethod);
         #[allow(improper_ctypes)] // IMService will never be dereferenced in C
         pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService);
+        pub fn eek_input_method_commit_string(im: *mut InputMethod, text: *const c_char);
+        pub fn eek_input_method_delete_surrounding_text(im: *mut InputMethod, before: u32, after: u32);
+        pub fn eek_input_method_commit(im: *mut InputMethod, serial: u32);
         fn eekboard_context_service_set_hint_purpose(state: *const StateManager, hint: u32, purpose: u32);
         fn server_context_service_show_keyboard(imservice: *const UIManager);
         fn server_context_service_hide_keyboard(imservice: *const UIManager);
@@ -328,7 +336,7 @@ impl Default for IMProtocolState {
 
 pub struct IMService {
     /// Owned reference (still created and destroyed in C)
-    pub im: *const c::InputMethod,
+    pub im: *mut c::InputMethod,
     /// Unowned reference. Be careful, it's shared with C at large
     state_manager: *const c::StateManager,
     /// Unowned reference. Be careful, it's shared with C at large
@@ -340,6 +348,11 @@ pub struct IMService {
     serial: Wrapping<u32>,
 }
 
+pub enum SubmitError {
+    /// The input method had not been activated
+    NotActive,
+}
+
 impl IMService {
     pub fn new(
         im: *mut c::InputMethod,
@@ -364,4 +377,51 @@ impl IMService {
         }
         imservice
     }
+    
+    pub fn commit_string(&self, text: &CString) -> Result<(), SubmitError> {
+        match self.current.active {
+            true => {
+                unsafe {
+                    c::eek_input_method_commit_string(self.im, text.as_ptr())
+                }
+                Ok(())
+            },
+            false => Err(SubmitError::NotActive),
+        }
+    }
+
+    pub fn delete_surrounding_text(
+        &self,
+        before: u32, after: u32,
+    ) -> Result<(), SubmitError> {
+        match self.current.active {
+            true => {
+                unsafe {
+                    c::eek_input_method_delete_surrounding_text(
+                        self.im,
+                        before, after,
+                    )
+                }
+                Ok(())
+            },
+            false => Err(SubmitError::NotActive),
+        }
+    }
+
+    pub fn commit(&mut self) -> Result<(), SubmitError> {
+        match self.current.active {
+            true => {
+                unsafe {
+                    c::eek_input_method_commit(self.im, self.serial.0)
+                }
+                self.serial += Wrapping(1u32);
+                Ok(())
+            },
+            false => Err(SubmitError::NotActive),
+        }
+    }
+
+    pub fn is_active(&self) -> bool {
+        self.current.active
+    }
 }
diff --git a/src/keyboard.rs b/src/keyboard.rs
index bff1ac1618142b0ddbf514e1ea8562e43c32861b..46f4bbf923bef479c971972d9c4be068203f141c 100644
--- a/src/keyboard.rs
+++ b/src/keyboard.rs
@@ -1,14 +1,17 @@
 /*! State of the emulated keyboard and keys.
  * Regards the keyboard as if it was composed of switches. */
 
+use std::cell::RefCell;
 use std::collections::HashMap;
 use std::fmt;
 use std::io;
+use std::rc::Rc;
 use std::string::FromUtf8Error;
 
 use ::action::Action;
 use ::logging;
 
+// Traits
 use std::io::Write;
 use std::iter::{ FromIterator, IntoIterator };
 
@@ -20,6 +23,11 @@ pub enum PressType {
 
 pub type KeyCode = u32;
 
+/// When the submitted actions of keys need to be tracked,
+/// they need a stable, comparable ID
+#[derive(PartialEq)]
+pub struct KeyStateId(*const KeyState);
+
 #[derive(Debug, Clone)]
 pub struct KeyState {
     pub pressed: PressType,
@@ -49,6 +57,12 @@ impl KeyState {
             ..self
         }
     }
+
+    /// KeyStates instances are the unique identifiers of pressed keys,
+    /// and the actions submitted with them.
+    pub fn get_id(keystate: &Rc<RefCell<KeyState>>) -> KeyStateId {
+        KeyStateId(keystate.as_ptr() as *const KeyState)
+    }
 }
 
 /// Sorts an iterator by converting it to a Vector and back
@@ -66,9 +80,10 @@ fn sorted<'a, I: Iterator<Item=&'a str>>(
 pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
     key_names: C
 ) -> HashMap<String, u32> {
+    let special_keysyms = ["BackSpace", "Return"].iter().map(|&s| s);
     HashMap::from_iter(
         // sort to remove a source of indeterminism in keycode assignment
-        sorted(key_names.into_iter())
+        sorted(key_names.into_iter().chain(special_keysyms))
             .map(|name| String::from(name))
             .zip(9..)
     )
@@ -95,7 +110,10 @@ impl From<io::Error> for FormattingError {
     }
 }
 
-/// Generates a de-facto single level keymap. TODO: actually drop second level
+/// Generates a de-facto single level keymap.
+// TODO: don't rely on keys and their order,
+// but rather on what keysyms and keycodes are in use.
+// Iterating actions makes it hard to deduplicate keysyms.
 pub fn generate_keymap(
     keystates: &HashMap::<String, KeyState>
 ) -> Result<String, FormattingError> {
@@ -110,22 +128,40 @@ pub fn generate_keymap(
     )?;
     
     for (name, state) in keystates.iter() {
-        if let Action::Submit { text: _, keys } = &state.action {
-            if let 0 = keys.len() {
-                log_print!(
-                    logging::Level::Warning,
-                    "Key {} has no keysyms", name,
-                );
-            };
-            for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) {
+        match &state.action {
+            Action::Submit { text: _, keys } => {
+                if let 0 = keys.len() {
+                    log_print!(
+                        logging::Level::Warning,
+                        "Key {} has no keysyms", name,
+                    );
+                };
+                for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) {
+                    write!(
+                        buf,
+                        "
+        <{}> = {};",
+                        named_keysym.0,
+                        keycode,
+                    )?;
+                }
+            },
+            Action::Erase => {
+                let mut keycodes = state.keycodes.iter();
                 write!(
                     buf,
                     "
-        <{}> = {};",
-                    named_keysym.0,
-                    keycode,
+        <BackSpace> = {};",
+                    keycodes.next().expect("Erase key has no keycode"),
                 )?;
-            }
+                if let Some(_) = keycodes.next() {
+                    log_print!(
+                        logging::Level::Bug,
+                        "Erase key has multiple keycodes",
+                    );
+                }
+            },
+            _ => {},
         }
     }
     
@@ -137,7 +173,9 @@ pub fn generate_keymap(
     xkb_symbols \"squeekboard\" {{
 
         name[Group1] = \"Letters\";
-        name[Group2] = \"Numbers/Symbols\";"
+        name[Group2] = \"Numbers/Symbols\";
+        
+        key <BackSpace> {{ [ BackSpace ] }};"
     )?;
     
     for (name, state) in keystates.iter() {
diff --git a/src/layout.rs b/src/layout.rs
index 671e774a575e908d2126a41088e487dc97ee32c8..63068c0229ca6029f21246c95f6e63bcc71c7096 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -876,11 +876,7 @@ mod seat {
             );
         }
         let mut key = rckey.borrow_mut();
-        submission.virtual_keyboard.switch(
-            &key.keycodes,
-            PressType::Pressed,
-            time,
-        );
+        submission.handle_press(&key, KeyState::get_id(rckey), time);
         key.pressed = PressType::Pressed;
     }
 
@@ -906,13 +902,11 @@ mod seat {
 
         // process changes
         match action {
-            Action::Submit { text: _, keys: _ } => {
+            Action::Submit { text: _, keys: _ }
+                | Action::Erase
+            => {
                 unstick_locks(layout).apply();
-                submission.virtual_keyboard.switch(
-                    &key.keycodes,
-                    PressType::Released,
-                    time,
-                );
+                submission.handle_release(KeyState::get_id(rckey), time);
             },
             Action::SetView(view) => {
                 try_set_view(layout, view)
diff --git a/src/submission.rs b/src/submission.rs
index db7e6cc42749063c7ef86b4ea9a41eec1925679c..7d3e294997690a055873256de36ffd14dcc82f89 100644
--- a/src/submission.rs
+++ b/src/submission.rs
@@ -16,8 +16,12 @@
  * The text-input interface may be enabled and disabled at arbitrary times,
  * and those events SHOULD NOT cause any lost events.
  * */
-
+ 
+use ::action::Action;
+use ::imservice;
 use ::imservice::IMService;
+use ::keyboard::{ KeyCode, KeyState, KeyStateId, PressType };
+use ::logging;
 use ::vkeyboard::VirtualKeyboard;
 
 /// Gathers stuff defined in C or called by C
@@ -57,6 +61,7 @@ pub mod c {
             Submission {
                 imservice,
                 virtual_keyboard: VirtualKeyboard(vk),
+                pressed: Vec::new(),
             }
         ))
     }
@@ -92,9 +97,91 @@ pub mod c {
 #[derive(Clone, Copy)]
 pub struct Timestamp(pub u32);
 
+enum SubmittedAction {
+    /// A collection of keycodes that were pressed
+    VirtualKeyboard(Vec<KeyCode>),
+    IMService,
+}
+
 pub struct Submission {
-    // used by C callbacks internally, TODO: make use with virtual keyboard
-    #[allow(dead_code)]
     imservice: Option<Box<IMService>>,
-    pub virtual_keyboard: VirtualKeyboard,
+    virtual_keyboard: VirtualKeyboard,
+    pressed: Vec<(KeyStateId, SubmittedAction)>,
+}
+
+impl Submission {
+    /// Sends a submit text event if possible;
+    /// otherwise sends key press and makes a note of it
+    pub fn handle_press(
+        &mut self,
+        key: &KeyState, key_id: KeyStateId,
+        time: Timestamp,
+    ) {
+        match &key.action {
+            Action::Submit { text: _, keys: _ }
+                | Action::Erase
+            => (),
+            _ => {
+                log_print!(
+                    logging::Level::Bug,
+                    "Submitted key with action other than Submit or Erase",
+                );
+                return;
+            },
+        };
+
+        let was_committed_as_text = match (&mut self.imservice, &key.action) {
+            (Some(imservice), Action::Submit { text: Some(text), keys: _ }) => {
+                let submit_result = imservice.commit_string(text)
+                    .and_then(|_| imservice.commit());
+                match submit_result {
+                    Ok(()) => true,
+                    Err(imservice::SubmitError::NotActive) => false,
+                }
+            },
+            (Some(imservice), Action::Erase) => {
+                let submit_result = imservice.delete_surrounding_text(1, 0)
+                    .and_then(|_| imservice.commit());
+                match submit_result {
+                    Ok(()) => true,
+                    Err(imservice::SubmitError::NotActive) => false,
+                }
+            }
+            (_, _) => false,
+        };
+        
+        let submit_action = match was_committed_as_text {
+            true => SubmittedAction::IMService,
+            false => {
+                self.virtual_keyboard.switch(
+                    &key.keycodes,
+                    PressType::Pressed,
+                    time,
+                );
+                SubmittedAction::VirtualKeyboard(key.keycodes.clone())
+            },
+        };
+        
+        self.pressed.push((key_id, submit_action));
+    }
+    
+    pub fn handle_release(&mut self, key_id: KeyStateId, time: Timestamp) {
+        let index = self.pressed.iter().position(|(id, _)| *id == key_id);
+        if let Some(index) = index {
+            let (_id, action) = self.pressed.remove(index);
+            match action {
+                // string already sent, nothing to do
+                SubmittedAction::IMService => {},
+                // no matter if the imservice got activated,
+                // keys must be released
+                SubmittedAction::VirtualKeyboard(keycodes) => {
+                    self.virtual_keyboard.switch(
+                        &keycodes,
+                        PressType::Released,
+                        time,
+                    )
+                },
+            }
+        };
+    }
 }