Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View file

@ -0,0 +1 @@
{"files":{"Cargo.lock":"a1130c9836e2901b6b2f15f8582c9474d27cc2d04b10788d4fd3b7e96c70b666","Cargo.toml":"4d1f8c78a9fe5e7ef87491557c5d12e73da0bacfeae6b9524e7e86b33da1d3c2","LICENSE.txt":"10e5daea68c3ab7da6468ea51860e9d4ac87ea6a9a2d34127beff17b7193b618","README.md":"c4f1a5c35c0cd0e27a9694e29ef1972e1b14b6c9824b65e873601364deef8dbb","build.rs":"b6b886ce8f337caf6acbe900961e0e7021e448b0ab8e241d95d91526e54807e7","examples/dynamic_globals.rs":"bfc90aa38c6ea97f35b3775a54571566a76f44db9c3625f489832ccf7757de84","examples/list_globals.rs":"84331cc9d40fe6a0cbaa51c10b0ea630d0ab77731d8a54687db1041b88926672","examples/simple_window.rs":"5b6c76b60632e1200e9fc4bacc670836b80ad66b7ffcf48cf4ac374cd6404690","src/display.rs":"494dd4136e440554151b86a4fae64275fddf6f67ae5cc11e319d05ea869ec76f","src/event_queue.rs":"50ed69dd7f16156c5ba85af9740ab6c35d7ffd91fbda8764e317e48370b1dbf6","src/globals.rs":"d135d7e087e7c6018e69e21dafd65b24a3d49bfbbb9565cb287f780a6195a778","src/lib.rs":"4e86757fe8e6d5f1c6a489c480f94b056b2d720902250a74301dcc044a326de9","src/native_lib/display.rs":"ced4cdfac6c97c45d22e65a3df998d8bd7b096d21deaa5ed55f2a0edcbb520a7","src/native_lib/event_queue.rs":"1b76cceb36ea6539fc5f6ea2d2c0829c9d2ac8294a2b624581ce8a8116853cbb","src/native_lib/mod.rs":"f1180b3e6ad9fb3afa55a6e1a71693f79174f3952e3075cfa14f9534f0dd5c09","src/native_lib/proxy.rs":"f0dc9c2b209aad6513b70f345dd7aa50ca7584f27015b693e78ee410965f5445","src/proxy.rs":"56506bb1b1872dba975a2f194f56a795fa25101dc3ac85da953a9c6799666734","src/rust_imp/connection.rs":"e41e7888ad7af769d2ad696bff85a96b0541f374c82a2e029198dcb026a0c57e","src/rust_imp/display.rs":"b7cea1525eb4ff7b3c00a84a223fe2afb30aab7a822a8c8da7fb8de3159b3ad3","src/rust_imp/mod.rs":"0bc0a765490df2219195c2c8ae286999d4bc65d8e26514468eb9b4d8b75ec79f","src/rust_imp/proxy.rs":"91da236137458756fc1dcb882176b887b05a0a4c08412cc649ba1d712c8672c7","src/rust_imp/queues.rs":"87a2a2f02256553dcd35548e300b214d7d7b0c2bf840883f836ab0ba74e7f6c7","wayland.xml":"e20d8e766908e56828b3d39d20968382a3e725cfbca97bbbf0bbfab956cc7455"},"package":"3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715"}

254
third-party/vendor/wayland-client/Cargo.lock generated vendored Normal file
View file

