chore(wayland): update waynest
Signed-off-by: Schmarni <marnistromer@gmail.com>
This commit is contained in:
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -5515,6 +5515,7 @@ dependencies = [
|
||||
"dashmap",
|
||||
"directories",
|
||||
"drm-fourcc",
|
||||
"futures-sink",
|
||||
"glam",
|
||||
"global_counter",
|
||||
"input-event-codes",
|
||||
@@ -5525,6 +5526,7 @@ dependencies = [
|
||||
"nanoid",
|
||||
"openxr",
|
||||
"parking_lot 0.12.4",
|
||||
"pin-project-lite",
|
||||
"rand 0.9.2",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustix 1.1.2",
|
||||
@@ -6587,7 +6589,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "waynest"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/verdiwm/waynest.git#cbbc19ed20ba5610fcc5577375d6b743d615da31"
|
||||
source = "git+https://github.com/verdiwm/waynest.git#5169fdb3d96a09d0c65c2f73397f0576948b14a5"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -6601,7 +6603,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "waynest-macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/verdiwm/waynest.git#cbbc19ed20ba5610fcc5577375d6b743d615da31"
|
||||
source = "git+https://github.com/verdiwm/waynest.git#5169fdb3d96a09d0c65c2f73397f0576948b14a5"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"quote",
|
||||
@@ -6611,7 +6613,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "waynest-protocols"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/verdiwm/waynest.git#cbbc19ed20ba5610fcc5577375d6b743d615da31"
|
||||
source = "git+https://github.com/verdiwm/waynest.git#5169fdb3d96a09d0c65c2f73397f0576948b14a5"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"futures-core",
|
||||
@@ -6624,7 +6626,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "waynest-server"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/verdiwm/waynest.git#cbbc19ed20ba5610fcc5577375d6b743d615da31"
|
||||
source = "git+https://github.com/verdiwm/waynest.git#5169fdb3d96a09d0c65c2f73397f0576948b14a5"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures-core",
|
||||
|
||||
@@ -141,7 +141,10 @@ openxr = "0.19"
|
||||
|
||||
# linux stuffs
|
||||
input-event-codes = "6.2.0"
|
||||
zbus = { version = "5.11.0", features = ["blocking-api", "tokio"], default-features = false }
|
||||
zbus = { version = "5.11.0", features = [
|
||||
"blocking-api",
|
||||
"tokio",
|
||||
], default-features = false }
|
||||
directories = "6.0.0"
|
||||
xkbcommon-rs = "0.1.0"
|
||||
cosmic-text = "0.14.2"
|
||||
@@ -163,6 +166,8 @@ vulkano = { git = "https://github.com/Schmarni-Dev/vulkano", branch = "0_35_dmab
|
||||
wgpu-hal = { version = "24", optional = true, features = ["vulkan"] }
|
||||
ash = { version = "0.38.0", optional = true, default-features = false }
|
||||
rustix = { version = "1.0.8", features = ["time"] }
|
||||
pin-project-lite = "0.2.16"
|
||||
futures-sink = "0.3.31"
|
||||
|
||||
[dependencies.stardust-xr]
|
||||
workspace = true
|
||||
|
||||
@@ -10,7 +10,7 @@ use mint::Vector2;
|
||||
use std::sync::Arc;
|
||||
use waynest::ObjectId;
|
||||
pub use waynest_protocols::server::core::wayland::wl_buffer::*;
|
||||
use waynest_server::RequestDispatcher;
|
||||
use waynest_server::{Client as _, RequestDispatcher};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BufferUsage {
|
||||
@@ -40,7 +40,7 @@ pub enum BufferBacking {
|
||||
}
|
||||
|
||||
#[derive(Debug, RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Buffer {
|
||||
pub id: ObjectId,
|
||||
backing: BufferBacking,
|
||||
@@ -48,8 +48,12 @@ pub struct Buffer {
|
||||
|
||||
impl Buffer {
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
pub fn new(client: &mut Client, id: ObjectId, backing: BufferBacking) -> Arc<Self> {
|
||||
client.insert(id, Self { id, backing })
|
||||
pub fn new(
|
||||
client: &mut Client,
|
||||
id: ObjectId,
|
||||
backing: BufferBacking,
|
||||
) -> WaylandResult<Arc<Self>> {
|
||||
Ok(client.insert(id, Self { id, backing })?)
|
||||
}
|
||||
|
||||
/// Returns the tex if it was updated
|
||||
|
||||
@@ -3,7 +3,7 @@ pub use waynest_protocols::server::core::wayland::wl_callback::*;
|
||||
use waynest_server::RequestDispatcher;
|
||||
|
||||
#[derive(Debug, RequestDispatcher, Clone)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Callback(pub ObjectId);
|
||||
/// https://wayland.app/protocols/wayland#wl_callback
|
||||
impl WlCallback for Callback {
|
||||
|
||||
@@ -4,10 +4,10 @@ use crate::wayland::{core::surface::Surface, util::ClientExt};
|
||||
use waynest::ObjectId;
|
||||
use waynest_protocols::server::core::wayland::wl_surface::WlSurface;
|
||||
pub use waynest_protocols::server::core::wayland::{wl_compositor::*, wl_region::*};
|
||||
use waynest_server::RequestDispatcher;
|
||||
use waynest_server::{Client as _, RequestDispatcher};
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher, Default)]
|
||||
#[waynest(error = WaylandError)]
|
||||
#[waynest(error = WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Compositor;
|
||||
impl WlCompositor for Compositor {
|
||||
type Connection = crate::wayland::Client;
|
||||
@@ -19,7 +19,7 @@ impl WlCompositor for Compositor {
|
||||
_sender_id: ObjectId,
|
||||
id: ObjectId,
|
||||
) -> WaylandResult<()> {
|
||||
let surface = client.insert(id, Surface::new(client, id));
|
||||
let surface = client.insert(id, Surface::new(client, id))?;
|
||||
if let Some(output) = client.display().output.get() {
|
||||
surface.enter(client, id, output.id).await?;
|
||||
}
|
||||
@@ -35,13 +35,13 @@ impl WlCompositor for Compositor {
|
||||
_sender_id: ObjectId,
|
||||
id: ObjectId,
|
||||
) -> WaylandResult<()> {
|
||||
client.insert(id, Region { id });
|
||||
client.insert(id, Region { id })?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, RequestDispatcher)]
|
||||
#[waynest(error = WaylandError)]
|
||||
#[waynest(error = WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Region {
|
||||
id: ObjectId,
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ use waynest::ObjectId;
|
||||
use waynest_protocols::server::core::wayland::{
|
||||
wl_data_device::*, wl_data_device_manager::*, wl_data_offer::WlDataOffer, wl_data_source::*,
|
||||
};
|
||||
use waynest_server::Client as _;
|
||||
|
||||
// TODO: actually implement this
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct DataDeviceManager;
|
||||
impl WlDataDeviceManager for DataDeviceManager {
|
||||
type Connection = Client;
|
||||
@@ -19,7 +20,7 @@ impl WlDataDeviceManager for DataDeviceManager {
|
||||
_sender_id: ObjectId,
|
||||
id: ObjectId,
|
||||
) -> WaylandResult<()> {
|
||||
client.insert(id, DataSource { id });
|
||||
client.insert(id, DataSource { id })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -30,29 +31,19 @@ impl WlDataDeviceManager for DataDeviceManager {
|
||||
id: ObjectId,
|
||||
_seat: ObjectId,
|
||||
) -> WaylandResult<()> {
|
||||
client.insert(id, DataDevice);
|
||||
client.insert(id, DataDevice)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct DataSource {
|
||||
id: ObjectId,
|
||||
}
|
||||
impl WlDataSource for DataSource {
|
||||
type Connection = Client;
|
||||
|
||||
async fn send(
|
||||
&self,
|
||||
_client: &mut Self::Connection,
|
||||
_sender_id: ObjectId,
|
||||
_mime_type: String,
|
||||
_fd: OwnedFd,
|
||||
) -> WaylandResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn offer(
|
||||
&self,
|
||||
_client: &mut Self::Connection,
|
||||
@@ -82,7 +73,7 @@ impl WlDataSource for DataSource {
|
||||
}
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct DataDevice;
|
||||
impl WlDataDevice for DataDevice {
|
||||
type Connection = Client;
|
||||
@@ -119,7 +110,7 @@ impl WlDataDevice for DataDevice {
|
||||
}
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct DataOffer {
|
||||
id: ObjectId,
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::{
|
||||
collections::HashSet,
|
||||
io::Write,
|
||||
os::{
|
||||
fd::IntoRawFd,
|
||||
fd::{AsFd, IntoRawFd},
|
||||
unix::io::{FromRawFd, OwnedFd},
|
||||
},
|
||||
sync::{Arc, Weak},
|
||||
@@ -68,7 +68,7 @@ impl ModifierState {
|
||||
}
|
||||
|
||||
#[derive(waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Keyboard {
|
||||
pub id: ObjectId,
|
||||
focused_surface: Mutex<Weak<Surface>>,
|
||||
@@ -103,7 +103,7 @@ impl Keyboard {
|
||||
client,
|
||||
self.id,
|
||||
KeymapFormat::XkbV1,
|
||||
fd,
|
||||
fd.as_fd(),
|
||||
keymap.len() as u32,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -3,7 +3,7 @@ use waynest::ObjectId;
|
||||
pub use waynest_protocols::server::core::wayland::wl_output::*;
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Output {
|
||||
pub id: ObjectId,
|
||||
pub version: u32,
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::nodes::items::panel::Geometry;
|
||||
use crate::wayland::core::surface::Surface;
|
||||
use crate::wayland::{Client, WaylandResult};
|
||||
use mint::Vector2;
|
||||
use waynest_server::Client as _;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Weak;
|
||||
use tokio::sync::Mutex;
|
||||
@@ -12,7 +13,7 @@ use waynest::ObjectId;
|
||||
pub use waynest_protocols::server::core::wayland::wl_pointer::*;
|
||||
|
||||
#[derive(waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Pointer {
|
||||
pub id: ObjectId,
|
||||
version: u32,
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::wayland::Client;
|
||||
use crate::wayland::WaylandResult;
|
||||
use crate::wayland::core::{keyboard::Keyboard, pointer::Pointer, surface::Surface, touch::Touch};
|
||||
use mint::Vector2;
|
||||
use waynest_server::Client as _;
|
||||
use std::sync::Arc;
|
||||
use std::sync::OnceLock;
|
||||
use waynest::ObjectId;
|
||||
@@ -45,7 +46,7 @@ pub enum SeatMessage {
|
||||
}
|
||||
|
||||
#[derive(Default, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Seat {
|
||||
version: u32,
|
||||
pointer: OnceLock<Arc<Pointer>>,
|
||||
@@ -171,7 +172,7 @@ impl WlSeat for Seat {
|
||||
_sender_id: ObjectId,
|
||||
id: ObjectId,
|
||||
) -> WaylandResult<()> {
|
||||
let pointer = client.insert(id, Pointer::new(id, self.version));
|
||||
let pointer = client.insert(id, Pointer::new(id, self.version))?;
|
||||
let _ = self.pointer.set(pointer);
|
||||
Ok(())
|
||||
}
|
||||
@@ -184,7 +185,7 @@ impl WlSeat for Seat {
|
||||
id: ObjectId,
|
||||
) -> WaylandResult<()> {
|
||||
tracing::info!("Getting keyboard");
|
||||
let keyboard = client.insert(id, Keyboard::new(id));
|
||||
let keyboard = client.insert(id, Keyboard::new(id))?;
|
||||
let _ = self.keyboard.set(keyboard);
|
||||
Ok(())
|
||||
}
|
||||
@@ -196,7 +197,7 @@ impl WlSeat for Seat {
|
||||
_sender_id: ObjectId,
|
||||
id: ObjectId,
|
||||
) -> WaylandResult<()> {
|
||||
let touch = client.insert(id, Touch(id));
|
||||
let touch = client.insert(id, Touch(id))?;
|
||||
let _ = self.touch.set(touch);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ use crate::wayland::{Client, WaylandResult, core::shm_pool::ShmPool};
|
||||
use std::os::fd::OwnedFd;
|
||||
use waynest::ObjectId;
|
||||
pub use waynest_protocols::server::core::wayland::wl_shm::*;
|
||||
use waynest_server::Client as _;
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher, Default)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Shm;
|
||||
impl Shm {
|
||||
pub async fn advertise_formats(
|
||||
@@ -30,7 +31,7 @@ impl WlShm for Shm {
|
||||
fd: OwnedFd,
|
||||
size: i32,
|
||||
) -> WaylandResult<()> {
|
||||
client.insert(pool_id, ShmPool::new(fd, size, pool_id)?);
|
||||
client.insert(pool_id, ShmPool::new(fd, size, pool_id)?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,13 +5,14 @@ use crate::wayland::{
|
||||
};
|
||||
use memmap2::{MmapOptions, RemapOptions};
|
||||
use parking_lot::{Mutex, MutexGuard, RawMutex, lock_api::MappedMutexGuard};
|
||||
use waynest_server::Client as _;
|
||||
use std::os::fd::{IntoRawFd, OwnedFd};
|
||||
use waynest::ObjectId;
|
||||
use waynest_protocols::server::core::wayland::wl_shm::Format;
|
||||
pub use waynest_protocols::server::core::wayland::wl_shm_pool::*;
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct ShmPool {
|
||||
inner: Mutex<memmap2::MmapMut>,
|
||||
id: ObjectId,
|
||||
@@ -62,7 +63,7 @@ impl WlShmPool for ShmPool {
|
||||
format,
|
||||
);
|
||||
|
||||
Buffer::new(client, id, BufferBacking::Shm(params));
|
||||
Buffer::new(client, id, BufferBacking::Shm(params))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ use bevy::{
|
||||
use bevy_dmabuf::import::ImportedDmatexs;
|
||||
use mint::Vector2;
|
||||
use parking_lot::Mutex;
|
||||
use waynest_server::Client as _;
|
||||
use std::{
|
||||
fmt::Display,
|
||||
sync::{Arc, OnceLock, Weak},
|
||||
@@ -90,7 +91,7 @@ impl SurfaceState {
|
||||
// if returning false, don't run this callback again... just remove it
|
||||
pub type OnCommitCallback = Box<dyn FnMut(&Surface, &SurfaceState) -> bool + Send + Sync>;
|
||||
#[derive(waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Surface {
|
||||
pub id: ObjectId,
|
||||
pub surface_id: OnceLock<SurfaceId>,
|
||||
@@ -376,7 +377,7 @@ impl WlSurface for Surface {
|
||||
_sender_id: ObjectId,
|
||||
callback_id: ObjectId,
|
||||
) -> WaylandResult<()> {
|
||||
let callback = client.insert(callback_id, Callback(callback_id));
|
||||
let callback = client.insert(callback_id, Callback(callback_id))?;
|
||||
self.state.lock().pending.frame_callbacks.push(callback);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use waynest::ObjectId;
|
||||
pub use waynest_protocols::server::core::wayland::wl_touch::*;
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Touch(pub ObjectId);
|
||||
impl Touch {
|
||||
pub async fn handle_touch_down(
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::wayland::{
|
||||
registry::Registry,
|
||||
};
|
||||
use global_counter::primitive::exact::CounterU32;
|
||||
use waynest_server::Client as _;
|
||||
use std::{
|
||||
sync::{Arc, OnceLock},
|
||||
time::Instant,
|
||||
@@ -18,7 +19,7 @@ use waynest::ObjectId;
|
||||
pub use waynest_protocols::server::core::wayland::wl_display::*;
|
||||
|
||||
#[derive(waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Display {
|
||||
pub message_sink: MessageSink,
|
||||
pub pid: Option<i32>,
|
||||
@@ -69,7 +70,7 @@ impl WlDisplay for Display {
|
||||
_sender_id: ObjectId,
|
||||
registry_id: ObjectId,
|
||||
) -> WaylandResult<()> {
|
||||
let registry = client.insert(registry_id, Registry);
|
||||
let registry = client.insert(registry_id, Registry)?;
|
||||
|
||||
registry.advertise_globals(client, registry_id).await?;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use bevy_dmabuf::dmatex::DmatexPlane;
|
||||
use drm_fourcc::DrmFourcc;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use waynest_server::Client as _;
|
||||
use std::os::fd::{AsRawFd, OwnedFd};
|
||||
use waynest::ObjectId;
|
||||
use waynest_protocols::server::stable::linux_dmabuf_v1::zwp_linux_buffer_params_v1::{
|
||||
@@ -20,7 +21,7 @@ use waynest_protocols::server::stable::linux_dmabuf_v1::zwp_linux_buffer_params_
|
||||
/// that together form a single logical buffer. The object may eventually
|
||||
/// create one wl_buffer unless cancelled by destroying it.
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct BufferParams {
|
||||
pub id: ObjectId,
|
||||
pub(super) planes: Mutex<FxHashMap<u32, DmatexPlane>>,
|
||||
@@ -120,7 +121,7 @@ impl ZwpLinuxBufferParamsV1 for BufferParams {
|
||||
});
|
||||
|
||||
match buffer {
|
||||
Ok(buffer) => self.created(client, self.id, buffer.id).await,
|
||||
Ok(buffer) => self.created(client, self.id, buffer?.id).await,
|
||||
Err(_) => {
|
||||
client.remove(self.id);
|
||||
self.failed(client, self.id).await
|
||||
@@ -148,7 +149,7 @@ impl ZwpLinuxBufferParamsV1 for BufferParams {
|
||||
flags,
|
||||
) {
|
||||
Ok(backing) => {
|
||||
Buffer::new(client, buffer_id, BufferBacking::Dmabuf(backing));
|
||||
Buffer::new(client, buffer_id, BufferBacking::Dmabuf(backing))?;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to import dmabuf because {e}");
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::wayland::{Client, WaylandResult, vulkano_data::VULKANO_CONTEXT};
|
||||
use memfd::MemfdOptions;
|
||||
use std::{
|
||||
io::Write,
|
||||
os::fd::{FromRawFd, IntoRawFd, OwnedFd},
|
||||
os::fd::{AsFd as _, FromRawFd, IntoRawFd, OwnedFd},
|
||||
sync::Arc,
|
||||
};
|
||||
use waynest::ObjectId;
|
||||
@@ -12,7 +12,7 @@ use waynest_protocols::server::stable::linux_dmabuf_v1::zwp_linux_dmabuf_feedbac
|
||||
};
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct DmabufFeedback(pub Arc<Dmabuf>);
|
||||
impl DmabufFeedback {
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
@@ -81,14 +81,9 @@ impl DmabufFeedback {
|
||||
mfd.as_file().write_all(&0_u32.to_ne_bytes())?;
|
||||
mfd.as_file().write_all(&modifier.to_ne_bytes())?;
|
||||
}
|
||||
|
||||
self.format_table(
|
||||
client,
|
||||
sender_id,
|
||||
unsafe { OwnedFd::from_raw_fd(mfd.into_raw_fd()) },
|
||||
size,
|
||||
)
|
||||
.await?;
|
||||
let fd = unsafe { OwnedFd::from_raw_fd(mfd.into_raw_fd()) };
|
||||
self.format_table(client, sender_id, fd.as_fd(), size)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use buffer_params::BufferParams;
|
||||
use drm_fourcc::DrmFourcc;
|
||||
use feedback::DmabufFeedback;
|
||||
use rustc_hash::FxHashSet;
|
||||
use waynest_server::Client as _;
|
||||
use std::sync::LazyLock;
|
||||
use vulkano::format::FormatFeatures;
|
||||
use waynest::ObjectId;
|
||||
@@ -74,7 +75,7 @@ pub static DMABUF_FORMATS: LazyLock<Vec<(DrmFourcc, u64)>> = LazyLock::new(|| {
|
||||
/// - Proper lifetime management of dmabuf file descriptors
|
||||
/// - Safe handling of buffer attachments
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Dmabuf {
|
||||
// Track supported formats and modifiers
|
||||
// formats: Mutex<FxHashSet<DrmFormat>>,
|
||||
@@ -138,7 +139,7 @@ impl ZwpLinuxDmabufV1 for Dmabuf {
|
||||
params_id: ObjectId,
|
||||
) -> WaylandResult<()> {
|
||||
// Create new buffer parameters object
|
||||
let params = client.insert(params_id, BufferParams::new(params_id));
|
||||
let params = client.insert(params_id, BufferParams::new(params_id))?;
|
||||
self.active_params.add_raw(¶ms);
|
||||
Ok(())
|
||||
}
|
||||
@@ -157,7 +158,7 @@ impl ZwpLinuxDmabufV1 for Dmabuf {
|
||||
});
|
||||
}
|
||||
// Create feedback object for default (non-surface-specific) settings
|
||||
let feedback = client.insert(id, DmabufFeedback(client.get::<Dmabuf>(sender_id).unwrap()));
|
||||
let feedback = client.insert(id, DmabufFeedback(client.get::<Dmabuf>(sender_id).unwrap()))?;
|
||||
feedback.send_params(client, id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use waynest::ObjectId;
|
||||
use waynest_protocols::server::mesa::drm::wl_drm::*;
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher, Default)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct MesaDrm {
|
||||
version: u32,
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ use core::buffer::BufferUsage;
|
||||
use core::{buffer::Buffer, callback::Callback, surface::WL_SURFACE_REGISTRY};
|
||||
use display::Display;
|
||||
use mint::Vector2;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::fs::File;
|
||||
use std::io::ErrorKind;
|
||||
use std::mem::MaybeUninit;
|
||||
@@ -42,12 +43,13 @@ use std::{
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
use tokio::{net::UnixStream, sync::mpsc, task::AbortHandle};
|
||||
use tokio_stream::StreamExt;
|
||||
use tokio_stream::{Stream, StreamExt};
|
||||
use tracing::{debug_span, instrument};
|
||||
use vulkano_data::setup_vulkano_context;
|
||||
use waynest::ObjectId;
|
||||
use waynest::{Connection, Socket};
|
||||
use waynest::{ObjectId, ProtocolError};
|
||||
use waynest_protocols::server::core::wayland::wl_display::WlDisplay;
|
||||
use waynest_server::{Connection, Listener};
|
||||
use waynest_server::{Client as _, Listener, Store, StoreError};
|
||||
use xdg::toplevel::Toplevel;
|
||||
|
||||
pub static WAYLAND_DISPLAY: OnceLock<PathBuf> = OnceLock::new();
|
||||
@@ -76,6 +78,97 @@ pub enum WaylandError {
|
||||
DmabufImport(#[from] bevy_dmabuf::import::ImportError),
|
||||
#[error("Server error: {0}")]
|
||||
Server(#[from] ServerError),
|
||||
#[error("Failed to Insert Object")]
|
||||
FailedToInsertObject,
|
||||
}
|
||||
impl<T: Clone> From<StoreError<T>> for WaylandError {
|
||||
fn from(_value: StoreError<T>) -> Self {
|
||||
Self::FailedToInsertObject
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
pub struct Client {
|
||||
store: Store<Client, WaylandError>,
|
||||
#[pin]
|
||||
connection: Socket,
|
||||
next_event_serial: u32,
|
||||
}
|
||||
}
|
||||
impl Connection for Client {
|
||||
type Error = WaylandError;
|
||||
|
||||
fn fd(&mut self) -> Result<std::os::unix::prelude::OwnedFd, <Self as Connection>::Error> {
|
||||
Ok(self.connection.fd()?)
|
||||
}
|
||||
}
|
||||
impl Stream for Client {
|
||||
type Item = <Socket as Stream>::Item;
|
||||
|
||||
fn poll_next(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Option<Self::Item>> {
|
||||
// <Socket as Stream>::poll_next(self.project().connection, cx)
|
||||
self.project().connection.poll_next(cx)
|
||||
}
|
||||
}
|
||||
impl futures_sink::Sink<waynest::Message> for Client {
|
||||
type Error = ProtocolError;
|
||||
|
||||
fn poll_ready(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), Self::Error>> {
|
||||
self.project().connection.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn start_send(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
item: waynest::Message,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.project().connection.start_send(item)
|
||||
}
|
||||
|
||||
fn poll_flush(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), Self::Error>> {
|
||||
self.project().connection.poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_close(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), Self::Error>> {
|
||||
self.project().connection.poll_close(cx)
|
||||
}
|
||||
}
|
||||
impl Client {
|
||||
fn new(unix_stream: UnixStream) -> tokio::io::Result<Self> {
|
||||
Ok(Self {
|
||||
store: Store::new(),
|
||||
connection: Socket::new(unix_stream.into_std()?)?,
|
||||
next_event_serial: 0,
|
||||
})
|
||||
}
|
||||
pub fn next_event_serial(&mut self) -> u32 {
|
||||
let prev = self.next_event_serial;
|
||||
self.next_event_serial = self.next_event_serial.wrapping_add(1);
|
||||
prev
|
||||
}
|
||||
}
|
||||
|
||||
impl waynest_server::Client for Client {
|
||||
type Store = Store<Client, WaylandError>;
|
||||
|
||||
fn store(&self) -> &Self::Store {
|
||||
&self.store
|
||||
}
|
||||
|
||||
fn store_mut(&mut self) -> &mut Self::Store {
|
||||
&mut self.store
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_free_wayland_socket_path() -> Option<(PathBuf, File)> {
|
||||
@@ -116,7 +209,6 @@ pub fn get_free_wayland_socket_path() -> Option<(PathBuf, File)> {
|
||||
}
|
||||
|
||||
pub type WaylandResult<T, E = WaylandError> = std::result::Result<T, E>;
|
||||
pub type Client = waynest_server::Connection<WaylandError>;
|
||||
|
||||
pub enum Message {
|
||||
Frame(Vec<Arc<Callback>>),
|
||||
@@ -148,10 +240,10 @@ struct WaylandClient {
|
||||
impl WaylandClient {
|
||||
pub fn from_stream(socket: UnixStream) -> WaylandResult<Self> {
|
||||
let pid = socket.peer_cred().ok().and_then(|c| c.pid());
|
||||
let mut client = Connection::new(socket)?;
|
||||
let mut client = Client::new(socket)?;
|
||||
let (message_sink, message_source) = mpsc::unbounded_channel();
|
||||
|
||||
client.insert(ObjectId::DISPLAY, Display::new(message_sink, pid));
|
||||
client.insert(ObjectId::DISPLAY, Display::new(message_sink, pid))?;
|
||||
let abort_handle =
|
||||
tokio::task::spawn(Self::dispatch_loop(client, message_source)).abort_handle();
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ use waynest::ObjectId;
|
||||
use waynest_protocols::server::stable::presentation_time::{
|
||||
wp_presentation::*, wp_presentation_feedback::*,
|
||||
};
|
||||
use waynest_server::Client as _;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct MonotonicTimestamp {
|
||||
@@ -33,7 +34,7 @@ impl From<Timespec> for MonotonicTimestamp {
|
||||
}
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Presentation {
|
||||
id: ObjectId,
|
||||
}
|
||||
@@ -65,7 +66,7 @@ impl WpPresentation for Presentation {
|
||||
tracing::error!("unable to get surface#{surface}");
|
||||
return Ok(());
|
||||
};
|
||||
let feedback = client.insert(id, PresentationFeedback(id));
|
||||
let feedback = client.insert(id, PresentationFeedback(id))?;
|
||||
surface.add_presentation_feedback(feedback);
|
||||
|
||||
Ok(())
|
||||
@@ -73,7 +74,7 @@ impl WpPresentation for Presentation {
|
||||
}
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct PresentationFeedback(pub ObjectId);
|
||||
impl WpPresentationFeedback for PresentationFeedback {
|
||||
type Connection = crate::wayland::Client;
|
||||
|
||||
@@ -25,6 +25,7 @@ use waynest_protocols::server::{
|
||||
viewporter::wp_viewporter::WpViewporter,
|
||||
},
|
||||
};
|
||||
use waynest_server::Client as _;
|
||||
|
||||
struct RegistryGlobals;
|
||||
impl RegistryGlobals {
|
||||
@@ -41,7 +42,7 @@ impl RegistryGlobals {
|
||||
}
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher, Default)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Registry;
|
||||
|
||||
impl Registry {
|
||||
@@ -157,11 +158,11 @@ impl WlRegistry for Registry {
|
||||
match name {
|
||||
RegistryGlobals::COMPOSITOR => {
|
||||
tracing::info!("Binding compositor");
|
||||
client.insert(new_id.object_id, Compositor);
|
||||
client.insert(new_id.object_id, Compositor)?;
|
||||
}
|
||||
RegistryGlobals::SHM => {
|
||||
tracing::info!("Binding SHM");
|
||||
let shm = client.insert(new_id.object_id, Shm);
|
||||
let shm = client.insert(new_id.object_id, Shm)?;
|
||||
shm.advertise_formats(client, new_id.object_id).await?;
|
||||
}
|
||||
RegistryGlobals::WM_BASE => {
|
||||
@@ -169,19 +170,19 @@ impl WlRegistry for Registry {
|
||||
client.insert(
|
||||
new_id.object_id,
|
||||
WmBase::new(new_id.object_id, new_id.version),
|
||||
);
|
||||
)?;
|
||||
}
|
||||
RegistryGlobals::SEAT => {
|
||||
tracing::info!("Binding seat with id {}", new_id.object_id);
|
||||
let seat = Seat::new(client, new_id.object_id, new_id.version).await?;
|
||||
let seat = client.insert(new_id.object_id, seat);
|
||||
let seat = client.insert(new_id.object_id, seat)?;
|
||||
let _ = client.display().seat.set(seat.clone());
|
||||
|
||||
tracing::info!("Seat capabilities advertised");
|
||||
}
|
||||
RegistryGlobals::DATA_DEVICE_MANAGER => {
|
||||
tracing::info!("Binding data device manager");
|
||||
client.insert(new_id.object_id, DataDeviceManager);
|
||||
client.insert(new_id.object_id, DataDeviceManager)?;
|
||||
}
|
||||
RegistryGlobals::OUTPUT => {
|
||||
tracing::info!("Binding output");
|
||||
@@ -191,7 +192,7 @@ impl WlRegistry for Registry {
|
||||
id: new_id.object_id,
|
||||
version: new_id.version,
|
||||
},
|
||||
);
|
||||
)?;
|
||||
let _ = client.display().output.set(output.clone());
|
||||
output.advertise_outputs(client).await?;
|
||||
}
|
||||
@@ -199,23 +200,23 @@ impl WlRegistry for Registry {
|
||||
tracing::info!("Binding dmabuf");
|
||||
|
||||
let dmabuf = Dmabuf::new(client, new_id.object_id, new_id.version).await?;
|
||||
client.insert(new_id.object_id, dmabuf);
|
||||
client.insert(new_id.object_id, dmabuf)?;
|
||||
}
|
||||
RegistryGlobals::WL_DRM => {
|
||||
tracing::info!("Binding wl_drm");
|
||||
|
||||
let drm = MesaDrm::new(client, new_id.object_id, new_id.version).await?;
|
||||
client.insert(new_id.object_id, drm);
|
||||
client.insert(new_id.object_id, drm)?;
|
||||
}
|
||||
RegistryGlobals::PRESENTATION => {
|
||||
tracing::info!("Binding wp_presentation");
|
||||
|
||||
client.insert(new_id.object_id, Presentation::new(new_id.object_id));
|
||||
client.insert(new_id.object_id, Presentation::new(new_id.object_id))?;
|
||||
}
|
||||
RegistryGlobals::VIEWPORTER => {
|
||||
tracing::info!("Binding wp_viewporter");
|
||||
|
||||
client.insert(new_id.object_id, Viewporter::new(new_id.object_id));
|
||||
client.insert(new_id.object_id, Viewporter::new(new_id.object_id))?;
|
||||
}
|
||||
id => {
|
||||
tracing::error!(id, "Wayland: failed to bind to registry global");
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::wayland::{Client, WaylandError, WaylandResult};
|
||||
use std::{fmt::Debug, sync::Arc};
|
||||
use waynest::ObjectId;
|
||||
use waynest_protocols::server::core::wayland::wl_display::WlDisplay;
|
||||
use waynest_server::RequestDispatcher;
|
||||
use waynest_server::{Client as _, RequestDispatcher};
|
||||
|
||||
pub trait ClientExt {
|
||||
fn message_sink(&self) -> MessageSink;
|
||||
|
||||
@@ -3,11 +3,12 @@ use waynest::Fixed;
|
||||
use waynest::ObjectId;
|
||||
pub use waynest_protocols::server::stable::viewporter::wp_viewport::*;
|
||||
pub use waynest_protocols::server::stable::viewporter::wp_viewporter::*;
|
||||
use waynest_server::Client as _;
|
||||
|
||||
// This is a barebones/stub no-op implementation of wp_viewporter to make xwayland apps work
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Viewporter {
|
||||
id: ObjectId,
|
||||
}
|
||||
@@ -38,13 +39,13 @@ impl WpViewporter for Viewporter {
|
||||
surface_id: ObjectId,
|
||||
) -> WaylandResult<()> {
|
||||
let viewport = Viewport::new(id, surface_id);
|
||||
client.insert(id, viewport);
|
||||
client.insert(id, viewport)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Viewport {
|
||||
id: ObjectId,
|
||||
_surface_id: ObjectId,
|
||||
|
||||
@@ -6,12 +6,13 @@ use crate::nodes::items::panel::SurfaceId;
|
||||
use crate::wayland::WaylandResult;
|
||||
use parking_lot::Mutex;
|
||||
use rand::Rng;
|
||||
use waynest_server::Client as _;
|
||||
use std::sync::Arc;
|
||||
use waynest::ObjectId;
|
||||
use waynest_protocols::server::stable::xdg_shell::xdg_popup::XdgPopup;
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Popup {
|
||||
version: u32,
|
||||
pub surface: Arc<Surface>,
|
||||
|
||||
@@ -3,6 +3,7 @@ use mint::Vector2;
|
||||
use parking_lot::Mutex;
|
||||
use waynest::ObjectId;
|
||||
use waynest_protocols::server::stable::xdg_shell::xdg_positioner::*;
|
||||
use waynest_server::Client as _;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PositionerData {
|
||||
@@ -127,7 +128,7 @@ impl Default for PositionerData {
|
||||
}
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Positioner {
|
||||
data: Mutex<PositionerData>,
|
||||
id: ObjectId,
|
||||
|
||||
@@ -9,9 +9,10 @@ use std::sync::Arc;
|
||||
use waynest::ObjectId;
|
||||
use waynest_protocols::server::stable::xdg_shell::xdg_popup::XdgPopup;
|
||||
pub use waynest_protocols::server::stable::xdg_shell::xdg_surface::*;
|
||||
use waynest_server::Client as _;
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Surface {
|
||||
id: ObjectId,
|
||||
version: u32,
|
||||
@@ -65,7 +66,7 @@ impl XdgSurface for Surface {
|
||||
self.wl_surface.clone(),
|
||||
client.get::<Self>(sender_id).unwrap(),
|
||||
),
|
||||
);
|
||||
)?;
|
||||
|
||||
self.wl_surface
|
||||
.try_set_role(SurfaceRole::XdgToplevel, Error::AlreadyConstructed)
|
||||
@@ -139,7 +140,7 @@ impl XdgSurface for Surface {
|
||||
let popup = client.insert(
|
||||
popup_id,
|
||||
Popup::new(self.version, surface, &positioner, popup_id),
|
||||
);
|
||||
)?;
|
||||
|
||||
let positioner_geometry = positioner.data().infinite_geometry();
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::{
|
||||
};
|
||||
use mint::Vector2;
|
||||
use parking_lot::Mutex;
|
||||
use waynest_server::Client as _;
|
||||
use std::sync::Arc;
|
||||
use waynest::ObjectId;
|
||||
pub use waynest_protocols::server::stable::xdg_shell::xdg_toplevel::*;
|
||||
@@ -55,7 +56,7 @@ impl Default for ToplevelData {
|
||||
}
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct Toplevel {
|
||||
pub id: ObjectId,
|
||||
xdg_surface: Arc<super::surface::Surface>,
|
||||
|
||||
@@ -3,9 +3,10 @@ use crate::wayland::{WaylandError, WaylandResult, util::ClientExt, xdg::surface:
|
||||
|
||||
use waynest::ObjectId;
|
||||
pub use waynest_protocols::server::stable::xdg_shell::xdg_wm_base::*;
|
||||
use waynest_server::Client as _;
|
||||
|
||||
#[derive(Debug, waynest_server::RequestDispatcher)]
|
||||
#[waynest(error = crate::wayland::WaylandError)]
|
||||
#[waynest(error = crate::wayland::WaylandError, connection = crate::wayland::Client)]
|
||||
pub struct WmBase {
|
||||
version: u32,
|
||||
id: ObjectId,
|
||||
@@ -33,7 +34,7 @@ impl XdgWmBase for WmBase {
|
||||
_sender_id: ObjectId,
|
||||
id: ObjectId,
|
||||
) -> WaylandResult<()> {
|
||||
client.insert(id, Positioner::new(id));
|
||||
client.insert(id, Positioner::new(id))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -56,7 +57,7 @@ impl XdgWmBase for WmBase {
|
||||
}
|
||||
};
|
||||
let xdg_surface = Surface::new(xdg_surface_id, self.version, wl_surface);
|
||||
client.insert(xdg_surface_id, xdg_surface);
|
||||
client.insert(xdg_surface_id, xdg_surface)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user