rewrite in rust

This commit is contained in:
Richard Acayan 2024-07-18 20:32:55 -04:00
parent d43f61ec76
commit 4a6b261be0
51 changed files with 3104 additions and 9319 deletions

191
src/wayland/buffer.rs Normal file
View file

@ -0,0 +1,191 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Copyright (c) 2024, Richard Acayan. All rights reserved.
*/
extern crate libc;
extern crate memmap2;
use core::imgref::ImgRef;
use core::imgref::ImgRefMut;
use core::rgb::alt::BGRA;
use core::rgb::FromSlice;
use self::memmap2::MmapMut;
use std::ffi::CString;
use std::fs::File;
use std::io::Error;
use std::io::ErrorKind;
use std::iter;
use std::os::fd::FromRawFd;
use std::os::fd::AsFd;
use std::process;
use wayland::wayland_client::protocol::wl_buffer::WlBuffer;
use wayland::wayland_client::protocol::wl_shm_pool::WlShmPool;
use wayland::wayland_client::protocol::wl_shm;
use wayland::wayland_client::protocol::wl_surface::WlSurface;
use wayland::wayland_client::Dispatch;
use wayland::wayland_client::QueueHandle;
pub struct Buffer<T> {
file: File,
map: MmapMut,
id: u32,
capacity: i32,
width: i32,
height: i32,
queue: QueueHandle<T>,
pool: WlShmPool,
buf: WlBuffer,
used: bool,
resizing: bool,
}
impl<T: Dispatch<WlShmPool, u32>
+ Dispatch<WlBuffer, u32>
+ 'static> Buffer<T> {
pub fn new(queue: QueueHandle<T>, shm: &wl_shm::WlShm, id: u32) -> Result<Self, Error>
{
// Integers are normally not represented as text using NUL.
let name = match CString::new(format!("/ufkbd_pid{}_{}", process::id(), id)) {
Ok(name) => name,
Err(e) => return Err(Error::new(ErrorKind::InvalidInput, e)),
};
let file = unsafe {
let fd = libc::shm_open(name.as_ptr(),
libc::O_RDWR | libc::O_CREAT | libc::O_EXCL,
libc::S_IRUSR | libc::S_IWUSR);
if fd == -1 {
return Err(Error::last_os_error());
}
File::from_raw_fd(fd)
};
/*
* A buffer must be at least 1x1, and 32 bits per pixel
* (ARGB8888 and XRGB8888) is supported on all Wayland compositors.
*/
file.set_len(4)?;
let map = unsafe { MmapMut::map_mut(&file) }?;
let pool = shm.create_pool(file.as_fd(), 4, &queue, id);
let buf = pool.create_buffer(0, 1, 1, 4, wl_shm::Format::Xrgb8888,
&queue, id);
/*
* This can happen before expanding the file. The shm_unlink(3p) manual
* says:
*
* If one or more references to the shared memory object exist when
* the object is unlinked, the name shall be removed before
* shm_unlink() returns, but the removal of the memory object
* contents shall be postponed until all open and map references to
* the shared memory object have been removed.
*/
unsafe {
libc::shm_unlink(name.as_ptr());
};
Ok(Buffer {
file, map,
id, capacity: 4, width: 1, height: 1,
queue, pool, buf,
used: false, resizing: false,
})
}
fn commit_resize(&mut self)
{
if self.resizing {
self.buf = self.pool.create_buffer(0, self.width, self.height,
self.width * 4,
wl_shm::Format::Xrgb8888,
&self.queue, self.id);
self.resizing = false;
}
}
pub fn resize(&mut self, width: i32, height: i32) -> Result<(), Error>
{
if width <= 0 || height <= 0 {
return Err(Error::new(ErrorKind::InvalidInput, "Buffer must be at least 1x1"));
}
// Expand as necessary. Shrinking is not supported by wl_shm_pool.resize().
if self.capacity < width * height * 4 {
self.capacity = width * height * 4;
// Failure is a bug in the above check or the capacity calculation.
let filelen = self.capacity as u64;
self.file.set_len(filelen)?;
self.pool.resize(self.capacity);
self.map = unsafe { MmapMut::map_mut(&self.file) }?;
}
// Try to resize the buffer now, otherwise defer it.
self.width = width;
self.height = height;
self.resizing = true;
if !self.used {
self.commit_resize();
}
Ok(())
}
pub fn image(&self) -> ImgRef<BGRA<u8>>
{
let ptr = self.map.as_ref().as_bgra();
ImgRef::new(ptr, self.width as usize, self.height as usize)
}
pub fn consume(&mut self, surface: &WlSurface) -> Option<ImgRefMut<BGRA<u8>>>
{
if self.used {
return None;
}
self.used = true;
surface.attach(Some(&self.buf), 0, 0);
// The ARGB format is in little endian.
let ptr = self.map.as_mut().as_bgra_mut();
let img = ImgRefMut::new(ptr, self.width as usize, self.height as usize);
Some(img)
}
pub fn release(&mut self)
{
self.used = false;
self.commit_resize();
}
pub fn writeback(&mut self, src: &ImgRef<BGRA<u8>>, x: usize, y: usize)
{
if self.used {
panic!("Writeback must always be on an inactive buffer");
}
// The ARGB format is in little endian.
let ptr = self.map.as_mut().as_bgra_mut();
let mut img = ImgRefMut::new(ptr, self.width as usize, self.height as usize);
let mut subimg = img.sub_image_mut(x, y, src.width(), src.height());
for (dest, src) in iter::zip(subimg.pixels_mut(), src.pixels()) {
*dest = src;
}
}
#[inline(always)]
pub fn size(&self) -> (i32, i32)
{
(self.width, self.height)
}
}