@ -0,0 +1,254 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[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.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794"
dependencies = [
"libloading",
]
[[package]]
name = "downcast-rs"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "fastrand"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
dependencies = [
"instant",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
[[package]]
name = "libloading"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]]
name = "nix"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc"
dependencies = [
"bitflags",
"cfg-if",
"libc",
"memoffset",
]
[[package]]
name = "once_cell"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
[[package]]
name = "pkg-config"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
[[package]]
name = "proc-macro2"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "scoped-tls"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]]
name = "smallvec"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
[[package]]
name = "tempfile"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
"cfg-if",
"fastrand",
"libc",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "unicode-ident"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
[[package]]
name = "wayland-client"
version = "0.29.5"
dependencies = [
"bitflags",
"downcast-rs",
"libc",
"nix",
"scoped-tls",
"tempfile",
"wayland-commons",
"wayland-scanner",
"wayland-sys",
]
[[package]]
name = "wayland-commons"
version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902"
dependencies = [
"nix",
"once_cell",
"smallvec",
"wayland-sys",
]
[[package]]
name = "wayland-scanner"
version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53"
dependencies = [
"proc-macro2",
"quote",
"xml-rs",
]
[[package]]
name = "wayland-sys"
version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4"
dependencies = [
"dlib",
"lazy_static",
"pkg-config",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "xml-rs"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"

View file

@ -0,0 +1,73 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
name = "wayland-client"
version = "0.29.5"
authors = ["Victor Berger <victor.berger@m4x.org>"]
build = "build.rs"
description = "Bindings to the standard C implementation of the wayland protocol, client side."
documentation = "https://smithay.github.io/wayland-rs/wayland_client/"
readme = "README.md"
keywords = [
"wayland",
"client",
]
categories = [
"gui",
"api-bindings",
]
license = "MIT"
repository = "https://github.com/smithay/wayland-rs"
[dependencies.bitflags]
version = "1.0"
[dependencies.downcast-rs]
version = "1.0"
[dependencies.libc]
version = "0.2"
[dependencies.nix]
version = "0.24.1"
features = [
"fs",
"poll",
]
default-features = false
[dependencies.scoped-tls]
version = "1.0"
optional = true
[dependencies.wayland-commons]
version = "0.29.5"
[dependencies.wayland-sys]
version = "0.29.5"
[dev-dependencies.tempfile]
version = ">=2.0, <4.0"
[build-dependencies.wayland-scanner]
version = "0.29.5"
[features]
dlopen = [
"wayland-sys/dlopen",
"use_system_lib",
]
use_system_lib = [
"wayland-sys/client",
"scoped-tls",
]

View file

@ -0,0 +1,19 @@
Copyright (c) 2015 Victor Berger
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,27 @@
[![crates.io](https://img.shields.io/crates/v/wayland-client.svg)](https://crates.io/crates/wayland-client)
[![docs.rs](https://docs.rs/wayland-client/badge.svg)](https://docs.rs/wayland-client)
[![Continuous Integration](https://github.com/Smithay/wayland-rs/workflows/Continuous%20Integration/badge.svg)](https://github.com/Smithay/wayland-rs/actions?query=workflow%3A%22Continuous+Integration%22)
[![codecov](https://codecov.io/gh/Smithay/wayland-rs/branch/master/graph/badge.svg)](https://codecov.io/gh/Smithay/wayland-rs)
# wayland-client
Client side API for the Wayland protocol. This crate provides infrastructure for manipulating
Wayland objects, as well as object definitions for the core Wayland protocol. Protocol extensions
can be supported as well by combining this crate with `wayland-protocols`, which provides object
definitions for a large set of extensions.
**Note:** This crate is a low-level interface to the Wayland protocol. If you are looking for a more
battery-included toolkit for writing a Wayland client app, you may consider
[Smithay's Client Toolkit](https://crates.io/crates/smithay-client-toolkit), which is built on top
of it.
The crate has different backends to Wayland protocol serialization:
- By default, it uses a pure-rust implementation of the protocol, and contains little `unsafe` code.
- Activating the `use_system_lib` makes it instead bind to the system `libwayland-client.so`. This
allows you to access C pointer versions of the wayland objects, which is necessary for interfacing
with other non-Rust Wayland-related libraries (such as for OpenGL support, see the `wayland-egl` crate).
- Activating the `dlopen` implies `use_system_lib`, but additionnaly the crate will not explicitly
link to `libwayland-client.so` and instead try to open it at runtime, and return an error if it cannot
find it. This allows you to build apps that can gracefully run in non-Wayland environment without needing
compile-time switches.

View file

@ -0,0 +1,20 @@
extern crate wayland_scanner;
use std::env::var;
use std::path::Path;
use wayland_scanner::*;
fn main() {
let protocol_file = "./wayland.xml";
let out_dir_str = var("OUT_DIR").unwrap();
let out_dir = Path::new(&out_dir_str);
println!("cargo:rerun-if-changed={}", protocol_file);
generate_code_with_destructor_events(
protocol_file,
out_dir.join("wayland_api.rs"),
Side::Client,
&[("wl_callback", "done")],
);
}

View file

@ -0,0 +1,110 @@
#[macro_use]
extern crate wayland_client;
use wayland_client::{Display, GlobalManager, Main};
use wayland_client::protocol::{wl_output, wl_seat};
// An example showcasing the capability of GlobalManager to handle
// dynamically created globals like wl_seat or wl_output, which can
// exist with multiplicity and created at any time
fn main() {
let display = Display::connect_to_env().unwrap();
let mut event_queue = display.create_event_queue();
let attached_display = (*display).clone().attach(event_queue.token());
// We create a GlobalManager with a callback, that will be
// advertised of any global creation or deletion
let _globals = GlobalManager::new_with_cb(
&attached_display,
// This macro generates a callback for auto-creating the globals
// that interest us and calling our provided callbacks
global_filter!(
// Here we ask that all seats be automatically instantiated
// with version 1 when advertised, and provide a callback that
// will handle the created wl_seat to implement them
//
// NOTE: the type annotations are necessary because rustc's
// inference is apparently not smart enough
[wl_seat::WlSeat, 1, |seat: Main<wl_seat::WlSeat>, _: DispatchData| {
let mut seat_name = None;
let mut caps = None;
seat.quick_assign(move |_, event, _| {
use wayland_client::protocol::wl_seat::Event;
match event {
Event::Name { name } => {
seat_name = Some(name);
}
Event::Capabilities { capabilities } => {
// We *should* have received the "name" event first
caps = Some(capabilities);
}
_ => {}
}
if let (Some(ref name), Some(caps)) = (&seat_name, caps) {
println!("Seat '{}' with caps: {:x}", name, caps);
}
})
}],
// Same thing with wl_output, but we require version 2
[wl_output::WlOutput, 2, |output: Main<wl_output::WlOutput>, _: DispatchData| {
let mut name = "<unknown>".to_owned();
let mut modes = vec![];
let mut scale = 1;
output.quick_assign(move |_, event, _| {
use wayland_client::protocol::wl_output::Event;
match event {
Event::Geometry {
x,
y,
physical_width,
physical_height,
subpixel,
make,
model,
transform,
} => {
println!("New output: \"{} ({})\"", make, model);
println!(
" -> physical dimensions {}x{}",
physical_width, physical_height
);
println!(" -> location in the compositor space: ({}, {})", x, y);
println!(" -> transform: {:?}", transform);
println!(" -> subpixel orientation: {:?}", subpixel);
name = format!("{} ({})", make, model);
}
Event::Mode { flags, width, height, refresh } => {
modes.push((flags, width, height, refresh));
}
Event::Scale { factor } => {
scale = factor;
}
Event::Done => {
println!("Modesetting information for output \"{}\"", name);
println!(" -> scaling factor: {}", scale);
println!(" -> mode list:");
for &(f, w, h, r) in &modes {
println!(
" -> {}x{} @{}Hz (flags: [ {:?} ])",
w,
h,
(r as f32) / 1000.0,
f
);
}
}
_ => unreachable!(),
}
})
}]
),
);
event_queue.sync_roundtrip(&mut (), |_, _, _| unreachable!()).unwrap();
event_queue.sync_roundtrip(&mut (), |_, _, _| unreachable!()).unwrap();
event_queue.sync_roundtrip(&mut (), |_, _, _| unreachable!()).unwrap();
}

View file

@ -0,0 +1,29 @@
extern crate wayland_client;
use wayland_client::{Display, GlobalManager};
// A minimal example printing the list of globals advertised by the server and
// then exiting
fn main() {
// Connect to the server
let display = Display::connect_to_env().unwrap();
let mut event_queue = display.create_event_queue();
let attached_display = (*display).clone().attach(event_queue.token());
// We use the GlobalManager convenience provided by the crate, it covers
// most classic use cases and avoids us the trouble to manually implement
// the registry
let globals = GlobalManager::new(&attached_display);
// A roundtrip synchronization to make sure the server received our registry
// creation and sent us the global list
event_queue.sync_roundtrip(&mut (), |_, _, _| unreachable!()).unwrap();
// Print the list
for (id, interface, version) in globals.list() {
println!("{}: {} (version {})", id, interface, version);
}
}

View file

@ -0,0 +1,206 @@
// Allow single character names so clippy doesn't lint on x, y, r, g, b, which
// are reasonable variable names in this domain.
#![allow(clippy::many_single_char_names)]
use std::{
cmp::min,
io::{BufWriter, Write},
os::unix::io::AsRawFd,
process::exit,
time::Instant,
};
use wayland_client::{
event_enum,
protocol::{wl_compositor, wl_keyboard, wl_pointer, wl_seat, wl_shm},
Display, Filter, GlobalManager,
};
use wayland_protocols::xdg_shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
// declare an event enum containing the events we want to receive in the iterator
event_enum!(
Events |
Pointer => wl_pointer::WlPointer,
Keyboard => wl_keyboard::WlKeyboard
);
fn main() {
let now = Instant::now();
let display = Display::connect_to_env().unwrap();
let mut event_queue = display.create_event_queue();
let attached_display = (*display).clone().attach(event_queue.token());
let globals = GlobalManager::new(&attached_display);
// Make a synchronized roundtrip to the wayland server.
//
// When this returns it must be true that the server has already
// sent us all available globals.
event_queue.sync_roundtrip(&mut (), |_, _, _| unreachable!()).unwrap();
/*
* Create a buffer with window contents
*/
// buffer (and window) width and height
let buf_x: u32 = 320;
let buf_y: u32 = 240;
// create a tempfile to write the contents of the window on
let mut tmp = tempfile::tempfile().expect("Unable to create a tempfile.");
// write the contents to it, lets put a nice color gradient
{
let now = Instant::now();
let mut buf = BufWriter::new(&mut tmp);
for y in 0..buf_y {
for x in 0..buf_x {
let a = 0xFF;
let r = min(((buf_x - x) * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
let g = min((x * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
let b = min(((buf_x - x) * 0xFF) / buf_x, (y * 0xFF) / buf_y);
let color = (a << 24) + (r << 16) + (g << 8) + b;
buf.write_all(&color.to_ne_bytes()).unwrap();
}
}
buf.flush().unwrap();
println!(
"Time used to create the nice color gradient: {:?}",
Instant::now().duration_since(now)
);
}
/*
* Init wayland objects
*/
// The compositor allows us to creates surfaces
let compositor = globals.instantiate_exact::<wl_compositor::WlCompositor>(1).unwrap();
let surface = compositor.create_surface();
// The SHM allows us to share memory with the server, and create buffers
// on this shared memory to paint our surfaces
let shm = globals.instantiate_exact::<wl_shm::WlShm>(1).unwrap();
let pool = shm.create_pool(
tmp.as_raw_fd(), // RawFd to the tempfile serving as shared memory
(buf_x * buf_y * 4) as i32, // size in bytes of the shared memory (4 bytes per pixel)
);
let buffer = pool.create_buffer(
0, // Start of the buffer in the pool
buf_x as i32, // width of the buffer in pixels
buf_y as i32, // height of the buffer in pixels
(buf_x * 4) as i32, // number of bytes between the beginning of two consecutive lines
wl_shm::Format::Argb8888, // chosen encoding for the data
);
let xdg_wm_base = globals
.instantiate_exact::<xdg_wm_base::XdgWmBase>(2)
.expect("Compositor does not support xdg_shell");
xdg_wm_base.quick_assign(|xdg_wm_base, event, _| {
if let xdg_wm_base::Event::Ping { serial } = event {
xdg_wm_base.pong(serial);
};
});
let xdg_surface = xdg_wm_base.get_xdg_surface(&surface);
xdg_surface.quick_assign(move |xdg_surface, event, _| match event {
xdg_surface::Event::Configure { serial } => {
println!("xdg_surface (Configure)");
xdg_surface.ack_configure(serial);
}
_ => unreachable!(),
});
let xdg_toplevel = xdg_surface.get_toplevel();
xdg_toplevel.quick_assign(move |_, event, _| match event {
xdg_toplevel::Event::Close => {
exit(0);
}
xdg_toplevel::Event::Configure { width, height, states } => {
println!(
"xdg_toplevel (Configure) width: {}, height: {}, states: {:?}",
width, height, states
);
}
_ => unreachable!(),
});
xdg_toplevel.set_title("Simple Window".to_string());
// initialize a seat to retrieve pointer & keyboard events
//
// example of using a common filter to handle both pointer & keyboard events
let common_filter = Filter::new(move |event, _, _| match event {
Events::Pointer { event, .. } => match event {
wl_pointer::Event::Enter { surface_x, surface_y, .. } => {
println!("Pointer entered at ({}, {}).", surface_x, surface_y);
}
wl_pointer::Event::Leave { .. } => {
println!("Pointer left.");
}
wl_pointer::Event::Motion { surface_x, surface_y, .. } => {
println!("Pointer moved to ({}, {}).", surface_x, surface_y);
}
wl_pointer::Event::Button { button, state, .. } => {
println!("Button {} was {:?}.", button, state);
}
_ => {}
},
Events::Keyboard { event, .. } => match event {
wl_keyboard::Event::Enter { .. } => {
println!("Gained keyboard focus.");
}
wl_keyboard::Event::Leave { .. } => {
println!("Lost keyboard focus.");
}
wl_keyboard::Event::Key { key, state, .. } => {
println!("Key with id {} was {:?}.", key, state);
}
_ => (),
},
});
// to be handled properly this should be more dynamic, as more
// than one seat can exist (and they can be created and destroyed
// dynamically), however most "traditional" setups have a single
// seat, so we'll keep it simple here
let mut pointer_created = false;
let mut keyboard_created = false;
globals.instantiate_exact::<wl_seat::WlSeat>(1).unwrap().quick_assign(move |seat, event, _| {
// The capabilities of a seat are known at runtime and we retrieve
// them via an events. 3 capabilities exists: pointer, keyboard, and touch
// we are only interested in pointer & keyboard here
use wayland_client::protocol::wl_seat::{Capability, Event as SeatEvent};
if let SeatEvent::Capabilities { capabilities } = event {
if !pointer_created && capabilities.contains(Capability::Pointer) {
// create the pointer only once
pointer_created = true;
seat.get_pointer().assign(common_filter.clone());
}
if !keyboard_created && capabilities.contains(Capability::Keyboard) {
// create the keyboard only once
keyboard_created = true;
seat.get_keyboard().assign(common_filter.clone());
}
}
});
surface.commit();
event_queue.sync_roundtrip(&mut (), |_, _, _| { /* we ignore unfiltered messages */ }).unwrap();
surface.attach(Some(&buffer), 0, 0);
surface.commit();
println!("Time used before enter in the main loop: {:?}", Instant::now().duration_since(now));
loop {
event_queue.dispatch(&mut (), |_, _, _| { /* we ignore unfiltered messages */ }).unwrap();
}
}

View file

@ -0,0 +1,261 @@
use std::env;
use std::ffi::OsString;
use std::io;
use std::ops::Deref;
use std::os::unix::io::{IntoRawFd, RawFd};
use std::os::unix::net::UnixStream;
use std::path::PathBuf;
use std::sync::Arc;
use nix::fcntl;
use crate::{EventQueue, Proxy};
use crate::imp::DisplayInner;
#[cfg(feature = "use_system_lib")]
use wayland_sys::client::wl_display;
/// Enum representing the possible reasons why connecting to the wayland server failed
#[derive(Debug)]
pub enum ConnectError {
/// The library was compiled with the `dlopen` feature, and the `libwayland-client.so`
/// library could not be found at runtime
NoWaylandLib,
/// The `XDG_RUNTIME_DIR` variable is not set while it should be
XdgRuntimeDirNotSet,
/// Any needed library was found, but the listening socket of the server was not.
///
/// Most of the time, this means that the program was not started from a wayland session.
NoCompositorListening,
/// The provided socket name is invalid
InvalidName,
/// The FD provided in `WAYLAND_SOCKET` was invalid
InvalidFd,
}
impl ::std::error::Error for ConnectError {}
impl ::std::fmt::Display for ConnectError {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
match *self {
ConnectError::NoWaylandLib => f.write_str("Could not find libwayland-client.so."),
ConnectError::XdgRuntimeDirNotSet => f.write_str("XDG_RUNTIME_DIR is not set."),
ConnectError::NoCompositorListening => {
f.write_str("Could not find a listening wayland compositor.")
}
ConnectError::InvalidName => f.write_str("The wayland socket name is invalid."),
ConnectError::InvalidFd => f.write_str("The FD provided in WAYLAND_SOCKET is invalid."),
}
}
}
/// A protocol error
///
/// This kind of error is generated by the server if your client didn't respect
/// the protocol, after which the server will kill your connection.
///
/// If the dispatching methods of `EventQueues` start to fail, you may want to
/// check `Display::protocol_error()` to see if a protocol error was generated.
#[derive(Clone, Debug)]
pub struct ProtocolError {
/// The error code associated with the error
///
/// It should be interpreted as an instance of the `Error` enum of the
/// associated interface.
pub code: u32,
/// The id of the object that caused the error
pub object_id: u32,
/// The interface of the object that caused the error
pub object_interface: &'static str,
/// The message sent by the server describing the error
pub message: String,
}
impl ::std::error::Error for ProtocolError {
fn description(&self) -> &str {
"Wayland protocol error"
}
}
impl ::std::fmt::Display for ProtocolError {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
write!(
f,
"Protocol error {} on object {}@{}: {}",
self.code, self.object_interface, self.object_id, self.message
)
}
}
/// A connection to a wayland server
///
/// This object both represent the connection to the server and contains the
/// primary `WlDisplay` wayland object. As such, it must be kept alive as long
/// as you are connected. You can access the contained `WlDisplay` via `Deref`
/// to create all the objects you need.
///
/// **Safety note:** If you activate the `use_system_lib` cargo feature and provide pointers
/// to wayland objects to other libraries, you **must** ensure that these libraries clean up
/// their state before the last clone of this `Display` is dropped, otherwise these libraries
/// will access freed memory when doing their cleanup.
#[derive(Clone)]
pub struct Display {
pub(crate) inner: Arc<DisplayInner>,
}
impl std::fmt::Debug for Display {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Display { ... }")
}
}
impl Display {
/// Attempt to connect to a wayland server using the contents of the environment variables
///
/// First of all, if the `WAYLAND_SOCKET` environment variable is set, it'll try to interpret
/// it as a FD number to use.
///
/// Otherwise, it will try to connect to the socket name defined in the `WAYLAND_DISPLAY`
/// environment variable, and error if it is not set.
///
/// This requires the `XDG_RUNTIME_DIR` variable to be properly set.
pub fn connect_to_env() -> Result<Display, ConnectError> {
if let Ok(txt) = env::var("WAYLAND_SOCKET") {
// We should connect to the provided WAYLAND_SOCKET
let fd = txt.parse::<i32>().map_err(|_| ConnectError::InvalidFd)?;
// remove the variable so any child processes don't see it
env::remove_var("WAYLAND_SOCKET");
// set the CLOEXEC flag on this FD
let flags = fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFD);
let result = flags
.map(|f| fcntl::FdFlag::from_bits(f).unwrap() | fcntl::FdFlag::FD_CLOEXEC)
.and_then(|f| fcntl::fcntl(fd, fcntl::FcntlArg::F_SETFD(f)));
match result {
Ok(_) => {
// setting the O_CLOEXEC worked
unsafe { Display::from_fd(fd) }
}
Err(_) => {
// something went wrong in F_GETFD or F_SETFD
let _ = ::nix::unistd::close(fd);
Err(ConnectError::InvalidFd)
}
}
} else {
let mut socket_path = env::var_os("XDG_RUNTIME_DIR")
.map(Into::<PathBuf>::into)
.ok_or(ConnectError::XdgRuntimeDirNotSet)?;
socket_path
.push(env::var_os("WAYLAND_DISPLAY").ok_or(ConnectError::NoCompositorListening)?);
let socket = UnixStream::connect(socket_path)
.map_err(|_| ConnectError::NoCompositorListening)?;
unsafe { Display::from_fd(socket.into_raw_fd()) }
}
}
/// Attempt to connect to a wayland server socket with given name
///
/// On success, you are given the `Display` object as well as the main `EventQueue` hosting
/// the `WlDisplay` wayland object.
///
/// This requires the `XDG_RUNTIME_DIR` variable to be properly set.
pub fn connect_to_name<S: Into<OsString>>(name: S) -> Result<Display, ConnectError> {
let mut socket_path = env::var_os("XDG_RUNTIME_DIR")
.map(Into::<PathBuf>::into)
.ok_or(ConnectError::XdgRuntimeDirNotSet)?;
socket_path.push(name.into());
let socket =
UnixStream::connect(socket_path).map_err(|_| ConnectError::NoCompositorListening)?;
unsafe { Display::from_fd(socket.into_raw_fd()) }
}
/// Attempt to use an already connected unix socket on given FD to start a wayland connection
///
/// On success, you are given the `Display` object.
///
/// Will take ownership of the FD.
///
/// # Safety
///
/// The file descriptor must be associated to a connected unix socket.
pub unsafe fn from_fd(fd: RawFd) -> Result<Display, ConnectError> {
Ok(Display { inner: DisplayInner::from_fd(fd)? })
}
/// Non-blocking write to the server
///
/// Outgoing messages to the server are buffered by the library for efficiency. This method
/// flushes the internal buffer to the server socket.
///
/// Will write as many pending requests as possible to the server socket. Never blocks: if not all
/// requests could be written, will return an io error `WouldBlock`.
pub fn flush(&self) -> io::Result<()> {
self.inner.flush()
}
/// Create a new event queue associated with this wayland connection
pub fn create_event_queue(&self) -> EventQueue {
let evq_inner = DisplayInner::create_event_queue(&self.inner);
EventQueue::new(evq_inner, self.clone())
}
/// Retrieve the last protocol error if any occured
///
/// If your client does not respect some part of a protocol it is using, the server
/// will send a special "protocol error" event and kill your connection. This method
/// allows you to retrieve the contents of this event if it occured.
///
/// If the dispatch methods of the `EventQueue` return an error, this is an indication
/// that a protocol error may have occured. Such errors are not recoverable, but this
/// method allows you to gracefully display them to the user, along with indications for
/// submitting a bug-report for example.
pub fn protocol_error(&self) -> Option<ProtocolError> {
self.inner.protocol_error()
}
/// Retrieve the file descriptor associated with the wayland socket
///
/// This FD should only be used to integrate into a polling mechanism, and should
/// never be directly read from or written to.
pub fn get_connection_fd(&self) -> ::std::os::unix::io::RawFd {
self.inner.get_connection_fd()
}
#[cfg(feature = "use_system_lib")]
/// Create a Display and from an external display
///
/// This allows you to interface with an already-existing wayland connection,
/// for example provided by a GUI toolkit.
///
/// Note that if you need to retrieve the actual `wl_display` pointer back (rather than
/// its wrapper), you must use the `get_display_ptr()` method.
///
/// # Safety
///
/// The provided pointer must point to a valid `wl_display` from `libwayland-client`
pub unsafe fn from_external_display(display_ptr: *mut wl_display) -> Display {
Display { inner: DisplayInner::from_external(display_ptr) }
}
#[cfg(feature = "use_system_lib")]
/// Retrieve the `wl_display` pointer
///
/// If this `Display` was created from an external `wl_display`, its `c_ptr()` method will
/// return a wrapper to the actual display. While this is perfectly good as a `wl_proxy`
/// pointer, to send requests, this is not the actual `wl_display` and cannot be used as such.
///
/// This method will give you the `wl_display`.
pub fn get_display_ptr(&self) -> *mut wl_display {
self.inner.ptr()
}
}
impl Deref for Display {
type Target = Proxy<crate::protocol::wl_display::WlDisplay>;
fn deref(&self) -> &Proxy<crate::protocol::wl_display::WlDisplay> {
self.inner.get_proxy()
}
}

View file

@ -0,0 +1,283 @@
use std::{io, rc::Rc};
use crate::imp::EventQueueInner;
use crate::{AnonymousObject, DispatchData, Display, Main, RawEvent};
/// An event queue for protocol messages
///
/// Event dispatching in wayland is made on a queue basis, allowing you
/// to organize your objects into different queues that can be dispatched
/// independently, for example from different threads.
///
/// An `EventQueue` is not `Send`, and thus must stay on the thread on which
/// it was created. However the `Display` object is `Send + Sync`, allowing
/// you to create the queues directly on the threads that host them.
///
/// When a queue is dispatched (via the `dispatch(..)` or `dispatch_pending(..)` methods)
/// all the incoming messages from the server designated to objects associated with
/// the queue are processed sequentially, and the appropriate implementation for each
/// is invoked. When all messages have been processed these methods return.
///
/// There are two main ways to driving an event queue forward. The first way is the
/// simplest and generally sufficient for single-threaded apps that only process events
/// from wayland. It consists of using the `EventQueue::dispatch(..)` method, which will
/// take care of sending pending requests to the server, block until some events are
/// available, read them, and call the associated handlers:
///
/// ```no_run
/// # extern crate wayland_client;
/// # use wayland_client::{Display};
/// # let display = Display::connect_to_env().unwrap();
/// # let mut event_queue = display.create_event_queue();
/// loop {
/// // The dispatch() method returns once it has received some events to dispatch
/// // and have emptied the wayland socket from its pending messages, so it needs
/// // to be called in a loop. If this method returns an error, your connection to
/// // the wayland server is very likely dead. See its documentation for more details.
/// event_queue.dispatch(&mut (), |_,_,_| {
/// /* This closure will be called for every event received by an object not
/// assigned to any Filter. If you plan to assign all your objects to Filter,
/// the simplest thing to do is to assert this is never called. */
/// unreachable!();
/// }).expect("An error occurred during event dispatching!");
/// }
/// ```
///
/// The second way is more appropriate for apps that are either multithreaded (and need to process
/// wayland events from different threads conccurently) or need to react to events from different
/// sources and can't affort to just block on the wayland socket. It centers around three methods:
/// `Display::flush()`, `EventQueue::read_events()` and `EventQueue::dispatch_pending()`:
///
/// ```no_run
/// # extern crate wayland_client;
/// # use wayland_client::Display;
/// # let display = Display::connect_to_env().unwrap();
/// # let mut event_queue = display.create_event_queue();
/// loop {
/// // The first method, called on the Display, is flush(). It writes all pending
/// // requests to the socket. Calling it ensures that the server will indeed
/// // receive your requests (so it can react to them).
/// if let Err(e) = display.flush() {
/// if e.kind() != ::std::io::ErrorKind::WouldBlock {
/// // if you are sending a realy large number of request, it might fill
/// // the internal buffers of the socket, in which case you should just
/// // retry flushing later. Other errors are a problem though.
/// eprintln!("Error while trying to flush the wayland socket: {:?}", e);
/// }
/// }
///
/// // The second method will try to read events from the socket. It is done in two
/// // steps, first the read is prepared, and then it is actually executed. This allows
/// // lower contention when different threads are trying to trigger a read of events
/// // concurently
/// if let Some(guard) = event_queue.prepare_read() {
/// // prepare_read() returns None if there are already events pending in this
/// // event queue, in which case there is no need to try to read from the socket
/// if let Err(e) = guard.read_events() {
/// if e.kind() != ::std::io::ErrorKind::WouldBlock {
/// // if read_events() returns Err(WouldBlock), this just means that no new
/// // messages are available to be read
/// eprintln!("Error while trying to read from the wayland socket: {:?}", e);
/// }
/// }
/// }
///
/// // Then, once events have been read from the socket and stored in the internal
/// // queues, they need to be dispatched to their handler. Note that while flush()
/// // and read_events() are global and will affect the whole connection, this last
/// // method will only affect the event queue it is being called on. This method
/// // cannot error unless there is a bug in the server or a previous read of events
/// // already errored.
/// event_queue.dispatch_pending(&mut (), |_,_,_| {}).expect("Failed to dispatch all messages.");
///
/// // Note that none of these methods are blocking, as such they should not be used
/// // as a loop as-is if there are no other sources of events your program is waiting on.
///
/// // The wayland socket can also be integrated in a poll-like mechanism by using
/// // the file descriptor provided by the `get_connection_fd()` method.
/// }
/// ```
pub struct EventQueue {
// EventQueue is *not* Send
pub(crate) inner: Rc<EventQueueInner>,
display: Display,
}
impl std::fmt::Debug for EventQueue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("EventQueue { ... }")
}
}
/// A token representing this event queue
///
/// This token can be cloned and is meant to allow easier
/// interaction with other functions in the library that
/// require the specification of an event queue, like
/// `Proxy::assign`.
#[derive(Clone)]
pub struct QueueToken {
pub(crate) inner: Rc<EventQueueInner>,
}
impl std::fmt::Debug for QueueToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("QueueToken { ... }")
}
}
impl EventQueue {
pub(crate) fn new(inner: EventQueueInner, display: Display) -> EventQueue {
EventQueue { inner: Rc::new(inner), display }
}
/// Dispatches events from the internal buffer.
///
/// Dispatches all events to their appropriate filters.
/// If no events were in the internal buffer, will block until
/// some events are read and dispatch them.
/// This process can insert events in the internal buffers of
/// other event queues.
///
/// The provided `data` will be mutably accessible from all the callbacks, via the
/// [`DispatchData`](struct.DispatchData.html) mechanism. If you don't need global data, you
/// can just provide a `&mut ()` there.
///
/// If an error is returned, your connection with the wayland compositor is probably lost.
/// You may want to check `Display::protocol_error()` to see if it was caused by a protocol error.
pub fn dispatch<T: std::any::Any, F>(&mut self, data: &mut T, fallback: F) -> io::Result<u32>
where
F: FnMut(RawEvent, Main<AnonymousObject>, DispatchData<'_>),
{
let mut data = DispatchData::wrap(data);
self.inner.dispatch(data.reborrow(), fallback)
}
/// Dispatches pending events from the internal buffer.
///
/// Dispatches all events to their appropriate callbacks.
/// Never blocks, if no events were pending, simply returns
/// `Ok(0)`.
///
/// The provided `data` will be mutably accessible from all the callbacks, via the
/// [`DispatchData`](struct.DispatchData.html) mechanism. If you don't need global data, you
/// can just provide a `&mut ()` there.
///
/// If an error is returned, your connection with the wayland compositor is probably lost.
/// You may want to check `Display::protocol_error()` to see if it was caused by a protocol error.
pub fn dispatch_pending<T: std::any::Any, F>(
&mut self,
data: &mut T,
fallback: F,
) -> io::Result<u32>
where
F: FnMut(RawEvent, Main<AnonymousObject>, DispatchData<'_>),
{
let mut data = DispatchData::wrap(data);
self.inner.dispatch_pending(data.reborrow(), fallback)
}
/// Synchronous roundtrip
///
/// This call will cause a synchronous roundtrip with the wayland server. It will block until all
/// pending requests of this queue are sent to the server and it has processed all of them and
/// send the appropriate events.
///
/// Handlers are called as a consequence.
///
/// The provided `data` will be mutably accessible from all the callbacks, via the
/// [`DispatchData`](struct.DispatchData.html) mechanism. If you don't need global data, you
/// can just provide a `&mut ()` there.
///
/// On success returns the number of dispatched events.
/// If an error is returned, your connection with the wayland compositor is probably lost.
/// You may want to check `Display::protocol_error()` to see if it was caused by a protocol error.
pub fn sync_roundtrip<T: std::any::Any, F>(
&mut self,
data: &mut T,
fallback: F,
) -> io::Result<u32>
where
F: FnMut(RawEvent, Main<AnonymousObject>, DispatchData<'_>),
{
let mut data = DispatchData::wrap(data);
self.inner.sync_roundtrip(data.reborrow(), fallback)
}
/// Create a new token associated with this event queue
///
/// See `QueueToken` documentation for its use.
pub fn token(&self) -> QueueToken {
QueueToken { inner: self.inner.clone() }
}
/// Prepare an concurrent read
///
/// Will declare your intention to read events from the server socket.
///
/// Will return `None` if there are still some events awaiting dispatch on this EventIterator.
/// In this case, you need to call `dispatch_pending()` before calling this method again.
///
/// The guard can then be used by two means:
///
/// - Calling its `cancel()` method (or letting it go out of scope): the read intention will
/// be cancelled
/// - Calling its `read_events()` method: will block until all existing guards are destroyed
/// by one of these methods, then events will be read and all blocked `read_events()` calls
/// will return.
///
/// This call will otherwise not block on the server socket if it is empty, and return
/// an io error `WouldBlock` in such cases.
pub fn prepare_read(&self) -> Option<ReadEventsGuard> {
match self.inner.prepare_read() {
Ok(()) => Some(ReadEventsGuard { inner: self.inner.clone(), done: false }),
Err(()) => None,
}
}
/// Access the `Display` of the connection
pub fn display(&self) -> &Display {
&self.display
}
}
/// A guard over a read intention.
///
/// See `EventQueue::prepare_read()` for details about its use.
pub struct ReadEventsGuard {
inner: Rc<EventQueueInner>,
done: bool,
}
impl std::fmt::Debug for ReadEventsGuard {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("ReadEventsGuard { ... }")
}
}
impl ReadEventsGuard {
/// Read events
///
/// Reads events from the server socket. If other `ReadEventsGuard` exists, will block
/// until they are all consumed or destroyed.
pub fn read_events(mut self) -> io::Result<()> {
self.done = true;
self.inner.read_events()
}
/// Cancel the read
///
/// Will cancel the read intention associated with this guard. Never blocks.
///
/// Has the same effect as letting the guard go out of scope.
pub fn cancel(self) {
// just run the destructor
}
}
impl Drop for ReadEventsGuard {
fn drop(&mut self) {
if !self.done {
self.inner.cancel_read();
}
}
}

View file

@ -0,0 +1,343 @@
use std::sync::{Arc, Mutex};
use crate::protocol::wl_display;
use crate::protocol::wl_registry;
use crate::{Attached, DispatchData, Interface, Main, Proxy};
#[derive(Debug)]
struct Inner {
list: Vec<(u32, String, u32)>,
}
/// An utility to manage global objects
///
/// This utility provides an implemenation for the registry
/// that track the list of globals for you, as well as utilities
/// to bind them.
#[derive(Clone, Debug)]
pub struct GlobalManager {
inner: Arc<Mutex<Inner>>,
registry: Main<wl_registry::WlRegistry>,
}
/// An error that occurred trying to bind a global
#[derive(Debug, PartialEq)]
pub enum GlobalError {
/// The requested global was missing
Missing,
/// The global advertised by the server has a lower version number
/// than the one requested
VersionTooLow(u32),
}
impl ::std::error::Error for GlobalError {}
impl ::std::fmt::Display for GlobalError {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
match *self {
GlobalError::Missing => f.write_str("The requested global was missing."),
GlobalError::VersionTooLow(_) => {
f.write_str("The requested global's version is too low.")
}
}
}
}
/// Event provided to the user callback of GlobalManager
#[derive(Debug)]
pub enum GlobalEvent {
/// A new global was created
New {
/// Id of the new global
id: u32,
/// Interface of the new global
interface: String,
/// Maximum supported version of the new global
version: u32,
},
/// A global was removed
Removed {
/// Id of the removed global
id: u32,
/// Interface of the removed global
interface: String,
},
}
impl GlobalManager {
/// Create a global manager handling a registry
///
/// You need to provide an attached handle of the Waland display, and the
/// global manager will be managed by the associated event queue.
pub fn new(display: &Attached<wl_display::WlDisplay>) -> GlobalManager {
let inner = Arc::new(Mutex::new(Inner { list: Vec::new() }));
let inner_clone = inner.clone();
let registry = display
.as_ref()
.send::<wl_registry::WlRegistry>(wl_display::Request::GetRegistry {}, None)
.unwrap();
registry.quick_assign(move |_proxy, msg, _data| {
let mut inner = inner.lock().unwrap();
match msg {
wl_registry::Event::Global { name, interface, version } => {
inner.list.push((name, interface, version));
}
wl_registry::Event::GlobalRemove { name } => {
inner.list.retain(|&(n, _, _)| n != name);
}
}
});
GlobalManager { inner: inner_clone, registry }
}
/// Create a global manager handling a registry with a callback
///
/// This global manager will track globals as a simple one, but will
/// also forward the registry events to your callback.
///
/// This can be used if you want to handle specially certain globals, but want
/// to use the default mechanism for the rest.
///
/// You need to provide an attached handle of the Waland display, and the
/// global manager will be managed by the associated event queue.
pub fn new_with_cb<F>(
display: &Attached<wl_display::WlDisplay>,
mut callback: F,
) -> GlobalManager
where
F: FnMut(GlobalEvent, Attached<wl_registry::WlRegistry>, DispatchData) + 'static,
{
let inner = Arc::new(Mutex::new(Inner { list: Vec::new() }));
let inner_clone = inner.clone();
let registry = display
.as_ref()
.send::<wl_registry::WlRegistry>(wl_display::Request::GetRegistry {}, None)
.unwrap();
registry.quick_assign(move |proxy, msg, data| {
let mut inner = inner.lock().unwrap();
let inner = &mut *inner;
match msg {
wl_registry::Event::Global {
name,
interface,
version,
} => {
inner.list.push((name, interface.clone(), version));
callback(
GlobalEvent::New {
id: name,
interface,
version,
},
(*proxy).clone(),
data,
);
}
wl_registry::Event::GlobalRemove { name } => {
if let Some((i, _)) = inner.list.iter().enumerate().find(|&(_, &(n, _, _))| n == name) {
let (id, interface, _) = inner.list.swap_remove(i);
callback(GlobalEvent::Removed { id, interface }, (*proxy).clone(), data);
} else {
panic!(
"Wayland protocol error: the server removed non-existing global \"{}\".",
name
);
}
}
}
});
GlobalManager { inner: inner_clone, registry }
}
/// Instantiate a global with a specific version
///
/// Meaning of requests and events can change depending on the object version you use,
/// as such unless you specifically want to support several versions of a protocol, it is
/// recommended to use this method with an hardcoded value for the version (the one you'll
/// use a as reference for your implementation). Notably you should *not* use `I::VERSION`
/// as a version, as this value can change when the protocol files are updated.
///
/// This method is only appropriate for globals that are expected to
/// not exist with multiplicity (such as `wl_compositor` or `wl_shm`),
/// as it will always bind the first one that was advertized.
pub fn instantiate_exact<I>(&self, version: u32) -> Result<Main<I>, GlobalError>
where
I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>,
{
let inner = self.inner.lock().unwrap();
for &(id, ref interface, server_version) in &inner.list {
if interface == I::NAME {
if version > server_version {
return Err(GlobalError::VersionTooLow(server_version));
} else {
return Ok(self.registry.bind::<I>(version, id));
}
}
}
Err(GlobalError::Missing)
}
/// Instantiate a global from a version range
///
/// If you want to support several versions of a particular global, this method allows you to
/// specify a range of versions that you accept. It'll bind the highest possible version that
/// is between `min_version` and `max_version` inclusive, and return an error if the highest
/// version supported by the compositor is lower than `min_version`. As for
/// `instantiate_exact`, you should not use `I::VERSION` here: the versions your code support
/// do not change when the protocol files are updated.
///
/// When trying to support several versions of a protocol, you can check which version has
/// actually been used on any object using the `Proxy::version()` method.
///
/// As `instantiate_exact`, it should only be used for singleton globals, for the same reasons.
pub fn instantiate_range<I>(
&self,
min_version: u32,
max_version: u32,
) -> Result<Main<I>, GlobalError>
where
I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>,
{
let inner = self.inner.lock().unwrap();
for &(id, ref interface, version) in &inner.list {
if interface == I::NAME {
if version >= min_version {
let version = ::std::cmp::min(version, max_version);
return Ok(self.registry.bind::<I>(version, id));
} else {
return Err(GlobalError::VersionTooLow(version));
}
}
}
Err(GlobalError::Missing)
}
/// Retrieve the list of currently known globals
pub fn list(&self) -> Vec<(u32, String, u32)> {
self.inner.lock().unwrap().list.clone()
}
}
/// A trait for implementation of the global advertisement
///
/// It is automatically implemented for `FnMut(Main<I>, DispatchData)` closures,
/// in which case the `error` messages are ignored.
pub trait GlobalImplementor<I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>> {
/// A new global of given interface has been instantiated and you can assign
/// a filter to it.
fn new_global(&mut self, global: Main<I>, data: DispatchData);
/// A global was advertised but its version was lower than the minimal version
/// you requested.
///
/// The advertised version is provided as argument.
fn error(&mut self, _version: u32, _data: DispatchData) {}
}
impl<F, I: Interface> GlobalImplementor<I> for F
where
I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>,
F: FnMut(Main<I>, DispatchData),
{
fn new_global(&mut self, global: Main<I>, data: DispatchData) {
(*self)(global, data)
}
}
/// Convenience macro to create a `GlobalManager` callback
///
/// This macro aims to simplify the specific but common case of
/// providing a callback to the `GlobalManager` that needs to
/// auto-bind all advertised instances of some specific globals
/// whenever they happen. Typically, your application will likely
/// want to keep track of all `wl_seat` and `wl_output` globals
/// to be able to correctly react to user input and their different
/// monitors.
///
/// The output of this macro is a closure, that can be given to
/// `GlobalManager::new_with_cb` as the callback argument.
///
/// Example use is typically:
///
/// ```no_run
/// # #[macro_use] extern crate wayland_client;
/// use wayland_client::GlobalManager;
/// # use wayland_client::{Display, Main, DispatchData};
/// use wayland_client::protocol::{wl_output, wl_seat};
///
/// # fn main() {
/// # let display = Display::connect_to_env().unwrap();
/// # let mut event_queue = display.create_event_queue();
/// # let seat_implementor: fn(Main<_>, DispatchData) = unimplemented!();
/// # let output_implementor: fn(Main<_>, DispatchData) = unimplemented!();
/// let globals = GlobalManager::new_with_cb(
/// &display.attach(event_queue.token()),
/// global_filter!(
/// // Bind all wl_seat with version 4
/// [wl_seat::WlSeat, 4, seat_implementor],
/// // Bind all wl_output with version 1
/// [wl_output::WlOutput, 1, output_implementor]
/// )
/// );
/// # }
/// ```
///
/// The supplied callbacks for each global kind must be an instance of a type
/// implementing the `GlobalImplementor<I>` trait. The argument provided to your
/// callback is a `Main` handle of the newly instantiated global, and you should assign it
/// to a filter in this callback if you plan to do so.. The error case happens if the server
/// advertised a lower version of the global than the one you requested, in which case you
/// are given the version it advertised in the error method, if you want to handle it graciously.
///
/// You can also provide closures for the various callbacks, in this case the errors will
/// be ignored. However, due to a lack of capability of rustc's inference, you'll likely need
/// to add some type annotation to your closure, typically something like this:
///
/// ```ignore
/// global_filter!(
/// [Interface, version, |proxy: Main<_>, dispatch_data| {
/// /* Setup the global as required */
/// }]
/// );
/// ```
#[macro_export]
macro_rules! global_filter {
($([$interface:ty, $version:expr, $callback:expr]),*) => {
{
use $crate::protocol::wl_registry;
use $crate::{GlobalEvent, Interface, Attached, GlobalImplementor, DispatchData};
type Callback = Box<dyn FnMut(u32, u32, Attached<wl_registry::WlRegistry>, DispatchData<'_>)>;
let mut callbacks: Vec<(&'static str, Callback)> = Vec::new();
// Create the callback list
$({
let mut cb = { $callback };
callbacks.push((
<$interface as Interface>::NAME,
Box::new(move |id, version, registry: Attached<wl_registry::WlRegistry>, ddata: DispatchData| {
if version < $version {
GlobalImplementor::<$interface>::error(&mut cb, version, ddata);
} else {
let proxy = registry.bind::<$interface>(version, id);
GlobalImplementor::<$interface>::new_global(&mut cb, proxy, ddata);
}
}) as Box<_>
));
})*
// return the global closure
move |event: GlobalEvent, registry: Attached<wl_registry::WlRegistry>, ddata| {
if let GlobalEvent::New { id, interface, version } = event {
for &mut (iface, ref mut cb) in &mut callbacks {
if iface == interface {
cb(id, version, registry, ddata);
break;
}
}
}
}
}
}
}

View file

@ -0,0 +1,321 @@
//! Client-side Wayland connector
//!
//! ## Overview
//!
//! This crate provides the interfaces and machinery to safely create
//! client applications for the Wayland protocol. It can be used as a rust
//! implementation of the protocol or as a wrapper around the system-wide
//! `libwayland-client.so` if you enable the `use_system_lib` cargo feature.
//!
//! The Wayland protocol revolves around the creation of various objects
//! and the exchange of messages associated to these objects. The initial
//! object is always the `Display`, that you get at initialization of the
//! connection, exposed by this crate as `Display::connect_to_env()`.
//!
//! ## Protocol and messages handling model
//!
//! The protocol being bi-directional, you can send and receive messages.
//! Sending messages is done via methods of Rust objects corresponding to the wayland protocol
//! objects, receiving and handling them is done by providing callbacks.
//!
//! ### Proxies
//!
//! Wayland objects are represented by proxies, which are handles to them.
//! You can interact with them in 4 states:
//!
//! - As the interface object directly `I`. This representation is the most immediate
//! one. It allows you to send requests though this object and can be send accross threads.
//! - As a `Proxy<I>`. This representation is suitable if you want to access the proxy as
//! a proxy, rather than a wayland object. You can convert between `I` and `Proxy<I>` via
//! the `From` and `Into` traits, and get a `&Proxy<I>` from an `I` via the `AsRef` trait.
//! - As a `Main<I>`. This represents a main handle to this proxy, and allows you greater
//! control of the object, but cannot be shared accros threads. This handle allows you to
//! assign filters to the object, and send requests that create new objects.
//! - As an `Attached<I>`. If you use more than one event queue (see below), this allows you
//! to control on which event queue the children object are created.
//!
//! There is not a 1 to 1 mapping between Rust object instances and protocol
//! objects. Rather, you can think of the Rust objects as `Rc`-like handles to a
//! Wayland object. Multiple instances of a Rust object can exist referring to the same
//! protocol object.
//!
//! Similarly, the lifetimes of the protocol objects and the Rust objects are
//! not tightly tied. As protocol objects are created and destroyed by protocol
//! messages, it can happen that an object gets destroyed while one or more
//! Rust objects still refer to it. In such case, these Rust objects will be disabled
//! and the `alive()` method on the underlying `Proxy<I>` will start to return `false`.
//!
//! Sending requests on dead objects will be silently ignored. And if these requests
//! would create new objects, these objects will be created dead.
//!
//! ### Filters
//!
//! Your wayland objects can receive events from the server, which need to be processed.
//! To do so, you can assign `Filter`s to your object. These are specially wrapped closure
//! so that several objects can be assigned to the same `Filter`, to ease state sharing
//! between the code handling different objects.
//!
//! If an object is not assigned to any `Filter`, its events will instead be delivered to the
//! fallback closure given to its event queue when dispatching it.
//!
//! ## Event Queues
//!
//! The Wayland client machinery provides the possibility to have one or more event queues
//! handling the processing of received messages. All Wayland objects are associated to an
//! event queue, which controls when its events are dispatched.
//!
//! Events received from the server are stored in an internal buffer, and processed (by calling
//! the appropriate callbacks) when the associated event queue is dispatched.
//!
//! When you send a request creating a new object, this new object will be assigned to an event
//! queue depending on the parent object that created it.
//!
//! - If the request was sent from a `Main<I>` handle, the child object will be assigned to the
//! same event queue as its parent.
//! - If the request was sent from an `Attached<I>` handle, the child object will be assigned to
//! the event queue its parent has been attached to.
//!
//! At the beginning you'll need to create an event queue and assign the initial `Proxy<WlDisplay>`
//! to it.
//!
//! ## Dynamic linking with `libwayland-client.so`
//!
//! If you need to gracefully handle the case of a system on which Wayland is not installed (by
//! fallbacking to X11 for example), you can do so by activating the `dlopen` cargo feature.
//!
//! When this is done, the library will be loaded a runtime rather than directly linked. And trying
//! to create a `Display` on a system that does not have this library will return a `NoWaylandLib`
//! error.
#![warn(missing_docs, missing_debug_implementations)]
#[macro_use]
extern crate bitflags;
#[cfg(not(feature = "use_system_lib"))]
#[macro_use]
extern crate downcast_rs as downcast;
#[cfg_attr(feature = "use_system_lib", macro_use)]
extern crate wayland_sys;
mod display;
mod event_queue;
mod globals;
mod proxy;
pub use anonymous_object::AnonymousObject;
pub use display::{ConnectError, Display, ProtocolError};
pub use event_queue::{EventQueue, QueueToken, ReadEventsGuard};
pub use globals::{GlobalError, GlobalEvent, GlobalImplementor, GlobalManager};
pub use imp::ProxyMap;
pub use proxy::{Attached, Main, Proxy};
pub use wayland_commons::{
filter::{DispatchData, Filter},
user_data::UserData,
Interface, MessageGroup, NoMessage,
};
// rust implementation
#[cfg(not(feature = "use_system_lib"))]
#[path = "rust_imp/mod.rs"]
mod imp;
// C-lib based implementation
#[cfg(feature = "use_system_lib")]
#[path = "native_lib/mod.rs"]
mod imp;
/// C-associated types
///
/// Required for plugging wayland-scanner generated protocols
/// or interfacing with C code using wayland objects.
pub mod sys {
pub use wayland_sys::{client, common};
}
pub mod protocol {
#![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)]
#![allow(non_upper_case_globals, non_snake_case, unused_imports)]
#![allow(missing_docs, clippy::all)]
pub(crate) use crate::{AnonymousObject, Attached, Main, Proxy, ProxyMap};
pub(crate) use wayland_commons::map::{Object, ObjectMetadata};
pub(crate) use wayland_commons::smallvec;
pub(crate) use wayland_commons::wire::{Argument, ArgumentType, Message, MessageDesc};
pub(crate) use wayland_commons::{Interface, MessageGroup};
pub(crate) use wayland_sys as sys;
include!(concat!(env!("OUT_DIR"), "/wayland_api.rs"));
}
mod anonymous_object {
use super::{Interface, NoMessage, Proxy};
use std::fmt::{self, Debug, Formatter};
/// Anonymous interface
///
/// A special Interface implementation representing an
/// handle to an object for which the interface is not known.
#[derive(Clone, Eq, PartialEq)]
pub struct AnonymousObject(pub(crate) Proxy<AnonymousObject>);
impl Interface for AnonymousObject {
type Request = NoMessage;
type Event = NoMessage;
const NAME: &'static str = "<anonymous>";
const VERSION: u32 = 0;
fn c_interface() -> *const crate::sys::common::wl_interface {
std::ptr::null()
}
}
impl AsRef<Proxy<AnonymousObject>> for AnonymousObject {
#[inline]
fn as_ref(&self) -> &Proxy<Self> {
&self.0
}
}
impl From<Proxy<AnonymousObject>> for AnonymousObject {
#[inline]
fn from(proxy: Proxy<Self>) -> Self {
AnonymousObject(proxy)
}
}
impl From<AnonymousObject> for Proxy<AnonymousObject> {
#[inline]
fn from(value: AnonymousObject) -> Self {
value.0
}
}
impl Debug for AnonymousObject {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("{:?}", self.0))
}
}
}
/// Enum of possible argument in an event
#[derive(Debug)]
pub enum Argument {
/// i32
Int(i32),
/// u32
Uint(u32),
/// float
Float(f32),
/// CString
Str(Option<String>),
/// id of a wayland object
Object(Option<Proxy<AnonymousObject>>),
/// id of a newly created wayland object
NewId(Option<Main<AnonymousObject>>),
/// Vec<u8>
Array(Option<Vec<u8>>),
/// RawFd
Fd(std::os::unix::io::RawFd),
}
/// An generic event
#[derive(Debug)]
pub struct RawEvent {
/// Interface of the associated object
pub interface: &'static str,
/// Opcode of the event
pub opcode: u16,
/// Name of the event
pub name: &'static str,
/// Arguments of the message
pub args: Vec<Argument>,
}
/// Generate an enum joining several objects events
///
/// This macro allows you to easily create a enum type for use with your message Filters. It is
/// used like so:
///
/// ```no_run
/// # use wayland_client::protocol::{wl_pointer::WlPointer, wl_keyboard::WlKeyboard, wl_surface::WlSurface};
/// # use wayland_client::event_enum;
/// event_enum!(
/// MyEnum |
/// Pointer => WlPointer,
/// Keyboard => WlKeyboard,
/// Surface => WlSurface
/// );
/// ```
///
/// This will generate the following enum, unifying the events from each of the provided interface:
///
/// ```ignore
/// pub enum MyEnum {
/// Pointer { event: WlPointer::Event, object: Main<WlPointer> },
/// Keyboard { event: WlKeyboard::Event, object: Main<WlKeyboard> },
/// Surface { event: WlSurface::Event, object: Main<WlSurface> }
/// }
/// ```
///
/// It will also generate the appropriate `From<_>` implementation so that a `Filter<MyEnum>` can be
/// used as an implementation for `WlPointer`, `WlKeyboard` and `WlSurface`.
///
/// If you want to add custom messages to the enum, the macro also supports it:
///
/// ```no_run
/// # use wayland_client::protocol::{wl_pointer::WlPointer, wl_keyboard::WlKeyboard, wl_surface::WlSurface};
/// # use wayland_client::event_enum;
/// # struct SomeType;
/// # struct OtherType;
/// event_enum!(
/// MyEnum |
/// Pointer => WlPointer,
/// Keyboard => WlKeyboard,
/// Surface => WlSurface |
/// MyMessage => SomeType,
/// OtherMessage => OtherType
/// );
/// ```
///
/// will generate the following enum:
///
/// ```ignore
/// pub enum MyEnum {
/// Pointer { event: WlPointer::Event, object: Main<WlPointer> },
/// Keyboard { event: WlKeyboard::Event, object: Main<WlKeyboard> },
/// Surface { event: WlSurface::Event, object: Main<WlSurface> },
/// MyMessage(SomeType),
/// OtherMessage(OtherType)
/// }
/// ```
///
/// as well as implementations of `From<SomeType>` and `From<OtherType>`, so that these types can
/// directly be provided into a `Filter<MyEnum>`.
#[macro_export]
macro_rules! event_enum(
($(#[$attrs:meta])* $enu:ident | $($evt_name:ident => $iface:ty),*) => {
$crate::event_enum!($(#[$attrs])* $enu | $($evt_name => $iface),* | );
};
($(#[$attrs:meta])* $enu:ident | $($evt_name:ident => $iface:ty),* | $($name:ident => $value:ty),*) => {
$(#[$attrs])*
pub enum $enu {
$(
$evt_name { event: <$iface as $crate::Interface>::Event, object: $crate::Main<$iface> },
)*
$(
$name($value),
)*
}
$(
impl From<($crate::Main<$iface>, <$iface as $crate::Interface>::Event)> for $enu {
fn from((object, event): ($crate::Main<$iface>, <$iface as $crate::Interface>::Event)) -> $enu {
$enu::$evt_name { event, object }
}
}
)*
$(
impl From<$value> for $enu {
fn from(value: $value) -> $enu {
$enu::$name(value)
}
}
)*
};
);

View file

@ -0,0 +1,128 @@
use std::io;
use std::os::unix::io::RawFd;
use std::sync::Arc;
use crate::protocol::wl_display::WlDisplay;
use wayland_sys::client::*;
use crate::{ConnectError, Proxy};
use super::{EventQueueInner, ProxyInner};
pub(crate) struct DisplayInner {
proxy: Proxy<WlDisplay>,
display: Arc<DisplayGuard>,
}
pub(crate) struct DisplayGuard {
ptr: *mut wl_display,
external: bool,
}
unsafe impl Send for DisplayInner {}
unsafe impl Sync for DisplayInner {}
unsafe fn make_display(ptr: *mut wl_display) -> Result<Arc<DisplayInner>, ConnectError> {
if ptr.is_null() {
return Err(ConnectError::NoCompositorListening);
}
let mut inner = DisplayInner {
proxy: Proxy::from_c_ptr(ptr as *mut _),
display: Arc::new(DisplayGuard { ptr, external: false }),
};
inner.proxy.inner.display = Some(Arc::downgrade(&inner.display));
Ok(Arc::new(inner))
}
impl DisplayInner {
pub unsafe fn from_fd(fd: RawFd) -> Result<Arc<DisplayInner>, ConnectError> {
if !::wayland_sys::client::is_lib_available() {
return Err(ConnectError::NoWaylandLib);
}
let display_ptr = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_connect_to_fd, fd);
make_display(display_ptr)
}
pub(crate) fn get_connection_fd(&self) -> ::std::os::unix::io::RawFd {
unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_get_fd, self.ptr()) }
}
pub(crate) fn ptr(&self) -> *mut wl_display {
self.display.ptr
}
pub(crate) fn flush(&self) -> io::Result<()> {
let ret = unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_flush, self.ptr()) };
if ret >= 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
pub(crate) fn create_event_queue(me: &Arc<DisplayInner>) -> EventQueueInner {
unsafe {
let ptr = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_create_queue, me.ptr());
EventQueueInner::new(me.clone(), ptr)
}
}
pub(crate) fn get_proxy(&self) -> &Proxy<WlDisplay> {
&self.proxy
}
pub(crate) fn protocol_error(&self) -> Option<crate::ProtocolError> {
let ret = unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_get_error, self.ptr()) };
if ret == ::nix::errno::Errno::EPROTO as i32 {
let mut interface = ::std::ptr::null_mut();
let mut id = 0;
let code = unsafe {
ffi_dispatch!(
WAYLAND_CLIENT_HANDLE,
wl_display_get_protocol_error,
self.ptr(),
&mut interface,
&mut id
)
};
let interface_name = if !interface.is_null() {
unsafe { ::std::ffi::CStr::from_ptr((*interface).name) }
.to_str()
.unwrap_or("<unknown>")
} else {
"<unknown>"
};
Some(crate::ProtocolError {
code,
object_id: id,
object_interface: interface_name,
message: String::new(),
})
} else {
None
}
}
pub(crate) unsafe fn from_external(display_ptr: *mut wl_display) -> Arc<DisplayInner> {
Arc::new(DisplayInner {
proxy: Proxy::wrap(ProxyInner::from_external_display(display_ptr as *mut _)),
display: Arc::new(DisplayGuard { ptr: display_ptr, external: true }),
})
}
}
impl Drop for DisplayGuard {
fn drop(&mut self) {
if !self.external {
// disconnect only if we are owning this display
unsafe {
ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_disconnect, self.ptr);
}
}
}
}

View file

@ -0,0 +1,142 @@
use std::cell::RefCell;
use std::io;
use std::sync::Arc;
use crate::{AnonymousObject, DispatchData, Main, RawEvent};
use wayland_sys::client::*;
use super::DisplayInner;
scoped_tls::scoped_thread_local! {
pub(crate) static DISPATCH_METADATA: RefCell<(&mut dyn FnMut(RawEvent, Main<AnonymousObject>, DispatchData), DispatchData)>
}
#[allow(clippy::transmute_ptr_to_ptr)]
fn with_dispatch_meta<T, FB, F>(mut fb: FB, data: DispatchData, f: F) -> T
where
FB: FnMut(RawEvent, Main<AnonymousObject>, DispatchData),
F: FnOnce() -> T,
{
// We erase the lifetime of the callback to be able to store it in the tls,
// it's safe as it'll only last until the end of this function call anyway
let fb = unsafe { std::mem::transmute(&mut fb as &mut dyn FnMut(_, _, _)) };
let data = unsafe { std::mem::transmute(data) };
DISPATCH_METADATA.set(&RefCell::new((fb, data)), f)
}
pub(crate) struct EventQueueInner {
wlevq: *mut wl_event_queue,
inner: Arc<super::DisplayInner>,
}
impl EventQueueInner {
pub(crate) fn new(inner: Arc<DisplayInner>, wlevq: *mut wl_event_queue) -> EventQueueInner {
EventQueueInner { wlevq, inner }
}
pub(crate) fn dispatch<F>(&self, data: DispatchData, fallback: F) -> io::Result<u32>
where
F: FnMut(RawEvent, Main<AnonymousObject>, DispatchData<'_>),
{
with_dispatch_meta(fallback, data, || {
let ret = unsafe {
ffi_dispatch!(
WAYLAND_CLIENT_HANDLE,
wl_display_dispatch_queue,
self.inner.ptr(),
self.wlevq
)
};
if ret >= 0 {
Ok(ret as u32)
} else {
Err(io::Error::last_os_error())
}
})
}
pub(crate) fn dispatch_pending<F>(&self, data: DispatchData, fallback: F) -> io::Result<u32>
where
F: FnMut(RawEvent, Main<AnonymousObject>, DispatchData<'_>),
{
with_dispatch_meta(fallback, data, || {
let ret = unsafe {
ffi_dispatch!(
WAYLAND_CLIENT_HANDLE,
wl_display_dispatch_queue_pending,
self.inner.ptr(),
self.wlevq
)
};
if ret >= 0 {
Ok(ret as u32)
} else {
Err(io::Error::last_os_error())
}
})
}
pub(crate) fn sync_roundtrip<F>(&self, data: DispatchData, fallback: F) -> io::Result<u32>
where
F: FnMut(RawEvent, Main<AnonymousObject>, DispatchData<'_>),
{
with_dispatch_meta(fallback, data, || {
let ret = unsafe {
ffi_dispatch!(
WAYLAND_CLIENT_HANDLE,
wl_display_roundtrip_queue,
self.inner.ptr(),
self.wlevq
)
};
if ret >= 0 {
Ok(ret as u32)
} else {
Err(io::Error::last_os_error())
}
})
}
pub(crate) fn prepare_read(&self) -> Result<(), ()> {
let ret = unsafe {
ffi_dispatch!(
WAYLAND_CLIENT_HANDLE,
wl_display_prepare_read_queue,
self.inner.ptr(),
self.wlevq
)
};
if ret >= 0 {
Ok(())
} else {
Err(())
}
}
pub(crate) fn read_events(&self) -> io::Result<()> {
let ret = unsafe {
ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_read_events, self.inner.ptr())
};
if ret >= 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
pub(crate) fn cancel_read(&self) {
unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_cancel_read, self.inner.ptr()) }
}
pub(crate) unsafe fn assign_proxy(&self, proxy: *mut wl_proxy) {
ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_set_queue, proxy, self.wlevq)
}
}
impl Drop for EventQueueInner {
fn drop(&mut self) {
unsafe {
ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_event_queue_destroy, self.wlevq);
}
}
}

View file

@ -0,0 +1,36 @@
mod display;
mod event_queue;
mod proxy;
pub(crate) use self::display::DisplayInner;
pub(crate) use self::event_queue::EventQueueInner;
pub(crate) use self::proxy::ProxyInner;
use crate::{Interface, Main, Proxy};
/// This type only exists for type-level compatibility
/// with the rust implementation.
///
/// It is an empty enum that cannot be instantiated
#[derive(Debug)]
pub enum ProxyMap {}
impl ProxyMap {
/// Unusable method only existing for type-level compatibility
pub fn get<I: Interface>(&mut self, _: u32) -> Option<Proxy<I>> {
match *self {}
}
/// Unusable method only existing for type-level compatibility
pub fn get_or_dead<I: Interface>(&mut self, _: u32) -> Proxy<I> {
match *self {}
}
/// Unusable method only existing for type-level compatibility
pub fn get_new<I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>>(
&mut self,
_: u32,
) -> Option<Main<I>> {
match *self {}
}
}

View file

@ -0,0 +1,519 @@
use std::cell::RefCell;
use std::os::raw::{c_int, c_void};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Weak};
use crate::{Interface, Main, Proxy, RawEvent};
use wayland_commons::filter::Filter;
use wayland_commons::user_data::UserData;
use wayland_commons::wire::ArgumentType;
use wayland_commons::MessageGroup;
use super::EventQueueInner;
use wayland_sys::client::*;
use wayland_sys::common::*;
pub struct ProxyInternal {
alive: AtomicBool,
user_data: UserData,
}
impl ProxyInternal {
pub fn new(user_data: UserData) -> ProxyInternal {
ProxyInternal { alive: AtomicBool::new(true), user_data }
}
}
pub(crate) struct ProxyInner {
internal: Option<Arc<ProxyInternal>>,
ptr: *mut wl_proxy,
wrapping: Option<*mut wl_proxy>,
pub(crate) display: Option<Weak<super::display::DisplayGuard>>,
}
unsafe impl Send for ProxyInner {}
unsafe impl Sync for ProxyInner {}
impl ProxyInner {
pub(crate) fn is_alive(&self) -> bool {
if let Some(ref weak) = self.display {
if Weak::strong_count(weak) == 0 {
// the connection is dead
return false;
}
}
self.internal.as_ref().map(|i| i.alive.load(Ordering::Acquire)).unwrap_or(true)
}
pub(crate) fn is_external(&self) -> bool {
self.internal.is_none()
}
pub(crate) fn version(&self) -> u32 {
if !self.is_alive() {
return 0;
}
let version =
unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_version, self.ptr) as u32 };
if version == 0 {
// For backcompat reasons the C libs return 0 as a version for the wl_display
// So override it
return 1;
}
version
}
pub(crate) fn is_interface<I: Interface>(&self) -> bool {
if !self.is_alive() {
return false;
}
unsafe {
let c_class_ptr = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_class, self.ptr);
let class = std::ffi::CStr::from_ptr(c_class_ptr);
let i_class = std::ffi::CStr::from_ptr((*I::c_interface()).name);
class == i_class
}
}
pub(crate) fn id(&self) -> u32 {
if !self.is_alive() {
return 0;
}
unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_id, self.ptr) }
}
pub(crate) fn user_data(&self) -> &UserData {
static INVALID_USERDATA: UserData = UserData::new();
if let Some(ref inner) = self.internal {
&inner.user_data
} else {
INVALID_USERDATA.set_threadsafe(|| ());
&INVALID_USERDATA
}
}
pub(crate) fn send<I, J>(&self, msg: I::Request, version: Option<u32>) -> Option<ProxyInner>
where
I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>,
J: Interface + AsRef<Proxy<J>> + From<Proxy<J>>,
{
let destructor = msg.is_destructor();
let opcode = msg.opcode();
// figure out if the call creates an object
let nid_idx = I::Request::MESSAGES[opcode as usize]
.signature
.iter()
.position(|&t| t == ArgumentType::NewId);
let alive = self.is_alive();
let ret = if let Some(mut nid_idx) = nid_idx {
if let Some(o) = I::Request::child(opcode, 1, &()) {
if !o.is_interface::<J>() {
panic!("Trying to use 'send_constructor' with the wrong return type. Required interface {} but the message creates interface {}", J::NAME, o.interface)
}
} else {
// there is no target interface in the protocol, this is a generic object-creating
// function (likely wl_registry.bind), the newid arg will thus expand to (str, u32, obj)
nid_idx += 2;
}
let version = version.unwrap_or_else(|| self.version());
if alive {
if self.wrapping.is_none() {
panic!("Attemping to create an object from a non-attached proxy.");
}
unsafe {
let ptr = msg.as_raw_c_in(|opcode, args| {
assert!(
args[nid_idx].o.is_null(),
"Trying to use 'send_constructor' with a non-placeholder object."
);
ffi_dispatch!(
WAYLAND_CLIENT_HANDLE,
wl_proxy_marshal_array_constructor_versioned,
self.wrapping.unwrap_or(self.ptr),
opcode,
args.as_mut_ptr(),
J::c_interface(),
version
)
});
let mut new_proxy = ProxyInner::init_from_c_ptr::<J>(ptr);
new_proxy.display = self.display.clone();
Some(new_proxy)
}
} else {
// Create a dead proxy ex-nihilo
Some(ProxyInner::dead())
}
} else {
// not a constructor, nothing much to do
if alive {
msg.as_raw_c_in(|opcode, args| unsafe {
ffi_dispatch!(
WAYLAND_CLIENT_HANDLE,
wl_proxy_marshal_array,
self.wrapping.unwrap_or(self.ptr),
opcode,
args.as_ptr() as *mut _
);
});
}
None
};
if destructor && alive {
// we need to destroy the proxy now
if let Some(ref internal) = self.internal {
internal.alive.store(false, Ordering::Release);
unsafe {
let user_data =
ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, self.ptr);
ffi_dispatch!(
WAYLAND_CLIENT_HANDLE,
wl_proxy_set_user_data,
self.ptr,
std::ptr::null_mut()
);
let _ = Box::from_raw(user_data as *mut ProxyUserData<I>);
}
}
unsafe {
ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy, self.ptr);
}
}
ret
}
pub(crate) fn equals(&self, other: &ProxyInner) -> bool {
if !self.is_alive() {
return false;
}
match (&self.internal, &other.internal) {
(&Some(ref my_inner), &Some(ref other_inner)) => Arc::ptr_eq(my_inner, other_inner),
(&None, &None) => self.ptr == other.ptr,
_ => false,
}
}
pub(crate) fn detach(&mut self) {
if !self.is_external() && !self.is_alive() {
return;
}
if let Some(ptr) = self.wrapping.take() {
// self.ptr == self.wrapping means we are a Main<_>
if ptr != self.ptr {
unsafe {
ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_wrapper_destroy, ptr);
}
}
}
}
pub(crate) fn attach(&mut self, queue: &EventQueueInner) {
if !self.is_external() && !self.is_alive() {
return;
}
let wrapper_ptr;
unsafe {
wrapper_ptr = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_create_wrapper, self.ptr);
queue.assign_proxy(wrapper_ptr);
}
self.wrapping = Some(wrapper_ptr);
}
pub(crate) fn c_ptr(&self) -> *mut wl_proxy {
self.wrapping.unwrap_or(self.ptr)
}
pub fn assign<I, E>(&self, filter: Filter<E>)
where
I: Interface + AsRef<Proxy<I>> + From<Proxy<I>> + Sync,
E: From<(Main<I>, I::Event)> + 'static,
I::Event: MessageGroup<Map = super::ProxyMap>,
{
if self.is_external() {
panic!("Cannot assign an external proxy to a filter.");
}
if !self.is_alive() {
return;
}
unsafe {
let user_data = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, self.ptr)
as *mut ProxyUserData<I>;
if let Ok(ref mut guard) = (*user_data).implem.try_borrow_mut() {
**guard =
Some(Box::new(move |evt, obj, data| filter.send((obj, evt).into(), data)));
} else {
panic!("Re-assigning an object from within its own callback is not supported.");
}
}
}
pub(crate) unsafe fn init_from_c_ptr<I: Interface + From<Proxy<I>> + AsRef<Proxy<I>>>(
ptr: *mut wl_proxy,
) -> Self {
let new_user_data = Box::new(ProxyUserData::<I>::new(UserData::new()));
let internal = new_user_data.internal.clone();
ffi_dispatch!(
WAYLAND_CLIENT_HANDLE,
wl_proxy_add_dispatcher,
ptr,
proxy_dispatcher::<I>,
&::wayland_sys::RUST_MANAGED as *const _ as *const _,
Box::into_raw(new_user_data) as *mut _
);
// We are a Main<_>, so ptr == wrapping
ProxyInner { internal: Some(internal), ptr, wrapping: Some(ptr), display: None }
}
fn dead() -> Self {
ProxyInner {
internal: Some(Arc::new(ProxyInternal {
alive: AtomicBool::new(false),
user_data: UserData::new(),
})),
ptr: std::ptr::null_mut(),
wrapping: None,
display: None,
}
}
pub(crate) unsafe fn from_c_ptr<I: Interface + From<Proxy<I>> + AsRef<Proxy<I>>>(
ptr: *mut wl_proxy,
) -> Self {
if ptr.is_null() {
return Self::dead();
}
let is_managed = {
ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_listener, ptr)
== &::wayland_sys::RUST_MANAGED as *const u8 as *const _
};
let internal = if is_managed {
let user_data = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, ptr)
as *mut ProxyUserData<I>;
Some((*user_data).internal.clone())
} else {
None
};
ProxyInner { internal, ptr, wrapping: None, display: None }
}
pub(crate) unsafe fn from_external_display(d: *mut wl_proxy) -> ProxyInner {
ProxyInner { internal: None, ptr: d, wrapping: None, display: None }
}
}
impl Clone for ProxyInner {
fn clone(&self) -> ProxyInner {
let mut new = ProxyInner {
internal: self.internal.clone(),
ptr: self.ptr,
wrapping: None,
display: self.display.clone(),
};
if !self.is_external() && !self.is_alive() {
return new;
}
if let Some(ptr) = self.wrapping {
if ptr != self.ptr {
// create a new wrapper to keep correct track of them
let wrapper_ptr;
unsafe {
wrapper_ptr =
ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_create_wrapper, ptr);
}
new.wrapping = Some(wrapper_ptr);
} else {
// self.wrapping == self.ptr means we are a Main<_>, not a wrapper
new.wrapping = Some(self.ptr);
}
}
new
}
}
impl Drop for ProxyInner {
fn drop(&mut self) {
// always detach on drop to avoid leaking wrappers
self.detach();
}
}
type BoxedCallback<I> = Box<dyn Fn(<I as Interface>::Event, Main<I>, crate::DispatchData<'_>)>;
struct ProxyUserData<I: Interface + From<Proxy<I>> + AsRef<Proxy<I>>> {
internal: Arc<ProxyInternal>,
implem: RefCell<Option<BoxedCallback<I>>>,
}
impl<I: Interface + From<Proxy<I>> + AsRef<Proxy<I>>> ProxyUserData<I> {
fn new(user_data: UserData) -> ProxyUserData<I> {
ProxyUserData {
internal: Arc::new(ProxyInternal::new(user_data)),
implem: RefCell::new(None),
}
}
}
unsafe extern "C" fn proxy_dispatcher<I: Interface>(
_implem: *const c_void,
proxy: *mut c_void,
opcode: u32,
_msg: *const wl_message,
args: *const wl_argument,
) -> c_int
where
I: Interface + From<Proxy<I>> + AsRef<Proxy<I>>,
{
let proxy = proxy as *mut wl_proxy;
// We don't need to worry about panic-safeness, because if there is a panic,
// we'll abort the process, so no access to corrupted data is possible.
let ret = ::std::panic::catch_unwind(move || {
let must_destroy = I::Event::MESSAGES[opcode as usize].destructor;
// retrieve the impl
let user_data = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, proxy);
{
// a scope to ensure the &mut reference to the user data no longer exists when
// the callback is invoked
let (implem, internal) = {
let user_data = &mut *(user_data as *mut ProxyUserData<I>);
if must_destroy {
user_data.internal.alive.store(false, Ordering::Release);
ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy, proxy);
}
// remove the implem from the user data, so that it remains valid even if the
// object is destroyed from within the callback
(user_data.implem.borrow_mut().take(), user_data.internal.clone())
};
// if there is an implem, call it, otherwise call the fallback
match implem {
Some(ref implem) => {
// parse the message:
let msg = I::Event::from_raw_c(proxy as *mut _, opcode, args)?;
// create the proxy object
let mut proxy_inner = ProxyInner::from_c_ptr::<I>(proxy);
// This proxy must be a Main, so it as attached wrapping itself
proxy_inner.wrapping = Some(proxy_inner.ptr);
let proxy_obj = crate::Main::wrap(proxy_inner);
super::event_queue::DISPATCH_METADATA.with(|meta| {
let mut meta = meta.borrow_mut();
let (_, ref mut dispatch_data) = *meta;
implem(msg, proxy_obj, dispatch_data.reborrow());
})
}
None => {
// parse the message:
let msg = parse_raw_event::<I>(opcode, args);
// create the proxy object
let proxy_obj = crate::Main::wrap(ProxyInner::from_c_ptr::<I>(proxy));
super::event_queue::DISPATCH_METADATA.with(|meta| {
let mut meta = meta.borrow_mut();
let (ref mut fallback, ref mut dispatch_data) = *meta;
(&mut *fallback)(msg, proxy_obj, dispatch_data.reborrow());
})
}
}
// if the proxy is still alive, put the implem back in place
if internal.alive.load(Ordering::Acquire) {
let user_data = &mut *(user_data as *mut ProxyUserData<I>);
let mut implem_place = user_data.implem.borrow_mut();
// only place the implem back if it has not been replaced with an other
if implem_place.is_none() {
*implem_place = implem;
}
}
}
if must_destroy {
// final cleanup
let _ = Box::from_raw(user_data as *mut ProxyUserData<I>);
}
Ok(())
});
// check the return status
match ret {
Ok(Ok(())) => 0,
Ok(Err(())) => {
eprintln!(
"[wayland-client error] Attempted to dispatch unknown opcode {} for {}, aborting.",
opcode,
I::NAME
);
libc::abort();
}
Err(_) => {
eprintln!("[wayland-client error] A handler for {} panicked.", I::NAME);
libc::abort()
}
}
}
unsafe fn parse_raw_event<I: Interface>(opcode: u32, args: *const wl_argument) -> RawEvent {
let desc = &I::Event::MESSAGES[opcode as usize];
let c_args = std::slice::from_raw_parts(args, desc.signature.len());
let mut args = Vec::with_capacity(c_args.len());
// parse the arguments one by one
for (t, a) in desc.signature.iter().zip(c_args.iter()) {
args.push(match t {
ArgumentType::Int => crate::Argument::Int(a.i),
ArgumentType::Uint => crate::Argument::Uint(a.u),
ArgumentType::Fixed => crate::Argument::Float((a.f as f32) / 256.),
ArgumentType::Array => {
if a.a.is_null() {
crate::Argument::Array(None)
} else {
let array = &*a.a;
crate::Argument::Array(Some(
::std::slice::from_raw_parts(array.data as *const u8, array.size)
.to_owned(),
))
}
}
ArgumentType::Str => {
if a.s.is_null() {
crate::Argument::Str(None)
} else {
crate::Argument::Str(Some(
::std::ffi::CStr::from_ptr(a.s).to_string_lossy().into_owned(),
))
}
}
ArgumentType::Fd => crate::Argument::Fd(a.h),
ArgumentType::Object => {
if a.o.is_null() {
crate::Argument::Object(None)
} else {
crate::Argument::Object(Some(Proxy::from_c_ptr(a.o as *mut _)))
}
}
ArgumentType::NewId => {
if a.o.is_null() {
crate::Argument::NewId(None)
} else {
crate::Argument::NewId(Some(Main::from_c_ptr(a.o as *mut _)))
}
}
});
}
RawEvent { interface: I::NAME, opcode: opcode as u16, name: desc.name, args }
}

