diff --git a/.rustfmt.toml b/.rustfmt.toml index 0409b3d98c1..284d219685e 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,10 +1 @@ -error_on_line_overflow = false -fn_args_density = "Compressed" -fn_args_layout = "Visual" -fn_arg_intent = "Tabbed" -reorder_imports = true -reorder_imported_names = true -report_todo = "Never" -report_fixme = "Never" -use_try_shorthand = true max_width = 110 diff --git a/.travis.yml b/.travis.yml index 6569bc59a3f..10d4cef8ff0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,13 +4,7 @@ cache: cargo dist: trusty -addons: - apt: - sources: - - sourceline: 'ppa:wayland.admin/daily-builds' - packages: - - libssl-dev - - libwayland-dev +sudo: required rust: - 1.20.0 @@ -21,6 +15,13 @@ rust: matrix: allow_failures: - rust: nightly + include: + - rust: stable + env: BUILD_FMT=1 + - rust: stable + env: TARPAULIN=1 + - rust: stable + env: BUILD_DOC=1 branches: only: @@ -31,29 +32,40 @@ before_script: - mkdir $(pwd)/socket - export XDG_RUNTIME_DIR="$(pwd)/socket" - | - if [ $TRAVIS_RUST_VERSION = "nightly" ]; then - cargo install rustfmt-nightly --force + if [ -n "$BUILD_FMT" ]; then + rustup component add rustfmt-preview + elif [ -n "$TARPAULIN" ]; then + bash <(curl https://raw.githubusercontent.com/xd009642/tarpaulin/master/travis-install.sh) + elif [ -n "$BUILD_DOC" ]; then + echo "Building doc, nothing to install..." + else + # Building & running tests, we need to install the wayland lib + ./travis_install_wayland.sh "1.12.0" fi - - which cargo-tarpaulin || cargo install cargo-tarpaulin - - which cargo-install-update || cargo install cargo-update - - cargo install-update -a os: - linux script: - | - if [ $TRAVIS_RUST_VERSION = "nightly" ]; then + if [ -n "$BUILD_FMT" ]; then cargo fmt --all -- --write-mode=diff + elif [ -n "$TARPAULIN" ]; then + cargo tarpaulin --ignore-tests --out Xml + elif [ -n "$BUILD_DOC" ]; then + cargo doc --all --no-deps --all-features + else + env LD_LIBRARY_PATH=~/install/lib:$LD_LIBRARY_PATH cargo test --all --all-features fi - - cargo build --all --all-features - - cargo test --all --all-features - - cargo tarpaulin --ignore-tests --ciserver travis-ci --coveralls $TRAVIS_JOB_ID - - cargo doc --all --no-deps --all-features after_success: - - cp ./doc_index.html ./target/doc/index.html - - cp ./rust.css ./target/doc/rust.css + - | + if [ -n "$TARPAULIN" ]; then + bash <(curl -s https://codecov.io/bash) + elif [ -n "$BUILD_DOC" ]; then + cp ./doc_index.html ./target/doc/index.html + cp ./rust.css ./target/doc/rust.css + fi deploy: provider: pages @@ -63,6 +75,7 @@ deploy: on: branch: master rust: stable + env: BUILD_DOC=1 notifications: webhooks: diff --git a/CHANGELOG.md b/CHANGELOG.md index 393efc0ec38..923a825d8e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Change Log -## Unreleased +## 0.20.0 - Unreleased + +- **Breaking** Complete rework of the libraries, basically everything is changed. ## 0.14.1 - 2018-03-09 diff --git a/Cargo.toml b/Cargo.toml index ad0708c844b..e25a49c7506 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,12 +5,12 @@ publish = false [dependencies] wayland-scanner = { path = "./wayland-scanner" } -wayland-client = { path = "./wayland-client", default-features = false, features = ["cursor"] } -wayland-server = { path = "./wayland-server" } +wayland-client = { path = "./wayland-client", default-features = false, features = ["cursor", "dlopen"] } +wayland-server = { path = "./wayland-server", features = ["dlopen"] } wayland-protocols = { path = "./wayland-protocols", features = ["client", "server", "unstable_protocols"] } [dev-dependencies] difference = "1.0" [workspace] -members = [ "wayland-sys", "wayland-scanner", "wayland-client", "wayland-server", "wayland-protocols" ] +members = [ "wayland-sys", "wayland-scanner", "wayland-client", "wayland-server", "wayland-protocols", "wayland-commons" ] diff --git a/README.md b/README.md index 40b6cd7cc1e..4c02c647214 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -[![](http://meritbadge.herokuapp.com/wayland-client)](https://crates.io/crates/wayland-client) +[![crates.io](http://meritbadge.herokuapp.com/wayland-client)](https://crates.io/crates/wayland-client) [![Build Status](https://travis-ci.org/Smithay/wayland-rs.svg?branch=master)](https://travis-ci.org/Smithay/wayland-rs) -[![Coverage Status](https://coveralls.io/repos/github/Smithay/wayland-rs/badge.svg)](https://coveralls.io/github/Smithay/wayland-rs) +[![codecov](https://codecov.io/gh/Smithay/wayland-rs/branch/master/graph/badge.svg)](https://codecov.io/gh/Smithay/wayland-rs) # Wayland client @@ -32,4 +32,5 @@ The documentation for the releases can be found on [docs.rs](https://docs.rs/): ## Requirements -Requires at least rust 1.20 to be used (using bitflags 1.0 for associated constants). +Requires at least rust 1.20 to be used (using bitflags 1.0 for associated constants), and version 1.12 of the +wayland system libraries. diff --git a/tests/attach_null_to_surface.rs b/tests/attach_null_to_surface.rs index 18063b50983..92e977158a4 100644 --- a/tests/attach_null_to_surface.rs +++ b/tests/attach_null_to_surface.rs @@ -1,102 +1,81 @@ -#[macro_use] -extern crate wayland_client as wayc; -extern crate wayland_server as ways; +use std::sync::{Arc, Mutex}; mod helpers; -use helpers::{roundtrip, TestClient, TestServer}; +use helpers::{roundtrip, wayc, ways, TestClient, TestServer}; -mod server_utils { - use ways::{EventLoop, StateToken}; - use ways::protocol::{wl_compositor, wl_surface}; - - pub struct CompositorState { - pub got_buffer: bool, - } +use wayc::protocol::wl_display::RequestsTrait as DisplayRequests; +use wayc::protocol::wl_compositor::RequestsTrait as CompositorRequests; +use wayc::protocol::wl_surface::RequestsTrait as SurfaceRequests; - impl CompositorState { - fn new() -> CompositorState { - CompositorState { got_buffer: false } - } - } - - fn compositor_impl() -> wl_compositor::Implementation> { - wl_compositor::Implementation { - create_surface: |evlh, token, _client, _compositor, surface| { - evlh.register(&surface, surface_impl(), token.clone(), None); - }, - create_region: |_, _, _, _, _| {}, - } - } +fn insert_compositor(server: &mut TestServer) -> Arc> { + use ways::protocol::{wl_compositor, wl_surface}; + use ways::NewResource; - fn surface_impl() -> wl_surface::Implementation> { - wl_surface::Implementation { - attach: |evlh, token, _client, _surface, buffer, _, _| { - assert!(buffer.is_none()); - evlh.state().get_mut(token).got_buffer = true; - }, - commit: |_, _, _, _| {}, - damage: |_, _, _, _, _, _, _, _| {}, - damage_buffer: |_, _, _, _, _, _, _, _| {}, - destroy: |_, _, _, _| {}, - frame: |_, _, _, _, _| {}, - set_buffer_scale: |_, _, _, _, _| {}, - set_buffer_transform: |_, _, _, _, _| {}, - set_input_region: |_, _, _, _, _| {}, - set_opaque_region: |_, _, _, _, _| {}, - } - } + let seen_surface = Arc::new(Mutex::new(false)); + let seen_surface2 = seen_surface.clone(); - pub fn insert_compositor(event_loop: &mut EventLoop) -> StateToken { - let token = event_loop.state().insert(CompositorState::new()); - let _ = event_loop.register_global::( + let loop_token = server.event_loop.token(); + server + .display + .create_global::( + &loop_token, 1, - |evlh, token, _client, compositor| { - evlh.register(&compositor, compositor_impl(), token.clone(), None); + move |version, compositor: NewResource<_>| { + assert!(version == 1); + let compositor_seen_surface = seen_surface.clone(); + compositor.implement( + move |event, _| { + if let wl_compositor::Request::CreateSurface { id: surface } = event { + let my_seen_surface = compositor_seen_surface.clone(); + surface.implement( + move |event, _| { + if let wl_surface::Request::Attach { buffer, x, y } = event { + assert!(buffer.is_none()); + assert!(x == 0); + assert!(y == 0); + *(my_seen_surface.lock().unwrap()) = true; + } else { + panic!("Unexpected event on surface!"); + } + }, + None::, + ); + } else { + panic!("Unexpected event on compositor!"); + } + }, + None::, + ); }, - token.clone(), ); - token - } -} -wayland_env!(pub ClientEnv, - compositor: ::wayc::protocol::wl_compositor::WlCompositor -); + seen_surface2 +} #[test] fn attach_null() { - // server setup + // Server setup + // let mut server = TestServer::new(); - let server_comp_token = self::server_utils::insert_compositor(&mut server.event_loop); + let seen_surface = insert_compositor(&mut server); - // client setup + // Client setup // let mut client = TestClient::new(&server.socket_name); - let client_registry = client.display.get_registry(); - let client_env_token = wayc::EnvHandler::::init(&mut client.event_queue, &client_registry); + let manager = wayc::GlobalManager::new(client.display.get_registry().unwrap()); - // double roundtrip for env init - // - roundtrip(&mut client, &mut server); + // Initial sync roundtrip(&mut client, &mut server); - // create a surface and attach it a null buffer - // - { - let state = client.event_queue.state(); - let env = state.get(&client_env_token); - let surface = env.compositor.create_surface(); - surface.attach(None, 0, 0); - } + let compositor = manager + .instanciate_exact::(1) + .unwrap() + .implement(|_, _| {}); + let surface = compositor.create_surface().unwrap().implement(|_, _| {}); + surface.attach(None, 0, 0); roundtrip(&mut client, &mut server); - // final assertions - // - { - let state = server.event_loop.state(); - let handler = state.get(&server_comp_token); - assert!(handler.got_buffer); - } + assert!(*seen_surface.lock().unwrap()); } diff --git a/tests/basic_global.rs b/tests/basic_global.rs index 72b0ce3dd0b..bc91d14b813 100644 --- a/tests/basic_global.rs +++ b/tests/basic_global.rs @@ -1,77 +1,52 @@ -#[macro_use(wayland_env)] -extern crate wayland_client as wayc; -extern crate wayland_server as ways; - mod helpers; -use helpers::{roundtrip, TestClient, TestServer}; - -mod server_utils { - use ways::EventLoop; - use ways::protocol::wl_compositor::WlCompositor; +use helpers::{roundtrip, wayc, ways, TestClient, TestServer}; - // max supported version: 4 - pub fn insert_compositor(event_loop: &mut EventLoop, v: i32) { - let _ = event_loop.register_global::(v, |_, _, _, _| {}, ()); - } -} - -wayland_env!(pub ClientEnv); +use ways::protocol::wl_compositor::WlCompositor as ServerCompositor; +use wayc::protocol::wl_display::RequestsTrait; #[test] fn simple_global() { - // server setup - // let mut server = TestServer::new(); - self::server_utils::insert_compositor(&mut server.event_loop, 1); + let loop_token = server.event_loop.token(); + server + .display + .create_global::(&loop_token, 1, |_, _| {}); - // client setup - // let mut client = TestClient::new(&server.socket_name); - let client_registry = client.display.get_registry(); - let client_env_token = wayc::EnvHandler::::init(&mut client.event_queue, &client_registry); + let manager = wayc::GlobalManager::new(client.display.get_registry().unwrap()); - // message passing - // roundtrip(&mut client, &mut server); - - // result assertions - // - let state = client.event_queue.state(); - let env = state.get(&client_env_token); - let globals = env.globals(); + let globals = manager.list(); assert!(globals.len() == 1); assert_eq!(globals[0], (1, "wl_compositor".into(), 1)); } #[test] fn multi_versions() { - // server setup - // let mut server = TestServer::new(); - self::server_utils::insert_compositor(&mut server.event_loop, 4); - self::server_utils::insert_compositor(&mut server.event_loop, 2); - self::server_utils::insert_compositor(&mut server.event_loop, 3); - self::server_utils::insert_compositor(&mut server.event_loop, 1); + let loop_token = server.event_loop.token(); + server + .display + .create_global::(&loop_token, 4, |_, _| {}); + server + .display + .create_global::(&loop_token, 3, |_, _| {}); + server + .display + .create_global::(&loop_token, 2, |_, _| {}); + server + .display + .create_global::(&loop_token, 1, |_, _| {}); - // client setup - // let mut client = TestClient::new(&server.socket_name); - let client_registry = client.display.get_registry(); - let client_env_token = wayc::EnvHandler::::init(&mut client.event_queue, &client_registry); + let manager = wayc::GlobalManager::new(client.display.get_registry().unwrap()); - // message passing - // roundtrip(&mut client, &mut server); - - // result assertions - // - let state = client.event_queue.state(); - let env = state.get(&client_env_token); - let globals = env.globals(); + let globals = manager.list(); assert!(globals.len() == 4); let mut seen = [false; 4]; - for &(_, ref interface, version) in globals { + for &(_, ref interface, version) in &globals { assert!(interface == "wl_compositor"); seen[version as usize - 1] = true; } diff --git a/tests/destructor_destroys.rs b/tests/destructor_destroys.rs deleted file mode 100644 index c9e706b8dd1..00000000000 --- a/tests/destructor_destroys.rs +++ /dev/null @@ -1,107 +0,0 @@ -#[macro_use(wayland_env)] -extern crate wayland_client as wayc; -extern crate wayland_server as ways; - -mod helpers; - -use helpers::{roundtrip, TestClient, TestServer}; - -mod server_utils { - use ways::EventLoop; - use ways::protocol::wl_compositor::WlCompositor; - - // max supported version: 4 - pub fn insert_compositor(event_loop: &mut EventLoop) { - let _ = event_loop.register_global::(4, |_, _, _, _| {}, ()); - } -} - -wayland_env!(pub ClientEnv, - compositor: wayc::protocol::wl_compositor::WlCompositor -); - -#[test] -fn destroy() { - use wayc::Proxy; - // Server setup - // - let mut server = TestServer::new(); - self::server_utils::insert_compositor(&mut server.event_loop); - - // Client setup - // - let mut client = TestClient::new(&server.socket_name); - let client_registry = client.display.get_registry(); - let client_env_token = wayc::EnvHandler::::init(&mut client.event_queue, &client_registry); - - // Some message passing - // - roundtrip(&mut client, &mut server); - - // Final asserts - // - let state = client.event_queue.state(); - let env = state.get(&client_env_token); - let surface = env.compositor.create_surface(); - assert!(surface.status() == wayc::Liveness::Alive); - let msg1 = surface.destroy(); - if let wayc::RequestResult::Destroyed = msg1 { - panic!("First message should succeed."); - } - assert!(surface.status() == wayc::Liveness::Dead); - let msg2 = surface.destroy(); - if let wayc::RequestResult::Sent(_) = msg2 { - panic!("First message should fail."); - } -} - -#[test] -fn destroy_implementation_data() { - use std::cell::Cell; - use std::rc::Rc; - // Server setup - // - let mut server = TestServer::new(); - self::server_utils::insert_compositor(&mut server.event_loop); - - // Client setup - // - let mut client = TestClient::new(&server.socket_name); - let client_registry = client.display.get_registry(); - let client_env_token = wayc::EnvHandler::::init(&mut client.event_queue, &client_registry); - - // Some message passing - // - roundtrip(&mut client, &mut server); - - // Utility - // - struct IData { - destroyed: Rc>, - } - - impl Drop for IData { - fn drop(&mut self) { - self.destroyed.set(true); - } - } - - // Final asserts - // - let surface = client - .event_queue - .state() - .with_value(&client_env_token, |_, env| env.compositor.create_surface()); - - let idata = IData { - destroyed: Rc::new(Cell::new(false)), - }; - let destroyed = idata.destroyed.clone(); - let implem = wayc::protocol::wl_surface::Implementation { - enter: |_, _, _, _| {}, - leave: |_, _, _, _| {}, - }; - client.event_queue.register(&surface, implem, idata); - surface.destroy(); - assert!(destroyed.get()); -} diff --git a/tests/dispatch_idle.rs b/tests/dispatch_idle.rs deleted file mode 100644 index 020b68076ce..00000000000 --- a/tests/dispatch_idle.rs +++ /dev/null @@ -1,24 +0,0 @@ -extern crate wayland_client as wayc; -extern crate wayland_server as ways; - -use ways::sources::EventSource; - -mod helpers; - -use helpers::TestServer; - -#[test] -fn skel() { - // Server setup - // - let mut server = TestServer::new(); - - let idle = server - .event_loop - .add_idle_event_source(|_, idata| *idata = true, false); - - server.event_loop.dispatch(Some(1)).unwrap(); - - let done = idle.remove(); - assert!(done); -} diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index 46d86f176ba..4d5ee0753dc 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -3,17 +3,20 @@ #![allow(dead_code)] +pub extern crate wayland_client as wayc; +pub extern crate wayland_server as ways; + use std::ffi::{OsStr, OsString}; pub struct TestServer { - pub display: ::ways::Display, - pub event_loop: ::ways::EventLoop, + pub display: self::ways::Display, + pub event_loop: self::ways::EventLoop, pub socket_name: OsString, } impl TestServer { pub fn new() -> TestServer { - let (mut display, event_loop) = ::ways::create_display(); + let (mut display, event_loop) = self::ways::Display::new(); let socket_name = display .add_socket_auto() .expect("Failed to create a server socket."); @@ -34,13 +37,14 @@ impl TestServer { } pub struct TestClient { - pub display: ::wayc::protocol::wl_display::WlDisplay, - pub event_queue: ::wayc::EventQueue, + pub display: self::wayc::Display, + pub event_queue: self::wayc::EventQueue, } impl TestClient { pub fn new(socket_name: &OsStr) -> TestClient { - let (display, event_queue) = ::wayc::connect_to(socket_name).expect("Failed to connect to server."); + let (display, event_queue) = + self::wayc::Display::connect_to_name(socket_name).expect("Failed to connect to server."); TestClient { display: display, event_queue: event_queue, diff --git a/tests/scanner.rs b/tests/scanner.rs index d79e2fbc8c1..10fb690ea10 100644 --- a/tests/scanner.rs +++ b/tests/scanner.rs @@ -1,7 +1,6 @@ extern crate difference; extern crate wayland_scanner; - use difference::{Changeset, Difference}; use std::io::Cursor; use std::str::from_utf8; @@ -9,29 +8,65 @@ use wayland_scanner::Side; const PROTOCOL: &'static str = include_str!("./scanner_assets/protocol.xml"); -const INTERFACES_TARGET: &'static str = include_str!("./scanner_assets/interfaces.rs"); +const C_INTERFACES_TARGET: &'static str = include_str!("./scanner_assets/c_interfaces.rs"); -const CLIENT_CODE_TARGET: &'static str = include_str!("./scanner_assets/client_code.rs"); +const CLIENT_C_CODE_TARGET: &'static str = include_str!("./scanner_assets/client_c_code.rs"); -const SERVER_CODE_TARGET: &'static str = include_str!("./scanner_assets/server_code.rs"); +const SERVER_C_CODE_TARGET: &'static str = include_str!("./scanner_assets/server_c_code.rs"); fn print_diff(diffs: &[Difference]) { - println!(""); - for d in diffs { - match *d { - Difference::Same(ref x) => for l in x.lines() { - println!(" {}", l); - }, - Difference::Add(ref x) => for l in x.lines() { - println!("\x1b[92m + {}\x1b[0m", l); - }, - Difference::Rem(ref x) => for l in x.lines() { - println!("\x1b[91m - {}\x1b[0m", l); - }, + println!("Partial diffs found:"); + let diffs = flatten_diffs(diffs); + let mut print_idx = diffs + .iter() + .enumerate() + .flat_map(|(i, d)| { + if let &Difference::Same(_) = d { + Vec::new().into_iter() + } else { + ((i - 3)..(i + 4)).collect::>().into_iter() + } + }) + .collect::>(); + print_idx.sort(); + print_idx.dedup(); + let mut last_idx = 0; + for idx in print_idx { + if idx != last_idx + 1 { + println!("\n=== Partial diff ==="); + } + last_idx = idx; + match diffs[idx] { + Difference::Same(ref l) => println!(" {}", l), + Difference::Add(ref l) => println!("\x1b[92m + {}\x1b[0m", l), + Difference::Rem(ref l) => println!("\x1b[91m - {}\x1b[0m", l), } } } +fn flatten_diffs(diffs: &[Difference]) -> Vec { + diffs + .iter() + .flat_map(|d| match *d { + Difference::Same(ref x) => x.lines() + .map(Into::::into) + .map(Difference::Same) + .collect::>() + .into_iter(), + Difference::Add(ref x) => x.lines() + .map(Into::::into) + .map(Difference::Add) + .collect::>() + .into_iter(), + Difference::Rem(ref x) => x.lines() + .map(Into::::into) + .map(Difference::Rem) + .collect::>() + .into_iter(), + }) + .collect() +} + fn only_newlines_err(diffs: &[Difference]) -> bool { for d in diffs { match *d { @@ -43,11 +78,11 @@ fn only_newlines_err(diffs: &[Difference]) -> bool { } #[test] -fn interfaces_generation() { +fn c_interfaces_generation() { let mut out = Vec::new(); - wayland_scanner::generate_interfaces_streams(Cursor::new(PROTOCOL.as_bytes()), &mut out); + wayland_scanner::generate_c_interfaces_streams(Cursor::new(PROTOCOL.as_bytes()), &mut out); let changeset = Changeset::new( - INTERFACES_TARGET, + C_INTERFACES_TARGET, from_utf8(&out).expect("Output of scanner was not UTF8."), "\n", ); @@ -61,11 +96,11 @@ fn interfaces_generation() { } #[test] -fn client_code_generation() { +fn client_c_code_generation() { let mut out = Vec::new(); - wayland_scanner::generate_code_streams(Cursor::new(PROTOCOL.as_bytes()), &mut out, Side::Client); + wayland_scanner::generate_c_code_streams(Cursor::new(PROTOCOL.as_bytes()), &mut out, Side::Client); let changeset = Changeset::new( - CLIENT_CODE_TARGET, + CLIENT_C_CODE_TARGET, from_utf8(&out).expect("Output of scanner was not UTF8."), "\n", ); @@ -79,11 +114,11 @@ fn client_code_generation() { } #[test] -fn server_code_generation() { +fn server_c_code_generation() { let mut out = Vec::new(); - wayland_scanner::generate_code_streams(Cursor::new(PROTOCOL.as_bytes()), &mut out, Side::Server); + wayland_scanner::generate_c_code_streams(Cursor::new(PROTOCOL.as_bytes()), &mut out, Side::Server); let changeset = Changeset::new( - SERVER_CODE_TARGET, + SERVER_C_CODE_TARGET, from_utf8(&out).expect("Output of scanner was not UTF8."), "\n", ); diff --git a/tests/scanner_assets/interfaces.rs b/tests/scanner_assets/c_interfaces.rs similarity index 100% rename from tests/scanner_assets/interfaces.rs rename to tests/scanner_assets/c_interfaces.rs diff --git a/tests/scanner_assets/client_c_code.rs b/tests/scanner_assets/client_c_code.rs new file mode 100644 index 00000000000..035e62519eb --- /dev/null +++ b/tests/scanner_assets/client_c_code.rs @@ -0,0 +1,582 @@ +// +// This file was auto-generated, do not edit directly. +// + +/* +This is an example copyright. + It contains several lines. + AS WELL AS ALL CAPS TEXT. +*/ + +pub mod wl_foo { + //! Interface for fooing + //! + //! This is the dedicated interface for doing foos over any + //! kind of other foos. + + use super::{Proxy, NewProxy, AnonymousObject, Interface, MessageGroup}; + use super::sys::common::{wl_argument, wl_interface, wl_array}; + use super::sys::client::*; + + /// Possible cake kinds + /// + /// List of the possible kind of cake supported by the protocol. + + #[repr(u32)] + #[derive(Copy,Clone,Debug,PartialEq)] + pub enum CakeKind { + /// mild cake without much flavor + Basic = 0, + /// spicy cake to burn your tongue + Spicy = 1, + /// fruity cake to get vitamins + Fruity = 2, + } + + impl CakeKind { + pub fn from_raw(n: u32) -> Option { + match n { + 0 => Some(CakeKind::Basic), + 1 => Some(CakeKind::Spicy), + 2 => Some(CakeKind::Fruity), + + _ => Option::None + } + } + pub fn to_raw(&self) -> u32 { + *self as u32 + } + } + + bitflags! { + /// possible delivery modes + /// + pub struct DeliveryKind: u32 { + /// pick your cake up yourself + const PickUp = 1; + /// flying drone delivery + const Drone = 2; + /// because we fear nothing + const Catapult = 4; + } + } + + impl DeliveryKind { + pub fn from_raw(n: u32) -> Option { + Some(DeliveryKind::from_bits_truncate(n)) + + } + pub fn to_raw(&self) -> u32 { + self.bits() + } + } + + pub enum Request { + /// do some foo + /// + /// This will do some foo with its args. + FooIt {number: i32, unumber: u32, text: String, float: f64, file: ::std::os::unix::io::RawFd, }, + /// create a bar + /// + /// Create a bar which will do its bar job. + CreateBar {id: Proxy, }, + } + + impl super::MessageGroup for Request { + fn is_destructor(&self) -> bool { + match *self { + _ => false + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + panic!("Request::from_raw_c can not be used Client-side.") + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + match self { + Request::FooIt { number, unumber, text, float, file, } => { + let mut _args_array: [wl_argument; 5] = unsafe { ::std::mem::zeroed() }; + _args_array[0].i = number; + _args_array[1].u = unumber; + let _arg_2 = ::std::ffi::CString::new(text).unwrap(); + _args_array[2].s = _arg_2.as_ptr(); + _args_array[3].f = (float * 256.) as i32; + _args_array[4].h = file; + f(0, &mut _args_array) + }, + Request::CreateBar { id, } => { + let mut _args_array: [wl_argument; 1] = unsafe { ::std::mem::zeroed() }; + _args_array[0].o = id.c_ptr() as *mut _; + f(1, &mut _args_array) + }, + } + } + } + + pub enum Event { + /// a cake is possible + /// + /// The server advertizes that a kind of cake is available + /// + /// Only available since version 2 of the interface + Cake {kind: CakeKind, amount: u32, }, + } + + impl super::MessageGroup for Event { + fn is_destructor(&self) -> bool { + match *self { + _ => false + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + match opcode { + 0 => { + let _args = ::std::slice::from_raw_parts(args, 2); + Ok(Event::Cake { + kind: CakeKind::from_raw(_args[0].u).ok_or(())?, + amount: _args[1].u, + }) }, + _ => return Err(()) + } + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + panic!("Event::as_raw_c_in can not be used Client-side.") + } + } + + + pub struct WlFoo; + + impl Interface for WlFoo { + type Request = Request; + type Event = Event; + const NAME: &'static str = "wl_foo"; + fn c_interface() -> *const wl_interface { + unsafe { &super::super::c_interfaces::wl_foo_interface } + } + } + + pub trait RequestsTrait { + /// do some foo + /// + /// This will do some foo with its args. + fn foo_it(&self, number: i32, unumber: u32, text: String, float: f64, file: ::std::os::unix::io::RawFd) ->(); + /// create a bar + /// + /// Create a bar which will do its bar job. + fn create_bar(&self) ->Result, ()>; + } + + impl RequestsTrait for Proxy { + fn foo_it(&self, number: i32, unumber: u32, text: String, float: f64, file: ::std::os::unix::io::RawFd) ->() { + if !self.is_external() && !self.is_alive() { + return; + } + let msg = Request::FooIt { + number: number, + unumber: unumber, + text: text, + float: float, + file: file, + }; + self.send(msg); + } + + fn create_bar(&self) ->Result, ()> { + if !self.is_external() && !self.is_alive() { + return Err(()); + } + let _arg_id_newproxy = self.child::(); + let msg = Request::CreateBar { + id: unsafe { Proxy::::from_c_ptr(_arg_id_newproxy.c_ptr()) }, + }; + self.send(msg); + Ok(_arg_id_newproxy) + } + } +} + +pub mod wl_bar { + //! Interface for bars + //! + //! This interface allows you to bar your foos. + + use super::{Proxy, NewProxy, AnonymousObject, Interface, MessageGroup}; + use super::sys::common::{wl_argument, wl_interface, wl_array}; + use super::sys::client::*; + + pub enum Request { + /// ask for a bar delivery + /// + /// Proceed to a bar delivery of given foo. + /// + /// Only available since version 2 of the interface + BarDelivery {kind: super::wl_foo::DeliveryKind, target: Proxy, metadata: Vec, }, + /// release this bar + /// + /// Notify the compositor that you have finished using this bar. + /// + /// This is a destructor, once sent this object cannot be used any longer. + Release, + } + + impl super::MessageGroup for Request { + fn is_destructor(&self) -> bool { + match *self { + Request::Release => true, + _ => false + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + panic!("Request::from_raw_c can not be used Client-side.") + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + match self { + Request::BarDelivery { kind, target, metadata, } => { + let mut _args_array: [wl_argument; 3] = unsafe { ::std::mem::zeroed() }; + _args_array[0].u = kind.to_raw(); + _args_array[1].o = target.c_ptr() as *mut _; + let _arg_2 = wl_array { size: metadata.len(), alloc: metadata.capacity(), data: metadata.as_ptr() as *mut _ }; + _args_array[2].a = &_arg_2; + f(0, &mut _args_array) + }, + Request::Release => { + let mut _args_array: [wl_argument; 0] = unsafe { ::std::mem::zeroed() }; + f(1, &mut _args_array) + }, + } + } + } + + pub enum Event { + } + + impl super::MessageGroup for Event { + fn is_destructor(&self) -> bool { + match *self { + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + match opcode { + _ => return Err(()) + } + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + panic!("Event::as_raw_c_in can not be used Client-side.") + } + } + + + pub struct WlBar; + + impl Interface for WlBar { + type Request = Request; + type Event = Event; + const NAME: &'static str = "wl_bar"; + fn c_interface() -> *const wl_interface { + unsafe { &super::super::c_interfaces::wl_bar_interface } + } + } + + pub trait RequestsTrait { + /// ask for a bar delivery + /// + /// Proceed to a bar delivery of given foo. + /// + /// Only available since version 2 of the interface + fn bar_delivery(&self, kind: super::wl_foo::DeliveryKind, target: &Proxy, metadata: Vec) ->(); + /// release this bar + /// + /// Notify the compositor that you have finished using this bar. + /// + /// This is a destructor, you cannot send requests to this object any longer once this method is called. + fn release(&self) ->(); + } + + impl RequestsTrait for Proxy { + fn bar_delivery(&self, kind: super::wl_foo::DeliveryKind, target: &Proxy, metadata: Vec) ->() { + if !self.is_external() && !self.is_alive() { + return; + } + + let msg = Request::BarDelivery { + kind: kind, + target: target.clone(), + metadata: metadata, + }; + self.send(msg); + } + + fn release(&self) ->() { + if !self.is_external() && !self.is_alive() { + return; + } + let msg = Request::Release; + self.send(msg); + } + } +} + +pub mod wl_display { + //! core global object + //! + //! This global is special and should only generate code client-side, not server-side. + + use super::{Proxy, NewProxy, AnonymousObject, Interface, MessageGroup}; + use super::sys::common::{wl_argument, wl_interface, wl_array}; + use super::sys::client::*; + + pub enum Request { + } + + impl super::MessageGroup for Request { + fn is_destructor(&self) -> bool { + match *self { + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + panic!("Request::from_raw_c can not be used Client-side.") + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + match self { + } + } + } + + pub enum Event { + } + + impl super::MessageGroup for Event { + fn is_destructor(&self) -> bool { + match *self { + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + match opcode { + _ => return Err(()) + } + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + panic!("Event::as_raw_c_in can not be used Client-side.") + } + } + + + pub struct WlDisplay; + + impl Interface for WlDisplay { + type Request = Request; + type Event = Event; + const NAME: &'static str = "wl_display"; + fn c_interface() -> *const wl_interface { + unsafe { &super::super::c_interfaces::wl_display_interface } + } + } + + pub trait RequestsTrait { + } + + impl RequestsTrait for Proxy { + } +} + +pub mod wl_registry { + //! global registry object + //! + //! This global is special and should only generate code client-side, not server-side. + + use super::{Proxy, NewProxy, AnonymousObject, Interface, MessageGroup}; + use super::sys::common::{wl_argument, wl_interface, wl_array}; + use super::sys::client::*; + + pub enum Request { + /// bind an object to the display + /// + /// This request is a special code-path, as its new-id argument as no target type. + Bind {name: u32, id: (String, u32, Proxy), }, + } + + impl super::MessageGroup for Request { + fn is_destructor(&self) -> bool { + match *self { + _ => false + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + panic!("Request::from_raw_c can not be used Client-side.") + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + match self { + Request::Bind { name, id, } => { + let mut _args_array: [wl_argument; 4] = unsafe { ::std::mem::zeroed() }; + _args_array[0].u = name; + let _arg_1_s = ::std::ffi::CString::new(id.0).unwrap(); + _args_array[1].s = _arg_1_s.as_ptr(); + _args_array[2].u = id.1; + _args_array[3].o = ::std::ptr::null_mut(); + f(0, &mut _args_array) + }, + } + } + } + + pub enum Event { + } + + impl super::MessageGroup for Event { + fn is_destructor(&self) -> bool { + match *self { + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + match opcode { + _ => return Err(()) + } + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + panic!("Event::as_raw_c_in can not be used Client-side.") + } + } + + + pub struct WlRegistry; + + impl Interface for WlRegistry { + type Request = Request; + type Event = Event; + const NAME: &'static str = "wl_registry"; + fn c_interface() -> *const wl_interface { + unsafe { &super::super::c_interfaces::wl_registry_interface } + } + } + + pub trait RequestsTrait { + /// bind an object to the display + /// + /// This request is a special code-path, as its new-id argument as no target type. + fn bind(&self, version: u32, name: u32) ->Result, ()>; + } + + impl RequestsTrait for Proxy { + fn bind(&self, version: u32, name: u32) ->Result, ()> { + if !self.is_external() && !self.is_alive() { + return Err(()); + } + let msg = Request::Bind { + name: name, + id: (T::NAME.into(), version, unsafe { Proxy::::new_null() }), + }; + + unsafe { + let ret = msg.as_raw_c_in(|opcode, args| { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_proxy_marshal_array_constructor_versioned, + self.c_ptr(), + opcode, + args.as_mut_ptr(), + T::c_interface(), + version + ) + }); + Ok(NewProxy::::from_c_ptr(ret)) + } + } + } +} + +pub mod wl_callback { + //! callback object + //! + //! This object has a special behavior regarding its destructor. + + use super::{Proxy, NewProxy, AnonymousObject, Interface, MessageGroup}; + use super::sys::common::{wl_argument, wl_interface, wl_array}; + use super::sys::client::*; + + pub enum Request { + } + + impl super::MessageGroup for Request { + fn is_destructor(&self) -> bool { + match *self { + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + panic!("Request::from_raw_c can not be used Client-side.") + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + match self { + } + } + } + + pub enum Event { + /// done event + /// + /// This event is actually a destructor, but the protocol XML has no wait of specifying it. + /// As such, the scanner should consider wl_callback.done as a special case. + /// + /// This is a destructor, once received this object cannot be used any longer. + Done {callback_data: u32, }, + } + + impl super::MessageGroup for Event { + fn is_destructor(&self) -> bool { + match *self { + Event::Done { .. } => true, + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + match opcode { + 0 => { + let _args = ::std::slice::from_raw_parts(args, 1); + Ok(Event::Done { + callback_data: _args[0].u, + }) }, + _ => return Err(()) + } + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + panic!("Event::as_raw_c_in can not be used Client-side.") + } + } + + + pub struct WlCallback; + + impl Interface for WlCallback { + type Request = Request; + type Event = Event; + const NAME: &'static str = "wl_callback"; + fn c_interface() -> *const wl_interface { + unsafe { &super::super::c_interfaces::wl_callback_interface } + } + } + + pub trait RequestsTrait { + } + + impl RequestsTrait for Proxy { + } +} + diff --git a/tests/scanner_assets/client_code.rs b/tests/scanner_assets/client_code.rs deleted file mode 100644 index 78671c8e32f..00000000000 --- a/tests/scanner_assets/client_code.rs +++ /dev/null @@ -1,739 +0,0 @@ -// -// This file was auto-generated, do not edit directly -// - -/* -This is an example copyright. - It contains several lines. - AS WELL AS ALL CAPS TEXT. -*/ - -pub mod wl_foo { - //! Interface for fooing - //! - //! This is the dedicated interface for doing foos over any - //! kind of other foos. - use super::EventQueueHandle; - use super::Proxy; - use super::RequestResult; - use super::{Liveness, Implementable}; - use super::interfaces::*; - use wayland_sys::common::*; - use std::any::Any; - use std::ffi::{CString,CStr}; - use std::os::raw::c_void; - use std::ptr; - use std::sync::Arc; - use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; - use wayland_sys::RUST_MANAGED; - use wayland_sys::client::*; - type UserData = (*mut EventQueueHandle, Option>, Arc<(AtomicBool, AtomicPtr<()>)>); - - pub struct WlFoo { - ptr: *mut wl_proxy, - data: Option)>> - } - - unsafe impl Send for WlFoo {} - unsafe impl Sync for WlFoo {} - - unsafe impl Proxy for WlFoo { - fn ptr(&self) -> *mut wl_proxy { self.ptr } - - unsafe fn from_ptr_new(ptr: *mut wl_proxy) -> WlFoo { - let data: *mut UserData = Box::into_raw(Box::new(( - ptr::null_mut(), - Option::None, - Arc::new((AtomicBool::new(true), AtomicPtr::new(ptr::null_mut()))), - ))); - ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_set_user_data, ptr, data as *mut c_void); - WlFoo { ptr: ptr, data: Some((&*data).2.clone()) } - } - unsafe fn from_ptr_initialized(ptr: *mut wl_proxy) -> WlFoo { - - let implem = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_listener, ptr); - let rust_managed = implem == &RUST_MANAGED as *const _ as *const _; - - - if rust_managed { - let data = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, ptr) as *mut UserData; - WlFoo { ptr: ptr, data: Some((&*data).2.clone()) } - } else { - WlFoo { ptr: ptr, data: Option::None } - } - } - - fn interface_ptr() -> *const wl_interface { unsafe { &wl_foo_interface } } - fn interface_name() -> &'static str { "wl_foo" } - fn supported_version() -> u32 { 3 } - fn version(&self) -> u32 { unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_version, self.ptr()) } } - - fn status(&self) -> Liveness { - if let Some(ref data) = self.data { - if data.0.load(Ordering::SeqCst) { - Liveness::Alive - } else { - Liveness::Dead - } - } else { - Liveness::Unmanaged - } - } - fn equals(&self, other: &WlFoo) -> bool { - self.status() != Liveness::Dead && other.status() != Liveness::Dead && self.ptr == other.ptr - } - - fn set_user_data(&self, ptr: *mut ()) { - if let Some(ref data) = self.data { - data.1.store(ptr, Ordering::SeqCst); - } - } - fn get_user_data(&self) -> *mut () { - if let Some(ref data) = self.data { - data.1.load(Ordering::SeqCst) - } else { - ::std::ptr::null_mut() - } - } - unsafe fn clone_unchecked(&self) -> WlFoo { - WlFoo { - ptr: self.ptr, - data: self.data.clone() - } - } - } - - unsafe impl Implementable for WlFoo { - type Implementation = Implementation; - #[allow(unused_mut,unused_assignments)] - unsafe fn __dispatch_msg(&self, opcode: u32, args: *const wl_argument) -> Result<(),()> { - - let data = &mut *(ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, self.ptr()) as *mut UserData); - let evq = &mut *(data.0); - let mut kill = false; - { - let &mut (ref implementation, ref mut idata) = data.1.as_mut().unwrap().downcast_mut::<(Implementation, ID)>().unwrap(); - match opcode { - 0 => { - let kind = {match CakeKind::from_raw(*(args.offset(0) as *const u32)) { Some(v) => v, Option::None => return Err(()) }}; - let amount = {*(args.offset(1) as *const u32)}; - (implementation.cake)(evq, idata, self, kind, amount); - }, - _ => return Err(()) - } - } - - if kill { - let _impl = data.1.take(); - ::std::mem::drop(_impl); - } - Ok(()) - } - } - - /// Possible cake kinds - /// - /// List of the possible kind of cake supported by the protocol. - #[repr(u32)] - #[derive(Copy,Clone,Debug,PartialEq)] - pub enum CakeKind { - /// mild cake without much flavor - Basic = 0, - /// spicy cake to burn your tongue - Spicy = 1, - /// fruity cake to get vitamins - Fruity = 2, - } - impl CakeKind { - pub fn from_raw(n: u32) -> Option { - match n { - 0 => Some(CakeKind::Basic), - 1 => Some(CakeKind::Spicy), - 2 => Some(CakeKind::Fruity), - _ => Option::None - } - } - pub fn to_raw(&self) -> u32 { - *self as u32 - } - } - - bitflags! { - /// possible delivery modes - /// - pub struct DeliveryKind: u32 { - /// pick your cake up yourself - const PickUp = 1; - /// flying drone delivery - const Drone = 2; - /// because we fear nothing - const Catapult = 4; - } - } - - impl DeliveryKind { - pub fn from_raw(n: u32) -> Option { - Some(DeliveryKind::from_bits_truncate(n)) - } - pub fn to_raw(&self) -> u32 { - self.bits() - } - } - - pub struct Implementation { - /// a cake is possible - /// - /// The server advertizes that a kind of cake is available - /// - /// **Arguments:** event_queue_handle, interface_data, wl_foo, kind, amount - /// - /// This request only exists since version 2 of the interface - pub cake: fn(evqh: &mut EventQueueHandle, data: &mut ID, wl_foo: &WlFoo, kind: CakeKind, amount: u32), - } - - impl Copy for Implementation {} - impl Clone for Implementation { - fn clone(&self) -> Implementation { - *self - } - } - - impl PartialEq for Implementation { - fn eq(&self, other: &Implementation) -> bool { - true - && (self.cake as usize == other.cake as usize) - - } - } - - - const WL_FOO_FOO_IT: u32 = 0; - const WL_FOO_CREATE_BAR: u32 = 1; - impl WlFoo { - /// do some foo - /// - /// This will do some foo with its args. - pub fn foo_it(&self, number: i32, unumber: u32, text: String, float: f64, file: ::std::os::unix::io::RawFd) ->() { - let text = CString::new(text).unwrap_or_else(|_| panic!("Got a String with interior null in wl_foo.foo_it:text")); - let float = wl_fixed_from_double(float); - unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_marshal, self.ptr(), WL_FOO_FOO_IT, number, unumber, text.as_ptr(), float, file) }; - } - - /// create a bar - /// - /// Create a bar which will do its bar job. - pub fn create_bar(&self) ->super::wl_bar::WlBar { - let ptr = unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_marshal_constructor, self.ptr(), WL_FOO_CREATE_BAR, &wl_bar_interface, ptr::null_mut::()) }; - let proxy = unsafe { Proxy::from_ptr_new(ptr) }; - proxy - } - } -} -pub mod wl_bar { - //! Interface for bars - //! - //! This interface allows you to bar your foos. - use super::EventQueueHandle; - use super::Proxy; - use super::RequestResult; - - use super::{Liveness, Implementable}; - use super::interfaces::*; - use wayland_sys::common::*; - use std::any::Any; - use std::ffi::{CString,CStr}; - use std::os::raw::c_void; - use std::ptr; - use std::sync::Arc; - use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; - use wayland_sys::RUST_MANAGED; - use wayland_sys::client::*; - type UserData = (*mut EventQueueHandle, Option>, Arc<(AtomicBool, AtomicPtr<()>)>); - - pub struct WlBar { - ptr: *mut wl_proxy, - data: Option)>> - } - - unsafe impl Send for WlBar {} - unsafe impl Sync for WlBar {} - - unsafe impl Proxy for WlBar { - fn ptr(&self) -> *mut wl_proxy { self.ptr } - - unsafe fn from_ptr_new(ptr: *mut wl_proxy) -> WlBar { - let data: *mut UserData = Box::into_raw(Box::new(( - ptr::null_mut(), - Option::None, - Arc::new((AtomicBool::new(true), AtomicPtr::new(ptr::null_mut()))), - ))); - ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_set_user_data, ptr, data as *mut c_void); - WlBar { ptr: ptr, data: Some((&*data).2.clone()) } - } - unsafe fn from_ptr_initialized(ptr: *mut wl_proxy) -> WlBar { - - let implem = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_listener, ptr); - let rust_managed = implem == &RUST_MANAGED as *const _ as *const _; - - - if rust_managed { - let data = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, ptr) as *mut UserData; - WlBar { ptr: ptr, data: Some((&*data).2.clone()) } - } else { - WlBar { ptr: ptr, data: Option::None } - } - } - - fn interface_ptr() -> *const wl_interface { unsafe { &wl_bar_interface } } - fn interface_name() -> &'static str { "wl_bar" } - fn supported_version() -> u32 { 1 } - fn version(&self) -> u32 { unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_version, self.ptr()) } } - - fn status(&self) -> Liveness { - if let Some(ref data) = self.data { - if data.0.load(Ordering::SeqCst) { - Liveness::Alive - } else { - Liveness::Dead - } - } else { - Liveness::Unmanaged - } - } - fn equals(&self, other: &WlBar) -> bool { - self.status() != Liveness::Dead && other.status() != Liveness::Dead && self.ptr == other.ptr - } - - fn set_user_data(&self, ptr: *mut ()) { - if let Some(ref data) = self.data { - data.1.store(ptr, Ordering::SeqCst); - } - } - fn get_user_data(&self) -> *mut () { - if let Some(ref data) = self.data { - data.1.load(Ordering::SeqCst) - } else { - ::std::ptr::null_mut() - } - } - unsafe fn clone_unchecked(&self) -> WlBar { - WlBar { - ptr: self.ptr, - data: self.data.clone() - } - } - } - - unsafe impl Implementable for WlBar { - type Implementation = (); - #[allow(unused_mut,unused_assignments)] - unsafe fn __dispatch_msg(&self, opcode: u32, args: *const wl_argument) -> Result<(),()> { - return Err(()) - } - } - - const WL_BAR_BAR_DELIVERY: u32 = 0; - const WL_BAR_RELEASE: u32 = 1; - - impl WlBar { - /// ask for a bar delivery - /// - /// Proceed to a bar delivery of given foo. - /// - /// This request is only available since version 2 of the interface - pub fn bar_delivery(&self, kind: super::wl_foo::DeliveryKind, target: &super::wl_foo::WlFoo, metadata: Vec) ->RequestResult<()> { - if self.status() == Liveness::Dead { return RequestResult::Destroyed } - let metadata = wl_array { size: metadata.len(), alloc: metadata.capacity(), data: metadata.as_ptr() as *mut _ }; - unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_marshal, self.ptr(), WL_BAR_BAR_DELIVERY, kind, target.ptr(), &metadata as *const wl_array) }; - RequestResult::Sent(()) - } - - /// release this bar - /// - /// Notify the compositor that you have finished using this bar. - /// - /// This is a destructor, you cannot send requests to this object once this method is called. - pub fn release(&self) ->RequestResult<()> { - if self.status() == Liveness::Dead { return RequestResult::Destroyed } - unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_marshal, self.ptr(), WL_BAR_RELEASE) }; - if let Some(ref data) = self.data { - data.0.store(false, ::std::sync::atomic::Ordering::SeqCst); - } - let udata = unsafe { &mut *(ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, self.ptr()) as *mut UserData) }; - let _impl = udata.1.take(); - ::std::mem::drop(_impl); - unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy, self.ptr()); } - RequestResult::Sent(()) - } - } -} - -pub mod wl_display { - //! core global object - //! - //! This global is special and should only generate code client-side, not server-side. - use super::EventQueueHandle; - use super::Proxy; - use super::RequestResult; - use super::{Liveness, Implementable}; - use super::interfaces::*; - use wayland_sys::common::*; - use std::any::Any; - use std::ffi::{CString,CStr}; - use std::os::raw::c_void; - use std::ptr; - use std::sync::Arc; - use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; - use wayland_sys::RUST_MANAGED; - use wayland_sys::client::*; - type UserData = (*mut EventQueueHandle, Option>, Arc<(AtomicBool, AtomicPtr<()>)>); - - pub struct WlDisplay { - ptr: *mut wl_proxy, - data: Option)>> - } - - unsafe impl Send for WlDisplay {} - unsafe impl Sync for WlDisplay {} - unsafe impl Proxy for WlDisplay { - fn ptr(&self) -> *mut wl_proxy { self.ptr } - - unsafe fn from_ptr_new(ptr: *mut wl_proxy) -> WlDisplay { - let data: *mut UserData = Box::into_raw(Box::new(( - ptr::null_mut(), - Option::None, - Arc::new((AtomicBool::new(true), AtomicPtr::new(ptr::null_mut()))), - ))); - ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_set_user_data, ptr, data as *mut c_void); - WlDisplay { ptr: ptr, data: Some((&*data).2.clone()) } - } - unsafe fn from_ptr_initialized(ptr: *mut wl_proxy) -> WlDisplay { - - let implem = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_listener, ptr); - let rust_managed = implem == &RUST_MANAGED as *const _ as *const _; - - if rust_managed { - let data = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, ptr) as *mut UserData; - WlDisplay { ptr: ptr, data: Some((&*data).2.clone()) } - } else { - WlDisplay { ptr: ptr, data: Option::None } - } - } - - fn interface_ptr() -> *const wl_interface { unsafe { &wl_display_interface } } - fn interface_name() -> &'static str { "wl_display" } - fn supported_version() -> u32 { 1 } - fn version(&self) -> u32 { unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_version, self.ptr()) } } - - fn status(&self) -> Liveness { - if let Some(ref data) = self.data { - if data.0.load(Ordering::SeqCst) { - Liveness::Alive - } else { - Liveness::Dead - } - } else { - Liveness::Unmanaged - } - } - - fn equals(&self, other: &WlDisplay) -> bool { - self.status() != Liveness::Dead && other.status() != Liveness::Dead && self.ptr == other.ptr - } - - fn set_user_data(&self, ptr: *mut ()) { - if let Some(ref data) = self.data { - data.1.store(ptr, Ordering::SeqCst); - } - } - fn get_user_data(&self) -> *mut () { - if let Some(ref data) = self.data { - data.1.load(Ordering::SeqCst) - } else { - ::std::ptr::null_mut() - } - } - unsafe fn clone_unchecked(&self) -> WlDisplay { - WlDisplay { - ptr: self.ptr, - data: self.data.clone() - } - } - } - impl WlDisplay { - } -} -pub mod wl_registry { - //! global registry object - //! - //! This global is special and should only generate code client-side, not server-side. - use super::EventQueueHandle; - use super::Proxy; - use super::RequestResult; - - use super::{Liveness, Implementable}; - use super::interfaces::*; - use wayland_sys::common::*; - use std::any::Any; - use std::ffi::{CString,CStr}; - use std::os::raw::c_void; - use std::ptr; - use std::sync::Arc; - use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; - use wayland_sys::RUST_MANAGED; - use wayland_sys::client::*; - type UserData = (*mut EventQueueHandle, Option>, Arc<(AtomicBool, AtomicPtr<()>)>); - - pub struct WlRegistry { - ptr: *mut wl_proxy, - data: Option)>> - } - - unsafe impl Send for WlRegistry {} - unsafe impl Sync for WlRegistry {} - unsafe impl Proxy for WlRegistry { - fn ptr(&self) -> *mut wl_proxy { self.ptr } - - unsafe fn from_ptr_new(ptr: *mut wl_proxy) -> WlRegistry { - let data: *mut UserData = Box::into_raw(Box::new(( - ptr::null_mut(), - Option::None, - Arc::new((AtomicBool::new(true), AtomicPtr::new(ptr::null_mut()))), - ))); - ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_set_user_data, ptr, data as *mut c_void); - WlRegistry { ptr: ptr, data: Some((&*data).2.clone()) } - } - unsafe fn from_ptr_initialized(ptr: *mut wl_proxy) -> WlRegistry { - - let implem = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_listener, ptr); - let rust_managed = implem == &RUST_MANAGED as *const _ as *const _; - - if rust_managed { - let data = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, ptr) as *mut UserData; - WlRegistry { ptr: ptr, data: Some((&*data).2.clone()) } - } else { - WlRegistry { ptr: ptr, data: Option::None } - } - } - - fn interface_ptr() -> *const wl_interface { unsafe { &wl_registry_interface } } - fn interface_name() -> &'static str { "wl_registry" } - fn supported_version() -> u32 { 1 } - fn version(&self) -> u32 { unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_version, self.ptr()) } } - - fn status(&self) -> Liveness { - if let Some(ref data) = self.data { - if data.0.load(Ordering::SeqCst) { - Liveness::Alive - } else { - Liveness::Dead - } - } else { - Liveness::Unmanaged - } - } - - fn equals(&self, other: &WlRegistry) -> bool { - self.status() != Liveness::Dead && other.status() != Liveness::Dead && self.ptr == other.ptr - } - - fn set_user_data(&self, ptr: *mut ()) { - if let Some(ref data) = self.data { - data.1.store(ptr, Ordering::SeqCst); - } - } - fn get_user_data(&self) -> *mut () { - if let Some(ref data) = self.data { - data.1.load(Ordering::SeqCst) - } else { - ::std::ptr::null_mut() - } - } - unsafe fn clone_unchecked(&self) -> WlRegistry { - WlRegistry { - ptr: self.ptr, - data: self.data.clone() - } - } - } - unsafe impl Implementable for WlRegistry { - type Implementation = (); - #[allow(unused_mut,unused_assignments)] - unsafe fn __dispatch_msg(&self, opcode: u32, args: *const wl_argument) -> Result<(),()> { - return Err(()) - } - } - const WL_REGISTRY_BIND: u32 = 0; - impl WlRegistry { - /// bind an object to the display - /// - /// This request is a special code-path, as its new-id argument as no target type. - pub fn bind(&self, version: u32, name: u32) ->T { - if version > ::supported_version() { - panic!("Tried to bind interface {} with version {} while it is only supported up to {}.", ::interface_name(), version, ::supported_version()) - } - let ptr = unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_marshal_constructor_versioned, self.ptr(), WL_REGISTRY_BIND, ::interface_ptr(), version, name, (*::interface_ptr()).name, version, ptr::null_mut::()) }; - let proxy = unsafe { Proxy::from_ptr_new(ptr) }; - proxy - } - } -} - -pub mod wl_callback { - //! callback object - //! - //! This object has a special behavior regarding its destructor. - use super::EventQueueHandle; - use super::Proxy; - use super::RequestResult; - - use super::{Liveness, Implementable}; - use super::interfaces::*; - use wayland_sys::common::*; - use std::any::Any; - use std::ffi::{CString,CStr}; - use std::os::raw::c_void; - use std::ptr; - use std::sync::Arc; - use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; - use wayland_sys::RUST_MANAGED; - use wayland_sys::client::*; - type UserData = (*mut EventQueueHandle, Option>, Arc<(AtomicBool, AtomicPtr<()>)>); - - pub struct WlCallback { - ptr: *mut wl_proxy, - data: Option)>> - } - - unsafe impl Send for WlCallback {} - unsafe impl Sync for WlCallback {} - unsafe impl Proxy for WlCallback { - fn ptr(&self) -> *mut wl_proxy { self.ptr } - - unsafe fn from_ptr_new(ptr: *mut wl_proxy) -> WlCallback { - let data: *mut UserData = Box::into_raw(Box::new(( - ptr::null_mut(), - Option::None, - Arc::new((AtomicBool::new(true), AtomicPtr::new(ptr::null_mut()))), - - ))); - ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_set_user_data, ptr, data as *mut c_void); - WlCallback { ptr: ptr, data: Some((&*data).2.clone()) } - } - unsafe fn from_ptr_initialized(ptr: *mut wl_proxy) -> WlCallback { - - let implem = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_listener, ptr); - let rust_managed = implem == &RUST_MANAGED as *const _ as *const _; - - if rust_managed { - let data = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, ptr) as *mut UserData; - WlCallback { ptr: ptr, data: Some((&*data).2.clone()) } - } else { - WlCallback { ptr: ptr, data: Option::None } - } - } - - fn interface_ptr() -> *const wl_interface { unsafe { &wl_callback_interface } } - fn interface_name() -> &'static str { "wl_callback" } - fn supported_version() -> u32 { 1 } - fn version(&self) -> u32 { unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_version, self.ptr()) } } - - fn status(&self) -> Liveness { - if let Some(ref data) = self.data { - if data.0.load(Ordering::SeqCst) { - Liveness::Alive - } else { - Liveness::Dead - } - } else { - Liveness::Unmanaged - } - } - - fn equals(&self, other: &WlCallback) -> bool { - self.status() != Liveness::Dead && other.status() != Liveness::Dead && self.ptr == other.ptr - } - - fn set_user_data(&self, ptr: *mut ()) { - if let Some(ref data) = self.data { - data.1.store(ptr, Ordering::SeqCst); - } - } - fn get_user_data(&self) -> *mut () { - if let Some(ref data) = self.data { - data.1.load(Ordering::SeqCst) - } else { - ::std::ptr::null_mut() - } - } - unsafe fn clone_unchecked(&self) -> WlCallback { - WlCallback { - ptr: self.ptr, - data: self.data.clone() - } - } - } - - unsafe impl Implementable for WlCallback { - type Implementation = Implementation; - #[allow(unused_mut,unused_assignments)] - unsafe fn __dispatch_msg(&self, opcode: u32, args: *const wl_argument) -> Result<(),()> { - let data = &mut *(ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, self.ptr()) as *mut UserData); - let evq = &mut *(data.0); - let mut kill = false; - { - let &mut (ref implementation, ref mut idata) = data.1.as_mut().unwrap().downcast_mut::<(Implementation, ID)>().unwrap(); - - match opcode { - 0 => { - let callback_data = {*(args.offset(0) as *const u32)}; - - (data.2).0.store(false, ::std::sync::atomic::Ordering::SeqCst); - kill = true; - ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy, self.ptr()); - (implementation.done)(evq, idata, self, callback_data); - }, - _ => return Err(()) - } - } - - if kill { - let _impl = data.1.take(); - ::std::mem::drop(_impl); - } - - Ok(()) - } - } - pub struct Implementation { - /// done event - /// - /// This event is actually a destructor, but the protocol XML has no wait of specifying it. - /// As such, the scanner should consider wl_callback.done as a special case. - /// - /// **Arguments:** event_queue_handle, interface_data, wl_callback, callback_data - /// - /// This is a destructor, you cannot send requests to this object once this method is called. - pub done: fn(evqh: &mut EventQueueHandle, data: &mut ID, wl_callback: &WlCallback, callback_data: u32), - } - - impl Copy for Implementation {} - impl Clone for Implementation { - fn clone(&self) -> Implementation { - *self - } - } - - impl PartialEq for Implementation { - fn eq(&self, other: &Implementation) -> bool { - true - - && (self.done as usize == other.done as usize) - - } - } - - impl WlCallback { - } -} - diff --git a/tests/scanner_assets/server_c_code.rs b/tests/scanner_assets/server_c_code.rs new file mode 100644 index 00000000000..0f60aa65391 --- /dev/null +++ b/tests/scanner_assets/server_c_code.rs @@ -0,0 +1,320 @@ +// +// This file was auto-generated, do not edit directly. +// + +/* +This is an example copyright. + It contains several lines. + AS WELL AS ALL CAPS TEXT. +*/ + +pub mod wl_foo { + //! Interface for fooing + //! + //! This is the dedicated interface for doing foos over any + //! kind of other foos. + + use super::{Resource, NewResource, AnonymousObject, Interface, MessageGroup}; + use super::sys::common::{wl_argument, wl_interface, wl_array}; + use super::sys::server::*; + + /// Possible cake kinds + /// + /// List of the possible kind of cake supported by the protocol. + + #[repr(u32)] + #[derive(Copy,Clone,Debug,PartialEq)] + pub enum CakeKind { + /// mild cake without much flavor + Basic = 0, + /// spicy cake to burn your tongue + Spicy = 1, + /// fruity cake to get vitamins + Fruity = 2, + } + impl CakeKind { + pub fn from_raw(n: u32) -> Option { + match n { + 0 => Some(CakeKind::Basic), + 1 => Some(CakeKind::Spicy), + 2 => Some(CakeKind::Fruity), + + _ => Option::None + } + } + pub fn to_raw(&self) -> u32 { + *self as u32 + } + } + + bitflags! { + /// possible delivery modes + /// + pub struct DeliveryKind: u32 { + /// pick your cake up yourself + const PickUp = 1; + /// flying drone delivery + const Drone = 2; + /// because we fear nothing + const Catapult = 4; + } + } + + impl DeliveryKind { + pub fn from_raw(n: u32) -> Option { + Some(DeliveryKind::from_bits_truncate(n)) + + } + pub fn to_raw(&self) -> u32 { + self.bits() + } + } + + pub enum Request { + /// do some foo + /// + /// This will do some foo with its args. + FooIt {number: i32, unumber: u32, text: String, float: f64, file: ::std::os::unix::io::RawFd, }, + /// create a bar + /// + /// Create a bar which will do its bar job. + CreateBar {id: NewResource, }, + } + + impl super::MessageGroup for Request { + fn is_destructor(&self) -> bool { + match *self { + _ => false + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + match opcode { + 0 => { + let _args = ::std::slice::from_raw_parts(args, 5); + Ok(Request::FooIt { + number: _args[0].i, + unumber: _args[1].u, + text: ::std::ffi::CStr::from_ptr(_args[2].s).to_string_lossy().into_owned(), + float: (_args[3].f as f64)/256., + file: _args[4].h, + }) }, + 1 => { + let _args = ::std::slice::from_raw_parts(args, 1); + Ok(Request::CreateBar { + id: { let client = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_client, obj as *mut _); let version = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_version, obj as *mut _); let new_ptr = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_create, client, super::wl_bar::WlBar::c_interface(), version, _args[0].n);NewResource::::from_c_ptr(new_ptr) }, + }) }, + _ => return Err(()) + } + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + panic!("Request::as_raw_c_in can not be used Server-side.") + } + } + + pub enum Event { + /// a cake is possible + /// + /// The server advertizes that a kind of cake is available + /// + /// Only available since version 2 of the interface + Cake {kind: CakeKind, amount: u32, }, + } + + impl super::MessageGroup for Event { + fn is_destructor(&self) -> bool { + match *self { + _ => false + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + panic!("Event::from_raw_c can not be used Server-side.") + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + match self { + Event::Cake { kind, amount, } => { + let mut _args_array: [wl_argument; 2] = unsafe { ::std::mem::zeroed() }; + _args_array[0].u = kind.to_raw(); + _args_array[1].u = amount; + f(0, &mut _args_array) + }, + } + } + } + + + pub struct WlFoo; + + impl Interface for WlFoo { + type Request = Request; + type Event = Event; + const NAME: &'static str = "wl_foo"; + fn c_interface() -> *const wl_interface { + unsafe { &super::super::c_interfaces::wl_foo_interface } + } + } + +} + +pub mod wl_bar { + //! Interface for bars + //! + //! This interface allows you to bar your foos. + + use super::{Resource, NewResource, AnonymousObject, Interface, MessageGroup}; + use super::sys::common::{wl_argument, wl_interface, wl_array}; + use super::sys::server::*; + + pub enum Request { + /// ask for a bar delivery + /// + /// Proceed to a bar delivery of given foo. + /// + /// Only available since version 2 of the interface + BarDelivery {kind: super::wl_foo::DeliveryKind, target: Resource, metadata: Vec, }, + /// release this bar + /// + /// Notify the compositor that you have finished using this bar. + /// + /// This is a destructor, once received this object cannot be used any longer. + Release, + } + + impl super::MessageGroup for Request { + fn is_destructor(&self) -> bool { + match *self { + Request::Release => true, + _ => false + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + match opcode { + 0 => { + let _args = ::std::slice::from_raw_parts(args, 3); + Ok(Request::BarDelivery { + kind: super::wl_foo::DeliveryKind::from_raw(_args[0].u).ok_or(())?, + target: Resource::::from_c_ptr(_args[1].o as *mut _), + metadata: { let array = &*_args[2].a; ::std::slice::from_raw_parts(array.data as *const u8, array.size).to_owned() }, + }) }, + 1 => { + Ok(Request::Release) }, + _ => return Err(()) + } + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + panic!("Request::as_raw_c_in can not be used Server-side.") + } + } + + pub enum Event { + } + + impl super::MessageGroup for Event { + fn is_destructor(&self) -> bool { + match *self { + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + panic!("Event::from_raw_c can not be used Server-side.") + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + match self { + } + } + } + + + pub struct WlBar; + + impl Interface for WlBar { + type Request = Request; + type Event = Event; + const NAME: &'static str = "wl_bar"; + fn c_interface() -> *const wl_interface { + unsafe { &super::super::c_interfaces::wl_bar_interface } + } + } + +} + +pub mod wl_callback { + //! callback object + //! + //! This object has a special behavior regarding its destructor. + + use super::{Resource, NewResource, AnonymousObject, Interface, MessageGroup}; + use super::sys::common::{wl_argument, wl_interface, wl_array}; + use super::sys::server::*; + + pub enum Request { + } + + impl super::MessageGroup for Request { + fn is_destructor(&self) -> bool { + match *self { + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + match opcode { + _ => return Err(()) + } + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + panic!("Request::as_raw_c_in can not be used Server-side.") + } + } + + pub enum Event { + /// done event + /// + /// This event is actually a destructor, but the protocol XML has no wait of specifying it. + /// As such, the scanner should consider wl_callback.done as a special case. + /// + /// This is a destructor, once sent this object cannot be used any longer. + Done {callback_data: u32, }, + } + + impl super::MessageGroup for Event { + fn is_destructor(&self) -> bool { + match *self { + Event::Done { .. } => true, + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + panic!("Event::from_raw_c can not be used Server-side.") + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + match self { + Event::Done { callback_data, } => { + let mut _args_array: [wl_argument; 1] = unsafe { ::std::mem::zeroed() }; + _args_array[0].u = callback_data; + f(0, &mut _args_array) + }, + } + } + } + + + pub struct WlCallback; + + impl Interface for WlCallback { + type Request = Request; + type Event = Event; + const NAME: &'static str = "wl_callback"; + fn c_interface() -> *const wl_interface { + unsafe { &super::super::c_interfaces::wl_callback_interface } + } + } +} + diff --git a/tests/scanner_assets/server_code.rs b/tests/scanner_assets/server_code.rs deleted file mode 100644 index 36dacfbb376..00000000000 --- a/tests/scanner_assets/server_code.rs +++ /dev/null @@ -1,537 +0,0 @@ -// -// This file was auto-generated, do not edit directly -// - -/* -This is an example copyright. - It contains several lines. - AS WELL AS ALL CAPS TEXT. -*/ - -pub mod wl_foo { - //! Interface for fooing - //! - //! This is the dedicated interface for doing foos over any - //! kind of other foos. - use super::Client; - use super::EventLoopHandle; - use super::Resource; - use super::EventResult; - use super::{Liveness, Implementable}; - use super::interfaces::*; - use wayland_sys::common::*; - use std::any::Any; - use std::ffi::{CString,CStr}; - use std::os::raw::c_void; - use std::ptr; - use std::sync::Arc; - use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; - use wayland_sys::RUST_MANAGED; - use wayland_sys::server::*; - type UserData = (*mut EventLoopHandle, Option>,Arc<(AtomicBool, AtomicPtr<()>)>, Option); - - pub struct WlFoo { - ptr: *mut wl_resource, - data: Option)>> - } - - unsafe impl Send for WlFoo {} - unsafe impl Sync for WlFoo {} - - unsafe impl Resource for WlFoo { - fn ptr(&self) -> *mut wl_resource { self.ptr } - - unsafe fn from_ptr_new(ptr: *mut wl_resource) -> WlFoo { - let data: *mut UserData = Box::into_raw(Box::new(( - ptr::null_mut(), - Option::None, - Arc::new((AtomicBool::new(true), AtomicPtr::new(ptr::null_mut()))), - Option::None - ))); - ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_set_user_data, ptr, data as *mut c_void); - WlFoo { ptr: ptr, data: Some((&*data).2.clone()) } - } - unsafe fn from_ptr_initialized(ptr: *mut wl_resource) -> WlFoo { - - let rust_managed = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_instance_of, - ptr, Self::interface_ptr(), &RUST_MANAGED as *const _ as *const _ - ) != 0; - - - if rust_managed { - let data = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_user_data, ptr) as *mut UserData; - WlFoo { ptr: ptr, data: Some((&*data).2.clone()) } - } else { - WlFoo { ptr: ptr, data: Option::None } - } - } - - fn interface_ptr() -> *const wl_interface { unsafe { &wl_foo_interface } } - fn interface_name() -> &'static str { "wl_foo" } - fn supported_version() -> u32 { 3 } - fn version(&self) -> i32 { unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_version, self.ptr()) } } - - fn status(&self) -> Liveness { - if let Some(ref data) = self.data { - if data.0.load(Ordering::SeqCst) { - Liveness::Alive - } else { - Liveness::Dead - } - } else { - Liveness::Unmanaged - } - } - fn equals(&self, other: &WlFoo) -> bool { - self.status() != Liveness::Dead && other.status() != Liveness::Dead && self.ptr == other.ptr - } - - fn set_user_data(&self, ptr: *mut ()) { - if let Some(ref data) = self.data { - data.1.store(ptr, Ordering::SeqCst); - } - } - fn get_user_data(&self) -> *mut () { - if let Some(ref data) = self.data { - data.1.load(Ordering::SeqCst) - } else { - ::std::ptr::null_mut() - } - } - unsafe fn clone_unchecked(&self) -> WlFoo { - WlFoo { - ptr: self.ptr, - data: self.data.clone() - } - } - } - unsafe impl Implementable for WlFoo { - type Implementation = Implementation; - #[allow(unused_mut,unused_assignments)] - unsafe fn __dispatch_msg(&self, client: &Client, opcode: u32, args: *const wl_argument) -> Result<(),()> { - - let data = &mut *(ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_user_data, self.ptr()) as *mut UserData); - let evq = &mut *(data.0); - let mut kill = false; - { - let &mut (ref implementation, ref mut idata) = data.1.as_mut().unwrap().downcast_mut::<(Implementation, ID)>().unwrap(); - match opcode { - 0 => { - let number = {*(args.offset(0) as *const i32)}; - let unumber = {*(args.offset(1) as *const u32)}; - let text = {String::from_utf8_lossy(CStr::from_ptr(*(args.offset(2) as *const *const _)).to_bytes()).into_owned()}; - let float = {wl_fixed_to_double(*(args.offset(3) as *const i32))}; - let file = {*(args.offset(4) as *const i32)}; - (implementation.foo_it)(evq, idata, client, self, number, unumber, text, float, file); - }, - 1 => { - let id = {Resource::from_ptr_new(ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_create, client.ptr(), ::interface_ptr(), self.version(), *(args.offset(0) as *const u32)))}; - (implementation.create_bar)(evq, idata, client, self, id); - }, - _ => return Err(()) - } - } - - if kill { - let _impl = data.1.take(); - ::std::mem::drop(_impl); - } - Ok(()) - } - } - - /// Possible cake kinds - /// - /// List of the possible kind of cake supported by the protocol. - #[repr(u32)] - #[derive(Copy,Clone,Debug,PartialEq)] - pub enum CakeKind { - /// mild cake without much flavor - Basic = 0, - /// spicy cake to burn your tongue - Spicy = 1, - /// fruity cake to get vitamins - Fruity = 2, - } - impl CakeKind { - pub fn from_raw(n: u32) -> Option { - match n { - 0 => Some(CakeKind::Basic), - 1 => Some(CakeKind::Spicy), - 2 => Some(CakeKind::Fruity), - _ => Option::None - } - } - pub fn to_raw(&self) -> u32 { - *self as u32 - } - } - - bitflags! { - /// possible delivery modes - /// - pub struct DeliveryKind: u32 { - /// pick your cake up yourself - const PickUp = 1; - /// flying drone delivery - const Drone = 2; - /// because we fear nothing - const Catapult = 4; - } - } - impl DeliveryKind { - pub fn from_raw(n: u32) -> Option { - Some(DeliveryKind::from_bits_truncate(n)) - } - pub fn to_raw(&self) -> u32 { - self.bits() - } - } - - pub struct Implementation { - /// do some foo - /// - /// This will do some foo with its args. - /// - /// **Arguments:** event_queue_handle, interface_data, client, wl_foo, number, unumber, text, float, file - pub foo_it: fn(evqh: &mut EventLoopHandle, data: &mut ID, client: &Client, wl_foo: &WlFoo, number: i32, unumber: u32, text: String, float: f64, file: ::std::os::unix::io::RawFd), - /// create a bar - /// - /// Create a bar which will do its bar job. - /// - /// **Arguments:** event_queue_handle, interface_data, client, wl_foo, id - pub create_bar: fn(evqh: &mut EventLoopHandle, data: &mut ID, client: &Client, wl_foo: &WlFoo, id: super::wl_bar::WlBar), - } - impl Copy for Implementation {} - impl Clone for Implementation { - fn clone(&self) -> Implementation { - *self - } - } - - impl PartialEq for Implementation { - fn eq(&self, other: &Implementation) -> bool { - true - && (self.foo_it as usize == other.foo_it as usize) - && (self.create_bar as usize == other.create_bar as usize) - - } - } - - const WL_FOO_CAKE: u32 = 0; - - impl WlFoo { - /// a cake is possible - /// - /// The server advertizes that a kind of cake is available - /// - /// This event is only available since version 2 of the interface - pub fn cake(&self, kind: CakeKind, amount: u32) ->EventResult<()> { - if self.status() == Liveness::Dead { return EventResult::Destroyed } - unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_post_event, self.ptr(), WL_FOO_CAKE, kind, amount) }; - EventResult::Sent(()) - } - - } -} -pub mod wl_bar { - //! Interface for bars - //! - //! This interface allows you to bar your foos. - use super::Client; - use super::EventLoopHandle; - use super::Resource; - use super::EventResult; - - use super::{Liveness, Implementable}; - use super::interfaces::*; - use wayland_sys::common::*; - use std::any::Any; - use std::ffi::{CString,CStr}; - use std::os::raw::c_void; - use std::ptr; - use std::sync::Arc; - use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; - use wayland_sys::RUST_MANAGED; - use wayland_sys::server::*; - type UserData = (*mut EventLoopHandle, Option>,Arc<(AtomicBool, AtomicPtr<()>)>, Option); - - pub struct WlBar { - ptr: *mut wl_resource, - data: Option)>> - } - - unsafe impl Send for WlBar {} - unsafe impl Sync for WlBar {} - - unsafe impl Resource for WlBar { - fn ptr(&self) -> *mut wl_resource { self.ptr } - - unsafe fn from_ptr_new(ptr: *mut wl_resource) -> WlBar { - let data: *mut UserData = Box::into_raw(Box::new(( - ptr::null_mut(), - Option::None, - Arc::new((AtomicBool::new(true), AtomicPtr::new(ptr::null_mut()))), - Option::None - ))); - ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_set_user_data, ptr, data as *mut c_void); - WlBar { ptr: ptr, data: Some((&*data).2.clone()) } - } - unsafe fn from_ptr_initialized(ptr: *mut wl_resource) -> WlBar { - - let rust_managed = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_instance_of, - ptr, Self::interface_ptr(), &RUST_MANAGED as *const _ as *const _ - ) != 0; - - - if rust_managed { - let data = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_user_data, ptr) as *mut UserData; - WlBar { ptr: ptr, data: Some((&*data).2.clone()) } - } else { - WlBar { ptr: ptr, data: Option::None } - } - } - - fn interface_ptr() -> *const wl_interface { unsafe { &wl_bar_interface } } - fn interface_name() -> &'static str { "wl_bar" } - fn supported_version() -> u32 { 1 } - fn version(&self) -> i32 { unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_version, self.ptr()) } } - - fn status(&self) -> Liveness { - if let Some(ref data) = self.data { - if data.0.load(Ordering::SeqCst) { - Liveness::Alive - } else { - Liveness::Dead - } - } else { - Liveness::Unmanaged - } - } - fn equals(&self, other: &WlBar) -> bool { - self.status() != Liveness::Dead && other.status() != Liveness::Dead && self.ptr == other.ptr - } - - fn set_user_data(&self, ptr: *mut ()) { - if let Some(ref data) = self.data { - data.1.store(ptr, Ordering::SeqCst); - } - } - fn get_user_data(&self) -> *mut () { - if let Some(ref data) = self.data { - data.1.load(Ordering::SeqCst) - } else { - ::std::ptr::null_mut() - } - } - unsafe fn clone_unchecked(&self) -> WlBar { - WlBar { - ptr: self.ptr, - data: self.data.clone() - } - } - } - - unsafe impl Implementable for WlBar { - type Implementation = Implementation; - #[allow(unused_mut,unused_assignments)] - unsafe fn __dispatch_msg(&self, client: &Client, opcode: u32, args: *const wl_argument) -> Result<(),()> { - - let data = &mut *(ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_user_data, self.ptr()) as *mut UserData); - let evq = &mut *(data.0); - let mut kill = false; - { - let &mut (ref implementation, ref mut idata) = data.1.as_mut().unwrap().downcast_mut::<(Implementation, ID)>().unwrap(); - match opcode { - 0 => { - let kind = {match super::wl_foo::DeliveryKind::from_raw(*(args.offset(0) as *const u32)) { Some(v) => v, Option::None => return Err(()) }}; - let target = {Resource::from_ptr_initialized(*(args.offset(1) as *const *mut wl_resource))}; - let metadata = {let array = *(args.offset(2) as *const *mut wl_array); ::std::slice::from_raw_parts((*array).data as *const u8, (*array).size as usize).to_owned()}; - (implementation.bar_delivery)(evq, idata, client, self, kind, &target, metadata); - }, - 1 => { - - (data.2).0.store(false, ::std::sync::atomic::Ordering::SeqCst); - kill = true; - ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_destroy, self.ptr()); - (implementation.release)(evq, idata, client, self); - }, - _ => return Err(()) - } - } - - if kill { - let _impl = data.1.take(); - ::std::mem::drop(_impl); - } - Ok(()) - } - } - - pub struct Implementation { - /// ask for a bar delivery - /// - /// Proceed to a bar delivery of given foo. - /// - /// **Arguments:** event_queue_handle, interface_data, client, wl_bar, kind, target, metadata - /// - /// This event only exists since version 2 of the interface - pub bar_delivery: fn(evqh: &mut EventLoopHandle, data: &mut ID, client: &Client, wl_bar: &WlBar, kind: super::wl_foo::DeliveryKind, target: &super::wl_foo::WlFoo, metadata: Vec), - /// release this bar - /// - /// Notify the compositor that you have finished using this bar. - /// - /// **Arguments:** event_queue_handle, interface_data, client, wl_bar - /// - /// This is a destructor, you cannot send events to this object once this method is called. - pub release: fn(evqh: &mut EventLoopHandle, data: &mut ID, client: &Client, wl_bar: &WlBar), - } - - impl Copy for Implementation {} - impl Clone for Implementation { - fn clone(&self) -> Implementation { - *self - } - } - - impl PartialEq for Implementation { - fn eq(&self, other: &Implementation) -> bool { - true - && (self.bar_delivery as usize == other.bar_delivery as usize) - && (self.release as usize == other.release as usize) - - } - } - - impl WlBar { - } -} - -pub mod wl_callback { - //! callback object - //! - //! This object has a special behavior regarding its destructor. - use super::Client; - use super::EventLoopHandle; - use super::Resource; - use super::EventResult; - use super::{Liveness, Implementable}; - use super::interfaces::*; - use wayland_sys::common::*; - use std::any::Any; - use std::ffi::{CString,CStr}; - use std::os::raw::c_void; - use std::ptr; - use std::sync::Arc; - use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; - use wayland_sys::RUST_MANAGED; - use wayland_sys::server::*; - type UserData = (*mut EventLoopHandle, Option>,Arc<(AtomicBool, AtomicPtr<()>)>, Option); - - pub struct WlCallback { - ptr: *mut wl_resource, - data: Option)>> - } - - unsafe impl Send for WlCallback {} - unsafe impl Sync for WlCallback {} - unsafe impl Resource for WlCallback { - fn ptr(&self) -> *mut wl_resource { self.ptr } - - unsafe fn from_ptr_new(ptr: *mut wl_resource) -> WlCallback { - let data: *mut UserData = Box::into_raw(Box::new(( - ptr::null_mut(), - Option::None, - Arc::new((AtomicBool::new(true), AtomicPtr::new(ptr::null_mut()))), - Option::None - ))); - ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_set_user_data, ptr, data as *mut c_void); - WlCallback { ptr: ptr, data: Some((&*data).2.clone()) } - } - unsafe fn from_ptr_initialized(ptr: *mut wl_resource) -> WlCallback { - - let rust_managed = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_instance_of, - ptr, Self::interface_ptr(), &RUST_MANAGED as *const _ as *const _ - ) != 0; - - if rust_managed { - let data = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_user_data, ptr) as *mut UserData; - WlCallback { ptr: ptr, data: Some((&*data).2.clone()) } - } else { - WlCallback { ptr: ptr, data: Option::None } - } - } - - fn interface_ptr() -> *const wl_interface { unsafe { &wl_callback_interface } } - fn interface_name() -> &'static str { "wl_callback" } - fn supported_version() -> u32 { 1 } - fn version(&self) -> i32 { unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_version, self.ptr()) } } - - fn status(&self) -> Liveness { - if let Some(ref data) = self.data { - if data.0.load(Ordering::SeqCst) { - Liveness::Alive - } else { - Liveness::Dead - } - } else { - Liveness::Unmanaged - } - } - - fn equals(&self, other: &WlCallback) -> bool { - self.status() != Liveness::Dead && other.status() != Liveness::Dead && self.ptr == other.ptr - } - - fn set_user_data(&self, ptr: *mut ()) { - if let Some(ref data) = self.data { - data.1.store(ptr, Ordering::SeqCst); - } - } - fn get_user_data(&self) -> *mut () { - if let Some(ref data) = self.data { - data.1.load(Ordering::SeqCst) - } else { - ::std::ptr::null_mut() - } - } - unsafe fn clone_unchecked(&self) -> WlCallback { - WlCallback { - ptr: self.ptr, - data: self.data.clone() - } - } - } - - unsafe impl Implementable for WlCallback { - type Implementation = (); - #[allow(unused_mut,unused_assignments)] - unsafe fn __dispatch_msg(&self, client: &Client, opcode: u32, args: *const wl_argument) -> Result<(),()> { - return Err(()) - } - } - - const WL_CALLBACK_DONE: u32 = 0; - impl WlCallback { - /// done event - /// - /// This event is actually a destructor, but the protocol XML has no wait of specifying it. - /// As such, the scanner should consider wl_callback.done as a special case. - /// - /// This is a destructor, you cannot send events to this object once this method is called. - pub fn done(&self, callback_data: u32) ->EventResult<()> { - if self.status() == Liveness::Dead { return EventResult::Destroyed } - unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_post_event, self.ptr(), WL_CALLBACK_DONE, callback_data) }; - - if let Some(ref data) = self.data { - data.0.store(false, ::std::sync::atomic::Ordering::SeqCst); - } - let udata = unsafe { &mut *(ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_user_data, self.ptr()) as *mut UserData) }; - let _impl = udata.1.take(); - ::std::mem::drop(_impl); - unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_destroy, self.ptr()); } - EventResult::Sent(()) - } - } -} - diff --git a/tests/server.rs b/tests/server.rs new file mode 100644 index 00000000000..9a0cd9bc391 --- /dev/null +++ b/tests/server.rs @@ -0,0 +1,25 @@ +use std::cell::Cell; +use std::rc::Rc; + +mod helpers; + +use helpers::TestServer; + +#[test] +fn dispatch_idle() { + // Server setup + // + let mut server = TestServer::new(); + + let dispatched = Rc::new(Cell::new(false)); + + let impl_dispatched = dispatched.clone(); + server + .event_loop + .token() + .add_idle_event_source(move |_, _| impl_dispatched.set(true)); + + server.event_loop.dispatch(Some(1)).unwrap(); + + assert!(dispatched.get()); +} diff --git a/tests/skel.rs b/tests/skel.rs index fcad3644d60..839e60298f9 100644 --- a/tests/skel.rs +++ b/tests/skel.rs @@ -1,9 +1,6 @@ -extern crate wayland_client as wayc; -extern crate wayland_server as ways; - mod helpers; -use helpers::{roundtrip, TestClient, TestServer}; +use helpers::{roundtrip, wayc, ways, TestClient, TestServer}; #[test] fn skel() { diff --git a/travis_install_wayland.sh b/travis_install_wayland.sh new file mode 100755 index 00000000000..9667b94d8b5 --- /dev/null +++ b/travis_install_wayland.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +# download and compile the wayland libs for given version, they will be installed in ~/install + +wayland_version=$1 + +mkdir ~/temp/ ~/install + +# download and extract +cd ~/temp/ +wget https://github.com/wayland-project/wayland/archive/${wayland_version}.tar.gz -O wayland.tar.gz +tar xf wayland.tar.gz +cd wayland-${wayland_version} + +# compile and install +./autogen.sh --prefix=$HOME/install --disable-documentation --disable-dtd-validation --disable-dependency-tracking +make +make install diff --git a/wayland-client/Cargo.toml b/wayland-client/Cargo.toml index 911bdd54083..952024647df 100644 --- a/wayland-client/Cargo.toml +++ b/wayland-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wayland-client" -version = "0.14.1" +version = "0.20.0" documentation = "https://smithay.github.io/wayland-rs/wayland_client/" repository = "https://github.com/smithay/wayland-rs" authors = ["Victor Berger "] @@ -14,20 +14,21 @@ build = "build.rs" travis-ci = { repository = "smithay/wayland-rs" } [dependencies] -wayland-sys = { version = "0.14.1", features = ["client"], path = "../wayland-sys" } -libc = "0.2" +wayland-commons = { version = "0.20.0", path = "../wayland-commons" } +wayland-sys = { version = "0.20.0", features = ["client"], path = "../wayland-sys", optional = true } bitflags = "1.0" -token_store = "0.1.1" +libc = "0.2" [build-dependencies] -wayland-scanner = { version = "0.14.1", path = "../wayland-scanner" } +wayland-scanner = { version = "0.20.0", path = "../wayland-scanner" } [dev-dependencies] -byteorder = "1" -tempfile = "2" +byteorder = "1.0" +tempfile = "2.0" [features] -default = ["egl", "cursor"] +default = ["egl", "cursor", "native_lib"] +native_lib = [ "wayland-sys", "wayland-commons/native_lib" ] dlopen = ["wayland-sys/dlopen"] egl = ["wayland-sys/egl"] cursor = ["wayland-sys/cursor"] diff --git a/wayland-client/build.rs b/wayland-client/build.rs index cb8b4b6be3a..9e8e4b380f8 100644 --- a/wayland-client/build.rs +++ b/wayland-client/build.rs @@ -2,7 +2,7 @@ extern crate wayland_scanner; use std::env::var; use std::path::Path; -use wayland_scanner::{generate_code, generate_interfaces, Side}; +use wayland_scanner::*; fn main() { let protocol_file = "./wayland.xml"; @@ -10,7 +10,13 @@ fn main() { let out_dir_str = var("OUT_DIR").unwrap(); let out_dir = Path::new(&out_dir_str); - generate_code(protocol_file, out_dir.join("wayland_api.rs"), Side::Client); - - generate_interfaces(protocol_file, out_dir.join("wayland_interfaces.rs")); + if var("CARGO_FEATURE_NATIVE_LIB").ok().is_some() { + // generate the C code + generate_c_code( + protocol_file, + out_dir.join("wayland_c_api.rs"), + Side::Client, + ); + generate_c_interfaces(protocol_file, out_dir.join("wayland_c_interfaces.rs")); + } } diff --git a/wayland-client/examples/dynamic_globals.rs b/wayland-client/examples/dynamic_globals.rs new file mode 100644 index 00000000000..3d99fbd6367 --- /dev/null +++ b/wayland-client/examples/dynamic_globals.rs @@ -0,0 +1,123 @@ +#[macro_use] +extern crate wayland_client; + +use wayland_client::{Display, GlobalManager}; + +use wayland_client::protocol::wl_display::RequestsTrait; +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, mut event_queue) = Display::connect_to_env().unwrap(); + + // We create a GlobalManager with a callback, that will be + // advertized of any global creation or deletion + let _globals = GlobalManager::new_with_cb( + display.get_registry().unwrap(), + // 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 instanciated + // with version 1 when advertized, 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: Result, _>, ()| { + // here seat is a result, as failure can happen if the server + // advertized an lower version than we requested. + // This cannot happen here as we requested version 1 + let seat = seat.unwrap(); + let mut seat_name = None; + let mut caps = None; + seat.implement(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 caps), &Some(ref name)) = (&caps, &seat_name) { + println!("New seat \"{}\" with capabilities [ {:?} ]", name, caps); + } + }); + } + ], + // Same thing with wl_output, but we require version 2 + [ + wl_output::WlOutput, + 2, + |output: Result, _>, ()| { + let output = output.unwrap(); + let mut name = "".to_owned(); + let mut modes = Vec::new(); + let mut scale = 1; + output.implement(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 + ); + } + } + } + }); + } + ] + ), + ); + + event_queue.sync_roundtrip().unwrap(); + event_queue.sync_roundtrip().unwrap(); + event_queue.sync_roundtrip().unwrap(); +} diff --git a/wayland-client/examples/list_globals.rs b/wayland-client/examples/list_globals.rs new file mode 100644 index 00000000000..71d15a2a58a --- /dev/null +++ b/wayland-client/examples/list_globals.rs @@ -0,0 +1,27 @@ +extern crate wayland_client; + +use wayland_client::{Display, GlobalManager}; + +use wayland_client::protocol::wl_display::RequestsTrait; + +// A minimal example printing the list of globals advertized by the server and +// then exiting + +fn main() { + // Connect to the server + let (display, mut event_queue) = Display::connect_to_env().unwrap(); + + // 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(display.get_registry().unwrap()); + + // A roundtrip synchronization to make sure the server received our registry + // creation and sent us the global list + event_queue.sync_roundtrip().unwrap(); + + // Print the list + for (id, interface, version) in globals.list() { + println!("{}: {} (version {})", id, interface, version); + } +} diff --git a/wayland-client/examples/simple_client.rs b/wayland-client/examples/simple_client.rs deleted file mode 100644 index 1cee57e1163..00000000000 --- a/wayland-client/examples/simple_client.rs +++ /dev/null @@ -1,28 +0,0 @@ -#[macro_use] -extern crate wayland_client; - -use wayland_client::EnvHandler; - -wayland_env!(WaylandEnv); - -fn main() { - let (display, mut event_queue) = match wayland_client::default_connect() { - Ok(ret) => ret, - Err(e) => panic!("Cannot connect to wayland server: {:?}", e), - }; - - let registry = display.get_registry(); - - let env_token = EnvHandler::::init(&mut event_queue, ®istry); - - event_queue.sync_roundtrip().unwrap(); - - let state = event_queue.state(); - - let env = state.get(&env_token); - - println!("Globals advertised by server:"); - for &(name, ref interface, version) in env.globals() { - println!("{:4} : {} (version {})", name, interface, version); - } -} diff --git a/wayland-client/examples/simple_window.rs b/wayland-client/examples/simple_window.rs index 06de8a45255..2b8c6d7de44 100644 --- a/wayland-client/examples/simple_window.rs +++ b/wayland-client/examples/simple_window.rs @@ -1,78 +1,34 @@ -#[macro_use] -extern crate wayland_client; - +extern crate byteorder; extern crate tempfile; +extern crate wayland_client; -extern crate byteorder; -use byteorder::{NativeEndian, WriteBytesExt}; use std::cmp::min; use std::io::Write; use std::os::unix::io::AsRawFd; -use wayland_client::EnvHandler; -use wayland_client::protocol::{wl_compositor, wl_pointer, wl_seat, wl_shell, wl_shell_surface, wl_shm}; - -wayland_env!( - WaylandEnv, - compositor: wl_compositor::WlCompositor, - seat: wl_seat::WlSeat, - shell: wl_shell::WlShell, - shm: wl_shm::WlShm -); - -fn shell_surface_impl() -> wl_shell_surface::Implementation<()> { - wl_shell_surface::Implementation { - ping: |_, _, shell_surface, serial| { - shell_surface.pong(serial); - }, - configure: |_, _, _, _, _, _| { /* not used in this example */ }, - popup_done: |_, _, _| { /* not used in this example */ }, - } -} -fn pointer_impl() -> wl_pointer::Implementation<()> { - wl_pointer::Implementation { - enter: |_, _, _pointer, _serial, _surface, x, y| { - println!("Pointer entered surface at ({},{}).", x, y); - }, - leave: |_, _, _pointer, _serial, _surface| { - println!("Pointer left surface."); - }, - motion: |_, _, _pointer, _time, x, y| { - println!("Pointer moved to ({},{}).", x, y); - }, - button: |_, _, _pointer, _serial, _time, button, state| { - println!( - "Button {} ({}) was {:?}.", - match button { - 272 => "Left", - 273 => "Right", - 274 => "Middle", - _ => "Unknown", - }, - button, - state - ); - }, - axis: |_, _, _, _, _, _| { /* not used in this example */ }, - frame: |_, _, _| { /* not used in this example */ }, - axis_source: |_, _, _, _| { /* not used in this example */ }, - axis_discrete: |_, _, _, _, _| { /* not used in this example */ }, - axis_stop: |_, _, _, _, _| { /* not used in this example */ }, - } -} - -fn main() { - let (display, mut event_queue) = match wayland_client::default_connect() { - Ok(ret) => ret, - Err(e) => panic!("Cannot connect to wayland server: {:?}", e), - }; +use byteorder::{NativeEndian, WriteBytesExt}; - let registry = display.get_registry(); +use wayland_client::{Display, GlobalManager, Proxy}; +use wayland_client::protocol::{wl_compositor, wl_seat, wl_shell, wl_shell_surface, wl_shm}; +use wayland_client::protocol::wl_display::RequestsTrait as DisplayRequests; +use wayland_client::protocol::wl_compositor::RequestsTrait as CompositorRequests; +use wayland_client::protocol::wl_surface::RequestsTrait as SurfaceRequests; +use wayland_client::protocol::wl_shm::RequestsTrait as ShmRequests; +use wayland_client::protocol::wl_shm_pool::RequestsTrait as PoolRequests; +use wayland_client::protocol::wl_shell::RequestsTrait as ShellRequests; +use wayland_client::protocol::wl_shell_surface::RequestsTrait as ShellSurfaceRequests; - let env_token = EnvHandler::::init(&mut event_queue, ®istry); +fn main() { + let (display, mut event_queue) = Display::connect_to_env().unwrap(); + let globals = GlobalManager::new(display.get_registry().unwrap()); + // roundtrip to retrieve the globals list event_queue.sync_roundtrip().unwrap(); + /* + * Create a buffer with window contents + */ + // buffer (and window) width and height let buf_x: u32 = 320; let buf_y: u32 = 240; @@ -92,37 +48,111 @@ fn main() { } let _ = tmp.flush(); - // retrieve the env - let env = event_queue.state().get(&env_token).clone_inner().unwrap(); - - // prepare the wayland surface - let surface = env.compositor.create_surface(); - let shell_surface = env.shell.get_shell_surface(&surface); - - let pool = env.shm - .create_pool(tmp.as_raw_fd(), (buf_x * buf_y * 4) as i32); - // match a buffer on the part we wrote on + /* + * Init wayland objects + */ + + // The compositor allows us to creates surfaces + let compositor = globals + .instanciate_auto::() + .unwrap() + .implement(|_, _| {}); + let surface = compositor.create_surface().unwrap().implement(|_, _| {}); + + // The SHM allows us to share memory with the server, and create buffers + // on this shared memory to paint our surfaces + let shm = globals + .instanciate_auto::() + .unwrap() + .implement(|_, _| {}); + 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) + ).unwrap() + .implement(|_, _| {}); let buffer = pool.create_buffer( - 0, - buf_x as i32, - buf_y as i32, - (buf_x * 4) as i32, - wl_shm::Format::Argb8888, - ).expect("The pool cannot be already dead"); - - // make our surface as a toplevel one + 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 + ).unwrap() + .implement(|_, _| {}); + + // The shell allows us to define our surface as a "toplevel", meaning the + // server will treat it as a window + // + // NOTE: the wl_shell interface is actually deprecated in favour of the xdg_shell + // protocol, available in wayland-protocols. But this will do for this example. + let shell = globals + .instanciate_auto::() + .unwrap() + .implement(|_, _| {}); + let shell_surface = shell.get_shell_surface(&surface).unwrap().implement( + |event, shell_surface: Proxy| { + use wayland_client::protocol::wl_shell_surface::{Event, RequestsTrait}; + // This ping/pong mechanism is used by the wayland server to detect + // unresponsive applications + if let Event::Ping { serial } = event { + shell_surface.pong(serial); + } + }, + ); + + // Set our surface as toplevel and define its contents shell_surface.set_toplevel(); - // attach the buffer to it surface.attach(Some(&buffer), 0, 0); - // commit surface.commit(); - let pointer = env.seat - .get_pointer() - .expect("Seat cannot be already destroyed."); - - event_queue.register(&shell_surface, shell_surface_impl(), ()); - event_queue.register(&pointer, pointer_impl(), ()); + // initialize a seat to retrieve pointer events + // 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 _seat = globals + .instanciate_auto::() + .unwrap() + .implement(move |event, seat: Proxy| { + // 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 here + use wayland_client::protocol::wl_seat::{Capability, Event as SeatEvent, + RequestsTrait as SeatRequests}; + use wayland_client::protocol::wl_pointer::Event as PointerEvent; + + if let SeatEvent::Capabilities { capabilities } = event { + if !pointer_created && capabilities.contains(Capability::Pointer) { + // create the pointer only once + pointer_created = true; + seat.get_pointer() + .unwrap() + .implement(|event, _| match event { + PointerEvent::Enter { + surface_x, + surface_y, + .. + } => { + println!("Pointer entered at ({}, {}).", surface_x, surface_y); + } + PointerEvent::Leave { .. } => { + println!("Pointer left."); + } + PointerEvent::Motion { + surface_x, + surface_y, + .. + } => { + println!("Pointer moved to ({}, {}).", surface_x, surface_y); + } + PointerEvent::Button { button, state, .. } => { + println!("Button {} was {:?}.", button, state); + } + _ => {} + }); + } + } + }); loop { display.flush().unwrap(); diff --git a/wayland-client/src/cursor.rs b/wayland-client/src/cursor.rs index cf9533289f2..d1b4a53b4a8 100644 --- a/wayland-client/src/cursor.rs +++ b/wayland-client/src/cursor.rs @@ -14,7 +14,6 @@ //! displayed at which time, as well as handles to the buffers containing //! these frames, to attach them to a wayland surface. - use Proxy; use protocol::wl_buffer::WlBuffer; use protocol::wl_shm::WlShm; @@ -54,7 +53,7 @@ unsafe impl Send for CursorTheme {} /// in this module. /// - In case of memory allocation failure. /// - If the theme name provided as argument contains an interior null -pub fn load_theme(name: Option<&str>, size: u32, shm: &WlShm) -> CursorTheme { +pub fn load_theme(name: Option<&str>, size: u32, shm: &Proxy) -> CursorTheme { let ptr = if let Some(theme) = name { let cstr = CString::new(theme).expect("Theme name contained an interior null."); unsafe { @@ -63,7 +62,7 @@ pub fn load_theme(name: Option<&str>, size: u32, shm: &WlShm) -> CursorTheme { wl_cursor_theme_load, cstr.as_ptr(), size as c_int, - shm.ptr() + shm.c_ptr() ) } } else { @@ -73,7 +72,7 @@ pub fn load_theme(name: Option<&str>, size: u32, shm: &WlShm) -> CursorTheme { wl_cursor_theme_load, ptr::null(), size as c_int, - shm.ptr() + shm.c_ptr() ) } }; @@ -193,16 +192,7 @@ impl<'a> Cursor<'a> { unsafe { let image = *(*self.cursor).images.offset(frame as isize); let ptr = ffi_dispatch!(WAYLAND_CURSOR_HANDLE, wl_cursor_image_get_buffer, image); - // init the user_data only once - let data = { - use wayland_sys::client::*; - ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, ptr) - }; - let buffer = if data.is_null() { - Proxy::from_ptr_new(ptr) - } else { - Proxy::from_ptr_initialized(ptr) - }; + let buffer = Proxy::from_c_ptr(ptr); Some(CursorImageBuffer { _cursor: PhantomData, @@ -234,16 +224,21 @@ impl<'a> Cursor<'a> { } /// A buffer containing a cursor image. +/// +/// You can access the `Proxy` via `Deref`. +/// +/// Note that this proxy will be considered as "unmanaged" by the crate, as such you should +/// not try to act with it beyong assigning it to `wl_surface`s. pub struct CursorImageBuffer<'a> { _cursor: PhantomData<&'a Cursor<'a>>, - buffer: WlBuffer, + buffer: Proxy, } unsafe impl<'a> Send for CursorImageBuffer<'a> {} impl<'a> Deref for CursorImageBuffer<'a> { - type Target = WlBuffer; - fn deref(&self) -> &WlBuffer { + type Target = Proxy; + fn deref(&self) -> &Proxy { &self.buffer } } diff --git a/wayland-client/src/display.rs b/wayland-client/src/display.rs index 202ce7c07a7..9f7e15ccd6e 100644 --- a/wayland-client/src/display.rs +++ b/wayland-client/src/display.rs @@ -1,9 +1,15 @@ -use Proxy; -use event_queue::{create_event_queue, EventQueue}; -use generated::client::wl_display::WlDisplay; -use std::ffi::{CStr, CString, OsStr}; +#[cfg(feature = "native_lib")] +use std::ffi::{CString, OsString}; use std::io; -use std::os::unix::ffi::OsStrExt; +#[cfg(feature = "native_lib")] +use std::os::unix::ffi::OsStringExt; +use std::sync::Arc; +use std::ops::Deref; + +use Proxy; +use EventQueue; + +#[cfg(feature = "native_lib")] use wayland_sys::client::*; /// Enum representing the possible reasons why connecting to the wayland server failed @@ -17,92 +23,135 @@ pub enum ConnectError { /// /// Most of the time, this means that the program was not started from a wayland session. NoCompositorListening, + /// The provided socket name is invalid + InvalidName, } -/// Enum representing possible errors fatal to a wayland session -/// -/// These errors are fatal, so there is no way to recover the session, you -/// must create a new one (or report failure to your user). But recovering -/// this error can provide usefull debug information and/or help provide -/// a sensible error message to the user. -#[derive(Debug)] -pub enum FatalError { - /// Session aborted after an I/O error - Io(io::Error), - /// Session aborted after a protocol error - Protocol { - /// name of the interface of the proxy that generated this error - interface: String, - /// internal id of the proxy that generated this error - proxy_id: u32, - /// code of the error, as defined by the `Error` enum of the interface of the proxy. - /// It can directly be fed to the `from_raw` static method of this enum. - error_code: u32, - }, +pub(crate) struct DisplayInner { + proxy: Proxy<::protocol::wl_display::WlDisplay>, } -/// Connect to the compositor socket -/// -/// Attempt to connect to a Wayland compositor according to the environment variables. -/// -/// On success, returns the display object, as well as the default event iterator associated with it. -pub fn default_connect() -> Result<(WlDisplay, EventQueue), ConnectError> { - if !::wayland_sys::client::is_lib_available() { - return Err(ConnectError::NoWaylandLib); +impl DisplayInner { + #[cfg(feature = "native_lib")] + pub(crate) fn ptr(&self) -> *mut wl_display { + self.proxy.c_ptr() as *mut _ } - let ptr = unsafe { - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_display_connect, - ::std::ptr::null() - ) - }; - if ptr.is_null() { - Err(ConnectError::NoCompositorListening) - } else { - let display = unsafe { WlDisplay::from_ptr_new(ptr as *mut _) }; - let eventiter = unsafe { create_event_queue(display.ptr() as *mut wl_display, None) }; - Ok((display, eventiter)) +} + +impl Drop for DisplayInner { + fn drop(&mut self) { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!(); + } + #[cfg(feature = "native_lib")] + { + unsafe { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_disconnect, + self.proxy.c_ptr() as *mut wl_display + ); + } + } } } -/// Connect to the compositor socket -/// -/// Attempt to connect to a Wayland compositor on a given socket name +/// A connection to a wayland server /// -/// On success, returns the display object, as well as the default event iterator associated with it. -pub fn connect_to(name: &OsStr) -> Result<(WlDisplay, EventQueue), ConnectError> { - if !::wayland_sys::client::is_lib_available() { - return Err(ConnectError::NoWaylandLib); +/// This object both represent the connection to the server, and as such +/// must be kept alive as long as you are connected, and contains the +/// primary `WlDisplay` wayland object, from which you can create all +/// your need objects. The inner `Proxy` can be accessed via +/// `Deref`. +pub struct Display { + inner: Arc, +} + +impl Display { + #[cfg(feature = "native_lib")] + unsafe fn make_display(ptr: *mut wl_display) -> Result<(Display, EventQueue), ConnectError> { + if ptr.is_null() { + return Err(ConnectError::NoCompositorListening); + } + + let display = Display { + inner: Arc::new(DisplayInner { + proxy: Proxy::from_display(ptr), + }), + }; + + let evq = EventQueue::new(display.inner.clone(), None); + + Ok((display, evq)) + } + + /// Attempt to connect to a wayland server using the contents of the environment variables + /// + /// If the `WAYLAND_DISPLAY` variable is set, it will try to connect to the socket it points + /// to. Otherwise, it will default to `wayland-0`. + /// + /// 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_env() -> Result<(Display, EventQueue), ConnectError> { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + unsafe { + let display_ptr = ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_connect, + ::std::ptr::null() + ); + + Display::make_display(display_ptr) + } + } } - // Only possible error is interior null, and in this case, no compositor will be listening to a socket - // with null in its name. - let name = CString::new(name.as_bytes().to_owned()).map_err(|_| ConnectError::NoCompositorListening)?; - let ptr = unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_connect, name.as_ptr()) }; - if ptr.is_null() { - Err(ConnectError::NoCompositorListening) - } else { - let display = unsafe { WlDisplay::from_ptr_new(ptr as *mut _) }; - let eventiter = unsafe { create_event_queue(display.ptr() as *mut wl_display, None) }; - Ok((display, eventiter)) + + /// 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>(name: S) -> Result<(Display, EventQueue), ConnectError> { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + if !::wayland_sys::client::is_lib_available() { + return Err(ConnectError::NoWaylandLib); + } + + let name = CString::new(name.into().into_vec()).map_err(|_| ConnectError::InvalidName)?; + + unsafe { + let display_ptr = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_connect, name.as_ptr()); + + Display::make_display(display_ptr) + } + } } -} -impl WlDisplay { /// 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 coul be written, will return an io error `WouldBlock`. /// /// On success returns the number of written requests. pub fn flush(&self) -> io::Result { - let ret = unsafe { - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_display_flush, - self.ptr() as *mut _ - ) - }; + let ret = unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_flush, self.inner.ptr()) }; if ret >= 0 { Ok(ret) } else { @@ -110,93 +159,28 @@ impl WlDisplay { } } - /// Create a new EventQueue - /// - /// No object is by default attached to it. + /// Create a new event queue associated with this wayland connection pub fn create_event_queue(&self) -> EventQueue { - let evq = unsafe { - ffi_dispatch!( + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + unsafe { + let ptr = ffi_dispatch!( WAYLAND_CLIENT_HANDLE, wl_display_create_queue, - self.ptr() as *mut _ - ) - }; - unsafe { create_event_queue(self.ptr() as *mut _, Some(evq)) } - } + self.inner.ptr() + ); - /// Get the last error that occured on the session - /// - /// Such errors are *fatal*, meaning that if this function does not - /// return `None`, your session is not usable any longer. - /// - /// As such, this function mostly provide diagnistics information. You can have a hint - /// an error might have been generated if I/O methods of EventQueue start returning errors. - pub fn last_error(&self) -> Option { - let err = unsafe { - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_display_get_error, - self.ptr() as *mut _ - ) - }; - if err == 0 { - None - } else if err == ::libc::EPROTO { - 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() as *mut _, - &mut interface, - &mut id - ) - }; - let interface = if interface.is_null() { - "".to_owned() - } else { - unsafe { CStr::from_ptr((*interface).name) } - .to_string_lossy() - .into_owned() - }; - Some(FatalError::Protocol { - interface: interface, - proxy_id: id, - error_code: code, - }) - } else { - Some(FatalError::Io(io::Error::from_raw_os_error(err))) + EventQueue::new(self.inner.clone(), Some(ptr)) } } +} - /// Get the raw File Descriptor associated with the connection - /// - /// This is provided to be used in conjunction with some polling mecanism, - /// if you want to manually control the flow with something like `epoll`. - /// In this case, you'll most likely want to use `EventQueue::prepare_read()` and - /// `EventQueue::dispatch_pending()` rather than `EventQueue::dispatch()`. - /// - /// Reading or writing anything to this FD will corrupt the internal state of - /// the lib. - pub unsafe fn get_fd(&self) -> ::std::os::unix::io::RawFd { - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_display_get_fd, - self.ptr() as *mut _ - ) - } - - /// Close the wayland connection - /// - /// This is unsafe because you must ensure you do this only - /// after all wayland objects are destroyed, as this will - /// release the wayland shared state. - pub unsafe fn disconnect(self) { - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_display_disconnect, - self.ptr() as *mut _ - ) +impl Deref for Display { + type Target = Proxy<::protocol::wl_display::WlDisplay>; + fn deref(&self) -> &Proxy<::protocol::wl_display::WlDisplay> { + &self.inner.proxy } } diff --git a/wayland-client/src/egl.rs b/wayland-client/src/egl.rs index a8eb1a894a6..24476a33a80 100644 --- a/wayland-client/src/egl.rs +++ b/wayland-client/src/egl.rs @@ -37,8 +37,8 @@ pub struct WlEglSurface { impl WlEglSurface { /// Create an EGL surface from a wayland surface - pub fn new(surface: &WlSurface, width: i32, height: i32) -> WlEglSurface { - unsafe { WlEglSurface::new_from_raw(surface.ptr(), width, height) } + pub fn new(surface: &Proxy, width: i32, height: i32) -> WlEglSurface { + unsafe { WlEglSurface::new_from_raw(surface.c_ptr(), width, height) } } /// Create an EGL surface from a raw pointer to a wayland surface diff --git a/wayland-client/src/env.rs b/wayland-client/src/env.rs deleted file mode 100644 index 2d390afe665..00000000000 --- a/wayland-client/src/env.rs +++ /dev/null @@ -1,350 +0,0 @@ -use {EventQueueHandle, StateToken}; -use protocol::wl_registry::{Implementation, WlRegistry}; - -#[doc(hidden)] -pub trait EnvHandlerInner: Sized { - fn create(&WlRegistry, &[(u32, String, u32)]) -> Option; - fn clone_env(&self) -> Self; -} - -/// Utility type to handle the registry and global objects -/// -/// This struct provides you with a generic handler for the `wl_registry` -/// and the instanciation of global objects. -/// -/// To use it, you need to declare your globals of interest using the -/// `wayland_env!(..)` macro. Then, insert this handler in your event loop and -/// register your registry to it. -/// -/// Once this handler is fully initialized (and all globals have been -/// instantiated), it makes them usable via deref-ing towards the struct -/// previously declared by the `wayland_env!(...)` macro. -/// -/// The `globals()` method also give you a list of all globals declared by -/// the server, had them been instantiated or not. It is perfectly safe -/// to instantiate again a global that have already been instantiated. -/// -/// This list is updated whenever the server declares or removes a global object, -/// (as long as you don't change the handler associated to your registry). -/// -/// If you want to manage all you globals manually, but still want to use -/// this utility to maintain the list of evailable globals, you can simply -/// create an empty env type using the macro, like this : `wayland_env!(WaylandEnv)`. -/// No global will be automatically instantiated for you, but you still can use -/// this `globals()` method. -/// -/// ## example of use -/// -/// ```ignore -/// // Declare your globals of interest using the macro. -/// // This creates a struct named WaylandEnv (but you can change it). -/// wayland_env!(WaylandEnv, compositor: wl_compositor::WlCompositor); -/// -/// let registry = display.get_registry(); -/// let env_token = EnvHandler::::init(&mut event_queue, ®istry); -/// // a roundtrip sync will dispatch all event declaring globals to the handler -/// eventqueue.sync_roundtrip().unwrap(); -/// // then, we can access them via the state of the event queue: -/// let state = eventqueue.state(); -/// let env = state.get(&env_token); -/// // We can now access the globals as fields of env. -/// // Note that is some globals were missing, this acces (via Deref) -/// // will panic! -/// let compositor = env.compositor; -/// ``` -pub struct EnvHandler { - globals: Vec<(u32, String, u32)>, - inner: Option, -} - -impl EnvHandler { - /// Insert a new EnvHandler in this event queue and register the registry to it - pub fn init(evqh: &mut EventQueueHandle, registry: &WlRegistry) -> StateToken> { - let token = { - let state = evqh.state(); - state.insert(EnvHandler { - globals: Vec::new(), - inner: None, - }) - }; - evqh.register(registry, env_handler_impl(), token.clone()); - token - } - - /// Insert a new EnvHandler in this event queue with a notify implementation - /// - /// Does the same as `EnvHandler::init(..)`, but you additionnaly supply an implementation - /// struct that the EnvHandler will use to notify you of events: - /// - /// - events of creation/deletion of a global are forwarded - /// - event of readiness of this EnvHandler (when all the necessary globals could be bound) - pub fn init_with_notify(evqh: &mut EventQueueHandle, registry: &WlRegistry, - notify: EnvNotify, idata: ID) - -> StateToken> { - let token = { - let state = evqh.state(); - state.insert(EnvHandler { - globals: Vec::new(), - inner: None, - }) - }; - evqh.register( - registry, - env_handler_impl_notify(), - (token.clone(), (notify, idata)), - ); - token - } - - /// Is the handler ready - /// - /// Returns true if all required globals have been created. - /// - /// If this method returns false, trying to access a global - /// field will panic. - pub fn ready(&self) -> bool { - self.inner.is_some() - } - - /// List of advertised globals - /// - /// Returns a list of all globals that have been advertised by the server. - /// - /// The type format of each tuple is: `(global_id, interface_name, global_version)`. - pub fn globals(&self) -> &[(u32, String, u32)] { - &self.globals - } - - /// Retrieve an owned copy of the environment - /// - /// This clones the inner env so that you can have access to the - /// clobals without borrowing the event queue - pub fn clone_inner(&self) -> Option { - self.inner.as_ref().map(|h| h.clone_env()) - } - - /// Returns true if the env became ready after this call - fn try_make_ready(&mut self, registry: &WlRegistry) -> bool { - if self.inner.is_some() { - return false; - } - self.inner = H::create(registry, &self.globals); - self.ready() - } -} - -impl ::std::ops::Deref for EnvHandler { - type Target = H; - fn deref(&self) -> &H { - self.inner - .as_ref() - .expect("Tried to get contents of a not-ready EnvHandler.") - } -} - -fn env_handler_impl() -> Implementation>> { - Implementation { - global: |evqh, token, registry, name, interface, version| { - let state = evqh.state(); - let me = state.get_mut(token); - me.globals.push((name, interface, version)); - me.try_make_ready(registry); - }, - global_remove: |evqh, token, _, name| { - let state = evqh.state(); - let me = state.get_mut(token); - me.globals.retain(|&(i, _, _)| i != name) - }, - } -} - -fn env_handler_impl_notify( - ) - -> Implementation<(StateToken>, (EnvNotify, ID))> -{ - Implementation { - global: |evqh, &mut (ref token, (ref implem, ref mut idata)), registry, name, interface, version| { - (implem.new_global)(evqh, idata, registry, name, &interface, version); - let became_ready = { - let state = evqh.state(); - let me = state.get_mut(token); - me.globals.push((name, interface, version)); - me.try_make_ready(registry) - }; - if became_ready { - (implem.ready)(evqh, idata, registry); - } - }, - global_remove: |evqh, &mut (ref token, (ref implem, ref mut idata)), registry, name| { - (implem.del_global)(evqh, idata, registry, name); - let state = evqh.state(); - let me = state.get_mut(token); - me.globals.retain(|&(i, _, _)| i != name); - }, - } -} - -/// An implementation to receive globals notifications for the EnvHandler -/// -/// You can provide this implementation to have the EnvHandler notify you -/// about new globals in addition to processing them. -/// -/// See `EnvHandler::init_with_notify(..)`. -pub struct EnvNotify { - /// A new global was advertized by the server - /// - /// Arguments are: - /// - /// - The `&mut EventQueueHandle` - /// - A mutable reference to the implementation data you provided - /// - A handle to the wayland registry - /// - the id of this new global - /// - the interface of this global - /// - the version of this global - pub new_global: fn( - evqh: &mut EventQueueHandle, - idata: &mut ID, - registry: &WlRegistry, - id: u32, - interface: &str, - version: u32, - ), - /// A global was removed by the server - /// - /// Arguments are: - /// - /// - The `&mut EventQueueHandle` - /// - A mutable reference to the implementation data you provided - /// - A handle to the wayland registry - /// - the id of the removed global - pub del_global: fn(evqh: &mut EventQueueHandle, idata: &mut ID, registry: &WlRegistry, id: u32), - /// The EnvHandler is ready - /// - /// This is called once all necessary globals defined with the `wayland_env!()` - /// macro were advertized and instanciated. - /// - /// Arguments are: - /// - /// - The `&mut EventQueueHandle` - /// - A mutable reference to the implementation data you provided - /// - A handle to the wayland registry - pub ready: fn(evqh: &mut EventQueueHandle, idata: &mut ID, registry: &WlRegistry), -} - -/// Create an environment handling struct -/// -/// To be used in conjunction with the `EnvHandler` utility. -/// -/// Declare the globals your application needs to use, like this, following the -/// general pattern: `$name : $type`: -/// -/// ```ignore -/// use wayland_client::protocol::{Wl_compositor,wl_shell}; -/// -/// wayland_env!(WaylandEnv, -/// compositor: wl_compositor::WlCompositor, -/// shell : wl_shell::WlShell -/// ); -/// ``` -/// -/// `$name` (`compositor` and `shell` in this example) are the name of the -/// fields that will contain the global objects one initialisation is done. -/// `$type` must be a wayland object type, implementing the `Proxy` trait. -/// -/// If more than one field with a given type are provided, the handler will expect -/// the server to declare as many global objects of given type. If more globals of -/// a given type are declared by the server than in this macro, only the first `N` -/// will be bound in the environment struct. -/// -/// This utility will interpret all globals declared in this macro as _necessary_, and -/// thus will not give you access to anything util they have all been declared by the compositor. -/// As such, only declare globals that your application cannot run without, like probably -/// `wl_compositor`, `wl_shm` or `wl_seat`. If there are globals that you can optionnaly -/// use, you'll have to instantiate them manually via `WlRegistry::bind(..)`. -#[macro_export] -macro_rules! wayland_env( - (pub $name: ident) => { - pub struct $name; - impl $crate::EnvHandlerInner for $name { - fn create(_registry: &$crate::protocol::wl_registry::WlRegistry, _globals: &[(u32, String, u32)]) -> Option<$name> { - Some($name) - } - fn clone_env(&self) -> $name { - $name - } - } - }; - (pub $name: ident, $($global_name: ident : $global_type: path),+) => { - pub struct $name { - $( - pub $global_name: $global_type - ),+ - } - wayland_env!(__impl $name, $($global_name : $global_type),+); - }; - ($name: ident) => { - struct $name; - impl $crate::EnvHandlerInner for $name { - fn create(_registry: &$crate::protocol::wl_registry::WlRegistry, _globals: &[(u32, String, u32)]) -> Option<$name> { - Some($name) - } - fn clone_env(&self) -> $name { - $name - } - } - }; - ($name: ident, $($global_name: ident : $global_type: path),+) => { - struct $name { - $( - pub $global_name: $global_type - ),+ - } - wayland_env!(__impl $name, $($global_name : $global_type),+); - }; - (__impl $name: ident, $($global_name: ident : $global_type: path),+) => { - impl $crate::EnvHandlerInner for $name { - fn create(registry: &$crate::protocol::wl_registry::WlRegistry, globals: &[(u32, String, u32)]) -> Option<$name> { - // hopefully llvm will optimize this - let mut need = 0usize; - $( - let _ = stringify!($global_name); - need += 1; - )+ - if need > globals.len() { return None } - - let mut my_globals: Vec<(u32, &str, u32)> = globals.iter().map(|&(i,ref n,v)| (i,&n[..],v)).collect(); - - $( - let $global_name = { - let iname = <$global_type as $crate::Proxy>::interface_name(); - let index = my_globals.iter().position(|&(_,name,_)| name == iname); - match index { - None => return None, - Some(i) => my_globals.swap_remove(i) - } - }; - )* - $( - let $global_name = { - let (id, _, v) = $global_name; - let version = ::std::cmp::min(v, <$global_type as $crate::Proxy>::supported_version()); - registry.bind::<$global_type>(version, id) - }; - )+ - Some($name { - $( - $global_name: $global_name - ),+ - }) - } - - fn clone_env(&self) -> $name { - $name { - $( - $global_name: $crate::Proxy::clone(&self.$global_name).unwrap() - ),+ - } - } - } - }; -); diff --git a/wayland-client/src/event_queue.rs b/wayland-client/src/event_queue.rs index d2d42b45562..4d46d2edde4 100644 --- a/wayland-client/src/event_queue.rs +++ b/wayland-client/src/event_queue.rs @@ -1,136 +1,80 @@ -use {Implementable, Proxy}; -use std::any::Any; use std::io::{Error as IoError, Result as IoResult}; -use std::io::Write; -use std::ops::{Deref, DerefMut}; -use std::os::raw::{c_int, c_void}; +use std::ptr; +use std::rc::Rc; use std::sync::Arc; -use std::sync::atomic::{AtomicBool, AtomicPtr}; -pub use token_store::{Store as State, StoreProxy as StateProxy, Token as StateToken}; -use wayland_sys::RUST_MANAGED; -use wayland_sys::client::*; -use wayland_sys::common::*; -type ProxyUserData = ( - *mut EventQueueHandle, - Option>, - Arc<(AtomicBool, AtomicPtr<()>)>, -); +use display::DisplayInner; -/// Status of a registration attempt of a proxy. -pub enum RegisterStatus { - /// The proxy was properly registered to this event queue & handler. - Registered, - /// The proxy was not registered because it is not managed by `wayland-client`. - Unmanaged, - /// The proxy was not registered because it is already destroyed. - Dead, -} +#[cfg(feature = "native_lib")] +use wayland_sys::client::*; -/// Handle to an event queue -/// -/// This handle gives you access to methods on an event queue -/// that are safe to do from within a callback. -/// -/// They are also available on an `EventQueue` object via `Deref`. -pub struct EventQueueHandle { - state: State, +struct EventQueueInner { + #[cfg(feature = "native_lib")] wlevq: Option<*mut wl_event_queue>, + inner: Arc, } -impl EventQueueHandle { - /// Register a proxy to this event queue. - /// - /// You are required to provide a valid implementation for this proxy - /// as well as some associated implementation data. This implementation - /// is expected to be a struct holding the various relevant - /// function pointers. - /// - /// This implementation data can typically contain indexes to state value - /// that the implementation will need to work on. - /// - /// This overwrites any precedently set implementation for this proxy. - /// - /// Returns appropriately and does nothing if this proxy is dead or already managed by - /// something else than this library. - pub fn register(&mut self, proxy: &P, implementation: P::Implementation, idata: ID) - -> RegisterStatus - where - P: Proxy + Implementable, - ID: 'static, - { - match proxy.status() { - ::Liveness::Dead => return RegisterStatus::Dead, - ::Liveness::Unmanaged => return RegisterStatus::Unmanaged, - ::Liveness::Alive => { /* ok, we can continue */ } - } - - unsafe { - let data: *mut ProxyUserData = - ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, proxy.ptr()) as *mut _; - // This cast from *const to *mut is legit because we enforce that a proxy - // can only be assigned to a single EventQueue. - // (this is actually the whole point of the design of this lib) - (&mut *data).0 = self as *const _ as *mut _; - (&mut *data).1 = Some(Box::new((implementation, idata)) as Box); - // even if this call fails, we updated the user_data, so the new implementation is in place. - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_proxy_add_dispatcher, - proxy.ptr(), - dispatch_func::, - &RUST_MANAGED as *const _ as *const _, - data as *mut c_void - ); - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_proxy_set_queue, - proxy.ptr(), - match self.wlevq { - Some(ptr) => ptr, - None => ::std::ptr::null_mut(), - } - ); - } - RegisterStatus::Registered - } - - /// Get a handle to the internal state - /// - /// The returned guard object allows you to get references - /// to the handler objects you previously inserted in this - /// event queue. - pub fn state(&mut self) -> &mut State { - &mut self.state - } -} - -/// An event queue managing wayland events +/// An event queue for protocol messages /// -/// Each wayland object can receive events from the server. To handle these events -/// you must associate to these objects an implementation: a struct defined in their -/// respective module, in which you provide a set of functions that will handle each event. +/// Event dispatching in wayland is made on a queue basis, allowing you +/// to organise your objects into different queues that can be dispatched +/// independently, for example from different threads. /// -/// Your implementation can also access a shared state managed by the event queue. See -/// the `State` struct and the `state()` method on `EventQueueHandle`. If you need this, -/// the way to do it is: +/// And `EventQueue` is not `Send`, and thus must stay on the thread on which +/// they were created. However the `Display` object is `Send + Sync`, allowing +/// you to create the queues directly in the threads that host them. /// -/// - insert your state value in the event queue state store, your are then provided with a -/// token to access it -/// - provide this token (you can clone it) as implementation data to any wayland object -/// that need to access this state in its event callbacks. +/// When a queue is dispatched (via the `dispatch()` or `dispatch_pending()` methods) +/// all the incoming messages from the server destinated 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. /// -/// The event queues also provides you control on the flow of the program, via the `dispatch()` and -/// `dispatch_pending()` methods. +/// Thus, a typical single-queue event loop for a simple wayland app can be: +/// +/// ```no_run +/// # extern crate wayland_client; +/// # use wayland_client::{Display}; +/// # fn main() { +/// # let (display, mut event_queue) = Display::connect_to_env().unwrap(); +/// loop { +/// display.flush().unwrap(); +/// event_queue.dispatch().expect("An error occured during event dispatching!"); +/// } +/// # } +/// ``` +/// +/// See `EventQueue::prepare_read()` if you need more control about when the connection +/// socket is read. This will typically the case if you need to integrate other sources +/// of event into the event loop of your application. pub struct EventQueue { - handle: Box, - display: *mut wl_display, + // EventQueue is *not* Send + inner: Rc, +} + +/// 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::make_wrapper` and `NewProxy::implement_nonsend`. +pub struct QueueToken { + inner: Rc, } impl EventQueue { + #[cfg(feature = "native_lib")] + pub(crate) unsafe fn new(inner: Arc, evq: Option<*mut wl_event_queue>) -> EventQueue { + EventQueue { + inner: Rc::new(EventQueueInner { + inner: inner, + wlevq: evq, + }), + } + } + /// Dispatches events from the internal buffer. /// - /// Dispatches all events to their appropriate handlers. + /// Dispatches all events to their appropriaters. /// If not 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 @@ -139,54 +83,70 @@ impl EventQueue { /// If an error is returned, your connection with the wayland /// compositor is probably lost. pub fn dispatch(&mut self) -> IoResult { - let ret = match self.handle.wlevq { - Some(evq) => unsafe { - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_display_dispatch_queue, - self.display, - evq - ) - }, - None => unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_dispatch, self.display) }, - }; - if ret >= 0 { - Ok(ret as u32) - } else { - Err(IoError::last_os_error()) + #[cfg(not(feature = "native_lib"))] + {} + #[cfg(feature = "native_lib")] + { + let ret = match self.inner.wlevq { + Some(evq) => unsafe { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_dispatch_queue, + self.inner.inner.ptr(), + evq + ) + }, + None => unsafe { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_dispatch, + self.inner.inner.ptr() + ) + }, + }; + if ret >= 0 { + Ok(ret as u32) + } else { + Err(IoError::last_os_error()) + } } } /// Dispatches pending events from the internal buffer. /// - /// Dispatches all events to their appropriate handlers. + /// Dispatches all events to their appropriaters. /// Never blocks, if not events were pending, simply returns /// `Ok(0)`. /// /// If an error is returned, your connection with the wayland /// compositor is probably lost. pub fn dispatch_pending(&mut self) -> IoResult { - let ret = match self.handle.wlevq { - Some(evq) => unsafe { - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_display_dispatch_queue_pending, - self.display, - evq - ) - }, - None => unsafe { - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_display_dispatch_pending, - self.display - ) - }, - }; - if ret >= 0 { - Ok(ret as u32) - } else { - Err(IoError::last_os_error()) + #[cfg(not(feature = "native_lib"))] + {} + #[cfg(feature = "native_lib")] + { + let ret = match self.inner.wlevq { + Some(evq) => unsafe { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_dispatch_queue_pending, + self.inner.inner.ptr(), + evq + ) + }, + None => unsafe { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_dispatch_pending, + self.inner.inner.ptr() + ) + }, + }; + if ret >= 0 { + Ok(ret as u32) + } else { + Err(IoError::last_os_error()) + } } } @@ -200,21 +160,39 @@ impl EventQueue { /// /// On success returns the number of dispatched events. pub fn sync_roundtrip(&mut self) -> IoResult { - let ret = unsafe { - match self.handle.wlevq { - Some(evtq) => ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_display_roundtrip_queue, - self.display, - evtq - ), - None => ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_roundtrip, self.display), + #[cfg(not(feature = "native_lib"))] + {} + #[cfg(feature = "native_lib")] + { + let ret = unsafe { + match self.inner.wlevq { + Some(evtq) => ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_roundtrip_queue, + self.inner.inner.ptr(), + evtq + ), + None => ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_roundtrip, + self.inner.inner.ptr() + ), + } + }; + if ret >= 0 { + Ok(ret) + } else { + Err(IoError::last_os_error()) } - }; - if ret >= 0 { - Ok(ret) - } else { - Err(IoError::last_os_error()) + } + } + + /// Create a new token associated with this event queue + /// + /// See `QueueToken` documentation for its use. + pub fn get_token(&self) -> QueueToken { + QueueToken { + inner: self.inner.clone(), } } @@ -239,19 +217,23 @@ impl EventQueue { /// an io error `WouldBlock` in such cases. pub fn prepare_read(&self) -> Option { let ret = unsafe { - match self.handle.wlevq { + match self.inner.wlevq { Some(evtq) => ffi_dispatch!( WAYLAND_CLIENT_HANDLE, wl_display_prepare_read_queue, - self.display, + self.inner.inner.ptr(), evtq ), - None => ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_prepare_read, self.display), + None => ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_prepare_read, + self.inner.inner.ptr() + ), } }; if ret >= 0 { Some(ReadEventsGuard { - display: self.display, + inner: self.inner.clone(), }) } else { None @@ -259,16 +241,32 @@ impl EventQueue { } } -impl Deref for EventQueue { - type Target = EventQueueHandle; - fn deref(&self) -> &EventQueueHandle { - &*self.handle +impl QueueToken { + pub(crate) unsafe fn assign_proxy(&self, proxy: *mut wl_proxy) { + #[cfg(not(feature = "native_lib"))] + {} + #[cfg(feature = "native_lib")] + { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_proxy_set_queue, + proxy, + self.inner.wlevq.unwrap_or(ptr::null_mut()) + ) + } } } -impl DerefMut for EventQueue { - fn deref_mut(&mut self) -> &mut EventQueueHandle { - &mut *self.handle +impl Drop for EventQueueInner { + fn drop(&mut self) { + #[cfg(feature = "nativel_lib")] + { + if let Some(evq) = self.wlevq { + unsafe { + ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_event_queue_destroy, evq); + } + } + } } } @@ -276,7 +274,7 @@ impl DerefMut for EventQueue { /// /// See `EventQueue::prepare_read()` for details about its use. pub struct ReadEventsGuard { - display: *mut wl_display, + inner: Rc, } impl ReadEventsGuard { @@ -285,7 +283,13 @@ impl ReadEventsGuard { /// Reads events from the server socket. If other `ReadEventsGuard` exists, will block /// until they are all consumed or destroyed. pub fn read_events(self) -> IoResult { - let ret = unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_read_events, self.display) }; + let ret = unsafe { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_read_events, + self.inner.inner.ptr() + ) + }; // Don't run destructor that would cancel the read intent ::std::mem::forget(self); if ret >= 0 { @@ -307,61 +311,12 @@ impl ReadEventsGuard { impl Drop for ReadEventsGuard { fn drop(&mut self) { - unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_cancel_read, self.display) } - } -} - -pub unsafe fn create_event_queue(display: *mut wl_display, evq: Option<*mut wl_event_queue>) -> EventQueue { - EventQueue { - display: display, - handle: Box::new(EventQueueHandle { - state: State::new(), - wlevq: evq, - }), - } -} - -unsafe extern "C" fn dispatch_func(_impl: *const c_void, proxy: *mut c_void, opcode: u32, - _msg: *const wl_message, args: *const wl_argument) - -> c_int -where - P: Proxy + Implementable, - ID: 'static, -{ - // sanity check, if it triggers, it is a bug - if _impl != &RUST_MANAGED as *const _ as *const _ { - let _ = write!( - ::std::io::stderr(), - "[wayland-client error] Dispatcher got called for a message on a non-managed object." - ); - ::libc::abort(); - } - // 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 proxy = P::from_ptr_initialized(proxy as *mut wl_proxy); - proxy.__dispatch_msg(opcode, args) - }); - match ret { - Ok(Ok(())) => return 0, // all went well - Ok(Err(())) => { - // an unknown opcode was dispatched, this is not normal - let _ = write!( - ::std::io::stderr(), - "[wayland-client error] Attempted to dispatch unknown opcode {} for {}, aborting.", - opcode, - P::interface_name() - ); - ::libc::abort(); - } - Err(_) => { - // a panic occured - let _ = write!( - ::std::io::stderr(), - "[wayland-client error] A handler for {} panicked, aborting.", - P::interface_name() - ); - ::libc::abort(); + unsafe { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_cancel_read, + self.inner.inner.ptr() + ) } } } diff --git a/wayland-client/src/globals.rs b/wayland-client/src/globals.rs new file mode 100644 index 00000000000..86f777a3b0c --- /dev/null +++ b/wayland-client/src/globals.rs @@ -0,0 +1,284 @@ +use std::sync::{Arc, Mutex}; + +use commons::{Implementation, Interface}; +use {NewProxy, Proxy}; +use protocol::wl_registry::{self, RequestsTrait}; + +struct Inner { + list: Vec<(u32, String, u32)>, + callback: Box, GlobalEvent> + Send>, +} + +/// 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. +pub struct GlobalManager { + inner: Arc>, + registry: Proxy, +} + +/// An error that occured trying to bind a global +#[derive(Debug)] +pub enum GlobalError { + /// The requested global was missing + Missing, + /// The global abvertized by the server has a lower version number + /// than the one requested + VersionTooLow(u32), +} + +/// Event provided to the user callback of GlobalManager +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 + pub fn new(registry: NewProxy) -> GlobalManager { + let inner = Arc::new(Mutex::new(Inner { + list: Vec::new(), + callback: Box::new(|_, _| {}), + })); + let inner_clone = inner.clone(); + + let registry = registry.implement(move |msg, _proxy| { + 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: 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. + pub fn new_with_cb(registry: NewProxy, callback: Impl) -> GlobalManager + where + Impl: Implementation, GlobalEvent> + Send + 'static, + { + let inner = Arc::new(Mutex::new(Inner { + list: Vec::new(), + callback: Box::new(callback), + })); + let inner_clone = inner.clone(); + + let registry = registry.implement(move |msg, proxy| { + let mut inner = inner.lock().unwrap(); + match msg { + wl_registry::Event::Global { + name, + interface, + version, + } => { + inner.list.push((name, interface.clone(), version)); + inner.callback.receive( + GlobalEvent::New { + id: name, + interface: interface, + version: version, + }, + proxy, + ); + } + 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); + inner.callback.receive( + GlobalEvent::Removed { + id: id, + interface: interface, + }, + proxy, + ); + } else { + panic!( + "Wayland protocol error: the server removed non-existing global \"{}\".", + name + ); + } + } + } + }); + + GlobalManager { + inner: inner_clone, + registry: registry, + } + } + + /// Instanciate a global with highest available version + /// + /// This method is only appropriate for globals that are expected to + /// not exist with multiplicity (sur as `wl_compositor` or `wl_shm`), + /// as it will only bind a single one. + pub fn instanciate_auto(&self) -> Result, GlobalError> { + let inner = self.inner.lock().unwrap(); + for &(id, ref interface, version) in &inner.list { + if interface == I::NAME { + return Ok(self.registry.bind::(version, id).unwrap()); + } + } + Err(GlobalError::Missing) + } + + /// Instanciate a global with a specific version + /// + /// Like `instanciate_auto`, but will bind a specific version of + /// this global an not the highest available. + pub fn instanciate_exact(&self, version: u32) -> Result, GlobalError> { + 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::(version, id).unwrap()); + } + } + } + Err(GlobalError::Missing) + } + + /// Retrieve the list of currently known globals + pub fn list(&self) -> Vec<(u32, String, u32)> { + self.inner.lock().unwrap().list.clone() + } +} + +/// 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 advertized 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, NewProxy}; +/// use wayland_client::protocol::wl_display::RequestsTrait; +/// use wayland_client::protocol::{wl_output, wl_seat}; +/// +/// # fn main() { +/// # let (display, mut event_queue) = Display::connect_to_env().unwrap(); +/// # let seat_callback: fn(Result,_>, ()) = unimplemented!(); +/// # let output_callback: fn(Result,_>, ()) = unimplemented!(); +/// let globals = GlobalManager::new_with_cb( +/// display.get_registry().unwrap(), +/// global_filter!( +/// // Bind all wl_seat with version 4 +/// [wl_seat::WlSeat, 4, seat_callback], +/// // Bind all wl_output with version 1 +/// [wl_output::WlOutput, 1, output_callback] +/// ) +/// ); +/// # } +/// ``` +/// +/// The supplied callbacks for each global kind must be an instance of a type +/// implementing the `Interface, u32>, ()>` trait. The +/// argument provided to your callback is a result containing the `NewProxy` +/// of the newly instanciated global on success. The error case happens if the +/// server advertized a lower version of the global than the one you requested, +/// in which case you are given the version it advertized. +/// +/// As with all implementations, you can also provide closures for the various +/// callbacks. 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 +/// +/// ```ignore +/// global_filer!( +/// [Interface, version, |new_proxy: Result, _>, ()| { +/// ... +/// }] +/// ); +/// ``` +#[macro_export] +macro_rules! global_filter { + ($([$interface:ty, $version:expr, $callback:expr]),*) => { + { + use $crate::commons::{Implementation, Interface}; + use $crate::protocol::wl_registry::{self, RequestsTrait}; + use $crate::{Proxy, GlobalEvent, NewProxy}; + type Callback = Box, (u32, u32)> + Send>; + let mut callbacks: Vec<(&'static str, Callback)> = Vec::new(); + // Create the callback list + $({ + let mut cb = { $callback }; + fn typecheck, u32>>>(_: &Impl) {} + typecheck(&cb); + callbacks.push(( + <$interface as Interface>::NAME, + Box::new(move |(id, version), registry: Proxy| { + if version < $version { + cb.receive(Err(version), ()) + } else { + cb.receive(Ok(registry.bind::<$interface>(version, id).unwrap()), ()) + } + }) as Box<_> + )); + })* + + // return the global closure + move |event: GlobalEvent, registry: Proxy| { + if let GlobalEvent::New { id, interface, version } = event { + for &mut (iface, ref mut cb) in &mut callbacks { + if iface == interface { + cb.receive((id, version), registry); + break; + } + } + } + } + } + } +} diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index 3a817884b70..6aa9717c0fd 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -1,232 +1,173 @@ //! Client-side Wayland connector //! -//! # Overview +//! ## Overview +//! +//! This crate provides the interfaces and machinery to safely create +//! client applications for the wayland protocol. It is a rust wrapper +//! around the `libwayland-client.so` C library. +//! +//! 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 `Proxy<_>` objects, receiving +//! and handling them is done by providing implementations. +//! +//! ### Proxies +//! +//! Wayland protocol objects are represented in this crate by `Proxy` +//! objects, where `I` is a type representing the interface of the considered +//! object. And object's interface (think "class" in an object-oriented context) +//! defines which messages it can send and receive. +//! +//! These proxies are used to send messages to the server (in the wayland context, +//! these are called "requests"). To do so, you need to import the appropriate +//! extension trait adding these methods. For example, to use a `Proxy`, +//! you need to import `protocol::wl_surface::RequestsTrait` from this crate. +//! It is also possible to directly use the `Proxy::::send(..)` method, but +//! this should only be done carefully: using it improperly can mess the protocol +//! state and cause protocol errors, which are fatal to the connection (the server +//! will kill you). +//! +//! There is not a 1 to 1 mapping between `Proxy` instances and protocol +//! objects. Rather, you can think of `Proxy` as an `Rc`-like handle to a +//! wayland object. Multiple instances of it can exist referring to the same +//! protocol object. +//! +//! Similarly, the lifetimes of the protocol objects and the `Proxy` are +//! not tighly tied. As protocol objects are created and destroyed by protocol +//! messages, it can happen that an object gets destroyed while one or more +//! `Proxy` still refers to it. In such case, these proxies will be disabled +//! and their `alive()` method will start to return `false`. Trying to send messages +//! with them will also fail. +//! +//! ### Implementations +//! +//! To receive and process messages from the server to you (in wayland context they are +//! called "events"), you need to provide an `Implementation` for each wayland object +//! created in the protocol session. Whenever a new protocol object is created, you will +//! receive a `NewProxy` object. Providing an implementation via its `implement()` method +//! will turn it into a regular `Proxy` object. +//! +//! **All objects must be implemented**, even if it is an implementation doing nothing. +//! Failure to do so (by dropping the `NewProxy` for example) can cause future fatal +//! errors if the server tries to send an event to this object. +//! +//! An implementation is just a struct implementing the `Implementation, I::Event>` +//! trait, where `I` is the interface of the considered object: //! -//! Connection to the Wayland compositor is achieved by -//! the `default_connect()` function, which provides you -//! with a `WlDisplay` and an `EventQueue`. -//! -//! From the display, you'll retrieve the registry, from -//! which you can instantiate the globals you need. This -//! step being really similar in most cases, this crate -//! contains an utility struct `EnvHandler` which can do -//! this job for you. See its documentation for details. -//! -//! You then register your handlers for events to the -//! event queue, and integrate it in your main event loop. -//! -//! # Implementations and event queues +//! ``` +//! // Example implementation for the wl_surface interface +//! use wayland_client::Proxy; +//! use wayland_client::protocol::wl_surface; +//! use wayland_client::commons::Implementation; //! -//! This crate mirrors the callback-oriented design of the -//! Wayland C library by using implementation structs: each wayland -//! type defines an `Implementation` struct in its module, with -//! one function field for each possible event this object can receive. +//! struct MyImpl { +//! // ... +//! } //! -//! When registering an object on an event queue, you need to provide an -//! implementation for this object. You can also provide some -//! "implementation data": a value that will be provided as second -//! argument to all the callback methods of your implementation. +//! impl Implementation, wl_surface::Event> for MyImpl { +//! fn receive(&mut self, msg: wl_surface::Event, proxy: Proxy) { +//! // process the message... +//! } +//! } +//! # fn main() {} +//! ``` //! -//! A typical use of implementation data is to store here one or more -//! state tokens to access some part of the shared state from your -//! callback. +//! The trait is also automatically implemented for `FnMut(I::Event, Proxy)` closures, +//! so you can use them for simplicity if a full struct would be too cumbersome. //! -//! ## Example of implementation +//! ## Event Queues //! -//! You can register your wayland objects to an event queue: +//! The wayland client machinnery 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. //! -//! ```ignore -//! event_queue.register(&my_object, implementation, impl_data); -//! ``` +//! Events received from the server are stored in an internal buffer, and processed (by calling +//! the appropriate implementations) when the associated event queue is dispatched. //! -//! A given wayland object can only be registered to a event -//! queue at a given time, re-registering it will overwrite -//! the previous configuration. +//! A default event queue is created at the same time as the initial `Display`, and by default +//! whenever a wayland object is created, it inherits the queue of its parent (the object that sent +//! or receive the message that created the new object). It means that if you only plan to use the +//! default event queue, you don't need to worry about assigning objects to their queues. //! -//! Objects can be registered to event queues using the `&EventQueueHandle` -//! argument, available from withing an event callback. +//! See the documentation of `EventQueue` for details about dispatching and integrating the event +//! queue into the event loop of your application. See the `Proxy::make_wrapper()` method for +//! details about assigning objects to event queues. //! -//! ## Event loop integration +//! ## Dynamic linking with `libwayland-client.so` //! -//! Once this setup is done, you can integrate the event queue -//! to the main event loop of your program: +//! 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. //! -//! ```ignore -//! loop { -//! // flush events to the server -//! display.flush().unwrap(); -//! // receive events from the server and dispatch them -//! // to handlers (might block) -//! event_queue.dispatch().unwrap(); -//! } -//! ``` +//! 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. //! -//! For more precise control of the flow of the event queue -//! (and importantly non-blocking options), see `EventQueue` -//! documentation. +//! ## Auxiliary libraries //! -//! # Protocols integration +//! Two auxiliary libraries are also available behind cargo features: //! -//! This crate provides the basic primitives as well as the -//! core wayland protocol (in the `protocol` module), but -//! other protocols can be integrated from XML descriptions. +//! - the `cursor` feature will try to load `libwayland-cursor.so`, a library helping with loading +//! system themed cursor textures, to integrate your app in the system theme. +//! - the `egl` feature will try to load `libwayland-egl.so`, a library allowing the creation of +//! OpenGL surface from wayland surfaces. //! -//! The the crate `wayland_scanner` and its documentation for -//! details about how to do so. +//! Both of them will also be loaded at runtime if the `dlopen` feature was provided. See their +//! respective submodules for details about their use. #![warn(missing_docs)] #[macro_use] extern crate bitflags; extern crate libc; -extern crate token_store; + +extern crate wayland_commons; +#[cfg(feature = "native_lib")] #[macro_use] extern crate wayland_sys; -pub use generated::client as protocol; -pub use generated::interfaces as protocol_interfaces; -use wayland_sys::client::wl_proxy; -use wayland_sys::common::{wl_argument, wl_interface}; - mod display; mod event_queue; -mod env; +mod globals; +mod proxy; -#[cfg(feature = "egl")] -pub mod egl; +pub use proxy::{NewProxy, Proxy}; +pub use display::Display; +pub use globals::{GlobalError, GlobalEvent, GlobalManager}; +pub use event_queue::{EventQueue, QueueToken, ReadEventsGuard}; #[cfg(feature = "cursor")] pub mod cursor; -pub use display::{connect_to, default_connect, ConnectError, FatalError}; -pub use env::{EnvHandler, EnvHandlerInner, EnvNotify}; -pub use event_queue::{EventQueue, EventQueueHandle, ReadEventsGuard, RegisterStatus, State, StateProxy, - StateToken}; +#[cfg(feature = "egl")] +pub mod egl; -/// Common routines for wayland proxy objects. +/// Re-export of wayland-commons /// -/// All wayland objects automatically implement this trait -/// as generated by the scanner. -/// -/// It is mostly used for internal use by the library, and you -/// should only need these methods for interfacing with C library -/// working on wayland objects. -pub unsafe trait Proxy { - /// Pointer to the underlying wayland proxy object - fn ptr(&self) -> *mut wl_proxy; - /// Create an instance from a wayland pointer - /// - /// The pointer must refer to a valid wayland proxy - /// of the appropriate interface, but that have not - /// yet been seen by the library. - /// - /// The library will take control of the object (notably - /// overwrite its user_data). - unsafe fn from_ptr_new(*mut wl_proxy) -> Self; - /// Create an instance from a wayland pointer - /// - /// The pointer must refer to a valid wayland proxy - /// of the appropriate interface. The library will detect if the - /// proxy is already managed by it or not. If it is not, this - /// proxy will be considered as "unmanaged", and should then - /// be handled with care. - unsafe fn from_ptr_initialized(*mut wl_proxy) -> Self; - /// Pointer to the interface representation - fn interface_ptr() -> *const wl_interface; - /// Internal wayland name of this interface - fn interface_name() -> &'static str; - /// Max version of this interface supported - fn supported_version() -> u32; - /// Current version of the interface this proxy is instantiated with - fn version(&self) -> u32; - /// Check if the proxy behind this handle is actually still alive - fn status(&self) -> Liveness; - /// Check of two handles are actually the same wayland object - /// - /// Returns `false` if any of the objects has already been destroyed - fn equals(&self, &Self) -> bool; - /// Set a pointer associated as user data on this proxy - /// - /// All proxies to the same wayland object share the same user data pointer. - /// - /// The get/set operations are atomic, no more guarantee is given. If you need - /// to synchronise access to this data, it is your responsibility to add a Mutex - /// or any other similar mechanism. - /// - /// If this proxy is not managed by wayland-client, this does nothing. - fn set_user_data(&self, ptr: *mut ()); - /// Get the pointer associated as user data on this proxy - /// - /// All proxies to the same wayland object share the same user data pointer. - /// - /// See `set_user_data` for synchronisation guarantee. - /// - /// If this proxy is not managed by wayland-client, this returns a null pointer. - fn get_user_data(&self) -> *mut (); - /// Clone this proxy handle - /// - /// Will only succeed if the proxy is managed by this library and - /// is still alive. - fn clone(&self) -> Option - where - Self: Sized, - { - if self.status() == Liveness::Alive { - Some(unsafe { self.clone_unchecked() }) - } else { - None - } - } - /// Unsafely clone this proxy handle - /// - /// This function is unsafe because if the proxy is unmanaged, the lib - /// has no knowledge of its lifetime, and cannot ensure that the new handle - /// will not outlive the object. - unsafe fn clone_unchecked(&self) -> Self - where - Self: Sized; -} - -/// Common trait for wayland objects that can be registered to an EventQueue -pub unsafe trait Implementable: Proxy { - /// The type containing the implementation for the event callbacks - type Implementation: PartialEq + Copy + 'static; - #[doc(hidden)] - unsafe fn __dispatch_msg(&self, opcode: u32, args: *const wl_argument) -> Result<(), ()>; +/// Common traits and functions to work with wayland objects +pub mod commons { + pub use wayland_commons::*; } -/// Possible outcome of the call of a request on a proxy -#[derive(Debug)] -pub enum RequestResult { - /// Message has been buffered and will be sent to server - Sent(T), - /// This proxy is already destroyed, request has been ignored - Destroyed, -} - -impl RequestResult { - /// Assert that result is successfull and extract the value. - /// - /// Panics with provided error message if the result was `Destroyed`. - pub fn expect(self, error: &str) -> T { - match self { - RequestResult::Sent(v) => v, - RequestResult::Destroyed => panic!("{}", error), - } - } +#[cfg(feature = "native_lib")] +/// C-associated types +/// +/// Required for plugging wayland-scanner generated protocols +/// or interfacing with C code using wayland objects. +pub mod sys { + pub use super::generated::c_interfaces as protocol_interfaces; + pub use wayland_sys::{client, common}; } -/// Represents the state of liveness of a wayland object -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum Liveness { - /// This object is alive and its requests can be called - Alive, - /// This object is dead, calling its requests will do nothing and - /// return and error. - Dead, - /// This object is not managed by `wayland-client`, you can call its methods - /// but this might crash the program if it was actually dead. - Unmanaged, +/// Generated interfaces for the core wayland protocol +pub mod protocol { + #[cfg(feature = "native_lib")] + pub use generated::c_api::*; } mod generated { @@ -234,31 +175,15 @@ mod generated { #![allow(non_upper_case_globals, non_snake_case, unused_imports)] #![allow(missing_docs)] - pub mod interfaces { - //! Interfaces for the core protocol - //! - //! You might need them for the bindings generated for protocol extensions - include!(concat!(env!("OUT_DIR"), "/wayland_interfaces.rs")); + #[cfg(feature = "native_lib")] + pub mod c_interfaces { + include!(concat!(env!("OUT_DIR"), "/wayland_c_interfaces.rs")); } - - pub mod client { - //! The wayland core protocol - //! - //! This module contains all objects of the core wayland protocol. - //! - //! It has been generated from the `wayland.xml` protocol file - //! using `wayland_scanner`. - - pub(crate) use super::interfaces; - pub(crate) use {Implementable, Liveness, Proxy, RequestResult}; - pub(crate) use event_queue::EventQueueHandle; - include!(concat!(env!("OUT_DIR"), "/wayland_api.rs")); + #[cfg(feature = "native_lib")] + pub mod c_api { + pub(crate) use {NewProxy, Proxy}; + pub(crate) use wayland_commons::{AnonymousObject, Interface, MessageGroup}; + pub(crate) use wayland_sys as sys; + include!(concat!(env!("OUT_DIR"), "/wayland_c_api.rs")); } } - -pub mod sys { - //! Reexports of types and objects from wayland-sys - - pub use wayland_sys::client::*; - pub use wayland_sys::common::*; -} diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs new file mode 100644 index 00000000000..3ca7328681d --- /dev/null +++ b/wayland-client/src/proxy.rs @@ -0,0 +1,606 @@ +use wayland_commons::{Implementation, Interface, MessageGroup}; + +#[cfg(feature = "native_lib")] +use wayland_sys::client::*; + +use event_queue::QueueToken; + +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; + +pub(crate) struct ProxyInternal { + alive: AtomicBool, + user_data: AtomicPtr<()>, +} + +impl ProxyInternal { + fn new() -> ProxyInternal { + ProxyInternal { + alive: AtomicBool::new(true), + user_data: AtomicPtr::new(::std::ptr::null_mut()), + } + } +} + +/// An handle to a wayland proxy +/// +/// This represents a wayland object instanciated 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 +/// you need to import the associated `RequestsTrait` trait from the module +/// of this interface. +pub struct Proxy { + _i: ::std::marker::PhantomData<*const I>, + #[cfg(not(feature = "native_lib"))] + internal: Arc, + #[cfg(feature = "native_lib")] + internal: Option>, + #[cfg(feature = "native_lib")] + ptr: *mut wl_proxy, + #[cfg(feature = "native_lib")] + is_wrapper: bool, +} + +unsafe impl Send for Proxy {} +unsafe impl Sync for Proxy {} + +impl Proxy { + /// Send a request through this object + /// + /// This is the generic method to send requests. + /// + /// Several requests require the creation of new objects using + /// the `child()` method, which if done wrong can cause protocol + /// errors (in which case the server will terminate your connexion). + /// Thus unless your know exactly what you are doing, you should use + /// the helper methods provided by the various `RequestsTrait` for + /// each interface, which handle this correctly for you. + pub fn send(&self, msg: I::Request) { + #[cfg(not(feature = "native_lib"))] + { + if !self.internal.alive.load(Ordering::Acquire) { + // don't send message to dead objects ! + return; + } + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + if let Some(ref internal) = self.internal { + // object is managed + if !internal.alive.load(Ordering::Acquire) { + // don't send message to dead objects ! + return; + } + } + msg.as_raw_c_in(|opcode, args| unsafe { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_proxy_marshal_array, + self.ptr, + opcode, + args.as_ptr() as *mut _ + ); + }); + } + } + + /// Check if the object associated with this proxy is still alive + /// + /// Will return `false` if either: + /// + /// - The object has been destroyed + /// - The object is not managed by this library (see the `from_c_ptr` method) + pub fn is_alive(&self) -> bool { + #[cfg(not(feature = "native_lib"))] + { + self.internal.alive.load(Ordering::Acquire) + } + #[cfg(feature = "native_lib")] + { + self.internal + .as_ref() + .map(|i| i.alive.load(Ordering::Acquire)) + .unwrap_or(false) + } + } + + /// Retrieve the interface version of this wayland object instance + /// + /// Returns 0 on dead objects + pub fn version(&self) -> u32 { + if !self.is_alive() { + return 0; + } + + #[cfg(not(feature = "native_lib"))] + { + unimplemented!(); + } + #[cfg(feature = "native_lib")] + { + unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_version, self.ptr) as u32 } + } + } + + /// Associate an arbitrary payload to this object + /// + /// The pointer you associate here can be retrieved from any + /// other proxy to the same wayland object. + /// + /// Setting or getting user data is done as an atomic operation. + /// You are responsible for the correct initialization of this + /// pointer, synchronisation of access, and destruction of the + /// contents at the appropriate time. + pub fn set_user_data(&self, ptr: *mut ()) { + #[cfg(not(feature = "native_lib"))] + { + self.internal.user_data.store(ptr, Ordering::Release); + } + #[cfg(feature = "native_lib")] + { + if let Some(ref inner) = self.internal { + inner.user_data.store(ptr, Ordering::Release); + } + } + } + + /// Retrieve the arbitrary payload associated to this object + /// + /// See `set_user_data` for explanations. + pub fn get_user_data(&self) -> *mut () { + #[cfg(not(feature = "native_lib"))] + { + self.internal.user_data.load(Ordering::Acquire) + } + #[cfg(feature = "native_lib")] + { + if let Some(ref inner) = self.internal { + inner.user_data.load(Ordering::Acquire) + } else { + ::std::ptr::null_mut() + } + } + } + + #[cfg(feature = "native_lib")] + /// Check whether this proxy is managed by the library or not + /// + /// See `from_c_ptr` for details. + pub fn is_external(&self) -> bool { + self.internal.is_none() + } + + /// Clone this proxy + /// + /// It only creates a new handle to the same wayland + /// object, and does not create a new one. + pub fn clone(&self) -> Proxy { + Proxy { + _i: ::std::marker::PhantomData, + internal: self.internal.clone(), + #[cfg(feature = "native_lib")] + ptr: self.ptr, + #[cfg(feature = "native_lib")] + is_wrapper: self.is_wrapper, + } + } + + /// Check if the other proxy refers to the same underlying wayland object + pub fn equals(&self, other: &Proxy) -> bool { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + 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, + } + } + } + + #[cfg(feature = "native_lib")] + /// 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). + pub fn c_ptr(&self) -> *mut wl_proxy { + self.ptr + } + + #[cfg(feature = "native_lib")] + /// 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 `false` 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 pointer to this object. + /// + /// In order to handle protocol races, invoking it with a NULL pointer will + /// create an already-dead object. + pub unsafe fn from_c_ptr(ptr: *mut wl_proxy) -> Self { + use wayland_sys::client::*; + + if ptr.is_null() { + return Proxy { + _i: ::std::marker::PhantomData, + internal: Some(Arc::new(ProxyInternal { + alive: AtomicBool::new(false), + user_data: AtomicPtr::new(::std::ptr::null_mut()), + })), + ptr: ptr, + is_wrapper: false, + }; + } + + 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 self::native_machinery::ProxyUserData; + Some((*user_data).internal.clone()) + } else { + None + }; + Proxy { + _i: ::std::marker::PhantomData, + internal: internal, + ptr: ptr, + is_wrapper: false, + } + } + + #[doc(hidden)] + #[cfg(feature = "native_lib")] + pub unsafe fn new_null() -> Proxy { + Proxy { + _i: ::std::marker::PhantomData, + internal: None, + ptr: ::std::ptr::null_mut(), + is_wrapper: false, + } + } + + #[cfg(feature = "native_lib")] + /// Create a wrapper for this object for queue management + /// + /// As assigning a proxy to an event queue can be a racy operation + /// in contextes involving multiple thread, this provides a facility + /// to do this safely. + /// + /// The wrapper object created behaves like a regular `Proxy`, except that + /// all objects created as the result of its requests will be assigned to + /// the queue associated to the provided token, rather than the queue of + /// their parent. This does not change the queue of the proxy itself. + pub fn make_wrapper(&self, queue: &QueueToken) -> Result, ()> { + if !self.is_external() && !self.is_alive() { + return Err(()); + } + + let wrapper_ptr; + unsafe { + wrapper_ptr = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_create_wrapper, self.ptr); + queue.assign_proxy(wrapper_ptr); + } + + Ok(Proxy { + _i: ::std::marker::PhantomData, + internal: self.internal.clone(), + ptr: wrapper_ptr, + is_wrapper: true, + }) + } + + /// Create a new child object + /// + /// This creates a new wayland object, considered as a + /// child of this object. It will notably inherit its interface + /// version. + /// + /// The created object should immediatly be implemented and sent + /// in a request to the server, to keep the object list properly + /// synchronized. Failure to do so will likely cause a protocol + /// error. + pub fn child(&self) -> NewProxy { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + let ptr = unsafe { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_proxy_create, + self.ptr, + C::c_interface() + ) + }; + NewProxy { + _i: ::std::marker::PhantomData, + ptr: ptr, + } + } + } + + /// Check whether this proxy has been implemented with given type + /// + /// Always returns false if the proxy is no longer alive + pub fn is_implemented_with(&self) -> bool + where + Impl: Implementation, I::Event> + 'static, + { + if !self.is_alive() { + return false; + } + #[cfg(not(feature = "native_lib"))] + { + unimplemented!(); + } + #[cfg(feature = "native_lib")] + { + let user_data = unsafe { + let ptr = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, self.ptr) + as *mut self::native_machinery::ProxyUserData; + &*ptr + }; + user_data.is_impl::() + } + } +} + +#[cfg(feature = "native_lib")] +impl Proxy<::protocol::wl_display::WlDisplay> { + pub(crate) fn from_display(d: *mut wl_display) -> Proxy<::protocol::wl_display::WlDisplay> { + Proxy { + _i: ::std::marker::PhantomData, + internal: None, + ptr: d as *mut wl_proxy, + is_wrapper: false, + } + } +} + +impl Drop for Proxy { + fn drop(&mut self) { + #[cfg(feature = "native_lib")] + { + if self.is_wrapper { + unsafe { + ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_wrapper_destroy, self.ptr); + } + } + } + } +} + +/// A newly-created proxy that needs implementation +/// +/// Whenever a new wayland object is created, you will +/// receive it as a `NewProxy`. You then have to provide an +/// implementation for it, in order to process the incoming +/// events it may receive. Once this done you will be able +/// to use it as a regular `Proxy`. +/// +/// Implementations are structs implementing the appropriate +/// variant of the `Implementation` trait. They can also be +/// closures. +pub struct NewProxy { + _i: ::std::marker::PhantomData<*const I>, + #[cfg(feature = "native_lib")] + ptr: *mut wl_proxy, +} + +impl NewProxy { + /// Implement this proxy using given function and implementation data. + pub fn implement(self, implementation: Impl) -> Proxy + where + Impl: Implementation, I::Event> + Send + 'static, + { + unsafe { self.implement_inner(implementation) } + } + + /// Implement this proxy using given function and implementation data. + /// + /// This method allows the implementation to not be `Send`, but requires for + /// safety that you provide a token to the event queue this proxy will be implemented + /// on. This method will then ensure that this proxy is registered on this event queue, + /// so that it cannot be dispatched from an other thread. + /// + /// **Unsafety:** + /// + /// This call can be racy if the proxy is not already registered on this event queue and its + /// old queue is being dispatched from an other thread. + /// + /// To ensure safety, see `Proxy::make_wrapper`. + pub unsafe fn implement_nonsend(self, implementation: Impl, queue: &QueueToken) -> Proxy + where + Impl: Implementation, I::Event> + 'static, + { + #[cfg(not(feature = "native_lib"))] + {} + #[cfg(feature = "native_lib")] + { + queue.assign_proxy(self.c_ptr()); + self.implement_inner(implementation) + } + } + + unsafe fn implement_inner(self, implementation: Impl) -> Proxy + where + Impl: Implementation, I::Event> + 'static, + { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + use wayland_sys::client::*; + + let new_user_data = Box::new(self::native_machinery::ProxyUserData::new(implementation)); + let internal = new_user_data.internal.clone(); + + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_proxy_add_dispatcher, + self.ptr, + self::native_machinery::proxy_dispatcher::, + &::wayland_sys::RUST_MANAGED as *const _ as *const _, + Box::into_raw(new_user_data) as *mut _ + ); + + Proxy { + _i: ::std::marker::PhantomData, + internal: Some(internal), + ptr: self.ptr, + is_wrapper: false, + } + } + } + + #[cfg(feature = "native_lib")] + /// 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). + /// + /// Use this if you need to pass an unimplemented object to the C library + /// you are interfacing with. + pub fn c_ptr(&self) -> *mut wl_proxy { + self.ptr + } + + #[cfg(feature = "native_lib")] + /// Create a `NewProxy` instance from a C pointer. + /// + /// By doing so, you assert that this wayland object was newly created and + /// can be safely implemented. As implementing it will overwrite any previously + /// associated data or implementation, this can cause weird errors akin to + /// memory corruption if it was not the case. + pub unsafe fn from_c_ptr(ptr: *mut wl_proxy) -> Self { + NewProxy { + _i: ::std::marker::PhantomData, + ptr: ptr, + } + } +} + +#[cfg(feature = "native_lib")] +mod native_machinery { + use wayland_sys::common::*; + use wayland_sys::client::*; + + use std::sync::Arc; + use std::sync::atomic::Ordering; + use std::os::raw::{c_int, c_void}; + + use super::Proxy; + + use wayland_commons::{Implementation, Interface, MessageGroup}; + + pub(crate) struct ProxyUserData { + pub(crate) internal: Arc, + implem: Option, I::Event>>>, + } + + impl ProxyUserData { + pub(crate) fn new(implem: Impl) -> ProxyUserData + where + Impl: Implementation, I::Event> + 'static, + { + ProxyUserData { + internal: Arc::new(super::ProxyInternal::new()), + implem: Some(Box::new(implem)), + } + } + + pub(crate) fn is_impl(&self) -> bool + where + Impl: Implementation, I::Event> + 'static, + { + self.implem + .as_ref() + .map(|implem| implem.is::()) + .unwrap_or(false) + } + } + + pub(crate) unsafe extern "C" fn proxy_dispatcher( + _implem: *const c_void, + proxy: *mut c_void, + opcode: u32, + _msg: *const wl_message, + args: *const wl_argument, + ) -> c_int + where + I: Interface, + { + 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 || { + // parse the message: + let msg = I::Event::from_raw_c(proxy as *mut _, opcode, args)?; + let must_destroy = msg.is_destructor(); + // create the proxy object + let proxy_obj = super::Proxy::::from_c_ptr(proxy); + // retrieve the impl + let user_data = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, proxy); + { + let user_data = &mut *(user_data as *mut ProxyUserData); + let implem = user_data.implem.as_mut().unwrap(); + if must_destroy { + user_data.internal.alive.store(false, Ordering::Release); + } + // call the impl + implem.receive(msg, proxy_obj); + } + if must_destroy { + // final cleanup + let _ = Box::from_raw(user_data as *mut ProxyUserData); + ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy, proxy); + } + Ok(()) + }); + // check the return status + match ret { + Ok(Ok(())) => return 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() + } + } + } +} diff --git a/wayland-commons/Cargo.toml b/wayland-commons/Cargo.toml new file mode 100644 index 00000000000..d2c33865efe --- /dev/null +++ b/wayland-commons/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "wayland-commons" +version = "0.20.0" +authors = ["Victor Berger "] +repository = "https://github.com/smithay/wayland-rs" +documentation = "https://smithay.github.io/wayland-rs/wayland_commons/" +description = "Common types and structures used by wayland-client and wayland-server." +license = "MIT" +categories = ["gui", "api-bindings"] +keywords = ["wayland"] + +[badges] +travis-ci = { repository = "smithay/wayland-rs" } + +[dependencies] +wayland-sys = { version = "0.20.0", path = "../wayland-sys", optional = true } +downcast-rs = "1.0" + +[features] +default = ["native_lib"] +native_lib = [ "wayland-sys" ] diff --git a/wayland-commons/src/lib.rs b/wayland-commons/src/lib.rs new file mode 100644 index 00000000000..82a4de54eb6 --- /dev/null +++ b/wayland-commons/src/lib.rs @@ -0,0 +1,159 @@ +//! Common definitions for wayland +//! +//! This crate hosts common type and traits used to represent wayland messages +//! and routines in the `wayland-client` and `wayland-server` crates. +//! +//! This notably includes the `Interface` trait, which can exhaustively describe +//! any wayland interface. Its implementations are intented to be generated by the +//! `wayland-scanner` crate. +//! +//! The principal user-facing definition provided by this crate is the `Implementation` +//! trait, which as a user of `wayland-client` or `wayland-server` you will be using +//! to define objects able to handle the messages your program receives. Note that +//! this trait is auto-implemented for closures with appropriate signature, for +//! convenience. + +#![warn(missing_docs)] + +#[macro_use] +extern crate downcast_rs as downcast; +#[cfg(feature = "native_lib")] +extern crate wayland_sys; +#[cfg(feature = "native_lib")] +use wayland_sys::common as syscom; + +use std::os::raw::c_void; + +use downcast::Downcast; + +/// A group of messages +/// +/// This represents a group of message that can be serialized on the protocol wire. +/// Typically the set of events or requests of a single interface. +/// +/// Implementations of this trait are supposed to be +/// generated using the `wayland-scanner` crate. +pub trait MessageGroup: Sized { + /// Whether this message is a destructor + /// + /// If it is, once send or receive the associated object cannot be used any more. + fn is_destructor(&self) -> bool; + #[cfg(feature = "native_lib")] + /// Construct a message of this group from its C representation + unsafe fn from_raw_c(obj: *mut c_void, opcode: u32, args: *const syscom::wl_argument) + -> Result; + #[cfg(feature = "native_lib")] + /// Build a C representation of this message + /// + /// It can only be accessed from the provided closure, and this consumes + /// the message. + fn as_raw_c_in(self, f: F) -> T + where + F: FnOnce(u32, &mut [syscom::wl_argument]) -> T; +} + +/// The description of a wayland interface +/// +/// Implementations of this trait are supposed to be +/// generated using the `wayland-scanner` crate. +pub trait Interface: 'static { + /// Set of requests associated to this interface + /// + /// Requests are messages from the client to the server + type Request: MessageGroup + 'static; + /// Set of events associated to this interface + /// + /// Events are messages from the server to the client + type Event: MessageGroup + 'static; + /// Name of this interface + const NAME: &'static str; + #[cfg(feature = "native_lib")] + /// Pointer to the C representation of this interface + fn c_interface() -> *const ::syscom::wl_interface; +} + +/// Trait representing implementations for wayland objects +/// +/// Several wayland objects require you to act when some messages +/// are received. You program this act by providing an object +/// implementing this trait. +/// +/// The trait requires a single method: `self.receive(message, metadata)`. +/// the `message` argument will often be an enum of the possible messages, +/// and the `metadata` argument contains associated information. Typically +/// an handle to the wayland object that received this message. +/// +/// The trait is automatically implemented for `FnMut(Msg, Meta)` closures. +/// +/// This is mostly used as a trait object in `wayland-client` and `wayland-server`, +/// and thus also provide methods providing `Any`-like downcasting functionnality. +/// See also the `downcast_impl` freestanding function. +pub trait Implementation: Downcast { + /// Receive a message + fn receive(&mut self, msg: Msg, meta: Meta); +} + +impl_downcast!(Implementation); + +impl Implementation for F +where + F: FnMut(Msg, Meta) + 'static, +{ + fn receive(&mut self, msg: Msg, meta: Meta) { + (self)(msg, meta) + } +} + +/// Attempt to downcast a boxed `Implementation` trait object. +/// +/// Similar to `Box::::downcast()`. +pub fn downcast_impl>( + b: Box>, +) -> Result, Box>> { + if b.is::() { + unsafe { + let raw: *mut Implementation = Box::into_raw(b); + Ok(Box::from_raw(raw as *mut T)) + } + } else { + Err(b) + } +} + +/// Anonymous interface +/// +/// A special Interface implementation representing an +/// handle to an object for which the interface is not known. +pub struct AnonymousObject; + +/// An empty enum representing a MessageGroup with no messages +pub enum NoMessage {} + +impl Interface for AnonymousObject { + type Request = NoMessage; + type Event = NoMessage; + const NAME: &'static str = ""; + #[cfg(feature = "native_lib")] + fn c_interface() -> *const ::syscom::wl_interface { + ::std::ptr::null() + } +} + +impl MessageGroup for NoMessage { + fn is_destructor(&self) -> bool { + match *self {} + } + unsafe fn from_raw_c( + _obj: *mut c_void, + _opcode: u32, + _args: *const syscom::wl_argument, + ) -> Result { + Err(()) + } + fn as_raw_c_in(self, _f: F) -> T + where + F: FnOnce(u32, &mut [syscom::wl_argument]) -> T, + { + match self {} + } +} diff --git a/wayland-protocols/Cargo.toml b/wayland-protocols/Cargo.toml index a403282c61f..90b3710f8f4 100644 --- a/wayland-protocols/Cargo.toml +++ b/wayland-protocols/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wayland-protocols" -version = "0.14.1" +version = "0.20.0" documentation = "https://smithay.github.io/wayland-rs/wayland_protocols/" repository = "https://github.com/smithay/wayland-rs" authors = ["Victor Berger "] @@ -14,17 +14,20 @@ categories = ["gui", "api-bindings"] travis-ci = { repository = "smithay/wayland-rs" } [dependencies] -wayland-sys = { version = "0.14.1", path = "../wayland-sys" } -wayland-client = { version = "0.14.1", path = "../wayland-client", optional = true, default-features = false } -wayland-server = { version = "0.14.1", path = "../wayland-server", optional = true } +wayland-commons = { version = "0.20.0", path = "../wayland-commons" } +wayland-sys = { version = "0.20.0", path = "../wayland-sys", optional = true } +wayland-client = { version = "0.20.0", path = "../wayland-client", optional = true, default-features = false } +wayland-server = { version = "0.20.0", path = "../wayland-server", optional = true } bitflags = "1.0" [build-dependencies] -wayland-scanner = { version = "0.14.1", path = "../wayland-scanner" } +wayland-scanner = { version = "0.20.0", path = "../wayland-scanner" } [features] +default = ["native_lib"] client = ["wayland-client"] server = ["wayland-server"] +native_lib = ["wayland-sys", "wayland-client/native_lib", "wayland-server/native_lib"] misc_protocols = [] wall_protocols = [] unstable_protocols = [] diff --git a/wayland-protocols/build.rs b/wayland-protocols/build.rs index 742ccf4e50a..d683dc7e208 100644 --- a/wayland-protocols/build.rs +++ b/wayland-protocols/build.rs @@ -2,7 +2,7 @@ extern crate wayland_scanner; use std::env::var; use std::path::Path; -use wayland_scanner::{generate_code, generate_interfaces, Side}; +use wayland_scanner::*; static STABLE_PROTOCOLS: &'static [&'static str] = &["presentation-time", "viewporter", "xdg-shell"]; @@ -36,25 +36,27 @@ static WALL_UNSTABLE_PROTOCOLS: &'static [(&'static str, &'static [&'static str] static MISC_STABLE_PROTOCOLS: &'static [&'static str] = &["server-decoration"]; fn generate_protocol(name: &str, protocol_file: &Path, out_dir: &Path, client: bool, server: bool) { - generate_interfaces( - &protocol_file, - out_dir.join(&format!("{}_interfaces.rs", name)), - ); - - if client { - generate_code( + if var("CARGO_FEATURE_NATIVE_LIB").ok().is_some() { + generate_c_interfaces( &protocol_file, - out_dir.join(&format!("{}_client_api.rs", name)), - Side::Client, + out_dir.join(&format!("{}_c_interfaces.rs", name)), ); - } - if server { - generate_code( - &protocol_file, - out_dir.join(&format!("{}_server_api.rs", name)), - Side::Server, - ); + if client { + generate_c_code( + &protocol_file, + out_dir.join(&format!("{}_c_client_api.rs", name)), + Side::Client, + ); + } + + if server { + generate_c_code( + &protocol_file, + out_dir.join(&format!("{}_c_server_api.rs", name)), + Side::Server, + ); + } } } @@ -139,5 +141,4 @@ fn main() { ); } } - } diff --git a/wayland-protocols/src/lib.rs b/wayland-protocols/src/lib.rs index 67996535cd1..51477bfe3f8 100644 --- a/wayland-protocols/src/lib.rs +++ b/wayland-protocols/src/lib.rs @@ -22,6 +22,8 @@ extern crate wayland_client; #[cfg(feature = "server")] extern crate wayland_server; +extern crate wayland_commons; + #[macro_use] extern crate wayland_sys; diff --git a/wayland-protocols/src/protocol_macro.rs b/wayland-protocols/src/protocol_macro.rs index 0f194650328..0346a0b8d12 100644 --- a/wayland-protocols/src/protocol_macro.rs +++ b/wayland-protocols/src/protocol_macro.rs @@ -1,12 +1,13 @@ #[macro_escape] macro_rules! wayland_protocol( ($name: expr, [$(($import: ident, $interface: ident)),*]) => { - #[cfg(feature = "client")] - pub use self::generated::client::api as client; + #[cfg(all(feature = "client", feature="native_lib"))] + pub use self::generated::client::c_api as client; - #[cfg(feature = "server")] - pub use self::generated::server::api as server; + #[cfg(all(feature = "server", feature="native_lib"))] + pub use self::generated::server::c_api as server; + #[cfg(feature = "native_lib")] mod generated { #![allow(dead_code,non_camel_case_types,unused_unsafe,unused_variables)] #![allow(non_upper_case_globals,non_snake_case,unused_imports)] @@ -14,33 +15,35 @@ macro_rules! wayland_protocol( #[cfg(feature = "client")] pub mod client { - pub mod interfaces { - pub use wayland_client::protocol_interfaces::{$($interface),*}; - include!(concat!(env!("OUT_DIR"), "/", $name, "_interfaces.rs")); + pub mod c_interfaces { + pub use wayland_client::sys::protocol_interfaces::{$($interface),*}; + include!(concat!(env!("OUT_DIR"), "/", $name, "_c_interfaces.rs")); } /// Client-side API of this protocol - pub mod api { - pub(crate) use wayland_client::{Proxy, Implementable, RequestResult, EventQueueHandle, Liveness}; - pub(crate) use super::interfaces; + pub mod c_api { + pub(crate) use wayland_client::{NewProxy, Proxy}; + pub(crate) use wayland_commons::{AnonymousObject, Interface, MessageGroup}; + pub(crate) use wayland_sys as sys; pub(crate) use wayland_client::protocol::{$($import),*}; - include!(concat!(env!("OUT_DIR"), "/", $name, "_client_api.rs")); + include!(concat!(env!("OUT_DIR"), "/", $name, "_c_client_api.rs")); } } #[cfg(feature = "server")] pub mod server { - pub mod interfaces { - pub use wayland_server::protocol_interfaces::{$($interface),*}; - include!(concat!(env!("OUT_DIR"), "/", $name, "_interfaces.rs")); + pub mod c_interfaces { + pub use wayland_server::sys::protocol_interfaces::{$($interface),*}; + include!(concat!(env!("OUT_DIR"), "/", $name, "_c_interfaces.rs")); } /// Server-side API of this protocol - pub mod api { - pub(crate) use wayland_server::{Resource, Implementable, EventResult, Client, EventLoopHandle, Liveness}; - pub(crate) use super::interfaces; + pub mod c_api { + pub(crate) use wayland_server::{NewResource, Resource}; + pub(crate) use wayland_commons::{AnonymousObject, Interface, MessageGroup}; + pub(crate) use wayland_sys as sys; pub(crate) use wayland_server::protocol::{$($import),*}; - include!(concat!(env!("OUT_DIR"), "/", $name, "_server_api.rs")); + include!(concat!(env!("OUT_DIR"), "/", $name, "_c_server_api.rs")); } } } diff --git a/wayland-scanner/Cargo.toml b/wayland-scanner/Cargo.toml index 42c8b927b4e..7f40d6d1b59 100644 --- a/wayland-scanner/Cargo.toml +++ b/wayland-scanner/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wayland-scanner" -version = "0.14.1" +version = "0.20.0" authors = ["Victor Berger "] repository = "https://github.com/smithay/wayland-rs" documentation = "https://smithay.github.io/wayland-rs/wayland_scanner/" diff --git a/wayland-scanner/src/c_code_gen.rs b/wayland-scanner/src/c_code_gen.rs new file mode 100644 index 00000000000..7c2c79e5799 --- /dev/null +++ b/wayland-scanner/src/c_code_gen.rs @@ -0,0 +1,628 @@ +use std::io::Result as IOResult; +use std::io::Write; + +use common_gen::*; +use protocol::*; +use util::*; +use Side; + +pub(crate) fn write_protocol_client(protocol: Protocol, out: &mut O) -> IOResult<()> { + write_prefix(&protocol, out)?; + + for iface in &protocol.interfaces { + writeln!(out, "pub mod {} {{", iface.name)?; + + if let Some((ref short, ref long)) = iface.description { + write_doc(Some(short), long, true, out, 1)?; + } + + writeln!( + out, + " use super::{{Proxy, NewProxy, AnonymousObject, Interface, MessageGroup}};\n" + )?; + writeln!( + out, + " use super::sys::common::{{wl_argument, wl_interface, wl_array}};" + )?; + writeln!(out, " use super::sys::client::*;")?; + + let iface_name = snake_to_camel(&iface.name); + + write_enums(&iface.enums, out)?; + write_messagegroup("Request", Side::Client, false, &iface.requests, out)?; + write_messagegroup_impl("Request", Side::Client, false, &iface.requests, out)?; + write_messagegroup("Event", Side::Client, true, &iface.events, out)?; + write_messagegroup_impl("Event", Side::Client, true, &iface.events, out)?; + write_interface(&iface_name, &iface.name, out)?; + write_client_methods(&iface_name, &iface.requests, out)?; + + writeln!(out, "}}\n")?; + } + + Ok(()) +} + +pub(crate) fn write_protocol_server(protocol: Protocol, out: &mut O) -> IOResult<()> { + write_prefix(&protocol, out)?; + + for iface in &protocol.interfaces { + // display and registry are handled specially + if iface.name == "wl_display" || iface.name == "wl_registry" { + continue; + } + + writeln!(out, "pub mod {} {{", iface.name)?; + + if let Some((ref short, ref long)) = iface.description { + write_doc(Some(short), long, true, out, 1)?; + } + + writeln!( + out, + " use super::{{Resource, NewResource, AnonymousObject, Interface, MessageGroup}};\n" + )?; + writeln!( + out, + " use super::sys::common::{{wl_argument, wl_interface, wl_array}};" + )?; + writeln!(out, " use super::sys::server::*;")?; + + let iface_name = snake_to_camel(&iface.name); + + write_enums(&iface.enums, out)?; + write_messagegroup("Request", Side::Server, true, &iface.requests, out)?; + write_messagegroup_impl("Request", Side::Server, true, &iface.requests, out)?; + write_messagegroup("Event", Side::Server, false, &iface.events, out)?; + write_messagegroup_impl("Event", Side::Server, false, &iface.events, out)?; + write_interface(&iface_name, &iface.name, out)?; + + writeln!(out, "}}\n")?; + } + + Ok(()) +} + +pub fn write_messagegroup_impl( + name: &str, + side: Side, + receiver: bool, + messages: &[Message], + out: &mut O, +) -> IOResult<()> { + writeln!(out, " impl super::MessageGroup for {} {{", name)?; + + // is_destructor + writeln!(out, " fn is_destructor(&self) -> bool {{")?; + writeln!(out, " match *self {{")?; + let mut n = messages.len(); + for msg in messages { + if msg.typ == Some(Type::Destructor) { + write!( + out, + " {}::{} ", + name, + snake_to_camel(&msg.name) + )?; + if msg.args.len() > 0 { + write!(out, "{{ .. }} ")?; + } + writeln!(out, "=> true,")?; + n -= 1; + } + } + if n > 0 { + // avoir "unreachable pattern" warnings =) + writeln!(out, " _ => false")?; + } + writeln!(out, " }}")?; + writeln!(out, " }}\n")?; + + // from_raw_c + writeln!(out, " unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result<{},()> {{", name)?; + if !receiver { + writeln!( + out, + " panic!(\"{}::from_raw_c can not be used {:?}-side.\")", + name, side + )?; + } else { + writeln!(out, " match opcode {{")?; + for (i, msg) in messages.iter().enumerate() { + writeln!(out, " {} => {{", i)?; + if msg.args.len() > 0 { + writeln!( + out, + " let _args = ::std::slice::from_raw_parts(args, {});", + msg.args.len() + )?; + } + write!( + out, + " Ok({}::{}", + name, + snake_to_camel(&msg.name) + )?; + if msg.args.len() > 0 { + writeln!(out, " {{")?; + let mut j = 0; + for a in &msg.args { + write!(out, " {}: ", a.name)?; + match a.typ { + Type::Uint => if let Some(ref enu) = a.enum_ { + write!( + out, + "{}::from_raw(_args[{}].u).ok_or(())?", + dotted_to_relname(enu), + j + )?; + } else { + write!(out, "_args[{}].u", j)?; + }, + Type::Int => if let Some(ref enu) = a.enum_ { + write!( + out, + "{}::from_raw(_args[{}].i as u32).ok_or(())?", + dotted_to_relname(enu), + j + )?; + } else { + write!(out, "_args[{}].i", j)?; + }, + Type::Fixed => write!(out, "(_args[{}].f as f64)/256.", j)?, + Type::String => { + if a.allow_null { + write!(out, "if _args[{}].s.is_null() {{ None }} else {{ Some(", j)?; + } + write!( + out, + "::std::ffi::CStr::from_ptr(_args[{}].s).to_string_lossy().into_owned()", + j + )?; + if a.allow_null { + write!(out, ") }}")?; + } + } + Type::Array => { + if a.allow_null { + write!(out, "if _args[{}].a.is_null() {{ None }} else {{ Some(", j)?; + } + write!(out, "{{ let array = &*_args[{}].a; ::std::slice::from_raw_parts(array.data as *const u8, array.size).to_owned() }}", j)?; + if a.allow_null { + write!(out, ") }}")?; + } + } + Type::Fd => write!(out, "_args[{}].h", j)?, + Type::Object => { + if a.allow_null { + write!(out, "if _args[{}].o.is_null() {{ None }} else {{ Some(", j)?; + } + if let Some(ref iface) = a.interface { + write!( + out, + "{}::::from_c_ptr(_args[{}].o as *mut _)", + side.object_name(), + iface, + snake_to_camel(iface), + j + )?; + } else { + write!( + out, + "{}::::from_c_ptr(_args[{}].o as *mut _)", + side.object_name(), + j + )?; + } + if a.allow_null { + write!(out, ") }}")?; + } + } + Type::NewId => { + if a.allow_null { + write!(out, "if _args[{}].o.is_null() {{ None }} else {{ Some(", j)?; + } + if let Some(ref iface) = a.interface { + match side { + Side::Client => write!( + out, + "NewProxy::::from_c_ptr(_args[{}].o as *mut _)", + iface, + snake_to_camel(iface), + j + )?, + Side::Server => { + write!(out, "{{ let client = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_client, obj as *mut _); ")?; + write!(out, "let version = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_version, obj as *mut _); ")?; + write!(out, "let new_ptr = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_create, client, super::{}::{}::c_interface(), version, _args[{}].n);", iface, snake_to_camel(iface), j)?; + write!( + out, + "NewResource::::from_c_ptr(new_ptr) }}", + iface, + snake_to_camel(iface) + )?; + } + } + } else { + // bind-like function + write!(out, "panic!(\"Cannot unserialize anonymous new id.\")")?; + } + if a.allow_null { + write!(out, ") }}")?; + } + } + Type::Destructor => panic!("An argument cannot have type \"destructor\"."), + } + j += 1; + writeln!(out, ",")?; + } + write!(out, " }}")?; + } + writeln!(out, ") }},")?; + } + writeln!(out, " _ => return Err(())")?; + writeln!(out, " }}")?; + } + writeln!(out, " }}\n")?; + + // as_raw_c_in + writeln!( + out, + " fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T {{" + )?; + if receiver { + writeln!( + out, + " panic!(\"{}::as_raw_c_in can not be used {:?}-side.\")", + name, side + )?; + } else { + writeln!(out, " match self {{")?; + for (i, msg) in messages.iter().enumerate() { + write!( + out, + " {}::{} ", + name, + snake_to_camel(&msg.name) + )?; + if msg.args.len() > 0 { + write!(out, "{{ ")?; + for a in &msg.args { + write!(out, "{}, ", a.name)?; + } + write!(out, "}} ")?; + } + writeln!(out, "=> {{")?; + let mut buffer_len = msg.args.len(); + for a in &msg.args { + if a.typ == Type::NewId && a.interface.is_none() { + buffer_len += 2 + } + } + writeln!(out, " let mut _args_array: [wl_argument; {}] = unsafe {{ ::std::mem::zeroed() }};", buffer_len)?; + let mut j = 0; + for a in &msg.args { + write!(out, " ")?; + match a.typ { + Type::Uint => if a.enum_.is_some() { + writeln!(out, "_args_array[{}].u = {}.to_raw();", j, a.name)?; + } else { + writeln!(out, "_args_array[{}].u = {};", j, a.name)?; + }, + Type::Int => if a.enum_.is_some() { + writeln!(out, "_args_array[{}].i = {}.to_raw() as i32;", j, a.name)?; + } else { + writeln!(out, "_args_array[{}].i = {};", j, a.name)?; + }, + Type::Fixed => writeln!(out, "_args_array[{}].f = ({} * 256.) as i32;", j, a.name)?, + Type::String => { + if a.allow_null { + writeln!( + out, + "let _arg_{} = {}.map(|s| ::std::ffi::CString::new(s).unwrap());", + j, a.name + )?; + write!(out, " ")?; + writeln!(out, "_args_array[{}].s = _arg_{}.map(|s| s.as_ptr()).unwrap_or(::std::ptr::null());", j, j)?; + } else { + writeln!( + out, + "let _arg_{} = ::std::ffi::CString::new({}).unwrap();", + j, a.name + )?; + write!(out, " ")?; + writeln!(out, "_args_array[{}].s = _arg_{}.as_ptr();", j, j)?; + } + } + Type::Array => { + if a.allow_null { + writeln!(out, "let _arg_{} = {}.as_ref().map(|vec| wl_array {{ size: vec.len(), alloc: vec.capacity(), data: vec.as_ptr() as *mut_ }});", j, a.name)?; + write!(out, " ")?; + writeln!(out, "_args_array[{}].a = _arg_{}.as_ref().map(|a| a as *const wl_array).unwrap_or(::std::ptr::null());", j, j)?; + } else { + writeln!(out, "let _arg_{} = wl_array {{ size: {a}.len(), alloc: {a}.capacity(), data: {a}.as_ptr() as *mut _ }};", j, a=a.name)?; + write!(out, " ")?; + writeln!(out, "_args_array[{}].a = &_arg_{};", j, j)?; + } + } + Type::Fd => writeln!(out, "_args_array[{}].h = {};", j, a.name)?, + Type::Object => { + if a.allow_null { + writeln!(out, "_args_array[{}].o = {}.map(|o| o.c_ptr() as *mut _).unwrap_or(::std::ptr::null_mut());", j, a.name)?; + } else { + writeln!(out, "_args_array[{}].o = {}.c_ptr() as *mut _;", j, a.name)?; + } + } + Type::NewId => { + if a.interface.is_some() { + writeln!(out, "_args_array[{}].o = {}.c_ptr() as *mut _;", j, a.name)?; + } else { + if side == Side::Server { + panic!("Cannot serialize anonymous NewID from server."); + } + // The arg is actually (string, uint, NULL) + writeln!( + out, + "let _arg_{}_s = ::std::ffi::CString::new({}.0).unwrap();", + j, a.name + )?; + write!(out, " ")?; + writeln!(out, "_args_array[{}].s = _arg_{}_s.as_ptr();", j, j)?; + write!(out, " ")?; + writeln!(out, "_args_array[{}].u = {}.1;", j + 1, a.name)?; + write!(out, " ")?; + writeln!(out, "_args_array[{}].o = ::std::ptr::null_mut();", j + 2)?; + j += 2; + } + } + Type::Destructor => panic!("An argument cannot have type \"destructor\"."), + } + j += 1; + } + writeln!(out, " f({}, &mut _args_array)", i)?; + writeln!(out, " }},")?; + } + writeln!(out, " }}")?; + } + writeln!(out, " }}")?; + + writeln!(out, " }}\n")?; + Ok(()) +} + +fn write_interface(name: &str, low_name: &str, out: &mut O) -> IOResult<()> { + writeln!( + out, + r#" + pub struct {name}; + + impl Interface for {name} {{ + type Request = Request; + type Event = Event; + const NAME: &'static str = "{low_name}"; + fn c_interface() -> *const wl_interface {{ + unsafe {{ &super::super::c_interfaces::{low_name}_interface }} + }} + }} +"#, + name = name, + low_name = low_name + )?; + Ok(()) +} + +fn write_client_methods(name: &str, messages: &[Message], out: &mut O) -> IOResult<()> { + writeln!(out, " pub trait RequestsTrait {{")?; + for msg in messages { + if let Some((ref short, ref long)) = msg.description { + write_doc(Some(short), long, false, out, 2)?; + } + if let Some(Type::Destructor) = msg.typ { + writeln!( + out, + " ///\n /// This is a destructor, you cannot send requests to this object any longer once this method is called.", + )?; + } + if msg.since > 1 { + writeln!( + out, + " ///\n /// Only available since version {} of the interface", + msg.since + )?; + } + print_method_prototype(name, &msg, out)?; + writeln!(out, ";")?; + } + writeln!(out, " }}\n")?; + + writeln!(out, " impl RequestsTrait for Proxy<{}> {{", name)?; + for msg in messages { + let return_type = print_method_prototype(name, &msg, out)?; + writeln!(out, " {{")?; + // liveness sanity check + writeln!( + out, + " if !self.is_external() && !self.is_alive() {{" + )?; + if return_type.is_some() { + writeln!(out, " return Err(());")?; + } else { + writeln!(out, " return;")?; + } + writeln!(out, " }}")?; + // prepare the proxies if applicable + let mut has_newp = false; + for a in &msg.args { + if a.typ == Type::NewId { + if let Some(ref iface) = a.interface { + writeln!( + out, + " let _arg_{}_newproxy = self.child::();", + a.name, + iface, + snake_to_camel(&iface) + )?; + has_newp = true; + } + } + } + // actually send the stuff + write!( + out, + " let msg = Request::{}", + snake_to_camel(&msg.name) + )?; + if msg.args.len() > 0 { + writeln!(out, " {{")?; + for a in &msg.args { + write!(out, " ")?; + if a.typ == Type::NewId { + if let Some(ref iface) = a.interface { + writeln!( + out, + "{}: unsafe {{ Proxy::::from_c_ptr(_arg_{0}_newproxy.c_ptr()) }},", + a.name, + iface, + snake_to_camel(&iface) + )?; + } else { + writeln!(out, "{}: (T::NAME.into(), version, unsafe {{ Proxy::::new_null() }}),", a.name)?; + } + } else if a.typ == Type::Object { + if a.allow_null { + writeln!(out, "{0} : {0}.map(|o| o.clone()),", a.name)?; + } else { + writeln!(out, "{0}: {0}.clone(),", a.name)?; + } + } else { + writeln!(out, "{0}: {0},", a.name)?; + } + } + write!(out, " }}")?; + } + writeln!(out, ";")?; + match return_type { + Some(ret_type) if ret_type.interface.is_none() => { + writeln!( + out, + r#" + unsafe {{ + let ret = msg.as_raw_c_in(|opcode, args| {{ + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_proxy_marshal_array_constructor_versioned, + self.c_ptr(), + opcode, + args.as_mut_ptr(), + T::c_interface(), + version + ) + }}); + Ok(NewProxy::::from_c_ptr(ret)) + }}"# + )?; + } + _ => { + writeln!(out, " self.send(msg);")?; + if has_newp { + for a in &msg.args { + if a.typ == Type::NewId { + if a.interface.is_some() { + writeln!(out, " Ok(_arg_{}_newproxy)", a.name)?; + } + } + } + } + } + } + writeln!(out, " }}\n")?; + } + writeln!(out, " }}")?; + + Ok(()) +} + +fn print_method_prototype<'a, O: Write>( + iname: &str, + msg: &'a Message, + out: &mut O, +) -> IOResult> { + // detect new_id + let mut newid = None; + for arg in &msg.args { + match arg.typ { + Type::NewId => if newid.is_some() { + panic!( + "Request {}.{} returns more than one new_id", + iname, msg.name + ); + } else { + newid = Some(arg); + }, + _ => (), + } + } + + // method start + match newid { + Some(arg) if arg.interface.is_none() => { + write!( + out, + " fn {}{}(&self, version: u32", + if is_keyword(&msg.name) { "_" } else { "" }, + msg.name, + )?; + } + _ => { + write!( + out, + " fn {}{}(&self", + if is_keyword(&msg.name) { "_" } else { "" }, + msg.name + )?; + } + } + + // print args + for arg in &msg.args { + write!( + out, + ", {}{}: {}{}{}", + if is_keyword(&arg.name) { "_" } else { "" }, + arg.name, + if arg.allow_null { "Option<" } else { "" }, + if let Some(ref name) = arg.enum_ { + dotted_to_relname(name) + } else { + match arg.typ { + Type::Object => arg.interface + .as_ref() + .map(|s| format!("&Proxy", s, snake_to_camel(s))) + .unwrap_or(format!("&Proxy")), + Type::NewId => { + // client-side, the return-type handles that + continue; + } + _ => arg.typ.rust_type().into(), + } + }, + if arg.allow_null { ">" } else { "" } + )?; + } + write!(out, ") ->")?; + + // return type + write!( + out, + "{}", + if let Some(arg) = newid { + arg.interface + .as_ref() + .map(|s| format!("Result, ()>", s, snake_to_camel(s))) + .unwrap_or("Result, ()>".into()) + } else { + "()".into() + } + )?; + + Ok(newid) +} diff --git a/wayland-scanner/src/interface_gen.rs b/wayland-scanner/src/c_interface_gen.rs similarity index 64% rename from wayland-scanner/src/interface_gen.rs rename to wayland-scanner/src/c_interface_gen.rs index a1a7f3d7a06..02c8c4c005e 100644 --- a/wayland-scanner/src/interface_gen.rs +++ b/wayland-scanner/src/c_interface_gen.rs @@ -1,56 +1,57 @@ - - +use std::io::Result as IOResult; use protocol::*; use std::cmp; use std::io::Write; -pub fn generate_interfaces(protocol: Protocol, out: &mut O) { +pub(crate) fn generate_interfaces(protocol: Protocol, out: &mut O) -> IOResult<()> { writeln!( out, "//\n// This file was auto-generated, do not edit directly\n//\n" - ).unwrap(); + )?; if let Some(text) = protocol.copyright { - writeln!(out, "/*\n{}\n*/\n", text).unwrap(); + writeln!(out, "/*\n{}\n*/\n", text)?; } - writeln!(out, "use std::os::raw::{{c_char, c_void}};\n").unwrap(); - writeln!(out, "use wayland_sys::common::*;\n").unwrap(); + writeln!( + out, + r#" +use std::os::raw::{{c_char, c_void}}; +use wayland_sys::common::*;"# + )?; // null types array // let longest_nulls = protocol.interfaces.iter().fold(0, |max, interface| { - let request_longest_null = interface - .requests - .iter() - .fold(0, |max, request| if request.all_null() { + let request_longest_null = interface.requests.iter().fold(0, |max, request| { + if request.all_null() { cmp::max(request.args.len(), max) } else { max - }); - let events_longest_null = interface - .events - .iter() - .fold(0, |max, event| if event.all_null() { + } + }); + let events_longest_null = interface.events.iter().fold(0, |max, event| { + if event.all_null() { cmp::max(event.args.len(), max) } else { max - }); + } + }); cmp::max(max, cmp::max(request_longest_null, events_longest_null)) }); - writeln!(out, "const NULLPTR: *const c_void = 0 as *const c_void;\n").unwrap(); + writeln!(out, "const NULLPTR: *const c_void = 0 as *const c_void;\n")?; writeln!( out, "static mut types_null: [*const wl_interface; {}] = [", longest_nulls - ).unwrap(); + )?; for _ in 0..longest_nulls { - writeln!(out, " NULLPTR as *const wl_interface,").unwrap(); + writeln!(out, " NULLPTR as *const wl_interface,")?; } - writeln!(out, "];\n").unwrap(); + writeln!(out, "];\n")?; // emit interfaces // @@ -62,57 +63,57 @@ pub fn generate_interfaces(protocol: Protocol, out: &mut O) { for msg in &$interface.$which { if msg.all_null() { continue; } writeln!(out, "static mut {}_{}_{}_types: [*const wl_interface; {}] = [", - $interface.name, stringify!($which), msg.name, msg.args.len()).unwrap(); + $interface.name, stringify!($which), msg.name, msg.args.len())?; for arg in &msg.args { match (arg.typ, &arg.interface) { (Type::Object, &Some(ref inter)) | (Type::NewId, &Some(ref inter)) => { - writeln!(out, " unsafe {{ &{}_interface as *const wl_interface }},", inter).unwrap() + writeln!(out, " unsafe {{ &{}_interface as *const wl_interface }},", inter)? } - _ => writeln!(out, " NULLPTR as *const wl_interface,").unwrap() + _ => writeln!(out, " NULLPTR as *const wl_interface,")? } } - writeln!(out, "];").unwrap(); + writeln!(out, "];")?; } // then, the message array writeln!(out, "pub static mut {}_{}: [wl_message; {}] = [", - $interface.name, stringify!($which), $interface.$which.len()).unwrap(); + $interface.name, stringify!($which), $interface.$which.len())?; for msg in &$interface.$which { write!(out, " wl_message {{ name: b\"{}\\0\" as *const u8 as *const c_char, signature: b\"", - msg.name).unwrap(); - if msg.since > 1 { write!(out, "{}", msg.since).unwrap(); } + msg.name)?; + if msg.since > 1 { write!(out, "{}", msg.since)?; } for arg in &msg.args { - if arg.typ.nullable() && arg.allow_null { write!(out, "?").unwrap(); } + if arg.typ.nullable() && arg.allow_null { write!(out, "?")?; } match arg.typ { Type::NewId => { - if arg.interface.is_none() { write!(out, "su").unwrap(); } - write!(out, "n").unwrap(); + if arg.interface.is_none() { write!(out, "su")?; } + write!(out, "n")?; }, - Type::Uint => write!(out, "u").unwrap(), - Type::Fixed => write!(out, "f").unwrap(), - Type::String => write!(out, "s").unwrap(), - Type::Object => write!(out, "o").unwrap(), - Type::Array => write!(out, "a").unwrap(), - Type::Fd => write!(out, "h").unwrap(), - Type::Int => write!(out, "i").unwrap(), + Type::Uint => write!(out, "u")?, + Type::Fixed => write!(out, "f")?, + Type::String => write!(out, "s")?, + Type::Object => write!(out, "o")?, + Type::Array => write!(out, "a")?, + Type::Fd => write!(out, "h")?, + Type::Int => write!(out, "i")?, _ => {} } } - write!(out, "\\0\" as *const u8 as *const c_char, types: ").unwrap(); + write!(out, "\\0\" as *const u8 as *const c_char, types: ")?; if msg.all_null() { - write!(out, "unsafe {{ &types_null as *const _ }}").unwrap(); + write!(out, "unsafe {{ &types_null as *const _ }}")?; } else { write!(out, "unsafe {{ &{}_{}_{}_types as *const _ }}", - $interface.name, stringify!($which), msg.name).unwrap(); + $interface.name, stringify!($which), msg.name)?; } - writeln!(out, " }},").unwrap(); + writeln!(out, " }},")?; } - writeln!(out, "];").unwrap(); + writeln!(out, "];")?; }); ); for interface in &protocol.interfaces { - writeln!(out, "// {}\n", interface.name).unwrap(); + writeln!(out, "// {}\n", interface.name)?; emit_messages!(interface, requests); emit_messages!(interface, events); @@ -121,35 +122,34 @@ pub fn generate_interfaces(protocol: Protocol, out: &mut O) { out, "\npub static mut {}_interface: wl_interface = wl_interface {{", interface.name - ).unwrap(); + )?; writeln!( out, " name: b\"{}\\0\" as *const u8 as *const c_char,", interface.name - ).unwrap(); - writeln!(out, " version: {},", interface.version).unwrap(); - writeln!(out, " request_count: {},", interface.requests.len()).unwrap(); + )?; + writeln!(out, " version: {},", interface.version)?; + writeln!(out, " request_count: {},", interface.requests.len())?; if interface.requests.len() > 0 { writeln!( out, " requests: unsafe {{ &{}_requests as *const _ }},", interface.name - ).unwrap(); + )?; } else { - writeln!(out, " requests: NULLPTR as *const wl_message,").unwrap(); + writeln!(out, " requests: NULLPTR as *const wl_message,")?; } - writeln!(out, " event_count: {},", interface.events.len()).unwrap(); + writeln!(out, " event_count: {},", interface.events.len())?; if interface.events.len() > 0 { writeln!( out, " events: unsafe {{ &{}_events as *const _ }},", interface.name - ).unwrap(); + )?; } else { - writeln!(out, " events: NULLPTR as *const wl_message,").unwrap(); + writeln!(out, " events: NULLPTR as *const wl_message,")?; } - writeln!(out, "}};").unwrap(); - - writeln!(out, "").unwrap(); + writeln!(out, "}};\n")?; } + Ok(()) } diff --git a/wayland-scanner/src/code_gen.rs b/wayland-scanner/src/code_gen.rs deleted file mode 100644 index 86f85b14134..00000000000 --- a/wayland-scanner/src/code_gen.rs +++ /dev/null @@ -1,1042 +0,0 @@ -use Side; -use protocol::*; -use std::io::Result as IOResult; -use std::io::Write; -use util::*; - -pub fn write_protocol(protocol: Protocol, out: &mut O, side: Side) -> IOResult<()> { - writeln!( - out, - "//\n// This file was auto-generated, do not edit directly\n//\n" - ).unwrap(); - - if let Some(text) = protocol.copyright { - writeln!(out, "/*\n{}\n*/\n", text).unwrap(); - } - - for iface in &protocol.interfaces { - if (iface.name == "wl_display" || iface.name == "wl_registry") && side == Side::Server { - continue; - } - write_interface(iface, out, side)?; - } - Ok(()) -} - -fn write_interface(interface: &Interface, out: &mut O, side: Side) -> IOResult<()> { - writeln!(out, "pub mod {} {{", interface.name)?; - - if let Some((ref short, ref long)) = interface.description { - write_doc(Some(short), long, true, out, 1)?; - } - - if side == Side::Server { - writeln!(out, " use super::Client;")?; - } - writeln!(out, " use super::{};", side.handle_type())?; - writeln!(out, " use super::{};", side.object_trait())?; - writeln!(out, " use super::{};", side.result_type())?; - writeln!( - out, - r#" - use super::{{Liveness, Implementable}}; - use super::interfaces::*; - use wayland_sys::common::*; - use std::any::Any; - use std::ffi::{{CString,CStr}}; - use std::os::raw::c_void; - use std::ptr; - use std::sync::Arc; - use std::sync::atomic::{{AtomicBool, AtomicPtr, Ordering}}; - use wayland_sys::RUST_MANAGED;"# - )?; - match side { - Side::Client => { - writeln!(out, " use wayland_sys::client::*;")?; - writeln!( - out, - " type UserData = (*mut EventQueueHandle, Option>, Arc<(AtomicBool, AtomicPtr<()>)>);" - )?; - } - Side::Server => { - writeln!(out, " use wayland_sys::server::*;")?; - writeln!( - out, - " type UserData = (*mut EventLoopHandle, Option>,Arc<(AtomicBool, AtomicPtr<()>)>, Option);", - snake_to_camel(&interface.name) - )?; - } - }; - - writeln!( - out, - r#" - pub struct {} {{ - ptr: *mut {}, - data: Option)>> - }}"#, - snake_to_camel(&interface.name), - side.object_ptr_type() - )?; - - // Wayland proxies/ressources are Send+Sync - writeln!( - out, - r#" - unsafe impl Send for {0} {{}} - unsafe impl Sync for {0} {{}}"#, - snake_to_camel(&interface.name) - )?; - - // Generate object trait impl - writeln!( - out, - " unsafe impl {} for {} {{", - side.object_trait(), - snake_to_camel(&interface.name) - )?; - writeln!( - out, - " fn ptr(&self) -> *mut {} {{ self.ptr }}", - side.object_ptr_type() - )?; - writeln!( - out, - r#" - unsafe fn from_ptr_new(ptr: *mut {0}) -> {1} {{ - let data: *mut UserData = Box::into_raw(Box::new(( - ptr::null_mut(), - Option::None, - Arc::new((AtomicBool::new(true), AtomicPtr::new(ptr::null_mut()))),{3} - ))); - ffi_dispatch!({2}, {0}_set_user_data, ptr, data as *mut c_void); - {1} {{ ptr: ptr, data: Some((&*data).2.clone()) }} - }}"#, - side.object_ptr_type(), - snake_to_camel(&interface.name), - side.handle(), - if side == Side::Server { - "\n Option::None" - } else { - "" - } - )?; - writeln!( - out, - " unsafe fn from_ptr_initialized(ptr: *mut {0}) -> {1} {{", - side.object_ptr_type(), - snake_to_camel(&interface.name) - )?; - if let Side::Client = side { - writeln!( - out, - r#" - let implem = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_listener, ptr); - let rust_managed = implem == &RUST_MANAGED as *const _ as *const _;"# - )?; - } else { - writeln!( - out, - r#" - let rust_managed = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_instance_of, - ptr, Self::interface_ptr(), &RUST_MANAGED as *const _ as *const _ - ) != 0;"# - )?; - } - writeln!( - out, - r#" - if rust_managed {{ - let data = ffi_dispatch!({2}, {0}_get_user_data, ptr) as *mut UserData; - {1} {{ ptr: ptr, data: Some((&*data).2.clone()) }} - }} else {{ - {1} {{ ptr: ptr, data: Option::None }} - }} - }}"#, - side.object_ptr_type(), - snake_to_camel(&interface.name), - side.handle(), - )?; - writeln!( - out, - r#" - fn interface_ptr() -> *const wl_interface {{ unsafe {{ &{0}_interface }} }} - fn interface_name() -> &'static str {{ "{0}" }}"#, - interface.name - )?; - writeln!( - out, - " fn supported_version() -> u32 {{ {} }}", - interface.version - )?; - match side { - Side::Client => writeln!( - out, - " fn version(&self) -> u32 {{ unsafe {{ ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_version, self.ptr()) }} }}" - )?, - Side::Server => writeln!( - out, - " fn version(&self) -> i32 {{ unsafe {{ ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_version, self.ptr()) }} }}" - )?, - }; - writeln!( - out, - r#" - fn status(&self) -> Liveness {{ - if let Some(ref data) = self.data {{ - if data.0.load(Ordering::SeqCst) {{ - Liveness::Alive - }} else {{ - Liveness::Dead - }} - }} else {{ - Liveness::Unmanaged - }} - }}"# - )?; - - writeln!( - out, - r#" - fn equals(&self, other: &{}) -> bool {{ - self.status() != Liveness::Dead && other.status() != Liveness::Dead && self.ptr == other.ptr - }}"#, - snake_to_camel(&interface.name) - )?; - - writeln!( - out, - r#" - fn set_user_data(&self, ptr: *mut ()) {{ - if let Some(ref data) = self.data {{ - data.1.store(ptr, Ordering::SeqCst); - }} - }} - fn get_user_data(&self) -> *mut () {{ - if let Some(ref data) = self.data {{ - data.1.load(Ordering::SeqCst) - }} else {{ - ::std::ptr::null_mut() - }} - }}"# - )?; - writeln!( - out, - r#" - unsafe fn clone_unchecked(&self) -> {0} {{ - {0} {{ - ptr: self.ptr, - data: self.data.clone() - }} - }}"#, - snake_to_camel(&interface.name) - )?; - - writeln!(out, " }}")?; - - // dispatch machinnery - write_dispatch_func( - match side { - Side::Client => &interface.events, - Side::Server => &interface.requests, - }, - out, - side, - &interface.name, - )?; - - write_enums(&interface.enums, out)?; - - - // client-side events of wl_display are handled by the lib - if side != Side::Client || interface.name != "wl_display" { - write_implementation( - match side { - Side::Client => &interface.events, - Side::Server => &interface.requests, - }, - out, - side, - &interface.name, - )?; - } - - write_opcodes( - match side { - Side::Client => &interface.requests, - Side::Server => &interface.events, - }, - out, - &interface.name, - )?; - - write_impl( - match side { - Side::Client => &interface.requests, - Side::Server => &interface.events, - }, - out, - &interface.name, - side, - interface.destructor_sanitize().unwrap(), - )?; - - writeln!(out, "}}")?; - Ok(()) -} - -fn write_opcodes(messages: &[Message], out: &mut O, iname: &str) -> IOResult<()> { - for (i, msg) in messages.iter().enumerate() { - writeln!( - out, - " const {}_{}: u32 = {};", - snake_to_screaming(&iname), - snake_to_screaming(&msg.name), - i - )?; - } - Ok(()) -} - -fn write_enums(enums: &[Enum], out: &mut O) -> IOResult<()> { - // generate contents - for enu in enums { - if enu.bitfield { - writeln!(out, " bitflags! {{")?; - if let Some((ref short, ref long)) = enu.description { - write_doc(Some(short), long, false, out, 2)?; - } - writeln!( - out, - " pub struct {}: u32 {{", - snake_to_camel(&enu.name) - )?; - for entry in &enu.entries { - if let Some((ref short, ref long)) = entry.description { - write_doc(Some(short), long, false, out, 3)?; - } else if let Some(ref summary) = entry.summary { - writeln!(out, " /// {}", summary)?; - } - writeln!( - out, - " const {}{} = {};", - if entry.name.chars().next().unwrap().is_numeric() { - "_" - } else { - "" - }, - snake_to_camel(&entry.name), - entry.value - )?; - } - writeln!(out, " }}")?; - writeln!(out, " }}")?; - writeln!(out, " impl {} {{", snake_to_camel(&enu.name))?; - writeln!( - out, - " pub fn from_raw(n: u32) -> Option<{}> {{", - snake_to_camel(&enu.name) - )?; - writeln!( - out, - " Some({}::from_bits_truncate(n))", - snake_to_camel(&enu.name) - )?; - writeln!(out, " }}")?; - writeln!(out, " pub fn to_raw(&self) -> u32 {{")?; - writeln!(out, " self.bits()")?; - writeln!(out, " }}")?; - writeln!(out, " }}")?; - } else { - // if enu.bitfield - if let Some((ref short, ref long)) = enu.description { - write_doc(Some(short), long, false, out, 1)?; - } - writeln!( - out, - " #[repr(u32)]\n #[derive(Copy,Clone,Debug,PartialEq)]\n pub enum {} {{", - snake_to_camel(&enu.name) - )?; - for entry in &enu.entries { - if let Some((ref short, ref long)) = entry.description { - write_doc(Some(short), long, false, out, 2)?; - } else if let Some(ref summary) = entry.summary { - writeln!(out, " /// {}", summary)?; - } - writeln!( - out, - " {}{} = {},", - if entry.name.chars().next().unwrap().is_numeric() { - "_" - } else { - "" - }, - snake_to_camel(&entry.name), - entry.value - )?; - } - writeln!(out, " }}")?; - - writeln!(out, " impl {} {{", snake_to_camel(&enu.name))?; - writeln!( - out, - " pub fn from_raw(n: u32) -> Option<{}> {{", - snake_to_camel(&enu.name) - )?; - writeln!(out, " match n {{")?; - for entry in &enu.entries { - writeln!( - out, - " {} => Some({}::{}{}),", - entry.value, - snake_to_camel(&enu.name), - if entry.name.chars().next().unwrap().is_numeric() { - "_" - } else { - "" - }, - snake_to_camel(&entry.name) - )?; - } - writeln!(out, " _ => Option::None")?; - writeln!(out, " }}")?; - writeln!(out, " }}")?; - writeln!(out, " pub fn to_raw(&self) -> u32 {{")?; - writeln!(out, " *self as u32")?; - writeln!(out, " }}")?; - writeln!(out, " }}")?; - } - } - Ok(()) -} - -fn write_dispatch_func(messages: &[Message], out: &mut O, side: Side, iname: &str) -> IOResult<()> { - if iname == "wl_display" { - // these are not implementable - return Ok(()); - } else if messages.len() == 0 { - writeln!( - out, - r#" - unsafe impl Implementable for {} {{ - type Implementation = (); - #[allow(unused_mut,unused_assignments)] - unsafe fn __dispatch_msg(&self, {} opcode: u32, args: *const wl_argument) -> Result<(),()> {{ - return Err(()) - }} - }}"#, - snake_to_camel(iname), - if side == Side::Server { - "client: &Client," - } else { - "" - } - )?; - return Ok(()); - } - writeln!( - out, - " unsafe impl Implementable for {} {{", - snake_to_camel(iname) - )?; - writeln!(out, " type Implementation = Implementation;")?; - writeln!(out, " #[allow(unused_mut,unused_assignments)]")?; - writeln!( - out, - " unsafe fn __dispatch_msg(&self, {} opcode: u32, args: *const wl_argument) -> Result<(),()> {{", - if side == Side::Server { - "client: &Client," - } else { - "" - } - )?; - writeln!( - out, - r#" - let data = &mut *(ffi_dispatch!({}, {}_get_user_data, self.ptr()) as *mut UserData); - let evq = &mut *(data.0); - let mut kill = false; - {{ - let &mut (ref implementation, ref mut idata) = data.1.as_mut().unwrap().downcast_mut::<(Implementation, ID)>().unwrap();"#, - side.handle(), - side.object_ptr_type() - )?; - writeln!(out, " match opcode {{")?; - for (op, msg) in messages.iter().enumerate() { - writeln!(out, " {} => {{", op)?; - for (i, arg) in msg.args.iter().enumerate() { - write!(out, " let {} = {{", arg.name)?; - if arg.allow_null { - match arg.typ { - Type::Uint | Type::Int | Type::Fixed | Type::NewId => panic!( - "Argument {} for message {}.{} cannot be null given its type!", - i, - iname, - msg.name - ), - _ => {} - } - write!( - out, - "if (*(args.offset({}) as *const *const c_void)).is_null() {{ Option::None }} else {{ Some({{", - i - )?; - } - if let Some(ref name) = arg.enum_ { - write!( - out, - "match {}::from_raw(*(args.offset({}) as *const u32)) {{ Some(v) => v, Option::None => return Err(()) }}", - dotted_to_relname(name), - i - )?; - } else { - match arg.typ { - Type::Uint => write!(out, "*(args.offset({}) as *const u32)", i)?, - Type::Int | Type::Fd => write!(out, "*(args.offset({}) as *const i32)", i)?, - Type::Fixed => write!( - out, - "wl_fixed_to_double(*(args.offset({}) as *const i32))", - i - )?, - Type::Object => write!( - out, - "{}::from_ptr_initialized(*(args.offset({}) as *const *mut {}))", - side.object_trait(), - i, - side.object_ptr_type() - )?, - Type::String => write!( - out, - "String::from_utf8_lossy(CStr::from_ptr(*(args.offset({}) as *const *const _)).to_bytes()).into_owned()", - i - )?, - Type::Array => write!( - out, - "let array = *(args.offset({}) as *const *mut wl_array); ::std::slice::from_raw_parts((*array).data as *const u8, (*array).size as usize).to_owned()", - i - )?, - Type::NewId => match side { - Side::Client => write!( - out, - "Proxy::from_ptr_new(*(args.offset({}) as *const *mut wl_proxy))", - i - )?, - Side::Server => write!( - out, - "Resource::from_ptr_new(ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_create, client.ptr(), ::interface_ptr(), self.version(), *(args.offset({}) as *const u32)))", - arg.interface.as_ref().unwrap(), - snake_to_camel(arg.interface.as_ref().unwrap()), - i - )?, - }, - Type::Destructor => unreachable!(), - } - } - if arg.allow_null { - write!(out, "}})}}")?; - } - writeln!(out, "}};")?; - } - - if let Some(Type::Destructor) = msg.typ { - writeln!( - out, - r#" - (data.2).0.store(false, ::std::sync::atomic::Ordering::SeqCst); - kill = true; - ffi_dispatch!({0}, {1}_destroy, self.ptr());"#, - side.handle(), - side.object_ptr_type() - )?; - } - - write!( - out, - " (implementation.{}{})(evq, idata, {} self", - msg.name, - if is_keyword(&msg.name) { "_" } else { "" }, - if side == Side::Server { "client," } else { "" } - )?; - for arg in &msg.args { - match arg.typ { - Type::Object => if arg.allow_null { - write!(out, ", {}.as_ref()", arg.name)? - } else { - write!(out, ", &{}", arg.name)? - }, - _ => write!(out, ", {}", arg.name)?, - }; - } - writeln!(out, ");")?; - writeln!(out, " }},")?; - } - writeln!(out, " _ => return Err(())")?; - writeln!(out, " }}")?; - writeln!(out, " }}")?; - writeln!( - out, - r#" - if kill {{ - let _impl = data.1.take(); - ::std::mem::drop(_impl); - }}"#, - )?; - writeln!(out, " Ok(())")?; - writeln!(out, " }}")?; - writeln!(out, " }}")?; - Ok(()) -} - -fn write_implementation(messages: &[Message], out: &mut O, side: Side, iname: &str) - -> IOResult<()> { - if messages.len() == 0 { - return Ok(()); - } - writeln!(out, " pub struct Implementation {{")?; - for msg in messages { - if let Some((ref short, ref long)) = msg.description { - write_doc(Some(short), long, false, out, 2)?; - } - write!( - out, - " ///\n /// **Arguments:** event_queue_handle, interface_data, {}{}", - if side == Side::Server { "client, " } else { "" }, - iname - )?; - for arg in &msg.args { - write!(out, ", {}", arg.name)?; - } - writeln!(out)?; - if let Some(Type::Destructor) = msg.typ { - writeln!( - out, - " ///\n /// This is a destructor, you cannot send {} to this object once this method is called.", - match side { - Side::Server => "events", - Side::Client => "requests", - } - )?; - } - if msg.since > 1 { - writeln!( - out, - " ///\n /// This {} only exists since version {} of the interface", - match side { - Side::Client => "request", - Side::Server => "event", - }, - msg.since - )?; - } - - write!( - out, - " pub {}{}: fn(evqh: &mut {}, data: &mut ID, {} {}: &{}", - msg.name, - if is_keyword(&msg.name) { "_" } else { "" }, - side.handle_type(), - if side == Side::Server { - "client: &Client, " - } else { - "" - }, - iname, - snake_to_camel(iname) - )?; - for arg in &msg.args { - write!( - out, - ", {}{}: {}{}{}", - if is_keyword(&arg.name) { "_" } else { "" }, - arg.name, - if arg.allow_null { "Option<" } else { "" }, - if let Some(ref name) = arg.enum_ { - dotted_to_relname(name) - } else { - match arg.typ { - // TODO handle unspecified interface - Type::Object => arg.interface - .as_ref() - .map(|s| format!("&super::{}::{}", s, snake_to_camel(s))) - .unwrap_or(format!("*mut {}", side.object_ptr_type())), - Type::NewId => arg.interface - .as_ref() - .map(|s| format!("super::{}::{}", s, snake_to_camel(s))) - .unwrap_or("(&str, u32)".into()), - _ => arg.typ.rust_type().into(), - } - }, - if arg.allow_null { ">" } else { "" } - )?; - } - writeln!(out, "),")?; - } - writeln!(out, " }}")?; - // manual impls to work around derive issues - writeln!( - out, - r#" - impl Copy for Implementation {{}} - impl Clone for Implementation {{ - fn clone(&self) -> Implementation {{ - *self - }} - }}"# - )?; - // manual partialEq impl, because we can't simply compare - // function pointers (duh :( ) - writeln!( - out, - r#" - impl PartialEq for Implementation {{ - fn eq(&self, other: &Implementation) -> bool {{ - true"# - )?; - for msg in messages { - writeln!( - out, - " && (self.{0}{1} as usize == other.{0}{1} as usize)", - msg.name, - if is_keyword(&msg.name) { "_" } else { "" }, - )?; - } - writeln!( - out, - r#" - }} - }} -"# - )?; - Ok(()) -} - -fn write_impl(messages: &[Message], out: &mut O, iname: &str, side: Side, destroyable: bool) - -> IOResult<()> { - // server-side objects can always be destroyed: if the client disconnects. - let destroyable = destroyable || side == Side::Server; - - writeln!(out, " impl {} {{", snake_to_camel(iname))?; - - for msg in messages { - if let Some((ref short, ref long)) = msg.description { - write_doc(Some(short), long, false, out, 2)?; - } - if let Some(Type::Destructor) = msg.typ { - writeln!( - out, - " ///\n /// This is a destructor, you cannot send {} to this object once this method is called.", - match side { - Side::Server => "events", - Side::Client => "requests", - } - )?; - } - if msg.since > 1 { - writeln!( - out, - " ///\n /// This {} is only available since version {} of the interface", - match side { - Side::Client => "request", - Side::Server => "event", - }, - msg.since - )?; - } - - // detect new_id - let mut newid = None; - for arg in &msg.args { - match arg.typ { - Type::NewId => if newid.is_some() { - panic!( - "Request {}.{} returns more than one new_id", - iname, - msg.name - ); - } else { - newid = Some(arg); - }, - _ => (), - } - } - - // method start - match newid { - Some(arg) if arg.interface.is_none() && side == Side::Client => { - write!( - out, - " pub fn {}{}(&self, version: u32", - if is_keyword(&msg.name) { "_" } else { "" }, - msg.name, - side.object_trait() - )?; - } - _ => { - write!( - out, - " pub fn {}{}(&self", - if is_keyword(&msg.name) { "_" } else { "" }, - msg.name - )?; - } - } - - // print args - for arg in &msg.args { - write!( - out, - ", {}{}: {}{}{}", - if is_keyword(&arg.name) { "_" } else { "" }, - arg.name, - if arg.allow_null { "Option<" } else { "" }, - if let Some(ref name) = arg.enum_ { - dotted_to_relname(name) - } else { - match arg.typ { - Type::Object => arg.interface - .as_ref() - .map(|s| format!("&super::{}::{}", s, snake_to_camel(s))) - .unwrap_or(format!("*mut {}", side.object_ptr_type())), - Type::NewId => { - if side == Side::Server { - arg.interface - .as_ref() - .map(|s| format!("&super::{}::{}", s, snake_to_camel(s))) - .unwrap_or(format!("*mut {}", side.object_ptr_type())) - } else { - // client-side, the return-type handles that - continue; - } - } - _ => arg.typ.rust_type().into(), - } - }, - if arg.allow_null { ">" } else { "" } - )?; - } - write!(out, ") ->")?; - - // return type - if destroyable { - write!(out, "{}<", side.result_type())?; - } - write!( - out, - "{}", - if let (Some(arg), Side::Client) = (newid, side) { - arg.interface - .as_ref() - .map(|s| format!("super::{}::{}", s, snake_to_camel(s))) - .unwrap_or("T".into()) - } else { - "()".into() - } - )?; - if destroyable { - write!(out, ">")?; - } - writeln!(out, " {{")?; - - // check liveness - if destroyable { - writeln!( - out, - " if self.status() == Liveness::Dead {{ return {}::Destroyed }}", - side.result_type() - )?; - } - - // arg translation for some types - for arg in &msg.args { - match arg.typ { - Type::Fixed => { - writeln!( - out, - " let {0} = wl_fixed_from_double({0});", - arg.name - )?; - } - Type::Array => if arg.allow_null { - writeln!( - out, - " let {0} = {0}.as_ref().map(|v| wl_array {{ size: v.len(), alloc: v.capacity(), data: v.as_ptr() as *mut _ }});", - arg.name - )?; - } else { - writeln!( - out, - " let {0} = wl_array {{ size: {0}.len(), alloc: {0}.capacity(), data: {0}.as_ptr() as *mut _ }};", - arg.name - )?; - }, - Type::String => if arg.allow_null { - writeln!( - out, - " let {0} = {0}.map(|s| CString::new(s).unwrap_or_else(|_| panic!(\"Got a String with interior null in {1}.{2}:{0}\")));", - arg.name, - iname, - msg.name - )?; - } else { - writeln!( - out, - " let {0} = CString::new({0}).unwrap_or_else(|_| panic!(\"Got a String with interior null in {1}.{2}:{0}\"));", - arg.name, - iname, - msg.name - )?; - }, - _ => (), - } - } - - // code generation - if side == Side::Client { - if let Some(arg) = newid { - if let Some(ref iface) = arg.interface { - // FIXME: figure if argument order is really correct in the general case - write!( - out, - " let ptr = unsafe {{ ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_marshal_constructor, self.ptr(), {}_{}, &{}_interface", - snake_to_screaming(iname), - snake_to_screaming(&msg.name), - iface - )?; - } else { - writeln!( - out, - " if version > ::supported_version() {{" - )?; - writeln!( - out, - " panic!(\"Tried to bind interface {{}} with version {{}} while it is only supported up to {{}}.\", ::interface_name(), version, ::supported_version())" - )?; - writeln!(out, " }}")?; - write!( - out, - " let ptr = unsafe {{ ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_marshal_constructor_versioned, self.ptr(), {}_{}, ::interface_ptr(), version", - snake_to_screaming(iname), - snake_to_screaming(&msg.name), - )?; - } - } else { - write!( - out, - " unsafe {{ ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_marshal, self.ptr(), {}_{}", - snake_to_screaming(iname), - snake_to_screaming(&msg.name) - )?; - } - } else { - write!( - out, - " unsafe {{ ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_post_event, self.ptr(), {}_{}", - snake_to_screaming(iname), - snake_to_screaming(&msg.name) - )?; - } - - // write actual args - for arg in &msg.args { - match arg.typ { - Type::NewId if side == Side::Client => { - if newid.map(|a| a.interface.is_none()).unwrap() { - write!(out, ", (*::interface_ptr()).name, version")?; - } - write!(out, ", ptr::null_mut::()")?; - } - Type::String => if arg.allow_null { - write!( - out, - ", {}.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null())", - arg.name - )?; - } else { - write!(out, ", {}.as_ptr()", arg.name)?; - }, - Type::Array => if arg.allow_null { - write!( - out, - ", {}.as_ref().map(|a| a as *const wl_array).unwrap_or(ptr::null())", - arg.name - )?; - } else { - write!(out, ", &{} as *const wl_array", arg.name)?; - }, - Type::Object => if arg.allow_null { - write!( - out, - ", {}.map({}::ptr).unwrap_or(ptr::null_mut())", - arg.name, - side.object_trait() - )?; - } else { - write!(out, ", {}.ptr()", arg.name)?; - }, - _ => if arg.allow_null { - write!(out, ", {}.unwrap_or(0)", arg.name)?; - } else { - write!(out, ", {}", arg.name)?; - }, - } - } - - writeln!(out, ") }};")?; - - if let Some(Type::Destructor) = msg.typ { - writeln!( - out, - r#" - if let Some(ref data) = self.data {{ - data.0.store(false, ::std::sync::atomic::Ordering::SeqCst); - }} - let udata = unsafe {{ &mut *(ffi_dispatch!({0}, {1}_get_user_data, self.ptr()) as *mut UserData) }}; - let _impl = udata.1.take(); - ::std::mem::drop(_impl); - unsafe {{ ffi_dispatch!({0}, {1}_destroy, self.ptr()); }}"#, - side.handle(), - side.object_ptr_type() - )?; - } - - if newid.is_some() && side == Side::Client { - writeln!( - out, - " let proxy = unsafe {{ Proxy::from_ptr_new(ptr) }};" - )?; - if destroyable { - writeln!(out, " {}::Sent(proxy)", side.result_type())?; - } else { - writeln!(out, " proxy")?; - } - } else if destroyable { - writeln!(out, " {}::Sent(())", side.result_type())?; - } - - writeln!(out, " }}")?; - } - writeln!(out, " }}")?; - Ok(()) -} - -fn write_doc(short: Option<&str>, long: &str, internal: bool, out: &mut O, indent: usize) - -> IOResult<()> { - let p = if internal { '!' } else { '/' }; - if let Some(txt) = short { - for _ in 0..indent { - write!(out, " ")? - } - writeln!(out, "//{} {}", p, txt)?; - for _ in 0..indent { - write!(out, " ")? - } - writeln!(out, "//{}", p)?; - } - for l in long.lines() { - for _ in 0..indent { - write!(out, " ")? - } - writeln!(out, "//{} {}", p, l.trim())?; - } - Ok(()) -} diff --git a/wayland-scanner/src/common_gen.rs b/wayland-scanner/src/common_gen.rs new file mode 100644 index 00000000000..025232995e5 --- /dev/null +++ b/wayland-scanner/src/common_gen.rs @@ -0,0 +1,267 @@ +use std::io::Result as IOResult; +use std::io::Write; + +use protocol::*; +use util::*; +use Side; + +pub(crate) fn write_prefix(protocol: &Protocol, out: &mut O) -> IOResult<()> { + writeln!( + out, + r#" +// +// This file was auto-generated, do not edit directly. +// +"# + )?; + if let Some(ref text) = protocol.copyright { + writeln!(out, "/*\n{}\n*/\n", text)?; + } + Ok(()) +} + +pub(crate) fn write_messagegroup( + name: &str, + side: Side, + receiver: bool, + messages: &[Message], + out: &mut O, +) -> IOResult<()> { + writeln!(out, " pub enum {} {{", name)?; + for m in messages { + if let Some((ref short, ref long)) = m.description { + write_doc(Some(short), long, false, out, 2)?; + } + if let Some(Type::Destructor) = m.typ { + writeln!( + out, + " ///\n /// This is a destructor, once {} this object cannot be used any longer.", + if receiver { "received" } else { "sent" } + )?; + } + if m.since > 1 { + writeln!( + out, + " ///\n /// Only available since version {} of the interface", + m.since + )?; + } + + write!(out, " {}", snake_to_camel(&m.name))?; + if m.args.len() > 0 { + write!(out, " {{")?; + for a in &m.args { + write!(out, "{}: ", a.name)?; + if a.allow_null { + write!(out, "Option<")?; + } + if let Some(ref enu) = a.enum_ { + write!(out, "{}", dotted_to_relname(enu))?; + } else { + match a.typ { + Type::Uint => write!(out, "u32")?, + Type::Int => write!(out, "i32")?, + Type::Fixed => write!(out, "f64")?, + Type::String => write!(out, "String")?, + Type::Array => write!(out, "Vec")?, + Type::Fd => write!(out, "::std::os::unix::io::RawFd")?, + Type::Object => { + if let Some(ref iface) = a.interface { + write!( + out, + "{}", + side.object_name(), + iface, + snake_to_camel(iface) + )?; + } else { + write!(out, "{}", side.object_name())?; + } + } + Type::NewId => { + if let Some(ref iface) = a.interface { + write!( + out, + "{}{}", + if receiver { "New" } else { "" }, + side.object_name(), + iface, + snake_to_camel(iface) + )?; + } else { + // bind-like function + write!( + out, + "(String, u32, {}{})", + if receiver { "New" } else { "" }, + side.object_name() + )?; + } + } + Type::Destructor => panic!("An argument cannot have type \"destructor\"."), + } + } + if a.allow_null { + write!(out, ">")?; + } + write!(out, ", ")?; + } + write!(out, "}}")?; + } + writeln!(out, ",")? + } + writeln!(out, " }}\n")?; + Ok(()) +} + +pub(crate) fn write_enums(enums: &[Enum], out: &mut O) -> IOResult<()> { + // generate contents + for enu in enums { + if enu.bitfield { + writeln!(out, " bitflags! {{")?; + if let Some((ref short, ref long)) = enu.description { + write_doc(Some(short), long, false, out, 2)?; + } + writeln!( + out, + " pub struct {}: u32 {{", + snake_to_camel(&enu.name) + )?; + for entry in &enu.entries { + if let Some((ref short, ref long)) = entry.description { + write_doc(Some(short), long, false, out, 3)?; + } else if let Some(ref summary) = entry.summary { + writeln!(out, " /// {}", summary)?; + } + writeln!( + out, + " const {}{} = {};", + if entry.name.chars().next().unwrap().is_numeric() { + "_" + } else { + "" + }, + snake_to_camel(&entry.name), + entry.value + )?; + } + writeln!(out, " }}")?; + writeln!(out, " }}")?; + writeln!(out, " impl {} {{", snake_to_camel(&enu.name))?; + writeln!( + out, + " pub fn from_raw(n: u32) -> Option<{}> {{", + snake_to_camel(&enu.name) + )?; + writeln!( + out, + " Some({}::from_bits_truncate(n))", + snake_to_camel(&enu.name) + )?; + writeln!( + out, + r#" + }} + pub fn to_raw(&self) -> u32 {{ + self.bits() + }} + }} +"# + )?; + } else { + // if enu.bitfield + if let Some((ref short, ref long)) = enu.description { + write_doc(Some(short), long, false, out, 1)?; + } + writeln!( + out, + r#" + #[repr(u32)] + #[derive(Copy,Clone,Debug,PartialEq)] + pub enum {} {{"#, + snake_to_camel(&enu.name) + )?; + for entry in &enu.entries { + if let Some((ref short, ref long)) = entry.description { + write_doc(Some(short), long, false, out, 2)?; + } else if let Some(ref summary) = entry.summary { + writeln!(out, " /// {}", summary)?; + } + writeln!( + out, + " {}{} = {},", + if entry.name.chars().next().unwrap().is_numeric() { + "_" + } else { + "" + }, + snake_to_camel(&entry.name), + entry.value + )?; + } + writeln!(out, " }}")?; + + writeln!(out, " impl {} {{", snake_to_camel(&enu.name))?; + writeln!( + out, + " pub fn from_raw(n: u32) -> Option<{}> {{", + snake_to_camel(&enu.name) + )?; + writeln!(out, " match n {{")?; + for entry in &enu.entries { + writeln!( + out, + " {} => Some({}::{}{}),", + entry.value, + snake_to_camel(&enu.name), + if entry.name.chars().next().unwrap().is_numeric() { + "_" + } else { + "" + }, + snake_to_camel(&entry.name) + )?; + } + writeln!( + out, + r#" + _ => Option::None + }} + }} + pub fn to_raw(&self) -> u32 {{ + *self as u32 + }} + }} +"# + )?; + } + } + Ok(()) +} + +pub(crate) fn write_doc( + short: Option<&str>, + long: &str, + internal: bool, + out: &mut O, + indent: usize, +) -> IOResult<()> { + let p = if internal { '!' } else { '/' }; + if let Some(txt) = short { + for _ in 0..indent { + write!(out, " ")? + } + writeln!(out, "//{} {}", p, txt)?; + for _ in 0..indent { + write!(out, " ")? + } + writeln!(out, "//{}", p)?; + } + for l in long.lines() { + for _ in 0..indent { + write!(out, " ")? + } + writeln!(out, "//{} {}", p, l.trim())?; + } + Ok(()) +} diff --git a/wayland-scanner/src/lib.rs b/wayland-scanner/src/lib.rs index 16846e7690b..c1e910e5ee2 100644 --- a/wayland-scanner/src/lib.rs +++ b/wayland-scanner/src/lib.rs @@ -11,7 +11,7 @@ //! ## How to use this crate //! //! This crate is to be used in a build script. It provides you two -//! functions, `generate_code` and `generate_interfaces`. They'll +//! functions, `generate_c_code` and `generate_c_interfaces`. They'll //! allow you to generate the code to use with the `wayland_client` or //! `wayland_server` crates for any XML wayland protocol file (NB: you don't //! need to do it for the core protocol, which is already included in both crates). @@ -19,7 +19,7 @@ //! First, have the XML files you want to use in your project, somewhere the build script //! will be able to read them. //! -//! Then, you'll need to invoke both `generate_interfaces` *and* `generate_code` for +//! Then, you'll need to invoke both `generate_c_interfaces` *and* `generate_c_code` for //! each of these files. //! //! A sample build script: @@ -30,7 +30,7 @@ //! use std::env::var; //! use std::path::Path; //! -//! use wayland_scanner::{Side, generate_code, generate_interfaces}; +//! use wayland_scanner::{Side, generate_c_code, generate_c_interfaces}; //! //! fn main() { //! // Location of the xml file, relative to the `Cargo.toml` @@ -40,14 +40,14 @@ //! let out_dir_str = var("OUT_DIR").unwrap(); //! let out_dir = Path::new(&out_dir_str); //! -//! generate_code( +//! generate_c_code( //! protocol_file, //! out_dir.join("my_protocol_api.rs"), //! Side::Client, // Replace by `Side::Server` for server-side code //! ); //! //! // interfaces are the same for client and server -//! generate_interfaces( +//! generate_c_interfaces( //! protocol_file, //! out_dir.join("my_protocol_interfaces.rs") //! ); @@ -83,17 +83,9 @@ //! } //! //! pub mod client { -//! // These import are mandatory, and need to by `pub`, because -//! // submodules in the generated code will try to import them. -//! // Hopefully someday pub(restricted) and friends will -//! // allow for a cleaner way to do that -//! #[doc(hidden)] pub use wayland_client::{Proxy, Handler, EventQueueHandle, RequestResult}; -//! // Replace the above line with this for server-side: -//! // #[doc(hidden)] pub use wayland_server::{Resource, Handler, EventLoopHandle, EventResult}; -//! #[doc(hidden)] pub use super::interfaces; //! // If you protocol interacts with objects from other protocols, you'll need to import //! // their modules, like so: -//! #[doc(hidden)] pub use wayland_client::protocol::{wl_surface, wl_region}; +//! pub(crate) use wayland_client::protocol::{wl_surface, wl_region}; //! include!(concat!(env!("OUT_DIR"), "/my_protocol_code.rs")); //! } //! } @@ -111,8 +103,9 @@ mod util; mod parse; mod protocol; mod side; -mod interface_gen; -mod code_gen; +mod common_gen; +mod c_interface_gen; +mod c_code_gen; pub use side::Side; @@ -136,7 +129,7 @@ fn load_xml>(prot: P) -> protocol::Protocol { /// - `protocol`: a path to the XML file describing the protocol, absolute or relative to /// the build script using this function. /// - `target`: the path of the file to store this interfaces in. -pub fn generate_interfaces, P2: AsRef>(protocol: P1, target: P2) { +pub fn generate_c_interfaces, P2: AsRef>(protocol: P1, target: P2) { let protocol = load_xml(protocol); let mut out = OpenOptions::new() .write(true) @@ -144,7 +137,7 @@ pub fn generate_interfaces, P2: AsRef>(protocol: P1, targe .create(true) .open(target) .unwrap(); - interface_gen::generate_interfaces(protocol, &mut out); + c_interface_gen::generate_interfaces(protocol, &mut out).unwrap() } /// Generate the code for a protocol @@ -157,7 +150,7 @@ pub fn generate_interfaces, P2: AsRef>(protocol: P1, targe /// the build script using this function. /// - `target`: the path of the file to store the code in. /// - `side`: the side (client or server) to generate code for. -pub fn generate_code, P2: AsRef>(prot: P1, target: P2, side: Side) { +pub fn generate_c_code, P2: AsRef>(prot: P1, target: P2, side: Side) { let protocol = load_xml(prot); let mut out = OpenOptions::new() .write(true) @@ -165,20 +158,23 @@ pub fn generate_code, P2: AsRef>(prot: P1, target: P2, sid .create(true) .open(target) .unwrap(); - code_gen::write_protocol(protocol, &mut out, side).unwrap() + match side { + Side::Client => c_code_gen::write_protocol_client(protocol, &mut out).unwrap(), + Side::Server => c_code_gen::write_protocol_server(protocol, &mut out).unwrap(), + } } /// Generate the interfaces for a protocol from/to IO streams /// -/// Like `generate_interfaces`, but takes IO Streams directly rather than filenames +/// Like `generate_c_interfaces`, but takes IO Streams directly rather than filenames /// /// Args: /// /// - `protocol`: an object `Read`-able containing the XML protocol file /// - `target`: a `Write`-able object to which the generated code will be outputed to -pub fn generate_interfaces_streams(protocol: P1, target: &mut P2) { +pub fn generate_c_interfaces_streams(protocol: P1, target: &mut P2) { let protocol = parse::parse_stream(protocol); - interface_gen::generate_interfaces(protocol, target); + c_interface_gen::generate_interfaces(protocol, target).unwrap(); } /// Generate the code for a protocol from/to IO streams @@ -190,7 +186,10 @@ pub fn generate_interfaces_streams(protocol: P1, target: &m /// - `protocol`: an object `Read`-able containing the XML protocol file /// - `target`: a `Write`-able object to which the generated code will be outputed to /// - `side`: the side (client or server) to generate code for. -pub fn generate_code_streams(protocol: P1, target: &mut P2, side: Side) { +pub fn generate_c_code_streams(protocol: P1, target: &mut P2, side: Side) { let protocol = parse::parse_stream(protocol); - code_gen::write_protocol(protocol, target, side).unwrap() + match side { + Side::Client => c_code_gen::write_protocol_client(protocol, target).unwrap(), + Side::Server => c_code_gen::write_protocol_server(protocol, target).unwrap(), + } } diff --git a/wayland-scanner/src/parse.rs b/wayland-scanner/src/parse.rs index 1b9c07bd4de..43fa2c6fa8e 100644 --- a/wayland-scanner/src/parse.rs +++ b/wayland-scanner/src/parse.rs @@ -1,5 +1,3 @@ - - use protocol::*; use std::io::Read; use xml::EventReader; @@ -59,18 +57,16 @@ fn parse_protocol<'a, S: Read + 'a>(mut iter: Events) -> Protocol { loop { match iter.next() { - Some( - Ok(XmlEvent::StartElement { - name, attributes, .. - }), - ) => { + Some(Ok(XmlEvent::StartElement { + name, attributes, .. + })) => { match &name.local_name[..] { "copyright" => { // parse the copyright let copyright = match iter.next() { - Some(Ok(XmlEvent::Characters(copyright))) | - Some(Ok(XmlEvent::CData(copyright))) => copyright, - e => panic!("Ill-formed protocol file: {:?}", e) + Some(Ok(XmlEvent::Characters(copyright))) + | Some(Ok(XmlEvent::CData(copyright))) => copyright, + e => panic!("Ill-formed protocol file: {:?}", e), }; extract_end_tag!(iter => "copyright"); @@ -86,8 +82,7 @@ fn parse_protocol<'a, S: Read + 'a>(mut iter: Events) -> Protocol { } _ => panic!( "Ill-formed protocol file: unexpected token `{}` in protocol {}", - name.local_name, - protocol.name + name.local_name, protocol.name ), } } @@ -118,11 +113,9 @@ fn parse_interface<'a, S: Read + 'a>(iter: &mut Events, attrs: Vec match &name.local_name[..] { + Some(Ok(XmlEvent::StartElement { + name, attributes, .. + })) => match &name.local_name[..] { "description" => interface.description = Some(parse_description(iter, attributes)), "request" => interface.requests.push(parse_request(iter, attributes)), "event" => interface.events.push(parse_event(iter, attributes)), @@ -171,11 +164,9 @@ fn parse_request<'a, S: Read + 'a>(iter: &mut Events, attrs: Vec match &name.local_name[..] { + Some(Ok(XmlEvent::StartElement { + name, attributes, .. + })) => match &name.local_name[..] { "description" => request.description = Some(parse_description(iter, attributes)), "arg" => request.args.push(parse_arg(iter, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), @@ -203,11 +194,9 @@ fn parse_enum<'a, S: Read + 'a>(iter: &mut Events, attrs: Vec loop { match iter.next() { - Some( - Ok(XmlEvent::StartElement { - name, attributes, .. - }), - ) => match &name.local_name[..] { + Some(Ok(XmlEvent::StartElement { + name, attributes, .. + })) => match &name.local_name[..] { "description" => enu.description = Some(parse_description(iter, attributes)), "entry" => enu.entries.push(parse_entry(iter, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), @@ -232,11 +221,9 @@ fn parse_event<'a, S: Read + 'a>(iter: &mut Events, attrs: Vec match &name.local_name[..] { + Some(Ok(XmlEvent::StartElement { + name, attributes, .. + })) => match &name.local_name[..] { "description" => event.description = Some(parse_description(iter, attributes)), "arg" => event.args.push(parse_arg(iter, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), @@ -267,11 +254,9 @@ fn parse_arg<'a, S: Read + 'a>(iter: &mut Events, attrs: Vec) loop { match iter.next() { - Some( - Ok(XmlEvent::StartElement { - name, attributes, .. - }), - ) => match &name.local_name[..] { + Some(Ok(XmlEvent::StartElement { + name, attributes, .. + })) => match &name.local_name[..] { "description" => arg.description = Some(parse_description(iter, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), }, @@ -312,11 +297,9 @@ fn parse_entry<'a, S: Read + 'a>(iter: &mut Events, attrs: Vec match &name.local_name[..] { + Some(Ok(XmlEvent::StartElement { + name, attributes, .. + })) => match &name.local_name[..] { "description" => entry.description = Some(parse_description(iter, attributes)), _ => panic!("Unexpected tocken: `{}`", name.local_name), }, diff --git a/wayland-scanner/src/protocol.rs b/wayland-scanner/src/protocol.rs index d1775f401e3..e1bfb96b38c 100644 --- a/wayland-scanner/src/protocol.rs +++ b/wayland-scanner/src/protocol.rs @@ -1,4 +1,3 @@ - #[derive(Debug)] pub struct Protocol { pub name: String, @@ -39,40 +38,6 @@ impl Interface { enums: Vec::new(), } } - - pub fn destructor_sanitize(&self) -> Result { - let mut found_destructor = false; - for req in &self.requests { - if req.name == "destroy" { - if let Some(Type::Destructor) = req.typ { - // all is good - } else { - return Err(format!( - "Request '{}.destroy' is not a destructor.", - self.name - )); - } - } - if let Some(Type::Destructor) = req.typ { - // sanity checks - if req.args.len() > 0 { - return Err(format!( - "Destructor request '{}.{}' cannot take arguments.", - self.name, - req.name - )); - } - if found_destructor { - return Err(format!( - "Interface {} has more than one destructor.", - self.name - )); - } - found_destructor = true - } - } - Ok(found_destructor) - } } #[derive(Debug)] @@ -98,9 +63,9 @@ impl Message { } pub fn all_null(&self) -> bool { - self.args.iter().all(|a| { - !((a.typ == Type::Object || a.typ == Type::NewId) && a.interface.is_some()) - }) + self.args + .iter() + .all(|a| !((a.typ == Type::Object || a.typ == Type::NewId) && a.interface.is_some())) } } diff --git a/wayland-scanner/src/side.rs b/wayland-scanner/src/side.rs index 6a369e34420..02f026f6555 100644 --- a/wayland-scanner/src/side.rs +++ b/wayland-scanner/src/side.rs @@ -4,7 +4,7 @@ use self::Side::{Client, Server}; /// /// This enum represents the two possible sides of /// the protocol API that can be generated. -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum Side { /// wayland client applications Client, @@ -12,40 +12,11 @@ pub enum Side { Server, } -#[doc(hidden)] impl Side { - pub fn object_ptr_type(&self) -> &'static str { - match *self { - Client => "wl_proxy", - Server => "wl_resource", - } - } - - pub fn object_trait(&self) -> &'static str { + pub(crate) fn object_name(&self) -> &'static str { match *self { Client => "Proxy", Server => "Resource", } } - - pub fn handle_type(&self) -> &'static str { - match *self { - Client => "EventQueueHandle", - Server => "EventLoopHandle", - } - } - - pub fn handle(&self) -> &'static str { - match *self { - Client => "WAYLAND_CLIENT_HANDLE", - Server => "WAYLAND_SERVER_HANDLE", - } - } - - pub fn result_type(&self) -> &'static str { - match *self { - Client => "RequestResult", - Server => "EventResult", - } - } } diff --git a/wayland-scanner/src/util.rs b/wayland-scanner/src/util.rs index fdabca71ecc..5e65e267cdd 100644 --- a/wayland-scanner/src/util.rs +++ b/wayland-scanner/src/util.rs @@ -1,59 +1,14 @@ +#[allow(unused_imports)] use std::ascii::AsciiExt; pub fn is_keyword(txt: &str) -> bool { match txt { - "abstract" | - "alignof" | - "as" | - "become" | - "box" | - "break" | - "const" | - "continue" | - "crate" | - "do" | - "else" | - "enum" | - "extern" | - "false" | - "final" | - "fn" | - "for" | - "if" | - "impl" | - "in" | - "let" | - "loop" | - "macro" | - "match" | - "mod" | - "move" | - "mut" | - "offsetof" | - "override" | - "priv" | - "proc" | - "pub" | - "pure" | - "ref" | - "return" | - "Self" | - "self" | - "sizeof" | - "static" | - "struct" | - "super" | - "trait" | - "true" | - "type" | - "typeof" | - "unsafe" | - "unsized" | - "use" | - "virtual" | - "where" | - "while" | - "yield" => true, + "abstract" | "alignof" | "as" | "become" | "box" | "break" | "const" | "continue" | "crate" + | "do" | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" | "if" | "impl" | "in" + | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut" | "offsetof" | "override" | "priv" + | "proc" | "pub" | "pure" | "ref" | "return" | "Self" | "self" | "sizeof" | "static" | "struct" + | "super" | "trait" | "true" | "type" | "typeof" | "unsafe" | "unsized" | "use" | "virtual" + | "where" | "while" | "yield" => true, _ => false, } } @@ -63,20 +18,18 @@ pub fn snake_to_camel(input: &str) -> String { .split('_') .flat_map(|s| { let mut first = true; - s.chars().map(move |c| if first { - first = false; - c.to_ascii_uppercase() - } else { - c + s.chars().map(move |c| { + if first { + first = false; + c.to_ascii_uppercase() + } else { + c + } }) }) .collect() } -pub fn snake_to_screaming(input: &str) -> String { - input.chars().map(|c| c.to_ascii_uppercase()).collect() -} - pub fn dotted_to_relname(input: &str) -> String { let mut it = input.split('.'); match (it.next(), it.next()) { diff --git a/wayland-server/Cargo.toml b/wayland-server/Cargo.toml index 00421db8bd4..079232f6b07 100644 --- a/wayland-server/Cargo.toml +++ b/wayland-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wayland-server" -version = "0.14.1" +version = "0.20.0" documentation = "https://smithay.github.io/wayland-rs/wayland_server/" repository = "https://github.com/smithay/wayland-rs" authors = ["Victor Berger "] @@ -14,14 +14,16 @@ build = "build.rs" travis-ci = { repository = "smithay/wayland-rs" } [dependencies] -wayland-sys = { version = "0.14.1", features = ["server"], path = "../wayland-sys" } -libc = "0.2" -nix = "0.9" +wayland-commons = { version = "0.20.0", path = "../wayland-commons" } +wayland-sys = { version = "0.20.0", features = ["server"], path = "../wayland-sys", optional = true } bitflags = "1.0" -token_store = "0.1.1" +libc = "0.2" +nix = "0.10" [build-dependencies] -wayland-scanner = { version = "0.14.1", path = "../wayland-scanner" } +wayland-scanner = { version = "0.20.0", path = "../wayland-scanner" } [features] +default = ["native_lib"] +native_lib = [ "wayland-sys", "wayland-commons/native_lib" ] dlopen = ["wayland-sys/dlopen"] diff --git a/wayland-server/build.rs b/wayland-server/build.rs index f9ae60c3854..1d0cf5c62f2 100644 --- a/wayland-server/build.rs +++ b/wayland-server/build.rs @@ -2,7 +2,7 @@ extern crate wayland_scanner; use std::env::var; use std::path::Path; -use wayland_scanner::{generate_code, generate_interfaces, Side}; +use wayland_scanner::*; fn main() { let protocol_file = "./wayland.xml"; @@ -10,7 +10,13 @@ fn main() { let out_dir_str = var("OUT_DIR").unwrap(); let out_dir = Path::new(&out_dir_str); - generate_code(protocol_file, out_dir.join("wayland_api.rs"), Side::Server); - - generate_interfaces(protocol_file, out_dir.join("wayland_interfaces.rs")); + if var("CARGO_FEATURE_NATIVE_LIB").ok().is_some() { + // Generate the C code + generate_c_code( + protocol_file, + out_dir.join("wayland_c_api.rs"), + Side::Server, + ); + generate_c_interfaces(protocol_file, out_dir.join("wayland_c_interfaces.rs")); + } } diff --git a/wayland-server/examples/simple_server.rs b/wayland-server/examples/simple_server.rs deleted file mode 100644 index 44e91e18e6c..00000000000 --- a/wayland-server/examples/simple_server.rs +++ /dev/null @@ -1,8 +0,0 @@ -extern crate wayland_server; - - -fn main() { - let (_display, mut event_loop) = wayland_server::create_display(); - - event_loop.run().unwrap(); -} diff --git a/wayland-server/src/client.rs b/wayland-server/src/client.rs index 73d523550ef..544164ecd93 100644 --- a/wayland-server/src/client.rs +++ b/wayland-server/src/client.rs @@ -1,28 +1,144 @@ +use std::os::raw::c_void; +use std::ptr; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; + +#[cfg(feature = "native_lib")] use wayland_sys::server::*; -/// A wayland client connected to your server +pub(crate) struct ClientInternal { + alive: AtomicBool, + user_data: AtomicPtr<()>, + destructor: AtomicPtr<()>, +} + +impl ClientInternal { + fn new() -> ClientInternal { + ClientInternal { + alive: AtomicBool::new(true), + user_data: AtomicPtr::new(::std::ptr::null_mut()), + destructor: AtomicPtr::new(::std::ptr::null_mut()), + } + } +} + +/// A handle to a client connected to your server +/// +/// There can be several handles referring to the same client pub struct Client { + internal: Arc, + #[cfg(feature = "native_lib")] ptr: *mut wl_client, } impl Client { - /// Get a pointer to the C wl_client object - /// - /// You may need it for FFI with C libraries. - pub fn ptr(&self) -> *mut wl_client { + #[cfg(feature = "native_lib")] + /// Create a client from a `wayland-server.so` pointer + pub unsafe fn from_ptr(ptr: *mut wl_client) -> Client { + // check if we are already registered + let listener = ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_client_get_destroy_listener, + ptr, + client_destroy + ); + if listener.is_null() { + // need to init this client + let listener = signal::rust_listener_create(client_destroy); + let internal = Arc::new(ClientInternal::new()); + signal::rust_listener_set_user_data( + listener, + Box::into_raw(Box::new(internal.clone())) as *mut c_void, + ); + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_client_add_destroy_listener, + ptr, + listener + ); + Client { + internal: internal, + ptr: ptr, + } + } else { + // client already initialized + let internal = signal::rust_listener_get_user_data(listener) as *mut Arc; + Client { + internal: (*internal).clone(), + ptr: ptr, + } + } + } + + #[cfg(feature = "native_lib")] + /// Retrieve a pointer to the underlying `wl_client` of `wayland-server.so` + pub fn c_ptr(&self) -> *mut wl_client { self.ptr } - /// Post a "no memory" message to the client + /// Check whether this client is still connected to the server + pub fn alive(&self) -> bool { + self.internal.alive.load(Ordering::Acquire) + } + + /// Check whether this client handle refers to the same client as + /// an other + pub fn equals(&self, other: &Client) -> bool { + Arc::ptr_eq(&self.internal, &other.internal) + } + + /// Flush the pending events to this client + pub fn flush(&self) { + if !self.alive() { + return; + } + unsafe { + ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_client_flush, self.ptr); + } + } + + /// Associate an arbitrary payload to this client /// - /// This will effectively kill this client's connection, and invalidates all its - /// objects. - pub fn post_no_memory(&self) { - unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_client_post_no_memory, self.ptr) } + /// The pointer you associate here can be retrieved from any + /// other handle to the same client. + /// + /// Setting or getting user data is done as an atomic operation. + /// You are responsible for the correct initialization of this + /// pointer, synchronisation of access, and destruction of the + /// contents at the appropriate time. + pub fn set_user_data(&self, data: *mut ()) { + self.internal.user_data.store(data, Ordering::Release); } - /// Create a client object from a pointer - pub unsafe fn from_ptr(ptr: *mut wl_client) -> Client { - Client { ptr: ptr } + /// Retrieve the arbitrary payload associated to this client + /// + /// See `set_user_data` for explanations. + pub fn get_user_data(&self) -> *mut () { + self.internal.user_data.load(Ordering::Acquire) + } + + /// Set a destructor for this client + /// + /// the provided function will be called when the client disconnects + /// or is killed. It's argument is what you would get from calling + /// `get_user_data`. + pub fn set_destructor(&self, destructor: fn(*mut ())) { + self.internal + .destructor + .store(destructor as *const () as *mut (), Ordering::Release); + } +} + +unsafe extern "C" fn client_destroy(listener: *mut wl_listener, _data: *mut c_void) { + let internal = Box::from_raw(signal::rust_listener_get_user_data(listener) as *mut Arc); + signal::rust_listener_set_user_data(listener, ptr::null_mut()); + // Store that we are dead + internal.alive.store(false, Ordering::Release); + // TODO: call the user callback + let destructor = internal.destructor.load(Ordering::Acquire); + if !destructor.is_null() { + let destructor_func: fn(*mut ()) = ::std::mem::transmute(destructor); + destructor_func(internal.user_data.load(Ordering::Acquire)); } + signal::rust_listener_destroy(listener); } diff --git a/wayland-server/src/display.rs b/wayland-server/src/display.rs index 38fa350ea79..45db9cde8bc 100644 --- a/wayland-server/src/display.rs +++ b/wayland-server/src/display.rs @@ -1,34 +1,159 @@ - - -use event_loop::{create_event_loop, EventLoop}; use std::env; use std::ffi::{CStr, CString, OsStr, OsString}; use std::io::{Error as IoError, ErrorKind, Result as IoResult}; +use std::os::raw::c_void; use std::os::unix::ffi::{OsStrExt, OsStringExt}; use std::os::unix::io::{IntoRawFd, RawFd}; use std::path::PathBuf; use std::ptr; +use std::sync::Arc; + +use wayland_commons::{Implementation, Interface}; + +use {Client, EventLoop, Global, LoopToken, NewResource}; +use globals::global_bind; + +#[cfg(feature = "native_lib")] use wayland_sys::server::*; -/// A wayland socket +pub(crate) struct DisplayInner { + #[cfg(feature = "native_lib")] + pub(crate) ptr: *mut wl_display, +} + +impl Drop for DisplayInner { + fn drop(&mut self) { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!(); + } + #[cfg(feature = "native_lib")] + { + unsafe { + ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_display_destroy, self.ptr); + } + } + } +} + +/// The wayland display /// -/// This represents a socket your compositor can receive clients on. +/// This is the core of your wayland server, this object must +/// be kept alive as long as your server is running. It allows +/// you to manage listening sockets and clients. pub struct Display { - ptr: *mut wl_display, + inner: Arc, } -/// Create a new display -/// -/// This display does not listen on any socket by default. You'll need to add one (or more) -/// using the `add_socket_*` methods. -pub fn create_display() -> (Display, EventLoop) { - unsafe { - let ptr = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_display_create,); - let el_ptr = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_display_get_event_loop, ptr); - (Display { ptr: ptr }, create_event_loop(el_ptr, Some(ptr))) +impl Display { + #[cfg(feature = "native_lib")] + /// Create a new display + /// + /// This method provides you a `Display` as well as the main `EventLoop` + /// which will host your clients' objects. + /// + /// Note that at this point, your server is not yet ready to receive connections, + /// your need to add listening sockets using the `add_socket*` methods. + pub fn new() -> (Display, EventLoop) { + let ptr = unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_display_create,) }; + + let display = Display { + inner: Arc::new(DisplayInner { ptr: ptr }), + }; + + // setup the client_created listener + unsafe { + let listener = signal::rust_listener_create(client_created); + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_display_add_client_created_listener, + ptr, + listener + ); + } + + let evq_ptr = unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_display_get_event_loop, ptr) }; + + let evq = unsafe { EventLoop::display_new(display.inner.clone(), evq_ptr) }; + + (display, evq) + } + + /// Create a new global object + /// + /// This object will be advertized to all clients, and they will + /// be able to instanciate it from their registries. + /// + /// Your implementation will be called whenever a client instanciates + /// this global. + /// + /// The version specified is the **highest supported version**, you must + /// be able to handle clients that choose to instanciate this global with + /// a lower version number. + pub fn create_global( + &mut self, + token: &LoopToken, + version: u32, + implementation: Impl, + ) -> Global + where + Impl: Implementation, u32> + 'static, + { + let token_inner = token + .inner + .inner + .as_ref() + .expect("Display::create_global requires the token associated with the display event loop."); + assert!( + Arc::ptr_eq(&self.inner, token_inner), + "Display::create_global requires the token associated with the display event loop." + ); + + let data = Box::new(Box::new(implementation) as Box, u32>>); + + unsafe { + let ptr = ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_global_create, + self.inner.ptr, + I::c_interface(), + version as i32, + &*data as *const Box<_> as *mut _, + global_bind:: + ); + + Global::create(ptr, data) + } + } + + /// Flush events to the clients + /// + /// Will send as many pending events as possible to the respective sockets of the clients. + /// Will not block, but might not send everything if the socket buffer fills up. + pub fn flush_clients(&self) { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + unsafe { + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_display_flush_clients, + self.inner.ptr + ) + }; + } } } +#[cfg(feature = "native_lib")] +unsafe extern "C" fn client_created(_listener: *mut wl_listener, data: *mut c_void) { + // init the client + let _client = Client::from_ptr(data as *mut wl_client); +} + impl Display { /// Add a listening socket to this display /// @@ -47,37 +172,44 @@ impl Display { where S: AsRef, { - let cname = match name.as_ref().map(|s| CString::new(s.as_ref().as_bytes())) { - Some(Ok(n)) => Some(n), - Some(Err(_)) => { - return Err(IoError::new( - ErrorKind::InvalidInput, - "nulls are not allowed in socket name", + #[cfg(not(feature = "native_lib"))] + { + unimplemented!(); + } + #[cfg(feature = "native_lib")] + { + let cname = match name.as_ref().map(|s| CString::new(s.as_ref().as_bytes())) { + Some(Ok(n)) => Some(n), + Some(Err(_)) => { + return Err(IoError::new( + ErrorKind::InvalidInput, + "nulls are not allowed in socket name", + )) + } + None => None, + }; + let ret = unsafe { + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_display_add_socket, + self.inner.ptr, + cname.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null()) + ) + }; + if ret == -1 { + // lets try to be helpfull + let mut socket_name = get_runtime_dir()?; + match name { + Some(s) => socket_name.push(s.as_ref()), + None => socket_name.push("wayland-0"), + } + Err(IoError::new( + ErrorKind::PermissionDenied, + format!("could not bind socket {}", socket_name.to_string_lossy()), )) + } else { + Ok(()) } - None => None, - }; - let ret = unsafe { - ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_display_add_socket, - self.ptr, - cname.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null()) - ) - }; - if ret == -1 { - // lets try to be helpfull - let mut socket_name = get_runtime_dir()?; - match name { - Some(s) => socket_name.push(s.as_ref()), - None => socket_name.push("wayland-0"), - } - Err(IoError::new( - ErrorKind::PermissionDenied, - format!("could not bind socket {}", socket_name.to_string_lossy()), - )) - } else { - Ok(()) } } @@ -91,22 +223,35 @@ impl Display { /// /// Errors if `XDG_RUNTIME_DIR` is not set, or all 32 names are already in use. pub fn add_socket_auto(&mut self) -> IoResult { - let ret = unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_display_add_socket_auto, self.ptr) }; - if ret.is_null() { - // try to be helpfull - let socket_name = get_runtime_dir()?; - Err(IoError::new( - ErrorKind::Other, - format!( - "no available wayland-* name in {}", - socket_name.to_string_lossy() - ), - )) - } else { - let sockname = unsafe { CStr::from_ptr(ret) }; - Ok(::from_vec( - sockname.to_bytes().into(), - )) + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + let ret = unsafe { + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_display_add_socket_auto, + self.inner.ptr + ) + }; + if ret.is_null() { + // try to be helpfull + let socket_name = get_runtime_dir()?; + Err(IoError::new( + ErrorKind::Other, + format!( + "no available wayland-* name in {}", + socket_name.to_string_lossy() + ), + )) + } else { + let sockname = unsafe { CStr::from_ptr(ret) }; + Ok(::from_vec( + sockname.to_bytes().into(), + )) + } } } @@ -137,43 +282,32 @@ impl Display { /// with both bind() and listen() already called. An error is returned /// otherwise. pub unsafe fn add_socket_fd(&mut self, fd: RawFd) -> IoResult<()> { - let ret = ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_display_add_socket_fd, - self.ptr, - fd - ); - if ret == 0 { - Ok(()) - } else { - Err(IoError::new(ErrorKind::InvalidInput, "invalid socket fd")) + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + let ret = ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_display_add_socket_fd, + self.inner.ptr, + fd + ); + if ret == 0 { + Ok(()) + } else { + Err(IoError::new(ErrorKind::InvalidInput, "invalid socket fd")) + } } - } - - /// Flush events to the clients - /// - /// Will send as many pending events as possible to the respective sockets of the clients. - /// Will not block, but might not send everything if the socket buffer fills up. - pub fn flush_clients(&self) { - unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_display_flush_clients, self.ptr) }; - } - - /// Obtain an FFI pointer - /// - /// This provides an FFI pointer to the underlying `*mut wl_display`. You'll typically - /// need it to interface with MESA for example. - /// - /// **Unsafety:** This pointer becomes invalid once the `Display` is dropped. - pub unsafe fn ptr(&self) -> *mut wl_display { - self.ptr } } -impl Drop for Display { - fn drop(&mut self) { - unsafe { - ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_display_destroy, self.ptr); - } +#[cfg(feature = "native_lib")] +impl Display { + /// Retrieve a pointer from the C lib to this `wl_display` + pub fn c_ptr(&self) -> *mut wl_display { + self.inner.ptr } } diff --git a/wayland-server/src/event_loop.rs b/wayland-server/src/event_loop.rs index 4d983b46f69..9f61642fa9d 100644 --- a/wayland-server/src/event_loop.rs +++ b/wayland-server/src/event_loop.rs @@ -1,180 +1,216 @@ -use {Client, Implementable, Resource}; -use std::any::Any; use std::cell::RefCell; use std::io::{Error as IoError, Result as IoResult}; -use std::io::Write; -use std::ops::{Deref, DerefMut}; use std::os::raw::{c_int, c_void}; use std::os::unix::io::RawFd; use std::rc::Rc; use std::sync::Arc; -use std::sync::atomic::{AtomicBool, AtomicPtr}; -pub use token_store::{Store as State, StoreProxy as StateProxy, Token as StateToken}; -use wayland_sys::RUST_MANAGED; -use wayland_sys::common::{wl_argument, wl_message}; -use wayland_sys::server::*; +use std::sync::atomic; + +use wayland_commons::{downcast_impl, Implementation}; + +use display::DisplayInner; +use sources::{FdEvent, FdInterest, IdleSource, SignalEvent, Source, TimerEvent}; -type ResourceUserData = ( - *mut EventLoopHandle, - Option>, - Arc<(AtomicBool, AtomicPtr<()>)>, - Option, -); +#[cfg(feature = "native_lib")] +use wayland_sys::server::*; -/// Status of a registration attempt of a resource. -pub enum RegisterStatus { - /// The resource was properly registered to this event loop & handler. - Registered, - /// The resource was not registered because it is not managed by `wayland-server`. - Unmanaged, - /// The resource was not registered because it is already destroyed. - Dead, +pub(crate) struct EventLoopInner { + #[cfg(feature = "native_lib")] + wlevl: *mut wl_event_loop, + pub(crate) inner: Option>, } -/// A handle to a global object +/// An event loop /// -/// This is given to you when you register a global to the event loop. +/// This is an event loop primitive provided by the wayland C libraries. +/// It is notably used for processing messages from the different clients +/// of your server, but additionnal event sources can be associated to it. /// -/// This handle allows you do destroy the global when needed. +/// You can also create other event loops (for a multithreaded server for example), +/// however the wayland clients can only be processed from the original event loop +/// created at the same time as the display. /// -/// If you know you will never destroy this global, you can let this -/// handle go out of scope. -pub struct Global { - ptr: *mut wl_global, - data: *mut (GlobalCallback, *mut EventLoopHandle, ID), +/// The event loops *cannot* be moved accross threads, so make sure you create them +/// on the thread you want to use them. +pub struct EventLoop { + // EventLoop is *not* Send + inner: Rc, + stop_signal: Arc, } -impl Global { - /// Destroy the associated global object. - pub fn destroy(self) { - unsafe { - // destroy the global - ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_global_destroy, self.ptr); - // free the user data - let data = Box::from_raw(self.data); - drop(data); - } - } +/// An event loop token +/// +/// This token allows some manipulations of the event loop, mainly +/// inserting new event sources in it. +/// +/// These token are light and clone-able, allowing easy access to these +/// functions without needing to share access to the main `EventLoop` object. +#[derive(Clone)] +pub struct LoopToken { + pub(crate) inner: Rc, } -/// Handle to an event loop +/// An event loop signal /// -/// This handle gives you access to methods on an event loop -/// that are safe to do from within a callback. -/// -/// They are also available on an `EventLoop` object via `Deref`. -pub struct EventLoopHandle { - state: State, - keep_going: bool, - ptr: *mut wl_event_loop, - display: Option<*mut wl_display>, +/// This handle can be cloned and be send accross threads, and allows you to +/// signal the event loop to stop running if you use the `EventLoop::run()` +/// method. +pub struct LoopSignal { + inner: Arc, } -impl EventLoopHandle { - /// Register a resource to this event loop. - /// - /// You are required to provide a valid implementation for this proxy - /// as well as some associated implementation data. This implementation - /// is expected to be a struct holding the various relevant - /// function pointers. - /// - /// This implementation data can typically contain indexes to state value - /// that the implementation will need to work on. - /// - /// If you provide a destructor function, it will be called whenever the resource - /// is destroyed, be it at the client request or because the associated client - /// was disconnected. You'd typically use this to cleanup resources - /// - /// This overwrites any precedently set implementation for this proxy. - /// - /// Returns appropriately and does nothing if this proxy is dead or already managed by - /// something else than this library. - pub fn register(&mut self, resource: &R, implementation: R::Implementation, idata: ID, - destructor: Option) - -> RegisterStatus - where - R: Resource + Implementable, - ID: 'static, - { - match resource.status() { - ::Liveness::Dead => return RegisterStatus::Dead, - ::Liveness::Unmanaged => return RegisterStatus::Unmanaged, - ::Liveness::Alive => { /* ok, we can continue */ } +impl LoopSignal { + /// Signal the event loop to stop running + pub fn stop(&self) { + self.inner.store(true, atomic::Ordering::Release); + } +} + +impl EventLoop { + /// Create a new event loop + pub fn new() -> EventLoop { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + let ptr = unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_event_loop_create,) }; + EventLoop { + inner: Rc::new(EventLoopInner { + wlevl: ptr, + inner: None, + }), + stop_signal: Arc::new(atomic::AtomicBool::new(false)), + } } + } - unsafe { - let data: *mut ResourceUserData = ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_resource_get_user_data, - resource.ptr() - ) as *mut _; - // This cast from *const to *mut is legit because we enforce that a Handler - // can only be assigned to a single EventQueue. - // (this is actually the whole point of the design of this lib) - (&mut *data).0 = self as *const _ as *mut _; - (&mut *data).1 = Some(Box::new((implementation, idata)) as Box); - (&mut *data).3 = destructor; - ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_resource_set_dispatcher, - resource.ptr(), - dispatch_func::, - &RUST_MANAGED as *const _ as *const _, - data as *mut c_void, - Some(resource_destroy::) - ); + #[cfg(feature = "native_lib")] + pub(crate) unsafe fn display_new(disp_inner: Arc, ptr: *mut wl_event_loop) -> EventLoop { + EventLoop { + inner: Rc::new(EventLoopInner { + wlevl: ptr, + inner: Some(disp_inner), + }), + stop_signal: Arc::new(atomic::AtomicBool::new(false)), + } + } + + /// Retrieve a `LoopToken` associated to this event loop + pub fn token(&self) -> LoopToken { + LoopToken { + inner: self.inner.clone(), } - RegisterStatus::Registered } - /// Stop looping + /// Retrieve a `LoopSignal` associated to this event loop + pub fn signal(&self) -> LoopSignal { + LoopSignal { + inner: self.stop_signal.clone(), + } + } + + /// Dispatch pending requests to their respective handlers + /// + /// If no request is pending, will block at most `timeout` ms if specified, + /// or indefinitely if `timeout` is `None`. /// - /// If the event loop this handle belongs to is currently running its `run()` - /// method, it'll stop and return as soon as the current dispatching session ends. - pub fn stop_loop(&mut self) { - self.keep_going = false; + /// Returns the number of requests dispatched or an error. + pub fn dispatch(&mut self, timeout: Option) -> IoResult { + #[cfg(feature = "native_lib")] + { + use std::i32; + let timeout = match timeout { + None => -1, + Some(v) if v >= (i32::MAX as u32) => i32::MAX, + Some(v) => (v as i32), + }; + let ret = unsafe { + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_event_loop_dispatch, + self.inner.wlevl, + timeout + ) + }; + if ret >= 0 { + Ok(ret as u32) + } else { + Err(IoError::last_os_error()) + } + } } - /// Get an handle to the internal state + /// Runs the event loop + /// + /// This method will call repetitively the dispatch method, + /// until one of the handlers call the `stop` method of an associated + /// `LoopSignal`. /// - /// The returned guard object allows you to get references - /// to the handler objects you previously inserted in this - /// event loop. - pub fn state(&mut self) -> &mut State { - &mut self.state + /// If this event loop is attached to a display, it will also + /// flush the events to the clients between two calls to + /// `dispatch()`. + /// + /// Note that this method will block indefinitely on waiting events, + /// as such, if you need to avoid a complete block even if no events + /// are received, you should use the `dispatch()` method instead and + /// set a timeout. + pub fn run(&mut self) -> IoResult<()> { + self.stop_signal.store(false, atomic::Ordering::Release); + loop { + if let Some(ref display_inner) = self.inner.inner { + unsafe { + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_display_flush_clients, + display_inner.ptr + ) + }; + } + self.dispatch(None)?; + if self.stop_signal.load(atomic::Ordering::Acquire) { + return Ok(()); + } + } } +} +impl LoopToken { /// Add a File Descriptor event source to this event loop /// /// The interest in read/write capability for this FD must be provided /// (and can be changed afterwards using the returned object), and the - /// associated handler will be called whenever these capabilities are + /// associated implementation will be called whenever these capabilities are /// satisfied, during the dispatching of this event loop. - pub fn add_fd_event_source(&mut self, fd: RawFd, - implementation: ::event_sources::FdEventSourceImpl, - idata: ID, interest: ::event_sources::FdInterest) - -> Result<::event_sources::FdEventSource, (IoError, ID)> { - let data = Box::new(( - implementation, - self as *const _ as *mut EventLoopHandle, - idata, - )); - + pub fn add_fd_event_source( + &self, + fd: RawFd, + interest: FdInterest, + implementation: Impl, + ) -> Result, (IoError, Impl)> + where + Impl: Implementation<(), FdEvent> + 'static, + { + let data = Box::new(Box::new(implementation) as Box>); let ret = unsafe { ffi_dispatch!( WAYLAND_SERVER_HANDLE, wl_event_loop_add_fd, - self.ptr, + self.inner.wlevl, fd, interest.bits(), - ::event_sources::event_source_fd_dispatcher::, + ::sources::event_source_fd_dispatcher, &*data as *const _ as *mut c_void ) }; if ret.is_null() { - Err((IoError::last_os_error(), data.2)) + Err(( + IoError::last_os_error(), + *(downcast_impl(*data).map_err(|_| ()).unwrap()), + )) } else { - Ok(::event_sources::make_fd_event_source(ret, data)) + Ok(Source::make(ret, data)) } } @@ -182,69 +218,67 @@ impl EventLoopHandle { /// /// It is a countdown, which can be reset using the struct /// returned by this function. When the countdown reaches 0, - /// the registered handler is called in the dispatching of + /// the implementation is called in the dispatching of /// this event loop. - pub fn add_timer_event_source(&mut self, implementation: ::event_sources::TimerEventSourceImpl, - idata: ID) - -> Result<::event_sources::TimerEventSource, (IoError, ID)> + pub fn add_timer_event_source( + &self, + implementation: Impl, + ) -> Result, (IoError, Impl)> where - ID: 'static, + Impl: Implementation<(), TimerEvent> + 'static, { - let data = Box::new(( - implementation, - self as *const _ as *mut EventLoopHandle, - idata, - )); - + let data = Box::new(Box::new(implementation) as Box>); let ret = unsafe { ffi_dispatch!( WAYLAND_SERVER_HANDLE, wl_event_loop_add_timer, - self.ptr, - ::event_sources::event_source_timer_dispatcher::, + self.inner.wlevl, + ::sources::event_source_timer_dispatcher, &*data as *const _ as *mut c_void ) }; if ret.is_null() { - Err((IoError::last_os_error(), data.2)) + Err(( + IoError::last_os_error(), + *(downcast_impl(*data).map_err(|_| ()).unwrap()), + )) } else { - Ok(::event_sources::make_timer_event_source(ret, data)) + Ok(Source::make(ret, data)) } } /// Add a signal event source to this event loop /// /// This will listen for a given unix signal (by setting up - /// a signalfd for it) and call the registered handler whenever + /// a signalfd for it) and call the implementation whenever /// the program receives this signal. Calls are made during the /// dispatching of this event loop. - pub fn add_signal_event_source(&mut self, - implementation: ::event_sources::SignalEventSourceImpl, - idata: ID, signal: ::nix::sys::signal::Signal) - -> Result<::event_sources::SignalEventSource, (IoError, ID)> + pub fn add_signal_event_source( + &self, + signal: ::nix::sys::signal::Signal, + implementation: Impl, + ) -> Result, (IoError, Impl)> where - ID: 'static, + Impl: Implementation<(), SignalEvent> + 'static, { - let data = Box::new(( - implementation, - self as *const _ as *mut EventLoopHandle, - idata, - )); - + let data = Box::new(Box::new(implementation) as Box>); let ret = unsafe { ffi_dispatch!( WAYLAND_SERVER_HANDLE, wl_event_loop_add_signal, - self.ptr, + self.inner.wlevl, signal as c_int, - ::event_sources::event_source_signal_dispatcher::, + ::sources::event_source_signal_dispatcher, &*data as *const _ as *mut c_void ) }; if ret.is_null() { - Err((IoError::last_os_error(), data.2)) + Err(( + IoError::last_os_error(), + *(downcast_impl(*data).map_err(|_| ()).unwrap()), + )) } else { - Ok(::event_sources::make_signal_event_source(ret, data)) + Ok(Source::make(ret, data)) } } @@ -256,320 +290,53 @@ impl EventLoopHandle { /// processing all the pending I/O. This callback will be fired exactly once the first /// time this condition is met. /// - /// You can cancel it using the returned `IdleEventSource`. - pub fn add_idle_event_source(&mut self, implementation: ::event_sources::IdleEventSourceImpl, - idata: ID) - -> ::event_sources::IdleEventSource + /// You can cancel or retrieve the implementation after it has fired using the + /// returned `IdleEventSource`. + pub fn add_idle_event_source(&self, implementation: Impl) -> IdleSource where - ID: 'static, + Impl: Implementation<(), ()> + 'static, { let data = Rc::new(RefCell::new(( - implementation, - self as *const _ as *mut EventLoopHandle, - idata, + Box::new(implementation) as Box>, false, ))); - let ret = unsafe { ffi_dispatch!( WAYLAND_SERVER_HANDLE, wl_event_loop_add_idle, - self.ptr, - ::event_sources::event_source_idle_dispatcher::, + self.inner.wlevl, + ::sources::event_source_idle_dispatcher, Rc::into_raw(data.clone()) as *mut _ ) }; - ::event_sources::make_idle_event_source(ret, data) - } - - /// Register a global object to the display. - /// - /// Specify the version of the interface to advertize, as well as the callback that will - /// receive requests to create an object. - /// - /// This uses an "implementation data" mechanism similar to regular wayland objects. - /// - /// Panics: - /// - /// - if the event loop is not associated to a display - pub fn register_global(&mut self, version: i32, callback: GlobalCallback, - idata: ID) - -> Global { - let display = self.display - .expect("Globals can only be registered on an event loop associated with a display."); - - let data = Box::new(( - callback, - self as *const _ as *mut EventLoopHandle, - idata, - )); - - let ptr = unsafe { - ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_global_create, - display, - R::interface_ptr(), - version, - &*data as *const (GlobalCallback, *mut EventLoopHandle, ID) as *mut _, - global_bind:: - ) - }; - - Global { - ptr: ptr, - data: Box::into_raw(data), - } + IdleSource::make(ret, data) } -} -/// Checks if a resource is registered with a given implementation on an event loop -/// -/// Returns `false` if the resource is dead, even if it was registered with -/// this implementation while alive. -pub fn resource_is_registered(resource: &R, implementation: &R::Implementation) -> bool -where - R: Resource + Implementable, -{ - if resource.status() != ::Liveness::Alive { - return false; - } - let resource_data = unsafe { - &*(ffi_dispatch!( + /// Checks whether given resource is indeed linked to this + /// event loop + #[cfg(feature = "native_lib")] + pub(crate) unsafe fn matches(&self, resource_ptr: *mut wl_resource) -> bool { + let client_ptr = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_client, resource_ptr); + let display_ptr = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_client_get_display, client_ptr); + let event_loop_ptr = ffi_dispatch!( WAYLAND_SERVER_HANDLE, - wl_resource_get_user_data, - resource.ptr() - ) as *mut ResourceUserData) - }; - if resource_data.0.is_null() { - return false; - } - ((&*resource_data).1) - .as_ref() - .and_then(|v| v.downcast_ref::<(R::Implementation, ID)>()) - .map(|t| &t.0 == implementation) - .unwrap_or(false) -} - -pub unsafe fn create_event_loop(ptr: *mut wl_event_loop, display: Option<*mut wl_display>) -> EventLoop { - EventLoop { - handle: Box::new(EventLoopHandle { - state: State::new(), - keep_going: false, - ptr: ptr, - display: display, - }), - } -} - -pub struct EventLoop { - handle: Box, -} - -/// Callback function called when a global is instanciated by a client -/// -/// Arguments are: -/// -/// - handle to the eventloop -/// - implementation data you provided to `register_global` -/// - client that instanciated the global -/// - the newly instanciated global -pub type GlobalCallback = fn(&mut EventLoopHandle, &mut ID, &Client, R); - -impl EventLoop { - /// Create a new EventLoop - /// - /// It is not associated to a wayland socket, and can be used for other - /// event sources. - pub fn new() -> EventLoop { - unsafe { - let ptr = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_event_loop_create,); - create_event_loop(ptr, None) - } - } - - /// Dispatch pending requests to their respective handlers - /// - /// If no request is pending, will block at most `timeout` ms if specified, - /// or indefinitely if `timeout` is `None`. - /// - /// Returns the number of requests dispatched or an error. - pub fn dispatch(&mut self, timeout: Option) -> IoResult { - use std::i32; - let timeout = match timeout { - None => -1, - Some(v) if v >= (i32::MAX as u32) => i32::MAX, - Some(v) => (v as i32), - }; - let ret = unsafe { - ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_event_loop_dispatch, - self.ptr, - timeout - ) - }; - if ret >= 0 { - Ok(ret as u32) - } else { - Err(IoError::last_os_error()) - } - } - - /// Runs the event loop - /// - /// This method will call repetitively the dispatch method, - /// until one of the handlers call the `stop_loop` method - /// on the `EventLoopHandle`. - /// - /// If this event loop is attached to a display, it will also - /// flush the events to the clients between two calls to - /// `dispatch()`. - /// - /// Note that this method will block indefinitely on waiting events, - /// as such, if you need to avoid a complete block even if no events - /// are received, you should use the `dispatch()` method instead and - /// set a timeout. - pub fn run(&mut self) -> IoResult<()> { - self.handle.keep_going = true; - loop { - if let Some(display) = self.handle.display { - unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_display_flush_clients, display) }; - } - self.dispatch(None)?; - if !self.handle.keep_going { - return Ok(()); - } - } - } -} - -impl Deref for EventLoop { - type Target = EventLoopHandle; - fn deref(&self) -> &EventLoopHandle { - &*self.handle - } -} - -impl DerefMut for EventLoop { - fn deref_mut(&mut self) -> &mut EventLoopHandle { - &mut *self.handle + wl_display_get_event_loop, + display_ptr + ); + return event_loop_ptr == self.inner.wlevl; } } -impl Drop for EventLoop { +impl Drop for EventLoopInner { fn drop(&mut self) { - if self.handle.display.is_none() { - // only destroy the event_loop if it's not the one - // from the display - unsafe { - ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_event_loop_destroy, self.ptr); + #[cfg(feature = "native_lib")] + { + if self.inner.is_none() { + // only destroy the event_loop if it's not the one from the display + unsafe { + ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_event_loop_destroy, self.wlevl); + } } } } } - -unsafe extern "C" fn dispatch_func(_impl: *const c_void, resource: *mut c_void, opcode: u32, - _msg: *const wl_message, args: *const wl_argument) - -> c_int -where - R: Resource + Implementable, - ID: 'static, -{ - // sanity check, if it triggers, it is a bug - if _impl != &RUST_MANAGED as *const _ as *const _ { - let _ = write!( - ::std::io::stderr(), - "[wayland-client error] Dispatcher got called for a message on a non-managed object." - ); - ::libc::abort(); - } - // 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 || { - // This cast from *const to *mut is legit because we enforce that a Handler - // can only be assigned to a single EventQueue. - // (this is actually the whole point of the design of this lib) - let resource = R::from_ptr_initialized(resource as *mut wl_resource); - let client = Client::from_ptr(ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_resource_get_client, - resource.ptr() - )); - resource.__dispatch_msg(&client, opcode, args) - }); - match ret { - Ok(Ok(())) => return 0, // all went well - Ok(Err(())) => { - // an unknown opcode was dispatched, this is not normal - let _ = write!( - ::std::io::stderr(), - "[wayland-server error] Attempted to dispatch unknown opcode {} for {}, aborting.", - opcode, - R::interface_name() - ); - ::libc::abort(); - } - Err(_) => { - // a panic occured - let _ = write!( - ::std::io::stderr(), - "[wayland-server error] A handler for {} panicked, aborting.", - R::interface_name() - ); - ::libc::abort(); - } - } -} - -unsafe extern "C" fn global_bind(client: *mut wl_client, data: *mut c_void, version: u32, - id: u32) { - // safety of this function is the same as dispatch_func - let ret = ::std::panic::catch_unwind(move || { - let data = &mut *(data as *mut (GlobalCallback, *mut EventLoopHandle, ID)); - let cb = data.0; - let evqhandle = &mut *data.1; - let idata = &mut data.2; - let client = Client::from_ptr(client); - let ptr = ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_resource_create, - client.ptr(), - R::interface_ptr(), - version as i32, // wayland already checks the validity of the version - id - ); - let resource = R::from_ptr_new(ptr as *mut wl_resource); - cb(evqhandle, idata, &client, resource) - }); - match ret { - Ok(()) => (), // all went well - Err(_) => { - // a panic occured - let _ = write!( - ::std::io::stderr(), - "[wayland-server error] A global handler for {} panicked, aborting.", - R::interface_name() - ); - ::libc::abort(); - } - } -} - -unsafe extern "C" fn resource_destroy(resource: *mut wl_resource) { - let resource = R::from_ptr_initialized(resource as *mut wl_resource); - if resource.status() == ::Liveness::Alive { - // mark the resource as dead - let data = &mut *(ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_resource_get_user_data, - resource.ptr() - ) as *mut ResourceUserData); - (data.2) - .0 - .store(false, ::std::sync::atomic::Ordering::SeqCst); - if let Some(destructor) = data.3 { - destructor(&resource) - } - } -} diff --git a/wayland-server/src/event_sources.rs b/wayland-server/src/event_sources.rs deleted file mode 100644 index e25b9b8672b..00000000000 --- a/wayland-server/src/event_sources.rs +++ /dev/null @@ -1,426 +0,0 @@ -use EventLoopHandle; -use std::cell::RefCell; -use std::io::Error as IoError; -use std::io::Write; -use std::os::raw::{c_int, c_void}; -use std::os::unix::io::RawFd; -use std::rc::Rc; -use wayland_sys::server::*; - -/// Common functions of all event sources -pub trait EventSource { - /// Access with the implementation data without removing it from the `EventLoop` - fn with_idata(&mut self, evlh: &mut EventLoopHandle, f: F) -> R - where F: FnOnce(&mut ID, &mut EventLoopHandle) -> R; - - /// Remove this event source from its event loop - /// - /// Any outstanding calls will be cancelled. - /// - /// Returns the implementation data in case you have something to do with it. - fn remove(self) -> ID; -} - -/// fd_event_source -/// -/// A handle to a registered FD event source -/// -/// Dropping this struct does not remove the event source, -/// use the `remove` method for that. -pub struct FdEventSource { - ptr: *mut wl_event_source, - data: *mut (FdEventSourceImpl, *mut EventLoopHandle, ID), -} - -bitflags!{ - /// Flags to register interest on a file descriptor - pub struct FdInterest: u32 { - /// Interest to be notified when the file descriptor is readable - const READ = 0x01; - /// Interest to be notified when the file descriptor is writable - const WRITE = 0x02; - } -} - -pub fn make_fd_event_source(ptr: *mut wl_event_source, - data: Box<(FdEventSourceImpl, *mut EventLoopHandle, ID)>) - -> FdEventSource { - FdEventSource { - ptr: ptr, - data: Box::into_raw(data), - } -} - -impl FdEventSource { - /// Change the registered interest for this FD - pub fn update_mask(&mut self, mask: FdInterest) { - unsafe { - ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_event_source_fd_update, - self.ptr, - mask.bits() - ); - } - } -} - -impl EventSource for FdEventSource { - fn with_idata(&mut self, evlh: &mut EventLoopHandle, f: F) -> R - where - F: FnOnce(&mut ID, &mut EventLoopHandle) -> R, - { - let mut data = unsafe { Box::from_raw(self.data) }; - let res = f(&mut data.2, evlh); - self.data = Box::into_raw(data); - res - } - - fn remove(self) -> ID { - unsafe { - ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_event_source_remove, self.ptr); - let data = Box::from_raw(self.data); - data.2 - } - } -} - -/// Implementation for FD events -pub struct FdEventSourceImpl { - /// The FD is ready to be read/written - /// - /// Details of the capability state are given as argument. - pub ready: fn(evlh: &mut EventLoopHandle, idata: &mut ID, fd: RawFd, mask: FdInterest), - /// An error occured with this FD - /// - /// Most likely it won't be usable any longer - pub error: fn(evlh: &mut EventLoopHandle, idata: &mut ID, fd: RawFd, mask: IoError), -} - - -pub unsafe extern "C" fn event_source_fd_dispatcher(fd: c_int, mask: u32, data: *mut c_void) -> c_int -where - ID: 'static, -{ - // 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 data = &mut *(data as *mut (FdEventSourceImpl, *mut EventLoopHandle, ID)); - let implem = &data.0; - let evlh = &mut *(data.1); - let idata = &mut data.2; - if mask & 0x08 > 0 { - // EPOLLERR - use nix::sys::socket; - let err = match socket::getsockopt(fd, socket::sockopt::SocketError) { - Ok(err) => err, - Err(_) => { - // error while retrieving the error code ??? - let _ = write!( - ::std::io::stderr(), - "[wayland-server error] Error while retrieving error code on socket {}, aborting.", - fd - ); - ::libc::abort(); - } - }; - (implem.error)(evlh, idata, fd, IoError::from_raw_os_error(err)); - } else if mask & 0x04 > 0 { - // EPOLLHUP - (implem.error)( - evlh, - idata, - fd, - IoError::new(::std::io::ErrorKind::ConnectionAborted, ""), - ) - } else { - let mut bits = FdInterest::empty(); - if mask & 0x02 > 0 { - bits = bits | FdInterest::WRITE; - } - if mask & 0x01 > 0 { - bits = bits | FdInterest::READ; - } - (implem.ready)(evlh, idata, fd, bits) - } - }); - match ret { - Ok(()) => return 0, // all went well - Err(_) => { - // a panic occured - let _ = write!( - ::std::io::stderr(), - "[wayland-server error] A handler for fd {} event source panicked, aborting.", - fd - ); - ::libc::abort(); - } - } -} - -/// timer_event_source -/// -/// A handle to a registered timer event source -/// -/// Dropping this struct does not remove the event source, -/// use the `remove` method for that. -pub struct TimerEventSource { - ptr: *mut wl_event_source, - data: *mut (TimerEventSourceImpl, *mut EventLoopHandle, ID), -} - - -pub fn make_timer_event_source(ptr: *mut wl_event_source, - data: Box<(TimerEventSourceImpl, *mut EventLoopHandle, ID)>) - -> TimerEventSource { - TimerEventSource { - ptr: ptr, - data: Box::into_raw(data), - } -} - -impl TimerEventSource { - /// Set the delay of this timer - /// - /// The callback will be called during the next dispatch of the - /// event loop after this time (in milliseconds) is elapsed. - /// - /// Manually the delay to 0 stops the timer (the callback won't be - /// called). - pub fn set_delay_ms(&mut self, delay: i32) { - unsafe { - ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_event_source_timer_update, - self.ptr, - delay - ); - } - } -} - - -impl EventSource for TimerEventSource { - fn with_idata(&mut self, evlh: &mut EventLoopHandle, f: F) -> R - where - F: FnOnce(&mut ID, &mut EventLoopHandle) -> R, - { - let mut data = unsafe { Box::from_raw(self.data) }; - let res = f(&mut data.2, evlh); - self.data = Box::into_raw(data); - res - } - - fn remove(self) -> ID { - unsafe { - ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_event_source_remove, self.ptr); - let data = Box::from_raw(self.data); - data.2 - } - } -} - -/// Called when the countdown reaches 0 -pub type TimerEventSourceImpl = fn(&mut EventLoopHandle, idata: &mut ID); - -pub unsafe extern "C" fn event_source_timer_dispatcher(data: *mut c_void) -> c_int -where - ID: 'static, -{ - // 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 data = &mut *(data as *mut (TimerEventSourceImpl, *mut EventLoopHandle, ID)); - let cb = data.0; - let evlh = &mut *(data.1); - let idata = &mut data.2; - cb(evlh, idata); - }); - match ret { - Ok(()) => return 0, // all went well - Err(_) => { - // a panic occured - let _ = write!( - ::std::io::stderr(), - "[wayland-server error] A handler for a timer event source panicked, aborting.", - ); - ::libc::abort(); - } - } -} - -/// signal_event_source -/// -/// A handle to a registered signal event source -/// -/// Dropping this struct does not remove the event source, -/// use the `remove` method for that. -pub struct SignalEventSource { - ptr: *mut wl_event_source, - data: *mut (SignalEventSourceImpl, *mut EventLoopHandle, ID), -} - - -pub fn make_signal_event_source(ptr: *mut wl_event_source, - data: Box< - (SignalEventSourceImpl, *mut EventLoopHandle, ID), ->) - -> SignalEventSource { - SignalEventSource { - ptr: ptr, - data: Box::into_raw(data), - } -} - -impl EventSource for SignalEventSource { - fn with_idata(&mut self, evlh: &mut EventLoopHandle, f: F) -> R - where - F: FnOnce(&mut ID, &mut EventLoopHandle) -> R, - { - let mut data = unsafe { Box::from_raw(self.data) }; - let res = f(&mut data.2, evlh); - self.data = Box::into_raw(data); - res - } - - fn remove(self) -> ID { - unsafe { - ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_event_source_remove, self.ptr); - let data = Box::from_raw(self.data); - data.2 - } - } -} - -/// A signal has been received -/// -/// The signal number is given has argument -pub type SignalEventSourceImpl = fn( - &mut EventLoopHandle, - idata: &mut ID, - signal: ::nix::sys::signal::Signal, -); - -pub unsafe extern "C" fn event_source_signal_dispatcher(signal: c_int, data: *mut c_void) -> c_int -where - ID: 'static, -{ - // 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 data = &mut *(data as *mut (SignalEventSourceImpl, *mut EventLoopHandle, ID)); - let cb = data.0; - let evlh = &mut *(data.1); - let idata = &mut data.2; - let sig = match ::nix::sys::signal::Signal::from_c_int(signal) { - Ok(sig) => sig, - Err(_) => { - // Actually, this cannot happen, as we cannot register an event source for - // an unknown signal... - let _ = write!( - ::std::io::stderr(), - "[wayland-server error] Unknown signal in signal event source: {}, aborting.", - signal - ); - ::libc::abort(); - } - }; - cb(evlh, idata, sig); - }); - match ret { - Ok(()) => return 0, // all went well - Err(_) => { - // a panic occured - let _ = write!( - ::std::io::stderr(), - "[wayland-server error] A handler for a timer event source panicked, aborting.", - ); - ::libc::abort(); - } - } -} - -/// Idle event source -/// -/// A handle to an idle event source -/// -/// Dropping this struct does not remove the event source, -/// use the `remove` method for that. -pub struct IdleEventSource { - ptr: *mut wl_event_source, - data: Rc, *mut EventLoopHandle, ID, bool)>>, -} - -pub fn make_idle_event_source(ptr: *mut wl_event_source, - data: Rc< - RefCell<(IdleEventSourceImpl, *mut EventLoopHandle, ID, bool)>, ->) - -> IdleEventSource { - IdleEventSource { - ptr: ptr, - data: data, - } -} - -/// The idle-throttled callback -pub type IdleEventSourceImpl = fn(&mut EventLoopHandle, idata: &mut ID); - -impl EventSource for IdleEventSource { - fn with_idata(&mut self, evlh: &mut EventLoopHandle, f: F) -> R - where - F: FnOnce(&mut ID, &mut EventLoopHandle) -> R, - { - f(&mut self.data.borrow_mut().2, evlh) - } - - fn remove(self) -> ID { - let dispatched = self.data.borrow().3; - if !dispatched { - unsafe { - // unregister this event source - ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_event_source_remove, self.ptr); - // recreate the outstanding reference that was not consumed - let _ = Rc::from_raw(&*self.data); - } - } - // we are now the only oustanding reference - let data = Rc::try_unwrap(self.data) - .unwrap_or_else(|_| panic!("Idle Rc was not singly owned.")) - .into_inner(); - data.2 - } -} - -pub unsafe extern "C" fn event_source_idle_dispatcher(data: *mut c_void) -where - ID: 'static, -{ - // 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 data = &*(data as *mut RefCell<(IdleEventSourceImpl, *mut EventLoopHandle, ID, bool)>); - let mut data = data.borrow_mut(); - let cb = data.0; - let evlh = &mut *(data.1); - let idata = &mut data.2; - cb(evlh, idata); - }); - match ret { - Ok(()) => { - // all went well - // free the refence to the idata, as this event source cannot be called again - let data = - Rc::from_raw(data as *mut RefCell<(IdleEventSourceImpl, *mut EventLoopHandle, ID, bool)>); - // store that the dispatching occured - data.borrow_mut().3 = true; - } - Err(_) => { - // a panic occured - let _ = write!( - ::std::io::stderr(), - "[wayland-server error] A handler for a timer event source panicked, aborting.", - ); - ::libc::abort(); - } - } -} diff --git a/wayland-server/src/globals.rs b/wayland-server/src/globals.rs new file mode 100644 index 00000000000..b57364d73e2 --- /dev/null +++ b/wayland-server/src/globals.rs @@ -0,0 +1,85 @@ +use std::os::raw::c_void; + +use wayland_commons::{Implementation, Interface}; + +use NewResource; + +#[cfg(feature = "native_lib")] +use wayland_sys::server::*; + +/// A handle to a global object +/// +/// This is given to you when you register a global to the event loop. +/// +/// This handle allows you do destroy the global when needed. +/// +/// If you know you will never destroy this global, you can let this +/// handle go out of scope. +pub struct Global { + _i: ::std::marker::PhantomData<*const I>, + #[cfg(feature = "native_lib")] + ptr: *mut wl_global, + #[cfg(feature = "native_lib")] + data: *mut Box, u32>>, +} + +impl Global { + #[cfg(feature = "native_lib")] + pub(crate) unsafe fn create( + ptr: *mut wl_global, + data: Box, u32>>>, + ) -> Global { + Global { + _i: ::std::marker::PhantomData, + ptr: ptr, + data: Box::into_raw(data), + } + } + + /// Destroy the associated global object. + pub fn destroy(self) { + #[cfg(not(feature = "native_lib"))] + {} + #[cfg(feature = "native_lib")] + unsafe { + // destroy the global + ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_global_destroy, self.ptr); + // free the user data + let data = Box::from_raw(self.data); + drop(data); + } + } +} + +pub(crate) unsafe extern "C" fn global_bind( + client: *mut wl_client, + data: *mut c_void, + version: u32, + id: u32, +) { + // safety of this function is the same as dispatch_func + let ret = ::std::panic::catch_unwind(move || { + let implem = &mut *(data as *mut Box, u32>>); + let ptr = ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_resource_create, + client, + I::c_interface(), + version as i32, // wayland already checks the validity of the version + id + ); + let resource = NewResource::from_c_ptr(ptr as *mut wl_resource); + implem.receive(version, resource); + }); + match ret { + Ok(()) => (), // all went well + Err(_) => { + // a panic occured + eprintln!( + "[wayland-server error] A global handler for {} panicked, aborting.", + I::NAME + ); + ::libc::abort(); + } + } +} diff --git a/wayland-server/src/lib.rs b/wayland-server/src/lib.rs index ee23d41227c..7bd5380dea2 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -1,97 +1,96 @@ //! Server-side Wayland connector //! -//! # Overview +//! ## Overview //! -//! Setting up the listening socket is done by the `create_display` -//! function, providing you a `Display` object and an `EventLoop`. +//! This crate provides the interfaces and machinery to safely create servers +//! for the wayland protocol. It is a rust wrapper around the `libwayland-server.so` +//! C library. //! -//! On the event loop, you'll be able to register the globals -//! you want to advertize, as well as handlers for all ressources -//! created by the clients. +//! The wayland protocol revolves around the creation of various objects and the exchange +//! of messages associated to these objects. Whenever a client connects, a `Display` object +//! is automatically created in their object space, which they use as a root to create new +//! objects and bootstrap their state. //! -//! You then integrate the wayland event loop in your main event -//! loop to run your compositor. +//! ## Protocol and messages handling model //! -//! # Implementation and event loop +//! The protocol being bi-directional, you can send and receive messages. Sending messages is +//! done via methods of `Resource<_>` objects, receiving and handling them is done by providing +//! implementations. //! -//! This crate mirrors the callback-oriented design of the -//! Wayland C library by using implementation structs: each wayland -//! type defines an `Implementation` struct in its module, with -//! one function field for each possible event this object can receive. +//! ### Resources //! -//! When registering an object on an event loop, you need to provide an -//! implementation for this object. You can also provide some -//! "implementation data": a value that will be provided as second -//! argument to all the callback methods of your implementation. +//! The protocol and message model is very similar to the one of `wayland-client`, with the +//! main difference being that the handles to objects are represented by the `Resource` +//! type. //! -//! A typical use of implementation data is to store here one or more -//! state tokens to access some part of the shared state from your -//! callback. +//! These resources are used to send messages to the clients (they are called "events" in the +//! wayland context). This is done by the `Resource::::send(..)` method. //! -//! ## Example of implementation +//! There is not a 1 to 1 mapping between `Resource` instances and protocol objects. Rather, +//! you can think of `Resource` as an `Rc`-like handle to a wayland object. Multiple instances +//! of it can exist referring to the same protocol object. //! -//! You can register your wayland objects to an event queue: +//! Similarly, the lifetimes of the protocol objects and the `Resource` are not tighly tied. +//! As protocol objects are created and destroyed by protocol messages, it can happen that an object +//! gets destroyed while one or more `Resource` still refers to it. In such case, these resources +//! will be disabled and their `alive()` method will start to return `false`. Events that are +//! subsequently sent to them are ignored. //! -//! ```ignore -//! event_loop.register(&my_object, implementation, impl_data); -//! ``` -//! -//! A given wayland object can only be registered to an event -//! loop at a given time, re-registering it will overwrite -//! the previous configuration. +//! ### Implementations //! -//! Objects can be registered to event loop using the `&EventLoopHandle` -//! argument, available from withing an event callback. +//! To receive and process messages from the clients to you (in wayland context they are +//! called "requests"), you need to provide an `Implementation` for each wayland object +//! created in the protocol session. Whenever a new protocol object is created, you will +//! receive a `NewResource` object. Providing an implementation via its `implement()` method +//! will turn it into a regular `Resource` object. //! -//! ## Globals definition +//! **All objects must be implemented**, even if it is an implementation doing nothing. +//! Failure to do so (by dropping the `NewResource` for example) can cause future fatal +//! protocol errors if the client tries to send a request to this object. //! -//! Some wayland objects are special and can be directly created by the -//! clients from their registry. To handle them your must declare -//! which globals you want to make available to your clients, like this: +//! An implementation is just a struct implementing the `Implementation, I::Request>` +//! trait, where `I` is the interface of the considered object: //! -//! ```ignore -//! event_loop.register_global(version, callback, idata); //! ``` +//! // Example implementation for the wl_surface interface +//! use wayland_server::Resource; +//! use wayland_server::protocol::wl_surface; +//! use wayland_server::commons::Implementation; //! -//! Where `callback` is a function or non-capturing closure, provided as -//! an implementation for when this global is instanciated by a client. -//! See the method documentation for details. -//! -//! ## Event loop integration -//! -//! Once the setup phase is done, you can integrate the -//! event loop in the main event loop of your program. -//! -//! Either all you need is for it to run indefinitely (external -//! events are checked in an other thread?): +//! struct MyImpl { +//! // ... +//! } //! -//! ```ignore -//! event_loop.run(); +//! impl Implementation, wl_surface::Request> for MyImpl { +//! fn receive(&mut self, msg: wl_surface::Request, resource: Resource) { +//! // process the message... +//! } +//! } +//! # fn main() {} //! ``` //! -//! Or you can integrate it with more control: +//! The trait is also automatically implemented for `FnMut(I::Request, Resource)` closures, +//! so you can use them for simplicity if a full struct would be too cumbersome. //! -//! ```ignore -//! loop { -//! // flush events to client sockets -//! display.flush_clients(); -//! // receive request from clients and dispatch them -//! // blocking if no request is pending for at most -//! // 10ms -//! event_loop.dispatch(Some(10)).unwrap(); -//! // then you can check events from other sources if -//! // you need to -//! } -//! ``` +//! The `Resource` passed to your implementation is garanteed to be alive (as it just received +//! a request), unless the exact message received is a destructor (which is indicated in the API +//! documentations). //! -//! # Protocols integration +//! ## Event loops and general structure //! -//! This crate provides the basic primitives as well as the -//! core wayland protocol (in the `protocol` module), but -//! other protocols can be integrated from XML descriptions. +//! The core of your server is the `Display` object. It represent the ability of your program to +//! process wayland messages. Once this object is created, you can configure it to listen on one +//! or more sockets for incoming client connections (see the `Display` docs for details). //! -//! The the crate `wayland_scanner` and its documentation for -//! details about how to do so. +//! The crate also provides an event loop structure. An `EventLoop` is automatically created at the +//! same time as the `Display`, and it will handle the connections of your wayland clients. See the +//! `EventLoop` API documentation for explanations of its use. +//! +//! It is also possible to both create other event loops and insert other kind of sources of events +//! to the event loops. These functions are typically useful to integrate, as a wayland compositor, +//! with other parts of the system (typically listening on file destrictor describing input devices). +//! Adding sources to an event loop is done via the `LoopToken` type, that is retrieved by the +//! `token()` method of `EventLoop`. See their documentations for more details. #![warn(missing_docs)] @@ -99,192 +98,46 @@ extern crate bitflags; extern crate libc; extern crate nix; -extern crate token_store; + +extern crate wayland_commons; +#[cfg(feature = "native_lib")] #[macro_use] extern crate wayland_sys; -pub use client::Client; -pub use display::{create_display, Display}; -pub use event_loop::{resource_is_registered, EventLoop, EventLoopHandle, Global, GlobalCallback, - RegisterStatus, State, StateProxy, StateToken}; -pub use generated::interfaces as protocol_interfaces; -pub use generated::server as protocol; -use wayland_sys::common::{wl_argument, wl_interface}; -use wayland_sys::server::*; - mod client; mod display; mod event_loop; -mod event_sources; +mod globals; +mod resource; +pub mod sources; -pub mod sources { - //! Secondary event sources - // This module contains the types & traits to work with - // different kind of event sources that can be registered to and - // event loop, other than the wayland protocol sockets. - - pub use event_sources::EventSource; - pub use event_sources::{FdEventSource, FdEventSourceImpl, FdInterest}; - pub use event_sources::{IdleEventSource, IdleEventSourceImpl}; - pub use event_sources::{SignalEventSource, SignalEventSourceImpl}; - pub use event_sources::{TimerEventSource, TimerEventSourceImpl}; -} +pub use client::Client; +pub use display::Display; +pub use globals::Global; +pub use event_loop::{EventLoop, LoopSignal, LoopToken}; +pub use resource::{NewResource, Resource}; -/// Common routines for wayland resource objects. -/// -/// All wayland objects automatically implement this trait -/// as generated by the scanner. +/// Re-export of wayland-commons /// -/// It is mostly used for internal use by the library, and you -/// should only need these methods for interfacing with C library -/// working on wayland objects. -pub unsafe trait Resource { - /// Pointer to the underlying wayland proxy object - fn ptr(&self) -> *mut wl_resource; - /// Create an instance from a wayland pointer - /// - /// The pointer must refer to a valid wayland resource - /// of the appropriate interface, but that have not yet - /// been seen by the library. - /// - /// The library will take control of the object (notably - /// overwrite its user_data). - unsafe fn from_ptr_new(*mut wl_resource) -> Self; - /// Create an instance from a wayland pointer - /// - /// The pointer must refer to a valid wayland resource - /// of the appropriate interface. The library will detect if the - /// resource is already managed by it or not. If it is not, this - /// resource will be considered as "unmanaged", and should then - /// be handled with care. - unsafe fn from_ptr_initialized(*mut wl_resource) -> Self; - /// Pointer to the interface representation - fn interface_ptr() -> *const wl_interface; - /// Internal wayland name of this interface - fn interface_name() -> &'static str; - /// Max version of this interface supported - fn supported_version() -> u32; - /// Current version of the interface this resource is instantiated with - fn version(&self) -> i32; - /// Check if the resource behind this handle is actually still alive - fn status(&self) -> Liveness; - /// Check of two handles are actually the same wayland object - /// - /// Returns `false` if any of the objects has already been destroyed - fn equals(&self, &Self) -> bool; - /// Set a pointer associated as user data on this resource - /// - /// All handles to the same wayland object share the same user data pointer. - /// - /// The get/set operations are atomic, no more guarantee is given. If you need - /// to synchronise access to this data, it is your responsibility to add a Mutex - /// or any other similar mechanism. - fn set_user_data(&self, ptr: *mut ()); - /// Get the pointer associated as user data on this resource - /// - /// All handles to the same wayland object share the same user data pointer. - /// - /// See `set_user_data` for synchronisation guarantee. - fn get_user_data(&self) -> *mut (); - /// Posts a protocol error to this resource - /// - /// The error code can be obtained from the various `Error` enums of the protocols. - /// - /// An error is fatal to the client that caused it. - fn post_error(&self, error_code: u32, msg: String) { - // If `str` contains an interior null, the actuall transmitted message will - // be truncated at this point. - unsafe { - let cstring = ::std::ffi::CString::from_vec_unchecked(msg.into()); - ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_resource_post_error, - self.ptr(), - error_code, - cstring.as_ptr() - ) - } - } - /// Clone this resource handle - /// - /// Will only succeed if the resource is managed by this library and - /// is still alive. - fn clone(&self) -> Option - where - Self: Sized, - { - if self.status() == Liveness::Alive { - Some(unsafe { self.clone_unchecked() }) - } else { - None - } - } - /// Unsafely clone this resource handle - /// - /// This function is unsafe because if the resource is unmanaged, the lib - /// has no knowledge of its lifetime, and cannot ensure that the new handle - /// will not outlive the object. - unsafe fn clone_unchecked(&self) -> Self; - /// Checks wether this resource and the other are from the same client - /// - /// Returns `true` if both are alive and belong to the same client, `false` - /// otherwise. - fn same_client_as(&self, other: &R) -> bool { - // comparing client pointers for equality is only meaningfull - // if resources are alive - if !(self.status() == Liveness::Alive && other.status() == Liveness::Alive) { - false - } else { - let my_client = - unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_client, self.ptr()) }; - let other_client = - unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_client, other.ptr()) }; - my_client == other_client - } - } -} - -/// Common trait for wayland objects that can be registered to an EventQueue -pub unsafe trait Implementable: Resource { - /// The type containing the implementation for the event callbacks - type Implementation: PartialEq + Copy + 'static; - #[doc(hidden)] - unsafe fn __dispatch_msg(&self, client: &Client, opcode: u32, args: *const wl_argument) - -> Result<(), ()>; +/// Common traits and functions to work with wayland objects +pub mod commons { + pub use wayland_commons::*; } -/// Possible outcome of the call of a event on a resource -#[derive(Debug)] -pub enum EventResult { - /// Message has been buffered and will be sent to client - Sent(T), - /// This resource is already destroyed, request has been ignored - Destroyed, -} - -impl EventResult { - /// Assert that result is successfull and extract the value. - /// - /// Panics with provided error message if the result was `Destroyed`. - pub fn expect(self, error: &str) -> T { - match self { - EventResult::Sent(v) => v, - EventResult::Destroyed => panic!("{}", error), - } - } +#[cfg(feature = "native_lib")] +/// C-associated types +/// +/// Required for plugging wayland-scanner generated protocols +/// or interfacing with C code using wayland objects. +pub mod sys { + pub use super::generated::c_interfaces as protocol_interfaces; + pub use wayland_sys::{common, server}; } -/// Represents the state of liveness of a wayland object -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum Liveness { - /// This object is alive and events can be sent to it - Alive, - /// This object is dead, sending it events will do nothing and - /// return and error. - Dead, - /// This object is not managed by `wayland-server`, you can send it events - /// but this might crash the program if it was actually dead. - Unmanaged, +/// Generated interfaces for the core wayland protocol +pub mod protocol { + #[cfg(feature = "native_lib")] + pub use generated::c_api::*; } mod generated { @@ -292,34 +145,15 @@ mod generated { #![allow(non_upper_case_globals, non_snake_case, unused_imports)] #![allow(missing_docs)] - pub mod interfaces { - //! Interfaces for the core protocol - // You might need them for the bindings generated for protocol extensions - include!(concat!(env!("OUT_DIR"), "/wayland_interfaces.rs")); + #[cfg(feature = "native_lib")] + pub mod c_interfaces { + include!(concat!(env!("OUT_DIR"), "/wayland_c_interfaces.rs")); } - - pub mod server { - //! The wayland core protocol - // This module contains all objects of the core wayland protocol. - // - // It has been generated from the `wayland.xml` protocol file - // using `wayland_scanner`. - // Imports that need to be available to submodules - // but should not be in public API. - // Will be fixable with pub(restricted). - - #[doc(hidden)] - pub use super::interfaces; - #[doc(hidden)] - pub use {Client, EventLoopHandle, EventResult, Implementable, Liveness, Resource}; - - include!(concat!(env!("OUT_DIR"), "/wayland_api.rs")); + #[cfg(feature = "native_lib")] + pub mod c_api { + pub(crate) use {NewResource, Resource}; + pub(crate) use wayland_commons::{AnonymousObject, Interface, MessageGroup}; + pub(crate) use wayland_sys as sys; + include!(concat!(env!("OUT_DIR"), "/wayland_c_api.rs")); } } - -pub mod sys { - //! Reexports of types and objects from wayland-sys - - pub use wayland_sys::common::*; - pub use wayland_sys::server::*; -} diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs new file mode 100644 index 00000000000..d427b52d425 --- /dev/null +++ b/wayland-server/src/resource.rs @@ -0,0 +1,609 @@ +use wayland_commons::{Implementation, Interface, MessageGroup}; + +use {Client, LoopToken}; + +#[cfg(feature = "native_lib")] +use wayland_sys::server::*; + +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; + +pub(crate) struct ResourceInternal { + alive: AtomicBool, + user_data: AtomicPtr<()>, +} + +impl ResourceInternal { + fn new() -> ResourceInternal { + ResourceInternal { + alive: AtomicBool::new(true), + user_data: AtomicPtr::new(::std::ptr::null_mut()), + } + } +} + +/// An handle to a wayland resource +/// +/// This represents a wayland object instanciated in a 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 events to the associated client, +/// via the `send` method. +pub struct Resource { + _i: ::std::marker::PhantomData<*const I>, + #[cfg(not(feature = "native_lib"))] + internal: Arc, + #[cfg(feature = "native_lib")] + internal: Option>, + #[cfg(feature = "native_lib")] + ptr: *mut wl_resource, +} + +unsafe impl Send for Resource {} +unsafe impl Sync for Resource {} + +impl Resource { + /// Send an event through this object + /// + /// The event will be send to the client associated to this + /// object. + pub fn send(&self, msg: I::Event) { + #[cfg(not(feature = "native_lib"))] + { + if !self.internal.alive.load(Ordering::Acquire) { + // don't send message to dead objects ! + return; + } + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + if let Some(ref internal) = self.internal { + // object is managed + if !internal.alive.load(Ordering::Acquire) { + // don't send message to dead objects ! + return; + } + } + msg.as_raw_c_in(|opcode, args| unsafe { + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_resource_post_event_array, + self.ptr, + opcode, + args.as_ptr() as *mut _ + ); + }); + } + } + + /// Check if the object associated with this resource is still alive + /// + /// Will return `false` if either: + /// + /// - The object has been destroyed + /// - The object is not managed by this library (see the `from_c_ptr` method) + pub fn is_alive(&self) -> bool { + #[cfg(not(feature = "native_lib"))] + { + self.internal.alive.load(Ordering::Acquire) + } + #[cfg(feature = "native_lib")] + { + self.internal + .as_ref() + .map(|i| i.alive.load(Ordering::Acquire)) + .unwrap_or(false) + } + } + + /// Retrieve the interface version of this wayland object instance + /// + /// Returns 0 on dead objects + pub fn version(&self) -> u32 { + if !self.is_alive() { + return 0; + } + + #[cfg(not(feature = "native_lib"))] + { + unimplemented!(); + } + #[cfg(feature = "native_lib")] + { + unsafe { ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_version, self.ptr) as u32 } + } + } + + #[cfg(feature = "native_lib")] + /// Check whether this resource is managed by the library or not + /// + /// See `from_c_ptr` for details. + pub fn is_external(&self) -> bool { + self.internal.is_none() + } + + /// Clone this resource + /// + /// It only creates a new handle to the same wayland + /// object, and does not create a new one. + pub fn clone(&self) -> Resource { + Resource { + _i: ::std::marker::PhantomData, + internal: self.internal.clone(), + #[cfg(feature = "native_lib")] + ptr: self.ptr, + } + } + + /// Check if the other resource refers to the same underlying wayland object + pub fn equals(&self, other: &Resource) -> bool { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + 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, + } + } + } + + /// Check if this resource and the other belong to the same client + /// + /// Always return false if either of them is dead + pub fn same_client_as(&self, other: &Resource) -> bool { + if !(self.is_alive() && other.is_alive()) { + return false; + } + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + unsafe { + let my_client_ptr = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_client, self.ptr); + let other_client_ptr = + ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_client, other.ptr); + my_client_ptr == other_client_ptr + } + } + } + + /// Posts a protocol error to this resource + /// + /// The error code can be obtained from the various `Error` enums of the protocols. + /// + /// An error is fatal to the client that caused it. + pub fn post_error(&self, error_code: u32, msg: String) { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!(); + } + #[cfg(feature = "native_lib")] + { + // If `str` contains an interior null, the actual transmitted message will + // be truncated at this point. + unsafe { + let cstring = ::std::ffi::CString::from_vec_unchecked(msg.into()); + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_resource_post_error, + self.ptr, + error_code, + cstring.as_ptr() + ) + } + } + } + + /// Associate an arbitrary payload to this object + /// + /// The pointer you associate here can be retrieved from any + /// other resource handle to the same wayland object. + /// + /// Setting or getting user data is done as an atomic operation. + /// You are responsible for the correct initialization of this + /// pointer, synchronisation of access, and destruction of the + /// contents at the appropriate time. + pub fn set_user_data(&self, ptr: *mut ()) { + #[cfg(not(feature = "native_lib"))] + { + self.internal.user_data.store(ptr, Ordering::Release); + } + #[cfg(feature = "native_lib")] + { + if let Some(ref inner) = self.internal { + inner.user_data.store(ptr, Ordering::Release); + } + } + } + + /// Retrieve the arbitrary payload associated to this object + /// + /// See `set_user_data` for explanations. + pub fn get_user_data(&self) -> *mut () { + #[cfg(not(feature = "native_lib"))] + { + self.internal.user_data.load(Ordering::Acquire) + } + #[cfg(feature = "native_lib")] + { + if let Some(ref inner) = self.internal { + inner.user_data.load(Ordering::Acquire) + } else { + ::std::ptr::null_mut() + } + } + } + + /// Retrieve an handle to the client associated with this resource + /// + /// Returns `None` if the resource is no longer alive. + pub fn client(&self) -> Option { + if self.is_alive() { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + unsafe { + let client_ptr = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_client, self.ptr); + Some(Client::from_ptr(client_ptr)) + } + } + } else { + None + } + } + + #[cfg(feature = "native_lib")] + /// Get a raw pointer to the underlying wayland object + /// + /// Retrieve a pointer to the object from the `libwayland-server.so` library. + /// You will mostly need it to interface with C libraries needing access + /// to wayland objects (to initialize an opengl context for example). + pub fn c_ptr(&self) -> *mut wl_resource { + self.ptr + } + + #[cfg(feature = "native_lib")] + /// Create a `Resource` instance from a C pointer + /// + /// Create a `Resource` 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 resource handle 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-server 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 `false` 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 pointer to this object. + /// + /// In order to handle protocol races, invoking it with a NULL pointer will + /// create an already-dead object. + pub unsafe fn from_c_ptr(ptr: *mut wl_resource) -> Self { + if ptr.is_null() { + return Resource { + _i: ::std::marker::PhantomData, + internal: Some(Arc::new(ResourceInternal { + alive: AtomicBool::new(false), + user_data: AtomicPtr::new(::std::ptr::null_mut()), + })), + ptr: ptr, + }; + } + + let is_managed = { + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_resource_instance_of, + ptr, + I::c_interface(), + &::wayland_sys::RUST_MANAGED as *const u8 as *const _ + ) != 0 + }; + let internal = if is_managed { + let user_data = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_user_data, ptr) + as *mut self::native_machinery::ResourceUserData; + Some((*user_data).internal.clone()) + } else { + None + }; + Resource { + _i: ::std::marker::PhantomData, + internal: internal, + ptr: ptr, + } + } + + /// Check whether this resource has been implemented with given type + /// + /// Always returns false if the resource is no longer alive + pub fn is_implemented_with(&self) -> bool + where + Impl: Implementation, I::Request> + 'static, + { + if !self.is_alive() { + return false; + } + #[cfg(not(feature = "native_lib"))] + { + unimplemented!(); + } + #[cfg(feature = "native_lib")] + { + let user_data = unsafe { + let ptr = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_user_data, self.ptr) + as *mut self::native_machinery::ResourceUserData; + &*ptr + }; + user_data.is_impl::() + } + } +} + +/// A newly-created resource that needs implementation +/// +/// Whenever a new wayland object is created, you will +/// receive it as a `NewResource`. You then have to provide an +/// implementation for it, in order to process the incoming +/// events it may receive. Once this done you will be able +/// to use it as a regular `Resource`. +/// +/// Implementations are structs implementing the appropriate +/// variant of the `Implementation` trait. They can also be +/// closures. +pub struct NewResource { + _i: ::std::marker::PhantomData<*const I>, + #[cfg(feature = "native_lib")] + ptr: *mut wl_resource, +} + +impl NewResource { + /// Implement this resource using given function, destructor, and implementation data. + pub fn implement(self, implementation: Impl, destructor: Option) -> Resource + where + Impl: Implementation, I::Request> + Send + 'static, + Dest: FnMut(Resource, Box, I::Request>>) + Send + 'static, + { + unsafe { self.implement_inner(implementation, destructor) } + } + + /// Implement this resource using given function and implementation data. + /// + /// This method allows the implementation data to not be `Send`, but requires for + /// safety that you provide a token to the event loop owning the proxy. As the token + /// is not `Send`, this ensures you are implementing the resource from the same thread + /// as the event loop runs on. + /// + /// ** Panics ** + /// + /// This function will panic if you create several wayland event loops and do not + /// provide a token to the right one. + pub fn implement_nonsend( + self, + implementation: Impl, + destructor: Option, + token: &LoopToken, + ) -> Resource + where + Impl: Implementation, I::Request> + 'static, + Dest: FnMut(Resource, Box, I::Request>>) + 'static, + { + unsafe { + assert!( + token.matches(self.ptr), + "Tried to implement a Resource with the wrong LoopToken." + ); + self.implement_inner(implementation, destructor) + } + } + + unsafe fn implement_inner(self, implementation: Impl, destructor: Option) -> Resource + where + Impl: Implementation, I::Request> + 'static, + Dest: FnMut(Resource, Box, I::Request>>) + 'static, + { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + let new_user_data = Box::new(self::native_machinery::ResourceUserData::new( + implementation, + destructor, + )); + let internal = new_user_data.internal.clone(); + + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_resource_set_dispatcher, + self.ptr, + self::native_machinery::resource_dispatcher::, + &::wayland_sys::RUST_MANAGED as *const _ as *const _, + Box::into_raw(new_user_data) as *mut _, + Some(self::native_machinery::resource_destroy::) + ); + + Resource { + _i: ::std::marker::PhantomData, + internal: Some(internal), + ptr: self.ptr, + } + } + } + + #[cfg(feature = "native_lib")] + /// Get a raw pointer to the underlying wayland object + /// + /// Retrieve a pointer to the object from the `libwayland-server.so` library. + /// You will mostly need it to interface with C libraries needing access + /// to wayland objects. + /// + /// Use this if you need to pass an unimplemented object to the C library + /// you are interfacing with. + pub fn c_ptr(&self) -> *mut wl_resource { + self.ptr + } + + #[cfg(feature = "native_lib")] + /// Create a `NewResource` instance from a C pointer. + /// + /// By doing so, you assert that this wayland object was newly created and + /// can be safely implemented. As implementing it will overwrite any previously + /// associated data or implementation, this can cause weird errors akin to + /// memory corruption if it was not the case. + pub unsafe fn from_c_ptr(ptr: *mut wl_resource) -> Self { + NewResource { + _i: ::std::marker::PhantomData, + ptr: ptr, + } + } +} + +#[cfg(feature = "native_lib")] +mod native_machinery { + use wayland_sys::common::*; + use wayland_sys::server::*; + + use std::sync::Arc; + use std::sync::atomic::Ordering; + use std::os::raw::{c_int, c_void}; + + use super::Resource; + + use wayland_commons::{Implementation, Interface, MessageGroup}; + + pub(crate) struct ResourceUserData { + _i: ::std::marker::PhantomData<*const I>, + pub(crate) internal: Arc, + implem: Option< + ( + Box, I::Request>>, + Option, Box, I::Request>>)>>, + ), + >, + } + + impl ResourceUserData { + pub(crate) fn new(implem: Impl, destructor: Option) -> ResourceUserData + where + Impl: Implementation, I::Request> + 'static, + Dest: FnMut(Resource, Box, I::Request>>) + 'static, + { + ResourceUserData { + _i: ::std::marker::PhantomData, + internal: Arc::new(super::ResourceInternal::new()), + implem: Some((Box::new(implem), destructor.map(|d| Box::new(d) as Box<_>))), + } + } + + pub(crate) fn is_impl(&self) -> bool + where + Impl: Implementation, I::Request> + 'static, + { + self.implem + .as_ref() + .map(|implem| implem.0.is::()) + .unwrap_or(false) + } + } + + pub(crate) unsafe extern "C" fn resource_dispatcher( + _implem: *const c_void, + resource: *mut c_void, + opcode: u32, + _msg: *const wl_message, + args: *const wl_argument, + ) -> c_int + where + I: Interface, + { + let resource = resource as *mut wl_resource; + + // 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 || { + // parse the message: + let msg = I::Request::from_raw_c(resource as *mut _, opcode, args)?; + let must_destroy = msg.is_destructor(); + // create the resource object + let resource_obj = super::Resource::::from_c_ptr(resource); + // retrieve the impl + let user_data = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_user_data, resource); + { + let user_data = &mut *(user_data as *mut ResourceUserData); + let implem = user_data.implem.as_mut().unwrap(); + let &mut (ref mut implem_func, _) = implem; + if must_destroy { + user_data.internal.alive.store(false, Ordering::Release); + } + // call the impl + implem_func.receive(msg, resource_obj); + } + if must_destroy { + // final cleanup + ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_destroy, resource); + } + Ok(()) + }); + // check the return status + match ret { + Ok(Ok(())) => return 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() + } + } + } + + pub(crate) unsafe extern "C" fn resource_destroy(resource: *mut wl_resource) { + let user_data = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_get_user_data, resource); + + // 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 mut user_data = Box::from_raw(user_data as *mut ResourceUserData); + user_data.internal.alive.store(false, Ordering::Release); + let implem = user_data.implem.as_mut().unwrap(); + let &mut (ref mut implem_func, ref mut destructor) = implem; + if let Some(mut dest_func) = destructor.take() { + let retrieved_implem = ::std::mem::replace(implem_func, Box::new(|_, _| {})); + let resource_obj = super::Resource::::from_c_ptr(resource); + dest_func(resource_obj, retrieved_implem); + } + }); + + if let Err(_) = ret { + eprintln!( + "[wayland-client error] A destructor for {} panicked.", + I::NAME + ); + ::libc::abort() + } + } +} diff --git a/wayland-server/src/sources.rs b/wayland-server/src/sources.rs new file mode 100644 index 00000000000..a824e4c2272 --- /dev/null +++ b/wayland-server/src/sources.rs @@ -0,0 +1,325 @@ +//! Event sources related functionnality +//! +//! Different event sources can be registered to event loops additionnaly +//! to the wayland listening and clients sockets. This module contains types +//! associated with this functionnality, notably the `Source` type. +//! +//! See the methods of the `LoopToken` type for the methods used to create these +//! event sources. + +use std::cell::RefCell; +use std::rc::Rc; +use std::io::Error as IoError; +use std::os::raw::{c_int, c_void}; +use std::os::unix::io::RawFd; + +#[cfg(feature = "native_lib")] +use wayland_sys::server::*; + +use wayland_commons::Implementation; + +/// A handle to an event source +/// +/// This object is obtained as the result of methods on the `LoopToken` +/// object adding event sources to the event loop. And can be used to +/// interact with the event source afterwards, modifying (if applicable) +/// or destroying it. +/// +/// Dropping this object will *not* remove the event source from the event +/// loop, you need to use the `remove()` method for that. +pub struct Source { + _e: ::std::marker::PhantomData<*const E>, + #[cfg(feature = "native_lib")] + ptr: *mut wl_event_source, + #[cfg(feature = "native_lib")] + data: *mut Box>, +} + +impl Source { + #[cfg(feature = "native_lib")] + pub(crate) fn make(ptr: *mut wl_event_source, data: Box>>) -> Source { + Source { + _e: ::std::marker::PhantomData, + ptr: ptr, + data: Box::into_raw(data), + } + } + + /// Remove this event source from the event loop + /// + /// You are returned the implementation you provided + /// for this event source at creation. + pub fn remove(self) -> Box> { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + unsafe { + ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_event_source_remove, self.ptr); + let data = Box::from_raw(self.data); + *data + } + } + } +} + +// FD event source + +bitflags!{ + /// Flags to register interest on a file descriptor + pub struct FdInterest: u32 { + /// Interest to be notified when the file descriptor is readable + const READ = 0x01; + /// Interest to be notified when the file descriptor is writable + const WRITE = 0x02; + } +} + +/// An event generated by an FD event source +pub enum FdEvent { + /// The FD is ready to be acted upon + Ready { + /// The concerned FD + fd: RawFd, + /// Mask indicating if the FD is ready for reading or writing + mask: FdInterest, + }, + /// An error occured while polling the FD + Error { + /// The concerned FD + fd: RawFd, + /// The reported error + error: IoError, + }, +} + +impl Source { + /// Change the registered interest for this FD + pub fn update_mask(&mut self, mask: FdInterest) { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + unsafe { + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_event_source_fd_update, + self.ptr, + mask.bits() + ); + } + } + } +} + +pub(crate) unsafe extern "C" fn event_source_fd_dispatcher(fd: c_int, mask: u32, data: *mut c_void) -> c_int { + // 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 implem = &mut *(data as *mut Box>); + if mask & 0x08 > 0 { + // EPOLLERR + use nix::sys::socket; + let err = match socket::getsockopt(fd, socket::sockopt::SocketError) { + Ok(err) => err, + Err(_) => { + // error while retrieving the error code ??? + eprintln!( + "[wayland-server error] Error while retrieving error code on socket {}, aborting.", + fd + ); + ::libc::abort(); + } + }; + implem.receive( + FdEvent::Error { + fd: fd, + error: IoError::from_raw_os_error(err), + }, + (), + ); + } else if mask & 0x04 > 0 { + // EPOLLHUP + implem.receive( + FdEvent::Error { + fd: fd, + error: IoError::new(::std::io::ErrorKind::ConnectionAborted, ""), + }, + (), + ); + } else { + let mut bits = FdInterest::empty(); + if mask & 0x02 > 0 { + bits = bits | FdInterest::WRITE; + } + if mask & 0x01 > 0 { + bits = bits | FdInterest::READ; + } + implem.receive(FdEvent::Ready { fd: fd, mask: bits }, ()); + } + }); + match ret { + Ok(()) => return 0, // all went well + Err(_) => { + // a panic occured + eprintln!( + "[wayland-server error] A handler for fd {} event source panicked, aborting.", + fd + ); + ::libc::abort(); + } + } +} + +// Timer event source + +/// A timer generated event. +pub struct TimerEvent; + +impl Source { + /// Set the delay of this timer + /// + /// The callback will be called during the next dispatch of the + /// event loop after this time (in milliseconds) is elapsed. + /// + /// Manually the delay to 0 stops the timer (the callback won't be + /// called). + pub fn set_delay_ms(&mut self, delay: i32) { + unsafe { + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_event_source_timer_update, + self.ptr, + delay + ); + } + } +} + +pub(crate) unsafe extern "C" fn event_source_timer_dispatcher(data: *mut c_void) -> c_int { + // 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 implem = &mut *(data as *mut Box>); + implem.receive(TimerEvent, ()); + }); + match ret { + Ok(()) => return 0, // all went well + Err(_) => { + // a panic occured + eprintln!("[wayland-server error] A handler for a timer event source panicked, aborting.",); + ::libc::abort(); + } + } +} + +// Signal event source + +/// An event generated by an UNIX signal event source +/// +/// Contains an enum indicating which signal was received. +pub struct SignalEvent(::nix::sys::signal::Signal); + +pub(crate) unsafe extern "C" fn event_source_signal_dispatcher(signal: c_int, data: *mut c_void) -> c_int { + // 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 implem = &mut *(data as *mut Box>); + let sig = match ::nix::sys::signal::Signal::from_c_int(signal) { + Ok(sig) => sig, + Err(_) => { + // Actually, this cannot happen, as we cannot register an event source for + // an unknown signal... + eprintln!( + "[wayland-server error] Unknown signal in signal event source: {}, aborting.", + signal + ); + ::libc::abort(); + } + }; + implem.receive(SignalEvent(sig), ()); + }); + match ret { + Ok(()) => return 0, // all went well + Err(_) => { + // a panic occured + eprintln!("[wayland-server error] A handler for a timer event source panicked, aborting.",); + ::libc::abort(); + } + } +} + +// Idle event source + +/// Idle event source +/// +/// A handle to an idle event source, see `LoopToken::add_idle_event_source()`. +/// +/// Dropping this struct does not remove the event source, +/// use the `remove` method for that. +pub struct IdleSource { + ptr: *mut wl_event_source, + data: Rc>, bool)>>, +} + +impl IdleSource { + #[cfg(feature = "native_lib")] + pub(crate) fn make( + ptr: *mut wl_event_source, + data: Rc>, bool)>>, + ) -> IdleSource { + IdleSource { + ptr: ptr, + data: data, + } + } + + /// Remove this event source from its event loop + /// + /// You retrieve the associated implementation. The event source + /// is removed and if it hadn't been fired yet, it is cancelled. + pub fn remove(self) -> Box> { + let dispatched = self.data.borrow().1; + if !dispatched { + unsafe { + // unregister this event source + ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_event_source_remove, self.ptr); + // recreate the outstanding reference that was not consumed + let _ = Rc::from_raw(&*self.data); + } + } + // we are now the only oustanding reference + let data = Rc::try_unwrap(self.data) + .unwrap_or_else(|_| panic!("Idle Rc was not singly owned.")) + .into_inner(); + data.0 + } +} + +pub(crate) unsafe extern "C" fn event_source_idle_dispatcher(data: *mut c_void) { + // 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 data = &*(data as *mut RefCell<(Box>, bool)>); + let mut data = data.borrow_mut(); + data.0.receive((), ()); + }); + match ret { + Ok(()) => { + // all went well + // free the refence to the idata, as this event source cannot be called again + let data = Rc::from_raw(data as *mut RefCell<(Box>, bool)>); + // store that the dispatching occured + data.borrow_mut().1 = true; + } + Err(_) => { + // a panic occured + eprintln!("[wayland-server error] A handler for a timer event source panicked, aborting.",); + ::libc::abort(); + } + } +} diff --git a/wayland-sys/Cargo.toml b/wayland-sys/Cargo.toml index 1ea762f822e..4328f499ddd 100644 --- a/wayland-sys/Cargo.toml +++ b/wayland-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wayland-sys" -version = "0.14.1" +version = "0.20.0" repository = "https://github.com/smithay/wayland-rs" documentation = "https://smithay.github.io/wayland-rs/wayland_sys/" authors = ["Victor Berger "] diff --git a/wayland-sys/src/client.rs b/wayland-sys/src/client.rs index c3a4cc63510..dfe22ccf339 100644 --- a/wayland-sys/src/client.rs +++ b/wayland-sys/src/client.rs @@ -2,6 +2,7 @@ //! //! The generated handle is named `WAYLAND_CLIENT_HANDLE` +#![cfg_attr(rustfmt, rustfmt_skip)] use super::common::*; use std::os::raw::{c_char, c_int, c_void}; @@ -44,9 +45,8 @@ external_library!(WaylandClient, "wayland-client", fn wl_proxy_add_listener(*mut wl_proxy, *mut extern fn(), *mut c_void) -> c_int, fn wl_proxy_get_listener(*mut wl_proxy) -> *const c_void, fn wl_proxy_add_dispatcher(*mut wl_proxy, wl_dispatcher_func_t, *const c_void, *mut c_void) -> c_int, - fn wl_proxy_marshal_array_constructor(*mut wl_proxy, u32, *mut wl_argument, *const wl_interface) -> c_int, - fn wl_proxy_marshal_array_constructor_versioned(*mut wl_proxy, u32, *mut wl_argument, *const wl_interface, u32) -> c_int, - + fn wl_proxy_marshal_array_constructor(*mut wl_proxy, u32, *mut wl_argument, *const wl_interface) -> *mut wl_proxy, + fn wl_proxy_marshal_array_constructor_versioned(*mut wl_proxy, u32, *mut wl_argument, *const wl_interface, u32) -> *mut wl_proxy, fn wl_proxy_marshal_array(*mut wl_proxy, u32, *mut wl_argument ) -> (), fn wl_proxy_set_user_data(*mut wl_proxy, *mut c_void) -> (), fn wl_proxy_get_user_data(*mut wl_proxy) -> *mut c_void, @@ -54,6 +54,8 @@ external_library!(WaylandClient, "wayland-client", fn wl_proxy_get_class(*mut wl_proxy) -> *const c_char, fn wl_proxy_set_queue(*mut wl_proxy, *mut wl_event_queue) -> (), fn wl_proxy_get_version(*mut wl_proxy) -> u32, + fn wl_proxy_create_wrapper(*mut wl_proxy) -> *mut wl_proxy, + fn wl_proxy_wrapper_destroy(*mut wl_proxy) -> (), // log fn wl_log_set_handler_client(wl_log_func_t) -> (), diff --git a/wayland-sys/src/common.rs b/wayland-sys/src/common.rs index df01871771f..7681d9baf0a 100644 --- a/wayland-sys/src/common.rs +++ b/wayland-sys/src/common.rs @@ -2,6 +2,7 @@ //! libraries. use std::os::raw::{c_char, c_int, c_void}; +use std::os::unix::io::RawFd; #[repr(C)] pub struct wl_message { @@ -54,15 +55,17 @@ pub fn wl_fixed_from_int(i: i32) -> wl_fixed_t { // must be the appropriate size // can contain i32, u32 and pointers #[repr(C)] -pub struct wl_argument { - _f: usize, +pub union wl_argument { + pub i: i32, + pub u: u32, + pub f: wl_fixed_t, + pub s: *const c_char, + pub o: *const c_void, + pub n: u32, + pub a: *const wl_array, + pub h: RawFd, } -pub type wl_dispatcher_func_t = unsafe extern "C" fn( - *const c_void, - *mut c_void, - u32, - *const wl_message, - *const wl_argument, -) -> c_int; +pub type wl_dispatcher_func_t = + unsafe extern "C" fn(*const c_void, *mut c_void, u32, *const wl_message, *const wl_argument) -> c_int; pub type wl_log_func_t = unsafe extern "C" fn(*const c_char, ...); diff --git a/wayland-sys/src/server.rs b/wayland-sys/src/server.rs index a27e418cd5d..e99852f5e74 100644 --- a/wayland-sys/src/server.rs +++ b/wayland-sys/src/server.rs @@ -2,6 +2,7 @@ //! //! The generated handle is named `WAYLAND_SERVER_HANDLE` +#![cfg_attr(rustfmt, rustfmt_skip)] use super::common::*; use libc::{gid_t, pid_t, uid_t}; @@ -64,6 +65,7 @@ external_library!(WaylandServer, "wayland-server", fn wl_display_get_destroy_listener(*mut wl_display, wl_notify_func_t) -> *mut wl_listener, fn wl_global_create(*mut wl_display, *const wl_interface, c_int, *mut c_void, wl_global_bind_func_t) -> *mut wl_global, fn wl_display_init_shm(*mut wl_display) -> c_int, + fn wl_display_add_client_created_listener(*mut wl_display, *mut wl_listener) -> (), // wl_event_loop fn wl_event_loop_create() -> *mut wl_event_loop, fn wl_event_loop_destroy(*mut wl_event_loop) -> (), @@ -180,6 +182,7 @@ pub mod signal { use super::WAYLAND_SERVER_HANDLE as WSH; use common::wl_list; use std::os::raw::c_void; + use std::ptr; // TODO: Is this really not UB ? macro_rules! offset_of( @@ -241,7 +244,7 @@ pub mod signal { } } ); - return ::std::ptr::null_mut(); + return ptr::null_mut(); } pub unsafe fn wl_signal_emit(signal: *mut wl_signal, data: *mut c_void) { @@ -255,4 +258,39 @@ pub mod signal { } ); } + + #[repr(C)] + struct ListenerWithUserData { + listener: wl_listener, + user_data: *mut c_void + } + + pub fn rust_listener_create(notify: wl_notify_func_t) -> *mut wl_listener { + let data = Box::into_raw(Box::new(ListenerWithUserData { + listener: wl_listener { + link: wl_list { + prev: ptr::null_mut(), + next: ptr::null_mut() + }, + notify: notify + }, + user_data: ptr::null_mut() + })); + return unsafe { &mut (*data).listener as *mut wl_listener } + } + + pub unsafe fn rust_listener_get_user_data(listener: *mut wl_listener) -> *mut c_void { + let data = container_of!(listener, ListenerWithUserData, listener); + return (*data).user_data + } + + pub unsafe fn rust_listener_set_user_data(listener: *mut wl_listener, user_data: *mut c_void) { + let data = container_of!(listener, ListenerWithUserData, listener); + (*data).user_data = user_data + } + + pub unsafe fn rust_listener_destroy(listener: *mut wl_listener) { + let data = container_of!(listener, ListenerWithUserData, listener); + let _ = Box::from_raw(data); + } }