oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git

converted keycodes to keysyms for international support, with 0 performance hit, fixed fullscreen behaviour to actually work as intended (toggle globally), fixed issue where dragging a window from monitor 1 to monitor 2, and then retiling it would tile it back to monitor 1, fixed bug where gentleman from discord was reporting he couldnt exit insert mode in vim, added support for increasing brightness and audio.

Commit
c15cb284f818d6897501ed2869ff789a975c1a06
Parent
4c5cbe7
Author
tonybtw <tonybtw@tonybtw.com>
Date
2025-11-03 08:24:37

Diff

diff --git a/default.nix b/default.nix
index 89832d6..0ba80d9 100644
--- a/default.nix
+++ b/default.nix
@@ -29,8 +29,8 @@ rustPlatform.buildRustPackage (finalAttrs: {
   ];
 
   postInstall = ''
-    install oxwm.desktop -Dt $out/share/xsessions
-    install -Dm644 oxwm.1 -t $out/share/man/man1
+    install resources/oxwm.desktop -Dt $out/share/xsessions
+    install -Dm644 resource/oxwm.1 -t $out/share/man/man1
   '';
 
   passthru.providedSessions = ["oxwm"];
diff --git a/justfile b/justfile
index 1fbc233..1f35402 100644
--- a/justfile
+++ b/justfile
@@ -19,17 +19,17 @@ test-clean:
 	pkill Xephyr || true
 	rm -rf ~/.config/oxwm
 	Xephyr -screen 1280x800 :1 & sleep 1
-	DISPLAY=:1 cargo run --release -- --config test-config.ron
+	DISPLAY=:1 cargo run --release -- --config resources/test-config.ron
 
 test:
 	pkill Xephyr || true
 	Xephyr -screen 1280x800 :1 & sleep 1
-	DISPLAY=:1 cargo run --release -- --config test-config.ron
+	DISPLAY=:1 cargo run --release -- --config resources/test-config.ron
 
 test-multimon:
 	pkill Xephyr || true
 	Xephyr +xinerama -screen 640x480 -screen 640x480 :1 & sleep 1
-	DISPLAY=:1 cargo run --release -- --config test-config.ron
+	DISPLAY=:1 cargo run --release -- --config resources/test-config.ron
 
 init:
     cargo run --release -- --init
diff --git a/PKGBUILD b/resources/PKGBUILD
similarity index 85%
rename from PKGBUILD
rename to resources/PKGBUILD
index 6742acd..1c3b655 100644
--- a/PKGBUILD
+++ b/resources/PKGBUILD
@@ -33,6 +33,6 @@ package() {
     cd $_pkgname
     install -Dm755 "target/release/$_pkgname" "$pkgdir/usr/bin/$_pkgname"
     install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
-    install -Dm644 oxwm.desktop "$pkgdir/usr/share/xsessions/oxwm.desktop"
-    install -Dm644 oxwm.1 "$pkgdir/usr/share/man/man1/oxwm.1"
+    install -Dm644 resources/oxwm.desktop "$pkgdir/usr/share/xsessions/oxwm.desktop"
+    install -Dm644 resources/oxwm.1 "$pkgdir/usr/share/man/man1/oxwm.1"
 }
diff --git a/oxwm.1 b/resources/oxwm.1
similarity index 100%
rename from oxwm.1
rename to resources/oxwm.1
diff --git a/oxwm.desktop b/resources/oxwm.desktop
similarity index 100%
rename from oxwm.desktop
rename to resources/oxwm.desktop
diff --git a/test-config.ron b/resources/test-config.ron
similarity index 100%
rename from test-config.ron
rename to resources/test-config.ron
diff --git a/src/config/mod.rs b/src/config/mod.rs
index d7aa2a4..8232b86 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -1,11 +1,12 @@
 use crate::bar::{BlockCommand, BlockConfig};
 use crate::errors::ConfigError;
 use crate::keyboard::handlers::{KeyBinding, KeyPress};
-use crate::keyboard::keycodes;
+use crate::keyboard::keysyms;
 use crate::keyboard::{Arg, KeyAction};
+use crate::keyboard::keysyms::Keysym;
 use serde::Deserialize;
 use std::collections::HashMap;
-use x11rb::protocol::xproto::{KeyButMask, Keycode};
+use x11rb::protocol::xproto::KeyButMask;
 
 #[derive(Debug, Deserialize)]
 pub enum ModKey {
@@ -76,172 +77,98 @@ pub enum KeyData {
     Comma,
     Period,
     Slash,
+    AudioRaiseVolume,
+    AudioLowerVolume,
+    AudioMute,
+    MonBrightnessUp,
+    MonBrightnessDown,
 }
 
 impl KeyData {
-    fn to_keycode(&self) -> Keycode {
+    fn to_keysym(&self) -> Keysym {
         match self {
-            KeyData::Return => keycodes::RETURN,
-            KeyData::Q => keycodes::Q,
-            KeyData::Escape => keycodes::ESCAPE,
-            KeyData::Space => keycodes::SPACE,
-            KeyData::Tab => keycodes::TAB,
-            KeyData::Backspace => keycodes::BACKSPACE,
-            KeyData::Delete => keycodes::DELETE,
-            KeyData::F1 => keycodes::F1,
-            KeyData::F2 => keycodes::F2,
-            KeyData::F3 => keycodes::F3,
-            KeyData::F4 => keycodes::F4,
-            KeyData::F5 => keycodes::F5,
-            KeyData::F6 => keycodes::F6,
-            KeyData::F7 => keycodes::F7,
-            KeyData::F8 => keycodes::F8,
-            KeyData::F9 => keycodes::F9,
-            KeyData::F10 => keycodes::F10,
-            KeyData::F11 => keycodes::F11,
-            KeyData::F12 => keycodes::F12,
-            KeyData::A => keycodes::A,
-            KeyData::B => keycodes::B,
-            KeyData::C => keycodes::C,
-            KeyData::D => keycodes::D,
-            KeyData::E => keycodes::E,
-            KeyData::F => keycodes::F,
-            KeyData::G => keycodes::G,
-            KeyData::H => keycodes::H,
-            KeyData::I => keycodes::I,
-            KeyData::J => keycodes::J,
-            KeyData::K => keycodes::K,
-            KeyData::L => keycodes::L,
-            KeyData::M => keycodes::M,
-            KeyData::N => keycodes::N,
-            KeyData::O => keycodes::O,
-            KeyData::P => keycodes::P,
-            KeyData::R => keycodes::R,
-            KeyData::S => keycodes::S,
-            KeyData::T => keycodes::T,
-            KeyData::U => keycodes::U,
-            KeyData::V => keycodes::V,
-            KeyData::W => keycodes::W,
-            KeyData::X => keycodes::X,
-            KeyData::Y => keycodes::Y,
-            KeyData::Z => keycodes::Z,
-            KeyData::Key0 => keycodes::KEY_0,
-            KeyData::Key1 => keycodes::KEY_1,
-            KeyData::Key2 => keycodes::KEY_2,
-            KeyData::Key3 => keycodes::KEY_3,
-            KeyData::Key4 => keycodes::KEY_4,
-            KeyData::Key5 => keycodes::KEY_5,
-            KeyData::Key6 => keycodes::KEY_6,
-            KeyData::Key7 => keycodes::KEY_7,
-            KeyData::Key8 => keycodes::KEY_8,
-            KeyData::Key9 => keycodes::KEY_9,
-            KeyData::Left => keycodes::LEFT,
-            KeyData::Right => keycodes::RIGHT,
-            KeyData::Up => keycodes::UP,
-            KeyData::Down => keycodes::DOWN,
-            KeyData::Home => keycodes::HOME,
-            KeyData::End => keycodes::END,
-            KeyData::PageUp => keycodes::PAGE_UP,
-            KeyData::PageDown => keycodes::PAGE_DOWN,
-            KeyData::Insert => keycodes::INSERT,
-            KeyData::Minus => keycodes::MINUS,
-            KeyData::Equal => keycodes::EQUAL,
-            KeyData::BracketLeft => keycodes::LEFT_BRACKET,
-            KeyData::BracketRight => keycodes::RIGHT_BRACKET,
-            KeyData::Semicolon => keycodes::SEMICOLON,
-            KeyData::Apostrophe => keycodes::APOSTROPHE,
-            KeyData::Grave => keycodes::GRAVE,
-            KeyData::Backslash => keycodes::BACKSLASH,
-            KeyData::Comma => keycodes::COMMA,
-            KeyData::Period => keycodes::PERIOD,
-            KeyData::Slash => keycodes::SLASH,
+            KeyData::Return => keysyms::XK_RETURN,
+            KeyData::Q => keysyms::XK_Q,
+            KeyData::Escape => keysyms::XK_ESCAPE,
+            KeyData::Space => keysyms::XK_SPACE,
+            KeyData::Tab => keysyms::XK_TAB,
+            KeyData::Backspace => keysyms::XK_BACKSPACE,
+            KeyData::Delete => keysyms::XK_DELETE,
+            KeyData::F1 => keysyms::XK_F1,
+            KeyData::F2 => keysyms::XK_F2,
+            KeyData::F3 => keysyms::XK_F3,
+            KeyData::F4 => keysyms::XK_F4,
+            KeyData::F5 => keysyms::XK_F5,
+            KeyData::F6 => keysyms::XK_F6,
+            KeyData::F7 => keysyms::XK_F7,
+            KeyData::F8 => keysyms::XK_F8,
+            KeyData::F9 => keysyms::XK_F9,
+            KeyData::F10 => keysyms::XK_F10,
+            KeyData::F11 => keysyms::XK_F11,
+            KeyData::F12 => keysyms::XK_F12,
+            KeyData::A => keysyms::XK_A,
+            KeyData::B => keysyms::XK_B,
+            KeyData::C => keysyms::XK_C,
+            KeyData::D => keysyms::XK_D,
+            KeyData::E => keysyms::XK_E,
+            KeyData::F => keysyms::XK_F,
+            KeyData::G => keysyms::XK_G,
+            KeyData::H => keysyms::XK_H,
+            KeyData::I => keysyms::XK_I,
+            KeyData::J => keysyms::XK_J,
+            KeyData::K => keysyms::XK_K,
+            KeyData::L => keysyms::XK_L,
+            KeyData::M => keysyms::XK_M,
+            KeyData::N => keysyms::XK_N,
+            KeyData::O => keysyms::XK_O,
+            KeyData::P => keysyms::XK_P,
+            KeyData::R => keysyms::XK_R,
+            KeyData::S => keysyms::XK_S,
+            KeyData::T => keysyms::XK_T,
+            KeyData::U => keysyms::XK_U,
+            KeyData::V => keysyms::XK_V,
+            KeyData::W => keysyms::XK_W,
+            KeyData::X => keysyms::XK_X,
+            KeyData::Y => keysyms::XK_Y,
+            KeyData::Z => keysyms::XK_Z,
+            KeyData::Key0 => keysyms::XK_0,
+            KeyData::Key1 => keysyms::XK_1,
+            KeyData::Key2 => keysyms::XK_2,
+            KeyData::Key3 => keysyms::XK_3,
+            KeyData::Key4 => keysyms::XK_4,
+            KeyData::Key5 => keysyms::XK_5,
+            KeyData::Key6 => keysyms::XK_6,
+            KeyData::Key7 => keysyms::XK_7,
+            KeyData::Key8 => keysyms::XK_8,
+            KeyData::Key9 => keysyms::XK_9,
+            KeyData::Left => keysyms::XK_LEFT,
+            KeyData::Right => keysyms::XK_RIGHT,
+            KeyData::Up => keysyms::XK_UP,
+            KeyData::Down => keysyms::XK_DOWN,
+            KeyData::Home => keysyms::XK_HOME,
+            KeyData::End => keysyms::XK_END,
+            KeyData::PageUp => keysyms::XK_PAGE_UP,
+            KeyData::PageDown => keysyms::XK_PAGE_DOWN,
+            KeyData::Insert => keysyms::XK_INSERT,
+            KeyData::Minus => keysyms::XK_MINUS,
+            KeyData::Equal => keysyms::XK_EQUAL,
+            KeyData::BracketLeft => keysyms::XK_LEFT_BRACKET,
+            KeyData::BracketRight => keysyms::XK_RIGHT_BRACKET,
+            KeyData::Semicolon => keysyms::XK_SEMICOLON,
+            KeyData::Apostrophe => keysyms::XK_APOSTROPHE,
+            KeyData::Grave => keysyms::XK_GRAVE,
+            KeyData::Backslash => keysyms::XK_BACKSLASH,
+            KeyData::Comma => keysyms::XK_COMMA,
+            KeyData::Period => keysyms::XK_PERIOD,
+            KeyData::Slash => keysyms::XK_SLASH,
+            KeyData::AudioRaiseVolume => keysyms::XF86_AUDIO_RAISE_VOLUME,
+            KeyData::AudioLowerVolume => keysyms::XF86_AUDIO_LOWER_VOLUME,
+            KeyData::AudioMute => keysyms::XF86_AUDIO_MUTE,
+            KeyData::MonBrightnessUp => keysyms::XF86_MON_BRIGHTNESS_UP,
+            KeyData::MonBrightnessDown => keysyms::XF86_MON_BRIGHTNESS_DOWN,
         }
     }
 
-    // Adding this reverse lookup for now so converting to KeySyms is no longer
-    // an 'unsafe' call to a C FFI (Sorry Bugsy...)
-    // This will set us up in the future to move away from hardcoded keycodes,
-    // and directly convert keysyms to keydata at runtime for international
-    // keyboard support.
-    pub fn from_keycode(keycode: Keycode) -> Option<&'static str> {
-        match keycode {
-            keycodes::RETURN => Some("Return"),
-            keycodes::Q => Some("q"),
-            keycodes::ESCAPE => Some("Esc"),
-            keycodes::SPACE => Some("Space"),
-            keycodes::TAB => Some("Tab"),
-            keycodes::BACKSPACE => Some("Backspace"),
-            keycodes::DELETE => Some("Del"),
-            keycodes::F1 => Some("F1"),
-            keycodes::F2 => Some("F2"),
-            keycodes::F3 => Some("F3"),
-            keycodes::F4 => Some("F4"),
-            keycodes::F5 => Some("F5"),
-            keycodes::F6 => Some("F6"),
-            keycodes::F7 => Some("F7"),
-            keycodes::F8 => Some("F8"),
-            keycodes::F9 => Some("F9"),
-            keycodes::F10 => Some("F10"),
-            keycodes::F11 => Some("F11"),
-            keycodes::F12 => Some("F12"),
-            keycodes::A => Some("a"),
-            keycodes::B => Some("b"),
-            keycodes::C => Some("c"),
-            keycodes::D => Some("d"),
-            keycodes::E => Some("e"),
-            keycodes::F => Some("f"),
-            keycodes::G => Some("g"),
-            keycodes::H => Some("h"),
-            keycodes::I => Some("i"),
-            keycodes::J => Some("j"),
-            keycodes::K => Some("k"),
-            keycodes::L => Some("l"),
-            keycodes::M => Some("m"),
-            keycodes::N => Some("n"),
-            keycodes::O => Some("o"),
-            keycodes::P => Some("p"),
-            keycodes::R => Some("r"),
-            keycodes::S => Some("s"),
-            keycodes::T => Some("t"),
-            keycodes::U => Some("u"),
-            keycodes::V => Some("v"),
-            keycodes::W => Some("w"),
-            keycodes::X => Some("x"),
-            keycodes::Y => Some("y"),
-            keycodes::Z => Some("z"),
-            keycodes::KEY_0 => Some("0"),
-            keycodes::KEY_1 => Some("1"),
-            keycodes::KEY_2 => Some("2"),
-            keycodes::KEY_3 => Some("3"),
-            keycodes::KEY_4 => Some("4"),
-            keycodes::KEY_5 => Some("5"),
-            keycodes::KEY_6 => Some("6"),
-            keycodes::KEY_7 => Some("7"),
-            keycodes::KEY_8 => Some("8"),
-            keycodes::KEY_9 => Some("9"),
-            keycodes::LEFT => Some("Left"),
-            keycodes::RIGHT => Some("Right"),
-            keycodes::UP => Some("Up"),
-            keycodes::DOWN => Some("Down"),
-            keycodes::HOME => Some("Home"),
-            keycodes::END => Some("End"),
-            keycodes::PAGE_UP => Some("PgUp"),
-            keycodes::PAGE_DOWN => Some("PgDn"),
-            keycodes::INSERT => Some("Ins"),
-            keycodes::MINUS => Some("-"),
-            keycodes::EQUAL => Some("="),
-            keycodes::LEFT_BRACKET => Some("["),
-            keycodes::RIGHT_BRACKET => Some("]"),
-            keycodes::SEMICOLON => Some(";"),
-            keycodes::APOSTROPHE => Some("'"),
-            keycodes::GRAVE => Some("`"),
-            keycodes::BACKSLASH => Some("\\"),
-            keycodes::COMMA => Some(","),
-            keycodes::PERIOD => Some("."),
-            keycodes::SLASH => Some("/"),
-            _ => None,
-        }
-    }
 }
 
 fn preprocess_variables(input: &str) -> Result<String, ConfigError> {
@@ -412,7 +339,7 @@ fn config_data_to_config(data: ConfigData) -> Result<crate::Config, ConfigError>
 
                     KeyPress {
                         modifiers,
-                        key: kp.key.to_keycode(),
+                        keysym: kp.key.to_keysym(),
                     }
                 })
                 .collect()
@@ -425,7 +352,7 @@ fn config_data_to_config(data: ConfigData) -> Result<crate::Config, ConfigError>
                         _ => m.to_keybut_mask(),
                     })
                     .collect(),