391
src/wayland/dispatcher.rs Normal file
View file

@ -0,0 +1,391 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Copyright (c) 2024, Richard Acayan. All rights reserved.
*/
use core::Button;
use core::Display;
use core::Graphics;
use core::Layout;
use std::io::Error;
use std::sync::Arc;
use std::sync::Mutex;
use wayland::Seat;
use wayland::Surface;
use wayland::wayland_client::globals::GlobalList;
use wayland::wayland_client::globals::GlobalListContents;
use wayland::wayland_client::protocol::wl_buffer;
use wayland::wayland_client::protocol::wl_compositor;
use wayland::wayland_client::protocol::wl_pointer;
use wayland::wayland_client::protocol::wl_registry;
use wayland::wayland_client::protocol::wl_seat;
use wayland::wayland_client::protocol::wl_shm;
use wayland::wayland_client::protocol::wl_shm_pool;
use wayland::wayland_client::protocol::wl_surface;
use wayland::wayland_client::protocol::wl_touch;
use wayland::wayland_client::Connection;
use wayland::wayland_client::Dispatch;
use wayland::wayland_client::Proxy;
use wayland::wayland_client::QueueHandle;
use wayland::wayland_client::WEnum;
use wayland::wlr_layer_shell_unstable_v1::zwlr_layer_shell_v1;
use wayland::wlr_layer_shell_unstable_v1::zwlr_layer_surface_v1;
use wayland::fractional_scale_v1::wp_fractional_scale_manager_v1;
use wayland::fractional_scale_v1::wp_fractional_scale_v1;
use wayland::viewporter::wp_viewporter;
use wayland::viewporter::wp_viewport;
use wayland::virtual_keyboard_unstable_v1::zwp_virtual_keyboard_manager_v1;
use wayland::virtual_keyboard_unstable_v1::zwp_virtual_keyboard_v1;
use wayland::VirtualKeyboard;
pub struct Dispatcher {
seat: Seat<Surface<Self>, VirtualKeyboard, Self>,
gfx: Arc<Mutex<Graphics<Surface<Self>>>>,
}
impl Dispatcher {
pub fn new(layout: Layout, queue: QueueHandle<Self>, globals: &GlobalList) -> Result<Self, Error>
{
let shm = globals.bind(&queue, 1..=1, ())
.expect("Compositor must implement wl_shm");
let compositor = globals.bind(&queue, 4..=6, ())
.expect("Compositor must implement wl_compositor");
let seat = globals.bind(&queue, 5..=9, ())
.expect("Compositor must implement wl_seat");
let layer_shell = globals.bind(&queue, 3..=4, ())
.expect("Compositor must implement zwlr_layer_shell_v1");
let vk_man = globals.bind(&queue, 1..=1, ())
.expect("Compositor must implement zwp_virtual_keyboard_manager_v1");
let frac_scale_man = match globals.bind(&queue, 1..=1, ()) {
Ok(g) => Some(g),
Err(_) => None,
};
let vper = match globals.bind(&queue, 1..=1, ()) {
Ok(g) => Some(g),
Err(_) => None,
};
let disp = Surface::new(queue.clone(),
&shm, &compositor, &layer_shell,
frac_scale_man, vper, 185)?;
let gfx = Graphics::new(disp);
let gfx = Mutex::new(gfx);
let gfx = Arc::new(gfx);
let vk = VirtualKeyboard::new(&queue, &vk_man, &seat);
let seat = Seat::new(layout, vk, gfx.clone(), queue.clone(), seat);
Ok(Dispatcher {
seat,
gfx,
})
}
#[inline(always)]
pub fn button(&self) -> &Button<Surface<Self>, VirtualKeyboard>
{
self.seat.button()
}
pub fn dispatch_timers(&mut self)
{
self.seat.button_mut().dispatch_timers();
}
}
impl Dispatch<wl_buffer::WlBuffer, u32> for Dispatcher {
fn event(_ctx: &mut Dispatcher,
_buf: &wl_buffer::WlBuffer,
_evt: <wl_buffer::WlBuffer as Proxy>::Event,
_data: &u32,
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
}
}
impl Dispatch<wl_compositor::WlCompositor, ()> for Dispatcher {
fn event(_ctx: &mut Dispatcher,
_comp: &wl_compositor::WlCompositor,
_evt: <wl_compositor::WlCompositor as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
}
}
impl Dispatch<wl_pointer::WlPointer, ()> for Dispatcher {
fn event(ctx: &mut Dispatcher,
_ptr: &wl_pointer::WlPointer,
evt: <wl_pointer::WlPointer as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
match evt {
wl_pointer::Event::Motion { surface_x, surface_y, .. } => {
ctx.seat.ptr_motion(surface_x, surface_y);
},
wl_pointer::Event::Button { state: WEnum::Value(state), .. } => {
ctx.seat.ptr_button(state);
},
wl_pointer::Event::Frame => {
ctx.seat.commit();
},
wl_pointer::Event::Enter { .. } => (),
wl_pointer::Event::Leave { .. } => (),
_ => eprintln!("warn: unknown wl_pointer event emitted"),
};
}
}
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for Dispatcher {
fn event(_ctx: &mut Dispatcher,
_reg: &wl_registry::WlRegistry,
_evt: <wl_registry::WlRegistry as Proxy>::Event,
_data: &GlobalListContents,
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
}
}
impl Dispatch<wl_seat::WlSeat, ()> for Dispatcher {
fn event(ctx: &mut Dispatcher,
_seat: &wl_seat::WlSeat,
evt: <wl_seat::WlSeat as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
match evt {
wl_seat::Event::Name { .. } => (),
wl_seat::Event::Capabilities { capabilities: WEnum::Value(caps) } => {
ctx.seat.set_capabilities(caps);
},
_ => eprintln!("warn: unknown wl_seat event emitted"),
};
}
}
impl Dispatch<wl_shm::WlShm, ()> for Dispatcher {
fn event(_ctx: &mut Dispatcher,
_shm: &wl_shm::WlShm,
evt: <wl_shm::WlShm as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
match evt {
wl_shm::Event::Format { .. } => (),
_ => eprintln!("warn: unknown wl_shm event emitted"),
};
}
}
impl Dispatch<wl_shm_pool::WlShmPool, u32> for Dispatcher {
fn event(_ctx: &mut Dispatcher,
_pool: &wl_shm_pool::WlShmPool,
_evt: <wl_shm_pool::WlShmPool as Proxy>::Event,
_data: &u32,
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
eprintln!("warn: unknown wl_shm_pool event emitted");
}
}
impl Dispatch<wl_surface::WlSurface, ()> for Dispatcher {
fn event(_ctx: &mut Dispatcher,
_pool: &wl_surface::WlSurface,
evt: <wl_surface::WlSurface as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
match evt {
wl_surface::Event::PreferredBufferTransform { .. } => (),
wl_surface::Event::PreferredBufferScale { .. } => (), // TODO
_ => eprintln!("warn: unknown wl_surface event emitted"),
}
}
}
impl Dispatch<wl_touch::WlTouch, ()> for Dispatcher {
fn event(ctx: &mut Dispatcher,
_ptr: &wl_touch::WlTouch,
evt: <wl_touch::WlTouch as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
match evt {
wl_touch::Event::Down { id, x, y, .. } => {
ctx.seat.touch_press(id as u32, x, y);
},
wl_touch::Event::Motion { id, x, y, .. } => {
ctx.seat.touch_pos(id as u32, x, y);
},
wl_touch::Event::Up { id, .. } => {
ctx.seat.touch_release(id as u32);
},
wl_touch::Event::Frame => {
ctx.seat.commit();
},
_ => eprintln!("warn: unknown wl_touch event emitted"),
};
}
}
impl Dispatch<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1, ()> for Dispatcher {
fn event(_ctx: &mut Dispatcher,
_pool: &wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1,
_evt: wp_fractional_scale_manager_v1::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
eprintln!("warn: unknown wp_fractional_scale_manager_v1 event emitted");
}
}
impl Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ()> for Dispatcher {
fn event(ctx: &mut Dispatcher,
_pool: &wp_fractional_scale_v1::WpFractionalScaleV1,
evt: wp_fractional_scale_v1::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
match evt {
wp_fractional_scale_v1::Event::PreferredScale { scale } => {
let mut gfx = ctx.gfx.lock().unwrap();
if scale == gfx.display().scale() {
return;
}
let old_scale = gfx.display().scale();
let (width, height) = gfx.display().size();
let (width, height) = (width * scale / old_scale,
height * scale / old_scale);
gfx.display_mut().update_scale(scale);
if gfx.display().configured() {
let layout = ctx.seat.layout();
let mod_state = ctx.seat.mod_state();
gfx.resize(layout, mod_state, width, height);
let (width, height) = gfx.display().size();
let width = width as f64 * 120.0 / scale as f64;
let height = height as f64 * 120.0 / scale as f64;
ctx.seat.set_scale(layout.width() / width,
layout.height() / height);
}
},
_ => eprintln!("warn: unknown wp_fractional_scale_v1 event emitted"),
};
}
}
impl Dispatch<wp_viewporter::WpViewporter, ()> for Dispatcher {
fn event(_ctx: &mut Dispatcher,
_pool: &wp_viewporter::WpViewporter,
_evt: wp_viewporter::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
eprintln!("warn: unknown wp_viewporter event emitted");
}
}
impl Dispatch<wp_viewport::WpViewport, ()> for Dispatcher {
fn event(_ctx: &mut Dispatcher,
_pool: &wp_viewport::WpViewport,
_evt: wp_viewport::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
eprintln!("warn: unknown wp_viewport event emitted");
}
}
impl Dispatch<zwlr_layer_shell_v1::ZwlrLayerShellV1, ()> for Dispatcher {
fn event(_ctx: &mut Dispatcher,
_pool: &zwlr_layer_shell_v1::ZwlrLayerShellV1,
_evt: <zwlr_layer_shell_v1::ZwlrLayerShellV1 as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
eprintln!("warn: unknown zwlr_layer_shell_v1 event emitted");
}
}
impl Dispatch<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, ()> for Dispatcher {
fn event(ctx: &mut Dispatcher,
_surface: &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
evt: zwlr_layer_surface_v1::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
match evt {
zwlr_layer_surface_v1::Event::Configure { width, height, serial } => {
let mut gfx = ctx.gfx.lock().unwrap();
gfx.display_mut().ack_configure(serial);
let layout = ctx.seat.layout();
let mod_state = ctx.seat.mod_state();
let scale = gfx.display().scale() / 120;
let (width, height) = (width * scale, height * scale);
gfx.resize(layout, mod_state, width, height);
let disp = gfx.display();
let scale = disp.scale() as f64;
let (width, height) = disp.size();
let width = width as f64 * 120.0 / scale;
let height = height as f64 * 120.0 / scale;
ctx.seat.set_scale(layout.width() / width,
layout.height() / height);
},
_ => eprintln!("warn: unknown zwlr_layer_surface_v1 event emitted"),
}
}
}
impl Dispatch<zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1, ()> for Dispatcher {
fn event(_ctx: &mut Dispatcher,
_vk_man: &zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1,
_evt: zwp_virtual_keyboard_manager_v1::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
eprintln!("warn: unknown zwp_virtual_keyboard_manager_v1 event emitted");
}
}
impl Dispatch<zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1, ()> for Dispatcher {
fn event(_ctx: &mut Dispatcher,
_vk: &zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1,
_evt: zwp_virtual_keyboard_v1::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Dispatcher>)
{
eprintln!("warn: unknown zwp_virtual_keyboard_v1 event emitted");
}
}

