All `GlobalRemove` events sent from the server are now handled by recording them in a new clientside `vec` and passing the identifier returned by `create_global` (now stored by a map in the state) to `disable_global`. `handle_globals` (the top-level function) and `handle_new_globals` (the `InnerServerState` member function) have swapped names to better represent their new purposes. This enables action to be taken when globals are removed. In this case, the desired action is to forward output removal, so that the scaling calculation does not account for disconnected monitors in its logic. Resolves #351.
1594 lines
55 KiB
Rust
1594 lines
55 KiB
Rust
use super::*;
|
|
use hecs::{CommandBuffer, DynamicBundle};
|
|
use log::{debug, error, trace, warn};
|
|
use macros::simple_event_shunt;
|
|
use std::sync::{Arc, OnceLock};
|
|
use wayland_client::globals::Global;
|
|
use wayland_protocols::{
|
|
wp::{
|
|
fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1,
|
|
linux_dmabuf::zv1::{client as c_dmabuf, server as s_dmabuf},
|
|
linux_drm_syncobj::v1::{client as c_sync, server as s_sync},
|
|
pointer_constraints::zv1::{
|
|
client::zwp_confined_pointer_v1::ZwpConfinedPointerV1 as ConfinedPointerClient,
|
|
client::zwp_locked_pointer_v1::ZwpLockedPointerV1 as LockedPointerClient,
|
|
client::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1 as PointerConstraintsClient,
|
|
server::{
|
|
zwp_confined_pointer_v1::{
|
|
self as cp, ZwpConfinedPointerV1 as ConfinedPointerServer,
|
|
},
|
|
zwp_locked_pointer_v1::{self as lp, ZwpLockedPointerV1 as LockedPointerServer},
|
|
zwp_pointer_constraints_v1::{
|
|
self as pc, ZwpPointerConstraintsV1 as PointerConstraintsServer,
|
|
},
|
|
},
|
|
},
|
|
relative_pointer::zv1::{
|
|
client::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1 as RelativePointerManClient,
|
|
client::zwp_relative_pointer_v1::ZwpRelativePointerV1 as RelativePointerClient,
|
|
server::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1 as RelativePointerManServer,
|
|
server::zwp_relative_pointer_v1::ZwpRelativePointerV1 as RelativePointerServer,
|
|
},
|
|
tablet::zv2::{client as c_tablet, server as s_tablet},
|
|
viewporter::client::wp_viewport::WpViewport,
|
|
},
|
|
xdg::xdg_output::zv1::{
|
|
client::zxdg_output_manager_v1::ZxdgOutputManagerV1 as OutputManClient,
|
|
client::zxdg_output_v1::ZxdgOutputV1 as XdgOutputClient,
|
|
server::{
|
|
zxdg_output_manager_v1::{
|
|
self as s_output_man, ZxdgOutputManagerV1 as OutputManServer,
|
|
},
|
|
zxdg_output_v1::{self as s_xdgo, ZxdgOutputV1 as XdgOutputServer},
|
|
},
|
|
},
|
|
xwayland::shell::v1::server::{
|
|
xwayland_shell_v1::{self, XwaylandShellV1},
|
|
xwayland_surface_v1::{self, XwaylandSurfaceV1},
|
|
},
|
|
};
|
|
use wayland_server::{
|
|
Dispatch, DisplayHandle, GlobalDispatch, Resource,
|
|
protocol::{
|
|
wl_buffer::WlBuffer,
|
|
wl_callback::WlCallback,
|
|
wl_compositor::WlCompositor,
|
|
wl_keyboard::WlKeyboard,
|
|
wl_output::WlOutput,
|
|
wl_pointer::WlPointer,
|
|
wl_region::{self, WlRegion},
|
|
wl_seat::WlSeat,
|
|
wl_shm::WlShm,
|
|
wl_shm_pool::WlShmPool,
|
|
wl_surface::WlSurface,
|
|
wl_touch::WlTouch,
|
|
},
|
|
};
|
|
|
|
// noop
|
|
impl<S: X11Selection> Dispatch<WlCallback, ()> for InnerServerState<S> {
|
|
fn request(
|
|
_: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &WlCallback,
|
|
_: <WlCallback as Resource>::Request,
|
|
_: &(),
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
unreachable!();
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<WlSurface, Entity> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &WlSurface,
|
|
request: <WlSurface as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
let data = state.world.entity(*entity).unwrap();
|
|
let mut role = data.get::<&mut SurfaceRole>();
|
|
let xdg = role.as_ref().and_then(|role| role.xdg());
|
|
let configured = xdg.is_none_or(|xdg| xdg.configured);
|
|
let client = data.get::<&client::wl_surface::WlSurface>().unwrap();
|
|
|
|
let mut cmd = CommandBuffer::new();
|
|
|
|
match request {
|
|
Request::<WlSurface>::Attach { buffer, x, y } => {
|
|
if buffer.is_none() {
|
|
trace!("xwayland attached null buffer to {client:?}");
|
|
}
|
|
let buffer = buffer.as_ref().map(|b| {
|
|
let entity: Entity = b.data().copied().unwrap();
|
|
state
|
|
.world
|
|
.get::<&client::wl_buffer::WlBuffer>(entity)
|
|
.unwrap()
|
|
});
|
|
|
|
if configured {
|
|
client.attach(buffer.as_deref(), x, y);
|
|
} else {
|
|
let buffer = buffer.as_deref().cloned();
|
|
cmd.insert(*entity, (SurfaceAttach { buffer, x, y },));
|
|
}
|
|
}
|
|
Request::<WlSurface>::DamageBuffer {
|
|
x,
|
|
y,
|
|
width,
|
|
height,
|
|
} => {
|
|
if configured {
|
|
client.damage_buffer(x, y, width, height);
|
|
}
|
|
}
|
|
Request::<WlSurface>::Frame { callback } => {
|
|
let cb = data_init.init(callback, ());
|
|
if configured {
|
|
client.frame(&state.qh, cb);
|
|
} else {
|
|
cmd.insert(*entity, (cb,));
|
|
}
|
|
}
|
|
Request::<WlSurface>::Commit => {
|
|
if configured {
|
|
client.commit();
|
|
}
|
|
}
|
|
Request::<WlSurface>::Destroy => {
|
|
if !data.has::<x::Window>() {
|
|
cmd.despawn(*entity);
|
|
}
|
|
|
|
if let Some(role) = role.as_mut() {
|
|
role.destroy();
|
|
}
|
|
client.destroy();
|
|
|
|
debug!(
|
|
"deleting (surface {:?})",
|
|
data.get::<&WlSurface>().unwrap().id().protocol_id()
|
|
);
|
|
|
|
let mut query = data.query::<(&WpViewport, Option<&WpFractionalScaleV1>)>();
|
|
let (viewport, fractional) = query.get().unwrap();
|
|
viewport.destroy();
|
|
|
|
cmd.remove::<event::SurfaceBundle>(*entity);
|
|
if let Some(f) = fractional {
|
|
f.destroy();
|
|
cmd.remove_one::<WpFractionalScaleV1>(*entity);
|
|
}
|
|
}
|
|
Request::<WlSurface>::SetBufferScale { scale } => {
|
|
client.set_buffer_scale(scale);
|
|
}
|
|
Request::<WlSurface>::SetInputRegion { region } => {
|
|
let region = region.as_ref().map(|r| r.data().unwrap());
|
|
client.set_input_region(region);
|
|
}
|
|
other => warn!("unhandled surface request: {other:?}"),
|
|
}
|
|
|
|
drop(client);
|
|
drop(role);
|
|
|
|
cmd.run_on(&mut state.world);
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<WlRegion, client::wl_region::WlRegion> for InnerServerState<S> {
|
|
fn request(
|
|
_: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &WlRegion,
|
|
request: <WlRegion as Resource>::Request,
|
|
client: &client::wl_region::WlRegion,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
macros::simple_event_shunt! {
|
|
client, request: wl_region::Request => [
|
|
Add { x, y, width, height },
|
|
Subtract { x, y, width, height },
|
|
Destroy
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection>
|
|
Dispatch<WlCompositor, ClientGlobalWrapper<client::wl_compositor::WlCompositor>>
|
|
for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &WlCompositor,
|
|
request: <WlCompositor as wayland_server::Resource>::Request,
|
|
client: &ClientGlobalWrapper<client::wl_compositor::WlCompositor>,
|
|
_: &DisplayHandle,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
match request {
|
|
Request::<WlCompositor>::CreateSurface { id } => {
|
|
let entity = state.world.reserve_entity();
|
|
let client = client.create_surface(&state.qh, entity);
|
|
let server = data_init.init(id, entity);
|
|
debug!("new surface ({})", server.id());
|
|
let viewport = state.viewporter.get_viewport(&client, &state.qh, ());
|
|
let fractional = state
|
|
.fractional_scale
|
|
.as_ref()
|
|
.map(|f| f.get_fractional_scale(&client, &state.qh, entity));
|
|
|
|
state.world.spawn_at(
|
|
entity,
|
|
event::SurfaceBundle {
|
|
client,
|
|
server,
|
|
viewport,
|
|
scale: SurfaceScaleFactor(1.0),
|
|
},
|
|
);
|
|
if let Some(f) = fractional {
|
|
state.world.insert(entity, (f,)).unwrap();
|
|
}
|
|
}
|
|
Request::<WlCompositor>::CreateRegion { id } => {
|
|
let c_region = client.create_region(&state.qh, ());
|
|
data_init.init(id, c_region);
|
|
}
|
|
other => {
|
|
warn!("unhandled wlcompositor request: {other:?}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<WlBuffer, Entity> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &WlBuffer,
|
|
request: <WlBuffer as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
assert!(matches!(request, Request::<WlBuffer>::Destroy));
|
|
|
|
state
|
|
.world
|
|
.get::<&client::wl_buffer::WlBuffer>(*entity)
|
|
.unwrap()
|
|
.destroy();
|
|
state.world.despawn(*entity).unwrap();
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<WlShmPool, client::wl_shm_pool::WlShmPool> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &WlShmPool,
|
|
request: <WlShmPool as Resource>::Request,
|
|
c_pool: &client::wl_shm_pool::WlShmPool,
|
|
_: &DisplayHandle,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
match request {
|
|
Request::<WlShmPool>::CreateBuffer {
|
|
id,
|
|
offset,
|
|
width,
|
|
height,
|
|
stride,
|
|
format,
|
|
} => {
|
|
let entity = state.world.reserve_entity();
|
|
let client = c_pool.create_buffer(
|
|
offset,
|
|
width,
|
|
height,
|
|
stride,
|
|
convert_wenum(format),
|
|
&state.qh,
|
|
entity,
|
|
);
|
|
let server = data_init.init(id, entity);
|
|
state.world.spawn_at(entity, (client, server));
|
|
}
|
|
Request::<WlShmPool>::Resize { size } => {
|
|
c_pool.resize(size);
|
|
}
|
|
Request::<WlShmPool>::Destroy => {
|
|
c_pool.destroy();
|
|
state.queue.flush().unwrap();
|
|
}
|
|
other => warn!("unhandled shmpool request: {other:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<WlShm, ClientGlobalWrapper<client::wl_shm::WlShm>>
|
|
for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &WlShm,
|
|
request: <WlShm as Resource>::Request,
|
|
client: &ClientGlobalWrapper<client::wl_shm::WlShm>,
|
|
_: &DisplayHandle,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
match request {
|
|
Request::<WlShm>::CreatePool { id, fd, size } => {
|
|
let c_pool = client.create_pool(fd.as_fd(), size, &state.qh, ());
|
|
data_init.init(id, c_pool);
|
|
}
|
|
other => {
|
|
warn!("unhandled shm pool request: {other:?}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<WlPointer, Entity> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &WlPointer,
|
|
request: <WlPointer as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
match request {
|
|
Request::<WlPointer>::SetCursor {
|
|
serial,
|
|
hotspot_x,
|
|
hotspot_y,
|
|
surface,
|
|
} => {
|
|
let c_pointer = state
|
|
.world
|
|
.get::<&client::wl_pointer::WlPointer>(*entity)
|
|
.unwrap();
|
|
|
|
let c_surface = surface.and_then(|s| {
|
|
let e = s.data().copied()?;
|
|
Some(
|
|
state
|
|
.world
|
|
.get::<&client::wl_surface::WlSurface>(e)
|
|
.unwrap(),
|
|
)
|
|
});
|
|
c_pointer.set_cursor(serial, c_surface.as_deref(), hotspot_x, hotspot_y);
|
|
}
|
|
Request::<WlPointer>::Release => {
|
|
let (client, _) = state
|
|
.world
|
|
.remove::<(client::wl_pointer::WlPointer, WlPointer)>(*entity)
|
|
.unwrap();
|
|
client.release();
|
|
}
|
|
_ => warn!("unhandled cursor request: {request:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<WlKeyboard, Entity> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &WlKeyboard,
|
|
request: <WlKeyboard as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
match request {
|
|
Request::<WlKeyboard>::Release => {
|
|
let (client, _) = state
|
|
.world
|
|
.remove::<(client::wl_keyboard::WlKeyboard, WlKeyboard)>(*entity)
|
|
.unwrap();
|
|
client.release();
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<WlTouch, Entity> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &WlTouch,
|
|
request: <WlTouch as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
match request {
|
|
Request::<WlTouch>::Release => {
|
|
state
|
|
.world
|
|
.get::<&client::wl_touch::WlTouch>(*entity)
|
|
.unwrap()
|
|
.release();
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<WlSeat, Entity> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &WlSeat,
|
|
request: <WlSeat as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
match request {
|
|
Request::<WlSeat>::GetPointer { id } => {
|
|
let client = {
|
|
state
|
|
.world
|
|
.get::<&client::wl_seat::WlSeat>(*entity)
|
|
.unwrap()
|
|
.get_pointer(&state.qh, *entity)
|
|
};
|
|
let server = data_init.init(id, *entity);
|
|
state.world.insert(*entity, (client, server)).unwrap();
|
|
}
|
|
Request::<WlSeat>::GetKeyboard { id } => {
|
|
let client = {
|
|
state
|
|
.world
|
|
.get::<&client::wl_seat::WlSeat>(*entity)
|
|
.unwrap()
|
|
.get_keyboard(&state.qh, *entity)
|
|
};
|
|
let server = data_init.init(id, *entity);
|
|
state.world.insert(*entity, (client, server)).unwrap();
|
|
}
|
|
Request::<WlSeat>::GetTouch { id } => {
|
|
let client = {
|
|
state
|
|
.world
|
|
.get::<&client::wl_seat::WlSeat>(*entity)
|
|
.unwrap()
|
|
.get_touch(&state.qh, *entity)
|
|
};
|
|
let server = data_init.init(id, *entity);
|
|
state.world.insert(*entity, (client, server)).unwrap();
|
|
}
|
|
other => warn!("unhandled seat request: {other:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! only_destroy_request_impl {
|
|
($server:ty, $client:ty) => {
|
|
impl<S: X11Selection> Dispatch<$server, Entity> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &Client,
|
|
_: &$server,
|
|
request: <$server as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
if !matches!(request, <$server as Resource>::Request::Destroy) {
|
|
warn!(
|
|
"unrecognized {} request: {:?}",
|
|
stringify!($server),
|
|
request
|
|
);
|
|
return;
|
|
}
|
|
|
|
state.world.get::<&$client>(*entity).unwrap().destroy();
|
|
state.world.despawn(*entity).unwrap();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
only_destroy_request_impl!(RelativePointerServer, RelativePointerClient);
|
|
|
|
impl<S: X11Selection>
|
|
Dispatch<RelativePointerManServer, ClientGlobalWrapper<RelativePointerManClient>>
|
|
for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &RelativePointerManServer,
|
|
request: <RelativePointerManServer as Resource>::Request,
|
|
client: &ClientGlobalWrapper<RelativePointerManClient>,
|
|
_: &DisplayHandle,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
match request {
|
|
Request::<RelativePointerManServer>::GetRelativePointer { id, pointer } => {
|
|
let pointer_entity: Entity = pointer.data().copied().unwrap();
|
|
let entity = state.world.reserve_entity();
|
|
let client = {
|
|
let client_pointer = state
|
|
.world
|
|
.get::<&client::wl_pointer::WlPointer>(pointer_entity)
|
|
.unwrap();
|
|
|
|
client.get_relative_pointer(&client_pointer, &state.qh, entity)
|
|
};
|
|
let server = data_init.init(id, entity);
|
|
state.world.spawn_at(entity, (server, client));
|
|
}
|
|
_ => warn!("unhandled relative pointer request: {request:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<WlOutput, Entity> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &WlOutput,
|
|
request: <WlOutput as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
match request {
|
|
wayland_server::protocol::wl_output::Request::Release => {
|
|
state
|
|
.world
|
|
.get::<&client::wl_output::WlOutput>(*entity)
|
|
.unwrap()
|
|
.release();
|
|
todo!("handle wloutput destruction");
|
|
}
|
|
_ => warn!("unhandled output request {request:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection>
|
|
Dispatch<s_dmabuf::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, Entity>
|
|
for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &s_dmabuf::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
|
|
request: <s_dmabuf::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1 as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
use s_dmabuf::zwp_linux_dmabuf_feedback_v1::Request::*;
|
|
match request {
|
|
Destroy => {
|
|
state
|
|
.world
|
|
.get::<&c_dmabuf::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1>(
|
|
*entity,
|
|
)
|
|
.unwrap()
|
|
.destroy();
|
|
state.world.despawn(*entity).unwrap();
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection>
|
|
Dispatch<
|
|
s_dmabuf::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
|
|
c_dmabuf::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
|
|
> for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &s_dmabuf::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
|
|
request: <s_dmabuf::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1 as Resource>::Request,
|
|
c_params: &c_dmabuf::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
|
|
_: &DisplayHandle,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
use s_dmabuf::zwp_linux_buffer_params_v1::Request::*;
|
|
match request {
|
|
// TODO: Xwayland doesn't actually seem to use the Create request, and I don't feel like implementing it...
|
|
Create { .. } => todo!(),
|
|
CreateImmed {
|
|
buffer_id,
|
|
width,
|
|
height,
|
|
format,
|
|
flags,
|
|
} => {
|
|
let entity = state.world.reserve_entity();
|
|
let client = c_params.create_immed(
|
|
width,
|
|
height,
|
|
format,
|
|
convert_wenum(flags),
|
|
&state.qh,
|
|
entity,
|
|
);
|
|
let server = data_init.init(buffer_id, entity);
|
|
state.world.spawn_at(entity, (client, server));
|
|
}
|
|
Add {
|
|
fd,
|
|
plane_idx,
|
|
offset,
|
|
stride,
|
|
modifier_hi,
|
|
modifier_lo,
|
|
} => {
|
|
c_params.add(
|
|
fd.as_fd(),
|
|
plane_idx,
|
|
offset,
|
|
stride,
|
|
modifier_hi,
|
|
modifier_lo,
|
|
);
|
|
}
|
|
Destroy => {
|
|
c_params.destroy();
|
|
}
|
|
_ => warn!("unhandled params request: {request:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection>
|
|
Dispatch<
|
|
s_dmabuf::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
|
|
ClientGlobalWrapper<c_dmabuf::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1>,
|
|
> for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &s_dmabuf::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
|
|
request: <s_dmabuf::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1 as Resource>::Request,
|
|
client: &ClientGlobalWrapper<c_dmabuf::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1>,
|
|
_: &DisplayHandle,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
use s_dmabuf::zwp_linux_dmabuf_v1::Request::*;
|
|
match request {
|
|
Destroy => {
|
|
client.destroy();
|
|
}
|
|
CreateParams { params_id } => {
|
|
let c_params = client.create_params(&state.qh, ());
|
|
data_init.init(params_id, c_params);
|
|
}
|
|
GetDefaultFeedback { id } => {
|
|
let entity = state.world.reserve_entity();
|
|
let client = client.get_default_feedback(&state.qh, entity);
|
|
let server = data_init.init(id, entity);
|
|
state.world.spawn_at(entity, (client, server));
|
|
}
|
|
GetSurfaceFeedback { id, surface } => {
|
|
let entity = state.world.reserve_entity();
|
|
let surface_entity: Entity = surface.data().copied().unwrap();
|
|
let client = {
|
|
let c_surface = state
|
|
.world
|
|
.get::<&client::wl_surface::WlSurface>(surface_entity)
|
|
.unwrap();
|
|
client.get_surface_feedback(&c_surface, &state.qh, entity)
|
|
};
|
|
let server = data_init.init(id, entity);
|
|
|
|
state.world.spawn_at(entity, (client, server));
|
|
}
|
|
_ => warn!("unhandled dmabuf request: {request:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<WlDrmServer, Entity> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &WlDrmServer,
|
|
request: <WlDrmServer as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
use wl_drm::server::wl_drm::Request::*;
|
|
|
|
type DrmFn = dyn FnOnce(
|
|
&wl_drm::client::wl_drm::WlDrm,
|
|
Entity,
|
|
&QueueHandle<MyWorld>,
|
|
) -> client::wl_buffer::WlBuffer;
|
|
|
|
let mut bufs: Option<(Box<DrmFn>, wayland_server::New<WlBuffer>)> = None;
|
|
match request {
|
|
CreateBuffer {
|
|
id,
|
|
name,
|
|
width,
|
|
height,
|
|
stride,
|
|
format,
|
|
} => {
|
|
bufs = Some((
|
|
Box::new(move |drm, key, qh| {
|
|
drm.create_buffer(name, width, height, stride, format, qh, key)
|
|
}),
|
|
id,
|
|
));
|
|
}
|
|
CreatePlanarBuffer {
|
|
id,
|
|
name,
|
|
width,
|
|
height,
|
|
format,
|
|
offset0,
|
|
stride0,
|
|
offset1,
|
|
stride1,
|
|
offset2,
|
|
stride2,
|
|
} => {
|
|
bufs = Some((
|
|
Box::new(move |drm, key, qh| {
|
|
drm.create_planar_buffer(
|
|
name, width, height, format, offset0, stride0, offset1, stride1,
|
|
offset2, stride2, qh, key,
|
|
)
|
|
}),
|
|
id,
|
|
));
|
|
}
|
|
CreatePrimeBuffer {
|
|
id,
|
|
name,
|
|
width,
|
|
height,
|
|
format,
|
|
offset0,
|
|
stride0,
|
|
offset1,
|
|
stride1,
|
|
offset2,
|
|
stride2,
|
|
} => {
|
|
bufs = Some((
|
|
Box::new(move |drm, key, qh| {
|
|
drm.create_prime_buffer(
|
|
name.as_fd(),
|
|
width,
|
|
height,
|
|
format,
|
|
offset0,
|
|
stride0,
|
|
offset1,
|
|
stride1,
|
|
offset2,
|
|
stride2,
|
|
qh,
|
|
key,
|
|
)
|
|
}),
|
|
id,
|
|
));
|
|
}
|
|
Authenticate { id } => {
|
|
state
|
|
.world
|
|
.get::<&wl_drm::client::wl_drm::WlDrm>(*entity)
|
|
.unwrap()
|
|
.authenticate(id);
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
|
|
if let Some((buf_create, id)) = bufs {
|
|
let new_entity = state.world.reserve_entity();
|
|
let client = {
|
|
let drm_client = state
|
|
.world
|
|
.get::<&wl_drm::client::wl_drm::WlDrm>(*entity)
|
|
.unwrap();
|
|
buf_create(&drm_client, new_entity, &state.qh)
|
|
};
|
|
let server = data_init.init(id, new_entity);
|
|
state.world.spawn_at(new_entity, (client, server));
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<XdgOutputServer, Entity> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &XdgOutputServer,
|
|
request: <XdgOutputServer as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
let s_xdgo::Request::Destroy = request else {
|
|
unreachable!();
|
|
};
|
|
|
|
let (client, _) = state
|
|
.world
|
|
.query_one_mut::<(&XdgOutputClient, &XdgOutputServer)>(*entity)
|
|
.unwrap();
|
|
client.destroy();
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<OutputManServer, ClientGlobalWrapper<OutputManClient>>
|
|
for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &OutputManServer,
|
|
request: <OutputManServer as Resource>::Request,
|
|
client: &ClientGlobalWrapper<OutputManClient>,
|
|
_: &DisplayHandle,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
match request {
|
|
s_output_man::Request::GetXdgOutput { id, output } => {
|
|
let entity: Entity = output.data().copied().unwrap();
|
|
let client = {
|
|
let c_output = state
|
|
.world
|
|
.get::<&client::wl_output::WlOutput>(entity)
|
|
.unwrap();
|
|
client.get_xdg_output(&c_output, &state.qh, entity)
|
|
};
|
|
let server = data_init.init(id, entity);
|
|
state.world.insert(entity, (client, server)).unwrap();
|
|
}
|
|
s_output_man::Request::Destroy => {}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<ConfinedPointerServer, Entity> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &ConfinedPointerServer,
|
|
request: <ConfinedPointerServer as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
let client = state.world.get::<&ConfinedPointerClient>(*entity).unwrap();
|
|
simple_event_shunt! {
|
|
client, request: cp::Request => [
|
|
SetRegion {
|
|
|region| region.as_ref().map(|r| r.data().unwrap())
|
|
},
|
|
Destroy
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<LockedPointerServer, Entity> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &LockedPointerServer,
|
|
request: <LockedPointerServer as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
match request {
|
|
lp::Request::SetCursorPositionHint {
|
|
surface_x,
|
|
surface_y,
|
|
} => {
|
|
let (client, scale) = state
|
|
.world
|
|
.query_one_mut::<(&LockedPointerClient, &SurfaceScaleFactor)>(*entity)
|
|
.unwrap();
|
|
|
|
// Xwayland believes that the surface is actually <surface scale factor> times bigger
|
|
// than it currently is, and therefore that the cursor position is also scaled up by the same
|
|
// amount. So we need to divide the cursor position from Xwayland by the surface scale
|
|
// to get where the cursor should actually be positioned.
|
|
|
|
client.set_cursor_position_hint(surface_x / scale.0, surface_y / scale.0);
|
|
}
|
|
lp::Request::Destroy => {
|
|
{
|
|
let client = state.world.get::<&LockedPointerClient>(*entity).unwrap();
|
|
client.destroy();
|
|
}
|
|
state.world.despawn(*entity).unwrap();
|
|
}
|
|
_ => warn!("unhandled locked pointer request: {request:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection>
|
|
Dispatch<PointerConstraintsServer, ClientGlobalWrapper<PointerConstraintsClient>>
|
|
for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &PointerConstraintsServer,
|
|
request: <PointerConstraintsServer as Resource>::Request,
|
|
client: &ClientGlobalWrapper<PointerConstraintsClient>,
|
|
_: &DisplayHandle,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
use pc::Request;
|
|
|
|
match request {
|
|
Request::ConfinePointer {
|
|
id,
|
|
surface,
|
|
pointer,
|
|
region,
|
|
lifetime,
|
|
} => {
|
|
let surf_key: Entity = surface.data().copied().unwrap();
|
|
let ptr_key: Entity = pointer.data().copied().unwrap();
|
|
|
|
let entity = state.world.reserve_entity();
|
|
let client = {
|
|
let c_surface = state
|
|
.world
|
|
.get::<&client::wl_surface::WlSurface>(surf_key)
|
|
.unwrap();
|
|
let c_ptr = state
|
|
.world
|
|
.get::<&client::wl_pointer::WlPointer>(ptr_key)
|
|
.unwrap();
|
|
client.confine_pointer(
|
|
&c_surface,
|
|
&c_ptr,
|
|
region.as_ref().map(|r| r.data().unwrap()),
|
|
convert_wenum(lifetime),
|
|
&state.qh,
|
|
entity,
|
|
)
|
|
};
|
|
let server = data_init.init(id, entity);
|
|
|
|
state.world.spawn_at(entity, (client, server));
|
|
}
|
|
Request::LockPointer {
|
|
id,
|
|
surface,
|
|
pointer,
|
|
region,
|
|
lifetime,
|
|
} => {
|
|
let surf_key: Entity = surface.data().copied().unwrap();
|
|
let ptr_key: Entity = pointer.data().copied().unwrap();
|
|
let entity = state.world.reserve_entity();
|
|
|
|
let client = {
|
|
let c_surface = state
|
|
.world
|
|
.get::<&client::wl_surface::WlSurface>(surf_key)
|
|
.unwrap();
|
|
let c_ptr = state
|
|
.world
|
|
.get::<&client::wl_pointer::WlPointer>(ptr_key)
|
|
.unwrap();
|
|
client.lock_pointer(
|
|
&c_surface,
|
|
&c_ptr,
|
|
region.as_ref().map(|r| r.data().unwrap()),
|
|
convert_wenum(lifetime),
|
|
&state.qh,
|
|
entity,
|
|
)
|
|
};
|
|
let server = data_init.init(id, entity);
|
|
let surface_scale = state
|
|
.world
|
|
.get::<&SurfaceScaleFactor>(surf_key)
|
|
.as_deref()
|
|
.copied()
|
|
.unwrap();
|
|
|
|
state
|
|
.world
|
|
.spawn_at(entity, (client, server, surface_scale));
|
|
}
|
|
Request::Destroy => {
|
|
client.destroy();
|
|
}
|
|
_ => unreachable!("unhandled pointer constraints request"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection>
|
|
Dispatch<
|
|
s_tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2,
|
|
ClientGlobalWrapper<c_tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2>,
|
|
> for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &s_tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2,
|
|
request: <s_tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2 as Resource>::Request,
|
|
client: &ClientGlobalWrapper<c_tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2>,
|
|
_: &DisplayHandle,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
use s_tablet::zwp_tablet_manager_v2::Request::*;
|
|
match request {
|
|
GetTabletSeat { tablet_seat, seat } => {
|
|
let seat_key: Entity = seat.data().copied().unwrap();
|
|
|
|
let entity = state.world.reserve_entity();
|
|
let client = {
|
|
let c_seat = state
|
|
.world
|
|
.get::<&client::wl_seat::WlSeat>(seat_key)
|
|
.unwrap();
|
|
client.get_tablet_seat(&c_seat, &state.qh, entity)
|
|
};
|
|
let server = data_init.init(tablet_seat, entity);
|
|
|
|
state.world.spawn_at(entity, (client, server));
|
|
}
|
|
other => {
|
|
warn!("unhandled tablet request: {other:?}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
only_destroy_request_impl!(
|
|
s_tablet::zwp_tablet_seat_v2::ZwpTabletSeatV2,
|
|
c_tablet::zwp_tablet_seat_v2::ZwpTabletSeatV2
|
|
);
|
|
only_destroy_request_impl!(
|
|
s_tablet::zwp_tablet_v2::ZwpTabletV2,
|
|
c_tablet::zwp_tablet_v2::ZwpTabletV2
|
|
);
|
|
only_destroy_request_impl!(
|
|
s_tablet::zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2,
|
|
c_tablet::zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2
|
|
);
|
|
|
|
impl<S: X11Selection> Dispatch<s_tablet::zwp_tablet_pad_v2::ZwpTabletPadV2, Entity>
|
|
for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &Client,
|
|
_: &s_tablet::zwp_tablet_pad_v2::ZwpTabletPadV2,
|
|
request: <s_tablet::zwp_tablet_pad_v2::ZwpTabletPadV2 as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
let client = state
|
|
.world
|
|
.get::<&c_tablet::zwp_tablet_pad_v2::ZwpTabletPadV2>(*entity)
|
|
.unwrap();
|
|
match request {
|
|
s_tablet::zwp_tablet_pad_v2::Request::SetFeedback {
|
|
button,
|
|
description,
|
|
serial,
|
|
} => {
|
|
client.set_feedback(button, description, serial);
|
|
}
|
|
s_tablet::zwp_tablet_pad_v2::Request::Destroy => {
|
|
client.destroy();
|
|
drop(client);
|
|
state.world.despawn(*entity).unwrap();
|
|
}
|
|
other => warn!("unhandled tablet pad request: {other:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<s_tablet::zwp_tablet_tool_v2::ZwpTabletToolV2, Entity>
|
|
for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &Client,
|
|
_: &s_tablet::zwp_tablet_tool_v2::ZwpTabletToolV2,
|
|
request: <s_tablet::zwp_tablet_tool_v2::ZwpTabletToolV2 as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
let client = state
|
|
.world
|
|
.get::<&c_tablet::zwp_tablet_tool_v2::ZwpTabletToolV2>(*entity)
|
|
.unwrap();
|
|
match request {
|
|
s_tablet::zwp_tablet_tool_v2::Request::SetCursor {
|
|
serial,
|
|
surface,
|
|
hotspot_x,
|
|
hotspot_y,
|
|
} => {
|
|
let surf_key: Option<Entity> = surface.map(|s| s.data().copied().unwrap());
|
|
let c_surface = surf_key.map(|key| {
|
|
state
|
|
.world
|
|
.get::<&client::wl_surface::WlSurface>(key)
|
|
.unwrap()
|
|
});
|
|
client.set_cursor(serial, c_surface.as_deref(), hotspot_x, hotspot_y);
|
|
}
|
|
s_tablet::zwp_tablet_tool_v2::Request::Destroy => {
|
|
client.destroy();
|
|
drop(client);
|
|
state.world.despawn(*entity).unwrap();
|
|
}
|
|
other => warn!("unhandled tablet tool request: {other:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<s_tablet::zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2, Entity>
|
|
for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &Client,
|
|
_: &s_tablet::zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2,
|
|
request: <s_tablet::zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2 as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
let client = state
|
|
.world
|
|
.get::<&c_tablet::zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2>(*entity)
|
|
.unwrap();
|
|
match request {
|
|
s_tablet::zwp_tablet_pad_ring_v2::Request::SetFeedback {
|
|
description,
|
|
serial,
|
|
} => {
|
|
client.set_feedback(description, serial);
|
|
}
|
|
s_tablet::zwp_tablet_pad_ring_v2::Request::Destroy => {
|
|
client.destroy();
|
|
drop(client);
|
|
state.world.despawn(*entity).unwrap();
|
|
}
|
|
other => warn!("unhandled tablet pad ring request: {other:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<s_tablet::zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2, Entity>
|
|
for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &Client,
|
|
_: &s_tablet::zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2,
|
|
request: <s_tablet::zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2 as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
let client = state
|
|
.world
|
|
.get::<&c_tablet::zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2>(*entity)
|
|
.unwrap();
|
|
|
|
match request {
|
|
s_tablet::zwp_tablet_pad_strip_v2::Request::SetFeedback {
|
|
description,
|
|
serial,
|
|
} => {
|
|
client.set_feedback(description, serial);
|
|
}
|
|
s_tablet::zwp_tablet_pad_strip_v2::Request::Destroy => {
|
|
client.destroy();
|
|
drop(client);
|
|
state.world.despawn(*entity).unwrap();
|
|
}
|
|
other => warn!("unhandled tablet pad strip request: {other:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection>
|
|
Dispatch<
|
|
s_sync::wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1,
|
|
ClientGlobalWrapper<c_sync::wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1>,
|
|
> for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &Client,
|
|
_: &s_sync::wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1,
|
|
request: <s_sync::wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1 as Resource>::Request,
|
|
client: &ClientGlobalWrapper<
|
|
c_sync::wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1,
|
|
>,
|
|
_: &DisplayHandle,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
use s_sync::wp_linux_drm_syncobj_manager_v1::Request;
|
|
match request {
|
|
Request::GetSurface { id, surface } => {
|
|
let c_surface = state
|
|
.world
|
|
.get::<&client::wl_surface::WlSurface>(surface.data().copied().unwrap())
|
|
.unwrap();
|
|
let client = client.get_surface(&c_surface, &state.qh, ());
|
|
data_init.init(id, client);
|
|
}
|
|
Request::ImportTimeline { id, fd } => {
|
|
let timeline = client.import_timeline(fd.as_fd(), &state.qh, ());
|
|
data_init.init(id, timeline);
|
|
}
|
|
Request::Destroy => {
|
|
client.destroy();
|
|
}
|
|
other => warn!("unhandled drm syncobj request: {other:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection>
|
|
Dispatch<
|
|
s_sync::wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
|
|
c_sync::wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
|
|
> for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
_: &mut Self,
|
|
_: &Client,
|
|
_: &s_sync::wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
|
|
request: <s_sync::wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1 as Resource>::Request,
|
|
client: &c_sync::wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
use s_sync::wp_linux_drm_syncobj_surface_v1::Request;
|
|
match request {
|
|
Request::SetAcquirePoint {
|
|
timeline,
|
|
point_hi,
|
|
point_lo,
|
|
} => {
|
|
let c_timeline: &c_sync::wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1 = timeline.data().unwrap();
|
|
client.set_acquire_point(c_timeline, point_hi, point_lo);
|
|
}
|
|
Request::SetReleasePoint {
|
|
timeline,
|
|
point_hi,
|
|
point_lo,
|
|
} => {
|
|
let c_timeline: &c_sync::wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1 = timeline.data().unwrap();
|
|
client.set_release_point(c_timeline, point_hi, point_lo);
|
|
}
|
|
Request::Destroy => client.destroy(),
|
|
other => warn!("unhandled drm syncobj surface request: {other:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection>
|
|
Dispatch<
|
|
s_sync::wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1,
|
|
c_sync::wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1,
|
|
> for InnerServerState<S>
|
|
{
|
|
fn request(
|
|
_: &mut Self,
|
|
_: &Client,
|
|
_: &s_sync::wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1,
|
|
request: <s_sync::wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1 as Resource>::Request,
|
|
client: &c_sync::wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
use s_sync::wp_linux_drm_syncobj_timeline_v1::Request;
|
|
match request {
|
|
Request::Destroy => client.destroy(),
|
|
other => warn!("unhandled drm syncobj timeline request: {other:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub(crate) struct ClientGlobalWrapper<T: Proxy>(Arc<OnceLock<T>>);
|
|
impl<T: Proxy> std::ops::Deref for ClientGlobalWrapper<T> {
|
|
type Target = T;
|
|
fn deref(&self) -> &Self::Target {
|
|
self.0.get().unwrap()
|
|
}
|
|
}
|
|
|
|
impl<T: Proxy> Default for ClientGlobalWrapper<T> {
|
|
fn default() -> Self {
|
|
Self(Arc::default())
|
|
}
|
|
}
|
|
|
|
macro_rules! global_dispatch_no_events {
|
|
($server:ty, $client:ty) => {
|
|
impl<S: X11Selection> GlobalDispatch<$server, Global> for InnerServerState<S>
|
|
where
|
|
InnerServerState<S>: Dispatch<$server, ClientGlobalWrapper<$client>>,
|
|
MyWorld: wayland_client::Dispatch<$client, ()>,
|
|
{
|
|
fn bind(
|
|
state: &mut Self,
|
|
_: &DisplayHandle,
|
|
_: &wayland_server::Client,
|
|
resource: wayland_server::New<$server>,
|
|
data: &Global,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
let client = ClientGlobalWrapper::<$client>::default();
|
|
let server = data_init.init(resource, client.clone());
|
|
client
|
|
.0
|
|
.set(state.world.global_list.registry().bind::<$client, _, _>(
|
|
data.name,
|
|
server.version(),
|
|
&state.qh,
|
|
(),
|
|
))
|
|
.unwrap();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! global_dispatch_with_events {
|
|
($server:ty, $client:ty) => {
|
|
impl<S: X11Selection> GlobalDispatch<$server, Global> for InnerServerState<S>
|
|
where
|
|
$server: Resource,
|
|
$client: Proxy,
|
|
InnerServerState<S>: Dispatch<$server, Entity>,
|
|
MyWorld: wayland_client::Dispatch<$client, Entity>,
|
|
{
|
|
fn bind(
|
|
state: &mut Self,
|
|
_: &DisplayHandle,
|
|
_: &wayland_server::Client,
|
|
resource: wayland_server::New<$server>,
|
|
data: &Global,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
let entity = state.world.reserve_entity();
|
|
let server = data_init.init(resource, entity);
|
|
let client = state.world.global_list.registry().bind::<$client, _, _>(
|
|
data.name,
|
|
server.version(),
|
|
&state.qh,
|
|
entity,
|
|
);
|
|
state.world.spawn_at(entity, (server, client));
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
global_dispatch_no_events!(WlShm, client::wl_shm::WlShm);
|
|
global_dispatch_no_events!(WlCompositor, client::wl_compositor::WlCompositor);
|
|
global_dispatch_no_events!(RelativePointerManServer, RelativePointerManClient);
|
|
global_dispatch_no_events!(
|
|
s_dmabuf::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
|
|
c_dmabuf::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1
|
|
);
|
|
global_dispatch_no_events!(OutputManServer, OutputManClient);
|
|
global_dispatch_no_events!(PointerConstraintsServer, PointerConstraintsClient);
|
|
global_dispatch_no_events!(
|
|
s_tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2,
|
|
c_tablet::zwp_tablet_manager_v2::ZwpTabletManagerV2
|
|
);
|
|
global_dispatch_no_events!(
|
|
s_sync::wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1,
|
|
c_sync::wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1
|
|
);
|
|
|
|
impl<S: X11Selection> GlobalDispatch<WlSeat, Global> for InnerServerState<S> {
|
|
fn bind(
|
|
state: &mut Self,
|
|
_: &DisplayHandle,
|
|
_: &wayland_server::Client,
|
|
resource: wayland_server::New<WlSeat>,
|
|
data: &Global,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
let entity = state.world.reserve_entity();
|
|
let server = data_init.init(resource, entity);
|
|
let client = state
|
|
.world
|
|
.global_list
|
|
.registry()
|
|
.bind::<client::wl_seat::WlSeat, _, _>(data.name, server.version(), &state.qh, entity);
|
|
|
|
state.selection_states.seat_created(&state.qh, &client);
|
|
state.world.spawn_at(entity, (server, client));
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> GlobalDispatch<WlOutput, Global> for InnerServerState<S> {
|
|
fn bind(
|
|
state: &mut Self,
|
|
_: &DisplayHandle,
|
|
_: &wayland_server::Client,
|
|
resource: wayland_server::New<WlOutput>,
|
|
data: &Global,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
let entity = state.world.reserve_entity();
|
|
let server = data_init.init(resource, entity);
|
|
let client = state
|
|
.world
|
|
.global_list
|
|
.registry()
|
|
.bind::<client::wl_output::WlOutput, _, _>(
|
|
data.name,
|
|
server.version(),
|
|
&state.qh,
|
|
entity,
|
|
);
|
|
state.world.spawn_at(
|
|
entity,
|
|
(
|
|
server,
|
|
client,
|
|
event::OutputScaleFactor::Output(1),
|
|
event::OutputDimensions::default(),
|
|
GlobalName(data.name),
|
|
),
|
|
);
|
|
state.updated_outputs.push(entity);
|
|
}
|
|
}
|
|
global_dispatch_with_events!(WlDrmServer, WlDrmClient);
|
|
|
|
impl<S: X11Selection> GlobalDispatch<XwaylandShellV1, ()> for InnerServerState<S> {
|
|
fn bind(
|
|
_: &mut Self,
|
|
_: &DisplayHandle,
|
|
_: &wayland_server::Client,
|
|
resource: wayland_server::New<XwaylandShellV1>,
|
|
_: &(),
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
data_init.init(resource, ());
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<XwaylandShellV1, ()> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
client: &wayland_server::Client,
|
|
_: &XwaylandShellV1,
|
|
request: <XwaylandShellV1 as Resource>::Request,
|
|
_: &(),
|
|
dhandle: &DisplayHandle,
|
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
use xwayland_shell_v1::Request;
|
|
match request {
|
|
Request::GetXwaylandSurface { id, surface } => {
|
|
let e: Entity = surface.data().copied().unwrap();
|
|
if state.world.entity(e).unwrap().has::<XwaylandSurfaceV1>() {
|
|
error!("Surface {surface:?} already has the xwayland surface role!");
|
|
client.kill(
|
|
dhandle,
|
|
wayland_client::backend::protocol::ProtocolError {
|
|
code: 0,
|
|
object_id: surface.id().protocol_id(),
|
|
object_interface: "wl_surface".to_string(),
|
|
message: "Surface already has role".to_string(),
|
|
},
|
|
);
|
|
return;
|
|
}
|
|
|
|
data_init.init(id, e);
|
|
}
|
|
Request::Destroy => {}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: X11Selection> Dispatch<XwaylandSurfaceV1, Entity> for InnerServerState<S> {
|
|
fn request(
|
|
state: &mut Self,
|
|
_: &wayland_server::Client,
|
|
_: &XwaylandSurfaceV1,
|
|
request: <XwaylandSurfaceV1 as Resource>::Request,
|
|
entity: &Entity,
|
|
_: &DisplayHandle,
|
|
_: &mut wayland_server::DataInit<'_, Self>,
|
|
) {
|
|
use xwayland_surface_v1::Request;
|
|
match request {
|
|
Request::SetSerial {
|
|
serial_lo,
|
|
serial_hi,
|
|
} => {
|
|
let surface_id = state.world.get::<&WlSurface>(*entity).unwrap().id();
|
|
let serial = SurfaceSerial([serial_lo, serial_hi]);
|
|
let win_entity = state
|
|
.world
|
|
.query_mut::<&SurfaceSerial>()
|
|
.without::<&WlSurface>()
|
|
.into_iter()
|
|
.find(|(_, surface_serial)| **surface_serial == serial)
|
|
.map(|i| i.0);
|
|
|
|
if let Some(win_entity) = win_entity {
|
|
let bundle = state.world.take(win_entity).unwrap();
|
|
if !bundle.has::<x::Window>() {
|
|
warn!(
|
|
"Window with same serial ({serial:?}) as {surface_id} has been destroyed?"
|
|
);
|
|
return;
|
|
}
|
|
let mut builder = hecs::EntityBuilder::new();
|
|
builder.add_bundle(bundle);
|
|
state.world.insert(*entity, builder.build()).unwrap();
|
|
state.world.remove_one::<SurfaceSerial>(*entity).unwrap();
|
|
let data = state.world.entity(*entity).unwrap();
|
|
let win = data.get::<&x::Window>().as_deref().copied().unwrap();
|
|
state.windows.insert(win, *entity);
|
|
debug!("associate {surface_id} with {win:?} (serial {serial:?})");
|
|
if data.get::<&WindowData>().unwrap().mapped
|
|
&& state.create_role_window(win, *entity)
|
|
{
|
|
state.activate_window(win);
|
|
}
|
|
} else {
|
|
state.world.insert(*entity, (serial,)).unwrap();
|
|
}
|
|
}
|
|
Request::Destroy => {}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|