use anyhow::{Context, Result};
use std::os::unix::io::{AsFd, OwnedFd};
use tempfile::NamedTempFile;
use wayland_client::protocol::{wl_keyboard, wl_registry, wl_seat};
use wayland_client::{Connection, Dispatch, QueueHandle};
pub mod virtual_keyboard {
use wayland_client;
use wayland_client::protocol::*;
pub mod __interfaces {
use wayland_client::protocol::__interfaces::*;
wayland_scanner::generate_interfaces!("wtype/protocol/virtual-keyboard-unstable-v1.xml");
}
use self::__interfaces::*;
wayland_scanner::generate_client_code!("wtype/protocol/virtual-keyboard-unstable-v1.xml");
}
use self::virtual_keyboard::{
zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1,
zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1,
};
pub struct WaylandState {
seat: Option<wl_seat::WlSeat>,
manager: Option<ZwpVirtualKeyboardManagerV1>,
keyboard: Option<ZwpVirtualKeyboardV1>,
pub mod_state: u32,
}
impl Default for WaylandState {
fn default() -> Self {
Self::new()
}
}
impl WaylandState {
pub fn new() -> Self {
Self {
seat: None, manager: None, keyboard: None, mod_state: 0, }
}
pub fn create_keyboard(&mut self, qh: &QueueHandle<Self>) -> Result<()> {
let seat = self.seat.as_ref().context("No seat available")?;
let manager = self
.manager
.as_ref()
.context("No virtual keyboard manager available")?;
self.keyboard = Some(manager.create_virtual_keyboard(seat, qh, ()));
Ok(())
}
pub fn upload_keymap(&self, keymap_data: &str) -> Result<()> {
let keyboard = self.keyboard.as_ref().context("No virtual keyboard")?;
let mut temp_file = NamedTempFile::new().context("Failed to create temporary file")?;
std::io::Write::write_all(&mut temp_file, keymap_data.as_bytes())
.context("Failed to write keymap data")?;
std::io::Write::write_all(&mut temp_file, b"\0")
.context("Failed to write null terminator")?;
let fd = temp_file.into_file();
let owned_fd = OwnedFd::from(fd);
keyboard.keymap(
wl_keyboard::KeymapFormat::XkbV1.into(), owned_fd.as_fd(), keymap_data.len() as u32 + 1, );
Ok(())
}
pub fn press_key(&self, keycode: u32) -> Result<()> {
let keyboard = self.keyboard.as_ref().context("No virtual keyboard")?;
keyboard.key(
0, keycode, wl_keyboard::KeyState::Pressed.into() );
Ok(())
}
pub fn release_key(&self, keycode: u32) -> Result<()> {
let keyboard = self.keyboard.as_ref().context("No virtual keyboard")?;
keyboard.key(
0, keycode, wl_keyboard::KeyState::Released.into() );
Ok(())
}
pub fn set_modifiers(&mut self, mods: u32) -> Result<()> {
let keyboard = self.keyboard.as_ref().context("No virtual keyboard")?;
self.mod_state = mods;
let depressed = mods & !2;
let locked = if mods & 2 != 0 { 2 } else { 0 };
keyboard.modifiers(
depressed, 0, locked, 0 );
Ok(())
}
}
impl Dispatch<wl_registry::WlRegistry, ()> for WaylandState {
fn event(
state: &mut Self,
registry: &wl_registry::WlRegistry,
event: wl_registry::Event,
_: &(),
_: &Connection,
qh: &QueueHandle<Self>,
) {
if let wl_registry::Event::Global {
name,
interface,
version,
} = event
{
match interface.as_str() {
"wl_seat" => {
let seat = registry.bind::<wl_seat::WlSeat, _, _>(
name, std::cmp::min(version, 7), qh, (), );
state.seat = Some(seat);
}
"zwp_virtual_keyboard_manager_v1" => {
let manager = registry.bind::<ZwpVirtualKeyboardManagerV1, _, _>(
name, 1, qh, (), );
state.manager = Some(manager);
}
_ => {} }
}
}
}
impl Dispatch<wl_seat::WlSeat, ()> for WaylandState {
fn event(
_state: &mut Self,
_seat: &wl_seat::WlSeat,
_event: wl_seat::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
}
}
impl Dispatch<ZwpVirtualKeyboardManagerV1, ()> for WaylandState {
fn event(
_state: &mut Self,
_manager: &ZwpVirtualKeyboardManagerV1,
_event: virtual_keyboard::zwp_virtual_keyboard_manager_v1::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
}
}
impl Dispatch<ZwpVirtualKeyboardV1, ()> for WaylandState {
fn event(
_state: &mut Self,
_keyboard: &ZwpVirtualKeyboardV1,
_event: virtual_keyboard::zwp_virtual_keyboard_v1::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
}
}
pub fn connect_wayland() -> Result<(Connection, WaylandState)> {
let conn = Connection::connect_to_env().context("Failed to connect to Wayland display")?;
let mut state = WaylandState::new();
let display = conn.display();
let mut event_queue = conn.new_event_queue();
let qh = event_queue.handle();
let _registry = display.get_registry(&qh, ());
event_queue
.roundtrip(&mut state)
.context("Failed to get globals")?;
if state.seat.is_none() {
anyhow::bail!("No seat found - compositor may not support input devices");
}
if state.manager.is_none() {
anyhow::bail!("Compositor does not support the virtual keyboard protocol (zwp_virtual_keyboard_unstable_v1)");
}
state.create_keyboard(&qh)?;
Ok((conn, state))
}