199
src/wayland/keyboard.rs Normal file
View file

@ -0,0 +1,199 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Copyright (c) 2024, Richard Acayan. All rights reserved.
*/
use core::Keyboard;
use core::Layout;
use core::Part;
use core::xkeysym::Keysym;
use std::collections::HashMap;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Seek;
use std::io::Write;
use std::os::fd::AsFd;
use std::process;
use wayland::wayland_client::Dispatch;
use wayland::wayland_client::QueueHandle;
use wayland::wayland_client::protocol::wl_seat::WlSeat;
use wayland::wayland_client::protocol::wl_keyboard::KeymapFormat;
use wayland::virtual_keyboard_unstable_v1::zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1;
use wayland::virtual_keyboard_unstable_v1::zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1;
pub struct VirtualKeyboard {
vk: ZwpVirtualKeyboardV1,
keymap: File,
keymap_id: u8,
keycodes: HashMap<Keysym, u8>,
mod_state: u32,
}
impl VirtualKeyboard {
pub fn new<T: Dispatch<ZwpVirtualKeyboardV1, ()>
+ 'static>(queue: &QueueHandle<T>,
vk_man: &ZwpVirtualKeyboardManagerV1,
seat: &WlSeat)
-> VirtualKeyboard
{
let vk = vk_man.create_virtual_keyboard(seat, queue, ());
let path = format!("/tmp/ufkbd-keymap-pid{}-1", process::id());
let keymap = File::create(path).unwrap();
VirtualKeyboard {
vk,
keymap,
keymap_id: 1,
keycodes: HashMap::new(),
mod_state: 0,
}
}
fn write_key(&mut self, part: &Part, keycode: u8)
{
let sym = part.sym();
let lower = &sym.name().unwrap()[3..];
let upper = &Part::modify_shift(sym).name().unwrap()[3..];
write!(self.keymap, " key <I{:03}> {{ [ {}, {} ] }};\n",
keycode, lower, upper).unwrap();
if sym == Keysym::Shift_L {
write!(self.keymap,
" modifier_map Shift {{ <I{:03}> }};\n",
keycode).unwrap();
} else if sym == Keysym::Control_L {
write!(self.keymap,
" modifier_map Control {{ <I{:03}> }};\n",
keycode).unwrap();
} else if sym == Keysym::Alt_L {
write!(self.keymap,
" modifier_map Mod1 {{ <I{:03}> }};\n",
keycode).unwrap();
}
}
}
impl Keyboard for VirtualKeyboard {
fn press(&mut self, sym: Keysym)
{
if sym == Keysym::NoSymbol || sym == Keysym::XF86_Fn {
return;
}
match sym {
Keysym::Shift_L => {
self.mod_state |= 0x1;
self.vk.modifiers(self.mod_state, 0, 0, 0);
},
Keysym::Control_L => {
self.mod_state |= 0x4;
self.vk.modifiers(self.mod_state, 0, 0, 0);
},
Keysym::Alt_L => {
self.mod_state |= 0x8;
self.vk.modifiers(self.mod_state, 0, 0, 0);
},
_ => (),
}
let keycode = *self.keycodes.get(&sym).unwrap();
self.vk.key(0, keycode as u32, 1);
}
fn release(&mut self, sym: Keysym)
{
if sym == Keysym::NoSymbol || sym == Keysym::XF86_Fn {
return;
}
match sym {
Keysym::Shift_L => {
self.mod_state &= !0x1;
self.vk.modifiers(self.mod_state, 0, 0, 0);
},
Keysym::Control_L => {
self.mod_state &= !0x4;
self.vk.modifiers(self.mod_state, 0, 0, 0);
},
Keysym::Alt_L => {
self.mod_state &= !0x8;
self.vk.modifiers(self.mod_state, 0, 0, 0);
},
_ => (),
}
let keycode = *self.keycodes.get(&sym).unwrap();
self.vk.key(0, keycode as u32, 0);
}
fn change_layout(&mut self, layout: &Layout)
{
let mut keycode = 8;
self.keymap_id = match self.keymap_id {
0 => 1,
1 => 0,
_ => panic!("There should only be up to two keymaps"),
};
let path = format!("/tmp/ufkbd-keymap-pid{}-{}",
process::id(), self.keymap_id);
self.keymap = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(path)
.unwrap();
self.keymap.write(b"xkb_keymap {\n").unwrap();
self.keymap.write(b" xkb_symbols \"ufkbd\" {\n").unwrap();
for row in layout.rows() {
for key in row {
for part in &key.parts {
if part.sym() == Keysym::NoSymbol || part.sym() == Keysym::XF86_Fn {
continue;
}
self.keycodes.insert(part.sym(), keycode - 8);
self.write_key(part, keycode);
keycode += 1;
}
}
}
self.keymap.write(b" };\n").unwrap();
self.keymap.write(b"\n").unwrap();
self.keymap.write(b" xkb_keycodes \"ufkbd\" {\n").unwrap();
self.keymap.write(b" minimum = 8;\n").unwrap();
self.keymap.write(b" maximum = 255;\n").unwrap();
for i in 8..keycode {
write!(self.keymap, " <I{:03}> = {};\n", i, i).unwrap();
}
self.keymap.write(b" indicator 1 = \"Caps Lock\";\n").unwrap();
self.keymap.write(b" };\n").unwrap();
self.keymap.write(b"\n").unwrap();
self.keymap.write(b" xkb_types \"ufkbd\" {\n").unwrap();
self.keymap.write(b" type \"TWO_LEVEL\" {\n").unwrap();
self.keymap.write(b" modifiers = Shift;\n").unwrap();
self.keymap.write(b" map[Shift] = Level2;\n").unwrap();
self.keymap.write(b" level_name[Level1] = \"Base\";\n").unwrap();
self.keymap.write(b" level_name[Level2] = \"Shift\";\n").unwrap();
self.keymap.write(b" };\n").unwrap();
self.keymap.write(b" };\n").unwrap();
self.keymap.write(b"\n").unwrap();
self.keymap.write(b" xkb_compatibility \"ufkbd\" {\n").unwrap();
self.keymap.write(b" };\n").unwrap();
self.keymap.write(b"};\n").unwrap();
let len = self.keymap.stream_position().unwrap() as u32;
self.vk.keymap(KeymapFormat::XkbV1 as u32, self.keymap.as_fd(), len);
}
}