View file

@ -0,0 +1,441 @@
use std::fmt::{self, Debug, Formatter};
use std::ops::Deref;
use super::AnonymousObject;
use wayland_commons::user_data::UserData;
use wayland_commons::Interface;
use wayland_sys::client::*;
use crate::event_queue::QueueToken;
use crate::imp::ProxyInner;
use wayland_commons::{filter::Filter, MessageGroup};
/// An handle to a wayland proxy
///
/// This represents a wayland object instantiated in your client
/// session. Several handles to the same object can exist at a given
/// time, and cloning them won't create a new protocol object, only
/// clone the handle. The lifetime of the protocol object is **not**
/// tied to the lifetime of these handles, but rather to sending or
/// receiving destroying messages.
///
/// These handles are notably used to send requests to the server. To do this
/// you need to convert them to the corresponding Rust object (using `.into()`)
/// and use methods on the Rust object.
///
/// This handle is the most conservative one: it can be sent between threads,
/// but you cannot send any message that would create a new object using it.
/// You must attach it to a event queue, that will host the newly created objects.
pub struct Proxy<I: Interface> {
_i: ::std::marker::PhantomData<&'static I>,
pub(crate) inner: ProxyInner,
}
impl<I: Interface> Clone for Proxy<I> {
fn clone(&self) -> Proxy<I> {
let mut cloned = self.inner.clone();
// an owned Proxy must always be detached
cloned.detach();
Proxy { _i: ::std::marker::PhantomData, inner: cloned }
}
}
impl<I: Interface> PartialEq for Proxy<I>
where
I: AsRef<Proxy<I>> + From<Proxy<I>>,
{
fn eq(&self, other: &Proxy<I>) -> bool {
self.equals(other)
}
}
impl<I: Interface> Debug for Proxy<I> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}@{}", I::NAME, self.inner.id())
}
}
impl<I: Interface> Eq for Proxy<I> where I: AsRef<Proxy<I>> + From<Proxy<I>> {}
impl<I: Interface> Proxy<I>
where
I: AsRef<Proxy<I>> + From<Proxy<I>>,
{
#[allow(dead_code)]
pub(crate) fn wrap(inner: ProxyInner) -> Proxy<I> {
Proxy { _i: ::std::marker::PhantomData, inner }
}
/// Send a request creating an object through this object
///
/// **Warning:** This method is mostly intended to be used by code generated
/// by `wayland-scanner`, and you should probably never need to use it directly,
/// but rather use the appropriate methods on the Rust object.
///
/// This is the generic method to send requests.
pub fn send<J>(&self, msg: I::Request, version: Option<u32>) -> Option<Main<J>>
where
J: Interface + AsRef<Proxy<J>> + From<Proxy<J>>,
{
if msg.since() > self.version() && self.version() > 0 {
let opcode = msg.opcode() as usize;
panic!(
"Cannot send request {} which requires version >= {} on proxy {}@{} which is version {}.",
I::Request::MESSAGES[opcode].name,
msg.since(),
I::NAME,
self.id(),
self.version()
);
}
self.inner.send::<I, J>(msg, version).map(Main::wrap)
}
/// Check if the object associated with this proxy is still alive
///
/// Will return `false` if the object has been destroyed.
///
/// If the object is not managed by this library (if it was created from a raw
/// pointer from some other library your program interfaces with), this will always
/// returns `true`.
pub fn is_alive(&self) -> bool {
self.inner.is_alive()
}
/// Retrieve the interface version of this wayland object instance
///
/// Returns 0 on dead objects
pub fn version(&self) -> u32 {
self.inner.version()
}
/// Retrieve the object id of this wayland object
pub fn id(&self) -> u32 {
self.inner.id()
}
/// Access the UserData associated to this object
///
/// Each wayland object has an associated UserData, that can store
/// a payload of arbitrary type and is shared by all proxies of this
/// object.
///
/// See [`UserData`](struct.UserData.html) documentation for more details.
pub fn user_data(&self) -> &UserData {
self.inner.user_data()
}
/// Check if the other proxy refers to the same underlying wayland object
///
/// You can also use the `PartialEq` implementation.
pub fn equals(&self, other: &Proxy<I>) -> bool {
self.inner.equals(&other.inner)
}
/// Attach this proxy to the event queue represented by this token
///
/// Once a proxy is attached, you can use it to send requests that
/// create new objects. These new objects will be handled by the
/// event queue represented by the provided token.
///
/// This does not impact the events received by this object, which
/// are still handled by their original event queue.
pub fn attach(&self, token: QueueToken) -> Attached<I> {
let mut other = self.clone();
other.inner.attach(&token.inner);
Attached { inner: other.into(), _s: std::marker::PhantomData }
}
/// Erase the actual type of this proxy
pub fn anonymize(self) -> Proxy<AnonymousObject> {
Proxy { _i: ::std::marker::PhantomData, inner: self.inner }
}
}
impl Proxy<AnonymousObject> {
/// Attempt to recover the typed variant of an anonymous proxy
pub fn deanonymize<I: Interface>(self) -> Result<Proxy<I>, Self> {
if self.inner.is_interface::<I>() {
Ok(Proxy { inner: self.inner, _i: ::std::marker::PhantomData })
} else {
Err(self)
}
}
}
impl<I: Interface + Debug> Debug for Attached<I> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:?}[ATTACHED]", self.inner)
}
}
/// A handle to a proxy that has been attached to an event queue
///
/// As opposed to `Proxy`, you can use it to send requests
/// that create new objects. The created objects will be handled
/// by the event queue this proxy has been attached to.
#[derive(PartialEq)]
pub struct Attached<I: Interface> {
// AttachedProxy is *not* send/sync
_s: ::std::marker::PhantomData<*mut ()>,
inner: I,
}
impl<I: Interface> Attached<I>
where
I: Into<Proxy<I>> + From<Proxy<I>> + AsRef<Proxy<I>>,
{
/// Create a non-attached handle from this one
pub fn detach(&self) -> I {
self.inner.as_ref().clone().into()
}
}
impl<I: Interface> Deref for Attached<I> {
type Target = I;
fn deref(&self) -> &I {
&self.inner
}
}
impl<I: Interface> Clone for Attached<I>
where
I: AsRef<Proxy<I>> + From<Proxy<I>>,
{
fn clone(&self) -> Attached<I> {
let cloned = self.inner.as_ref().inner.clone();
Attached {
inner: Proxy { _i: std::marker::PhantomData, inner: cloned }.into(),
_s: std::marker::PhantomData,
}
}
}
/// A main handle to a proxy
///
/// This handle allows the same control as an `Attached` handle,
/// but additionnaly can be used to assign the proxy to a `Filter`,
/// in order to process its events.
#[derive(Clone, PartialEq)]
pub struct Main<I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>> {
inner: Attached<I>,
}
impl<I: Interface> Main<I>
where
I: AsRef<Proxy<I>> + From<Proxy<I>>,
{
pub(crate) fn wrap(inner: ProxyInner) -> Main<I> {
Main {
inner: Attached {
inner: Proxy { _i: std::marker::PhantomData, inner }.into(),
_s: std::marker::PhantomData,
},
}
}
/// Assign this object to given filter
///
/// All future event received by this object will be delivered to this
/// filter.
///
/// An object that is not assigned to any filter will see its events
/// delivered to the fallback callback of its event queue.
///
/// Event message type of the filter should verify
/// `E: From<(Main<I>, I::Event)>`. See the `event_enum!` macro provided
/// in this library to easily generate appropriate types.
pub fn assign<E>(&self, filter: Filter<E>)
where
I: Sync,
E: From<(Main<I>, I::Event)> + 'static,
I::Event: MessageGroup<Map = crate::ProxyMap>,
{
self.inner.inner.as_ref().inner.assign(filter);
}
/// Shorthand for assigning a closure to an object
///
/// Behaves similarly as `assign(..)`, but is a shorthand if
/// you want to assign this object to its own filter. In which
/// case you just need to provide the appropriate closure, of
/// type `FnMut(Main<I>, I::Event)`.
pub fn quick_assign<F>(&self, mut f: F)
where
I: Interface + AsRef<Proxy<I>> + From<Proxy<I>> + Sync,
F: FnMut(Main<I>, I::Event, crate::DispatchData) + 'static,
I::Event: MessageGroup<Map = crate::ProxyMap>,
{
self.assign(Filter::new(move |(proxy, event), _, data| f(proxy, event, data)))
}
}
impl Main<AnonymousObject> {
/// Attempt to recover the typed variant of an anonymous proxy
pub fn deanonymize<I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>>(
self,
) -> Result<Main<I>, Self> {
if self.inner.as_ref().inner.is_interface::<I>() {
Ok(Main {
inner: Attached {
inner: Proxy { _i: std::marker::PhantomData, inner: self.inner.inner.0.inner }
.into(),
_s: std::marker::PhantomData,
},
})
} else {
Err(self)
}
}
}
impl<I: Interface> Deref for Main<I>
where
I: AsRef<Proxy<I>> + From<Proxy<I>>,
{
type Target = Attached<I>;
fn deref(&self) -> &Attached<I> {
&self.inner
}
}
impl<I: Interface> From<Main<I>> for Attached<I>
where
I: AsRef<Proxy<I>> + From<Proxy<I>>,
{
fn from(main: Main<I>) -> Attached<I> {
main.inner
}
}
impl<I: Interface> Debug for Main<I>
where
I: Debug + AsRef<Proxy<I>> + From<Proxy<I>>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:?}[MAIN]", self.inner.inner)
}
}
/*
* C-interfacing stuff
*/
impl<I: Interface> Main<I>
where
I: AsRef<Proxy<I>> + From<Proxy<I>>,
{
/// Create a `Main` instance from a C pointer
///
/// Create a `Main` from a raw pointer to a wayland object from the
/// C library.
///
/// In order to handle protocol races, invoking it with a NULL pointer will
/// create an already-dead object.
///
/// NOTE: This method will panic if called while the `use_system_lib` feature is
/// not activated.
///
/// # Safety
///
/// This will take control of the underlying proxy & manage it. To be safe
/// you must ensure that:
///
/// - The provided proxy has not already been used in any way (it was just created)
/// - This is called from the same thread as the one hosting the event queue
/// handling this proxy
pub unsafe fn from_c_ptr(_ptr: *mut wl_proxy) -> Main<I> {
#[cfg(feature = "use_system_lib")]
{
Main::wrap(ProxyInner::init_from_c_ptr::<I>(_ptr))
}
#[cfg(not(feature = "use_system_lib"))]
{
panic!("[wayland-client] C interfacing methods can only be used with the `use_system_lib` cargo feature.")
}
}
}
impl<I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>> Proxy<I> {
/// Check whether this proxy is managed by the library or not
///
/// See `from_c_ptr` for details.
///
/// NOTE: This method will panic if called while the `use_system_lib` feature is
/// not activated.
pub fn is_external(&self) -> bool {
#[cfg(feature = "use_system_lib")]
{
self.inner.is_external()
}
#[cfg(not(feature = "use_system_lib"))]
{
panic!("[wayland-client] C interfacing methods can only be used with the `use_system_lib` cargo feature.")
}
}
/// Get a raw pointer to the underlying wayland object
///
/// Retrieve a pointer to the object from the `libwayland-client.so` library.
/// You will mostly need it to interface with C libraries needing access
/// to wayland objects (to initialize an opengl context for example).
///
/// NOTE: This method will panic if called while the `use_system_lib` feature is
/// not activated.
pub fn c_ptr(&self) -> *mut wl_proxy {
#[cfg(feature = "use_system_lib")]
{
self.inner.c_ptr()
}
#[cfg(not(feature = "use_system_lib"))]
{
panic!("[wayland-client] C interfacing methods can only be used with the `use_system_lib` cargo feature.")
}
}
/// Create a `Proxy` instance from a C pointer
///
/// Create a `Proxy` from a raw pointer to a wayland object from the
/// C library.
///
/// If the pointer was previously obtained by the `c_ptr()` method, this
/// constructs a new proxy for the same object just like the `clone()`
/// method would have.
///
/// If the object was created by some other C library you are interfacing
/// with, it will be created in an "unmanaged" state: wayland-client will
/// treat it as foreign, and as such most of the safeties will be absent.
/// Notably the lifetime of the object can't be tracked, so the `alive()`
/// method will always return `true` and you are responsible of not using
/// an object past its destruction (as this would cause a protocol error).
/// You will also be unable to associate any user data value to this object.
///
/// In order to handle protocol races, invoking it with a NULL pointer will
/// create an already-dead object.
///
/// NOTE: This method will panic if called while the `use_system_lib` feature is
/// not activated.
///
/// # Safety
///
/// The provided pointer must point to a valid wayland object from `libwayland-client`
/// with the correct interface.
pub unsafe fn from_c_ptr(_ptr: *mut wl_proxy) -> Proxy<I>
where
I: From<Proxy<I>>,
{
#[cfg(feature = "use_system_lib")]
{
Proxy { _i: ::std::marker::PhantomData, inner: ProxyInner::from_c_ptr::<I>(_ptr) }
}
#[cfg(not(feature = "use_system_lib"))]
{
panic!("[wayland-client] C interfacing methods can only be used with the `use_system_lib` cargo feature.")
}
}
}

