Initial commit
This commit is contained in:
commit
85b940e427
21 changed files with 6825 additions and 0 deletions
273
testwl/Cargo.lock
generated
Normal file
273
testwl/Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "dlib"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
|
||||
dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "2.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "testwl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"wayland-server",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "wayland-backend"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"downcast-rs",
|
||||
"rustix",
|
||||
"scoped-tls",
|
||||
"smallvec",
|
||||
"wayland-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-scanner"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quick-xml",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-server"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00e6e4d5c285bc24ba4ed2d5a4bd4febd5fd904451f465973225c8e99772fdb7"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"downcast-rs",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"wayland-backend",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-sys"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af"
|
||||
dependencies = [
|
||||
"dlib",
|
||||
"log",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||
9
testwl/Cargo.toml
Normal file
9
testwl/Cargo.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "testwl"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
wayland-protocols = { version = "0.31.2", features = ["server", "unstable"] }
|
||||
wayland-server = "0.31.1"
|
||||
wl_drm = { path = "../wl_drm" }
|
||||
839
testwl/src/lib.rs
Normal file
839
testwl/src/lib.rs
Normal file
|
|
@ -0,0 +1,839 @@
|
|||
use std::collections::{hash_map, HashMap, HashSet};
|
||||
use std::os::fd::BorrowedFd;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::time::Instant;
|
||||
use wayland_protocols::{
|
||||
wp::{
|
||||
linux_dmabuf::zv1::server::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
|
||||
relative_pointer::zv1::server::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
|
||||
viewporter::server::wp_viewporter::WpViewporter,
|
||||
},
|
||||
xdg::{
|
||||
shell::server::{
|
||||
xdg_popup::{self, XdgPopup},
|
||||
xdg_positioner::{self, XdgPositioner},
|
||||
xdg_surface::XdgSurface,
|
||||
xdg_toplevel::{self, XdgToplevel},
|
||||
xdg_wm_base::{self, XdgWmBase},
|
||||
},
|
||||
xdg_output::zv1::server::zxdg_output_manager_v1::ZxdgOutputManagerV1,
|
||||
},
|
||||
};
|
||||
use wayland_server::{
|
||||
backend::protocol::ProtocolError,
|
||||
protocol::{
|
||||
self as proto,
|
||||
wl_buffer::WlBuffer,
|
||||
wl_callback::WlCallback,
|
||||
wl_compositor::WlCompositor,
|
||||
wl_output::WlOutput,
|
||||
wl_pointer::{self, WlPointer},
|
||||
wl_seat::{self, WlSeat},
|
||||
wl_shm::WlShm,
|
||||
wl_shm_pool::WlShmPool,
|
||||
wl_surface::WlSurface,
|
||||
},
|
||||
Client, Dispatch, Display, DisplayHandle, GlobalDispatch, Resource,
|
||||
};
|
||||
use wl_drm::server::wl_drm::WlDrm;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct BufferDamage {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct SurfaceData {
|
||||
pub surface: WlSurface,
|
||||
pub buffer: Option<WlBuffer>,
|
||||
pub last_damage: Option<BufferDamage>,
|
||||
pub role: Option<SurfaceRole>,
|
||||
}
|
||||
|
||||
impl SurfaceData {
|
||||
pub fn xdg(&self) -> &XdgSurfaceData {
|
||||
match self.role.as_ref().expect("Surface missing role") {
|
||||
SurfaceRole::Toplevel(ref t) => &t.xdg,
|
||||
SurfaceRole::Popup(ref p) => &p.xdg,
|
||||
SurfaceRole::Cursor => panic!("cursor surface doesn't have an XdgSurface"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toplevel(&self) -> &Toplevel {
|
||||
match self.role.as_ref().expect("Surface missing role") {
|
||||
SurfaceRole::Toplevel(ref t) => t,
|
||||
other => panic!("Surface role was not toplevel: {other:?}"),
|
||||
}
|
||||
}
|
||||
pub fn popup(&self) -> &Popup {
|
||||
match self.role.as_ref().expect("Surface missing role") {
|
||||
SurfaceRole::Popup(ref p) => p,
|
||||
other => panic!("Surface role was not popup: {other:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum SurfaceRole {
|
||||
Toplevel(Toplevel),
|
||||
Popup(Popup),
|
||||
Cursor,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Toplevel {
|
||||
pub xdg: XdgSurfaceData,
|
||||
pub toplevel: XdgToplevel,
|
||||
pub min_size: Option<Vec2>,
|
||||
pub max_size: Option<Vec2>,
|
||||
pub states: Vec<xdg_toplevel::State>,
|
||||
pub closed: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Popup {
|
||||
pub xdg: XdgSurfaceData,
|
||||
pub parent: XdgSurface,
|
||||
pub popup: XdgPopup,
|
||||
pub positioner_state: PositionerState,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
|
||||
pub struct Vec2 {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct XdgSurfaceData {
|
||||
pub surface: XdgSurface,
|
||||
pub last_configure_serial: u32,
|
||||
}
|
||||
|
||||
impl XdgSurfaceData {
|
||||
fn new(surface: XdgSurface) -> Self {
|
||||
Self {
|
||||
surface,
|
||||
last_configure_serial: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn configure(&mut self, serial: u32) {
|
||||
self.surface.configure(serial);
|
||||
self.last_configure_serial = serial;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct SurfaceId(u32);
|
||||
|
||||
#[derive(Hash, Clone, Copy, Eq, PartialEq)]
|
||||
struct PositionerId(u32);
|
||||
|
||||
struct State {
|
||||
surfaces: HashMap<SurfaceId, SurfaceData>,
|
||||
positioners: HashMap<PositionerId, PositionerState>,
|
||||
buffers: HashSet<WlBuffer>,
|
||||
begin: Instant,
|
||||
last_surface_id: Option<SurfaceId>,
|
||||
callbacks: Vec<WlCallback>,
|
||||
pointer: Option<WlPointer>,
|
||||
configure_serial: u32,
|
||||
}
|
||||
|
||||
impl State {
|
||||
#[track_caller]
|
||||
fn configure_toplevel(
|
||||
&mut self,
|
||||
surface_id: SurfaceId,
|
||||
width: i32,
|
||||
height: i32,
|
||||
states: Vec<xdg_toplevel::State>,
|
||||
) {
|
||||
let last_serial = self.configure_serial;
|
||||
let toplevel = self.get_toplevel(surface_id);
|
||||
toplevel.states = states.clone();
|
||||
let states = states
|
||||
.into_iter()
|
||||
.map(|state| u32::from(state) as u8)
|
||||
.collect();
|
||||
toplevel.toplevel.configure(width, height, states);
|
||||
toplevel.xdg.configure(last_serial);
|
||||
self.configure_serial += 1;
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn get_toplevel(&mut self, surface_id: SurfaceId) -> &mut Toplevel {
|
||||
let surface = self.surfaces.get_mut(&surface_id).unwrap();
|
||||
match &mut surface.role {
|
||||
Some(SurfaceRole::Toplevel(t)) => t,
|
||||
other => panic!("Surface does not have toplevel role: {:?}", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
surfaces: Default::default(),
|
||||
buffers: Default::default(),
|
||||
positioners: Default::default(),
|
||||
begin: Instant::now(),
|
||||
last_surface_id: None,
|
||||
callbacks: Vec::new(),
|
||||
pointer: None,
|
||||
configure_serial: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! simple_global_dispatch {
|
||||
($type:ty) => {
|
||||
impl GlobalDispatch<$type, ()> for State {
|
||||
fn bind(
|
||||
_: &mut Self,
|
||||
_: &DisplayHandle,
|
||||
_: &wayland_server::Client,
|
||||
resource: wayland_server::New<$type>,
|
||||
_: &(),
|
||||
data_init: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
data_init.init(resource, ());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct Server {
|
||||
display: Display<State>,
|
||||
dh: DisplayHandle,
|
||||
state: State,
|
||||
client: Option<Client>,
|
||||
configure_serial: u32,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn new(noops: bool) -> Self {
|
||||
let display = Display::new().unwrap();
|
||||
let dh = display.handle();
|
||||
|
||||
macro_rules! global_noop {
|
||||
($type:ty) => {
|
||||
if noops {
|
||||
dh.create_global::<State, $type, _>(1, ());
|
||||
}
|
||||
simple_global_dispatch!($type);
|
||||
impl Dispatch<$type, ()> for State {
|
||||
fn request(
|
||||
_: &mut Self,
|
||||
_: &Client,
|
||||
_: &$type,
|
||||
_: <$type as Resource>::Request,
|
||||
_: &(),
|
||||
_: &DisplayHandle,
|
||||
_: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
todo!("Dispatch for {} is no-op", stringify!($type));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
dh.create_global::<State, WlCompositor, _>(6, ());
|
||||
dh.create_global::<State, WlShm, _>(1, ());
|
||||
dh.create_global::<State, XdgWmBase, _>(6, ());
|
||||
dh.create_global::<State, WlSeat, _>(5, ());
|
||||
global_noop!(WlOutput);
|
||||
global_noop!(ZwpLinuxDmabufV1);
|
||||
global_noop!(ZwpRelativePointerManagerV1);
|
||||
global_noop!(ZxdgOutputManagerV1);
|
||||
global_noop!(WpViewporter);
|
||||
global_noop!(WlDrm);
|
||||
|
||||
Self {
|
||||
display,
|
||||
dh,
|
||||
state: State::default(),
|
||||
client: None,
|
||||
configure_serial: 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_fd(&mut self) -> BorrowedFd<'_> {
|
||||
self.display.backend().poll_fd()
|
||||
}
|
||||
|
||||
pub fn connect(&mut self, stream: UnixStream) {
|
||||
let client = self
|
||||
.dh
|
||||
.insert_client(stream, std::sync::Arc::new(()))
|
||||
.unwrap();
|
||||
assert!(
|
||||
self.client.replace(client).is_none(),
|
||||
"Client already connected to test server"
|
||||
);
|
||||
//self.dispatch();
|
||||
}
|
||||
|
||||
pub fn dispatch(&mut self) {
|
||||
self.display.dispatch_clients(&mut self.state).unwrap();
|
||||
for callback in std::mem::take(&mut self.state.callbacks) {
|
||||
callback.done(self.state.begin.elapsed().as_millis().try_into().unwrap());
|
||||
}
|
||||
self.display.flush_clients().unwrap();
|
||||
}
|
||||
|
||||
pub fn get_surface_data(&self, surface_id: SurfaceId) -> Option<&SurfaceData> {
|
||||
self.state.surfaces.get(&surface_id)
|
||||
}
|
||||
|
||||
pub fn last_created_surface_id(&self) -> Option<SurfaceId> {
|
||||
self.state.last_surface_id
|
||||
}
|
||||
|
||||
pub fn get_object<T: Resource + 'static>(
|
||||
&self,
|
||||
id: SurfaceId,
|
||||
) -> Result<T, wayland_server::backend::InvalidId> {
|
||||
let client = self.client.as_ref().unwrap();
|
||||
client.object_from_protocol_id::<T>(&self.display.handle(), id.0)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn configure_toplevel(
|
||||
&mut self,
|
||||
surface_id: SurfaceId,
|
||||
width: i32,
|
||||
height: i32,
|
||||
states: Vec<xdg_toplevel::State>,
|
||||
) {
|
||||
self.state
|
||||
.configure_toplevel(surface_id, width, height, states);
|
||||
self.display.flush_clients().unwrap();
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn configure_popup(&mut self, surface_id: SurfaceId) {
|
||||
let surface = self.state.surfaces.get_mut(&surface_id).unwrap();
|
||||
let Some(SurfaceRole::Popup(p)) = &mut surface.role else {
|
||||
panic!("Surface does not have popup role: {:?}", surface.role);
|
||||
};
|
||||
let PositionerState { size, offset, .. } = &p.positioner_state;
|
||||
let size = size.unwrap();
|
||||
p.popup.configure(offset.x, offset.y, size.x, size.y);
|
||||
p.xdg.configure(self.configure_serial);
|
||||
self.configure_serial += 1;
|
||||
self.dispatch();
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn close_toplevel(&mut self, surface_id: SurfaceId) {
|
||||
let toplevel = self.state.get_toplevel(surface_id);
|
||||
toplevel.toplevel.close();
|
||||
self.dispatch();
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn pointer(&self) -> &WlPointer {
|
||||
self.state.pointer.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
simple_global_dispatch!(WlShm);
|
||||
simple_global_dispatch!(WlCompositor);
|
||||
simple_global_dispatch!(XdgWmBase);
|
||||
|
||||
impl GlobalDispatch<WlSeat, ()> for State {
|
||||
fn bind(
|
||||
_: &mut Self,
|
||||
_: &DisplayHandle,
|
||||
_: &Client,
|
||||
resource: wayland_server::New<WlSeat>,
|
||||
_: &(),
|
||||
data_init: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
let seat = data_init.init(resource, ());
|
||||
seat.capabilities(wl_seat::Capability::Pointer);
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlSeat, ()> for State {
|
||||
fn request(
|
||||
state: &mut Self,
|
||||
_: &Client,
|
||||
_: &WlSeat,
|
||||
request: <WlSeat as Resource>::Request,
|
||||
_: &(),
|
||||
_: &DisplayHandle,
|
||||
data_init: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
match request {
|
||||
wl_seat::Request::GetPointer { id } => {
|
||||
state.pointer = Some(data_init.init(id, ()));
|
||||
}
|
||||
wl_seat::Request::Release => {}
|
||||
other => todo!("unhandled request {other:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlPointer, ()> for State {
|
||||
fn request(
|
||||
state: &mut Self,
|
||||
_: &Client,
|
||||
_: &WlPointer,
|
||||
request: <WlPointer as Resource>::Request,
|
||||
_: &(),
|
||||
_: &DisplayHandle,
|
||||
_: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
match request {
|
||||
wl_pointer::Request::SetCursor { surface, .. } => {
|
||||
if let Some(surface) = surface {
|
||||
let data = state
|
||||
.surfaces
|
||||
.get_mut(&SurfaceId(surface.id().protocol_id()))
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
data.role.replace(SurfaceRole::Cursor).is_none(),
|
||||
"Surface already had a role!"
|
||||
);
|
||||
}
|
||||
}
|
||||
wl_pointer::Request::Release => {
|
||||
state.pointer.take();
|
||||
}
|
||||
other => todo!("unhandled pointer request: {other:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgPopup, SurfaceId> for State {
|
||||
fn request(
|
||||
_: &mut Self,
|
||||
_: &Client,
|
||||
_: &XdgPopup,
|
||||
request: <XdgPopup as Resource>::Request,
|
||||
_: &SurfaceId,
|
||||
_: &DisplayHandle,
|
||||
_: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
match request {
|
||||
xdg_popup::Request::Destroy => {}
|
||||
other => todo!("unhandled request {other:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgToplevel, SurfaceId> for State {
|
||||
fn request(
|
||||
state: &mut Self,
|
||||
_: &wayland_server::Client,
|
||||
_: &XdgToplevel,
|
||||
request: <XdgToplevel as Resource>::Request,
|
||||
surface_id: &SurfaceId,
|
||||
_: &DisplayHandle,
|
||||
_: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
match request {
|
||||
xdg_toplevel::Request::SetMinSize { width, height } => {
|
||||
let data = state.surfaces.get_mut(surface_id).unwrap();
|
||||
let Some(SurfaceRole::Toplevel(toplevel)) = &mut data.role else {
|
||||
unreachable!();
|
||||
};
|
||||
toplevel.min_size = Some(Vec2 {
|
||||
x: width,
|
||||
y: height,
|
||||
});
|
||||
}
|
||||
xdg_toplevel::Request::SetMaxSize { width, height } => {
|
||||
let data = state.surfaces.get_mut(surface_id).unwrap();
|
||||
let Some(SurfaceRole::Toplevel(toplevel)) = &mut data.role else {
|
||||
unreachable!();
|
||||
};
|
||||
toplevel.max_size = Some(Vec2 {
|
||||
x: width,
|
||||
y: height,
|
||||
});
|
||||
}
|
||||
xdg_toplevel::Request::SetFullscreen { .. } => {
|
||||
let data = state.surfaces.get_mut(surface_id).unwrap();
|
||||
let Some(SurfaceRole::Toplevel(toplevel)) = &mut data.role else {
|
||||
unreachable!();
|
||||
};
|
||||
toplevel.states.push(xdg_toplevel::State::Fullscreen);
|
||||
let states = toplevel.states.clone();
|
||||
state.configure_toplevel(*surface_id, 100, 100, states);
|
||||
}
|
||||
xdg_toplevel::Request::UnsetFullscreen { .. } => {
|
||||
let data = state.surfaces.get_mut(surface_id).unwrap();
|
||||
let Some(SurfaceRole::Toplevel(toplevel)) = &mut data.role else {
|
||||
unreachable!();
|
||||
};
|
||||
let Some(pos) = toplevel
|
||||
.states
|
||||
.iter()
|
||||
.copied()
|
||||
.position(|p| p == xdg_toplevel::State::Fullscreen)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
toplevel.states.swap_remove(pos);
|
||||
let states = toplevel.states.clone();
|
||||
state.configure_toplevel(*surface_id, 100, 100, states);
|
||||
}
|
||||
xdg_toplevel::Request::Destroy => {}
|
||||
other => todo!("unhandled request {other:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgSurface, SurfaceId> for State {
|
||||
fn request(
|
||||
state: &mut Self,
|
||||
client: &wayland_server::Client,
|
||||
resource: &XdgSurface,
|
||||
request: <XdgSurface as Resource>::Request,
|
||||
surface_id: &SurfaceId,
|
||||
dh: &DisplayHandle,
|
||||
data_init: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
use wayland_protocols::xdg::shell::server::xdg_surface;
|
||||
|
||||
match request {
|
||||
xdg_surface::Request::GetToplevel { id } => {
|
||||
let toplevel = data_init.init(id, *surface_id);
|
||||
let t = Toplevel {
|
||||
xdg: XdgSurfaceData::new(resource.clone()),
|
||||
toplevel,
|
||||
min_size: None,
|
||||
max_size: None,
|
||||
states: Vec::new(),
|
||||
closed: false,
|
||||
};
|
||||
let data = state.surfaces.get_mut(surface_id).unwrap();
|
||||
data.role = Some(SurfaceRole::Toplevel(t));
|
||||
}
|
||||
xdg_surface::Request::GetPopup {
|
||||
id,
|
||||
parent,
|
||||
positioner,
|
||||
} => {
|
||||
let popup = data_init.init(id, *surface_id);
|
||||
let p = Popup {
|
||||
xdg: XdgSurfaceData::new(resource.clone()),
|
||||
popup,
|
||||
parent: parent.unwrap(),
|
||||
positioner_state: state.positioners
|
||||
[&PositionerId(positioner.id().protocol_id())]
|
||||
.clone(),
|
||||
};
|
||||
let data = state.surfaces.get_mut(surface_id).unwrap();
|
||||
data.role = Some(SurfaceRole::Popup(p));
|
||||
}
|
||||
xdg_surface::Request::AckConfigure { serial } => {
|
||||
let data = state.surfaces.get_mut(surface_id).unwrap();
|
||||
assert_eq!(data.xdg().last_configure_serial, serial);
|
||||
}
|
||||
xdg_surface::Request::Destroy => {
|
||||
let data = state.surfaces.get_mut(surface_id).unwrap();
|
||||
let role_alive = data.role.is_none()
|
||||
|| match data.role.as_ref().unwrap() {
|
||||
SurfaceRole::Toplevel(t) => t.toplevel.is_alive(),
|
||||
SurfaceRole::Popup(p) => p.popup.is_alive(),
|
||||
SurfaceRole::Cursor => false,
|
||||
};
|
||||
if role_alive {
|
||||
client.kill(
|
||||
dh,
|
||||
ProtocolError {
|
||||
code: xdg_surface::Error::DefunctRoleObject.into(),
|
||||
object_id: resource.id().protocol_id(),
|
||||
object_interface: XdgSurface::interface().name.to_string(),
|
||||
message: "destroyed xdg surface before role".to_string(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
other => todo!("unhandled request {other:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Rect {
|
||||
pub size: Vec2,
|
||||
pub offset: Vec2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PositionerState {
|
||||
pub size: Option<Vec2>,
|
||||
pub anchor_rect: Option<Rect>,
|
||||
pub offset: Vec2,
|
||||
pub anchor: xdg_positioner::Anchor,
|
||||
pub gravity: xdg_positioner::Gravity,
|
||||
}
|
||||
|
||||
impl Default for PositionerState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
size: None,
|
||||
anchor_rect: None,
|
||||
offset: Vec2 { x: 0, y: 0 },
|
||||
anchor: xdg_positioner::Anchor::None,
|
||||
gravity: xdg_positioner::Gravity::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgPositioner, ()> for State {
|
||||
fn request(
|
||||
state: &mut Self,
|
||||
_: &Client,
|
||||
resource: &XdgPositioner,
|
||||
request: <XdgPositioner as Resource>::Request,
|
||||
_: &(),
|
||||
_: &DisplayHandle,
|
||||
_: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
let hash_map::Entry::Occupied(mut data) = state
|
||||
.positioners
|
||||
.entry(PositionerId(resource.id().protocol_id()))
|
||||
else {
|
||||
unreachable!();
|
||||
};
|
||||
match request {
|
||||
xdg_positioner::Request::SetSize { width, height } => {
|
||||
data.get_mut().size = Some(Vec2 {
|
||||
x: width,
|
||||
y: height,
|
||||
});
|
||||
}
|
||||
xdg_positioner::Request::SetAnchorRect {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
} => {
|
||||
data.get_mut().anchor_rect = Some(Rect {
|
||||
size: Vec2 {
|
||||
x: width,
|
||||
y: height,
|
||||
},
|
||||
offset: Vec2 { x, y },
|
||||
});
|
||||
}
|
||||
xdg_positioner::Request::SetOffset { x, y } => {
|
||||
data.get_mut().offset = Vec2 { x, y };
|
||||
}
|
||||
xdg_positioner::Request::SetAnchor { anchor } => {
|
||||
data.get_mut().anchor = anchor.into_result().unwrap();
|
||||
}
|
||||
xdg_positioner::Request::SetGravity { gravity } => {
|
||||
data.get_mut().gravity = gravity.into_result().unwrap();
|
||||
}
|
||||
xdg_positioner::Request::Destroy => {
|
||||
data.remove();
|
||||
}
|
||||
other => todo!("unhandled positioner request {other:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgWmBase, ()> for State {
|
||||
fn request(
|
||||
state: &mut Self,
|
||||
client: &wayland_server::Client,
|
||||
_: &XdgWmBase,
|
||||
request: <XdgWmBase as Resource>::Request,
|
||||
_: &(),
|
||||
dhandle: &DisplayHandle,
|
||||
data_init: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
match request {
|
||||
xdg_wm_base::Request::GetXdgSurface { id, surface } => {
|
||||
let surface_id = SurfaceId(surface.id().protocol_id());
|
||||
let data = state.surfaces.get(&surface_id).unwrap();
|
||||
if data.buffer.is_some() {
|
||||
client.kill(
|
||||
dhandle,
|
||||
ProtocolError {
|
||||
code: xdg_wm_base::Error::InvalidSurfaceState.into(),
|
||||
object_id: surface_id.0,
|
||||
object_interface: XdgWmBase::interface().name.to_string(),
|
||||
message: "Buffer already attached to surface".to_string(),
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
data_init.init(id, surface_id);
|
||||
}
|
||||
xdg_wm_base::Request::CreatePositioner { id } => {
|
||||
let pos = data_init.init(id, ());
|
||||
state.positioners.insert(
|
||||
PositionerId(pos.id().protocol_id()),
|
||||
PositionerState::default(),
|
||||
);
|
||||
}
|
||||
other => todo!("unhandled request {other:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlShm, ()> for State {
|
||||
fn request(
|
||||
_: &mut Self,
|
||||
_: &wayland_server::Client,
|
||||
_: &WlShm,
|
||||
request: <WlShm as Resource>::Request,
|
||||
_: &(),
|
||||
_: &DisplayHandle,
|
||||
data_init: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
match request {
|
||||
proto::wl_shm::Request::CreatePool { id, .. } => {
|
||||
data_init.init(id, ());
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlShmPool, ()> for State {
|
||||
fn request(
|
||||
state: &mut Self,
|
||||
_: &wayland_server::Client,
|
||||
_: &WlShmPool,
|
||||
request: <WlShmPool as Resource>::Request,
|
||||
_: &(),
|
||||
_: &DisplayHandle,
|
||||
data_init: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
use proto::wl_shm_pool::Request::*;
|
||||
match request {
|
||||
CreateBuffer { id, .. } => {
|
||||
let buf = data_init.init(id, ());
|
||||
state.buffers.insert(buf);
|
||||
}
|
||||
Destroy => {}
|
||||
other => todo!("unhandled request {other:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlBuffer, ()> for State {
|
||||
fn request(
|
||||
state: &mut Self,
|
||||
_: &wayland_server::Client,
|
||||
resource: &WlBuffer,
|
||||
request: <WlBuffer as Resource>::Request,
|
||||
_: &(),
|
||||
_: &DisplayHandle,
|
||||
_: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
match request {
|
||||
proto::wl_buffer::Request::Destroy => {
|
||||
state.buffers.remove(resource);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlCompositor, ()> for State {
|
||||
fn request(
|
||||
state: &mut Self,
|
||||
_: &wayland_server::Client,
|
||||
_: &WlCompositor,
|
||||
request: <WlCompositor as wayland_server::Resource>::Request,
|
||||
_: &(),
|
||||
_: &DisplayHandle,
|
||||
data_init: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
match request {
|
||||
proto::wl_compositor::Request::CreateSurface { id } => {
|
||||
let surface = data_init.init(id, ());
|
||||
let id = surface.id().protocol_id();
|
||||
state.surfaces.insert(
|
||||
SurfaceId(id),
|
||||
SurfaceData {
|
||||
surface,
|
||||
buffer: None,
|
||||
last_damage: None,
|
||||
role: None,
|
||||
},
|
||||
);
|
||||
state.last_surface_id = Some(SurfaceId(id));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlSurface, ()> for State {
|
||||
fn request(
|
||||
state: &mut Self,
|
||||
_: &wayland_server::Client,
|
||||
resource: &WlSurface,
|
||||
request: <WlSurface as Resource>::Request,
|
||||
_: &(),
|
||||
_: &DisplayHandle,
|
||||
data_init: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
use proto::wl_surface::Request::*;
|
||||
|
||||
let data = state
|
||||
.surfaces
|
||||
.get_mut(&SurfaceId(resource.id().protocol_id()))
|
||||
.unwrap_or_else(|| panic!("{:?} missing from surface map", resource));
|
||||
|
||||
match request {
|
||||
Attach { buffer, .. } => {
|
||||
data.buffer = buffer;
|
||||
}
|
||||
Frame { callback } => {
|
||||
// XXX: calling done immediately will cause wayland_backend to panic,
|
||||
// report upstream
|
||||
state.callbacks.push(data_init.init(callback, ()));
|
||||
}
|
||||
DamageBuffer {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
} => {
|
||||
data.last_damage = Some(BufferDamage {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
});
|
||||
}
|
||||
Commit => {}
|
||||
Destroy => {
|
||||
state
|
||||
.surfaces
|
||||
.remove(&SurfaceId(resource.id().protocol_id()));
|
||||
}
|
||||
other => todo!("unhandled request {other:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlCallback, ()> for State {
|
||||
fn request(
|
||||
_: &mut Self,
|
||||
_: &wayland_server::Client,
|
||||
_: &WlCallback,
|
||||
_: <WlCallback as Resource>::Request,
|
||||
_: &(),
|
||||
_: &DisplayHandle,
|
||||
_: &mut wayland_server::DataInit<'_, Self>,
|
||||
) {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue