feat: wayland
This commit is contained in:
89
src/wayland/core/buffer.rs
Normal file
89
src/wayland/core/buffer.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "dmabuf")]
|
||||
use crate::wayland::dmabuf::buffer_backing::DmabufBacking;
|
||||
use crate::{
|
||||
core::registry::Registry,
|
||||
wayland::{GraphicsInfo, core::shm_buffer_backing::ShmBufferBacking},
|
||||
};
|
||||
use bevy::{
|
||||
asset::{Assets, Handle},
|
||||
image::Image,
|
||||
};
|
||||
use mint::Vector2;
|
||||
pub use waynest::server::protocol::core::wayland::wl_buffer::*;
|
||||
use waynest::{
|
||||
server::{Client, Dispatcher, Result},
|
||||
wire::ObjectId,
|
||||
};
|
||||
|
||||
pub static BUFFER_REGISTRY: Registry<Buffer> = Registry::new();
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BufferBacking {
|
||||
Shm(ShmBufferBacking),
|
||||
#[cfg(feature = "dmabuf")]
|
||||
Dmabuf(DmabufBacking),
|
||||
}
|
||||
impl BufferBacking {
|
||||
// Returns true if the buffer can be released immediately after texture update
|
||||
pub fn can_release_after_update(&self) -> bool {
|
||||
matches!(self, BufferBacking::Shm(_))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Dispatcher)]
|
||||
pub struct Buffer {
|
||||
pub id: ObjectId,
|
||||
backing: BufferBacking,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub fn new(client: &mut Client, id: ObjectId, backing: BufferBacking) -> Arc<Self> {
|
||||
let buffer = client.insert(id, Self { id, backing });
|
||||
BUFFER_REGISTRY.add_raw(&buffer);
|
||||
buffer
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn init_tex(self: Arc<Self>, graphics_info: &mut GraphicsInfo) {
|
||||
match &self.backing {
|
||||
BufferBacking::Shm(_) => (),
|
||||
#[cfg(feature = "dmabuf")]
|
||||
BufferBacking::Dmabuf(backing) => backing.init_tex(graphics_info, self.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the tex if it was updated
|
||||
pub fn update_tex(&self, images: &mut Assets<Image>) -> Option<Handle<Image>> {
|
||||
tracing::debug!("Updating texture for buffer {:?}", self.id);
|
||||
match &self.backing {
|
||||
BufferBacking::Shm(backing) => backing.update_tex(images),
|
||||
#[cfg(feature = "dmabuf")]
|
||||
BufferBacking::Dmabuf(backing) => backing
|
||||
.get_tex()
|
||||
.map(|tex| tex.get_id().to_string())
|
||||
.and_then(|tex_id| Tex::find(tex_id).ok()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_release_after_update(&self) -> bool {
|
||||
self.backing.can_release_after_update()
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Vector2<usize> {
|
||||
match &self.backing {
|
||||
BufferBacking::Shm(backing) => backing.size(),
|
||||
#[cfg(feature = "dmabuf")]
|
||||
BufferBacking::Dmabuf(backing) => backing.size(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WlBuffer for Buffer {
|
||||
/// https://wayland.app/protocols/wayland#wl_buffer:request:destroy
|
||||
async fn destroy(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
|
||||
tracing::info!("Destroying buffer {:?}", self.id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
10
src/wayland/core/callback.rs
Normal file
10
src/wayland/core/callback.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
pub use waynest::server::protocol::core::wayland::wl_callback::*;
|
||||
use waynest::{
|
||||
server::{Dispatcher, Result},
|
||||
wire::ObjectId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Dispatcher, Clone)]
|
||||
pub struct Callback(pub ObjectId);
|
||||
/// https://wayland.app/protocols/wayland#wl_callback
|
||||
impl WlCallback for Callback {}
|
||||
70
src/wayland/core/compositor.rs
Normal file
70
src/wayland/core/compositor.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use super::surface::WL_SURFACE_REGISTRY;
|
||||
use crate::wayland::core::surface::Surface;
|
||||
pub use waynest::server::protocol::core::wayland::wl_compositor::*;
|
||||
use waynest::{
|
||||
server::{Client, Dispatcher, Result, protocol::core::wayland::wl_region::WlRegion},
|
||||
wire::ObjectId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Dispatcher, Default)]
|
||||
pub struct Compositor;
|
||||
impl WlCompositor for Compositor {
|
||||
/// https://wayland.app/protocols/wayland#wl_compositor:request:create_surface
|
||||
async fn create_surface(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
id: ObjectId,
|
||||
) -> Result<()> {
|
||||
let surface = client.insert(id, Surface::new(client, id));
|
||||
WL_SURFACE_REGISTRY.add_raw(&surface);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_compositor:request:create_region
|
||||
async fn create_region(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
id: ObjectId,
|
||||
) -> Result<()> {
|
||||
client.insert(id, Region::default());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Dispatcher, Default)]
|
||||
pub struct Region {}
|
||||
impl WlRegion for Region {
|
||||
/// https://wayland.app/protocols/wayland#wl_region:request:add
|
||||
async fn add(
|
||||
&self,
|
||||
_client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
_x: i32,
|
||||
_y: i32,
|
||||
_width: i32,
|
||||
_height: i32,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_region:request:subtract
|
||||
async fn subtract(
|
||||
&self,
|
||||
_client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
_x: i32,
|
||||
_y: i32,
|
||||
_width: i32,
|
||||
_height: i32,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_region:request:destroy
|
||||
async fn destroy(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
75
src/wayland/core/display.rs
Normal file
75
src/wayland/core/display.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use crate::wayland::{
|
||||
MessageSink,
|
||||
core::{
|
||||
callback::{Callback, WlCallback},
|
||||
registry::Registry,
|
||||
seat::Seat,
|
||||
},
|
||||
};
|
||||
use global_counter::primitive::exact::CounterU32;
|
||||
use std::{
|
||||
sync::{Arc, OnceLock},
|
||||
time::Instant,
|
||||
};
|
||||
pub use waynest::server::protocol::core::wayland::wl_display::*;
|
||||
use waynest::{
|
||||
server::{Client, Dispatcher, Result},
|
||||
wire::ObjectId,
|
||||
};
|
||||
|
||||
#[derive(Dispatcher)]
|
||||
pub struct Display {
|
||||
pub message_sink: MessageSink,
|
||||
pub pid: Option<i32>,
|
||||
pub seat: OnceLock<Arc<Seat>>,
|
||||
id_counter: CounterU32,
|
||||
pub creation_time: Instant,
|
||||
}
|
||||
impl Display {
|
||||
pub fn new(message_sink: MessageSink, pid: Option<i32>) -> Self {
|
||||
Self {
|
||||
message_sink,
|
||||
pid,
|
||||
seat: OnceLock::new(),
|
||||
id_counter: CounterU32::new(0xff000000), // Start at 0xff000000 to avoid conflicts with client-generated IDs
|
||||
creation_time: Instant::now(),
|
||||
}
|
||||
}
|
||||
pub fn next_server_id(&self) -> ObjectId {
|
||||
unsafe { ObjectId::from_raw(self.id_counter.inc()) }
|
||||
}
|
||||
}
|
||||
impl WlDisplay for Display {
|
||||
/// https://wayland.app/protocols/wayland#wl_display:request:sync
|
||||
async fn sync(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
sender_id: ObjectId,
|
||||
callback_id: ObjectId,
|
||||
) -> Result<()> {
|
||||
let serial = client.next_event_serial();
|
||||
Callback(callback_id)
|
||||
.done(client, callback_id, serial)
|
||||
.await?;
|
||||
|
||||
self.delete_id(client, sender_id, callback_id.as_raw())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_display:request:get_registry
|
||||
async fn get_registry(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
registry_id: ObjectId,
|
||||
) -> Result<()> {
|
||||
let registry = client.insert(registry_id, Registry);
|
||||
|
||||
registry.advertise_globals(client, registry_id).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
259
src/wayland/core/keyboard.rs
Normal file
259
src/wayland/core/keyboard.rs
Normal file
@@ -0,0 +1,259 @@
|
||||
use crate::{
|
||||
nodes::items::panel::KEYMAPS,
|
||||
wayland::{core::surface::Surface, util::ClientExt},
|
||||
};
|
||||
use dashmap::{DashMap, DashSet};
|
||||
use memfd::MemfdOptions;
|
||||
use slotmap::{DefaultKey, KeyData};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
os::{
|
||||
fd::{AsRawFd, IntoRawFd},
|
||||
unix::io::{FromRawFd, OwnedFd},
|
||||
},
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
use tokio::sync::Mutex;
|
||||
pub use waynest::server::protocol::core::wayland::wl_keyboard::*;
|
||||
use waynest::{
|
||||
server::{Client, Dispatcher, Result},
|
||||
wire::ObjectId,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
struct ModifierState {
|
||||
pressed_keys: HashSet<u32>,
|
||||
mods_depressed: u32,
|
||||
mods_latched: u32,
|
||||
mods_locked: u32,
|
||||
group: u32,
|
||||
}
|
||||
|
||||
impl ModifierState {
|
||||
fn update_key(&mut self, key: u32, pressed: bool) -> bool {
|
||||
let changed = if pressed {
|
||||
self.pressed_keys.insert(key)
|
||||
} else {
|
||||
self.pressed_keys.remove(&key)
|
||||
};
|
||||
|
||||
if changed {
|
||||
self.update_modifiers();
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
fn update_modifiers(&mut self) {
|
||||
let mut mods = 0;
|
||||
|
||||
// Update modifier state based on currently pressed keys
|
||||
for key in &self.pressed_keys {
|
||||
match *key {
|
||||
input_event_codes::KEY_LEFTSHIFT!() | input_event_codes::KEY_RIGHTSHIFT!() => {
|
||||
mods |= 1
|
||||
}
|
||||
input_event_codes::KEY_LEFTCTRL!() | input_event_codes::KEY_RIGHTCTRL!() => {
|
||||
mods |= 4
|
||||
}
|
||||
input_event_codes::KEY_LEFTALT!() | input_event_codes::KEY_RIGHTALT!() => mods |= 8,
|
||||
input_event_codes::KEY_LEFTMETA!() | input_event_codes::KEY_RIGHTMETA!() => {
|
||||
mods |= 64
|
||||
}
|
||||
input_event_codes::KEY_CAPSLOCK!() => self.mods_locked ^= 1,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
self.mods_depressed = mods;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Dispatcher)]
|
||||
pub struct Keyboard {
|
||||
pub id: ObjectId,
|
||||
focused_surface: Mutex<Weak<Surface>>,
|
||||
modifier_state: Mutex<ModifierState>,
|
||||
pressed_keys: DashMap<ObjectId, DashSet<u32>>,
|
||||
current_keymap_id: Mutex<u64>,
|
||||
}
|
||||
|
||||
impl Keyboard {
|
||||
pub fn new(id: ObjectId) -> Self {
|
||||
Self {
|
||||
id,
|
||||
focused_surface: Mutex::new(Weak::new()),
|
||||
modifier_state: Mutex::new(ModifierState::default()),
|
||||
pressed_keys: DashMap::default(),
|
||||
current_keymap_id: Mutex::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_keymap(&self, client: &mut Client, keymap: &[u8]) -> Result<()> {
|
||||
let file = MemfdOptions::default()
|
||||
.create("stardust-keymap")
|
||||
.map_err(|e| waynest::server::Error::Custom(e.to_string()))?
|
||||
.into_file();
|
||||
file.set_len(keymap.len() as u64)?;
|
||||
// file.write_all(keymap)?;
|
||||
// file.flush()?;
|
||||
|
||||
// let map = libc::mmap(addr, len, prot, flags, fd, offset)
|
||||
|
||||
let mut map = unsafe { memmap2::MmapMut::map_mut(file.as_raw_fd()) }?;
|
||||
map.copy_from_slice(keymap);
|
||||
|
||||
let fd = unsafe { OwnedFd::from_raw_fd(file.into_raw_fd()) };
|
||||
|
||||
// Send keymap to client
|
||||
self.keymap(
|
||||
client,
|
||||
self.id,
|
||||
KeymapFormat::XkbV1,
|
||||
fd,
|
||||
keymap.len() as u32,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// has to be the wayland key, so -8 or whatever
|
||||
pub async fn handle_keyboard_key(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
surface: Arc<Surface>,
|
||||
keymap_id: u64,
|
||||
key: u32,
|
||||
pressed: bool,
|
||||
) -> Result<()> {
|
||||
// KEYMAP UPDATES
|
||||
{
|
||||
let mut old_keymap_id = self.current_keymap_id.lock().await;
|
||||
|
||||
if *old_keymap_id != keymap_id {
|
||||
// println!("Updating keymap to {keymap_id}");
|
||||
let keymap_key = DefaultKey::from(KeyData::from_ffi(keymap_id));
|
||||
|
||||
// Get keymap data and drop the lock immediately
|
||||
let keymap_data = {
|
||||
let keymap_lock = KEYMAPS.lock();
|
||||
keymap_lock
|
||||
.get(keymap_key)
|
||||
.map(|s| s.as_bytes().to_vec())
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
// Now we can safely await
|
||||
self.send_keymap(client, &keymap_data).await?;
|
||||
};
|
||||
*old_keymap_id = keymap_id;
|
||||
drop(old_keymap_id);
|
||||
}
|
||||
|
||||
// PRESSED KEYS UPDATE
|
||||
let pressed_keys = self.pressed_keys.entry(surface.id).or_default();
|
||||
if pressed {
|
||||
pressed_keys.insert(key);
|
||||
} else {
|
||||
pressed_keys.remove(&key);
|
||||
}
|
||||
// println!("pressed keys: {:?}", &*pressed_keys);
|
||||
|
||||
// FOCUS UPDATES
|
||||
let mut focused = self.focused_surface.lock().await;
|
||||
let mut modifier_state = self.modifier_state.lock().await;
|
||||
|
||||
let refocus = focused.as_ptr() != Arc::as_ptr(&surface);
|
||||
// If we're entering a new surface
|
||||
if refocus {
|
||||
// Send leave to old surface if it exists and is still alive
|
||||
if let Some(old_surface) = focused.upgrade() {
|
||||
let serial = client.next_event_serial();
|
||||
self.leave(client, old_surface.id, serial, self.id).await?;
|
||||
// println!("Left surface {}", old_surface.id);
|
||||
}
|
||||
|
||||
// Send enter to new surface
|
||||
let serial = client.next_event_serial();
|
||||
self.enter(
|
||||
client,
|
||||
self.id,
|
||||
serial,
|
||||
surface.id,
|
||||
pressed_keys.iter().flat_map(|k| k.to_ne_bytes()).collect(),
|
||||
)
|
||||
.await?;
|
||||
// println!("Entered new surface {}", surface.id);
|
||||
|
||||
// Update focused surface
|
||||
*focused = Arc::downgrade(&surface);
|
||||
}
|
||||
|
||||
// KEY EVENT SENDING
|
||||
let serial = client.next_event_serial();
|
||||
// println!(
|
||||
// "Sent key {key} {}",
|
||||
// if pressed { "pressed" } else { "released" }
|
||||
// );
|
||||
self.key(
|
||||
client,
|
||||
self.id,
|
||||
serial,
|
||||
client.display().creation_time.elapsed().as_millis() as u32, // time
|
||||
key,
|
||||
if pressed {
|
||||
KeyState::Pressed
|
||||
} else {
|
||||
KeyState::Released
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
// MODIFIER UPDATES
|
||||
// Update modifier state and send modifiers event if changed
|
||||
if refocus || modifier_state.update_key(key, pressed) {
|
||||
// println!("Update modifiers");
|
||||
let serial = client.next_event_serial();
|
||||
self.modifiers(
|
||||
client,
|
||||
self.id,
|
||||
serial,
|
||||
modifier_state.mods_depressed,
|
||||
modifier_state.mods_latched,
|
||||
modifier_state.mods_locked,
|
||||
modifier_state.group,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn reset(&self, client: &mut Client) -> Result<()> {
|
||||
let mut modifier_state = self.modifier_state.lock().await;
|
||||
modifier_state.pressed_keys.clear();
|
||||
modifier_state.mods_depressed = 0;
|
||||
modifier_state.mods_latched = 0;
|
||||
modifier_state.mods_locked = 0;
|
||||
modifier_state.group = 0;
|
||||
|
||||
let serial = client.next_event_serial();
|
||||
self.modifiers(
|
||||
client,
|
||||
self.id,
|
||||
serial,
|
||||
modifier_state.mods_depressed,
|
||||
modifier_state.mods_latched,
|
||||
modifier_state.mods_locked,
|
||||
modifier_state.group,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl WlKeyboard for Keyboard {
|
||||
/// https://wayland.app/protocols/wayland#wl_keyboard:request:release
|
||||
async fn release(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
14
src/wayland/core/mod.rs
Normal file
14
src/wayland/core/mod.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
pub mod buffer;
|
||||
pub mod callback;
|
||||
pub mod compositor;
|
||||
pub mod display;
|
||||
pub mod keyboard;
|
||||
pub mod output;
|
||||
pub mod pointer;
|
||||
pub mod registry;
|
||||
pub mod seat;
|
||||
pub mod shm;
|
||||
pub mod shm_buffer_backing;
|
||||
pub mod shm_pool;
|
||||
pub mod surface;
|
||||
pub mod touch;
|
||||
16
src/wayland/core/output.rs
Normal file
16
src/wayland/core/output.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use waynest::{
|
||||
server::{Client, Dispatcher, Result},
|
||||
wire::ObjectId,
|
||||
};
|
||||
|
||||
pub use waynest::server::protocol::core::wayland::wl_output::*;
|
||||
|
||||
#[derive(Debug, Dispatcher, Default)]
|
||||
pub struct Output;
|
||||
|
||||
impl WlOutput for Output {
|
||||
/// https://wayland.app/protocols/wayland#wl_output:request:release
|
||||
async fn release(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
183
src/wayland/core/pointer.rs
Normal file
183
src/wayland/core/pointer.rs
Normal file
@@ -0,0 +1,183 @@
|
||||
use crate::wayland::core::{seat::fixed_from_f32, surface::Surface};
|
||||
use mint::Vector2;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Weak;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing;
|
||||
pub use waynest::server::protocol::core::wayland::wl_pointer::*;
|
||||
use waynest::{
|
||||
server::{Client, Dispatcher, Result},
|
||||
wire::ObjectId,
|
||||
};
|
||||
|
||||
#[derive(Dispatcher)]
|
||||
pub struct Pointer {
|
||||
pub id: ObjectId,
|
||||
focused_surface: Mutex<Weak<Surface>>,
|
||||
}
|
||||
impl Pointer {
|
||||
pub fn new(id: ObjectId) -> Self {
|
||||
Self {
|
||||
id,
|
||||
focused_surface: Mutex::new(Weak::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle_pointer_motion(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
surface: Arc<Surface>,
|
||||
position: Vector2<f32>,
|
||||
) -> Result<()> {
|
||||
tracing::debug!(
|
||||
"Handling pointer motion at ({}, {})",
|
||||
position.x,
|
||||
position.y
|
||||
);
|
||||
let mut focused = self.focused_surface.lock().await;
|
||||
|
||||
// If we're entering a new surface
|
||||
if focused.as_ptr() != Arc::as_ptr(&surface) {
|
||||
tracing::debug!("Surface transition detected");
|
||||
// Send leave to old surface if it exists and is still alive
|
||||
if let Some(old_surface) = focused.upgrade() {
|
||||
let serial = client.next_event_serial();
|
||||
tracing::debug!("Sending leave event with serial {}", serial);
|
||||
self.leave(client, self.id, serial, old_surface.id).await?;
|
||||
}
|
||||
|
||||
// Send enter to new surface
|
||||
let serial = client.next_event_serial();
|
||||
tracing::debug!(
|
||||
"Sending enter event with serial {} to surface {:?}",
|
||||
serial,
|
||||
surface.id
|
||||
);
|
||||
self.enter(
|
||||
client,
|
||||
self.id,
|
||||
serial,
|
||||
surface.id,
|
||||
fixed_from_f32(position.x),
|
||||
fixed_from_f32(position.y),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Update focused surface
|
||||
*focused = Arc::downgrade(&surface);
|
||||
}
|
||||
|
||||
// Send motion event to current surface
|
||||
tracing::debug!("Sending motion event to surface");
|
||||
self.motion(
|
||||
client,
|
||||
self.id,
|
||||
0, // time
|
||||
fixed_from_f32(position.x),
|
||||
fixed_from_f32(position.y),
|
||||
)
|
||||
.await?;
|
||||
self.frame(client, self.id).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_pointer_button(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
surface: Arc<Surface>,
|
||||
button: u32,
|
||||
pressed: bool,
|
||||
) -> Result<()> {
|
||||
tracing::debug!(
|
||||
"Handling pointer button {} {} on surface {:?}",
|
||||
button,
|
||||
if pressed { "pressed" } else { "released" },
|
||||
surface.id
|
||||
);
|
||||
let serial = client.next_event_serial();
|
||||
self.button(
|
||||
client,
|
||||
self.id,
|
||||
serial,
|
||||
0, // time
|
||||
button,
|
||||
if pressed {
|
||||
ButtonState::Pressed
|
||||
} else {
|
||||
ButtonState::Released
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
self.frame(client, self.id).await
|
||||
}
|
||||
pub async fn handle_pointer_scroll(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
_surface: Arc<Surface>,
|
||||
scroll_distance: Option<Vector2<f32>>,
|
||||
scroll_steps: Option<Vector2<f32>>,
|
||||
) -> Result<()> {
|
||||
tracing::debug!(
|
||||
"Handling pointer scroll: distance={:?}, steps={:?}",
|
||||
scroll_distance,
|
||||
scroll_steps
|
||||
);
|
||||
if let Some(distance) = scroll_distance {
|
||||
self.axis(
|
||||
client,
|
||||
self.id,
|
||||
0, // time
|
||||
Axis::HorizontalScroll,
|
||||
fixed_from_f32(distance.x),
|
||||
)
|
||||
.await?;
|
||||
self.axis(
|
||||
client,
|
||||
self.id,
|
||||
0, // time
|
||||
Axis::VerticalScroll,
|
||||
fixed_from_f32(distance.y),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
if let Some(steps) = scroll_steps {
|
||||
self.axis_discrete(client, self.id, Axis::HorizontalScroll, steps.x as i32)
|
||||
.await?;
|
||||
self.axis_discrete(client, self.id, Axis::VerticalScroll, steps.y as i32)
|
||||
.await?;
|
||||
}
|
||||
self.frame(client, self.id).await
|
||||
}
|
||||
|
||||
pub async fn reset(&self, client: &mut Client) -> Result<()> {
|
||||
let mut focused = self.focused_surface.lock().await;
|
||||
if let Some(old_surface) = focused.upgrade() {
|
||||
let serial = client.next_event_serial();
|
||||
self.leave(client, self.id, serial, old_surface.id).await?;
|
||||
self.frame(client, self.id).await?;
|
||||
}
|
||||
*focused = Weak::new();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WlPointer for Pointer {
|
||||
/// https://wayland.app/protocols/wayland#wl_pointer:request:set_cursor
|
||||
async fn set_cursor(
|
||||
&self,
|
||||
_client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
_serial: u32,
|
||||
_surface: Option<ObjectId>,
|
||||
_hotspot_x: i32,
|
||||
_hotspot_y: i32,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_pointer:request:release
|
||||
async fn release(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
148
src/wayland/core/registry.rs
Normal file
148
src/wayland/core/registry.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
use crate::wayland::{
|
||||
core::{
|
||||
compositor::{Compositor, WlCompositor},
|
||||
display::Display,
|
||||
output::{Output, WlOutput},
|
||||
seat::{Seat, WlSeat},
|
||||
shm::{Shm, WlShm},
|
||||
},
|
||||
xdg::wm_base::{WmBase, XdgWmBase},
|
||||
};
|
||||
pub use waynest::server::protocol::core::wayland::wl_registry::*;
|
||||
#[cfg(feature = "dmabuf")]
|
||||
use waynest::server::protocol::stable::linux_dmabuf_v1::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1;
|
||||
use waynest::{
|
||||
server::{Client, Dispatcher, Error, Result},
|
||||
wire::{NewId, ObjectId},
|
||||
};
|
||||
|
||||
struct RegistryGlobals;
|
||||
impl RegistryGlobals {
|
||||
pub const COMPOSITOR: u32 = 0;
|
||||
pub const SHM: u32 = 1;
|
||||
pub const WM_BASE: u32 = 2;
|
||||
pub const SEAT: u32 = 3;
|
||||
pub const OUTPUT: u32 = 4;
|
||||
#[cfg(feature = "dmabuf")]
|
||||
pub const DMABUF: u32 = 5;
|
||||
}
|
||||
|
||||
#[derive(Debug, Dispatcher, Default)]
|
||||
pub struct Registry;
|
||||
|
||||
impl Registry {
|
||||
pub async fn advertise_globals(&self, client: &mut Client, sender_id: ObjectId) -> Result<()> {
|
||||
self.global(
|
||||
client,
|
||||
sender_id,
|
||||
RegistryGlobals::COMPOSITOR,
|
||||
Compositor::INTERFACE.to_string(),
|
||||
Compositor::VERSION,
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.global(
|
||||
client,
|
||||
sender_id,
|
||||
RegistryGlobals::SHM,
|
||||
Shm::INTERFACE.to_string(),
|
||||
Shm::VERSION,
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.global(
|
||||
client,
|
||||
sender_id,
|
||||
RegistryGlobals::WM_BASE,
|
||||
WmBase::INTERFACE.to_string(),
|
||||
WmBase::VERSION,
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.global(
|
||||
client,
|
||||
sender_id,
|
||||
RegistryGlobals::SEAT,
|
||||
Seat::INTERFACE.to_string(),
|
||||
Seat::VERSION,
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.global(
|
||||
client,
|
||||
sender_id,
|
||||
RegistryGlobals::OUTPUT,
|
||||
Output::INTERFACE.to_string(),
|
||||
Output::VERSION,
|
||||
)
|
||||
.await?;
|
||||
|
||||
#[cfg(feature = "dmabuf")]
|
||||
self.global(
|
||||
client,
|
||||
sender_id,
|
||||
RegistryGlobals::DMABUF,
|
||||
crate::wayland::dmabuf::Dmabuf::INTERFACE.to_string(),
|
||||
Dmabuf::VERSION,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WlRegistry for Registry {
|
||||
async fn bind(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
name: u32,
|
||||
new_id: NewId,
|
||||
) -> Result<()> {
|
||||
match name {
|
||||
RegistryGlobals::COMPOSITOR => {
|
||||
tracing::info!("Binding compositor");
|
||||
client.insert(new_id.object_id, Compositor);
|
||||
}
|
||||
RegistryGlobals::SHM => {
|
||||
tracing::info!("Binding SHM");
|
||||
let shm = client.insert(new_id.object_id, Shm);
|
||||
shm.advertise_formats(client, new_id.object_id).await?;
|
||||
}
|
||||
RegistryGlobals::WM_BASE => {
|
||||
tracing::info!("Binding WM_BASE");
|
||||
client.insert(new_id.object_id, WmBase);
|
||||
}
|
||||
RegistryGlobals::SEAT => {
|
||||
tracing::info!("Binding seat with id {}", new_id.object_id);
|
||||
let seat = client.insert(new_id.object_id, Seat::new());
|
||||
if let Some(display) = client.get::<Display>(ObjectId::DISPLAY) {
|
||||
tracing::info!("Setting seat in display");
|
||||
let _ = display.seat.set(seat.clone());
|
||||
tracing::info!("Seat set successfully");
|
||||
} else {
|
||||
tracing::warn!("No display found to set seat");
|
||||
}
|
||||
seat.advertise_capabilities(client, new_id.object_id)
|
||||
.await?;
|
||||
tracing::info!("Seat capabilities advertised");
|
||||
}
|
||||
RegistryGlobals::OUTPUT => {
|
||||
tracing::info!("Binding output");
|
||||
client.insert(new_id.object_id, Output);
|
||||
}
|
||||
#[cfg(feature = "dmabuf")]
|
||||
RegistryGlobals::DMABUF => {
|
||||
tracing::info!("Binding dmabuf");
|
||||
let dmabuf = client.insert(new_id.object_id, Dmabuf::new());
|
||||
dmabuf.send_modifiers(client, new_id.object_id).await?;
|
||||
}
|
||||
id => {
|
||||
tracing::error!(id, "Wayland: failed to bind to registry global");
|
||||
return Err(Error::MissingObject(unsafe { ObjectId::from_raw(name) }));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
191
src/wayland/core/seat.rs
Normal file
191
src/wayland/core/seat.rs
Normal file
@@ -0,0 +1,191 @@
|
||||
use crate::wayland::core::{keyboard::Keyboard, pointer::Pointer, surface::Surface, touch::Touch};
|
||||
use mint::Vector2;
|
||||
use std::sync::Arc;
|
||||
use std::sync::OnceLock;
|
||||
pub use waynest::server::protocol::core::wayland::wl_seat::*;
|
||||
use waynest::server::{Client, Dispatcher, Result};
|
||||
use waynest::wire::{Fixed, ObjectId};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SeatMessage {
|
||||
PointerMotion {
|
||||
surface: Arc<Surface>,
|
||||
position: Vector2<f32>,
|
||||
},
|
||||
PointerButton {
|
||||
surface: Arc<Surface>,
|
||||
button: u32,
|
||||
pressed: bool,
|
||||
},
|
||||
PointerScroll {
|
||||
surface: Arc<Surface>,
|
||||
scroll_distance: Option<Vector2<f32>>,
|
||||
scroll_steps: Option<Vector2<f32>>,
|
||||
},
|
||||
KeyboardKey {
|
||||
surface: Arc<Surface>,
|
||||
keymap_id: u64,
|
||||
key: u32,
|
||||
pressed: bool,
|
||||
},
|
||||
TouchDown {
|
||||
surface: Arc<Surface>,
|
||||
id: u32,
|
||||
position: Vector2<f32>,
|
||||
},
|
||||
TouchMove {
|
||||
id: u32,
|
||||
position: Vector2<f32>,
|
||||
},
|
||||
TouchUp {
|
||||
id: u32,
|
||||
},
|
||||
Reset,
|
||||
}
|
||||
|
||||
pub fn fixed_from_f32(f: f32) -> Fixed {
|
||||
unsafe { Fixed::from_raw((f * 256.0).round() as u32) }
|
||||
}
|
||||
|
||||
#[derive(Default, Dispatcher)]
|
||||
pub struct Seat {
|
||||
pointer: OnceLock<Arc<Pointer>>,
|
||||
keyboard: OnceLock<Arc<Keyboard>>,
|
||||
touch: OnceLock<Arc<Touch>>,
|
||||
}
|
||||
|
||||
impl Seat {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub async fn advertise_capabilities(&self, client: &mut Client, id: ObjectId) -> Result<()> {
|
||||
tracing::debug!("Advertising seat capabilities with id {}", id);
|
||||
let capabilities = Capability::Pointer | Capability::Keyboard | Capability::Touch;
|
||||
WlSeat::capabilities(self, client, id, capabilities).await?;
|
||||
tracing::debug!("Capabilities advertised: {:?}", capabilities);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_message(&self, client: &mut Client, message: SeatMessage) -> Result<()> {
|
||||
match message {
|
||||
SeatMessage::PointerMotion { surface, position } => {
|
||||
if let Some(pointer) = self.pointer.get() {
|
||||
pointer
|
||||
.handle_pointer_motion(client, surface, position)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
SeatMessage::PointerButton {
|
||||
surface,
|
||||
button,
|
||||
pressed,
|
||||
} => {
|
||||
if let Some(pointer) = self.pointer.get() {
|
||||
pointer
|
||||
.handle_pointer_button(client, surface, button, pressed)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
SeatMessage::PointerScroll {
|
||||
surface,
|
||||
scroll_distance,
|
||||
scroll_steps,
|
||||
} => {
|
||||
if let Some(pointer) = self.pointer.get() {
|
||||
pointer
|
||||
.handle_pointer_scroll(client, surface, scroll_distance, scroll_steps)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
SeatMessage::KeyboardKey {
|
||||
surface,
|
||||
keymap_id,
|
||||
key,
|
||||
pressed,
|
||||
} => {
|
||||
if let Some(keyboard) = self.keyboard.get() {
|
||||
keyboard
|
||||
.handle_keyboard_key(client, surface, keymap_id, key - 8, pressed)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
SeatMessage::TouchDown {
|
||||
surface,
|
||||
id,
|
||||
position,
|
||||
} => {
|
||||
if let Some(touch) = self.touch.get() {
|
||||
touch
|
||||
.handle_touch_down(client, surface, id, position)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
SeatMessage::TouchMove { id, position } => {
|
||||
if let Some(touch) = self.touch.get() {
|
||||
touch.handle_touch_move(client, id, position).await?;
|
||||
}
|
||||
}
|
||||
SeatMessage::TouchUp { id } => {
|
||||
if let Some(touch) = self.touch.get() {
|
||||
touch.handle_touch_up(client, id).await?;
|
||||
}
|
||||
}
|
||||
SeatMessage::Reset => {
|
||||
if let Some(pointer) = self.pointer.get() {
|
||||
pointer.reset(client).await?;
|
||||
}
|
||||
if let Some(keyboard) = self.keyboard.get() {
|
||||
keyboard.reset(client).await?;
|
||||
}
|
||||
if let Some(touch) = self.touch.get() {
|
||||
touch.reset(client).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl WlSeat for Seat {
|
||||
/// https://wayland.app/protocols/wayland#wl_seat:request:get_pointer
|
||||
async fn get_pointer(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
id: ObjectId,
|
||||
) -> Result<()> {
|
||||
let pointer = client.insert(id, Pointer::new(id));
|
||||
let _ = self.pointer.set(pointer);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_seat:request:get_keyboard
|
||||
async fn get_keyboard(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
id: ObjectId,
|
||||
) -> Result<()> {
|
||||
tracing::info!("Getting keyboard");
|
||||
let keyboard = client.insert(id, Keyboard::new(id));
|
||||
let _ = self.keyboard.set(keyboard);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_seat:request:get_touch
|
||||
async fn get_touch(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
id: ObjectId,
|
||||
) -> Result<()> {
|
||||
let touch = client.insert(id, Touch(id));
|
||||
let _ = self.touch.set(touch);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_seat:request:release
|
||||
async fn release(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
39
src/wayland/core/shm.rs
Normal file
39
src/wayland/core/shm.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use std::os::fd::OwnedFd;
|
||||
|
||||
use crate::wayland::core::shm_pool::ShmPool;
|
||||
pub use waynest::server::protocol::core::wayland::wl_shm::*;
|
||||
use waynest::{
|
||||
server::{Client, Dispatcher, Result},
|
||||
wire::ObjectId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Dispatcher, Default)]
|
||||
pub struct Shm;
|
||||
impl Shm {
|
||||
pub async fn advertise_formats(&self, client: &mut Client, sender_id: ObjectId) -> Result<()> {
|
||||
self.format(client, sender_id, Format::Argb8888).await?;
|
||||
self.format(client, sender_id, Format::Xrgb8888).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl WlShm for Shm {
|
||||
/// https://wayland.app/protocols/wayland#wl_shm:request:create_pool
|
||||
async fn create_pool(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
pool_id: ObjectId,
|
||||
fd: OwnedFd,
|
||||
size: i32,
|
||||
) -> Result<()> {
|
||||
client.insert(pool_id, ShmPool::new(fd, size)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_shm:request:release
|
||||
async fn release(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
102
src/wayland/core/shm_buffer_backing.rs
Normal file
102
src/wayland/core/shm_buffer_backing.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use super::shm_pool::ShmPool;
|
||||
use bevy::{
|
||||
asset::{Assets, Handle, RenderAssetUsages},
|
||||
image::Image,
|
||||
render::render_resource::{Extent3d, TextureDimension, TextureFormat},
|
||||
};
|
||||
use mint::Vector2;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use waynest::server::protocol::core::wayland::wl_shm::Format;
|
||||
|
||||
/// Parameters for a shared memory buffer
|
||||
#[derive(Debug)]
|
||||
pub struct ShmBufferBacking {
|
||||
pool: Arc<ShmPool>,
|
||||
offset: usize,
|
||||
stride: usize,
|
||||
size: Vector2<usize>,
|
||||
format: Format,
|
||||
image: OnceLock<Handle<Image>>,
|
||||
}
|
||||
|
||||
impl ShmBufferBacking {
|
||||
pub fn new(
|
||||
pool: Arc<ShmPool>,
|
||||
offset: usize,
|
||||
stride: usize,
|
||||
size: Vector2<usize>,
|
||||
format: Format,
|
||||
) -> Self {
|
||||
Self {
|
||||
pool,
|
||||
offset,
|
||||
stride,
|
||||
size,
|
||||
format,
|
||||
image: OnceLock::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_tex(&self, images: &mut Assets<Image>) -> Option<Handle<Image>> {
|
||||
let image = self.image.get_or_init(|| {
|
||||
let image = Image::new_fill(
|
||||
Extent3d {
|
||||
width: self.size.x as u32,
|
||||
height: self.size.y as u32,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
TextureDimension::D2,
|
||||
&[255, 0, 255, 255],
|
||||
TextureFormat::Rgba8Unorm,
|
||||
RenderAssetUsages::all(),
|
||||
);
|
||||
images.add(image)
|
||||
});
|
||||
|
||||
let src_data_lock = self.pool.data_lock();
|
||||
let mut src_cursor = self.offset;
|
||||
|
||||
// Calculate maximum cursor position needed - stride is already in bytes
|
||||
let max_cursor = self.offset + (self.size.y * self.stride);
|
||||
|
||||
// Check if we have enough data
|
||||
if max_cursor > src_data_lock.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let dst_data = images.get_mut(image).unwrap().data.get_or_insert_with(|| {
|
||||
let length = self.size.x as usize * self.size.y as usize * 4;
|
||||
vec![255; length]
|
||||
});
|
||||
let mut dst_cursor = 0;
|
||||
|
||||
for _y in 0..self.size.y {
|
||||
for _x in 0..self.size.x {
|
||||
match self.format {
|
||||
Format::Xrgb8888 => {
|
||||
dst_data[dst_cursor] = src_data_lock[src_cursor + 2]; // Red is byte 2
|
||||
dst_data[dst_cursor + 1] = src_data_lock[src_cursor + 1]; // Green is byte 1
|
||||
dst_data[dst_cursor + 2] = src_data_lock[src_cursor]; // Blue is byte 0
|
||||
dst_data[dst_cursor + 3] = 255; // X means ignore alpha, treat as fully opaque
|
||||
}
|
||||
Format::Argb8888 => {
|
||||
dst_data[dst_cursor] = src_data_lock[src_cursor + 2]; // Red is byte 2
|
||||
dst_data[dst_cursor + 1] = src_data_lock[src_cursor + 1]; // Green is byte 1
|
||||
dst_data[dst_cursor + 2] = src_data_lock[src_cursor]; // Blue is byte 0
|
||||
dst_data[dst_cursor + 3] = src_data_lock[src_cursor + 3]; // Alpha is byte 3
|
||||
}
|
||||
_ => panic!("Unsupported format {:?}", self.format),
|
||||
}
|
||||
src_cursor += 4;
|
||||
dst_cursor += 4;
|
||||
}
|
||||
src_cursor += self.stride - (self.size.x * 4);
|
||||
}
|
||||
|
||||
Some(image.clone())
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Vector2<usize> {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
74
src/wayland/core/shm_pool.rs
Normal file
74
src/wayland/core/shm_pool.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use memmap2::{MmapOptions, RemapOptions};
|
||||
use parking_lot::{Mutex, MutexGuard, RawMutex, lock_api::MappedMutexGuard};
|
||||
use std::os::fd::{IntoRawFd, OwnedFd};
|
||||
use waynest::{
|
||||
server::{Client, Dispatcher, Result, protocol::core::wayland::wl_shm::Format},
|
||||
wire::ObjectId,
|
||||
};
|
||||
|
||||
use crate::wayland::core::buffer::{Buffer, BufferBacking};
|
||||
|
||||
pub use waynest::server::protocol::core::wayland::wl_shm_pool::*;
|
||||
|
||||
use super::shm_buffer_backing::ShmBufferBacking;
|
||||
|
||||
#[derive(Debug, Dispatcher)]
|
||||
pub struct ShmPool {
|
||||
inner: Mutex<memmap2::MmapMut>,
|
||||
}
|
||||
|
||||
impl ShmPool {
|
||||
pub fn new(fd: OwnedFd, size: i32) -> Result<Self> {
|
||||
let map = unsafe {
|
||||
MmapOptions::new()
|
||||
.len(size as usize)
|
||||
.map_mut(fd.into_raw_fd())?
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
inner: Mutex::new(map),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn data_lock(&self) -> MappedMutexGuard<RawMutex, [u8]> {
|
||||
MutexGuard::map(self.inner.lock(), |i| i.as_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl WlShmPool for ShmPool {
|
||||
/// https://wayland.app/protocols/wayland#wl_shm_pool:request:create_buffer
|
||||
async fn create_buffer(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
sender_id: ObjectId,
|
||||
id: ObjectId,
|
||||
offset: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
format: Format,
|
||||
) -> Result<()> {
|
||||
let params = ShmBufferBacking::new(
|
||||
client.get::<ShmPool>(sender_id).unwrap(),
|
||||
offset as usize,
|
||||
stride as usize,
|
||||
[width as usize, height as usize].into(),
|
||||
format,
|
||||
);
|
||||
|
||||
Buffer::new(client, id, BufferBacking::Shm(params));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_shm_pool:request:resize
|
||||
async fn resize(&self, _client: &mut Client, _sender_id: ObjectId, size: i32) -> Result<()> {
|
||||
let mut inner = self.inner.lock();
|
||||
unsafe { inner.remap(size as usize, RemapOptions::new().may_move(true))? };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_shm_pool:request:destroy
|
||||
async fn destroy(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
367
src/wayland/core/surface.rs
Normal file
367
src/wayland/core/surface.rs
Normal file
@@ -0,0 +1,367 @@
|
||||
use super::{buffer::Buffer, callback::Callback};
|
||||
use crate::{
|
||||
BevyMaterial,
|
||||
core::registry::Registry,
|
||||
nodes::{drawable::model::ModelPart, items::panel::Geometry},
|
||||
wayland::{
|
||||
Message, MessageSink,
|
||||
util::{ClientExt, DoubleBuffer},
|
||||
xdg::{popup::Popup, toplevel::Toplevel},
|
||||
},
|
||||
};
|
||||
use bevy::{
|
||||
asset::{Assets, Handle},
|
||||
image::Image,
|
||||
};
|
||||
use mint::Vector2;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use waynest::{
|
||||
server::{
|
||||
Client, Dispatcher, Result,
|
||||
protocol::core::wayland::{wl_output::Transform, wl_surface::*},
|
||||
},
|
||||
wire::ObjectId,
|
||||
};
|
||||
|
||||
pub static WL_SURFACE_REGISTRY: Registry<Surface> = Registry::new();
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SurfaceRole {
|
||||
XdgToplevel(Arc<Toplevel>),
|
||||
XDGPopup(Arc<Popup>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SurfaceState {
|
||||
pub buffer: Option<Arc<Buffer>>,
|
||||
pub density: f32,
|
||||
pub geometry: Option<Geometry>,
|
||||
pub min_size: Option<Vector2<u32>>,
|
||||
pub max_size: Option<Vector2<u32>>,
|
||||
clean_lock: OnceLock<()>,
|
||||
}
|
||||
impl Default for SurfaceState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
buffer: Default::default(),
|
||||
density: 1.0,
|
||||
geometry: None,
|
||||
min_size: None,
|
||||
max_size: None,
|
||||
clean_lock: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if returning false, don't run this callback again... just remove it
|
||||
pub type OnCommitCallback = Box<dyn Fn(&Surface, &SurfaceState) -> bool + Send + Sync>;
|
||||
|
||||
#[derive(Dispatcher)]
|
||||
pub struct Surface {
|
||||
pub id: ObjectId,
|
||||
state: Mutex<DoubleBuffer<SurfaceState>>,
|
||||
pub message_sink: MessageSink,
|
||||
pub role: Mutex<Option<SurfaceRole>>,
|
||||
frame_callback_object: Mutex<Option<Arc<Callback>>>,
|
||||
on_commit_handlers: Mutex<Vec<OnCommitCallback>>,
|
||||
material: OnceLock<Handle<BevyMaterial>>,
|
||||
pending_material_applications: Registry<ModelPart>,
|
||||
}
|
||||
impl std::fmt::Debug for Surface {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Surface")
|
||||
.field("state", &self.state)
|
||||
.field("message_sink", &self.message_sink)
|
||||
.field("role", &self.role)
|
||||
.field("frame_callback_object", &self.frame_callback_object)
|
||||
.field(
|
||||
"on_commit_handlers",
|
||||
&format!("<{} handlers>", self.on_commit_handlers.lock().len()),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
impl Surface {
|
||||
pub fn new(client: &Client, id: ObjectId) -> Self {
|
||||
Surface {
|
||||
id,
|
||||
state: Default::default(),
|
||||
message_sink: client.message_sink(),
|
||||
role: Mutex::new(None),
|
||||
frame_callback_object: Default::default(),
|
||||
on_commit_handlers: Mutex::new(Vec::new()),
|
||||
material: OnceLock::new(),
|
||||
pending_material_applications: Registry::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pending_state(&self) -> parking_lot::MutexGuard<'_, DoubleBuffer<SurfaceState>> {
|
||||
self.state.lock()
|
||||
}
|
||||
|
||||
pub fn add_commit_handler<F: Fn(&Surface, &SurfaceState) -> bool + Send + Sync + 'static>(
|
||||
&self,
|
||||
handler: F,
|
||||
) {
|
||||
let mut handlers = self.on_commit_handlers.lock();
|
||||
handlers.push(Box::new(handler));
|
||||
}
|
||||
|
||||
pub fn update_graphics(
|
||||
&self,
|
||||
materials: &mut Assets<BevyMaterial>,
|
||||
images: &mut Assets<Image>,
|
||||
) {
|
||||
let state_lock = self.state.lock();
|
||||
if state_lock.current().clean_lock.get().is_some() {
|
||||
// then we don't need to reupload the texture
|
||||
return;
|
||||
}
|
||||
let Some(buffer) = state_lock.current().buffer.clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let material = self.material.get_or_init(|| {
|
||||
// // Set default shader parameters
|
||||
// let mut params = mat_wrapper.0.get_all_param_info();
|
||||
// params.set_vec2("uv_scale", stereokit_rust::maths::Vec2::new(1.0, 1.0));
|
||||
// params.set_vec2("uv_offset", stereokit_rust::maths::Vec2::new(0.0, 0.0));
|
||||
// params.set_float("fcFactor", 1.0);
|
||||
// params.set_float("ripple", 4.0);
|
||||
// params.set_float("alpha_min", 0.0);
|
||||
// params.set_float("alpha_max", 1.0);
|
||||
|
||||
let material = BevyMaterial::default();
|
||||
|
||||
materials.add(material)
|
||||
});
|
||||
|
||||
if let Some(new_tex) = buffer.update_tex(images) {
|
||||
materials
|
||||
.get_mut(material)
|
||||
.unwrap()
|
||||
.diffuse_texture
|
||||
.replace(new_tex);
|
||||
|
||||
// For SHM buffers, we can release immediately after copying to GPU
|
||||
if buffer.can_release_after_update() {
|
||||
let _ = self.message_sink.send(Message::ReleaseBuffer(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
self.apply_surface_materials();
|
||||
let _ = state_lock.current().clean_lock.set(());
|
||||
}
|
||||
|
||||
pub fn apply_material(&self, model_part: &Arc<ModelPart>) {
|
||||
// tracing::info!("uwu applying material");
|
||||
self.pending_material_applications.add_raw(model_part)
|
||||
}
|
||||
|
||||
fn apply_surface_materials(&self) {
|
||||
let Some(mat) = self.material.get() else {
|
||||
return;
|
||||
};
|
||||
|
||||
for model_node in self.pending_material_applications.get_valid_contents() {
|
||||
model_node.replace_material(mat.clone());
|
||||
}
|
||||
self.pending_material_applications.clear();
|
||||
}
|
||||
fn mark_dirty(&self) {
|
||||
self.state.lock().pending.clean_lock = Default::default();
|
||||
}
|
||||
pub fn current_state(&self) -> SurfaceState {
|
||||
self.state.lock().current().clone()
|
||||
}
|
||||
pub fn frame_event(&self) {
|
||||
if let Some(callback_obj) = self.frame_callback_object.lock().take() {
|
||||
let _ = self.message_sink.send(Message::Frame(callback_obj));
|
||||
}
|
||||
}
|
||||
// pub fn size(&self) -> Option<Vector2<u32>> {
|
||||
// self.state
|
||||
// .lock()
|
||||
// .current()
|
||||
// .buffer
|
||||
// .as_ref()
|
||||
// .map(|b| [b.size.x as u32, b.size.y as u32].into())
|
||||
// }
|
||||
|
||||
// pub async fn release_old_buffer(&self, client: &mut Client) -> Result<()> {
|
||||
// let (old_buffer, object) = {
|
||||
// let lock = self.state.lock();
|
||||
|
||||
// let Some(old_buffer) = lock.current().buffer.clone() else {
|
||||
// return Ok(());
|
||||
// };
|
||||
// let new_buffer = lock.pending.buffer.as_ref();
|
||||
// if new_buffer.map(Arc::as_ptr) == Some(Arc::as_ptr(&old_buffer)) {
|
||||
// return Ok(());
|
||||
// }
|
||||
// drop(lock);
|
||||
|
||||
// (old_buffer.clone(), old_buffer.id)
|
||||
// };
|
||||
// old_buffer.release(client, object).await?;
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
}
|
||||
impl WlSurface for Surface {
|
||||
/// https://wayland.app/protocols/wayland#wl_surface:request:attach
|
||||
async fn attach(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
buffer: Option<ObjectId>,
|
||||
_x: i32,
|
||||
_y: i32,
|
||||
) -> Result<()> {
|
||||
self.state.lock().pending.buffer = buffer.and_then(|b| client.get::<Buffer>(b));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_surface:request:damage
|
||||
async fn damage(
|
||||
&self,
|
||||
_client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
_x: i32,
|
||||
_y: i32,
|
||||
_width: i32,
|
||||
_height: i32,
|
||||
) -> Result<()> {
|
||||
// should be more intelligent about this but for now just make it copy everything to gpu next frame again
|
||||
self.mark_dirty();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_surface:request:frame
|
||||
async fn frame(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
callback_id: ObjectId,
|
||||
) -> Result<()> {
|
||||
let callback = client.insert(callback_id, Callback(callback_id));
|
||||
self.frame_callback_object.lock().replace(callback);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_surface:request:set_opaque_region
|
||||
async fn set_opaque_region(
|
||||
&self,
|
||||
_client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
_region: Option<ObjectId>,
|
||||
) -> Result<()> {
|
||||
// nothing we can really do to repaint behind this so ignore it
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_surface:request:set_input_region
|
||||
async fn set_input_region(
|
||||
&self,
|
||||
_client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
_region: Option<ObjectId>,
|
||||
) -> Result<()> {
|
||||
// too complicated to implement this for now so who the hell cares
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_surface:request:commit
|
||||
async fn commit(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
|
||||
{
|
||||
let mut lock = self.state.lock();
|
||||
|
||||
// If we're getting a new buffer and the current one is DMA-BUF, release it
|
||||
if let Some(new_buffer) = &lock.pending.buffer {
|
||||
if let Some(current_buffer) = &lock.current().buffer {
|
||||
// Don't release if it's the same buffer being reused
|
||||
if !Arc::ptr_eq(new_buffer, current_buffer)
|
||||
&& !current_buffer.can_release_after_update()
|
||||
{
|
||||
let _ = self
|
||||
.message_sink
|
||||
.send(Message::ReleaseBuffer(current_buffer.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dirty = lock.current().clean_lock.get().is_none()
|
||||
|| lock.pending.clean_lock.take().is_none();
|
||||
lock.apply();
|
||||
|
||||
if !dirty {
|
||||
let _ = lock.current().clean_lock.set(());
|
||||
}
|
||||
}
|
||||
|
||||
let current_state = self.current_state();
|
||||
let mut handlers = self.on_commit_handlers.lock();
|
||||
handlers.retain(|f| (f)(self, ¤t_state));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_surface:request:set_buffer_transform
|
||||
async fn set_buffer_transform(
|
||||
&self,
|
||||
_client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
_transform: Transform,
|
||||
) -> Result<()> {
|
||||
// we just don't have the output transform or fullscreen at all so this optimization is never needed
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_surface:request:set_buffer_scale
|
||||
async fn set_buffer_scale(
|
||||
&self,
|
||||
_client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
scale: i32,
|
||||
) -> Result<()> {
|
||||
self.state.lock().pending.density = scale as f32;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_surface:request:damage_buffer
|
||||
async fn damage_buffer(
|
||||
&self,
|
||||
_client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
_x: i32,
|
||||
_y: i32,
|
||||
_width: i32,
|
||||
_height: i32,
|
||||
) -> Result<()> {
|
||||
// we should upload only chunks to the gpu and do subimage copy but that's a lot rn so we won't
|
||||
self.mark_dirty();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_surface:request:offset
|
||||
async fn offset(
|
||||
&self,
|
||||
_client: &mut Client,
|
||||
_sender_id: ObjectId,
|
||||
_x: i32,
|
||||
_y: i32,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://wayland.app/protocols/wayland#wl_surface:request:destroy
|
||||
async fn destroy(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Surface {
|
||||
fn drop(&mut self) {
|
||||
self.role.lock().take();
|
||||
}
|
||||
}
|
||||
75
src/wayland/core/touch.rs
Normal file
75
src/wayland/core/touch.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use crate::wayland::core::surface::Surface;
|
||||
use mint::Vector2;
|
||||
use std::sync::Arc;
|
||||
pub use waynest::server::protocol::core::wayland::wl_touch::*;
|
||||
use waynest::{
|
||||
server::{Client, Dispatcher, Result},
|
||||
wire::ObjectId,
|
||||
};
|
||||
|
||||
use super::seat::fixed_from_f32;
|
||||
|
||||
#[derive(Debug, Dispatcher)]
|
||||
pub struct Touch(pub ObjectId);
|
||||
impl Touch {
|
||||
pub async fn handle_touch_down(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
surface: Arc<Surface>,
|
||||
id: u32,
|
||||
position: Vector2<f32>,
|
||||
) -> Result<()> {
|
||||
let serial = client.next_event_serial();
|
||||
self.down(
|
||||
client,
|
||||
self.0,
|
||||
serial,
|
||||
0,
|
||||
surface.id,
|
||||
id as i32,
|
||||
fixed_from_f32(position.x),
|
||||
fixed_from_f32(position.y),
|
||||
)
|
||||
.await?;
|
||||
self.frame(client, self.0).await
|
||||
}
|
||||
|
||||
pub async fn handle_touch_move(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
id: u32,
|
||||
position: Vector2<f32>,
|
||||
) -> Result<()> {
|
||||
self.motion(
|
||||
client,
|
||||
self.0,
|
||||
0,
|
||||
id as i32,
|
||||
fixed_from_f32(position.x),
|
||||
fixed_from_f32(position.y),
|
||||
)
|
||||
.await?;
|
||||
self.frame(client, self.0).await
|
||||
}
|
||||
|
||||
pub async fn handle_touch_up(&self, client: &mut Client, id: u32) -> Result<()> {
|
||||
let serial = client.next_event_serial();
|
||||
self.up(client, self.0, serial, 0, id as i32).await?;
|
||||
self.frame(client, self.0).await
|
||||
}
|
||||
|
||||
pub async fn reset(&self, client: &mut Client) -> Result<()> {
|
||||
self.frame(client, self.0).await
|
||||
}
|
||||
}
|
||||
|
||||
impl WlTouch for Touch {
|
||||
/// https://wayland.app/protocols/wayland#wl_touch:request:release
|
||||
async fn release(
|
||||
&self,
|
||||
_client: &mut waynest::server::Client,
|
||||
_sender_id: waynest::wire::ObjectId,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user