View file

@ -0,0 +1,181 @@
use std::cell::RefCell;
use std::os::unix::io::{FromRawFd, RawFd};
use std::sync::{Arc, Mutex};
use nix::Result as NixResult;
use wayland_commons::map::{Object, ObjectMap, SERVER_ID_LIMIT};
use wayland_commons::socket::{BufferedSocket, Socket};
use wayland_commons::wire::{Argument, ArgumentType, Message, MessageParseError};
use super::proxy::ObjectMeta;
use super::queues::QueueBuffer;
use crate::ProtocolError;
#[derive(Clone, Debug)]
pub(crate) enum Error {
Protocol(ProtocolError),
Parse(MessageParseError),
Nix(::nix::Error),
}
pub(crate) struct Connection {
pub(crate) socket: BufferedSocket,
pub(crate) map: Arc<Mutex<ObjectMap<ObjectMeta>>>,
pub(crate) last_error: Arc<Mutex<Option<Error>>>,
pub(crate) display_buffer: QueueBuffer,
}
impl Connection {
pub(crate) unsafe fn new(fd: RawFd, display_object: Object<ObjectMeta>) -> Connection {
let socket = BufferedSocket::new(Socket::from_raw_fd(fd));
let mut map = ObjectMap::new();
// Insert first pre-existing object
let display_buffer = display_object.meta.buffer.clone();
map.insert_at(1, display_object).unwrap();
Connection {
socket,
map: Arc::new(Mutex::new(map)),
last_error: Arc::new(Mutex::new(None)),
display_buffer,
}
}
pub(crate) fn write_message(&mut self, msg: &Message) -> NixResult<()> {
self.socket.write_message(msg)
}
pub(crate) fn flush(&mut self) -> NixResult<()> {
self.socket.flush()
}
pub(crate) fn read_events(&mut self) -> Result<usize, Error> {
if let Some(ref err) = *self.last_error.lock().unwrap() {
return Err(err.clone());
}
// acquire the map lock, this means no objects can be created nor destroyed while we
// are reading events
let mut map = self.map.lock().unwrap();
// wrap it in a RefCell for cheap sharing in the two closures below
let map = RefCell::new(&mut *map);
let mut last_error = self.last_error.lock().unwrap();
// read messages
let ret = self.socket.read_messages(
|id, opcode| {
map.borrow()
.find(id)
.and_then(|o| o.events.get(opcode as usize))
.map(|desc| desc.signature)
},
|msg| {
// Early exit on protocol error
if msg.sender_id == 1 && msg.opcode == 0 {
if let [Argument::Object(faulty_id), Argument::Uint(error_code), Argument::Str(ref error_msg)] = &msg.args[..] {
let error_msg = error_msg.to_string_lossy().into_owned();
let faulty_interface = map.borrow().find(*faulty_id).map(|obj| obj.interface).unwrap_or("unknown");
// abort parsing, this is an unrecoverable error
*last_error = Some(Error::Protocol(ProtocolError {
code: *error_code,
object_id: *faulty_id,
object_interface: faulty_interface,
message: error_msg
}));
} else {
unreachable!();
}
return false;
}
// dispatch the message to the proper object
let mut map = map.borrow_mut();
let object = map.find(msg.sender_id);
// create a new object if applicable
if let Some((mut child, dead_parent)) = object
.as_ref()
.and_then(|o| o.event_child(msg.opcode).map(|c| (c, o.meta.client_destroyed)))
{
let new_id = msg
.args
.iter()
.flat_map(|a| if let Argument::NewId(nid) = *a { Some(nid) } else { None })
.next()
.unwrap();
let child_interface = child.interface;
// if this ID belonged to a now destroyed server object, we can replace it
if new_id >= SERVER_ID_LIMIT
&& map.with(new_id, |obj| obj.meta.client_destroyed).unwrap_or(false)
{
map.remove(new_id)
}
// if the parent object is already destroyed, the user will never see this
// object, so we set it as client_destroyed to ignore all future messages to it
if dead_parent {
child.meta.client_destroyed = true;
}
if let Err(()) = map.insert_at(new_id, child) {
// abort parsing, this is an unrecoverable error
*last_error = Some(Error::Protocol(ProtocolError {
code: 0,
object_id: 0,
object_interface: "",
message: format!(
"Protocol error: server tried to create \
an object \"{}\" with invalid id \"{}\".",
child_interface, new_id
),
}));
return false;
}
} else {
// debug assert: if this opcode does not define a child, then there should be no
// NewId argument
debug_assert!(!msg.args.iter().any(|a| a.get_type() == ArgumentType::NewId));
}
// send the message to the appropriate pending queue
match object {
Some(Object { meta: ObjectMeta { client_destroyed: true, .. }, .. }) | None => {
// this is a message sent to a destroyed object
// to avoid dying because of races, we just consume it into void
// closing any associated FDs
for a in msg.args {
if let Argument::Fd(fd) = a {
let _ = ::nix::unistd::close(fd);
}
}
}
Some(obj) => {
obj.meta.buffer.lock().unwrap().push_back(msg);
}
};
// continue parsing
true
},
);
if let Some(ref e) = *last_error {
// a protocol error was generated, don't lose it, it is the source of any subsequent error
return Err(e.clone());
}
match ret {
Ok(Ok(n)) => Ok(n),
Ok(Err(e)) => {
*last_error = Some(Error::Parse(e.clone()));
Err(Error::Parse(e))
}
// non-fatal error
Err(e @ nix::Error::EAGAIN) => Err(Error::Nix(e)),
// fatal errors
Err(e) => {
*last_error = Some(Error::Nix(e));
Err(Error::Nix(e))
}
}
}
}