-                key: key.to_keycode(),
+                keysym: key.to_keysym(),
             }]
         } else {
             return Err(ConfigError::ValidationError(
diff --git a/src/keyboard/handlers.rs b/src/keyboard/handlers.rs
index 7e6ff2b..226303d 100644
--- a/src/keyboard/handlers.rs
+++ b/src/keyboard/handlers.rs
@@ -1,6 +1,5 @@
-use std::collections::HashSet;
-use std::io;
-use std::io::ErrorKind;
+use std::collections::HashMap;
+use std::io::{ErrorKind, Result};
 use std::process::Command;
 
 use serde::Deserialize;
@@ -8,7 +7,7 @@ use x11rb::connection::Connection;
 use x11rb::protocol::xproto::*;
 
 use crate::errors::X11Error;
-use crate::keyboard::keycodes;
+use crate::keyboard::keysyms::{self, Keysym};
 
 #[derive(Debug, Copy, Clone, Deserialize)]
 pub enum KeyAction {
@@ -48,7 +47,7 @@ impl Arg {
 #[derive(Clone, Debug)]
 pub struct KeyPress {
     pub(crate) modifiers: Vec<KeyButMask>,
-    pub(crate) key: Keycode,
+    pub(crate) keysym: Keysym,
 }
 
 #[derive(Clone)]
@@ -63,9 +62,14 @@ impl KeyBinding {
         Self { keys, func, arg }
     }
 
-    pub fn single_key(modifiers: Vec<KeyButMask>, key: Keycode, func: KeyAction, arg: Arg) -> Self {
+    pub fn single_key(
+        modifiers: Vec<KeyButMask>,
+        keysym: Keysym,
+        func: KeyAction,
+        arg: Arg,
+    ) -> Self {
         Self {
-            keys: vec![KeyPress { modifiers, key }],
+            keys: vec![KeyPress { modifiers, keysym }],
             func,
             arg,
         }
@@ -96,11 +100,48 @@ pub fn modifiers_to_mask(modifiers: &[KeyButMask]) -> u16 {
         .fold(0u16, |acc, &modifier| acc | u16::from(modifier))
 }
 
+fn build_keysym_maps(
+    connection: &impl Connection,
+) -> std::result::Result<(HashMap<Keysym, Vec<Keycode>>, HashMap<Keycode, Keysym>), X11Error> {
+    let setup = connection.setup();
+    let min_keycode = setup.min_keycode;
+    let max_keycode = setup.max_keycode;
+
+    let keyboard_mapping = connection
+        .get_keyboard_mapping(min_keycode, max_keycode - min_keycode + 1)?
+        .reply()?;
+
+    let mut keysym_to_keycode: HashMap<Keysym, Vec<Keycode>> = HashMap::new();
+    let mut keycode_to_keysym: HashMap<Keycode, Keysym> = HashMap::new();
+    let keysyms_per_keycode = keyboard_mapping.keysyms_per_keycode;
+
+    for keycode in min_keycode..=max_keycode {
+        let index = (keycode - min_keycode) as usize * keysyms_per_keycode as usize;
+
+        for i in 0..keysyms_per_keycode as usize {
+            if let Some(&keysym) = keyboard_mapping.keysyms.get(index + i) {
+                if keysym != 0 {
+                    keysym_to_keycode
+                        .entry(keysym)
+                        .or_insert_with(Vec::new)
+                        .push(keycode);
+                    keycode_to_keysym.entry(keycode).or_insert(keysym);
+                }
+            }
+        }
+    }
+
+    Ok((keysym_to_keycode, keycode_to_keysym))
+}
+
 pub fn setup_keybinds(
     connection: &impl Connection,
     root: Window,
     keybindings: &[KeyBinding],
-) -> Result<(), X11Error> {
+) -> std::result::Result<(), X11Error> {
+    use std::collections::HashSet;
+
+    let (keysym_to_keycode, _) = build_keysym_maps(connection)?;
     let mut grabbed_keys: HashSet<(u16, Keycode)> = HashSet::new();
 
     for keybinding in keybindings {
@@ -110,29 +151,25 @@ pub fn setup_keybinds(
 
         let first_key = &keybinding.keys[0];
         let modifier_mask = modifiers_to_mask(&first_key.modifiers);
-        let key_tuple = (modifier_mask, first_key.key);
-
-        if grabbed_keys.insert(key_tuple) {
-            connection.grab_key(
-                false,
-                root,
-                modifier_mask.into(),
-                first_key.key,
-                GrabMode::ASYNC,
-                GrabMode::ASYNC,
-            )?;
+
+        if let Some(keycodes) = keysym_to_keycode.get(&first_key.keysym) {
+            if let Some(&keycode) = keycodes.first() {
+                let key_tuple = (modifier_mask, keycode);
+
+                if grabbed_keys.insert(key_tuple) {
+                    connection.grab_key(
+                        false,
+                        root,
+                        modifier_mask.into(),
+                        keycode,
+                        GrabMode::ASYNC,
+                        GrabMode::ASYNC,
+                    )?;
+                }
+            }
         }
     }
 
-    connection.grab_key(
-        false,
-        root,
-        ModMask::from(0u16),
-        keycodes::ESCAPE,
-        GrabMode::ASYNC,
-        GrabMode::ASYNC,
-    )?;
-
     Ok(())
 }
 
@@ -140,24 +177,32 @@ pub fn handle_key_press(
     event: KeyPressEvent,
     keybindings: &[KeyBinding],
     keychord_state: &KeychordState,
-) -> KeychordResult {
-    if event.detail == keycodes::ESCAPE {
-        return match keychord_state {
+    connection: &impl Connection,
+) -> std::result::Result<KeychordResult, X11Error> {
+    let (_, keycode_to_keysym) = build_keysym_maps(connection)?;
+    let event_keysym = keycode_to_keysym.get(&event.detail).copied().unwrap_or(0);
+
+    if event_keysym == keysyms::XK_ESCAPE {
+        return Ok(match keychord_state {
             KeychordState::InProgress { .. } => KeychordResult::Cancelled,
             KeychordState::Idle => KeychordResult::None,
-        };
+        });
     }
 
-    match keychord_state {
-        KeychordState::Idle => handle_first_key(event, keybindings),
+    Ok(match keychord_state {
+        KeychordState::Idle => handle_first_key(event, event_keysym, keybindings),
         KeychordState::InProgress {
             candidates,
             keys_pressed,
-        } => handle_next_key(event, keybindings, candidates, *keys_pressed),
-    }
+        } => handle_next_key(event, event_keysym, keybindings, candidates, *keys_pressed),
+    })
 }
 
-fn handle_first_key(event: KeyPressEvent, keybindings: &[KeyBinding]) -> KeychordResult {
+fn handle_first_key(
+    event: KeyPressEvent,
+    event_keysym: Keysym,
+    keybindings: &[KeyBinding],
+) -> KeychordResult {
     let mut candidates = Vec::new();
 
     for (keybinding_index, keybinding) in keybindings.iter().enumerate() {
@@ -168,7 +213,7 @@ fn handle_first_key(event: KeyPressEvent, keybindings: &[KeyBinding]) -> Keychor
         let first_key = &keybinding.keys[0];
         let modifier_mask = modifiers_to_mask(&first_key.modifiers);
 
-        if event.detail == first_key.key && event.state == modifier_mask.into() {
+        if event_keysym == first_key.keysym && event.state == modifier_mask.into() {
             if keybinding.keys.len() == 1 {
                 return KeychordResult::Completed(keybinding.func, keybinding.arg.clone());
             } else {
@@ -186,6 +231,7 @@ fn handle_first_key(event: KeyPressEvent, keybindings: &[KeyBinding]) -> Keychor
 
 fn handle_next_key(
     event: KeyPressEvent,
+    event_keysym: Keysym,
     keybindings: &[KeyBinding],
     candidates: &[usize],
     keys_pressed: usize,
@@ -209,7 +255,7 @@ fn handle_next_key(
             (event_state & required_mask) == required_mask
         };
 
-        if event.detail == next_key.key && modifiers_match {
+        if event_keysym == next_key.keysym && modifiers_match {
             if keys_pressed + 1 == keybinding.keys.len() {
                 return KeychordResult::Completed(keybinding.func, keybinding.arg.clone());
             } else {
@@ -225,7 +271,7 @@ fn handle_next_key(
     }
 }
 
-pub fn handle_spawn_action(action: KeyAction, arg: &Arg) -> io::Result<()> {
+pub fn handle_spawn_action(action: KeyAction, arg: &Arg) -> Result<()> {
     if let KeyAction::Spawn = action {
         match arg {
             Arg::Str(command) => match Command::new(command.as_str()).spawn() {
diff --git a/src/keyboard/keycodes.rs b/src/keyboard/keycodes.rs
deleted file mode 100644
index 307b57c..0000000
--- a/src/keyboard/keycodes.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-#![allow(dead_code)]
-// Allowing dead code here because its just a file for potential keybinds
-pub const RETURN: u8 = 36;
-pub const Q: u8 = 24;
-pub const ESCAPE: u8 = 9;
-pub const SPACE: u8 = 65;
-pub const TAB: u8 = 23;
-pub const BACKSPACE: u8 = 22;
-pub const DELETE: u8 = 119;
-
-// Function Keys
-pub const F1: u8 = 67;
-pub const F2: u8 = 68;
-pub const F3: u8 = 69;
-pub const F4: u8 = 70;
-pub const F5: u8 = 71;
-pub const F6: u8 = 72;
-pub const F7: u8 = 73;
-pub const F8: u8 = 74;
-pub const F9: u8 = 75;
-pub const F10: u8 = 76;
-pub const F11: u8 = 95;
-pub const F12: u8 = 96;
-
-// Letters
-pub const A: u8 = 38;
-pub const B: u8 = 56;
-pub const C: u8 = 54;
-pub const D: u8 = 40;
-pub const E: u8 = 26;
-pub const F: u8 = 41;
-pub const G: u8 = 42;
-pub const H: u8 = 43;
-pub const I: u8 = 31;
-pub const J: u8 = 44;
-pub const K: u8 = 45;
-pub const L: u8 = 46;
-pub const M: u8 = 58;
-pub const N: u8 = 57;
-pub const O: u8 = 32;
-pub const P: u8 = 33;
-pub const R: u8 = 27;
-pub const S: u8 = 39;
-pub const T: u8 = 28;
-pub const U: u8 = 30;
-pub const V: u8 = 55;
-pub const W: u8 = 25;
-pub const X: u8 = 53;
-pub const Y: u8 = 29;
-pub const Z: u8 = 52;
-
-// Numbers
-pub const KEY_0: u8 = 19;
-pub const KEY_1: u8 = 10;
-pub const KEY_2: u8 = 11;
-pub const KEY_3: u8 = 12;
-pub const KEY_4: u8 = 13;
-pub const KEY_5: u8 = 14;
-pub const KEY_6: u8 = 15;
-pub const KEY_7: u8 = 16;
-pub const KEY_8: u8 = 17;
-pub const KEY_9: u8 = 18;
-
-// Arrows
-pub const LEFT: u8 = 113;
-pub const RIGHT: u8 = 114;
-pub const UP: u8 = 111;
-pub const DOWN: u8 = 116;
-pub const HOME: u8 = 110;
-pub const END: u8 = 115;
-pub const PAGE_UP: u8 = 112;
-pub const PAGE_DOWN: u8 = 117;
-pub const INSERT: u8 = 118;
-
-// Symbols
-pub const MINUS: u8 = 20;
-pub const EQUAL: u8 = 21;
-pub const LEFT_BRACKET: u8 = 34;
-pub const RIGHT_BRACKET: u8 = 35;
-pub const SEMICOLON: u8 = 47;
-pub const APOSTROPHE: u8 = 48;
-pub const GRAVE: u8 = 49;
-pub const BACKSLASH: u8 = 51;
-pub const COMMA: u8 = 59;
-pub const PERIOD: u8 = 60;
-pub const SLASH: u8 = 61;
diff --git a/src/keyboard/keysyms.rs b/src/keyboard/keysyms.rs
new file mode 100644
index 0000000..42e1330
--- /dev/null
+++ b/src/keyboard/keysyms.rs
@@ -0,0 +1,84 @@
+#![allow(dead_code)]
+
+pub type Keysym = u32;
+pub const XK_ESCAPE: Keysym = 0xff1b;
+pub const XK_RETURN: Keysym = 0xff0d;
+pub const XK_SPACE: Keysym = 0x0020;
+pub const XK_TAB: Keysym = 0xff09;
+pub const XK_BACKSPACE: Keysym = 0xff08;
+pub const XK_DELETE: Keysym = 0xffff;
+pub const XK_F1: Keysym = 0xffbe;
+pub const XK_F2: Keysym = 0xffbf;
+pub const XK_F3: Keysym = 0xffc0;
+pub const XK_F4: Keysym = 0xffc1;
+pub const XK_F5: Keysym = 0xffc2;
+pub const XK_F6: Keysym = 0xffc3;
+pub const XK_F7: Keysym = 0xffc4;
+pub const XK_F8: Keysym = 0xffc5;
+pub const XK_F9: Keysym = 0xffc6;
+pub const XK_F10: Keysym = 0xffc7;
+pub const XK_F11: Keysym = 0xffc8;
+pub const XK_F12: Keysym = 0xffc9;
+pub const XK_A: Keysym = 0x0061;
+pub const XK_B: Keysym = 0x0062;
+pub const XK_C: Keysym = 0x0063;
+pub const XK_D: Keysym = 0x0064;
+pub const XK_E: Keysym = 0x0065;
+pub const XK_F: Keysym = 0x0066;
+pub const XK_G: Keysym = 0x0067;
+pub const XK_H: Keysym = 0x0068;
+pub const XK_I: Keysym = 0x0069;
+pub const XK_J: Keysym = 0x006a;
+pub const XK_K: Keysym = 0x006b;
+pub const XK_L: Keysym = 0x006c;
+pub const XK_M: Keysym = 0x006d;
+pub const XK_N: Keysym = 0x006e;
+pub const XK_O: Keysym = 0x006f;
+pub const XK_P: Keysym = 0x0070;
+pub const XK_Q: Keysym = 0x0071;
+pub const XK_R: Keysym = 0x0072;
+pub const XK_S: Keysym = 0x0073;
+pub const XK_T: Keysym = 0x0074;
+pub const XK_U: Keysym = 0x0075;
+pub const XK_V: Keysym = 0x0076;
+pub const XK_W: Keysym = 0x0077;
+pub const XK_X: Keysym = 0x0078;
+pub const XK_Y: Keysym = 0x0079;
+pub const XK_Z: Keysym = 0x007a;
+pub const XK_0: Keysym = 0x0030;
+pub const XK_1: Keysym = 0x0031;
+pub const XK_2: Keysym = 0x0032;
+pub const XK_3: Keysym = 0x0033;
+pub const XK_4: Keysym = 0x0034;
+pub const XK_5: Keysym = 0x0035;
+pub const XK_6: Keysym = 0x0036;
+pub const XK_7: Keysym = 0x0037;
+pub const XK_8: Keysym = 0x0038;
+pub const XK_9: Keysym = 0x0039;
+pub const XK_LEFT: Keysym = 0xff51;
+pub const XK_UP: Keysym = 0xff52;
+pub const XK_RIGHT: Keysym = 0xff53;
+pub const XK_DOWN: Keysym = 0xff54;
+pub const XK_HOME: Keysym = 0xff50;
+pub const XK_END: Keysym = 0xff57;
+pub const XK_PAGE_UP: Keysym = 0xff55;
+pub const XK_PAGE_DOWN: Keysym = 0xff56;
+pub const XK_INSERT: Keysym = 0xff63;
+pub const XK_MINUS: Keysym = 0x002d;
+pub const XK_EQUAL: Keysym = 0x003d;
+pub const XK_LEFT_BRACKET: Keysym = 0x005b;
+pub const XK_RIGHT_BRACKET: Keysym = 0x005d;
+pub const XK_SEMICOLON: Keysym = 0x003b;
+pub const XK_APOSTROPHE: Keysym = 0x0027;
+pub const XK_GRAVE: Keysym = 0x0060;
+pub const XK_BACKSLASH: Keysym = 0x005c;
+pub const XK_COMMA: Keysym = 0x002c;
+pub const XK_PERIOD: Keysym = 0x002e;
+pub const XK_SLASH: Keysym = 0x002f;
+pub const XK_PRINT: Keysym = 0xff61;
+
+pub const XF86_AUDIO_RAISE_VOLUME: Keysym = 0x1008ff13;
+pub const XF86_AUDIO_LOWER_VOLUME: Keysym = 0x1008ff11;
+pub const XF86_AUDIO_MUTE: Keysym = 0x1008ff12;
+pub const XF86_MON_BRIGHTNESS_UP: Keysym = 0x1008ff02;
+pub const XF86_MON_BRIGHTNESS_DOWN: Keysym = 0x1008ff03;
diff --git a/src/keyboard/mod.rs b/src/keyboard/mod.rs
index 7e76b4a..5ba8b15 100644
--- a/src/keyboard/mod.rs
+++ b/src/keyboard/mod.rs
@@ -1,5 +1,5 @@
 pub mod handlers;
-pub mod keycodes;
+pub mod keysyms;
 
 pub use handlers::{Arg, KeyAction, handle_key_press, setup_keybinds};
-pub use keycodes::*;
+pub use keysyms::*;
diff --git a/src/lib.rs b/src/lib.rs
index 75da74b..8f55ebf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,7 +10,7 @@ pub mod prelude {
     pub use crate::ColorScheme;
     pub use crate::LayoutSymbolOverride;
     pub use crate::bar::{BlockCommand, BlockConfig};
-    pub use crate::keyboard::{Arg, KeyAction, handlers::KeyBinding, keycodes};
+    pub use crate::keyboard::{Arg, KeyAction, handlers::KeyBinding, keysyms};
     pub use x11rb::protocol::xproto::KeyButMask;
 }
 
@@ -67,7 +67,7 @@ pub struct ColorScheme {
 impl Default for Config {
     fn default() -> Self {
         use crate::keyboard::handlers::KeyBinding;
-        use crate::keyboard::{Arg, KeyAction, keycodes};
+        use crate::keyboard::{Arg, KeyAction, keysyms};
         use x11rb::protocol::xproto::KeyButMask;
 
         const MODKEY: KeyButMask = KeyButMask::MOD4;
@@ -95,13 +95,13 @@ impl Default for Config {
             keybindings: vec![
                 KeyBinding::single_key(
                     vec![MODKEY],
-                    keycodes::RETURN,
+                    keysyms::XK_RETURN,
                     KeyAction::Spawn,
                     Arg::Str(TERMINAL.to_string()),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY],
-                    keycodes::D,
+                    keysyms::XK_D,
                     KeyAction::Spawn,
                     Arg::Array(vec![
                         "sh".to_string(),
@@ -109,169 +109,169 @@ impl Default for Config {
                         "dmenu_run -l 10".to_string(),
                     ]),
                 ),
-                KeyBinding::single_key(vec![MODKEY], keycodes::Q, KeyAction::KillClient, Arg::None),
-                KeyBinding::single_key(vec![MODKEY], keycodes::N, KeyAction::CycleLayout, Arg::None),
+                KeyBinding::single_key(vec![MODKEY], keysyms::XK_Q, KeyAction::KillClient, Arg::None),
+                KeyBinding::single_key(vec![MODKEY], keysyms::XK_N, KeyAction::CycleLayout, Arg::None),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::F,
+                    keysyms::XK_F,
                     KeyAction::ToggleFullScreen,
                     Arg::None,
                 ),
-                KeyBinding::single_key(vec![MODKEY], keycodes::A, KeyAction::ToggleGaps, Arg::None),
-                KeyBinding::single_key(vec![MODKEY, SHIFT], keycodes::Q, KeyAction::Quit, Arg::None),
+                KeyBinding::single_key(vec![MODKEY], keysyms::XK_A, KeyAction::ToggleGaps, Arg::None),
+                KeyBinding::single_key(vec![MODKEY, SHIFT], keysyms::XK_Q, KeyAction::Quit, Arg::None),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::R,
+                    keysyms::XK_R,
                     KeyAction::Restart,
                     Arg::None,
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY],
-                    keycodes::F,
+                    keysyms::XK_F,
                     KeyAction::ToggleFloating,
                     Arg::None,
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY],
-                    keycodes::J,
+                    keysyms::XK_J,
                     KeyAction::FocusStack,
                     Arg::Int(-1),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY],
-                    keycodes::K,
+                    keysyms::XK_K,
                     KeyAction::FocusStack,
                     Arg::Int(1),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::K,
+                    keysyms::XK_K,
                     KeyAction::ExchangeClient,
                     Arg::Int(0), // UP
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::J,
+                    keysyms::XK_J,
                     KeyAction::ExchangeClient,
                     Arg::Int(1), // DOWN
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::H,
+                    keysyms::XK_H,
                     KeyAction::ExchangeClient,
                     Arg::Int(2), // LEFT
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::L,
+                    keysyms::XK_L,
                     KeyAction::ExchangeClient,
                     Arg::Int(3), // RIGHT
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY],
-                    keycodes::KEY_1,
+                    keysyms::XK_1,
                     KeyAction::ViewTag,
                     Arg::Int(0),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY],
-                    keycodes::KEY_2,
+                    keysyms::XK_2,
                     KeyAction::ViewTag,
                     Arg::Int(1),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY],
-                    keycodes::KEY_3,
+                    keysyms::XK_3,
                     KeyAction::ViewTag,
                     Arg::Int(2),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY],
-                    keycodes::KEY_4,
+                    keysyms::XK_4,
                     KeyAction::ViewTag,
                     Arg::Int(3),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY],
-                    keycodes::KEY_5,
+                    keysyms::XK_5,
                     KeyAction::ViewTag,
                     Arg::Int(4),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY],
-                    keycodes::KEY_6,
+                    keysyms::XK_6,
                     KeyAction::ViewTag,
                     Arg::Int(5),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY],
-                    keycodes::KEY_7,
+                    keysyms::XK_7,
                     KeyAction::ViewTag,
                     Arg::Int(6),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY],
-                    keycodes::KEY_8,
+                    keysyms::XK_8,
                     KeyAction::ViewTag,
                     Arg::Int(7),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY],
-                    keycodes::KEY_9,
+                    keysyms::XK_9,
                     KeyAction::ViewTag,
                     Arg::Int(8),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::KEY_1,
+                    keysyms::XK_1,
                     KeyAction::MoveToTag,
                     Arg::Int(0),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::KEY_2,
+                    keysyms::XK_2,
                     KeyAction::MoveToTag,
                     Arg::Int(1),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::KEY_3,
+                    keysyms::XK_3,
                     KeyAction::MoveToTag,
                     Arg::Int(2),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::KEY_4,
+                    keysyms::XK_4,
                     KeyAction::MoveToTag,
                     Arg::Int(3),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::KEY_5,
+                    keysyms::XK_5,
                     KeyAction::MoveToTag,
                     Arg::Int(4),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::KEY_6,
+                    keysyms::XK_6,
                     KeyAction::MoveToTag,
                     Arg::Int(5),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::KEY_7,
+                    keysyms::XK_7,
                     KeyAction::MoveToTag,
                     Arg::Int(6),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::KEY_8,
+                    keysyms::XK_8,
                     KeyAction::MoveToTag,
                     Arg::Int(7),
                 ),
                 KeyBinding::single_key(
                     vec![MODKEY, SHIFT],
-                    keycodes::KEY_9,
+                    keysyms::XK_9,
                     KeyAction::MoveToTag,
                     Arg::Int(8),
                 ),
diff --git a/src/window_manager.rs b/src/window_manager.rs
index 0980289..aa2a488 100644
--- a/src/window_manager.rs
+++ b/src/window_manager.rs
@@ -59,7 +59,7 @@ pub struct WindowManager {
     window_monitor: std::collections::HashMap<Window, usize>,
     window_geometries: std::collections::HashMap<Window, (i16, i16, u16, u16)>,
     gaps_enabled: bool,
-    fullscreen_window: Option<Window>,
+    fullscreen_enabled: bool,
     floating_windows: HashSet<Window>,
     bars: Vec<Bar>,
     monitors: Vec<Monitor>,
@@ -168,7 +168,7 @@ impl WindowManager {
             window_monitor: std::collections::HashMap::new(),
             window_geometries: std::collections::HashMap::new(),
             gaps_enabled,
-            fullscreen_window: None,
+            fullscreen_enabled: false,
             floating_windows: HashSet::new(),
             bars,
             monitors,
@@ -388,6 +388,17 @@ impl WindowManager {
         {
             if self.floating_windows.contains(&focused) {
                 self.floating_windows.remove(&focused);
+
+                let selected_tags = self
+                    .monitors
+                    .get(self.selected_monitor)
+                    .map(|m| m.selected_tags)
+                    .unwrap_or(tag_mask(0));
+
+                self.window_tags.insert(focused, selected_tags);
+                self.window_monitor.insert(focused, self.selected_monitor);
+                let _ = self.save_client_tag(focused, selected_tags);
+
                 self.apply_layout()?;
             } else {
                 let float_width = (self.screen.width_in_pixels / 2) as u32;
@@ -428,10 +439,6 @@ impl WindowManager {
             None => return Ok(()),
         };
 
-        if self.fullscreen_window == Some(focused) {
-            return Ok(());
-        }
-
         if !self.floating_windows.contains(&focused) {
             let float_width = (self.screen.width_in_pixels / 2) as u32;
             let float_height = (self.screen.height_in_pixels / 2) as u32;
@@ -690,7 +697,7 @@ impl WindowManager {
             None => return Ok(()),
         };
 
-        if self.fullscreen_window == Some(focused) || self.floating_windows.contains(&focused) {
+        if self.floating_windows.contains(&focused) {
             return Ok(());
         }
 
@@ -750,44 +757,19 @@ impl WindowManager {
     }
 
     fn toggle_fullscreen(&mut self) -> WmResult<()> {
-        if let Some(focused) = self
-            .monitors
-            .get(self.selected_monitor)
-            .and_then(|m| m.focused_window)
-        {
-            if self.fullscreen_window == Some(focused) {
-                self.fullscreen_window = None;
-
-                for bar in &self.bars {
-                    self.connection.map_window(bar.window())?;
-                }
+        self.fullscreen_enabled = !self.fullscreen_enabled;
 
-                self.apply_layout()?;
-            } else {
-                self.fullscreen_window = Some(focused);
-
-                if let Some(bar) = self.bars.get(self.selected_monitor) {
-                    self.connection.unmap_window(bar.window())?;
-                }
-
-                let monitor = &self.monitors[self.selected_monitor];
-                let screen_width = monitor.width;
-                let screen_height = monitor.height;
-
-                self.connection.configure_window(
-                    focused,
-                    &ConfigureWindowAux::new()
-                        .x(monitor.x)
-                        .y(monitor.y)
-                        .width(screen_width)
-                        .height(screen_height)
-                        .border_width(0)
-                        .stack_mode(StackMode::ABOVE),
-                )?;
-
-                self.connection.flush()?;
+        if self.fullscreen_enabled {
+            for bar in &self.bars {
+                self.connection.unmap_window(bar.window())?;
+            }
+        } else {
+            for bar in &self.bars {
+                self.connection.map_window(bar.window())?;
             }
         }
+
+        self.apply_layout()?;
         Ok(())
     }
 
@@ -822,7 +804,7 @@ impl WindowManager {
                         indicator.push('+');
                     }
 
-                    indicator.push_str(&self.format_keycode(key_press.key));
+                    indicator.push_str(&self.format_keysym(key_press.keysym));
                 }
 
                 indicator.push('-');
@@ -841,10 +823,58 @@ impl WindowManager {
         }
     }
 
-    fn format_keycode(&self, keycode: Keycode) -> String {
-        crate::config::KeyData::from_keycode(keycode)
-            .unwrap_or("?")
-            .to_string()
+    fn format_keysym(&self, keysym: keyboard::keysyms::Keysym) -> String {
+        use keyboard::keysyms::*;
+
+        match keysym {
+            XK_RETURN => "Return",
+            XK_ESCAPE => "Esc",
+            XK_SPACE => "Space",
+            XK_TAB => "Tab",
+            XK_BACKSPACE => "Backspace",
+            XK_DELETE => "Del",
+            XK_F1 => "F1",
+            XK_F2 => "F2",
+            XK_F3 => "F3",
+            XK_F4 => "F4",
+            XK_F5 => "F5",
+            XK_F6 => "F6",
+            XK_F7 => "F7",
+            XK_F8 => "F8",
+            XK_F9 => "F9",
+            XK_F10 => "F10",
+            XK_F11 => "F11",
+            XK_F12 => "F12",
+            XK_A..=XK_Z | XK_0..=XK_9 => {
+                return char::from_u32(keysym).unwrap_or('?').to_string();
+            }
+            XK_LEFT => "Left",
+            XK_RIGHT => "Right",
+            XK_UP => "Up",
+            XK_DOWN => "Down",
+            XK_HOME => "Home",
+            XK_END => "End",
+            XK_PAGE_UP => "PgUp",
+            XK_PAGE_DOWN => "PgDn",
+            XK_INSERT => "Ins",
+            XK_MINUS => "-",
+            XK_EQUAL => "=",
+            XK_LEFT_BRACKET => "[",
+            XK_RIGHT_BRACKET => "]",
+            XK_SEMICOLON => ";",
+            XK_APOSTROPHE => "'",
+            XK_GRAVE => "`",
+            XK_BACKSLASH => "\\",
+            XK_COMMA => ",",
+            XK_PERIOD => ".",
+            XK_SLASH => "/",
+            XF86_AUDIO_RAISE_VOLUME => "Vol+",
+            XF86_AUDIO_LOWER_VOLUME => "Vol-",
+            XF86_AUDIO_MUTE => "Mute",
+            XF86_MON_BRIGHTNESS_UP => "Bri+",
+            XF86_MON_BRIGHTNESS_DOWN => "Bri-",
+            _ => "?",
+        }.to_string()
     }
 
     fn update_bar(&mut self) -> WmResult<()> {
@@ -1070,13 +1100,6 @@ impl WindowManager {
             return Ok(());
         }
 
-        if self.fullscreen_window.is_some() {
-            self.fullscreen_window = None;
-            for bar in &self.bars {
-                self.connection.map_window(bar.window())?;
-            }
-        }
-
         if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
             monitor.selected_tags = tag_mask(tag_index);
         }
@@ -1175,6 +1198,32 @@ impl WindowManager {
     }
 
     fn grab_next_keys(&self, candidates: &[usize], keys_pressed: usize) -> WmResult<()> {
+        use std::collections::HashMap;
+        use x11rb::protocol::xproto::Keycode;
+
+        let setup = self.connection.setup();
+        let min_keycode = setup.min_keycode;
+        let max_keycode = setup.max_keycode;
+
+        let keyboard_mapping = self.connection.get_keyboard_mapping(
+            min_keycode,
+            max_keycode - min_keycode + 1,
+        )?.reply()?;
+
+        let mut keysym_to_keycode: HashMap<keyboard::keysyms::Keysym, Vec<Keycode>> = HashMap::new();
+        let keysyms_per_keycode = keyboard_mapping.keysyms_per_keycode;
+
+        for keycode in min_keycode..=max_keycode {
+            let index = (keycode - min_keycode) as usize * keysyms_per_keycode as usize;
+            for i in 0..keysyms_per_keycode as usize {
+                if let Some(&keysym) = keyboard_mapping.keysyms.get(index + i) {
+                    if keysym != 0 {
+                        keysym_to_keycode.entry(keysym).or_insert_with(Vec::new).push(keycode);
+                    }
+                }
+            }
+        }
+
         let mut grabbed_keys: HashSet<(u16, Keycode)> = HashSet::new();
 
         for &candidate_index in candidates {
@@ -1182,20 +1231,39 @@ impl WindowManager {
             if keys_pressed < binding.keys.len() {
                 let next_key = &binding.keys[keys_pressed];
                 let modifier_mask = keyboard::handlers::modifiers_to_mask(&next_key.modifiers);
-                let key_tuple = (modifier_mask, next_key.key);
-
-                if grabbed_keys.insert(key_tuple) {
-                    self.connection.grab_key(
-                        false,
-                        self.root,
-                        modifier_mask.into(),
-                        next_key.key,
-                        GrabMode::ASYNC,
-                        GrabMode::ASYNC,
-                    )?;
+
+                if let Some(keycodes) = keysym_to_keycode.get(&next_key.keysym) {
+                    if let Some(&keycode) = keycodes.first() {
+                        let key_tuple = (modifier_mask, keycode);
+
+                        if grabbed_keys.insert(key_tuple) {
+                            self.connection.grab_key(
+                                false,
+                                self.root,
+                                modifier_mask.into(),
+                                keycode,
+                                GrabMode::ASYNC,
+                                GrabMode::ASYNC,
+                            )?;
+                        }
+                    }
                 }
             }
         }
+
+        if let Some(keycodes) = keysym_to_keycode.get(&keyboard::keysyms::XK_ESCAPE) {
+            if let Some(&keycode) = keycodes.first() {
+                self.connection.grab_key(
+                    false,
+                    self.root,
+                    ModMask::from(0u16),
+                    keycode,
+                    GrabMode::ASYNC,
+                    GrabMode::ASYNC,
+                )?;
+            }
+        }
+
         self.connection.flush()?;
         Ok(())
     }
@@ -1459,7 +1527,8 @@ impl WindowManager {
                     event,
                     &self.config.keybindings,
                     &self.keychord_state,
-                );
+                    &self.connection,
+                )?;
 
                 match result {
                     keyboard::handlers::KeychordResult::Completed(action, arg) => {
@@ -1533,17 +1602,13 @@ impl WindowManager {
     }
 
     fn apply_layout(&self) -> WmResult<()> {
-        if self.fullscreen_window.is_some() {
-            return Ok(());
-        }
-
         if self.layout.name() == LayoutType::Normie.as_str() {
             return Ok(());
         }
 
-        let border_width = self.config.border_width;
+        let border_width = if self.fullscreen_enabled { 0 } else { self.config.border_width };
 
-        let gaps = if self.gaps_enabled {
+        let gaps = if self.gaps_enabled && !self.fullscreen_enabled {
             GapConfig {
                 inner_horizontal: self.config.gap_inner_horizontal,
                 inner_vertical: self.config.gap_inner_vertical,
@@ -1580,11 +1645,14 @@ impl WindowManager {
                 .copied()
                 .collect();
 
-            let bar_height = self
-                .bars
-                .get(monitor_index)
-                .map(|b| b.height() as u32)
-                .unwrap_or(0);
+            let bar_height = if self.fullscreen_enabled {
+                0
+            } else {
+                self.bars
+                    .get(monitor_index)
+                    .map(|b| b.height() as u32)
+                    .unwrap_or(0)
+            };
             let usable_height = monitor.height.saturating_sub(bar_height);
 
             let geometries = self
@@ -1627,13 +1695,6 @@ impl WindowManager {
         self.window_geometries.remove(&window);
         self.floating_windows.remove(&window);
 
-        if self.fullscreen_window == Some(window) {
-            self.fullscreen_window = None;
-            for bar in &self.bars {
-                self.connection.map_window(bar.window())?;
-            }
-        }
-
         if self.windows.len() < initial_count {
             let focused = self
                 .monitors