use crate::backend::KeyState;
use crate::client::Client;
use crate::clientmem::ClientMem;
use crate::clientmem::ClientMemError;
use crate::ifs::wl_seat::WlSeatGlobal;
use crate::ifs::wl_seat::wl_keyboard;
use crate::ifs::wl_seat::wl_keyboard::WlKeyboard;
use crate::ifs::wl_surface::WlSurface;
use crate::kbvm::KbvmError;
use crate::keyboard::KeyboardState;
use crate::leaks::Tracker;
use crate::object::Version;
use crate::wire::ZwpVirtualKeyboardV1Id;
use crate::wire::zwp_virtual_keyboard_v1::*;
use jay_proc::Object;
use std::cell::RefCell;
use std::rc::Rc;
use thiserror::Error;
#[derive(Object)]
pub struct ZwpVirtualKeyboardV1 {
pub id: ZwpVirtualKeyboardV1Id,
pub client: Rc<Client>,
pub seat: Rc<WlSeatGlobal>,
pub tracker: Tracker<Self>,
pub version: Version,
pub kb_state: Rc<RefCell<KeyboardState>>,
}
impl ZwpVirtualKeyboardV1 {
fn for_each_kb<F>(&self, mut f: F)
where
F: FnMut(u64, &WlSurface, &Rc<WlKeyboard>),
{
let Some(surface) = self.seat.keyboard_node.get().node_into_surface() else {
return;
};
let serial = surface.client.next_serial();
self.seat.surface_kb_event(Version::ALL, &surface, |kb| {
f(serial, &surface, kb);
});
}
}
impl ZwpVirtualKeyboardV1RequestHandler for ZwpVirtualKeyboardV1 {
type Error = ZwpVirtualKeyboardV1Error;
fn keymap(&self, req: Keymap, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if req.format != wl_keyboard::XKB_V1 {
return Err(ZwpVirtualKeyboardV1Error::UnsupportedFormat(req.format));
}
if req.size == 0 {
return Err(ZwpVirtualKeyboardV1Error::InvalidKeymap);
}
const MAX_SIZE: u32 = 1024 * 1024;
if req.size > MAX_SIZE {
return Err(ZwpVirtualKeyboardV1Error::OversizedKeymap);
}
let size = req.size as usize - 1;
let client_mem = ClientMem::new_private(&req.fd, size, true, Some(&self.client), None)
.map(Rc::new)
.map_err(ZwpVirtualKeyboardV1Error::MapKeymap)?;
let mut map = vec![];
client_mem
.offset(0, size)
.read(&mut map)
.map_err(ZwpVirtualKeyboardV1Error::ReadKeymap)?;
let map = self
.client
.state
.kb_ctx
.parse_keymap(&map, None)
.map_err(ZwpVirtualKeyboardV1Error::ParseKeymap)?;
*self.kb_state.borrow_mut() = KeyboardState {
id: self.client.state.keyboard_state_ids.next(),
map: map.clone(),
pressed_keys: Default::default(),
mods: Default::default(),
leds: Default::default(),
leds_changed: Default::default(),
};
Ok(())
}
fn key(&self, req: Key, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let kb_state = &mut *self.kb_state.borrow_mut();
let contains = kb_state.pressed_keys.contains(&req.key);
let (state, valid) = match req.state {
wl_keyboard::RELEASED => (KeyState::Released, contains),
wl_keyboard::PRESSED => (KeyState::Pressed, !contains),
_ => return Err(ZwpVirtualKeyboardV1Error::UnknownState(req.state)),
};
if valid {
self.for_each_kb(|serial, surface, kb| {
kb.on_key(serial, req.time, req.key, state, surface.id, kb_state);
});
match req.state {
wl_keyboard::RELEASED => kb_state.pressed_keys.remove(&req.key),
_ => kb_state.pressed_keys.insert(req.key),
};
self.seat.latest_kb_state_id.set(kb_state.id);
self.seat.latest_kb_state.set(self.kb_state.clone());
}
Ok(())
}
fn modifiers(&self, req: Modifiers, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let kb_state = &mut *self.kb_state.borrow_mut();
kb_state.mods.mods_pressed.0 = req.mods_depressed;
kb_state.mods.mods_latched.0 = req.mods_latched;
kb_state.mods.mods_locked.0 = req.mods_locked;
kb_state.mods.group_locked.0 = req.group;
kb_state.mods.update_effective();
kb_state.update_leds();
self.for_each_kb(|serial, surface, kb| {
kb.on_mods_changed(serial, surface.id, &kb_state);
});
self.seat.latest_kb_state_id.set(kb_state.id);
self.seat.latest_kb_state.set(self.kb_state.clone());
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.client.remove_obj(self);
Ok(())
}
}
#[derive(Debug, Error)]
pub enum ZwpVirtualKeyboardV1Error {
#[error("Unknown key state {0}")]
UnknownState(u32),
#[error("Unsupported keymap format {0}")]
UnsupportedFormat(u32),
#[error("Keymap is invalid")]
InvalidKeymap,
#[error("Keymap is too large")]
OversizedKeymap,
#[error("Could not map the keymap")]
MapKeymap(#[source] ClientMemError),
#[error("Could not read the keymap")]
ReadKeymap(#[source] ClientMemError),
#[error("Could not parse the keymap")]
ParseKeymap(#[source] KbvmError),
}