View file

@ -0,0 +1,152 @@
use std::io;
use std::os::unix::io::{AsRawFd, RawFd};
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex};
use wayland_commons::debug;
use wayland_commons::map::{Object, ObjectMap};
use wayland_commons::wire::Message;
use wayland_commons::MessageGroup;
use crate::protocol::wl_display::{self, WlDisplay};
use crate::{ConnectError, ProtocolError, Proxy};
use super::connection::{Connection, Error as CxError};
use super::proxy::{ObjectMeta, ProxyInner};
use super::{Dispatched, EventQueueInner, ProxyMap, WAYLAND_DEBUG};
pub(crate) struct DisplayInner {
connection: Arc<Mutex<Connection>>,
proxy: Proxy<WlDisplay>,
}
impl DisplayInner {
pub unsafe fn from_fd(fd: RawFd) -> Result<Arc<DisplayInner>, ConnectError> {
if let Some(value) = std::env::var_os("WAYLAND_DEBUG") {
// Follow libwayland-client and enable debug log only on `1` and `client` values.
if value == "1" || value == "client" {
// Toggle debug log.
WAYLAND_DEBUG.store(true, Ordering::Relaxed);
}
}
// The special buffer for display events
let buffer = super::queues::create_queue_buffer();
let display_object = Object::from_interface::<WlDisplay>(1, ObjectMeta::new(buffer));
let (connection, map) = {
let c = Connection::new(fd, display_object);
let m = c.map.clone();
(Arc::new(Mutex::new(c)), m)
};
// Setup the display dispatcher
map.lock()
.unwrap()
.with(1, |obj| {
obj.meta.dispatcher = Arc::new(Mutex::new(DisplayDispatcher {
map: map.clone(),
last_error: connection.lock().unwrap().last_error.clone(),
}));
})
.unwrap();
let display_proxy = ProxyInner::from_id(1, map, connection.clone()).unwrap();
let display = DisplayInner { proxy: Proxy::wrap(display_proxy), connection };
Ok(Arc::new(display))
}
pub(crate) fn flush(&self) -> io::Result<()> {
match self.connection.lock().unwrap().flush() {
Ok(()) => Ok(()),
Err(errno) => Err(errno.into()),
}
}
pub(crate) fn create_event_queue(me: &Arc<DisplayInner>) -> EventQueueInner {
EventQueueInner::new(me.connection.clone(), None)
}
pub(crate) fn get_proxy(&self) -> &Proxy<WlDisplay> {
&self.proxy
}
pub(crate) fn protocol_error(&self) -> Option<ProtocolError> {
let cx = self.connection.lock().unwrap();
let last_error = cx.last_error.lock().unwrap();
if let Some(CxError::Protocol(ref e)) = *last_error {
Some(e.clone())
} else {
None
}
}
pub(crate) fn get_connection_fd(&self) -> ::std::os::unix::io::RawFd {
self.connection.lock().unwrap().socket.get_socket().as_raw_fd()
}
}
// WlDisplay needs its own dispatcher, as it can be dispatched from multiple threads
struct DisplayDispatcher {
map: Arc<Mutex<ObjectMap<ObjectMeta>>>,
last_error: Arc<Mutex<Option<CxError>>>,
}
impl super::Dispatcher for DisplayDispatcher {
fn dispatch(
&mut self,
msg: Message,
proxy: ProxyInner,
map: &mut ProxyMap,
_data: crate::DispatchData,
) -> Dispatched {
if WAYLAND_DEBUG.load(Ordering::Relaxed) {
debug::print_dispatched_message(
proxy.object.interface,
proxy.id,
proxy.object.events[msg.opcode as usize].name,
&msg.args,
);
}
let event = match wl_display::Event::from_raw(msg, map) {
Ok(v) => v,
Err(()) => return Dispatched::BadMsg,
};
match event {
wl_display::Event::Error { object_id, code, message } => {
eprintln!(
"[wayland-client] Protocol error {} on object {}@{}: {}",
code,
object_id.as_ref().inner.object.interface,
object_id.as_ref().id(),
message
);
*self.last_error.lock().unwrap() = Some(CxError::Protocol(ProtocolError {
code,
object_id: object_id.as_ref().id(),
object_interface: object_id.as_ref().inner.object.interface,
message,
}));
}
wl_display::Event::DeleteId { id } => {
// cleanup the map as appropriate
let mut map = self.map.lock().unwrap();
let client_destroyed = map
.with(id, |obj| {
obj.meta.server_destroyed = true;
obj.meta.client_destroyed
})
.unwrap_or(false);
if client_destroyed {
map.remove(id);
}
}
}
Dispatched::Yes
}
}

