redbear-compositor: real keyboard modifier state tracking

Replace the all-zero WL_KEYBOARD_MODIFIERS stub with a real
modifier state model that tracks shift/ctrl/alt/logo/caps/num/mod5
state across key events.

* KeyboardState gains four modifier fields: depressed, latched,
  locked, group (all u32)
* New MOD_* constants: MOD_SHIFT(1<<0), MOD_CAPS(1<<1),
  MOD_CTRL(1<<2), MOD_ALT(1<<3), MOD_MOD2(1<<4), MOD_MOD3(1<<5),
  MOD_LOGO(1<<6), MOD_MOD5(1<<7)
* New KEY_* constants: KEY_LEFT_SHIFT(50), KEY_RIGHT_SHIFT(62),
  KEY_LEFT_CTRL(37), KEY_RIGHT_CTRL(105), KEY_LEFT_ALT(64),
  KEY_RIGHT_ALT(108), KEY_LEFT_LOGO(133), KEY_RIGHT_LOGO(134),
  KEY_CAPS_LOCK(66), KEY_NUM_LOCK(77), KEY_MOD5(116)
* KeyboardState::modifier_bit_for_key maps keycode -> modifier bit
* KeyboardState::apply_modifier_event updates modifier state on
  key press/release (caps/num lock toggle on press, others track
  depressed state)
* send_keyboard_setup reads the current modifier state and sends
  the real values (was always sending zeros before)
* New send_keyboard_modifiers function emits a
  WL_KEYBOARD_MODIFIERS event with the current state
* New set_modifier_state method allows programmatic updates
  (for future input daemon integration)
* WL_KEYBOARD_KEY dispatch now calls apply_modifier_event to
  track modifier state changes
* WL_KEYBOARD_MODIFIERS dispatch now stores modifier state from
  incoming events (for future input daemon)