30
src/wayland/mod.rs Normal file
View file

@ -0,0 +1,30 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Copyright (c) 2024, Richard Acayan. All rights reserved.
*/
pub extern crate wayland_client;
pub extern crate wayland_protocols;
pub extern crate wayland_protocols_wlr;
pub extern crate wayland_scanner;
mod buffer;
mod dispatcher;
mod keyboard;
mod protocols;
mod seat;
mod surface;
pub use self::buffer::Buffer;
pub use self::dispatcher::Dispatcher;
pub use self::keyboard::VirtualKeyboard;
pub use self::seat::Seat;
pub use self::surface::Surface;
pub use self::wayland_protocols_wlr::layer_shell::v1::client as wlr_layer_shell_unstable_v1;
pub use self::wayland_protocols::wp::fractional_scale::v1::client as fractional_scale_v1;
pub use self::wayland_protocols::wp::text_input::zv3::client as text_input_unstable_v3;
pub use self::wayland_protocols::wp::viewporter::client as viewporter;
pub use self::protocols::input_method_unstable_v2;
pub use self::protocols::virtual_keyboard_unstable_v1;

35
src/wayland/protocols.rs Normal file
View file

@ -0,0 +1,35 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Copyright (c) 2024, Richard Acayan. All rights reserved.
*/
pub mod input_method_unstable_v2 {
use wayland::wayland_client;
use wayland::wayland_client::protocol::*;
use wayland::text_input_unstable_v3::*;
pub mod __interfaces {
use wayland::wayland_client::protocol::__interfaces::*;
wayland_scanner::generate_interfaces!("wayland-protocols/input-method-unstable-v2.xml");
}
use self::__interfaces::*;
wayland_scanner::generate_client_code!("wayland-protocols/input-method-unstable-v2.xml");
}
pub mod virtual_keyboard_unstable_v1 {
use wayland::wayland_client;
use wayland::wayland_client::protocol::*;
pub mod __interfaces {
use wayland::wayland_client::protocol::__interfaces::*;
wayland_scanner::generate_interfaces!("wayland-protocols/virtual-keyboard-unstable-v1.xml");
}
use self::__interfaces::*;
wayland_scanner::generate_client_code!("wayland-protocols/virtual-keyboard-unstable-v1.xml");
}