View file

@ -0,0 +1,213 @@
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use downcast::Downcast;
use wayland_commons::debug;
use wayland_commons::filter::Filter;
use wayland_commons::map::ObjectMap;
use wayland_commons::wire::Message;
use wayland_commons::{MessageGroup, ThreadGuard};
use crate::{Interface, Main, Proxy};
mod connection;
mod display;
mod proxy;
mod queues;
pub(crate) use self::display::DisplayInner;
pub(crate) use self::proxy::ProxyInner;
pub(crate) use self::queues::EventQueueInner;
/// Flag to toggle debug output.
static WAYLAND_DEBUG: AtomicBool = AtomicBool::new(false);
/// A handle to the object map internal to the library state.
///
/// This type is only used by code generated by `wayland-scanner`, and can not
/// be instantiated directly.
pub struct ProxyMap {
map: Arc<Mutex<ObjectMap<self::proxy::ObjectMeta>>>,
connection: Arc<Mutex<self::connection::Connection>>,
}
impl std::fmt::Debug for ProxyMap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("ProxyMap { ... }")
}
}
impl ProxyMap {
pub(crate) fn make(
map: Arc<Mutex<ObjectMap<self::proxy::ObjectMeta>>>,
connection: Arc<Mutex<self::connection::Connection>>,
) -> ProxyMap {
ProxyMap { map, connection }
}
/// Returns the Proxy corresponding to a given id
pub fn get<I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>>(
&mut self,
id: u32,
) -> Option<Proxy<I>> {
ProxyInner::from_id(id, self.map.clone(), self.connection.clone()).map(|object| {
debug_assert!(I::NAME == "<anonymous>" || object.is_interface::<I>());
Proxy::wrap(object)
})
}
/// Returns the Proxy corresponding to a given id, and create a dead one if none is found
pub fn get_or_dead<I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>>(
&mut self,
id: u32,
) -> Proxy<I> {
self.get(id).unwrap_or_else(|| {
Proxy::wrap(ProxyInner::dead::<I>(id, self.map.clone(), self.connection.clone()))
})
}
/// Creates a new proxy for given id
pub fn get_new<I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>>(
&mut self,
id: u32,
) -> Option<Main<I>> {
debug_assert!(self
.map
.lock()
.unwrap()
.find(id)
.map(|obj| obj.is_interface::<I>())
.unwrap_or(true));
ProxyInner::from_id(id, self.map.clone(), self.connection.clone()).map(Main::wrap)
}
}
/*
* Dispatching logic
*/
#[allow(clippy::large_enum_variant)]
pub(crate) enum Dispatched {
Yes,
NoDispatch(Message, ProxyInner),
BadMsg,
}
pub(crate) trait Dispatcher: Downcast + Send {
fn dispatch(
&mut self,
msg: Message,
proxy: ProxyInner,
map: &mut ProxyMap,
data: crate::DispatchData,
) -> Dispatched;
}
mod dispatcher_impl {
// this mod has the sole purpose of silencing these `dead_code` warnings...
#![allow(dead_code)]
use super::Dispatcher;
impl_downcast!(Dispatcher);
}
pub(crate) struct ImplDispatcher<
I: Interface + AsRef<Proxy<I>> + From<Proxy<I>>,
F: FnMut(I::Event, Main<I>, crate::DispatchData<'_>) + 'static,
> {
_i: ::std::marker::PhantomData<&'static I>,
implementation: F,
}
impl<I, F> Dispatcher for ImplDispatcher<I, F>
where
I: Interface + AsRef<Proxy<I>> + From<Proxy<I>> + Sync,
F: FnMut(I::Event, Main<I>, crate::DispatchData<'_>) + 'static + Send,
I::Event: MessageGroup<Map = ProxyMap>,
{
fn dispatch(
&mut self,
msg: Message,
proxy: ProxyInner,
map: &mut ProxyMap,
data: crate::DispatchData,
) -> Dispatched {
let opcode = msg.opcode as usize;
if WAYLAND_DEBUG.load(Ordering::Relaxed) {
debug::print_dispatched_message(
proxy.object.interface,
proxy.id,
proxy.object.events[opcode].name,
&msg.args,
);
}
let message = match I::Event::from_raw(msg, map) {
Ok(v) => v,
Err(()) => return Dispatched::BadMsg,
};
if message.since() > proxy.version() {
eprintln!(
"Received an event {} requiring version >= {} while proxy {}@{} is version {}.",
proxy.object.events[opcode].name,
message.since(),
proxy.object.interface,
proxy.id,
proxy.version()
);
return Dispatched::BadMsg;
}
if message.is_destructor() {
proxy.object.meta.alive.store(false, Ordering::Release);
{
// cleanup the map as appropriate
let mut map = proxy.map.lock().unwrap();
let server_destroyed = map
.with(proxy.id, |obj| {
obj.meta.client_destroyed = true;
obj.meta.server_destroyed
})
.unwrap_or(false);
if server_destroyed {
map.remove(proxy.id);
}
}
}
(self.implementation)(message, Main::<I>::wrap(proxy), data);
Dispatched::Yes
}
}
pub(crate) fn make_dispatcher<I, E>(filter: Filter<E>) -> Arc<Mutex<dyn Dispatcher + Send>>
where
I: Interface + AsRef<Proxy<I>> + From<Proxy<I>> + Sync,
E: From<(Main<I>, I::Event)> + 'static,
I::Event: MessageGroup<Map = ProxyMap>,
{
let guard = ThreadGuard::new(filter);
Arc::new(Mutex::new(ImplDispatcher {
_i: ::std::marker::PhantomData,
implementation: move |evt, proxy, data| guard.get().send((proxy, evt).into(), data),
}))
}
pub(crate) fn default_dispatcher() -> Arc<Mutex<dyn Dispatcher + Send>> {
struct DefaultDisp;
impl Dispatcher for DefaultDisp {
fn dispatch(
&mut self,
msg: Message,
proxy: ProxyInner,
_map: &mut ProxyMap,
_data: crate::DispatchData,
) -> Dispatched {
Dispatched::NoDispatch(msg, proxy)
}
}
Arc::new(Mutex::new(DefaultDisp))
}

View file

@ -0,0 +1,248 @@
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use wayland_commons::debug;
use wayland_commons::filter::Filter;
use wayland_commons::map::{Object, ObjectMap, ObjectMetadata};
use wayland_commons::user_data::UserData;
use wayland_commons::wire::{Argument, ArgumentType};
use wayland_commons::MessageGroup;
use super::connection::Connection;
use super::queues::QueueBuffer;
use super::{Dispatcher, EventQueueInner, WAYLAND_DEBUG};
use crate::{Interface, Main, Proxy};
#[derive(Clone)]
pub(crate) struct ObjectMeta {
pub(crate) buffer: QueueBuffer,
pub(crate) alive: Arc<AtomicBool>,
user_data: Arc<UserData>,
pub(crate) dispatcher: Arc<Mutex<dyn Dispatcher>>,
pub(crate) server_destroyed: bool,
pub(crate) client_destroyed: bool,
}
impl ObjectMetadata for ObjectMeta {
fn child(&self) -> ObjectMeta {
ObjectMeta {
buffer: self.buffer.clone(),
alive: Arc::new(AtomicBool::new(true)),
user_data: Arc::new(UserData::new()),
dispatcher: super::default_dispatcher(),
server_destroyed: false,
client_destroyed: false,
}
}
}
impl ObjectMeta {
pub(crate) fn new(buffer: QueueBuffer) -> ObjectMeta {
ObjectMeta {
buffer,
alive: Arc::new(AtomicBool::new(true)),
user_data: Arc::new(UserData::new()),
dispatcher: super::default_dispatcher(),
server_destroyed: false,
client_destroyed: false,
}
}
fn dead() -> ObjectMeta {
ObjectMeta {
buffer: super::queues::create_queue_buffer(),
alive: Arc::new(AtomicBool::new(false)),
user_data: Arc::new(UserData::new()),
dispatcher: super::default_dispatcher(),
server_destroyed: true,
client_destroyed: true,
}
}
}
#[derive(Clone)]
pub(crate) struct ProxyInner {
pub(crate) map: Arc<Mutex<ObjectMap<ObjectMeta>>>,
pub(crate) connection: Arc<Mutex<Connection>>,
pub(crate) object: Object<ObjectMeta>,
pub(crate) id: u32,
pub(crate) queue: Option<QueueBuffer>,
}
impl ProxyInner {
pub(crate) fn from_id(
id: u32,
map: Arc<Mutex<ObjectMap<ObjectMeta>>>,
connection: Arc<Mutex<Connection>>,
) -> Option<ProxyInner> {
let me = map.lock().unwrap().find(id);
me.map(|obj| ProxyInner {
map,
connection,
id,
queue: Some(obj.meta.buffer.clone()),
object: obj,
})
}
pub(crate) fn dead<I: Interface>(
id: u32,
map: Arc<Mutex<ObjectMap<ObjectMeta>>>,
connection: Arc<Mutex<Connection>>,
) -> ProxyInner {
ProxyInner {
map,
connection,
id,
queue: None,
object: Object::from_interface::<I>(1, ObjectMeta::dead()),
}
}
pub(crate) fn is_interface<I: Interface>(&self) -> bool {
self.object.is_interface::<I>()
}
pub(crate) fn is_alive(&self) -> bool {
self.object.meta.alive.load(Ordering::Acquire)
}
pub fn version(&self) -> u32 {
self.object.version
}
pub(crate) fn id(&self) -> u32 {
if self.is_alive() {
self.id
} else {
0
}
}
pub(crate) fn user_data(&self) -> &UserData {
&*self.object.meta.user_data
}
pub(crate) fn detach(&mut self) {
self.queue = None;
}
pub(crate) fn attach(&mut self, queue: &EventQueueInner) {
self.queue = Some(queue.buffer.clone())
}
pub(crate) fn send<I, J>(&self, msg: I::Request, version: Option<u32>) -> Option<ProxyInner>
where
I: Interface,
J: Interface,
{
// grab the connection lock before anything else
// this avoids the risk or races during object creation
let mut conn_lock = self.connection.lock().unwrap();
let destructor = msg.is_destructor();
let mut msg = msg.into_raw(self.id);
let opcode = msg.opcode;
// figure out if the call creates an object
let nid_idx = I::Request::MESSAGES[opcode as usize]
.signature
.iter()
.position(|&t| t == ArgumentType::NewId);
let alive = self.is_alive();
let ret = if let Some(mut nid_idx) = nid_idx {
let target_queue = self
.queue
.clone()
.expect("Attemping to create an object from a non-attached proxy.");
if let Some(o) = I::Request::child(opcode, 1, &()) {
if !o.is_interface::<J>() {
panic!(
"Trying to use 'send_constructor' with the wrong return type. \
Required interface {} but the message creates interface {}",
J::NAME,
o.interface
)
}
} else {
// There is no target interface in the protocol, this is a generic object-creating
// function (likely wl_registry.bind), the newid arg will thus expand to
// (str, u32, obj).
nid_idx += 2;
}
// insert the newly created object in the message
let new_object = Object::from_interface::<J>(
version.unwrap_or(self.object.version),
if alive { ObjectMeta::new(target_queue.clone()) } else { ObjectMeta::dead() },
);
let mut new_id = 0;
if alive {
new_id = self.map.lock().unwrap().client_insert_new(new_object.clone());
msg.args[nid_idx] = Argument::NewId(new_id);
}
Some(ProxyInner {
map: self.map.clone(),
connection: self.connection.clone(),
id: new_id,
object: new_object,
queue: Some(target_queue),
})
} else {
None
};
if WAYLAND_DEBUG.load(Ordering::Relaxed) {
debug::print_send_message(
I::NAME,
self.id,
alive,
self.object.requests[msg.opcode as usize].name,
&msg.args,
);
}
// Only actually send the message (& process destructor) if the object is alive.
if !alive {
return ret;
}
conn_lock.write_message(&msg).expect("Sending a message failed.");
if destructor {
self.object.meta.alive.store(false, Ordering::Release);
// Cleanup the map as appropriate.
let mut map = conn_lock.map.lock().unwrap();
let server_destroyed = map
.with(self.id, |obj| {
obj.meta.client_destroyed = true;
obj.meta.server_destroyed
})
.unwrap_or(false);
if server_destroyed {
map.remove(self.id);
}
}
ret
}
pub(crate) fn equals(&self, other: &ProxyInner) -> bool {
self.is_alive() && Arc::ptr_eq(&self.object.meta.alive, &other.object.meta.alive)
}
pub fn assign<I, E>(&self, filter: Filter<E>)
where
I: Interface + AsRef<Proxy<I>> + From<Proxy<I>> + Sync,
E: From<(Main<I>, I::Event)> + 'static,
I::Event: MessageGroup<Map = super::ProxyMap>,
{
// ignore failure if target object is dead
let _ = self.map.lock().unwrap().with(self.id, |obj| {
obj.meta.dispatcher = super::make_dispatcher(filter);
});
}
}

View file

@ -0,0 +1,300 @@
use std::cell::Cell;
use std::collections::VecDeque;
use std::io;
use std::os::unix::io::AsRawFd;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use nix::poll::{poll, PollFd, PollFlags};
use wayland_commons::map::ObjectMap;
use wayland_commons::wire::{Argument, Message};
use super::connection::{Connection, Error as CError};
use super::proxy::{ObjectMeta, ProxyInner};
use super::Dispatched;
use crate::{AnonymousObject, DispatchData, Filter, Main, RawEvent};
pub(crate) type QueueBuffer = Arc<Mutex<VecDeque<Message>>>;
pub(crate) fn create_queue_buffer() -> QueueBuffer {
Arc::new(Mutex::new(VecDeque::new()))
}
pub(crate) struct EventQueueInner {
pub(crate) connection: Arc<Mutex<Connection>>,
pub(crate) map: Arc<Mutex<ObjectMap<ObjectMeta>>>,
pub(crate) buffer: QueueBuffer,
display_buffer: QueueBuffer,
}
impl EventQueueInner {
pub(crate) fn new(
connection: Arc<Mutex<Connection>>,
buffer: Option<QueueBuffer>,
) -> EventQueueInner {
let (map, display_buffer) = {
let cx = connection.lock().unwrap();
(cx.map.clone(), cx.display_buffer.clone())
};
EventQueueInner {
connection,
map,
buffer: buffer.unwrap_or_else(create_queue_buffer),
display_buffer,
}
}
pub(crate) fn dispatch<F>(&self, mut data: DispatchData, mut fallback: F) -> io::Result<u32>
where
F: FnMut(RawEvent, Main<AnonymousObject>, DispatchData<'_>),
{
// don't read events if there are some pending
if let Err(()) = self.prepare_read() {
return self.dispatch_pending(data.reborrow(), &mut fallback);
}
// temporarily retrieve the socket Fd, only using it for POLL-ing!
let socket_fd;
{
// Flush the outgoing socket
let mut conn_lock = self.connection.lock().unwrap();
socket_fd = conn_lock.socket.get_socket().as_raw_fd();
loop {
match conn_lock.flush() {
Ok(_) => break,
Err(nix::Error::EAGAIN) => {
// EAGAIN, we need to wait before writing, so we poll the socket
let poll_ret = poll(&mut [PollFd::new(socket_fd, PollFlags::POLLOUT)], -1);
match poll_ret {
Ok(_) => continue,
Err(e) => {
self.cancel_read();
return Err(e.into());
}
}
}
Err(e) => {
if e != nix::Error::EPIPE {
// don't abort on EPIPE, so we can continue reading
// to get the protocol error
self.cancel_read();
return Err(e.into());
}
}
}
}
}
// wait for incoming messages to arrive
match poll(&mut [PollFd::new(socket_fd, PollFlags::POLLIN)], -1) {
Ok(_) => (),
Err(e) => {
self.cancel_read();
return Err(e.into());
}
}
let read_ret = self.read_events();
// even if read_events returned an error, it may have queued messages the need dispatching
// so we dispatch them
let dispatch_ret = self.dispatch_pending(data.reborrow(), &mut fallback);
match read_ret {
Ok(()) => (),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
// we waited for read readiness be then received a WouldBlock error
// this means that an other thread was also reading events and read them
// under our nose
// this is alright, continue
}
Err(e) => return Err(e),
}
dispatch_ret
}
fn dispatch_buffer<F>(
&self,
buffer: &Mutex<VecDeque<Message>>,
mut data: DispatchData,
mut fallback: F,
) -> io::Result<u32>
where
F: FnMut(RawEvent, Main<AnonymousObject>, DispatchData<'_>),
{
let mut count = 0;
let mut proxymap = super::ProxyMap::make(self.map.clone(), self.connection.clone());
loop {
let msg = { buffer.lock().unwrap().pop_front() };
let msg = match msg {
Some(m) => m,
None => break,
};
let id = msg.sender_id;
if let Some(proxy) = ProxyInner::from_id(id, self.map.clone(), self.connection.clone())
{
let object = proxy.object.clone();
if object.meta.client_destroyed {
// This is a potential race, if we reach here it means that the proxy was
// destroyed by the user between this message was queued and now. To handle it
// correctly, we must close any FDs it contains, mark any child object as
// destroyed (but the server will never know about it, so the ids will be
// leaked) and discard the event.
for arg in msg.args {
match arg {
Argument::Fd(fd) => {
let _ = ::nix::unistd::close(fd);
}
Argument::NewId(id) => {
let mut map = self.map.lock().unwrap();
map.with(id, |obj| {
obj.meta.client_destroyed = true;
})
.unwrap();
}
_ => {}
}
}
continue;
}
let mut dispatcher = object.meta.dispatcher.lock().unwrap();
match dispatcher.dispatch(msg, proxy, &mut proxymap, data.reborrow()) {
Dispatched::Yes => {
count += 1;
}
Dispatched::NoDispatch(msg, proxy) => {
let raw_event = message_to_rawevent(msg, &proxy, &mut proxymap);
fallback(raw_event, Main::wrap(proxy), data.reborrow());
count += 1;
}
Dispatched::BadMsg => {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("Dispatch for object {}@{} errored.", object.interface, id),
))
}
}
} else {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("Received an event for unknown object {}.", id),
));
}
}
Ok(count)
}
pub(crate) fn dispatch_pending<F>(&self, mut data: DispatchData, fallback: F) -> io::Result<u32>
where
F: FnMut(RawEvent, Main<AnonymousObject>, DispatchData<'_>),
{
// First always dispatch the display buffer
let display_dispatched =
self.dispatch_buffer(&self.display_buffer, data.reborrow(), |_, _, _| unreachable!())?;
// Then our actual buffer
let self_dispatched = self.dispatch_buffer(&self.buffer, data.reborrow(), fallback)?;
Ok(display_dispatched + self_dispatched)
}
pub(crate) fn sync_roundtrip<F>(
&self,
mut data: DispatchData,
mut fallback: F,
) -> io::Result<u32>
where
F: FnMut(RawEvent, Main<AnonymousObject>, DispatchData<'_>),
{
use crate::protocol::wl_callback::{Event as CbEvent, WlCallback};
use crate::protocol::wl_display::{Request as DRequest, WlDisplay};
// first retrieve the display and make a wrapper for it in this event queue
let mut display =
ProxyInner::from_id(1, self.map.clone(), self.connection.clone()).unwrap();
display.attach(self);
let done = Rc::new(Cell::new(false));
let cb = display.send::<WlDisplay, WlCallback>(DRequest::Sync {}, Some(1)).unwrap();
let done2 = done.clone();
cb.assign::<WlCallback, _>(Filter::new(move |(_, CbEvent::Done { .. }), _, _| {
done2.set(true);
}));
let mut dispatched = 0;
loop {
dispatched += self.dispatch(data.reborrow(), &mut fallback)?;
if done.get() {
return Ok(dispatched);
}
}
}
pub(crate) fn prepare_read(&self) -> Result<(), ()> {
if !self.buffer.lock().unwrap().is_empty() {
return Err(());
}
// TODO: un-mock
Ok(())
}
pub(crate) fn read_events(&self) -> io::Result<()> {
// TODO: integrate more properly with prepare read with a fence
match self.connection.lock().unwrap().read_events() {
Ok(_) => Ok(()),
Err(CError::Protocol(e)) => {
eprintln!("[wayland-client] Protocol error while reading events: {}", e);
Err(::nix::errno::Errno::EPROTO.into())
}
Err(CError::Parse(e)) => {
eprintln!("[wayland-client] Parse error while reading events: {}", e);
Err(::nix::errno::Errno::EPROTO.into())
}
Err(CError::Nix(errno)) => Err(errno.into()),
}
}
pub(crate) fn cancel_read(&self) {
// TODO: un-mock
}
}
fn message_to_rawevent(msg: Message, proxy: &ProxyInner, map: &mut super::ProxyMap) -> RawEvent {
let Message { opcode, args, .. } = msg;
let args = args
.into_iter()
.map(|a| match a {
Argument::Int(i) => crate::Argument::Int(i),
Argument::Uint(u) => crate::Argument::Uint(u),
Argument::Array(v) => {
crate::Argument::Array(if v.is_empty() { None } else { Some(*v) })
}
Argument::Fixed(f) => crate::Argument::Float((f as f32) / 256.),
Argument::Fd(f) => crate::Argument::Fd(f),
Argument::Str(cs) => crate::Argument::Str({
let bytes = cs.into_bytes();
if bytes.is_empty() {
None
} else {
Some(
String::from_utf8(bytes)
.unwrap_or_else(|e| String::from_utf8_lossy(&e.into_bytes()).into()),
)
}
}),
Argument::Object(id) => crate::Argument::Object(map.get(id)),
Argument::NewId(id) => crate::Argument::NewId(map.get_new(id)),
})
.collect();
RawEvent {
interface: proxy.object.interface,
opcode,
name: proxy.object.events[opcode as usize].name,
args,
}
}

File diff suppressed because it is too large Load diff