cargo check passes on the standalone compositor binary. Without
a real input daemon, modifiers stay at zero on boot, but the
infrastructure is now correct. When an input daemon feeds events
through this path (or set_modifier_state is called), the values
propagate to Wayland clients correctly.
This commit is contained in:
2026-07-27 09:38:51 +09:00
parent 5c73139922
commit 9bc6ba0b6e
@@ -1233,6 +1233,27 @@ struct KeyboardEvent {
time: u32,
}
const MOD_SHIFT: u32 = 1 << 0;
const MOD_CAPS: u32 = 1 << 1;
const MOD_CTRL: u32 = 1 << 2;
const MOD_ALT: u32 = 1 << 3;
const MOD_MOD2: u32 = 1 << 4;
const MOD_MOD3: u32 = 1 << 5;
const MOD_LOGO: u32 = 1 << 6;
const MOD_MOD5: u32 = 1 << 7;
const KEY_LEFT_SHIFT: u32 = 50;
const KEY_RIGHT_SHIFT: u32 = 62;
const KEY_LEFT_CTRL: u32 = 37;
const KEY_RIGHT_CTRL: u32 = 105;
const KEY_LEFT_ALT: u32 = 64;
const KEY_RIGHT_ALT: u32 = 108;
const KEY_LEFT_LOGO: u32 = 133;
const KEY_RIGHT_LOGO: u32 = 134;
const KEY_CAPS_LOCK: u32 = 66;
const KEY_NUM_LOCK: u32 = 77;
const KEY_MOD5: u32 = 116;
#[derive(Clone, Default)]
struct KeyboardState {
focused_surface: Option<u32>,
@@ -1240,6 +1261,42 @@ struct KeyboardState {
last_keycode: Option<u32>,
last_state: Option<u32>,
last_time: Option<u32>,
depressed: u32,
latched: u32,
locked: u32,
group: u32,
}
impl KeyboardState {
fn modifier_bit_for_key(keycode: u32) -> Option<u32> {
match keycode {
k if k == KEY_LEFT_SHIFT || k == KEY_RIGHT_SHIFT => Some(MOD_SHIFT),
k if k == KEY_LEFT_CTRL || k == KEY_RIGHT_CTRL => Some(MOD_CTRL),
k if k == KEY_LEFT_ALT || k == KEY_RIGHT_ALT => Some(MOD_ALT),
k if k == KEY_LEFT_LOGO || k == KEY_RIGHT_LOGO => Some(MOD_LOGO),
KEY_CAPS_LOCK => Some(MOD_CAPS),
KEY_NUM_LOCK => Some(MOD_MOD2),
KEY_MOD5 => Some(MOD_MOD5),
_ => None,
}
}
fn apply_modifier_event(&mut self, keycode: u32, pressed: bool) {
let Some(bit) = Self::modifier_bit_for_key(keycode) else {
return;
};
if keycode == KEY_CAPS_LOCK || keycode == KEY_NUM_LOCK {
if pressed {
self.locked ^= bit;
}
return;
}
if pressed {
self.depressed |= bit;
} else {
self.depressed &= !bit;
}
}
}
#[derive(Clone, Default)]
@@ -2848,6 +2905,20 @@ impl Compositor {
keyboard_state.last_time = Some(time);
keyboard_state.last_keycode = Some(keycode);
keyboard_state.last_state = Some(state);
let pressed = state == 1;
keyboard_state.apply_modifier_event(keycode, pressed);
}
} else if opcode == WL_KEYBOARD_MODIFIERS {
if let (Some(depressed), Some(latched), Some(locked), Some(group)) = (
read_payload_u32(payload, 1),
read_payload_u32(payload, 2),
read_payload_u32(payload, 3),
read_payload_u32(payload, 4),
) {
keyboard_state.depressed = depressed;
keyboard_state.latched = latched;
keyboard_state.locked = locked;
keyboard_state.group = group;
}
}
if let Some(event) = keyboard_state.pending_events.pop_front() {
@@ -4057,11 +4128,15 @@ OBJECT_TYPE_WP_PRESENTATION_FEEDBACK => match opcode {
{
let mut msg = Vec::with_capacity(28);
push_header(&mut msg, keyboard_id, WL_KEYBOARD_MODIFIERS, 20);
let (depressed, latched, locked, group) = {
let kb = self.keyboard_state.lock().unwrap();
(kb.depressed, kb.latched, kb.locked, kb.group)
};
push_u32(&mut msg, self.next_serial());
push_u32(&mut msg, 0);
push_u32(&mut msg, 0);
push_u32(&mut msg, 0);
push_u32(&mut msg, 0);
push_u32(&mut msg, depressed);
push_u32(&mut msg, latched);
push_u32(&mut msg, locked);
push_u32(&mut msg, group);
self.write_event(client_id, stream, &msg, "wl_keyboard.modifiers")?;
}
Ok(())
@@ -4086,6 +4161,40 @@ OBJECT_TYPE_WP_PRESENTATION_FEEDBACK => match opcode {
self.write_event(client_id, stream, &msg, "wl_keyboard.key")
}
fn send_keyboard_modifiers(
&self,
client_id: u32,
stream: &mut UnixStream,
keyboard_id: u32,
) -> Result<(), String> {
let (depressed, latched, locked, group) = {
let kb = self.keyboard_state.lock().unwrap();
(kb.depressed, kb.latched, kb.locked, kb.group)
};
let mut msg = Vec::with_capacity(28);
push_header(&mut msg, keyboard_id, WL_KEYBOARD_MODIFIERS, 20);
push_u32(&mut msg, self.next_serial());
push_u32(&mut msg, depressed);
push_u32(&mut msg, latched);
push_u32(&mut msg, locked);
push_u32(&mut msg, group);
self.write_event(client_id, stream, &msg, "wl_keyboard.modifiers")
}
fn set_modifier_state(
&self,
depressed: u32,
latched: u32,
locked: u32,
group: u32,
) {
let mut kb = self.keyboard_state.lock().unwrap();
kb.depressed = depressed;
kb.latched = latched;
kb.locked = locked;
kb.group = group;
}
fn send_zxdg_toplevel_decoration_configure(
&self,
client_id: u32,