186
src/wayland/seat.rs Normal file
View file

@ -0,0 +1,186 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Copyright (c) 2024, Richard Acayan. All rights reserved.
*/
use core::Button;
use core::ModState;
use core::Display;
use core::Graphics;
use core::Keyboard;
use core::Layout;
use std::sync::Arc;
use std::sync::Mutex;
use wayland::wayland_client::protocol::wl_pointer;
use wayland::wayland_client::protocol::wl_seat;
use wayland::wayland_client::protocol::wl_touch::WlTouch;
use wayland::wayland_client::Dispatch;
use wayland::wayland_client::QueueHandle;
enum PressAction {
Pos(f64, f64),
Press(f64, f64),
Release,
}
struct TouchAction {
id: usize,
act: PressAction,
}
pub struct Seat<D: Display, K: Keyboard, T> {
seat: wl_seat::WlSeat,
queue: QueueHandle<T>,
ptr: Option<wl_pointer::WlPointer>,
touch: Option<WlTouch>,
button: Button<D, K>,
x_scale: f64,
y_scale: f64,
mouse_x: f64,
mouse_y: f64,
actions: Vec<TouchAction>,
}
impl<D: Display, K: Keyboard,
T: Dispatch<wl_pointer::WlPointer, ()>
+ Dispatch<WlTouch, ()>
+ 'static> Seat<D, K, T> {
pub fn new(layout: Layout, kbd: K, gfx: Arc<Mutex<Graphics<D>>>,
queue: QueueHandle<T>, seat: wl_seat::WlSeat) -> Seat<D, K, T>
{
let actions = Vec::new();
let button = Button::new(layout, kbd, gfx);
Seat {
seat,
queue,
ptr: None,
touch: None,
button,
x_scale: 0.0,
y_scale: 0.0,
mouse_x: 0.0,
mouse_y: 0.0,
actions,
}
}
#[inline(always)]
pub fn button(&self) -> &Button<D, K>
{
&self.button
}
#[inline(always)]
pub fn button_mut(&mut self) -> &mut Button<D, K>
{
&mut self.button
}
#[inline(always)]
pub fn layout(&self) -> &Layout
{
self.button.layout()
}
#[inline(always)]
pub fn mod_state(&self) -> &[ModState]
{
self.button.mod_state()
}
pub fn set_capabilities(&mut self, caps: wl_seat::Capability)
{
if caps.contains(wl_seat::Capability::Pointer) {
self.ptr = Some(self.seat.get_pointer(&self.queue, ()));
}
if caps.contains(wl_seat::Capability::Touch) {
self.touch = Some(self.seat.get_touch(&self.queue, ()));
}
}
pub fn set_scale(&mut self, x_scale: f64, y_scale: f64)
{
self.x_scale = x_scale;
self.y_scale = y_scale;
}
pub fn ptr_motion(&mut self, x: f64, y: f64)
{
let (x, y) = (x * self.x_scale, y * self.y_scale);
self.mouse_x = x;
self.mouse_y = y;
let act = TouchAction { id: 0, act: PressAction::Pos(x, y), };
self.actions.push(act);
}
pub fn ptr_button(&mut self, state: wl_pointer::ButtonState)
{
match state {
wl_pointer::ButtonState::Pressed => {
let (x, y) = (self.mouse_x, self.mouse_y);
let act = TouchAction { id: 0, act: PressAction::Press(x, y), };
self.actions.push(act);
},
wl_pointer::ButtonState::Released => {
let act = TouchAction { id: 0, act: PressAction::Release, };
self.actions.push(act);
},
_ => eprintln!("warn: ignoring unknown pointer event"),
}
}
pub fn touch_press(&mut self, id: u32, x: f64, y: f64)
{
let id = id as usize + 1;
let (x, y) = (x * self.x_scale, y * self.y_scale);
let act = TouchAction { id, act: PressAction::Press(x, y), };
self.actions.push(act);
}
pub fn touch_pos(&mut self, id: u32, x: f64, y: f64)
{
let id = id as usize + 1;
let (x, y) = (x * self.x_scale, y * self.y_scale);
let act = TouchAction { id, act: PressAction::Pos(x, y), };
self.actions.push(act);
}
pub fn touch_release(&mut self, id: u32)
{
let id = id as usize + 1;
let act = TouchAction { id, act: PressAction::Release, };
self.actions.push(act);
}
pub fn commit(&mut self)
{
for action in &self.actions {
match action.act {
PressAction::Press(x, y) => {
self.button.press(action.id, x, y);
},
PressAction::Pos(x, y) => {
self.button.pos(action.id, x, y);
},
PressAction::Release => {
self.button.release(action.id);
},
}
}
// Reset the action for the next frame.
self.actions.clear();
}
}

179
src/wayland/surface.rs Normal file
View file

@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Copyright (c) 2024, Richard Acayan. All rights reserved.
*/
use core::Display;
use core::imgref::ImgRefMut;
use core::rgb::alt::BGRA;
use std::io::Error;
use wayland::Buffer;
use wayland::wayland_client::protocol::wl_buffer::WlBuffer;
use wayland::wayland_client::protocol::wl_compositor::WlCompositor;
use wayland::wayland_client::protocol::wl_shm_pool::WlShmPool;
use wayland::wayland_client::protocol::wl_shm::WlShm;
use wayland::wayland_client::protocol::wl_surface::WlSurface;
use wayland::wayland_client::Dispatch;
use wayland::wayland_client::QueueHandle;
use wayland::wlr_layer_shell_unstable_v1::zwlr_layer_shell_v1;
use wayland::wlr_layer_shell_unstable_v1::zwlr_layer_surface_v1;
use wayland::fractional_scale_v1::wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1;
use wayland::fractional_scale_v1::wp_fractional_scale_v1::WpFractionalScaleV1;
use wayland::viewporter::wp_viewporter::WpViewporter;
use wayland::viewporter::wp_viewport::WpViewport;
pub struct Surface<T> {
bufs: [Buffer<T>; 2],
surface: WlSurface,
layer_surf: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
vp: Option<WpViewport>,
used_buf: usize,
configured: bool,
scale: u32,
}
const LAYER_ANCHOR: zwlr_layer_surface_v1::Anchor
= zwlr_layer_surface_v1::Anchor::from_bits_truncate(0)
.union(zwlr_layer_surface_v1::Anchor::Bottom)
.union(zwlr_layer_surface_v1::Anchor::Left)
.union(zwlr_layer_surface_v1::Anchor::Right);
impl<T: Dispatch<WlBuffer, u32>
+ Dispatch<WlShmPool, u32>
+ Dispatch<WlSurface, ()>
+ Dispatch<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, ()>
+ Dispatch<WpFractionalScaleV1, ()>
+ Dispatch<WpViewport, ()>
+ 'static> Surface<T> {
pub fn new(queue: QueueHandle<T>, shm: &WlShm, comp: &WlCompositor,
layer_shell: &zwlr_layer_shell_v1::ZwlrLayerShellV1,
frac_scale_man: Option<WpFractionalScaleManagerV1>,
vper: Option<WpViewporter>,
size: i32)
-> Result<Surface<T>, Error>
{
let buf1 = Buffer::new(queue.clone(), shm, 0)?;
let buf2 = Buffer::new(queue.clone(), shm, 1)?;
let surface = comp.create_surface(&queue, ());
let layer_surf = layer_shell.get_layer_surface(&surface, None,
zwlr_layer_shell_v1::Layer::Top,
String::from("osk-ufkbd"),
&queue, ());
frac_scale_man.map(|fsm| fsm.get_fractional_scale(&surface, &queue, ()));
let vp = vper.map(|vper| vper.get_viewport(&surface, &queue, ()));
layer_surf.set_size(0, size as u32);
layer_surf.set_exclusive_zone(size);
layer_surf.set_anchor(LAYER_ANCHOR);
surface.commit();
Ok(Surface {
bufs: [buf1, buf2],
surface,
layer_surf,
vp,
used_buf: 1,
configured: false,
scale: 120,
})
}
pub fn configured(&self) -> bool
{
self.configured
}
pub fn ack_configure(&mut self, serial: u32)
{
self.configured = true;
self.layer_surf.ack_configure(serial);
}
pub fn scale(&self) -> u32
{
self.scale
}
pub fn update_scale(&mut self, scale: u32)
{
self.scale = scale;
}
}
impl<T: Dispatch<WlBuffer, u32>
+ Dispatch<WlShmPool, u32>
+ Dispatch<WlSurface, ()>
+ Dispatch<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, ()>
+ Dispatch<WpFractionalScaleV1, ()>
+ Dispatch<WpViewport, ()>
+ 'static> Display for Surface<T> {
#[inline(always)]
fn size(&self) -> (u32, u32)
{
let (width, height) = self.bufs[self.used_buf].size();
(width as u32, height as u32)
}
fn begin(&mut self) -> ImgRefMut<BGRA<u8>>
{
for (id, buf) in self.bufs.iter_mut().enumerate() {
if let Some(img) = buf.consume(&self.surface) {
self.used_buf = id;
return img;
}
}
panic!("No buffers available")
}
fn resize(&mut self, width: u32, height: u32)
{
let width_unscaled = (width * 120 / self.scale) as i32;
let height_unscaled = (height * 120 / self.scale) as i32;
for buf in &mut self.bufs {
buf.resize(width as i32, height as i32).unwrap();
}
if let Some(vp) = &self.vp {
vp.set_source(0.0, 0.0, width as f64, height as f64);
vp.set_destination(width_unscaled, height_unscaled);
}
}
fn end(&mut self, x: u32, y: u32, width: u32, height: u32)
{
self.surface.damage_buffer(x as i32, y as i32, width as i32, height as i32);
self.surface.commit();
for (id, buf) in self.bufs.iter_mut().enumerate() {
if id != self.used_buf {
buf.release();
}
}
let (src, dest) = match self.used_buf {
0 => {
let [src, ref mut dest] = &mut self.bufs;
(src, dest)
},
1 => {
let [ref mut dest, src] = &mut self.bufs;
(src, dest)
},
2.. => panic!("Surface must only use two buffers"),
};
let img = src.image().sub_image(x as usize,
y as usize,
width as usize,
height as usize);
dest.writeback(&img, x as usize, y as usize);
}
}