From 2b1c5dbb96bc66c96e7d5b3a695eeb32feffe6f7 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Thu, 22 Feb 2018 10:55:22 +0100 Subject: [PATCH 01/60] Prepare rewrite, remove all that must go. --- Cargo.toml | 2 +- tests/attach_null_to_surface.rs | 102 --- tests/basic_global.rs | 79 -- tests/destructor_destroys.rs | 107 --- tests/dispatch_idle.rs | 24 - tests/helpers/mod.rs | 65 -- tests/scanner.rs | 97 -- tests/scanner_assets/client_code.rs | 739 --------------- tests/scanner_assets/interfaces.rs | 108 --- tests/scanner_assets/protocol.xml | 116 --- tests/scanner_assets/server_code.rs | 537 ----------- tests/skel.rs | 24 +- wayland-client/Cargo.toml | 17 +- wayland-client/examples/simple_client.rs | 28 - wayland-client/examples/simple_window.rs | 131 --- wayland-client/src/cursor.rs | 249 ------ wayland-client/src/display.rs | 202 ----- wayland-client/src/egl.rs | 109 --- wayland-client/src/env.rs | 350 -------- wayland-client/src/event_queue.rs | 367 -------- wayland-client/src/lib.rs | 264 ------ wayland-commons/Cargo.toml | 14 + wayland-commons/src/lib.rs | 0 wayland-protocols/Cargo.toml | 10 +- wayland-protocols/src/protocol_macro.rs | 44 +- wayland-scanner/Cargo.toml | 2 +- wayland-scanner/src/code_gen.rs | 1042 ---------------------- wayland-scanner/src/lib.rs | 5 +- wayland-server/Cargo.toml | 13 +- wayland-server/examples/simple_server.rs | 8 - wayland-server/src/client.rs | 28 - wayland-server/src/display.rs | 188 ---- wayland-server/src/event_loop.rs | 575 ------------ wayland-server/src/event_sources.rs | 426 --------- wayland-server/src/lib.rs | 325 ------- wayland-sys/Cargo.toml | 2 +- 36 files changed, 38 insertions(+), 6361 deletions(-) delete mode 100644 tests/attach_null_to_surface.rs delete mode 100644 tests/basic_global.rs delete mode 100644 tests/destructor_destroys.rs delete mode 100644 tests/dispatch_idle.rs delete mode 100644 tests/helpers/mod.rs delete mode 100644 tests/scanner.rs delete mode 100644 tests/scanner_assets/client_code.rs delete mode 100644 tests/scanner_assets/interfaces.rs delete mode 100644 tests/scanner_assets/protocol.xml delete mode 100644 tests/scanner_assets/server_code.rs delete mode 100644 wayland-client/examples/simple_client.rs delete mode 100644 wayland-client/examples/simple_window.rs delete mode 100644 wayland-client/src/cursor.rs delete mode 100644 wayland-client/src/display.rs delete mode 100644 wayland-client/src/egl.rs delete mode 100644 wayland-client/src/env.rs delete mode 100644 wayland-client/src/event_queue.rs create mode 100644 wayland-commons/Cargo.toml create mode 100644 wayland-commons/src/lib.rs delete mode 100644 wayland-scanner/src/code_gen.rs delete mode 100644 wayland-server/examples/simple_server.rs delete mode 100644 wayland-server/src/client.rs delete mode 100644 wayland-server/src/display.rs delete mode 100644 wayland-server/src/event_loop.rs delete mode 100644 wayland-server/src/event_sources.rs diff --git a/Cargo.toml b/Cargo.toml index ad0708c844b..3a6a25d3381 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,4 @@ wayland-protocols = { path = "./wayland-protocols", features = ["client", "serve 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/tests/attach_null_to_surface.rs b/tests/attach_null_to_surface.rs deleted file mode 100644 index 18063b50983..00000000000 --- a/tests/attach_null_to_surface.rs +++ /dev/null @@ -1,102 +0,0 @@ -#[macro_use] -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, StateToken}; - use ways::protocol::{wl_compositor, wl_surface}; - - pub struct CompositorState { - pub got_buffer: bool, - } - - 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 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: |_, _, _, _, _| {}, - } - } - - pub fn insert_compositor(event_loop: &mut EventLoop) -> StateToken { - let token = event_loop.state().insert(CompositorState::new()); - let _ = event_loop.register_global::( - 1, - |evlh, token, _client, compositor| { - evlh.register(&compositor, compositor_impl(), token.clone(), None); - }, - token.clone(), - ); - token - } -} - -wayland_env!(pub ClientEnv, - compositor: ::wayc::protocol::wl_compositor::WlCompositor -); - -#[test] -fn attach_null() { - // server setup - let mut server = TestServer::new(); - let server_comp_token = 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); - - // double roundtrip for env init - // - roundtrip(&mut client, &mut server); - 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); - } - - roundtrip(&mut client, &mut server); - - // final assertions - // - { - let state = server.event_loop.state(); - let handler = state.get(&server_comp_token); - assert!(handler.got_buffer); - } -} diff --git a/tests/basic_global.rs b/tests/basic_global.rs deleted file mode 100644 index 72b0ce3dd0b..00000000000 --- a/tests/basic_global.rs +++ /dev/null @@ -1,79 +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, v: i32) { - let _ = event_loop.register_global::(v, |_, _, _, _| {}, ()); - } -} - -wayland_env!(pub ClientEnv); - -#[test] -fn simple_global() { - // server setup - // - let mut server = TestServer::new(); - self::server_utils::insert_compositor(&mut server.event_loop, 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); - - // 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(); - 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); - - // 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); - - // 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(); - assert!(globals.len() == 4); - let mut seen = [false; 4]; - for &(_, ref interface, version) in globals { - assert!(interface == "wl_compositor"); - seen[version as usize - 1] = true; - } - assert_eq!(seen, [true, true, true, 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 deleted file mode 100644 index 46d86f176ba..00000000000 --- a/tests/helpers/mod.rs +++ /dev/null @@ -1,65 +0,0 @@ -// This module contains helpers functions and types that -// are not test in themselves, but are used by several tests. - -#![allow(dead_code)] - -use std::ffi::{OsStr, OsString}; - -pub struct TestServer { - pub display: ::ways::Display, - pub event_loop: ::ways::EventLoop, - pub socket_name: OsString, -} - -impl TestServer { - pub fn new() -> TestServer { - let (mut display, event_loop) = ::ways::create_display(); - let socket_name = display - .add_socket_auto() - .expect("Failed to create a server socket."); - - TestServer { - display: display, - event_loop: event_loop, - socket_name: socket_name, - } - } - - pub fn answer(&mut self) { - // for some reason, two dispatches are needed - self.event_loop.dispatch(Some(10)).unwrap(); - self.event_loop.dispatch(Some(10)).unwrap(); - self.display.flush_clients(); - } -} - -pub struct TestClient { - pub display: ::wayc::protocol::wl_display::WlDisplay, - pub event_queue: ::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."); - TestClient { - display: display, - event_queue: event_queue, - } - } -} - -pub fn roundtrip(client: &mut TestClient, server: &mut TestServer) { - // send to the server - client.display.flush().unwrap(); - // make it answer messages - server.answer(); - // dispatch all client-side - client.event_queue.dispatch_pending().unwrap(); - client - .event_queue - .prepare_read() - .unwrap() - .read_events() - .unwrap(); - client.event_queue.dispatch_pending().unwrap(); -} diff --git a/tests/scanner.rs b/tests/scanner.rs deleted file mode 100644 index d79e2fbc8c1..00000000000 --- a/tests/scanner.rs +++ /dev/null @@ -1,97 +0,0 @@ -extern crate difference; -extern crate wayland_scanner; - - -use difference::{Changeset, Difference}; -use std::io::Cursor; -use std::str::from_utf8; -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 CLIENT_CODE_TARGET: &'static str = include_str!("./scanner_assets/client_code.rs"); - -const SERVER_CODE_TARGET: &'static str = include_str!("./scanner_assets/server_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); - }, - } - } -} - -fn only_newlines_err(diffs: &[Difference]) -> bool { - for d in diffs { - match *d { - Difference::Add(_) | Difference::Rem(_) => return false, - _ => {} - } - } - return true; -} - -#[test] -fn interfaces_generation() { - let mut out = Vec::new(); - wayland_scanner::generate_interfaces_streams(Cursor::new(PROTOCOL.as_bytes()), &mut out); - let changeset = Changeset::new( - INTERFACES_TARGET, - from_utf8(&out).expect("Output of scanner was not UTF8."), - "\n", - ); - if changeset.distance != 0 && !only_newlines_err(&changeset.diffs) { - print_diff(&changeset.diffs); - panic!( - "Scanner output does not match expected output: d = {}", - changeset.distance - ); - } -} - -#[test] -fn client_code_generation() { - let mut out = Vec::new(); - wayland_scanner::generate_code_streams(Cursor::new(PROTOCOL.as_bytes()), &mut out, Side::Client); - let changeset = Changeset::new( - CLIENT_CODE_TARGET, - from_utf8(&out).expect("Output of scanner was not UTF8."), - "\n", - ); - if changeset.distance != 0 && !only_newlines_err(&changeset.diffs) { - print_diff(&changeset.diffs); - panic!( - "Scanner output does not match expected output: d = {}", - changeset.distance - ); - } -} - -#[test] -fn server_code_generation() { - let mut out = Vec::new(); - wayland_scanner::generate_code_streams(Cursor::new(PROTOCOL.as_bytes()), &mut out, Side::Server); - let changeset = Changeset::new( - SERVER_CODE_TARGET, - from_utf8(&out).expect("Output of scanner was not UTF8."), - "\n", - ); - if changeset.distance != 0 && !only_newlines_err(&changeset.diffs) { - print_diff(&changeset.diffs); - panic!( - "Scanner output does not match expected output: d = {}", - changeset.distance - ); - } -} 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/interfaces.rs b/tests/scanner_assets/interfaces.rs deleted file mode 100644 index db5e5e0dc53..00000000000 --- a/tests/scanner_assets/interfaces.rs +++ /dev/null @@ -1,108 +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. -*/ - -use std::os::raw::{c_char, c_void}; -use wayland_sys::common::*; - -const NULLPTR: *const c_void = 0 as *const c_void; - -static mut types_null: [*const wl_interface; 5] = [ - NULLPTR as *const wl_interface, - NULLPTR as *const wl_interface, - NULLPTR as *const wl_interface, - NULLPTR as *const wl_interface, - NULLPTR as *const wl_interface, -]; - -// wl_foo -static mut wl_foo_requests_create_bar_types: [*const wl_interface; 1] = [ - unsafe { &wl_bar_interface as *const wl_interface }, -]; - -pub static mut wl_foo_requests: [wl_message; 2] = [ - wl_message { name: b"foo_it\0" as *const u8 as *const c_char, signature: b"iusfh\0" as *const u8 as *const c_char, types: unsafe { &types_null as *const _ } }, - wl_message { name: b"create_bar\0" as *const u8 as *const c_char, signature: b"n\0" as *const u8 as *const c_char, types: unsafe { &wl_foo_requests_create_bar_types as *const _ } }, -]; - -pub static mut wl_foo_events: [wl_message; 1] = [ - wl_message { name: b"cake\0" as *const u8 as *const c_char, signature: b"2uu\0" as *const u8 as *const c_char, types: unsafe { &types_null as *const _ } }, -]; - -pub static mut wl_foo_interface: wl_interface = wl_interface { - name: b"wl_foo\0" as *const u8 as *const c_char, - version: 3, - request_count: 2, - requests: unsafe { &wl_foo_requests as *const _ }, - event_count: 1, - events: unsafe { &wl_foo_events as *const _ }, -}; - -// wl_bar -static mut wl_bar_requests_bar_delivery_types: [*const wl_interface; 3] = [ - NULLPTR as *const wl_interface, - unsafe { &wl_foo_interface as *const wl_interface }, - NULLPTR as *const wl_interface, -]; - -pub static mut wl_bar_requests: [wl_message; 2] = [ - wl_message { name: b"bar_delivery\0" as *const u8 as *const c_char, signature: b"2uoa\0" as *const u8 as *const c_char, types: unsafe { &wl_bar_requests_bar_delivery_types as *const _ } }, - wl_message { name: b"release\0" as *const u8 as *const c_char, signature: b"\0" as *const u8 as *const c_char, types: unsafe { &types_null as *const _ } }, -]; - -pub static mut wl_bar_interface: wl_interface = wl_interface { - name: b"wl_bar\0" as *const u8 as *const c_char, - version: 1, - request_count: 2, - requests: unsafe { &wl_bar_requests as *const _ }, - event_count: 0, - events: NULLPTR as *const wl_message, -}; - -// wl_display -pub static mut wl_display_interface: wl_interface = wl_interface { - name: b"wl_display\0" as *const u8 as *const c_char, - version: 1, - request_count: 0, - requests: NULLPTR as *const wl_message, - event_count: 0, - events: NULLPTR as *const wl_message, -}; - -// wl_registry - -pub static mut wl_registry_requests: [wl_message; 1] = [ - wl_message { name: b"bind\0" as *const u8 as *const c_char, signature: b"usun\0" as *const u8 as *const c_char, types: unsafe { &types_null as *const _ } }, -]; - -pub static mut wl_registry_interface: wl_interface = wl_interface { - name: b"wl_registry\0" as *const u8 as *const c_char, - version: 1, - request_count: 1, - requests: unsafe { &wl_registry_requests as *const _ }, - event_count: 0, - events: NULLPTR as *const wl_message, -}; - -// wl_callback - -pub static mut wl_callback_events: [wl_message; 1] = [ - wl_message { name: b"done\0" as *const u8 as *const c_char, signature: b"u\0" as *const u8 as *const c_char, types: unsafe { &types_null as *const _ } }, -]; - -pub static mut wl_callback_interface: wl_interface = wl_interface { - name: b"wl_callback\0" as *const u8 as *const c_char, - version: 1, - request_count: 0, - requests: NULLPTR as *const wl_message, - event_count: 1, - events: unsafe { &wl_callback_events as *const _ }, -}; - - diff --git a/tests/scanner_assets/protocol.xml b/tests/scanner_assets/protocol.xml deleted file mode 100644 index 89e148738f0..00000000000 --- a/tests/scanner_assets/protocol.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - - This is an example copyright. - It contains several lines. - AS WELL AS ALL CAPS TEXT. - - - - - This is the dedicated interface for doing foos over any - kind of other foos. - - - - - This will do some foo with its args. - - - - - - - - - - - Create a bar which will do its bar job. - - - - - - - List of the possible kind of cake supported by the protocol. - - - - - - - - - - - - - - - - The server advertizes that a kind of cake is available - - - - - - - - - This interface allows you to bar your foos. - - - - - Proceed to a bar delivery of given foo. - - - - - - - - - Notify the compositor that you have finished using this bar. - - - - - - - - - This global is special and should only generate code client-side, not server-side. - - - - - - This global is special and should only generate code client-side, not server-side. - - - - - This request is a special code-path, as its new-id argument as no target type. - - - - - - - - - This object has a special behavior regarding its destructor. - - - - - 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. - - - - - - - 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/skel.rs b/tests/skel.rs index fcad3644d60..a93251b65da 100644 --- a/tests/skel.rs +++ b/tests/skel.rs @@ -1,25 +1,3 @@ -extern crate wayland_client as wayc; -extern crate wayland_server as ways; - -mod helpers; - -use helpers::{roundtrip, TestClient, TestServer}; - #[test] -fn skel() { - // Server setup - // - let mut server = TestServer::new(); - - // Client setup - // - let mut client = TestClient::new(&server.socket_name); - - // Some message passing - // - roundtrip(&mut client, &mut server); - - // Final asserts - // - assert!(true); +fn it_works() { } diff --git a/wayland-client/Cargo.toml b/wayland-client/Cargo.toml index 911bdd54083..078f8130f85 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,15 @@ build = "build.rs" travis-ci = { repository = "smithay/wayland-rs" } [dependencies] -wayland-sys = { version = "0.14.1", features = ["client"], path = "../wayland-sys" } -libc = "0.2" -bitflags = "1.0" -token_store = "0.1.1" +wayland-commons = { version = "0.20.0", path = "../wayland-commons" } +wayland-sys = { version = "0.20.0", features = ["client"], path = "../wayland-sys", optional = true } [build-dependencies] -wayland-scanner = { version = "0.14.1", path = "../wayland-scanner" } - -[dev-dependencies] -byteorder = "1" -tempfile = "2" +wayland-scanner = { version = "0.20.0", path = "../wayland-scanner" } [features] -default = ["egl", "cursor"] +default = ["egl", "cursor", "native_lib"] +native_lib = [ "wayland-sys" ] dlopen = ["wayland-sys/dlopen"] egl = ["wayland-sys/egl"] cursor = ["wayland-sys/cursor"] 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 deleted file mode 100644 index 06de8a45255..00000000000 --- a/wayland-client/examples/simple_window.rs +++ /dev/null @@ -1,131 +0,0 @@ -#[macro_use] -extern crate wayland_client; - -extern crate tempfile; - -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), - }; - - let registry = display.get_registry(); - - let env_token = EnvHandler::::init(&mut event_queue, ®istry); - - event_queue.sync_roundtrip().unwrap(); - - // buffer (and window) width and height - let buf_x: u32 = 320; - let buf_y: u32 = 240; - - // create a tempfile to write the conents of the window on - let mut tmp = tempfile::tempfile() - .ok() - .expect("Unable to create a tempfile."); - // write the contents to it, lets put a nice color gradient - for i in 0..(buf_x * buf_y) { - let x = (i % buf_x) as u32; - let y = (i / buf_x) as u32; - let r: u32 = min(((buf_x - x) * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y); - let g: u32 = min((x * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y); - let b: u32 = min(((buf_x - x) * 0xFF) / buf_x, (y * 0xFF) / buf_y); - let _ = tmp.write_u32::((0xFF << 24) + (r << 16) + (g << 8) + b); - } - 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 - 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 - 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(), ()); - - loop { - display.flush().unwrap(); - event_queue.dispatch().unwrap(); - } -} diff --git a/wayland-client/src/cursor.rs b/wayland-client/src/cursor.rs deleted file mode 100644 index cf9533289f2..00000000000 --- a/wayland-client/src/cursor.rs +++ /dev/null @@ -1,249 +0,0 @@ -//! Cursor utilities -//! -//! This module contains bindings to the `libwayland-cursor.so` library. -//! -//! These utilities allows you to laod cursor images in order to match -//! your cursors to the ones of the system. -//! -//! First of all, the function `load_theme` will allow you to load a -//! `CursorTheme`, which represents the full cursor theme. -//! -//! From this theme, you can load a specific `Cursor`, which can each -//! contain several images if the cursor is animated. It provides you -//! with the means of knowing which frame of the animation shoudl be -//! 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; -use std::ffi::{CStr, CString}; -use std::marker::PhantomData; -use std::ops::Deref; -use std::os::raw::c_int; -use std::ptr; -use wayland_sys::cursor::*; - -/// Checks if the wayland-cursor lib is available and can be used -/// -/// Trying to call any function of this module if the lib cannot -/// be used will result in a panic. -pub fn is_available() -> bool { - is_lib_available() -} - -/// Represents a cursor theme loaded from the system. -pub struct CursorTheme { - theme: *mut wl_cursor_theme, -} - -unsafe impl Send for CursorTheme {} - -/// Attempts to load a cursor theme from given name. -/// -/// If no name is given or the requested theme is not found, will -/// load the default theme. -/// -/// Other arguments are the requested size for the cursor images (ex: 16) -/// and a handle to the global `WlShm` object. -/// -/// Panics: -/// -/// - If the `wayland-cursor` lib is not available (see `is_available()` function) -/// 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 { - let ptr = if let Some(theme) = name { - let cstr = CString::new(theme).expect("Theme name contained an interior null."); - unsafe { - ffi_dispatch!( - WAYLAND_CURSOR_HANDLE, - wl_cursor_theme_load, - cstr.as_ptr(), - size as c_int, - shm.ptr() - ) - } - } else { - unsafe { - ffi_dispatch!( - WAYLAND_CURSOR_HANDLE, - wl_cursor_theme_load, - ptr::null(), - size as c_int, - shm.ptr() - ) - } - }; - - assert!( - !ptr.is_null(), - "Memory allocation failure while loading a theme." - ); - - CursorTheme { theme: ptr } -} - -impl CursorTheme { - /// Retrieve a cursor from the theme. - /// - /// Returns `None` if this cursor is not provided by the theme. - /// - /// Panics if the name contains an interior null. - pub fn get_cursor(&self, name: &str) -> Option { - let cstr = CString::new(name).expect("Cursor name contained an interior null."); - let ptr = unsafe { - ffi_dispatch!( - WAYLAND_CURSOR_HANDLE, - wl_cursor_theme_get_cursor, - self.theme, - cstr.as_ptr() - ) - }; - if ptr.is_null() { - None - } else { - Some(Cursor { - _theme: PhantomData, - cursor: ptr, - }) - } - } -} - -impl Drop for CursorTheme { - fn drop(&mut self) { - unsafe { - ffi_dispatch!(WAYLAND_CURSOR_HANDLE, wl_cursor_theme_destroy, self.theme); - } - } -} - -/// A cursor from a theme. Can contain several images if animated. -pub struct Cursor<'a> { - _theme: PhantomData<&'a CursorTheme>, - cursor: *mut wl_cursor, -} - -unsafe impl<'a> Send for Cursor<'a> {} - -impl<'a> Cursor<'a> { - /// Retrieve the name of this cursor. - pub fn name(&self) -> String { - let name = unsafe { CStr::from_ptr((*self.cursor).name) }; - name.to_string_lossy().into_owned() - } - - /// Retrieve the number of images contained in this - /// animated cursor - pub fn image_count(&self) -> usize { - let count = unsafe { (*self.cursor).image_count }; - count as usize - } - - /// Retrieve the image number of cursor animation. - /// - /// Returns the image number of the animation that should be displayed - /// after a given amount of time since the beginning of the animation, - /// in milliseconds. - pub fn frame(&self, duration: u32) -> usize { - let frame = unsafe { - ffi_dispatch!( - WAYLAND_CURSOR_HANDLE, - wl_cursor_frame, - self.cursor, - duration - ) - }; - frame as usize - } - - /// Retrieve the image number and its duration. - /// - /// Same as `frame()`, but also returns the number of milliseconds this - /// frame should still be displayed. - pub fn frame_and_duration(&self, duration: u32) -> (usize, u32) { - let mut out_duration = 0u32; - let frame = unsafe { - ffi_dispatch!( - WAYLAND_CURSOR_HANDLE, - wl_cursor_frame_and_duration, - self.cursor, - duration, - &mut out_duration as *mut u32 - ) - } as usize; - (frame, out_duration) - } - - /// Retrieve a `CursorImageBuffer` containing the given image of an animation. - /// - /// It can be used to be attached to a surface as a classic `WlBuffer`. - /// - /// Returns `None` if the frame is out of bounds. - /// - /// Note: destroying this buffer (using the `destroy` method) will corrupt - /// your theme data, so you might not want to do it. - pub fn frame_buffer(&self, frame: usize) -> Option { - if frame >= self.image_count() { - None - } else { - 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) - }; - - Some(CursorImageBuffer { - _cursor: PhantomData, - buffer: buffer, - }) - } - } - } - - /// Retrive the metadate associated with given frame of the animation. - /// - /// The tuple contains: `(width, height, hotspot_x, hotspot_y, delay)` - /// - /// Returns `None` if the frame is out of bounds. - pub fn frame_info(&self, frame: usize) -> Option<(u32, u32, u32, u32, u32)> { - if frame >= self.image_count() { - None - } else { - let image = unsafe { &**(*self.cursor).images.offset(frame as isize) }; - Some(( - image.width, - image.height, - image.hotspot_x, - image.hotspot_y, - image.delay, - )) - } - } -} - -/// A buffer containing a cursor image. -pub struct CursorImageBuffer<'a> { - _cursor: PhantomData<&'a Cursor<'a>>, - buffer: WlBuffer, -} - -unsafe impl<'a> Send for CursorImageBuffer<'a> {} - -impl<'a> Deref for CursorImageBuffer<'a> { - type Target = WlBuffer; - fn deref(&self) -> &WlBuffer { - &self.buffer - } -} diff --git a/wayland-client/src/display.rs b/wayland-client/src/display.rs deleted file mode 100644 index 202ce7c07a7..00000000000 --- a/wayland-client/src/display.rs +++ /dev/null @@ -1,202 +0,0 @@ -use Proxy; -use event_queue::{create_event_queue, EventQueue}; -use generated::client::wl_display::WlDisplay; -use std::ffi::{CStr, CString, OsStr}; -use std::io; -use std::os::unix::ffi::OsStrExt; -use wayland_sys::client::*; - -/// Enum representing the possible reasons why connecting to the wayland server failed -#[derive(Debug)] -pub enum ConnectError { - /// The library was compiled with the `dlopen` feature, and the `libwayland-client.so` - /// library could not be found at runtime - NoWaylandLib, - /// Any needed library was found, but the listening socket of the server could not be - /// found. - /// - /// Most of the time, this means that the program was not started from a wayland session. - NoCompositorListening, -} - -/// 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, - }, -} - -/// 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); - } - 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)) - } -} - -/// Connect to the compositor socket -/// -/// Attempt to connect to a Wayland compositor on a given socket name -/// -/// 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); - } - // 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)) - } -} - -impl WlDisplay { - /// Non-blocking write to the server - /// - /// 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 _ - ) - }; - if ret >= 0 { - Ok(ret) - } else { - Err(io::Error::last_os_error()) - } - } - - /// Create a new EventQueue - /// - /// No object is by default attached to it. - pub fn create_event_queue(&self) -> EventQueue { - let evq = unsafe { - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_display_create_queue, - self.ptr() as *mut _ - ) - }; - unsafe { create_event_queue(self.ptr() as *mut _, Some(evq)) } - } - - /// 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))) - } - } - - /// 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 _ - ) - } -} diff --git a/wayland-client/src/egl.rs b/wayland-client/src/egl.rs deleted file mode 100644 index a8eb1a894a6..00000000000 --- a/wayland-client/src/egl.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! EGL utilities -//! -//! This module contains bindings to the `libwayland-egl.so` library. -//! -//! This library is used to interface with the OpenGL stack, and creating -//! EGL surfaces from a wayland surface. -//! -//! See WlEglSurface documentation for details. - -use Proxy; -use protocol::wl_surface::WlSurface; -use std::os::raw::c_void; -use wayland_sys::client::wl_proxy; -use wayland_sys::egl::*; - -/// Checks if the wayland-egl lib is available and can be used -/// -/// Trying to create an `WlEglSurface` while this function returns -/// `false` will result in a panic. -pub fn is_available() -> bool { - is_lib_available() -} - -unsafe impl Send for WlEglSurface {} -unsafe impl Sync for WlEglSurface {} - -/// EGL surface -/// -/// This object is a simple wrapper around a `WlSurface` to add the EGL -/// capabilities. Just use the `ptr` method once this object is created -/// to get the window pointer your OpenGL library is needing to initialize the -/// EGL context (you'll most likely need the display ptr as well, that you can -/// get via the `ptr` method of the `Proxy` trait on the `WlDisplay` object). -pub struct WlEglSurface { - ptr: *mut wl_egl_window, -} - -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) } - } - - /// Create an EGL surface from a raw pointer to a wayland surface - /// - /// This function is unsafe because `surface` must be a valid wl_surface pointer - pub unsafe fn new_from_raw(surface: *mut wl_proxy, width: i32, height: i32) -> WlEglSurface { - let ptr = ffi_dispatch!( - WAYLAND_EGL_HANDLE, - wl_egl_window_create, - surface, - width, - height - ); - WlEglSurface { ptr: ptr } - } - - /// Fetch current size of the EGL surface - pub fn get_size(&self) -> (i32, i32) { - let mut w = 0i32; - let mut h = 0i32; - unsafe { - ffi_dispatch!( - WAYLAND_EGL_HANDLE, - wl_egl_window_get_attached_size, - self.ptr, - &mut w as *mut i32, - &mut h as *mut i32 - ); - } - (w, h) - } - - /// Resize the EGL surface - /// - /// The two first arguments `(width, height)` are the new size of - /// the surface, the two others `(dx, dy)` represent the displacement - /// of the top-left corner of the surface. It allows you to control the - /// direction of the resizing if necessary. - pub fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) { - unsafe { - ffi_dispatch!( - WAYLAND_EGL_HANDLE, - wl_egl_window_resize, - self.ptr, - width, - height, - dx, - dy - ) - } - } - - /// Raw pointer to the EGL surface - /// - /// You'll need this pointer to initialize the EGL context in your - /// favourite OpenGL lib. - pub fn ptr(&self) -> *const c_void { - self.ptr as *const c_void - } -} - -impl Drop for WlEglSurface { - fn drop(&mut self) { - unsafe { - ffi_dispatch!(WAYLAND_EGL_HANDLE, wl_egl_window_destroy, self.ptr); - } - } -} 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 deleted file mode 100644 index d2d42b45562..00000000000 --- a/wayland-client/src/event_queue.rs +++ /dev/null @@ -1,367 +0,0 @@ -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::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<()>)>, -); - -/// 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, -} - -/// 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, - wlevq: Option<*mut wl_event_queue>, -} - -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 -/// -/// 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. -/// -/// 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: -/// -/// - 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. -/// -/// The event queues also provides you control on the flow of the program, via the `dispatch()` and -/// `dispatch_pending()` methods. -pub struct EventQueue { - handle: Box, - display: *mut wl_display, -} - -impl EventQueue { - /// Dispatches events from the internal buffer. - /// - /// Dispatches all events to their appropriate handlers. - /// 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 - /// other event queues. - /// - /// 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()) - } - } - - /// Dispatches pending events from the internal buffer. - /// - /// Dispatches all events to their appropriate handlers. - /// 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()) - } - } - - /// Synchronous roundtrip - /// - /// This call will cause a synchonous roundtrip with the wayland server. It will block until all - /// pending requests of this queue are sent to the server and it has processed all of them and - /// send the appropriate events. - /// - /// Handlers are called as a consequence. - /// - /// 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), - } - }; - if ret >= 0 { - Ok(ret) - } else { - Err(IoError::last_os_error()) - } - } - - /// Prepare an conccurent read - /// - /// Will declare your intention to read events from the server socket. - /// - /// Will return `None` if there are still some events awaiting dispatch on this EventIterator. - /// In this case, you need to call `dispatch_pending()` before calling this method again. - /// - /// As long as the returned guard is in scope, no events can be dispatched to any event iterator. - /// - /// The guard can then be destroyed by two means: - /// - /// - Calling its `cancel()` method (or letting it go out of scope): the read intention will - /// be cancelled - /// - Calling its `read_events()` method: will block until all existing guards are destroyed - /// by one of these methods, then events will be read and all blocked `read_events()` calls - /// will return. - /// - /// This call will otherwise not block on the server socket if it is empty, and return - /// an io error `WouldBlock` in such cases. - pub fn prepare_read(&self) -> Option { - let ret = unsafe { - match self.handle.wlevq { - Some(evtq) => ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_display_prepare_read_queue, - self.display, - evtq - ), - None => ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_prepare_read, self.display), - } - }; - if ret >= 0 { - Some(ReadEventsGuard { - display: self.display, - }) - } else { - None - } - } -} - -impl Deref for EventQueue { - type Target = EventQueueHandle; - fn deref(&self) -> &EventQueueHandle { - &*self.handle - } -} - -impl DerefMut for EventQueue { - fn deref_mut(&mut self) -> &mut EventQueueHandle { - &mut *self.handle - } -} - -/// A guard over a read intention. -/// -/// See `EventQueue::prepare_read()` for details about its use. -pub struct ReadEventsGuard { - display: *mut wl_display, -} - -impl ReadEventsGuard { - /// Read events - /// - /// Reads events from the server socket. If other `ReadEventsGuard` exists, will block - /// until they are all consumed or destroyed. - pub fn read_events(self) -> IoResult { - let ret = unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_read_events, self.display) }; - // Don't run destructor that would cancel the read intent - ::std::mem::forget(self); - if ret >= 0 { - Ok(ret) - } else { - Err(IoError::last_os_error()) - } - } - - /// Cancel the read - /// - /// Will cancel the read intention associated with this guard. Never blocks. - /// - /// Has the same effet as letting the guard go out of scope. - pub fn cancel(self) { - // just run the destructor - } -} - -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(); - } - } -} diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index 3a817884b70..e69de29bb2d 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -1,264 +0,0 @@ -//! Client-side Wayland connector -//! -//! # Overview -//! -//! 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 -//! -//! 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. -//! -//! 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. -//! -//! 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. -//! -//! ## Example of implementation -//! -//! You can register your wayland objects to an event queue: -//! -//! ```ignore -//! event_queue.register(&my_object, implementation, impl_data); -//! ``` -//! -//! A given wayland object can only be registered to a event -//! queue at a given time, re-registering it will overwrite -//! the previous configuration. -//! -//! Objects can be registered to event queues using the `&EventQueueHandle` -//! argument, available from withing an event callback. -//! -//! ## Event loop integration -//! -//! Once this setup is done, you can integrate the event queue -//! to the main event loop of your program: -//! -//! ```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(); -//! } -//! ``` -//! -//! For more precise control of the flow of the event queue -//! (and importantly non-blocking options), see `EventQueue` -//! documentation. -//! -//! # Protocols integration -//! -//! 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 the crate `wayland_scanner` and its documentation for -//! details about how to do so. - -#![warn(missing_docs)] - -#[macro_use] -extern crate bitflags; -extern crate libc; -extern crate token_store; -#[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; - -#[cfg(feature = "egl")] -pub mod egl; - -#[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}; - -/// Common routines for wayland proxy objects. -/// -/// 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<(), ()>; -} - -/// 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), - } - } -} - -/// 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, -} - -mod generated { - #![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)] - #![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")); - } - - 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")); - } -} - -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-commons/Cargo.toml b/wayland-commons/Cargo.toml new file mode 100644 index 00000000000..cbf29e7f85e --- /dev/null +++ b/wayland-commons/Cargo.toml @@ -0,0 +1,14 @@ +[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" } + diff --git a/wayland-commons/src/lib.rs b/wayland-commons/src/lib.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/wayland-protocols/Cargo.toml b/wayland-protocols/Cargo.toml index a403282c61f..ce138e505e5 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,13 +14,13 @@ 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-sys = { version = "0.20.0", path = "../wayland-sys" } +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] client = ["wayland-client"] diff --git a/wayland-protocols/src/protocol_macro.rs b/wayland-protocols/src/protocol_macro.rs index 0f194650328..990f6082aec 100644 --- a/wayland-protocols/src/protocol_macro.rs +++ b/wayland-protocols/src/protocol_macro.rs @@ -1,49 +1,7 @@ #[macro_escape] macro_rules! wayland_protocol( ($name: expr, [$(($import: ident, $interface: ident)),*]) => { - #[cfg(feature = "client")] - pub use self::generated::client::api as client; - - #[cfg(feature = "server")] - pub use self::generated::server::api as server; - - mod generated { - #![allow(dead_code,non_camel_case_types,unused_unsafe,unused_variables)] - #![allow(non_upper_case_globals,non_snake_case,unused_imports)] - #![allow(missing_docs)] - - #[cfg(feature = "client")] - pub mod client { - pub mod interfaces { - pub use wayland_client::protocol_interfaces::{$($interface),*}; - include!(concat!(env!("OUT_DIR"), "/", $name, "_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(crate) use wayland_client::protocol::{$($import),*}; - include!(concat!(env!("OUT_DIR"), "/", $name, "_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")); - } - - /// 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(crate) use wayland_server::protocol::{$($import),*}; - include!(concat!(env!("OUT_DIR"), "/", $name, "_server_api.rs")); - } - } - } + // TODO } ); 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/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/lib.rs b/wayland-scanner/src/lib.rs index 16846e7690b..9bb50decd55 100644 --- a/wayland-scanner/src/lib.rs +++ b/wayland-scanner/src/lib.rs @@ -112,7 +112,6 @@ mod parse; mod protocol; mod side; mod interface_gen; -mod code_gen; pub use side::Side; @@ -165,7 +164,7 @@ 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() + //code_gen::write_protocol(protocol, &mut out, side).unwrap() } /// Generate the interfaces for a protocol from/to IO streams @@ -192,5 +191,5 @@ pub fn generate_interfaces_streams(protocol: P1, target: &m /// - `side`: the side (client or server) to generate code for. pub fn generate_code_streams(protocol: P1, target: &mut P2, side: Side) { let protocol = parse::parse_stream(protocol); - code_gen::write_protocol(protocol, target, side).unwrap() + //code_gen::write_protocol(protocol, target, side).unwrap() } diff --git a/wayland-server/Cargo.toml b/wayland-server/Cargo.toml index 00421db8bd4..5de531b5213 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,13 @@ 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" -bitflags = "1.0" -token_store = "0.1.1" +wayland-commons = { version = "0.20.0", path = "../wayland-commons" } +wayland-sys = { version = "0.20.0", features = ["server"], path = "../wayland-sys", optional = true } [build-dependencies] -wayland-scanner = { version = "0.14.1", path = "../wayland-scanner" } +wayland-scanner = { version = "0.20.0", path = "../wayland-scanner" } [features] +default = ["native_lib"] dlopen = ["wayland-sys/dlopen"] +native_lib = ["wayland-sys"] 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 deleted file mode 100644 index 73d523550ef..00000000000 --- a/wayland-server/src/client.rs +++ /dev/null @@ -1,28 +0,0 @@ -use wayland_sys::server::*; - -/// A wayland client connected to your server -pub struct Client { - 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 { - self.ptr - } - - /// Post a "no memory" message to the 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) } - } - - /// Create a client object from a pointer - pub unsafe fn from_ptr(ptr: *mut wl_client) -> Client { - Client { ptr: ptr } - } -} diff --git a/wayland-server/src/display.rs b/wayland-server/src/display.rs deleted file mode 100644 index 38fa350ea79..00000000000 --- a/wayland-server/src/display.rs +++ /dev/null @@ -1,188 +0,0 @@ - - -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::unix::ffi::{OsStrExt, OsStringExt}; -use std::os::unix::io::{IntoRawFd, RawFd}; -use std::path::PathBuf; -use std::ptr; -use wayland_sys::server::*; - -/// A wayland socket -/// -/// This represents a socket your compositor can receive clients on. -pub struct Display { - ptr: *mut wl_display, -} - -/// 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 { - /// Add a listening socket to this display - /// - /// Wayland clients will be able to connect to your compositor from this socket. - /// - /// Socket will be created in the directory specified by the environment variable - /// `XDG_RUNTIME_DIR`. - /// - /// If a name is provided, it is used. Otherwise, if `WAYLAND_DISPLAY` environment - /// variable is set, its contents are used as socket name. Otherwise, `wayland-0` is used. - /// - /// Errors if `name` contains an interior null, or if `XDG_RUNTIME_DIR` is not set, - /// or if specified could not be bound (either it is already used or the compositor - /// does not have the rights to create it). - pub fn add_socket(&mut self, name: Option) -> IoResult<()> - 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", - )) - } - 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(()) - } - } - - /// Add an automatically named listening socket to this display - /// - /// Wayland clients will be able to connect to your compositor from this socket. - /// - /// Socket will be created in the directory specified by the environment variable - /// `XDG_RUNTIME_DIR`. The directory is scanned for any name in the form `wayland-$d` with - /// `0 <= $d < 32` and the first available one is used. - /// - /// 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(), - )) - } - } - - /// Add existing listening socket to this display - /// - /// Wayland clients will be able to connect to your compositor from this socket. - /// - /// The existing socket fd must already be created, opened, and locked. - /// The fd must be properly set to CLOEXEC and bound to a socket file - /// with both bind() and listen() already called. An error is returned - /// otherwise. - pub fn add_socket_from(&mut self, socket: T) -> IoResult<()> - where - T: IntoRawFd, - { - unsafe { self.add_socket_fd(socket.into_raw_fd()) } - } - - /// Add existing listening socket to this display from a raw file descriptor - /// - /// Wayland clients will be able to connect to your compositor from this socket. - /// - /// The library takes ownership of the provided socket if this method returns - /// successfully. - /// - /// The existing socket fd must already be created, opened, and locked. - /// The fd must be properly set to CLOEXEC and bound to a socket file - /// 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")) - } - } - - /// 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); - } - } -} - -fn get_runtime_dir() -> IoResult { - match env::var_os("XDG_RUNTIME_DIR") { - Some(s) => Ok(s.into()), - None => Err(IoError::new( - ErrorKind::NotFound, - "XDG_RUNTIME_DIR env variable is not set", - )), - } -} diff --git a/wayland-server/src/event_loop.rs b/wayland-server/src/event_loop.rs deleted file mode 100644 index 4d983b46f69..00000000000 --- a/wayland-server/src/event_loop.rs +++ /dev/null @@ -1,575 +0,0 @@ -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::*; - -type ResourceUserData = ( - *mut EventLoopHandle, - Option>, - Arc<(AtomicBool, AtomicPtr<()>)>, - Option, -); - -/// 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, -} - -/// 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 { - ptr: *mut wl_global, - data: *mut (GlobalCallback, *mut EventLoopHandle, ID), -} - -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); - } - } -} - -/// Handle to an event loop -/// -/// 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>, -} - -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 */ } - } - - 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::) - ); - } - RegisterStatus::Registered - } - - /// Stop looping - /// - /// 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; - } - - /// Get an handle to the internal state - /// - /// 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 - } - - /// 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 - /// 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, - )); - - let ret = unsafe { - ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_event_loop_add_fd, - self.ptr, - fd, - interest.bits(), - ::event_sources::event_source_fd_dispatcher::, - &*data as *const _ as *mut c_void - ) - }; - if ret.is_null() { - Err((IoError::last_os_error(), data.2)) - } else { - Ok(::event_sources::make_fd_event_source(ret, data)) - } - } - - /// Add a timer event source to this event loop - /// - /// 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 - /// this event loop. - pub fn add_timer_event_source(&mut self, implementation: ::event_sources::TimerEventSourceImpl, - idata: ID) - -> Result<::event_sources::TimerEventSource, (IoError, ID)> - where - ID: 'static, - { - let data = Box::new(( - implementation, - self as *const _ as *mut EventLoopHandle, - idata, - )); - - let ret = unsafe { - ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_event_loop_add_timer, - self.ptr, - ::event_sources::event_source_timer_dispatcher::, - &*data as *const _ as *mut c_void - ) - }; - if ret.is_null() { - Err((IoError::last_os_error(), data.2)) - } else { - Ok(::event_sources::make_timer_event_source(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 - /// 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)> - where - ID: 'static, - { - let data = Box::new(( - implementation, - self as *const _ as *mut EventLoopHandle, - idata, - )); - - let ret = unsafe { - ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_event_loop_add_signal, - self.ptr, - signal as c_int, - ::event_sources::event_source_signal_dispatcher::, - &*data as *const _ as *mut c_void - ) - }; - if ret.is_null() { - Err((IoError::last_os_error(), data.2)) - } else { - Ok(::event_sources::make_signal_event_source(ret, data)) - } - } - - /// Add an idle event source to this event loop - /// - /// This is a kind of "defer this computation for when there is nothing else to do". - /// - /// The provided implementation callback will be called when the event loop has finished - /// 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 - where - ID: 'static, - { - let data = Rc::new(RefCell::new(( - implementation, - self as *const _ as *mut EventLoopHandle, - idata, - false, - ))); - - let ret = unsafe { - ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_event_loop_add_idle, - self.ptr, - ::event_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), - } - } -} - -/// 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!( - 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 - } -} - -impl Drop for EventLoop { - 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); - } - } - } -} - -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/lib.rs b/wayland-server/src/lib.rs index ee23d41227c..e69de29bb2d 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -1,325 +0,0 @@ -//! Server-side Wayland connector -//! -//! # Overview -//! -//! Setting up the listening socket is done by the `create_display` -//! function, providing you a `Display` object and an `EventLoop`. -//! -//! 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. -//! -//! You then integrate the wayland event loop in your main event -//! loop to run your compositor. -//! -//! # Implementation and event loop -//! -//! 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. -//! -//! 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. -//! -//! 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. -//! -//! ## Example of implementation -//! -//! You can register your wayland objects to an event queue: -//! -//! ```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. -//! -//! Objects can be registered to event loop using the `&EventLoopHandle` -//! argument, available from withing an event callback. -//! -//! ## Globals definition -//! -//! 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: -//! -//! ```ignore -//! event_loop.register_global(version, callback, idata); -//! ``` -//! -//! 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?): -//! -//! ```ignore -//! event_loop.run(); -//! ``` -//! -//! Or you can integrate it with more control: -//! -//! ```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 -//! } -//! ``` -//! -//! # Protocols integration -//! -//! 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 the crate `wayland_scanner` and its documentation for -//! details about how to do so. - -#![warn(missing_docs)] - -#[macro_use] -extern crate bitflags; -extern crate libc; -extern crate nix; -extern crate token_store; -#[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; - -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}; -} - -/// Common routines for wayland resource objects. -/// -/// 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 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<(), ()>; -} - -/// 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), - } - } -} - -/// 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, -} - -mod generated { - #![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)] - #![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")); - } - - 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")); - } -} - -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-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 "] From 7daaa1d4c324231ea917222f21d72de0870de633 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sun, 25 Feb 2018 10:24:36 +0100 Subject: [PATCH 02/60] Message dispatching machinnery. --- wayland-client/Cargo.toml | 3 +- wayland-client/src/lib.rs | 9 + wayland-client/src/proxy.rs | 245 +++++++++++++++++++++ wayland-commons/Cargo.toml | 6 + wayland-commons/src/lib.rs | 25 +++ wayland-scanner/src/interface_gen.rs | 2 +- wayland-scanner/src/lib.rs | 4 +- wayland-server/Cargo.toml | 3 +- wayland-server/src/lib.rs | 11 + wayland-server/src/resource.rs | 308 +++++++++++++++++++++++++++ wayland-sys/src/common.rs | 12 +- 11 files changed, 621 insertions(+), 7 deletions(-) create mode 100644 wayland-client/src/proxy.rs create mode 100644 wayland-server/src/resource.rs diff --git a/wayland-client/Cargo.toml b/wayland-client/Cargo.toml index 078f8130f85..a705419bc23 100644 --- a/wayland-client/Cargo.toml +++ b/wayland-client/Cargo.toml @@ -16,13 +16,14 @@ travis-ci = { repository = "smithay/wayland-rs" } [dependencies] wayland-commons = { version = "0.20.0", path = "../wayland-commons" } wayland-sys = { version = "0.20.0", features = ["client"], path = "../wayland-sys", optional = true } +libc = "0.2" [build-dependencies] wayland-scanner = { version = "0.20.0", path = "../wayland-scanner" } [features] default = ["egl", "cursor", "native_lib"] -native_lib = [ "wayland-sys" ] +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/src/lib.rs b/wayland-client/src/lib.rs index e69de29bb2d..f10e9217814 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -0,0 +1,9 @@ +extern crate libc; + +extern crate wayland_commons; +#[cfg(feature = "native_lib")] +#[macro_use] +extern crate wayland_sys; + +mod proxy; +pub use proxy::{Proxy, NewProxy}; diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs new file mode 100644 index 00000000000..05f33e9a4de --- /dev/null +++ b/wayland-client/src/proxy.rs @@ -0,0 +1,245 @@ +use wayland_commons::{Interface, Implementation}; + +#[cfg(feature = "native_lib")] +use wayland_sys::client::wl_proxy; + +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()) + } + } +} + +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 +} + +impl Proxy { + // returns false is external + 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) + } + } + + #[cfg(feature = "native_lib")] + pub fn is_external(&self) -> bool { + self.internal.is_none() + } + + 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")] + pub fn c_ptr(&self) -> *mut wl_proxy { + self.ptr + } + + #[cfg(feature = "native_lib")] + pub unsafe fn from_c_ptr(ptr: *mut wl_proxy) -> Self { + use wayland_sys::client::*; + + 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 + } + } +} + +pub struct NewProxy { + _i: ::std::marker::PhantomData<*const I>, + #[cfg(feature = "native_lib")] + ptr: *mut wl_proxy +} + +impl NewProxy { + pub fn implement( + self, + idata: ID, + implementation: Implementation, I::Events, ID>, + ) -> Proxy + { + #[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(idata, implementation)); + let internal = new_user_data.internal.clone(); + + unsafe { + 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 + } + } + } + + #[cfg(feature = "native_lib")] + pub unsafe fn new_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::any::Any; + use std::sync::Arc; + use std::sync::atomic::Ordering; + use std::os::raw::{c_void, c_int}; + + use super::{Proxy, NewProxy}; + + use wayland_commons::{Interface, Implementation, MessageGroup}; + + pub(crate) struct ProxyUserData { + pub(crate) internal: Arc, + implem: Option>, + } + + impl ProxyUserData { + pub(crate) fn new( + idata: ID, + implem: Implementation, I::Events, ID> + ) -> ProxyUserData { + ProxyUserData { + internal: Arc::new(super::ProxyInternal::new()), + implem: Some(Box::new((implem, idata)) as Box) + } + } + } + + 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::Events::from_raw_c(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() + .downcast_mut::<(Implementation, I::Events, ID>, ID)>() + .unwrap(); + let &mut(ref implem_func, ref mut idata) = implem; + if must_destroy { + user_data.internal.alive.store(false, Ordering::Release); + } + // call the impl + implem_func(proxy_obj, msg, idata); + } + 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 index cbf29e7f85e..eb2024d2e9c 100644 --- a/wayland-commons/Cargo.toml +++ b/wayland-commons/Cargo.toml @@ -12,3 +12,9 @@ keywords = ["wayland"] [badges] travis-ci = { repository = "smithay/wayland-rs" } +[dependencies] +wayland-sys = { version = "0.20.0", features = ["client"], path = "../wayland-sys", optional = true } + +[features] +default = ["native_lib"] +native_lib = [ "wayland-sys" ] diff --git a/wayland-commons/src/lib.rs b/wayland-commons/src/lib.rs index e69de29bb2d..dccdd16ba68 100644 --- a/wayland-commons/src/lib.rs +++ b/wayland-commons/src/lib.rs @@ -0,0 +1,25 @@ +#[cfg(feature = "native_lib")] +extern crate wayland_sys; +#[cfg(feature = "native_lib")] +use wayland_sys::common as syscom; + +pub trait MessageGroup: Sized { + fn is_destructor(&self) -> bool; + #[cfg(feature = "native_lib")] + unsafe fn from_raw_c(opcode: u32, args: *const syscom::wl_argument) -> Result; + #[cfg(feature = "native_lib")] + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &[syscom::wl_argument]) -> T; +} + +pub trait Interface: 'static { + type Requests: MessageGroup + 'static; + type Events: MessageGroup + 'static; + + #[cfg(feature = "native_lib")] + const C_INTERFACE: *const ::syscom::wl_interface; + + fn name() -> &'static str; +} + +pub type Implementation = fn(Meta, M, &mut ID); + diff --git a/wayland-scanner/src/interface_gen.rs b/wayland-scanner/src/interface_gen.rs index a1a7f3d7a06..35d4e52347e 100644 --- a/wayland-scanner/src/interface_gen.rs +++ b/wayland-scanner/src/interface_gen.rs @@ -4,7 +4,7 @@ use protocol::*; use std::cmp; use std::io::Write; -pub fn generate_interfaces(protocol: Protocol, out: &mut O) { +pub fn generate_c_interfaces(protocol: Protocol, out: &mut O) { writeln!( out, "//\n// This file was auto-generated, do not edit directly\n//\n" diff --git a/wayland-scanner/src/lib.rs b/wayland-scanner/src/lib.rs index 9bb50decd55..fb0a0d11b01 100644 --- a/wayland-scanner/src/lib.rs +++ b/wayland-scanner/src/lib.rs @@ -143,7 +143,7 @@ pub fn generate_interfaces, P2: AsRef>(protocol: P1, targe .create(true) .open(target) .unwrap(); - interface_gen::generate_interfaces(protocol, &mut out); + //interface_gen::generate_interfaces(protocol, &mut out); } /// Generate the code for a protocol @@ -177,7 +177,7 @@ pub fn generate_code, P2: AsRef>(prot: P1, target: P2, sid /// - `target`: a `Write`-able object to which the generated code will be outputed to pub fn generate_interfaces_streams(protocol: P1, target: &mut P2) { let protocol = parse::parse_stream(protocol); - interface_gen::generate_interfaces(protocol, target); + //interface_gen::generate_interfaces(protocol, target); } /// Generate the code for a protocol from/to IO streams diff --git a/wayland-server/Cargo.toml b/wayland-server/Cargo.toml index 5de531b5213..673734c0527 100644 --- a/wayland-server/Cargo.toml +++ b/wayland-server/Cargo.toml @@ -16,11 +16,12 @@ travis-ci = { repository = "smithay/wayland-rs" } [dependencies] wayland-commons = { version = "0.20.0", path = "../wayland-commons" } wayland-sys = { version = "0.20.0", features = ["server"], path = "../wayland-sys", optional = true } +libc = "0.2" [build-dependencies] 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"] -native_lib = ["wayland-sys"] diff --git a/wayland-server/src/lib.rs b/wayland-server/src/lib.rs index e69de29bb2d..05b63030ff6 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -0,0 +1,11 @@ +extern crate libc; + +extern crate wayland_commons; +#[cfg(feature = "native_lib")] +#[macro_use] +extern crate wayland_sys; + +mod resource; +pub use resource::{Resource, NewResource}; + +struct Client; diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs new file mode 100644 index 00000000000..7b00e048749 --- /dev/null +++ b/wayland-server/src/resource.rs @@ -0,0 +1,308 @@ +use wayland_commons::{Interface, Implementation, MessageGroup}; + +#[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()) + } + } +} + +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 +} + +impl Resource { + pub fn send(&self, msg: I::Events) { + #[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 _ + ); + } + }); + } + } + + // returns false if external + 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) + } + } + + #[cfg(feature = "native_lib")] + pub fn is_external(&self) -> bool { + self.internal.is_none() + } + + pub fn clone(&self) -> Resource { + Resource { + _i: ::std::marker::PhantomData, + internal: self.internal.clone(), + #[cfg(feature = "native_lib")] + ptr: self.ptr + } + } + + #[cfg(feature = "native_lib")] + pub fn c_ptr(&self) -> *mut wl_resource { + self.ptr + } + + #[cfg(feature = "native_lib")] + pub unsafe fn from_c_ptr(ptr: *mut wl_resource) -> Self { + 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 + } + } +} + +pub struct NewResource { + _i: ::std::marker::PhantomData<*const I>, + #[cfg(feature = "native_lib")] + ptr: *mut wl_resource +} + +impl NewResource { + pub fn implement( + self, + idata: ID, + implementation: Implementation, I::Events, ID>, + destructor: Option, (), ID>> + ) -> Resource + { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + { + let new_user_data = Box::new( + self::native_machinery::ResourceUserData::new(idata, implementation, destructor) + ); + let internal = new_user_data.internal.clone(); + + unsafe { + 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")] + pub unsafe fn new_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::any::Any; + use std::sync::Arc; + use std::sync::atomic::Ordering; + use std::os::raw::{c_void, c_int}; + + use super::{Resource, NewResource}; + + use wayland_commons::{Interface, Implementation, MessageGroup}; + + pub(crate) struct ResourceUserData { + pub(crate) internal: Arc, + implem: Option>, + } + + impl ResourceUserData { + pub(crate) fn new( + idata: ID, + implem: Implementation, I::Events, ID>, + destructor: Option, (), ID>> + ) -> ResourceUserData { + ResourceUserData { + internal: Arc::new(super::ResourceInternal::new()), + implem: Some(Box::new((implem, idata, destructor)) as Box) + } + } + } + + 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::Requests::from_raw_c(opcode, args)?; + let must_destroy = msg.is_destructor(); + // create the proxy 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() + .downcast_mut::<(Implementation, I::Requests, ID>, ID, Option, (), ID>>)>() + .unwrap(); + let &mut(ref implem_func, ref mut idata, _) = implem; + if must_destroy { + user_data.internal.alive.store(false, Ordering::Release); + } + // call the impl + implem_func(resource_obj, msg, idata); + } + 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() + .downcast_mut::<(Implementation, I::Events, ID>, ID, Option, (), ID>>)>() + .unwrap(); + let &mut(_, ref mut idata, ref destructor) = implem; + if let &Some(dest_func) = destructor { + let resource_obj = super::Resource::::from_c_ptr(resource); + dest_func(resource_obj, (), idata); + } + }); + + if let Err(_) = ret { + eprintln!( + "[wayland-client error] A destructor for {} panicked.", + I::name() + ); + ::libc::abort() + } + } +} diff --git a/wayland-sys/src/common.rs b/wayland-sys/src/common.rs index df01871771f..81934bcb68e 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,8 +55,15 @@ 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 { + i: i32, + u: u32, + f: wl_fixed_t, + s: *const c_char, + o: *const c_void, + n: u32, + a: *const wl_array, + h: RawFd } pub type wl_dispatcher_func_t = unsafe extern "C" fn( From 9b7f3badc24075b8db2e5cfcfd53b4780d321ea9 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sun, 25 Feb 2018 11:07:11 +0100 Subject: [PATCH 03/60] Cleanup scanner structure --- wayland-client/build.rs | 10 +- wayland-protocols/Cargo.toml | 4 +- wayland-protocols/build.rs | 36 +++---- wayland-scanner/src/c_code_gen.rs | 14 +++ .../{interface_gen.rs => c_interface_gen.rs} | 93 ++++++++++--------- wayland-scanner/src/lib.rs | 23 +++-- wayland-server/build.rs | 10 +- 7 files changed, 110 insertions(+), 80 deletions(-) create mode 100644 wayland-scanner/src/c_code_gen.rs rename wayland-scanner/src/{interface_gen.rs => c_interface_gen.rs} (69%) diff --git a/wayland-client/build.rs b/wayland-client/build.rs index cb8b4b6be3a..887e093fbca 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,9 @@ 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-protocols/Cargo.toml b/wayland-protocols/Cargo.toml index ce138e505e5..f50c4567942 100644 --- a/wayland-protocols/Cargo.toml +++ b/wayland-protocols/Cargo.toml @@ -14,7 +14,7 @@ categories = ["gui", "api-bindings"] travis-ci = { repository = "smithay/wayland-rs" } [dependencies] -wayland-sys = { version = "0.20.0", path = "../wayland-sys" } +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" @@ -23,8 +23,10 @@ bitflags = "1.0" 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..82ee575eeef 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, + ); + } } } diff --git a/wayland-scanner/src/c_code_gen.rs b/wayland-scanner/src/c_code_gen.rs new file mode 100644 index 00000000000..3c78fed1042 --- /dev/null +++ b/wayland-scanner/src/c_code_gen.rs @@ -0,0 +1,14 @@ +use std::io::Result as IOResult; +use protocol::*; +use std::cmp; +use std::io::Write; + +pub(crate) fn write_protocol_client(protocol: Protocol, out: &mut O) -> IOResult<()> { + // TODO + Ok(()) +} + +pub(crate) fn write_protocol_server(protocol: Protocol, out: &mut O) -> IOResult<()> { + // TODO + Ok(()) +} diff --git a/wayland-scanner/src/interface_gen.rs b/wayland-scanner/src/c_interface_gen.rs similarity index 69% rename from wayland-scanner/src/interface_gen.rs rename to wayland-scanner/src/c_interface_gen.rs index 35d4e52347e..93497cf1e05 100644 --- a/wayland-scanner/src/interface_gen.rs +++ b/wayland-scanner/src/c_interface_gen.rs @@ -1,21 +1,23 @@ - - +use std::io::Result as IOResult; use protocol::*; use std::cmp; use std::io::Write; -pub fn generate_c_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 // @@ -40,17 +42,17 @@ pub fn generate_c_interfaces(protocol: Protocol, out: &mut O) { 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 +64,57 @@ pub fn generate_c_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 +123,34 @@ pub fn generate_c_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/lib.rs b/wayland-scanner/src/lib.rs index fb0a0d11b01..a0d84bc2f9e 100644 --- a/wayland-scanner/src/lib.rs +++ b/wayland-scanner/src/lib.rs @@ -111,7 +111,8 @@ mod util; mod parse; mod protocol; mod side; -mod interface_gen; +mod c_interface_gen; +mod c_code_gen; pub use side::Side; @@ -135,7 +136,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) @@ -143,7 +144,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); } /// Generate the code for a protocol @@ -156,7 +157,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) @@ -164,7 +165,10 @@ 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 @@ -177,7 +181,7 @@ pub fn generate_code, P2: AsRef>(prot: P1, target: P2, sid /// - `target`: a `Write`-able object to which the generated code will be outputed to pub fn generate_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); } /// Generate the code for a protocol from/to IO streams @@ -189,7 +193,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-server/build.rs b/wayland-server/build.rs index f9ae60c3854..aa78d4ef275 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,9 @@ 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")); + } } From ba63d1116c1268bfe322e62e428f648f043bad35 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sun, 25 Feb 2018 15:54:42 +0100 Subject: [PATCH 04/60] cargo fmt --- .rustfmt.toml | 9 -- tests/skel.rs | 3 +- wayland-client/build.rs | 6 +- wayland-client/src/proxy.rs | 99 ++++++++++------------ wayland-commons/src/lib.rs | 5 +- wayland-protocols/build.rs | 1 - wayland-scanner/src/parse.rs | 67 ++++++--------- wayland-scanner/src/protocol.rs | 10 +-- wayland-scanner/src/util.rs | 70 +++------------- wayland-server/build.rs | 6 +- wayland-server/src/resource.rs | 142 ++++++++++++++++---------------- wayland-sys/src/client.rs | 1 + wayland-sys/src/common.rs | 11 +-- wayland-sys/src/server.rs | 1 + 14 files changed, 177 insertions(+), 254 deletions(-) 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/tests/skel.rs b/tests/skel.rs index a93251b65da..2002ef17dd6 100644 --- a/tests/skel.rs +++ b/tests/skel.rs @@ -1,3 +1,2 @@ #[test] -fn it_works() { -} +fn it_works() {} diff --git a/wayland-client/build.rs b/wayland-client/build.rs index 887e093fbca..9e8e4b380f8 100644 --- a/wayland-client/build.rs +++ b/wayland-client/build.rs @@ -12,7 +12,11 @@ fn main() { 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_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/src/proxy.rs b/wayland-client/src/proxy.rs index 05f33e9a4de..d00f71c50ab 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -1,4 +1,4 @@ -use wayland_commons::{Interface, Implementation}; +use wayland_commons::{Implementation, Interface}; #[cfg(feature = "native_lib")] use wayland_sys::client::wl_proxy; @@ -8,31 +8,28 @@ use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; pub(crate) struct ProxyInternal { alive: AtomicBool, - user_data: AtomicPtr<()> + user_data: AtomicPtr<()>, } impl ProxyInternal { fn new() -> ProxyInternal { ProxyInternal { alive: AtomicBool::new(true), - user_data: AtomicPtr::new(::std::ptr::null_mut()) + user_data: AtomicPtr::new(::std::ptr::null_mut()), } } } 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 + _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, } impl Proxy { // returns false is external - pub fn is_alive(&self) -> bool { + pub fn is_alive(&self) -> bool { #[cfg(not(feature = "native_lib"))] { self.internal.alive.load(Ordering::Acquire) @@ -44,21 +41,21 @@ impl Proxy { .map(|i| i.alive.load(Ordering::Acquire)) .unwrap_or(false) } - } - + } + #[cfg(feature = "native_lib")] pub fn is_external(&self) -> bool { self.internal.is_none() } - pub fn clone(&self) -> Proxy { - Proxy { + pub fn clone(&self) -> Proxy { + Proxy { _i: ::std::marker::PhantomData, internal: self.internal.clone(), #[cfg(feature = "native_lib")] - ptr: self.ptr + ptr: self.ptr, } - } + } #[cfg(feature = "native_lib")] pub fn c_ptr(&self) -> *mut wl_proxy { @@ -70,18 +67,12 @@ impl Proxy { use wayland_sys::client::*; let is_managed = { - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_proxy_get_listener, - ptr - ) == &::wayland_sys::RUST_MANAGED as *const u8 as *const _ + 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; + 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 @@ -89,15 +80,14 @@ impl Proxy { Proxy { _i: ::std::marker::PhantomData, internal: internal, - ptr: ptr + ptr: ptr, } } } pub struct NewProxy { - _i: ::std::marker::PhantomData<*const I>, - #[cfg(feature = "native_lib")] - ptr: *mut wl_proxy + _i: ::std::marker::PhantomData<*const I>, + #[cfg(feature = "native_lib")] ptr: *mut wl_proxy, } impl NewProxy { @@ -105,8 +95,7 @@ impl NewProxy { self, idata: ID, implementation: Implementation, I::Events, ID>, - ) -> Proxy - { + ) -> Proxy { #[cfg(not(feature = "native_lib"))] { unimplemented!() @@ -115,7 +104,10 @@ impl NewProxy { { use wayland_sys::client::*; - let new_user_data = Box::new(self::native_machinery::ProxyUserData::new(idata, implementation)); + let new_user_data = Box::new(self::native_machinery::ProxyUserData::new( + idata, + implementation, + )); let internal = new_user_data.internal.clone(); unsafe { @@ -132,7 +124,7 @@ impl NewProxy { Proxy { _i: ::std::marker::PhantomData, internal: Some(internal), - ptr: self.ptr + ptr: self.ptr, } } } @@ -141,7 +133,7 @@ impl NewProxy { pub unsafe fn new_from_c_ptr(ptr: *mut wl_proxy) -> Self { NewProxy { _i: ::std::marker::PhantomData, - ptr: ptr + ptr: ptr, } } } @@ -150,15 +142,15 @@ impl NewProxy { mod native_machinery { use wayland_sys::common::*; use wayland_sys::client::*; - + use std::any::Any; use std::sync::Arc; use std::sync::atomic::Ordering; - use std::os::raw::{c_void, c_int}; + use std::os::raw::{c_int, c_void}; - use super::{Proxy, NewProxy}; + use super::{NewProxy, Proxy}; - use wayland_commons::{Interface, Implementation, MessageGroup}; + use wayland_commons::{Implementation, Interface, MessageGroup}; pub(crate) struct ProxyUserData { pub(crate) internal: Arc, @@ -168,11 +160,11 @@ mod native_machinery { impl ProxyUserData { pub(crate) fn new( idata: ID, - implem: Implementation, I::Events, ID> + implem: Implementation, I::Events, ID>, ) -> ProxyUserData { ProxyUserData { internal: Arc::new(super::ProxyInternal::new()), - implem: Some(Box::new((implem, idata)) as Box) + implem: Some(Box::new((implem, idata)) as Box), } } } @@ -182,7 +174,7 @@ mod native_machinery { proxy: *mut c_void, opcode: u32, msg: *const wl_message, - args: *const wl_argument + args: *const wl_argument, ) -> c_int where I: Interface, @@ -201,10 +193,13 @@ mod native_machinery { 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() - .downcast_mut::<(Implementation, I::Events, ID>, ID)>() - .unwrap(); - let &mut(ref implem_func, ref mut idata) = implem; + let implem = user_data + .implem + .as_mut() + .unwrap() + .downcast_mut::<(Implementation, I::Events, ID>, ID)>() + .unwrap(); + let &mut (ref implem_func, ref mut idata) = implem; if must_destroy { user_data.internal.alive.store(false, Ordering::Release); } @@ -214,11 +209,7 @@ mod native_machinery { if must_destroy { // final cleanup let _ = Box::from_raw(user_data as *mut ProxyUserData); - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_proxy_destroy, - proxy - ); + ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy, proxy); } Ok(()) }); @@ -232,14 +223,14 @@ mod native_machinery { I::name() ); ::libc::abort(); - }, + } Err(_) => { eprintln!( "[wayland-client error] A handler for {} panicked.", I::name() ); ::libc::abort() - } + } } } } diff --git a/wayland-commons/src/lib.rs b/wayland-commons/src/lib.rs index dccdd16ba68..4a9c4af2161 100644 --- a/wayland-commons/src/lib.rs +++ b/wayland-commons/src/lib.rs @@ -8,7 +8,9 @@ pub trait MessageGroup: Sized { #[cfg(feature = "native_lib")] unsafe fn from_raw_c(opcode: u32, args: *const syscom::wl_argument) -> Result; #[cfg(feature = "native_lib")] - fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &[syscom::wl_argument]) -> T; + fn as_raw_c_in(self, f: F) -> T + where + F: FnOnce(u32, &[syscom::wl_argument]) -> T; } pub trait Interface: 'static { @@ -22,4 +24,3 @@ pub trait Interface: 'static { } pub type Implementation = fn(Meta, M, &mut ID); - diff --git a/wayland-protocols/build.rs b/wayland-protocols/build.rs index 82ee575eeef..d683dc7e208 100644 --- a/wayland-protocols/build.rs +++ b/wayland-protocols/build.rs @@ -141,5 +141,4 @@ fn main() { ); } } - } 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..06338616cf5 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, @@ -58,8 +57,7 @@ impl Interface { if req.args.len() > 0 { return Err(format!( "Destructor request '{}.{}' cannot take arguments.", - self.name, - req.name + self.name, req.name )); } if found_destructor { @@ -98,9 +96,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/util.rs b/wayland-scanner/src/util.rs index fdabca71ecc..f49a5d204dd 100644 --- a/wayland-scanner/src/util.rs +++ b/wayland-scanner/src/util.rs @@ -2,58 +2,12 @@ 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,11 +17,13 @@ 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() diff --git a/wayland-server/build.rs b/wayland-server/build.rs index aa78d4ef275..1d0cf5c62f2 100644 --- a/wayland-server/build.rs +++ b/wayland-server/build.rs @@ -12,7 +12,11 @@ fn main() { 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_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/src/resource.rs b/wayland-server/src/resource.rs index 7b00e048749..6a12516e294 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -1,4 +1,4 @@ -use wayland_commons::{Interface, Implementation, MessageGroup}; +use wayland_commons::{Implementation, Interface, MessageGroup}; #[cfg(feature = "native_lib")] use wayland_sys::server::*; @@ -8,26 +8,23 @@ use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; pub(crate) struct ResourceInternal { alive: AtomicBool, - user_data: AtomicPtr<()> + user_data: AtomicPtr<()>, } impl ResourceInternal { fn new() -> ResourceInternal { ResourceInternal { alive: AtomicBool::new(true), - user_data: AtomicPtr::new(::std::ptr::null_mut()) + user_data: AtomicPtr::new(::std::ptr::null_mut()), } } } 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 + _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, } impl Resource { @@ -36,7 +33,7 @@ impl Resource { { if !self.internal.alive.load(Ordering::Acquire) { // don't send message to dead objects ! - return + return; } unimplemented!() } @@ -46,25 +43,23 @@ impl Resource { // object is managed if !internal.alive.load(Ordering::Acquire) { // don't send message to dead objects ! - return + 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 _ - ); - } + 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 _ + ); }); } } // returns false if external - pub fn is_alive(&self) -> bool { + pub fn is_alive(&self) -> bool { #[cfg(not(feature = "native_lib"))] { self.internal.alive.load(Ordering::Acquire) @@ -76,21 +71,21 @@ impl Resource { .map(|i| i.alive.load(Ordering::Acquire)) .unwrap_or(false) } - } - + } + #[cfg(feature = "native_lib")] pub fn is_external(&self) -> bool { self.internal.is_none() } - pub fn clone(&self) -> Resource { - Resource { + pub fn clone(&self) -> Resource { + Resource { _i: ::std::marker::PhantomData, internal: self.internal.clone(), #[cfg(feature = "native_lib")] - ptr: self.ptr + ptr: self.ptr, } - } + } #[cfg(feature = "native_lib")] pub fn c_ptr(&self) -> *mut wl_resource { @@ -109,11 +104,8 @@ impl Resource { ) != 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; + 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 @@ -121,15 +113,14 @@ impl Resource { Resource { _i: ::std::marker::PhantomData, internal: internal, - ptr: ptr + ptr: ptr, } } } pub struct NewResource { - _i: ::std::marker::PhantomData<*const I>, - #[cfg(feature = "native_lib")] - ptr: *mut wl_resource + _i: ::std::marker::PhantomData<*const I>, + #[cfg(feature = "native_lib")] ptr: *mut wl_resource, } impl NewResource { @@ -137,18 +128,19 @@ impl NewResource { self, idata: ID, implementation: Implementation, I::Events, ID>, - destructor: Option, (), ID>> - ) -> Resource - { + destructor: Option, (), ID>>, + ) -> Resource { #[cfg(not(feature = "native_lib"))] { unimplemented!() } #[cfg(feature = "native_lib")] { - let new_user_data = Box::new( - self::native_machinery::ResourceUserData::new(idata, implementation, destructor) - ); + let new_user_data = Box::new(self::native_machinery::ResourceUserData::new( + idata, + implementation, + destructor, + )); let internal = new_user_data.internal.clone(); unsafe { @@ -166,7 +158,7 @@ impl NewResource { Resource { _i: ::std::marker::PhantomData, internal: Some(internal), - ptr: self.ptr + ptr: self.ptr, } } } @@ -175,7 +167,7 @@ impl NewResource { pub unsafe fn new_from_c_ptr(ptr: *mut wl_resource) -> Self { NewResource { _i: ::std::marker::PhantomData, - ptr: ptr + ptr: ptr, } } } @@ -184,15 +176,15 @@ impl NewResource { mod native_machinery { use wayland_sys::common::*; use wayland_sys::server::*; - + use std::any::Any; use std::sync::Arc; use std::sync::atomic::Ordering; - use std::os::raw::{c_void, c_int}; + use std::os::raw::{c_int, c_void}; - use super::{Resource, NewResource}; + use super::{NewResource, Resource}; - use wayland_commons::{Interface, Implementation, MessageGroup}; + use wayland_commons::{Implementation, Interface, MessageGroup}; pub(crate) struct ResourceUserData { pub(crate) internal: Arc, @@ -203,11 +195,11 @@ mod native_machinery { pub(crate) fn new( idata: ID, implem: Implementation, I::Events, ID>, - destructor: Option, (), ID>> + destructor: Option, (), ID>>, ) -> ResourceUserData { ResourceUserData { internal: Arc::new(super::ResourceInternal::new()), - implem: Some(Box::new((implem, idata, destructor)) as Box) + implem: Some(Box::new((implem, idata, destructor)) as Box), } } } @@ -217,7 +209,7 @@ mod native_machinery { resource: *mut c_void, opcode: u32, msg: *const wl_message, - args: *const wl_argument + args: *const wl_argument, ) -> c_int where I: Interface, @@ -236,10 +228,17 @@ mod native_machinery { 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() - .downcast_mut::<(Implementation, I::Requests, ID>, ID, Option, (), ID>>)>() - .unwrap(); - let &mut(ref implem_func, ref mut idata, _) = implem; + let implem = user_data + .implem + .as_mut() + .unwrap() + .downcast_mut::<( + Implementation, I::Requests, ID>, + ID, + Option, (), ID>>, + )>() + .unwrap(); + let &mut (ref implem_func, ref mut idata, _) = implem; if must_destroy { user_data.internal.alive.store(false, Ordering::Release); } @@ -248,11 +247,7 @@ mod native_machinery { } if must_destroy { // final cleanup - ffi_dispatch!( - WAYLAND_SERVER_HANDLE, - wl_resource_destroy, - resource - ); + ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_resource_destroy, resource); } Ok(()) }); @@ -266,20 +261,18 @@ mod native_machinery { 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 - ) { + 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, @@ -287,10 +280,17 @@ mod native_machinery { 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() - .downcast_mut::<(Implementation, I::Events, ID>, ID, Option, (), ID>>)>() - .unwrap(); - let &mut(_, ref mut idata, ref destructor) = implem; + let implem = user_data + .implem + .as_mut() + .unwrap() + .downcast_mut::<( + Implementation, I::Events, ID>, + ID, + Option, (), ID>>, + )>() + .unwrap(); + let &mut (_, ref mut idata, ref destructor) = implem; if let &Some(dest_func) = destructor { let resource_obj = super::Resource::::from_c_ptr(resource); dest_func(resource_obj, (), idata); diff --git a/wayland-sys/src/client.rs b/wayland-sys/src/client.rs index c3a4cc63510..e406d57e4e2 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}; diff --git a/wayland-sys/src/common.rs b/wayland-sys/src/common.rs index 81934bcb68e..3ec8231be52 100644 --- a/wayland-sys/src/common.rs +++ b/wayland-sys/src/common.rs @@ -63,14 +63,9 @@ pub union wl_argument { o: *const c_void, n: u32, a: *const wl_array, - h: RawFd + 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..9fc05d2cdd0 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}; From 6a7e7f0c0c618f30cb2fd433bc20d24592d153bd Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sun, 25 Feb 2018 15:55:03 +0100 Subject: [PATCH 05/60] Bases of codegen & enums --- wayland-client/Cargo.toml | 1 + wayland-client/src/lib.rs | 19 ++- wayland-scanner/src/c_code_gen.rs | 32 ++++- wayland-scanner/src/c_interface_gen.rs | 23 ++-- wayland-scanner/src/common_gen.rs | 173 +++++++++++++++++++++++++ wayland-scanner/src/lib.rs | 17 +-- wayland-server/Cargo.toml | 1 + wayland-server/src/lib.rs | 19 ++- 8 files changed, 256 insertions(+), 29 deletions(-) create mode 100644 wayland-scanner/src/common_gen.rs diff --git a/wayland-client/Cargo.toml b/wayland-client/Cargo.toml index a705419bc23..9f96f1e5281 100644 --- a/wayland-client/Cargo.toml +++ b/wayland-client/Cargo.toml @@ -16,6 +16,7 @@ travis-ci = { repository = "smithay/wayland-rs" } [dependencies] 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" libc = "0.2" [build-dependencies] diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index f10e9217814..cd69cae5d6a 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -1,3 +1,5 @@ +#[macro_use] +extern crate bitflags; extern crate libc; extern crate wayland_commons; @@ -6,4 +8,19 @@ extern crate wayland_commons; extern crate wayland_sys; mod proxy; -pub use proxy::{Proxy, NewProxy}; +pub use proxy::{NewProxy, Proxy}; + +mod generated { + #![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)] + #![allow(non_upper_case_globals, non_snake_case, unused_imports)] + #![allow(missing_docs)] + + #[cfg(feature = "native_lib")] + mod c_interfaces { + include!(concat!(env!("OUT_DIR"), "/wayland_c_interfaces.rs")); + } + #[cfg(feature = "native_lib")] + mod c_api { + include!(concat!(env!("OUT_DIR"), "/wayland_c_api.rs")); + } +} diff --git a/wayland-scanner/src/c_code_gen.rs b/wayland-scanner/src/c_code_gen.rs index 3c78fed1042..fb5684f5607 100644 --- a/wayland-scanner/src/c_code_gen.rs +++ b/wayland-scanner/src/c_code_gen.rs @@ -1,14 +1,40 @@ use std::io::Result as IOResult; -use protocol::*; use std::cmp; use std::io::Write; +use common_gen::*; +use protocol::*; +use util::*; + pub(crate) fn write_protocol_client(protocol: Protocol, out: &mut O) -> IOResult<()> { - // TODO + write_prefix(&protocol, out)?; + + for iface in &protocol.interfaces { + writeln!(out, "pub mod {} {{", iface.name)?; + + write_enums(&iface.enums, out)?; + + writeln!(out, "}}\n")?; + } + Ok(()) } pub(crate) fn write_protocol_server(protocol: Protocol, out: &mut O) -> IOResult<()> { - // TODO + 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)?; + + write_enums(&iface.enums, out)?; + + writeln!(out, "}}\n")?; + } + Ok(()) } diff --git a/wayland-scanner/src/c_interface_gen.rs b/wayland-scanner/src/c_interface_gen.rs index 93497cf1e05..02c8c4c005e 100644 --- a/wayland-scanner/src/c_interface_gen.rs +++ b/wayland-scanner/src/c_interface_gen.rs @@ -13,8 +13,9 @@ pub(crate) fn generate_interfaces(protocol: Protocol, out: &mut O) -> writeln!(out, "/*\n{}\n*/\n", text)?; } - writeln!(out, - r#" + writeln!( + out, + r#" use std::os::raw::{{c_char, c_void}}; use wayland_sys::common::*;"# )?; @@ -23,22 +24,20 @@ use wayland_sys::common::*;"# // 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)) }); diff --git a/wayland-scanner/src/common_gen.rs b/wayland-scanner/src/common_gen.rs new file mode 100644 index 00000000000..1fb6a617526 --- /dev/null +++ b/wayland-scanner/src/common_gen.rs @@ -0,0 +1,173 @@ +use std::io::Result as IOResult; +use std::cmp; +use std::io::Write; + +use protocol::*; +use util::*; + +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_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 a0d84bc2f9e..95c34f109d8 100644 --- a/wayland-scanner/src/lib.rs +++ b/wayland-scanner/src/lib.rs @@ -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,6 +103,7 @@ mod util; mod parse; mod protocol; mod side; +mod common_gen; mod c_interface_gen; mod c_code_gen; diff --git a/wayland-server/Cargo.toml b/wayland-server/Cargo.toml index 673734c0527..477c085d9a2 100644 --- a/wayland-server/Cargo.toml +++ b/wayland-server/Cargo.toml @@ -16,6 +16,7 @@ travis-ci = { repository = "smithay/wayland-rs" } [dependencies] 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" libc = "0.2" [build-dependencies] diff --git a/wayland-server/src/lib.rs b/wayland-server/src/lib.rs index 05b63030ff6..8c9e2ad6d9c 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -1,3 +1,5 @@ +#[macro_use] +extern crate bitflags; extern crate libc; extern crate wayland_commons; @@ -6,6 +8,21 @@ extern crate wayland_commons; extern crate wayland_sys; mod resource; -pub use resource::{Resource, NewResource}; +pub use resource::{NewResource, Resource}; struct Client; + +mod generated { + #![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)] + #![allow(non_upper_case_globals, non_snake_case, unused_imports)] + #![allow(missing_docs)] + + #[cfg(feature = "native_lib")] + mod c_interfaces { + include!(concat!(env!("OUT_DIR"), "/wayland_c_interfaces.rs")); + } + #[cfg(feature = "native_lib")] + mod c_api { + include!(concat!(env!("OUT_DIR"), "/wayland_c_api.rs")); + } +} From 1747d0407683571e657666066aebc5cff400968e Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sun, 25 Feb 2018 15:58:53 +0100 Subject: [PATCH 06/60] Update travis for rustfmt-preview --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6569bc59a3f..c14dc1e6d45 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,7 @@ before_script: - export XDG_RUNTIME_DIR="$(pwd)/socket" - | if [ $TRAVIS_RUST_VERSION = "nightly" ]; then - cargo install rustfmt-nightly --force + rustup component add rustfmt-preview fi - which cargo-tarpaulin || cargo install cargo-tarpaulin - which cargo-install-update || cargo install cargo-update From fb93562f5b5fb661ae770b0103c8152e2d568ffb Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 26 Feb 2018 11:25:13 +0100 Subject: [PATCH 07/60] scanner: message ser/deser machinnery --- wayland-client/src/lib.rs | 7 +- wayland-client/src/proxy.rs | 11 +- wayland-commons/src/lib.rs | 45 +++- wayland-scanner/src/c_code_gen.rs | 362 ++++++++++++++++++++++++++++++ wayland-scanner/src/common_gen.rs | 76 +++++++ wayland-scanner/src/side.rs | 23 +- wayland-server/src/lib.rs | 10 +- wayland-server/src/resource.rs | 15 +- wayland-sys/src/common.rs | 16 +- 9 files changed, 515 insertions(+), 50 deletions(-) diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index cd69cae5d6a..6c25358fead 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -9,6 +9,8 @@ extern crate wayland_sys; mod proxy; pub use proxy::{NewProxy, Proxy}; +#[cfg(feature = "native_lib")] +pub use generated::c_api as protocol; mod generated { #![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)] @@ -20,7 +22,10 @@ mod generated { include!(concat!(env!("OUT_DIR"), "/wayland_c_interfaces.rs")); } #[cfg(feature = "native_lib")] - mod c_api { + 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")); } } diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs index d00f71c50ab..5dd41acb71b 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -130,7 +130,7 @@ impl NewProxy { } #[cfg(feature = "native_lib")] - pub unsafe fn new_from_c_ptr(ptr: *mut wl_proxy) -> Self { + pub unsafe fn from_c_ptr(ptr: *mut wl_proxy) -> Self { NewProxy { _i: ::std::marker::PhantomData, ptr: ptr, @@ -185,7 +185,7 @@ mod native_machinery { // 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::Events::from_raw_c(opcode, args)?; + let msg = I::Events::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); @@ -220,15 +220,12 @@ mod native_machinery { eprintln!( "[wayland-client error] Attempted to dispatch unknown opcode {} for {}, aborting.", opcode, - I::name() + I::NAME ); ::libc::abort(); } Err(_) => { - eprintln!( - "[wayland-client error] A handler for {} panicked.", - I::name() - ); + eprintln!("[wayland-client error] A handler for {} panicked.", I::NAME); ::libc::abort() } } diff --git a/wayland-commons/src/lib.rs b/wayland-commons/src/lib.rs index 4a9c4af2161..ebbc9ee9a63 100644 --- a/wayland-commons/src/lib.rs +++ b/wayland-commons/src/lib.rs @@ -3,10 +3,13 @@ extern crate wayland_sys; #[cfg(feature = "native_lib")] use wayland_sys::common as syscom; +use std::os::raw::c_void; + pub trait MessageGroup: Sized { fn is_destructor(&self) -> bool; #[cfg(feature = "native_lib")] - unsafe fn from_raw_c(opcode: u32, args: *const syscom::wl_argument) -> Result; + unsafe fn from_raw_c(obj: *mut c_void, opcode: u32, args: *const syscom::wl_argument) + -> Result; #[cfg(feature = "native_lib")] fn as_raw_c_in(self, f: F) -> T where @@ -17,10 +20,44 @@ pub trait Interface: 'static { type Requests: MessageGroup + 'static; type Events: MessageGroup + 'static; - #[cfg(feature = "native_lib")] - const C_INTERFACE: *const ::syscom::wl_interface; + const NAME: &'static str; - fn name() -> &'static str; + #[cfg(feature = "native_lib")] + fn c_interface() -> *const ::syscom::wl_interface; } pub type Implementation = fn(Meta, M, &mut ID); + +pub struct AnonymousObject; + +pub enum NoMessage { +} + +impl Interface for AnonymousObject { + type Requests = NoMessage; + type Events = 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, &[syscom::wl_argument]) -> T, + { + match self {} + } +} diff --git a/wayland-scanner/src/c_code_gen.rs b/wayland-scanner/src/c_code_gen.rs index fb5684f5607..4d0b934979b 100644 --- a/wayland-scanner/src/c_code_gen.rs +++ b/wayland-scanner/src/c_code_gen.rs @@ -5,6 +5,7 @@ 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)?; @@ -12,7 +13,42 @@ pub(crate) fn write_protocol_client(protocol: Protocol, out: &mut O) - 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}};\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("Requests", Side::Client, false, &iface.requests, out)?; + write_messagegroup_impl( + "Requests", + &iface_name, + Side::Client, + false, + &iface.requests, + out, + )?; + write_messagegroup("Events", Side::Client, true, &iface.events, out)?; + write_messagegroup_impl( + "Events", + &iface_name, + Side::Client, + true, + &iface.events, + out, + )?; + write_interface(&iface_name, &iface.name, out)?; writeln!(out, "}}\n")?; } @@ -31,10 +67,336 @@ pub(crate) fn write_protocol_server(protocol: Protocol, out: &mut O) - 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}};\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("Requests", Side::Server, true, &iface.requests, out)?; + write_messagegroup_impl( + "Requests", + &iface_name, + Side::Server, + true, + &iface.requests, + out, + )?; + write_messagegroup("Events", Side::Server, false, &iface.events, out)?; + write_messagegroup_impl( + "Events", + &iface_name, + Side::Server, + false, + &iface.events, + out, + )?; + write_interface(&iface_name, &iface.name, out)?; writeln!(out, "}}\n")?; } Ok(()) } + +pub fn write_messagegroup_impl( + name: &str, + iface: &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 {{")?; + 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,")?; + } + } + 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 => write!(out, "_args[{}].u", j)?, + Type::Int => 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, &[wl_argument]) -> T {{" + )?; + if receiver || side == Side::Client { + 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 => writeln!(out, "_args_array[{}].u = {};", j, a.name)?, + Type::Int => 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 let Some(ref iface) = a.interface { + // serialize like a regular 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)?; + } + } else { + // we don't serialize this sepcial case, its only the registry (at + // least for now) + panic!("Outgoing untyped newproxies are not supported in events."); + } + } + Type::Destructor => panic!("An argument cannot have type \"destructor\"."), + } + + j += 1; + } + writeln!(out, " f({}, &_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 Requests = Requests; + type Events = Events; + 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(()) +} diff --git a/wayland-scanner/src/common_gen.rs b/wayland-scanner/src/common_gen.rs index 1fb6a617526..142a8731ffa 100644 --- a/wayland-scanner/src/common_gen.rs +++ b/wayland-scanner/src/common_gen.rs @@ -4,6 +4,7 @@ use std::io::Write; use protocol::*; use util::*; +use Side; pub(crate) fn write_prefix(protocol: &Protocol, out: &mut O) -> IOResult<()> { writeln!( @@ -20,6 +21,81 @@ pub(crate) fn write_prefix(protocol: &Protocol, out: &mut O) -> IOResu 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)?; + } + 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<")?; + } + 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 { diff --git a/wayland-scanner/src/side.rs b/wayland-scanner/src/side.rs index 6a369e34420..6aca4a3e415 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,25 @@ pub enum Side { Server, } -#[doc(hidden)] impl Side { - pub fn object_ptr_type(&self) -> &'static str { + pub(crate) 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 { + pub(crate) 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-server/src/lib.rs b/wayland-server/src/lib.rs index 8c9e2ad6d9c..6e095f5acb7 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -12,17 +12,23 @@ pub use resource::{NewResource, Resource}; struct Client; +#[cfg(feature = "native_lib")] +pub use generated::c_api as protocol; + mod generated { #![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)] #![allow(non_upper_case_globals, non_snake_case, unused_imports)] #![allow(missing_docs)] #[cfg(feature = "native_lib")] - mod c_interfaces { + pub mod c_interfaces { include!(concat!(env!("OUT_DIR"), "/wayland_c_interfaces.rs")); } #[cfg(feature = "native_lib")] - mod c_api { + 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")); } } diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index 6a12516e294..fc5627be9ee 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -99,7 +99,7 @@ impl Resource { WAYLAND_SERVER_HANDLE, wl_resource_instance_of, ptr, - I::C_INTERFACE, + I::c_interface(), &::wayland_sys::RUST_MANAGED as *const u8 as *const _ ) != 0 }; @@ -164,7 +164,7 @@ impl NewResource { } #[cfg(feature = "native_lib")] - pub unsafe fn new_from_c_ptr(ptr: *mut wl_resource) -> Self { + pub unsafe fn from_c_ptr(ptr: *mut wl_resource) -> Self { NewResource { _i: ::std::marker::PhantomData, ptr: ptr, @@ -220,7 +220,7 @@ mod native_machinery { // 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::Requests::from_raw_c(opcode, args)?; + let msg = I::Requests::from_raw_c(resource as *mut _, opcode, args)?; let must_destroy = msg.is_destructor(); // create the proxy object let resource_obj = super::Resource::::from_c_ptr(resource); @@ -258,15 +258,12 @@ mod native_machinery { eprintln!( "[wayland-client error] Attempted to dispatch unknown opcode {} for {}, aborting.", opcode, - I::name() + I::NAME ); ::libc::abort(); } Err(_) => { - eprintln!( - "[wayland-client error] A handler for {} panicked.", - I::name() - ); + eprintln!("[wayland-client error] A handler for {} panicked.", I::NAME); ::libc::abort() } } @@ -300,7 +297,7 @@ mod native_machinery { if let Err(_) = ret { eprintln!( "[wayland-client error] A destructor for {} panicked.", - I::name() + I::NAME ); ::libc::abort() } diff --git a/wayland-sys/src/common.rs b/wayland-sys/src/common.rs index 3ec8231be52..7681d9baf0a 100644 --- a/wayland-sys/src/common.rs +++ b/wayland-sys/src/common.rs @@ -56,14 +56,14 @@ pub fn wl_fixed_from_int(i: i32) -> wl_fixed_t { // can contain i32, u32 and pointers #[repr(C)] pub union wl_argument { - i: i32, - u: u32, - f: wl_fixed_t, - s: *const c_char, - o: *const c_void, - n: u32, - a: *const wl_array, - h: RawFd, + 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 = From fc6de467146bb90f7b1ae35b4f0eac19ae27196c Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 26 Feb 2018 14:28:23 +0100 Subject: [PATCH 08/60] scanner: extension traits for client proxies --- wayland-client/src/proxy.rs | 10 + wayland-commons/src/lib.rs | 4 +- wayland-scanner/src/c_code_gen.rs | 302 ++++++++++++++++++++++++++++-- wayland-scanner/src/common_gen.rs | 95 ++++++---- wayland-sys/src/client.rs | 4 +- 5 files changed, 356 insertions(+), 59 deletions(-) diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs index 5dd41acb71b..cb7d25173dd 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -83,6 +83,16 @@ impl Proxy { ptr: ptr, } } + + #[doc(hidden)] + #[cfg(feature = "native_lib")] + pub unsafe fn new_null() -> Proxy { + Proxy { + _i: ::std::marker::PhantomData, + internal: None, + ptr: ::std::ptr::null_mut(), + } + } } pub struct NewProxy { diff --git a/wayland-commons/src/lib.rs b/wayland-commons/src/lib.rs index ebbc9ee9a63..f2f13628277 100644 --- a/wayland-commons/src/lib.rs +++ b/wayland-commons/src/lib.rs @@ -13,7 +13,7 @@ pub trait MessageGroup: Sized { #[cfg(feature = "native_lib")] fn as_raw_c_in(self, f: F) -> T where - F: FnOnce(u32, &[syscom::wl_argument]) -> T; + F: FnOnce(u32, &mut [syscom::wl_argument]) -> T; } pub trait Interface: 'static { @@ -56,7 +56,7 @@ impl MessageGroup for NoMessage { } fn as_raw_c_in(self, f: F) -> T where - F: FnOnce(u32, &[syscom::wl_argument]) -> T, + F: FnOnce(u32, &mut [syscom::wl_argument]) -> T, { match self {} } diff --git a/wayland-scanner/src/c_code_gen.rs b/wayland-scanner/src/c_code_gen.rs index 4d0b934979b..6f73192c1ee 100644 --- a/wayland-scanner/src/c_code_gen.rs +++ b/wayland-scanner/src/c_code_gen.rs @@ -19,7 +19,7 @@ pub(crate) fn write_protocol_client(protocol: Protocol, out: &mut O) - writeln!( out, - " use super::{{Proxy, NewProxy, AnonymousObject, Interface}};\n" + " use super::{{Proxy, NewProxy, AnonymousObject, Interface, MessageGroup}};\n" )?; writeln!( out, @@ -49,6 +49,7 @@ pub(crate) fn write_protocol_client(protocol: Protocol, out: &mut O) - out, )?; write_interface(&iface_name, &iface.name, out)?; + write_client_methods(&iface_name, &iface.requests, out)?; writeln!(out, "}}\n")?; } @@ -73,7 +74,7 @@ pub(crate) fn write_protocol_server(protocol: Protocol, out: &mut O) - writeln!( out, - " use super::{{Resource, NewResource, AnonymousObject, Interface}};\n" + " use super::{{Resource, NewResource, AnonymousObject, Interface, MessageGroup}};\n" )?; writeln!( out, @@ -172,8 +173,26 @@ pub fn write_messagegroup_impl( for a in &msg.args { write!(out, " {}: ", a.name)?; match a.typ { - Type::Uint => write!(out, "_args[{}].u", j)?, - Type::Int => write!(out, "_args[{}].i", j)?, + 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 { @@ -273,9 +292,9 @@ pub fn write_messagegroup_impl( // as_raw_c_in writeln!( out, - " fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &[wl_argument]) -> T {{" + " fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T {{" )?; - if receiver || side == Side::Client { + if receiver { writeln!( out, " panic!(\"{}::as_raw_c_in can not be used {:?}-side.\")", @@ -309,8 +328,16 @@ pub fn write_messagegroup_impl( for a in &msg.args { write!(out, " ")?; match a.typ { - Type::Uint => writeln!(out, "_args_array[{}].u = {};", j, a.name)?, - Type::Int => writeln!(out, "_args_array[{}].i = {};", j, a.name)?, + 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 { @@ -352,24 +379,41 @@ pub fn write_messagegroup_impl( } Type::NewId => { if let Some(ref iface) = a.interface { - // serialize like a regular 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)?; + if side == Side::Server { + // serialize like a regular 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)?; + } } else { - writeln!(out, "_args_array[{}].o = {}.c_ptr() as *mut _;", j, a.name)?; + // this must be a NULL, ignore the dummy object received + writeln!(out, "_args_array[{}].o = ::std::ptr::null_mut();", j)?; } } else { - // we don't serialize this sepcial case, its only the registry (at - // least for now) - panic!("Outgoing untyped newproxies are not supported in events."); + 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({}, &_args_array)", i)?; + writeln!(out, " f({}, &mut _args_array)", i)?; writeln!(out, " }},")?; } writeln!(out, " }}")?; @@ -400,3 +444,227 @@ fn write_interface(name: &str, low_name: &str, out: &mut O) -> IOResul )?; 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, " }}")?; + // actually send the stuff + write!( + out, + " let msg = Requests::{}", + snake_to_camel(&msg.name) + )?; + if msg.args.len() > 0 { + write!(out, " {{ ")?; + for a in &msg.args { + if a.typ == Type::NewId { + if let Some(ref iface) = a.interface { + write!( + out, + "{}: unsafe {{ Proxy::::new_null() }}, ", + a.name, + iface, + snake_to_camel(&iface) + )?; + } else { + write!(out, "{}: (T::NAME.into(), version, unsafe {{ Proxy::::new_null() }}),", a.name)?; + } + } else if a.typ == Type::Object { + if a.allow_null { + write!(out, "{0} : {0}.map(|o| o.clone()), ", a.name)?; + } else { + write!(out, "{0}: {0}.clone(), ", a.name)?; + } + } else { + write!(out, "{}, ", a.name)?; + } + } + write!(out, " }}")?; + } + writeln!(out, ";")?; + if let Some(ret_type) = return_type { + if let Some(ref iface) = ret_type.interface { + writeln!( + out, + r#" + unsafe {{ + let ret = msg.as_raw_c_in(|opcode, args| {{ + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_proxy_marshal_array_constructor, + self.c_ptr(), + opcode, + args.as_mut_ptr(), + super::{0}::{1}::c_interface() + ) + }}); + Ok(NewProxy::::from_c_ptr(ret)) + }}"#, + iface, + snake_to_camel(iface) + )?; + } else { + 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)) + }}"# + )?; + } + } else { + writeln!( + out, + r#" + unsafe {{ + msg.as_raw_c_in(|opcode, args| {{ + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_proxy_marshal_array, + self.c_ptr(), + opcode, + args.as_mut_ptr() + ); + }}); + }}"# + )?; + } + writeln!(out, " }}")?; + } + 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/common_gen.rs b/wayland-scanner/src/common_gen.rs index 142a8731ffa..76244b51e87 100644 --- a/wayland-scanner/src/common_gen.rs +++ b/wayland-scanner/src/common_gen.rs @@ -33,6 +33,21 @@ pub(crate) fn write_messagegroup( 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, " {{")?; @@ -41,47 +56,51 @@ pub(crate) fn write_messagegroup( if a.allow_null { write!(out, "Option<")?; } - 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())?; + 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::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\"."), } - Type::Destructor => panic!("An argument cannot have type \"destructor\"."), } if a.allow_null { write!(out, ">")?; diff --git a/wayland-sys/src/client.rs b/wayland-sys/src/client.rs index e406d57e4e2..84f18cbf3fe 100644 --- a/wayland-sys/src/client.rs +++ b/wayland-sys/src/client.rs @@ -45,8 +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) -> (), From 32b5edbd82f9586771270acd9c8a66ba7bf2a108 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 26 Feb 2018 15:07:18 +0100 Subject: [PATCH 09/60] cleanup & protocol_macro --- wayland-client/src/lib.rs | 7 +++- wayland-client/src/proxy.rs | 4 +- wayland-commons/Cargo.toml | 2 +- wayland-commons/src/lib.rs | 8 ++-- wayland-protocols/Cargo.toml | 1 + wayland-protocols/src/lib.rs | 2 + wayland-protocols/src/protocol_macro.rs | 47 ++++++++++++++++++++++- wayland-scanner/src/c_code_gen.rs | 51 +++++++------------------ wayland-scanner/src/common_gen.rs | 1 - wayland-scanner/src/lib.rs | 4 +- wayland-scanner/src/protocol.rs | 33 ---------------- wayland-scanner/src/side.rs | 14 ------- wayland-scanner/src/util.rs | 6 --- wayland-server/src/lib.rs | 5 +++ wayland-server/src/resource.rs | 4 +- 15 files changed, 84 insertions(+), 105 deletions(-) diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index 6c25358fead..6b51f3acc98 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -12,13 +12,18 @@ pub use proxy::{NewProxy, Proxy}; #[cfg(feature = "native_lib")] pub use generated::c_api as protocol; +#[cfg(feature = "native_lib")] +pub mod sys { + pub use super::generated::c_interfaces as protocol_interfaces; +} + mod generated { #![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)] #![allow(non_upper_case_globals, non_snake_case, unused_imports)] #![allow(missing_docs)] #[cfg(feature = "native_lib")] - mod c_interfaces { + pub mod c_interfaces { include!(concat!(env!("OUT_DIR"), "/wayland_c_interfaces.rs")); } #[cfg(feature = "native_lib")] diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs index cb7d25173dd..08630665ff2 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -158,7 +158,7 @@ mod native_machinery { use std::sync::atomic::Ordering; use std::os::raw::{c_int, c_void}; - use super::{NewProxy, Proxy}; + use super::Proxy; use wayland_commons::{Implementation, Interface, MessageGroup}; @@ -183,7 +183,7 @@ mod native_machinery { _implem: *const c_void, proxy: *mut c_void, opcode: u32, - msg: *const wl_message, + _msg: *const wl_message, args: *const wl_argument, ) -> c_int where diff --git a/wayland-commons/Cargo.toml b/wayland-commons/Cargo.toml index eb2024d2e9c..eddab49724e 100644 --- a/wayland-commons/Cargo.toml +++ b/wayland-commons/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["wayland"] travis-ci = { repository = "smithay/wayland-rs" } [dependencies] -wayland-sys = { version = "0.20.0", features = ["client"], path = "../wayland-sys", optional = true } +wayland-sys = { version = "0.20.0", path = "../wayland-sys", optional = true } [features] default = ["native_lib"] diff --git a/wayland-commons/src/lib.rs b/wayland-commons/src/lib.rs index f2f13628277..a91d9535240 100644 --- a/wayland-commons/src/lib.rs +++ b/wayland-commons/src/lib.rs @@ -48,13 +48,13 @@ impl MessageGroup for NoMessage { match *self {} } unsafe fn from_raw_c( - obj: *mut c_void, - opcode: u32, - args: *const syscom::wl_argument, + _obj: *mut c_void, + _opcode: u32, + _args: *const syscom::wl_argument, ) -> Result { Err(()) } - fn as_raw_c_in(self, f: F) -> T + fn as_raw_c_in(self, _f: F) -> T where F: FnOnce(u32, &mut [syscom::wl_argument]) -> T, { diff --git a/wayland-protocols/Cargo.toml b/wayland-protocols/Cargo.toml index f50c4567942..90b3710f8f4 100644 --- a/wayland-protocols/Cargo.toml +++ b/wayland-protocols/Cargo.toml @@ -14,6 +14,7 @@ categories = ["gui", "api-bindings"] travis-ci = { repository = "smithay/wayland-rs" } [dependencies] +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 } 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 990f6082aec..0346a0b8d12 100644 --- a/wayland-protocols/src/protocol_macro.rs +++ b/wayland-protocols/src/protocol_macro.rs @@ -1,7 +1,52 @@ #[macro_escape] macro_rules! wayland_protocol( ($name: expr, [$(($import: ident, $interface: ident)),*]) => { - // TODO + #[cfg(all(feature = "client", feature="native_lib"))] + pub use self::generated::client::c_api as client; + + #[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)] + #![allow(missing_docs)] + + #[cfg(feature = "client")] + pub mod client { + 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 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, "_c_client_api.rs")); + } + } + + #[cfg(feature = "server")] + pub mod server { + 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 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, "_c_server_api.rs")); + } + } + } } ); diff --git a/wayland-scanner/src/c_code_gen.rs b/wayland-scanner/src/c_code_gen.rs index 6f73192c1ee..56e9cf9b9f1 100644 --- a/wayland-scanner/src/c_code_gen.rs +++ b/wayland-scanner/src/c_code_gen.rs @@ -1,5 +1,4 @@ use std::io::Result as IOResult; -use std::cmp; use std::io::Write; use common_gen::*; @@ -25,29 +24,15 @@ pub(crate) fn write_protocol_client(protocol: Protocol, out: &mut O) - out, " use super::sys::common::{{wl_argument, wl_interface, wl_array}};" )?; - writeln!(out, " use super::sys::client::*;"); + writeln!(out, " use super::sys::client::*;")?; let iface_name = snake_to_camel(&iface.name); write_enums(&iface.enums, out)?; write_messagegroup("Requests", Side::Client, false, &iface.requests, out)?; - write_messagegroup_impl( - "Requests", - &iface_name, - Side::Client, - false, - &iface.requests, - out, - )?; + write_messagegroup_impl("Requests", Side::Client, false, &iface.requests, out)?; write_messagegroup("Events", Side::Client, true, &iface.events, out)?; - write_messagegroup_impl( - "Events", - &iface_name, - Side::Client, - true, - &iface.events, - out, - )?; + write_messagegroup_impl("Events", Side::Client, true, &iface.events, out)?; write_interface(&iface_name, &iface.name, out)?; write_client_methods(&iface_name, &iface.requests, out)?; @@ -86,23 +71,9 @@ pub(crate) fn write_protocol_server(protocol: Protocol, out: &mut O) - write_enums(&iface.enums, out)?; write_messagegroup("Requests", Side::Server, true, &iface.requests, out)?; - write_messagegroup_impl( - "Requests", - &iface_name, - Side::Server, - true, - &iface.requests, - out, - )?; + write_messagegroup_impl("Requests", Side::Server, true, &iface.requests, out)?; write_messagegroup("Events", Side::Server, false, &iface.events, out)?; - write_messagegroup_impl( - "Events", - &iface_name, - Side::Server, - false, - &iface.events, - out, - )?; + write_messagegroup_impl("Events", Side::Server, false, &iface.events, out)?; write_interface(&iface_name, &iface.name, out)?; writeln!(out, "}}\n")?; @@ -113,7 +84,6 @@ pub(crate) fn write_protocol_server(protocol: Protocol, out: &mut O) - pub fn write_messagegroup_impl( name: &str, - iface: &str, side: Side, receiver: bool, messages: &[Message], @@ -124,6 +94,7 @@ pub fn write_messagegroup_impl( // 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!( @@ -136,9 +107,13 @@ pub fn write_messagegroup_impl( write!(out, "{{ .. }} ")?; } writeln!(out, "=> true,")?; + n -= 1; } } - writeln!(out, " _ => false")?; + if n > 0 { + // avoir "unreachable pattern" warnings =) + writeln!(out, " _ => false")?; + } writeln!(out, " }}")?; writeln!(out, " }}\n")?; @@ -355,7 +330,7 @@ pub fn write_messagegroup_impl( j, a.name )?; write!(out, " ")?; - writeln!(out, "_args_array[{}].s = _arg_{}.as_ptr();", j, j); + writeln!(out, "_args_array[{}].s = _arg_{}.as_ptr();", j, j)?; } } Type::Array => { @@ -378,7 +353,7 @@ pub fn write_messagegroup_impl( } } Type::NewId => { - if let Some(ref iface) = a.interface { + if a.interface.is_some() { if side == Side::Server { // serialize like a regular object if a.allow_null { diff --git a/wayland-scanner/src/common_gen.rs b/wayland-scanner/src/common_gen.rs index 76244b51e87..025232995e5 100644 --- a/wayland-scanner/src/common_gen.rs +++ b/wayland-scanner/src/common_gen.rs @@ -1,5 +1,4 @@ use std::io::Result as IOResult; -use std::cmp; use std::io::Write; use protocol::*; diff --git a/wayland-scanner/src/lib.rs b/wayland-scanner/src/lib.rs index 95c34f109d8..1ac8e977ebf 100644 --- a/wayland-scanner/src/lib.rs +++ b/wayland-scanner/src/lib.rs @@ -137,7 +137,7 @@ pub fn generate_c_interfaces, P2: AsRef>(protocol: P1, tar .create(true) .open(target) .unwrap(); - c_interface_gen::generate_interfaces(protocol, &mut out); + c_interface_gen::generate_interfaces(protocol, &mut out).unwrap() } /// Generate the code for a protocol @@ -174,7 +174,7 @@ pub fn generate_c_code, P2: AsRef>(prot: P1, target: P2, s /// - `target`: a `Write`-able object to which the generated code will be outputed to pub fn generate_interfaces_streams(protocol: P1, target: &mut P2) { let protocol = parse::parse_stream(protocol); - c_interface_gen::generate_interfaces(protocol, target); + c_interface_gen::generate_interfaces(protocol, target).unwrap(); } /// Generate the code for a protocol from/to IO streams diff --git a/wayland-scanner/src/protocol.rs b/wayland-scanner/src/protocol.rs index 06338616cf5..e1bfb96b38c 100644 --- a/wayland-scanner/src/protocol.rs +++ b/wayland-scanner/src/protocol.rs @@ -38,39 +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)] diff --git a/wayland-scanner/src/side.rs b/wayland-scanner/src/side.rs index 6aca4a3e415..02f026f6555 100644 --- a/wayland-scanner/src/side.rs +++ b/wayland-scanner/src/side.rs @@ -13,24 +13,10 @@ pub enum Side { } impl Side { - pub(crate) fn object_ptr_type(&self) -> &'static str { - match *self { - Client => "wl_proxy", - Server => "wl_resource", - } - } - pub(crate) fn object_name(&self) -> &'static str { match *self { Client => "Proxy", Server => "Resource", } } - - pub(crate) fn handle(&self) -> &'static str { - match *self { - Client => "WAYLAND_CLIENT_HANDLE", - Server => "WAYLAND_SERVER_HANDLE", - } - } } diff --git a/wayland-scanner/src/util.rs b/wayland-scanner/src/util.rs index f49a5d204dd..6cf70754e73 100644 --- a/wayland-scanner/src/util.rs +++ b/wayland-scanner/src/util.rs @@ -1,5 +1,3 @@ -use std::ascii::AsciiExt; - pub fn is_keyword(txt: &str) -> bool { match txt { "abstract" | "alignof" | "as" | "become" | "box" | "break" | "const" | "continue" | "crate" @@ -29,10 +27,6 @@ pub fn snake_to_camel(input: &str) -> String { .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/src/lib.rs b/wayland-server/src/lib.rs index 6e095f5acb7..75e775e3cea 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -15,6 +15,11 @@ struct Client; #[cfg(feature = "native_lib")] pub use generated::c_api as protocol; +#[cfg(feature = "native_lib")] +pub mod sys { + pub use super::generated::c_interfaces as protocol_interfaces; +} + mod generated { #![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)] #![allow(non_upper_case_globals, non_snake_case, unused_imports)] diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index fc5627be9ee..1eb38b462fc 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -182,7 +182,7 @@ mod native_machinery { use std::sync::atomic::Ordering; use std::os::raw::{c_int, c_void}; - use super::{NewResource, Resource}; + use super::Resource; use wayland_commons::{Implementation, Interface, MessageGroup}; @@ -208,7 +208,7 @@ mod native_machinery { _implem: *const c_void, resource: *mut c_void, opcode: u32, - msg: *const wl_message, + _msg: *const wl_message, args: *const wl_argument, ) -> c_int where From a8e0e74b86ed9250ae36fe00e2bc786765657887 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 26 Feb 2018 15:26:32 +0100 Subject: [PATCH 10/60] Reintroduce scanner tests. --- tests/scanner.rs | 97 ++++ tests/scanner_assets/c_interfaces.rs | 108 +++++ tests/scanner_assets/client_c_code.rs | 610 ++++++++++++++++++++++++++ tests/scanner_assets/protocol.xml | 116 +++++ tests/scanner_assets/server_c_code.rs | 320 ++++++++++++++ wayland-scanner/src/lib.rs | 8 +- 6 files changed, 1255 insertions(+), 4 deletions(-) create mode 100644 tests/scanner.rs create mode 100644 tests/scanner_assets/c_interfaces.rs create mode 100644 tests/scanner_assets/client_c_code.rs create mode 100644 tests/scanner_assets/protocol.xml create mode 100644 tests/scanner_assets/server_c_code.rs diff --git a/tests/scanner.rs b/tests/scanner.rs new file mode 100644 index 00000000000..0e4e6c38357 --- /dev/null +++ b/tests/scanner.rs @@ -0,0 +1,97 @@ +extern crate difference; +extern crate wayland_scanner; + + +use difference::{Changeset, Difference}; +use std::io::Cursor; +use std::str::from_utf8; +use wayland_scanner::Side; + +const PROTOCOL: &'static str = include_str!("./scanner_assets/protocol.xml"); + +const C_INTERFACES_TARGET: &'static str = include_str!("./scanner_assets/c_interfaces.rs"); + +const CLIENT_C_CODE_TARGET: &'static str = include_str!("./scanner_assets/client_c_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); + }, + } + } +} + +fn only_newlines_err(diffs: &[Difference]) -> bool { + for d in diffs { + match *d { + Difference::Add(_) | Difference::Rem(_) => return false, + _ => {} + } + } + return true; +} + +#[test] +fn c_interfaces_generation() { + let mut out = Vec::new(); + wayland_scanner::generate_c_interfaces_streams(Cursor::new(PROTOCOL.as_bytes()), &mut out); + let changeset = Changeset::new( + C_INTERFACES_TARGET, + from_utf8(&out).expect("Output of scanner was not UTF8."), + "\n", + ); + if changeset.distance != 0 && !only_newlines_err(&changeset.diffs) { + print_diff(&changeset.diffs); + panic!( + "Scanner output does not match expected output: d = {}", + changeset.distance + ); + } +} + +#[test] +fn client_c_code_generation() { + let mut out = Vec::new(); + wayland_scanner::generate_c_code_streams(Cursor::new(PROTOCOL.as_bytes()), &mut out, Side::Client); + let changeset = Changeset::new( + CLIENT_C_CODE_TARGET, + from_utf8(&out).expect("Output of scanner was not UTF8."), + "\n", + ); + if changeset.distance != 0 && !only_newlines_err(&changeset.diffs) { + print_diff(&changeset.diffs); + panic!( + "Scanner output does not match expected output: d = {}", + changeset.distance + ); + } +} + +#[test] +fn server_c_code_generation() { + let mut out = Vec::new(); + wayland_scanner::generate_c_code_streams(Cursor::new(PROTOCOL.as_bytes()), &mut out, Side::Server); + let changeset = Changeset::new( + SERVER_C_CODE_TARGET, + from_utf8(&out).expect("Output of scanner was not UTF8."), + "\n", + ); + if changeset.distance != 0 && !only_newlines_err(&changeset.diffs) { + print_diff(&changeset.diffs); + panic!( + "Scanner output does not match expected output: d = {}", + changeset.distance + ); + } +} diff --git a/tests/scanner_assets/c_interfaces.rs b/tests/scanner_assets/c_interfaces.rs new file mode 100644 index 00000000000..db5e5e0dc53 --- /dev/null +++ b/tests/scanner_assets/c_interfaces.rs @@ -0,0 +1,108 @@ +// +// This file was auto-generated, do not edit directly +// + +/* +This is an example copyright. + It contains several lines. + AS WELL AS ALL CAPS TEXT. +*/ + +use std::os::raw::{c_char, c_void}; +use wayland_sys::common::*; + +const NULLPTR: *const c_void = 0 as *const c_void; + +static mut types_null: [*const wl_interface; 5] = [ + NULLPTR as *const wl_interface, + NULLPTR as *const wl_interface, + NULLPTR as *const wl_interface, + NULLPTR as *const wl_interface, + NULLPTR as *const wl_interface, +]; + +// wl_foo +static mut wl_foo_requests_create_bar_types: [*const wl_interface; 1] = [ + unsafe { &wl_bar_interface as *const wl_interface }, +]; + +pub static mut wl_foo_requests: [wl_message; 2] = [ + wl_message { name: b"foo_it\0" as *const u8 as *const c_char, signature: b"iusfh\0" as *const u8 as *const c_char, types: unsafe { &types_null as *const _ } }, + wl_message { name: b"create_bar\0" as *const u8 as *const c_char, signature: b"n\0" as *const u8 as *const c_char, types: unsafe { &wl_foo_requests_create_bar_types as *const _ } }, +]; + +pub static mut wl_foo_events: [wl_message; 1] = [ + wl_message { name: b"cake\0" as *const u8 as *const c_char, signature: b"2uu\0" as *const u8 as *const c_char, types: unsafe { &types_null as *const _ } }, +]; + +pub static mut wl_foo_interface: wl_interface = wl_interface { + name: b"wl_foo\0" as *const u8 as *const c_char, + version: 3, + request_count: 2, + requests: unsafe { &wl_foo_requests as *const _ }, + event_count: 1, + events: unsafe { &wl_foo_events as *const _ }, +}; + +// wl_bar +static mut wl_bar_requests_bar_delivery_types: [*const wl_interface; 3] = [ + NULLPTR as *const wl_interface, + unsafe { &wl_foo_interface as *const wl_interface }, + NULLPTR as *const wl_interface, +]; + +pub static mut wl_bar_requests: [wl_message; 2] = [ + wl_message { name: b"bar_delivery\0" as *const u8 as *const c_char, signature: b"2uoa\0" as *const u8 as *const c_char, types: unsafe { &wl_bar_requests_bar_delivery_types as *const _ } }, + wl_message { name: b"release\0" as *const u8 as *const c_char, signature: b"\0" as *const u8 as *const c_char, types: unsafe { &types_null as *const _ } }, +]; + +pub static mut wl_bar_interface: wl_interface = wl_interface { + name: b"wl_bar\0" as *const u8 as *const c_char, + version: 1, + request_count: 2, + requests: unsafe { &wl_bar_requests as *const _ }, + event_count: 0, + events: NULLPTR as *const wl_message, +}; + +// wl_display +pub static mut wl_display_interface: wl_interface = wl_interface { + name: b"wl_display\0" as *const u8 as *const c_char, + version: 1, + request_count: 0, + requests: NULLPTR as *const wl_message, + event_count: 0, + events: NULLPTR as *const wl_message, +}; + +// wl_registry + +pub static mut wl_registry_requests: [wl_message; 1] = [ + wl_message { name: b"bind\0" as *const u8 as *const c_char, signature: b"usun\0" as *const u8 as *const c_char, types: unsafe { &types_null as *const _ } }, +]; + +pub static mut wl_registry_interface: wl_interface = wl_interface { + name: b"wl_registry\0" as *const u8 as *const c_char, + version: 1, + request_count: 1, + requests: unsafe { &wl_registry_requests as *const _ }, + event_count: 0, + events: NULLPTR as *const wl_message, +}; + +// wl_callback + +pub static mut wl_callback_events: [wl_message; 1] = [ + wl_message { name: b"done\0" as *const u8 as *const c_char, signature: b"u\0" as *const u8 as *const c_char, types: unsafe { &types_null as *const _ } }, +]; + +pub static mut wl_callback_interface: wl_interface = wl_interface { + name: b"wl_callback\0" as *const u8 as *const c_char, + version: 1, + request_count: 0, + requests: NULLPTR as *const wl_message, + event_count: 1, + events: unsafe { &wl_callback_events as *const _ }, +}; + + diff --git a/tests/scanner_assets/client_c_code.rs b/tests/scanner_assets/client_c_code.rs new file mode 100644 index 00000000000..cdc03d5a074 --- /dev/null +++ b/tests/scanner_assets/client_c_code.rs @@ -0,0 +1,610 @@ +// +// 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 Requests { + /// 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 Requests { + 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!("Requests::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 { + Requests::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) + }, + Requests::CreateBar { id, } => { + let mut _args_array: [wl_argument; 1] = unsafe { ::std::mem::zeroed() }; + _args_array[0].o = ::std::ptr::null_mut(); + f(1, &mut _args_array) + }, + } + } + } + + pub enum Events { + /// 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 Events { + 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(Events::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!("Events::as_raw_c_in can not be used Client-side.") + } + } + + + pub struct WlFoo; + + impl Interface for WlFoo { + type Requests = Requests; + type Events = Events; + 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 = Requests::FooIt { number, unumber, text, float, file, }; + + unsafe { + msg.as_raw_c_in(|opcode, args| { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_proxy_marshal_array, + self.c_ptr(), + opcode, + args.as_mut_ptr() + ); + }); + } + } + + fn create_bar(&self) ->Result, ()> { + if !self.is_external() && !self.is_alive() { + return Err(()); + } + let msg = Requests::CreateBar { id: unsafe { Proxy::::new_null() }, }; + + unsafe { + let ret = msg.as_raw_c_in(|opcode, args| { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_proxy_marshal_array_constructor, + self.c_ptr(), + opcode, + args.as_mut_ptr(), + super::wl_bar::WlBar::c_interface() + ) + }); + Ok(NewProxy::::from_c_ptr(ret)) + } + } + } +} + +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 Requests { + /// 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 Requests { + fn is_destructor(&self) -> bool { + match *self { + Requests::Release => true, + _ => false + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + panic!("Requests::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 { + Requests::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) + }, + Requests::Release => { + let mut _args_array: [wl_argument; 0] = unsafe { ::std::mem::zeroed() }; + f(1, &mut _args_array) + }, + } + } + } + + pub enum Events { + } + + impl super::MessageGroup for Events { + 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!("Events::as_raw_c_in can not be used Client-side.") + } + } + + + pub struct WlBar; + + impl Interface for WlBar { + type Requests = Requests; + type Events = Events; + 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 = Requests::BarDelivery { kind, target: target.clone(), metadata, }; + + unsafe { + msg.as_raw_c_in(|opcode, args| { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_proxy_marshal_array, + self.c_ptr(), + opcode, + args.as_mut_ptr() + ); + }); + } + } + + fn release(&self) ->() { + if !self.is_external() && !self.is_alive() { + return; + } + let msg = Requests::Release; + + unsafe { + msg.as_raw_c_in(|opcode, args| { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_proxy_marshal_array, + self.c_ptr(), + opcode, + args.as_mut_ptr() + ); + }); + } + } + } +} + +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 Requests { + } + + impl super::MessageGroup for Requests { + 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!("Requests::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 Events { + } + + impl super::MessageGroup for Events { + 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!("Events::as_raw_c_in can not be used Client-side.") + } + } + + + pub struct WlDisplay; + + impl Interface for WlDisplay { + type Requests = Requests; + type Events = Events; + 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 Requests { + /// 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 Requests { + 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!("Requests::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 { + Requests::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 Events { + } + + impl super::MessageGroup for Events { + 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!("Events::as_raw_c_in can not be used Client-side.") + } + } + + + pub struct WlRegistry; + + impl Interface for WlRegistry { + type Requests = Requests; + type Events = Events; + 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 = Requests::Bind { 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 Requests { + } + + impl super::MessageGroup for Requests { + 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!("Requests::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 Events { + /// 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 Events { + fn is_destructor(&self) -> bool { + match *self { + Events::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(Events::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!("Events::as_raw_c_in can not be used Client-side.") + } + } + + + pub struct WlCallback; + + impl Interface for WlCallback { + type Requests = Requests; + type Events = Events; + 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/protocol.xml b/tests/scanner_assets/protocol.xml new file mode 100644 index 00000000000..89e148738f0 --- /dev/null +++ b/tests/scanner_assets/protocol.xml @@ -0,0 +1,116 @@ + + + + This is an example copyright. + It contains several lines. + AS WELL AS ALL CAPS TEXT. + + + + + This is the dedicated interface for doing foos over any + kind of other foos. + + + + + This will do some foo with its args. + + + + + + + + + + + Create a bar which will do its bar job. + + + + + + + List of the possible kind of cake supported by the protocol. + + + + + + + + + + + + + + + + The server advertizes that a kind of cake is available + + + + + + + + + This interface allows you to bar your foos. + + + + + Proceed to a bar delivery of given foo. + + + + + + + + + Notify the compositor that you have finished using this bar. + + + + + + + + + This global is special and should only generate code client-side, not server-side. + + + + + + This global is special and should only generate code client-side, not server-side. + + + + + This request is a special code-path, as its new-id argument as no target type. + + + + + + + + + This object has a special behavior regarding its destructor. + + + + + 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. + + + + + + + diff --git a/tests/scanner_assets/server_c_code.rs b/tests/scanner_assets/server_c_code.rs new file mode 100644 index 00000000000..cabdf4bec75 --- /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 Requests { + /// 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 Requests { + 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(Requests::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(Requests::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!("Requests::as_raw_c_in can not be used Server-side.") + } + } + + pub enum Events { + /// 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 Events { + 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!("Events::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 { + Events::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 Requests = Requests; + type Events = Events; + 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 Requests { + /// 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 Requests { + fn is_destructor(&self) -> bool { + match *self { + Requests::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(Requests::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(Requests::Release) }, + _ => return Err(()) + } + } + + fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { + panic!("Requests::as_raw_c_in can not be used Server-side.") + } + } + + pub enum Events { + } + + impl super::MessageGroup for Events { + 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!("Events::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 Requests = Requests; + type Events = Events; + 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 Requests { + } + + impl super::MessageGroup for Requests { + 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!("Requests::as_raw_c_in can not be used Server-side.") + } + } + + pub enum Events { + /// 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 Events { + fn is_destructor(&self) -> bool { + match *self { + Events::Done { .. } => true, + } + } + + unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + panic!("Events::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 { + Events::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 Requests = Requests; + type Events = Events; + const NAME: &'static str = "wl_callback"; + fn c_interface() -> *const wl_interface { + unsafe { &super::super::c_interfaces::wl_callback_interface } + } + } +} + diff --git a/wayland-scanner/src/lib.rs b/wayland-scanner/src/lib.rs index 1ac8e977ebf..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: @@ -166,13 +166,13 @@ pub fn generate_c_code, P2: AsRef>(prot: P1, target: P2, s /// 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); c_interface_gen::generate_interfaces(protocol, target).unwrap(); } From 5e2e48a3d8ac96bf26a3128891fe4b8dd11389bb Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 26 Feb 2018 15:59:37 +0100 Subject: [PATCH 11/60] fix 1.20 build & travis coverage --- .travis.yml | 2 ++ wayland-scanner/src/util.rs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index c14dc1e6d45..72b813fdc59 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ cache: cargo dist: trusty +sudo: required + addons: apt: sources: diff --git a/wayland-scanner/src/util.rs b/wayland-scanner/src/util.rs index 6cf70754e73..5e65e267cdd 100644 --- a/wayland-scanner/src/util.rs +++ b/wayland-scanner/src/util.rs @@ -1,3 +1,6 @@ +#[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" From da18af0e0910cdadf34ca800146d0e0b1b4a3b57 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Tue, 27 Feb 2018 10:48:54 +0100 Subject: [PATCH 12/60] Simplify client codegen with w_proxy_create --- tests/scanner.rs | 42 ++++++++---- tests/scanner_assets/client_c_code.rs | 80 ++++++++-------------- wayland-client/src/proxy.rs | 69 +++++++++++++++++-- wayland-scanner/src/c_code_gen.rs | 96 +++++++++++---------------- 4 files changed, 159 insertions(+), 128 deletions(-) diff --git a/tests/scanner.rs b/tests/scanner.rs index 0e4e6c38357..29d6db6fc11 100644 --- a/tests/scanner.rs +++ b/tests/scanner.rs @@ -16,22 +16,40 @@ const CLIENT_C_CODE_TARGET: &'static str = include_str!("./scanner_assets/client 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 { diff --git a/tests/scanner_assets/client_c_code.rs b/tests/scanner_assets/client_c_code.rs index cdc03d5a074..9c437b2b980 100644 --- a/tests/scanner_assets/client_c_code.rs +++ b/tests/scanner_assets/client_c_code.rs @@ -107,7 +107,7 @@ pub mod wl_foo { }, Requests::CreateBar { id, } => { let mut _args_array: [wl_argument; 1] = unsafe { ::std::mem::zeroed() }; - _args_array[0].o = ::std::ptr::null_mut(); + _args_array[0].o = id.c_ptr() as *mut _; f(1, &mut _args_array) }, } @@ -175,40 +175,26 @@ pub mod wl_foo { if !self.is_external() && !self.is_alive() { return; } - let msg = Requests::FooIt { number, unumber, text, float, file, }; - - unsafe { - msg.as_raw_c_in(|opcode, args| { - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_proxy_marshal_array, - self.c_ptr(), - opcode, - args.as_mut_ptr() - ); - }); - } + let msg = Requests::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 msg = Requests::CreateBar { id: unsafe { Proxy::::new_null() }, }; - - unsafe { - let ret = msg.as_raw_c_in(|opcode, args| { - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_proxy_marshal_array_constructor, - self.c_ptr(), - opcode, - args.as_mut_ptr(), - super::wl_bar::WlBar::c_interface() - ) - }); - Ok(NewProxy::::from_c_ptr(ret)) - } + let _arg_id_newproxy = self.child::(); + let msg = Requests::CreateBar { + id: unsafe { Proxy::::from_c_ptr(_arg_id_newproxy.c_ptr()) }, + }; + self.send(msg); + Ok(_arg_id_newproxy) } } } @@ -319,19 +305,13 @@ pub mod wl_bar { if !self.is_external() && !self.is_alive() { return; } - let msg = Requests::BarDelivery { kind, target: target.clone(), metadata, }; - unsafe { - msg.as_raw_c_in(|opcode, args| { - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_proxy_marshal_array, - self.c_ptr(), - opcode, - args.as_mut_ptr() - ); - }); - } + let msg = Requests::BarDelivery { + kind: kind, + target: target.clone(), + metadata: metadata, + }; + self.send(msg); } fn release(&self) ->() { @@ -339,18 +319,7 @@ pub mod wl_bar { return; } let msg = Requests::Release; - - unsafe { - msg.as_raw_c_in(|opcode, args| { - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_proxy_marshal_array, - self.c_ptr(), - opcode, - args.as_mut_ptr() - ); - }); - } + self.send(msg); } } } @@ -508,7 +477,10 @@ pub mod wl_registry { if !self.is_external() && !self.is_alive() { return Err(()); } - let msg = Requests::Bind { name, id: (T::NAME.into(), version, unsafe { Proxy::::new_null() }), }; + let msg = Requests::Bind { + name: name, + id: (T::NAME.into(), version, unsafe { Proxy::::new_null() }), + }; unsafe { let ret = msg.as_raw_c_in(|opcode, args| { diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs index 08630665ff2..c8d8ceffe73 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -1,7 +1,7 @@ -use wayland_commons::{Implementation, Interface}; +use wayland_commons::{Implementation, Interface, MessageGroup}; #[cfg(feature = "native_lib")] -use wayland_sys::client::wl_proxy; +use wayland_sys::client::*; use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; @@ -28,6 +28,36 @@ pub struct Proxy { } impl Proxy { + pub fn send(&self, msg: I::Requests) { + #[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 _ + ); + }); + } + } + // returns false is external pub fn is_alive(&self) -> bool { #[cfg(not(feature = "native_lib"))] @@ -85,12 +115,38 @@ impl Proxy { } #[doc(hidden)] - #[cfg(feature = "native_lib")] pub unsafe fn new_null() -> Proxy { Proxy { _i: ::std::marker::PhantomData, internal: None, - ptr: ::std::ptr::null_mut(), + ptr: ::std::ptr::null_mut() + } + } + + pub fn child(&self) -> NewProxy { + #[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, + } + } + } +} + +#[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 } } } @@ -139,6 +195,11 @@ impl NewProxy { } } + #[cfg(feature = "native_lib")] + pub fn c_ptr(&self) -> *mut wl_proxy { + self.ptr + } + #[cfg(feature = "native_lib")] pub unsafe fn from_c_ptr(ptr: *mut wl_proxy) -> Self { NewProxy { diff --git a/wayland-scanner/src/c_code_gen.rs b/wayland-scanner/src/c_code_gen.rs index 56e9cf9b9f1..62ff3abba28 100644 --- a/wayland-scanner/src/c_code_gen.rs +++ b/wayland-scanner/src/c_code_gen.rs @@ -354,17 +354,7 @@ pub fn write_messagegroup_impl( } Type::NewId => { if a.interface.is_some() { - if side == Side::Server { - // serialize like a regular 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)?; - } - } else { - // this must be a NULL, ignore the dummy object received - writeln!(out, "_args_array[{}].o = ::std::ptr::null_mut();", j)?; - } + writeln!(out, "_args_array[{}].o = {}.c_ptr() as *mut _;", j, a.name)?; } else { if side == Side::Server { panic!("Cannot serialize anonymous NewID from server."); @@ -459,6 +449,16 @@ fn write_client_methods(name: &str, messages: &[Message], out: &mut O) 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, @@ -466,55 +466,36 @@ fn write_client_methods(name: &str, messages: &[Message], out: &mut O) snake_to_camel(&msg.name) )?; if msg.args.len() > 0 { - write!(out, " {{ ")?; + writeln!(out, " {{")?; for a in &msg.args { + write!(out, " ")?; if a.typ == Type::NewId { if let Some(ref iface) = a.interface { - write!( + writeln!( out, - "{}: unsafe {{ Proxy::::new_null() }}, ", + "{}: unsafe {{ Proxy::::from_c_ptr(_arg_{0}_newproxy.c_ptr()) }},", a.name, iface, snake_to_camel(&iface) )?; } else { - write!(out, "{}: (T::NAME.into(), version, unsafe {{ Proxy::::new_null() }}),", a.name)?; + writeln!(out, "{}: (T::NAME.into(), version, unsafe {{ Proxy::::new_null() }}),", a.name)?; } } else if a.typ == Type::Object { if a.allow_null { - write!(out, "{0} : {0}.map(|o| o.clone()), ", a.name)?; + writeln!(out, "{0} : {0}.map(|o| o.clone()),", a.name)?; } else { - write!(out, "{0}: {0}.clone(), ", a.name)?; + writeln!(out, "{0}: {0}.clone(),", a.name)?; } } else { - write!(out, "{}, ", a.name)?; + writeln!(out, "{0}: {0},", a.name)?; } } - write!(out, " }}")?; + write!(out, " }}")?; } writeln!(out, ";")?; - if let Some(ret_type) = return_type { - if let Some(ref iface) = ret_type.interface { - writeln!( - out, - r#" - unsafe {{ - let ret = msg.as_raw_c_in(|opcode, args| {{ - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_proxy_marshal_array_constructor, - self.c_ptr(), - opcode, - args.as_mut_ptr(), - super::{0}::{1}::c_interface() - ) - }}); - Ok(NewProxy::::from_c_ptr(ret)) - }}"#, - iface, - snake_to_camel(iface) - )?; - } else { + match return_type { + Some(ret_type) if ret_type.interface.is_none() => { writeln!( out, r#" @@ -533,25 +514,24 @@ fn write_client_methods(name: &str, messages: &[Message], out: &mut O) 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 let Some(ref iface) = a.interface { + writeln!(out, " Ok(_arg_{}_newproxy)", a.name)?; + } + } + } + } } - } else { - writeln!( - out, - r#" - unsafe {{ - msg.as_raw_c_in(|opcode, args| {{ - ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_proxy_marshal_array, - self.c_ptr(), - opcode, - args.as_mut_ptr() - ); - }}); - }}"# - )?; } - writeln!(out, " }}")?; + writeln!(out, " }}\n")?; } writeln!(out, " }}")?; From 69ec5335d5bcf3b25dda7362690f03930acdf187 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 2 Mar 2018 16:01:28 +0100 Subject: [PATCH 13/60] fmt tests --- tests/scanner.rs | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/tests/scanner.rs b/tests/scanner.rs index 29d6db6fc11..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; @@ -18,19 +17,22 @@ const SERVER_C_CODE_TARGET: &'static str = include_str!("./scanner_assets/server fn print_diff(diffs: &[Difference]) { 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::>(); + 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 { + if idx != last_idx + 1 { println!("\n=== Partial diff ==="); } last_idx = idx; @@ -43,11 +45,26 @@ fn print_diff(diffs: &[Difference]) { } 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() + 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 { From d090c1149bbb709036b146a82138aa010ef5335a Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 2 Mar 2018 16:02:06 +0100 Subject: [PATCH 14/60] client: connection machinnery. --- wayland-client/examples/list_globals.rs | 30 +++++ wayland-client/src/display.rs | 125 ++++++++++++++++++++ wayland-client/src/event_queue.rs | 145 ++++++++++++++++++++++++ wayland-client/src/lib.rs | 5 + wayland-client/src/proxy.rs | 46 ++++++-- wayland-scanner/src/c_code_gen.rs | 15 ++- 6 files changed, 352 insertions(+), 14 deletions(-) create mode 100644 wayland-client/examples/list_globals.rs create mode 100644 wayland-client/src/display.rs create mode 100644 wayland-client/src/event_queue.rs diff --git a/wayland-client/examples/list_globals.rs b/wayland-client/examples/list_globals.rs new file mode 100644 index 00000000000..471282bfd8f --- /dev/null +++ b/wayland-client/examples/list_globals.rs @@ -0,0 +1,30 @@ +extern crate wayland_client; + +use wayland_client::Display; + +use wayland_client::protocol::wl_display::RequestsTrait; +use wayland_client::protocol::wl_registry::Events as RegistryEvents; + +fn main() { + let (display, mut event_queue) = Display::connect_to_env().unwrap(); + + let registry = display + .get_registry() + .unwrap() + .implement((), |_, evt, _| match evt { + RegistryEvents::Global { + name, + interface, + version, + } => { + println!( + "New global with id {} and version {} of interface '{}'.", + name, version, interface + ); + } + _ => {} + }); + + event_queue.sync_roundtrip().unwrap(); + event_queue.sync_roundtrip().unwrap(); +} diff --git a/wayland-client/src/display.rs b/wayland-client/src/display.rs new file mode 100644 index 00000000000..59c0b4d22e0 --- /dev/null +++ b/wayland-client/src/display.rs @@ -0,0 +1,125 @@ +#[cfg(feature = "native_lib")] +use std::ffi::{CString, OsString}; +#[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 +#[derive(Debug)] +pub enum ConnectError { + /// The library was compiled with the `dlopen` feature, and the `libwayland-client.so` + /// library could not be found at runtime + NoWaylandLib, + /// Any needed library was found, but the listening socket of the server could not be + /// found. + /// + /// Most of the time, this means that the program was not started from a wayland session. + NoCompositorListening, + InvalidName, +} + +pub(crate) struct DisplayInner { + proxy: Proxy<::protocol::wl_display::WlDisplay>, +} + +impl DisplayInner { + pub(crate) fn ptr(&self) -> *mut wl_display { + self.proxy.c_ptr() as *mut _ + } +} + +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 + ); + } + } + } +} + +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)) + } + + 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) + } + } + } + + 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 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/event_queue.rs b/wayland-client/src/event_queue.rs new file mode 100644 index 00000000000..f75eb9c0a29 --- /dev/null +++ b/wayland-client/src/event_queue.rs @@ -0,0 +1,145 @@ +use std::io::{Error as IoError, Result as IoResult}; +use std::sync::Arc; + +use display::DisplayInner; + +#[cfg(feature = "native_lib")] +use wayland_sys::client::*; + +pub struct EventQueue { + wlevq: Option<*mut wl_event_queue>, + inner: Arc, +} + +impl EventQueue { + pub(crate) unsafe fn new(inner: Arc, evq: Option<*mut wl_event_queue>) -> EventQueue { + EventQueue { + inner: inner, + wlevq: evq, + } + } + + /// Dispatches events from the internal buffer. + /// + /// 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 + /// other event queues. + /// + /// If an error is returned, your connection with the wayland + /// compositor is probably lost. + pub fn dispatch(&mut self) -> IoResult { + #[cfg(not(feature = "native_lib"))] + {} + #[cfg(feature = "native_lib")] + { + let ret = match self.wlevq { + Some(evq) => unsafe { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_dispatch_queue, + self.inner.ptr(), + evq + ) + }, + None => unsafe { + ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_dispatch, self.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 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 { + #[cfg(not(feature = "native_lib"))] + {} + #[cfg(feature = "native_lib")] + { + let ret = match self.wlevq { + Some(evq) => unsafe { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_dispatch_queue_pending, + self.inner.ptr(), + evq + ) + }, + None => unsafe { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_dispatch_pending, + self.inner.ptr() + ) + }, + }; + if ret >= 0 { + Ok(ret as u32) + } else { + Err(IoError::last_os_error()) + } + } + } + + /// Synchronous roundtrip + /// + /// This call will cause a synchonous roundtrip with the wayland server. It will block until all + /// pending requests of this queue are sent to the server and it has processed all of them and + /// send the appropriate events. + /// + /// Handlers are called as a consequence. + /// + /// On success returns the number of dispatched events. + pub fn sync_roundtrip(&mut self) -> IoResult { + #[cfg(not(feature = "native_lib"))] + {} + #[cfg(feature = "native_lib")] + { + let ret = unsafe { + match self.wlevq { + Some(evtq) => ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_roundtrip_queue, + self.inner.ptr(), + evtq + ), + None => ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_roundtrip, + self.inner.ptr() + ), + } + }; + if ret >= 0 { + Ok(ret) + } else { + Err(IoError::last_os_error()) + } + } + } +} + +impl Drop for EventQueue { + 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); + } + } + } + } +} diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index 6b51f3acc98..25593758c9e 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -7,8 +7,13 @@ extern crate wayland_commons; #[macro_use] extern crate wayland_sys; +mod display; +mod event_queue; mod proxy; + pub use proxy::{NewProxy, Proxy}; +pub use display::Display; +pub use event_queue::EventQueue; #[cfg(feature = "native_lib")] pub use generated::c_api as protocol; diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs index c8d8ceffe73..0205b2b7caf 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -73,6 +73,34 @@ impl Proxy { } } + 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); + } + } + } + + 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")] pub fn is_external(&self) -> bool { self.internal.is_none() @@ -119,19 +147,21 @@ impl Proxy { Proxy { _i: ::std::marker::PhantomData, internal: None, - ptr: ::std::ptr::null_mut() + ptr: ::std::ptr::null_mut(), } } pub fn child(&self) -> NewProxy { #[cfg(feature = "native_lib")] { - let ptr = unsafe { ffi_dispatch!( - WAYLAND_CLIENT_HANDLE, - wl_proxy_create, - self.ptr, - C::c_interface() - ) }; + let ptr = unsafe { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_proxy_create, + self.ptr, + C::c_interface() + ) + }; NewProxy { _i: ::std::marker::PhantomData, ptr: ptr, @@ -146,7 +176,7 @@ impl Proxy<::protocol::wl_display::WlDisplay> { Proxy { _i: ::std::marker::PhantomData, internal: None, - ptr: d as *mut wl_proxy + ptr: d as *mut wl_proxy, } } } diff --git a/wayland-scanner/src/c_code_gen.rs b/wayland-scanner/src/c_code_gen.rs index 62ff3abba28..fbcd45c3223 100644 --- a/wayland-scanner/src/c_code_gen.rs +++ b/wayland-scanner/src/c_code_gen.rs @@ -454,7 +454,13 @@ fn write_client_methods(name: &str, messages: &[Message], out: &mut O) 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))?; + writeln!( + out, + " let _arg_{}_newproxy = self.child::();", + a.name, + iface, + snake_to_camel(&iface) + )?; has_newp = true; } } @@ -514,12 +520,9 @@ fn write_client_methods(name: &str, messages: &[Message], out: &mut O) Ok(NewProxy::::from_c_ptr(ret)) }}"# )?; - }, + } _ => { - writeln!( - out, - " self.send(msg);" - )?; + writeln!(out, " self.send(msg);")?; if has_newp { for a in &msg.args { if a.typ == Type::NewId { From 408cb146fa50f78158e92719bdb68d9c621c3d78 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Tue, 13 Mar 2018 18:03:08 +0100 Subject: [PATCH 15/60] client: proxy wrapper and !send implementations --- wayland-client/src/event_queue.rs | 68 +++++++++++++++---- wayland-client/src/proxy.rs | 107 +++++++++++++++++++++++++++--- wayland-sys/src/client.rs | 3 +- 3 files changed, 152 insertions(+), 26 deletions(-) diff --git a/wayland-client/src/event_queue.rs b/wayland-client/src/event_queue.rs index f75eb9c0a29..1f1a41bf2e3 100644 --- a/wayland-client/src/event_queue.rs +++ b/wayland-client/src/event_queue.rs @@ -1,4 +1,6 @@ use std::io::{Error as IoError, Result as IoResult}; +use std::ptr; +use std::rc::Rc; use std::sync::Arc; use display::DisplayInner; @@ -6,16 +8,28 @@ use display::DisplayInner; #[cfg(feature = "native_lib")] use wayland_sys::client::*; -pub struct EventQueue { - wlevq: Option<*mut wl_event_queue>, +struct EventQueueInner { + #[cfg(feature = "native_lib")] wlevq: Option<*mut wl_event_queue>, inner: Arc, } +pub struct EventQueue { + // EventQueue is *not* Send + inner: Rc, +} + +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: inner, - wlevq: evq, + inner: Rc::new(EventQueueInner { + inner: inner, + wlevq: evq, + }), } } @@ -34,17 +48,21 @@ impl EventQueue { {} #[cfg(feature = "native_lib")] { - let ret = match self.wlevq { + let ret = match self.inner.wlevq { Some(evq) => unsafe { ffi_dispatch!( WAYLAND_CLIENT_HANDLE, wl_display_dispatch_queue, - self.inner.ptr(), + self.inner.inner.ptr(), evq ) }, None => unsafe { - ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_dispatch, self.inner.ptr()) + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_dispatch, + self.inner.inner.ptr() + ) }, }; if ret >= 0 { @@ -68,12 +86,12 @@ impl EventQueue { {} #[cfg(feature = "native_lib")] { - let ret = match self.wlevq { + let ret = match self.inner.wlevq { Some(evq) => unsafe { ffi_dispatch!( WAYLAND_CLIENT_HANDLE, wl_display_dispatch_queue_pending, - self.inner.ptr(), + self.inner.inner.ptr(), evq ) }, @@ -81,7 +99,7 @@ impl EventQueue { ffi_dispatch!( WAYLAND_CLIENT_HANDLE, wl_display_dispatch_pending, - self.inner.ptr() + self.inner.inner.ptr() ) }, }; @@ -108,17 +126,17 @@ impl EventQueue { #[cfg(feature = "native_lib")] { let ret = unsafe { - match self.wlevq { + match self.inner.wlevq { Some(evtq) => ffi_dispatch!( WAYLAND_CLIENT_HANDLE, wl_display_roundtrip_queue, - self.inner.ptr(), + self.inner.inner.ptr(), evtq ), None => ffi_dispatch!( WAYLAND_CLIENT_HANDLE, wl_display_roundtrip, - self.inner.ptr() + self.inner.inner.ptr() ), } }; @@ -129,9 +147,31 @@ impl EventQueue { } } } + + pub fn get_token(&self) -> QueueToken { + QueueToken { + inner: self.inner.clone(), + } + } +} + +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 Drop for EventQueue { +impl Drop for EventQueueInner { fn drop(&mut self) { #[cfg(feature = "nativel_lib")] { diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs index 0205b2b7caf..8d8d41eee67 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -3,6 +3,8 @@ 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}; @@ -25,8 +27,12 @@ pub struct Proxy { #[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 { pub fn send(&self, msg: I::Requests) { #[cfg(not(feature = "native_lib"))] @@ -112,6 +118,8 @@ impl Proxy { internal: self.internal.clone(), #[cfg(feature = "native_lib")] ptr: self.ptr, + #[cfg(feature = "native_lib")] + is_wrapper: self.is_wrapper, } } @@ -139,19 +147,46 @@ impl 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")] + 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, + }) + } + pub fn child(&self) -> NewProxy { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } #[cfg(feature = "native_lib")] { let ptr = unsafe { @@ -177,6 +212,20 @@ impl Proxy<::protocol::wl_display::WlDisplay> { _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); + } + } } } } @@ -187,7 +236,44 @@ pub struct NewProxy { } impl NewProxy { - pub fn implement( + /// Implement this proxy using given function and implementation data. + pub fn implement( + self, + idata: ID, + implementation: Implementation, I::Events, ID>, + ) -> Proxy { + unsafe { self.implement_inner(idata, implementation) } + } + + /// Implement this proxy 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 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, + idata: ID, + implementation: Implementation, I::Events, ID>, + queue: &QueueToken, + ) -> Proxy { + #[cfg(not(feature = "native_lib"))] + {} + #[cfg(feature = "native_lib")] + { + queue.assign_proxy(self.c_ptr()); + self.implement_inner(idata, implementation) + } + } + + unsafe fn implement_inner( self, idata: ID, implementation: Implementation, I::Events, ID>, @@ -206,21 +292,20 @@ impl NewProxy { )); let internal = new_user_data.internal.clone(); - unsafe { - 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 _ - ); - } + 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, } } } diff --git a/wayland-sys/src/client.rs b/wayland-sys/src/client.rs index 84f18cbf3fe..dfe22ccf339 100644 --- a/wayland-sys/src/client.rs +++ b/wayland-sys/src/client.rs @@ -47,7 +47,6 @@ external_library!(WaylandClient, "wayland-client", 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) -> *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, @@ -55,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) -> (), From 0b624e161ea611d526751f7df513a48ee59a5f60 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Tue, 13 Mar 2018 18:09:23 +0100 Subject: [PATCH 16/60] client: create other event queues --- wayland-client/src/display.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/wayland-client/src/display.rs b/wayland-client/src/display.rs index 59c0b4d22e0..5dd565689e6 100644 --- a/wayland-client/src/display.rs +++ b/wayland-client/src/display.rs @@ -30,6 +30,7 @@ pub(crate) struct DisplayInner { } impl DisplayInner { + #[cfg(feature = "native_lib")] pub(crate) fn ptr(&self) -> *mut wl_display { self.proxy.c_ptr() as *mut _ } @@ -115,6 +116,23 @@ impl Display { } } } + + pub fn create_event_queue(&self) -> EventQueue { + #[cfg(not(feature = "native_lib"))] + { + unimplemented!() + } + #[cfg(feature = "native_lib")] + unsafe { + let ptr = ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_create_queue, + self.inner.ptr() + ); + + EventQueue::new(self.inner.clone(), Some(ptr)) + } + } } impl Deref for Display { From c484280318e59b99a3d76b3d1842158578375271 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Tue, 20 Mar 2018 16:46:00 +0100 Subject: [PATCH 17/60] client: add missing export --- wayland-client/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index 25593758c9e..6823a168ce4 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -13,7 +13,7 @@ mod proxy; pub use proxy::{NewProxy, Proxy}; pub use display::Display; -pub use event_queue::EventQueue; +pub use event_queue::{EventQueue, QueueToken}; #[cfg(feature = "native_lib")] pub use generated::c_api as protocol; From 221c1de740a26dd882ef12b7debc2197c0a4c45a Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Thu, 22 Mar 2018 12:12:40 +0100 Subject: [PATCH 18/60] server: loop & globals machinnery --- wayland-server/src/display.rs | 87 ++++++++++++++++++++ wayland-server/src/event_loop.rs | 137 +++++++++++++++++++++++++++++++ wayland-server/src/globals.rs | 92 +++++++++++++++++++++ wayland-server/src/lib.rs | 7 ++ wayland-server/src/resource.rs | 62 ++++++++++---- 5 files changed, 370 insertions(+), 15 deletions(-) create mode 100644 wayland-server/src/display.rs create mode 100644 wayland-server/src/event_loop.rs create mode 100644 wayland-server/src/globals.rs diff --git a/wayland-server/src/display.rs b/wayland-server/src/display.rs new file mode 100644 index 00000000000..f631d4bc23c --- /dev/null +++ b/wayland-server/src/display.rs @@ -0,0 +1,87 @@ +#[cfg(feature = "native_lib")] +use std::ffi::{CString, OsString}; +#[cfg(feature = "native_lib")] +use std::os::unix::ffi::OsStringExt; +use std::sync::Arc; + +use wayland_commons::Interface; + +use {EventLoop, Global, GlobalImplementation, LoopToken}; +use globals::global_bind; + +#[cfg(feature = "native_lib")] +use wayland_sys::server::*; + +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); + } + } + } +} + +pub struct Display { + inner: Arc, +} + +impl Display { + #[cfg(feature = "native_lib")] + 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 }), + }; + + 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) + } + + pub fn create_global( + &mut self, + token: &LoopToken, + version: u32, + implementation: GlobalImplementation, + idata: ID, + ) -> Global { + 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((implementation, idata)); + + unsafe { + let ptr = ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_global_create, + self.inner.ptr, + I::c_interface(), + version as i32, + &*data as *const (GlobalImplementation, ID) as *mut _, + global_bind:: + ); + + Global::create(ptr, data) + } + } +} diff --git a/wayland-server/src/event_loop.rs b/wayland-server/src/event_loop.rs new file mode 100644 index 00000000000..dbfac4c836b --- /dev/null +++ b/wayland-server/src/event_loop.rs @@ -0,0 +1,137 @@ +use std::io::{Error as IoError, Result as IoResult}; +use std::rc::Rc; +use std::sync::Arc; +use std::sync::atomic; + +use display::DisplayInner; + +#[cfg(feature = "native_lib")] +use wayland_sys::server::*; + +pub(crate) struct EventLoopInner { + #[cfg(feature = "native_lib")] wlevl: *mut wl_event_loop, + pub(crate) inner: Option>, +} + +pub struct EventLoop { + // EventLoop is *not* Send + inner: Rc, + stop_signal: Arc, +} + +pub struct LoopToken { + pub(crate) inner: Rc, +} + +pub struct LoopSignal { + inner: Arc, +} + +impl LoopSignal { + pub fn stop(&self) { + self.inner.store(true, atomic::Ordering::Release); + } +} + +impl EventLoop { + #[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)), + } + } + + pub fn token(&self) -> LoopToken { + LoopToken { + inner: self.inner.clone(), + } + } + + 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`. + /// + /// 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()) + } + } + } + + /// 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`. + /// + /// 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 Drop for EventLoopInner { + fn drop(&mut self) { + #[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); + } + } + } + } +} diff --git a/wayland-server/src/globals.rs b/wayland-server/src/globals.rs new file mode 100644 index 00000000000..dbb36854bce --- /dev/null +++ b/wayland-server/src/globals.rs @@ -0,0 +1,92 @@ +use std::os::raw::c_void; + +use wayland_commons::Interface; + +use NewResource; + +#[cfg(feature = "native_lib")] +use wayland_sys::server::*; + +/// Callback function called when a global is instanciated by a client +/// +/// Arguments are: +/// +/// - handle to the eventloop +/// - implementation data you provided to `register_global` +/// - the newly instanciated global +pub type GlobalImplementation = fn(&mut ID, NewResource, u32); + +/// 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 { + #[cfg(feature = "native_lib")] ptr: *mut wl_global, + #[cfg(feature = "native_lib")] data: *mut (GlobalImplementation, ID), +} + +impl Global { + #[cfg(feature = "native_lib")] + pub(crate) unsafe fn create( + ptr: *mut wl_global, + data: Box<(GlobalImplementation, ID)>, + ) -> Global { + Global { + 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 data = &mut *(data as *mut (GlobalImplementation, ID)); + let cb = data.0; + let idata = &mut data.1; + 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); + cb(idata, resource, version) + }); + 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 75e775e3cea..a2b923e536e 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -7,7 +7,14 @@ extern crate wayland_commons; #[macro_use] extern crate wayland_sys; +mod display; +mod event_loop; +mod globals; mod resource; + +pub use display::Display; +pub use globals::{Global, GlobalImplementation}; +pub use event_loop::{EventLoop, LoopSignal, LoopToken}; pub use resource::{NewResource, Resource}; struct Client; diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index 1eb38b462fc..0537a82a33c 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -1,5 +1,7 @@ use wayland_commons::{Implementation, Interface, MessageGroup}; +use LoopToken; + #[cfg(feature = "native_lib")] use wayland_sys::server::*; @@ -124,10 +126,42 @@ pub struct NewResource { } impl NewResource { - pub fn implement( + /// Implement this resource using given function, destructor, and implementation data. + fn implement( self, idata: ID, - implementation: Implementation, I::Events, ID>, + implementation: Implementation, I::Requests, ID>, + destructor: Option, (), ID>>, + ) -> Resource { + unsafe { self.implement_inner(idata, 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. + /// + /// ** Unsafety ** + /// + /// This function is unsafe if you create several wayland event loops and do not + /// provide a token to the right one. + pub unsafe fn implement_nonsend( + self, + idata: ID, + implementation: Implementation, I::Requests, ID>, + destructor: Option, (), ID>>, + token: &LoopToken, + ) -> Resource { + let _ = token; + self.implement_inner(idata, implementation, destructor) + } + + unsafe fn implement_inner( + self, + idata: ID, + implementation: Implementation, I::Requests, ID>, destructor: Option, (), ID>>, ) -> Resource { #[cfg(not(feature = "native_lib"))] @@ -143,17 +177,15 @@ impl NewResource { )); let internal = new_user_data.internal.clone(); - unsafe { - 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::) - ); - } + 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, @@ -194,7 +226,7 @@ mod native_machinery { impl ResourceUserData { pub(crate) fn new( idata: ID, - implem: Implementation, I::Events, ID>, + implem: Implementation, I::Requests, ID>, destructor: Option, (), ID>>, ) -> ResourceUserData { ResourceUserData { @@ -222,7 +254,7 @@ mod native_machinery { // parse the message: let msg = I::Requests::from_raw_c(resource as *mut _, opcode, args)?; let must_destroy = msg.is_destructor(); - // create the proxy object + // 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); From df6ef54e46edb067c8aa30d8b53976638b21deb8 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Wed, 28 Mar 2018 12:49:47 +0200 Subject: [PATCH 19/60] sys: ListernerWithUserData --- wayland-sys/src/server.rs | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/wayland-sys/src/server.rs b/wayland-sys/src/server.rs index 9fc05d2cdd0..e99852f5e74 100644 --- a/wayland-sys/src/server.rs +++ b/wayland-sys/src/server.rs @@ -65,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) -> (), @@ -181,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( @@ -242,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) { @@ -256,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); + } } From e924dbebd0b4e30306799d4b8222ef4557aec756 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Thu, 29 Mar 2018 11:22:55 +0200 Subject: [PATCH 20/60] server: client tracking machinnery --- wayland-server/src/client.rs | 105 +++++++++++++++++++++++++++++++++ wayland-server/src/display.rs | 20 ++++++- wayland-server/src/lib.rs | 4 +- wayland-server/src/resource.rs | 50 +++++++++++++++- 4 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 wayland-server/src/client.rs diff --git a/wayland-server/src/client.rs b/wayland-server/src/client.rs new file mode 100644 index 00000000000..0a59c7ebb3e --- /dev/null +++ b/wayland-server/src/client.rs @@ -0,0 +1,105 @@ +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::*; + +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()), + } + } +} + +pub struct Client { + internal: Arc, + #[cfg(feature = "native_lib")] ptr: *mut wl_client, +} + +impl Client { + #[cfg(feature = "native_lib")] + 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")] + pub fn c_ptr(&self) -> *mut wl_client { + self.ptr + } + + pub fn alive(&self) -> bool { + self.internal.alive.load(Ordering::Acquire) + } + + pub fn set_user_data(&self, data: *mut ()) { + self.internal.user_data.store(data, Ordering::Release); + } + + pub fn get_user_data(&self) -> *mut () { + self.internal.user_data.load(Ordering::Acquire) + } + + 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 f631d4bc23c..36ab7c8bf82 100644 --- a/wayland-server/src/display.rs +++ b/wayland-server/src/display.rs @@ -1,12 +1,13 @@ #[cfg(feature = "native_lib")] use std::ffi::{CString, OsString}; +use std::os::raw::c_void; #[cfg(feature = "native_lib")] use std::os::unix::ffi::OsStringExt; use std::sync::Arc; use wayland_commons::Interface; -use {EventLoop, Global, GlobalImplementation, LoopToken}; +use {Client, EventLoop, Global, GlobalImplementation, LoopToken}; use globals::global_bind; #[cfg(feature = "native_lib")] @@ -44,6 +45,17 @@ impl 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) }; @@ -85,3 +97,9 @@ impl Display { } } } + +#[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); +} diff --git a/wayland-server/src/lib.rs b/wayland-server/src/lib.rs index a2b923e536e..d0bbca7915f 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -7,18 +7,18 @@ extern crate wayland_commons; #[macro_use] extern crate wayland_sys; +mod client; mod display; mod event_loop; mod globals; mod resource; +pub use client::Client; pub use display::Display; pub use globals::{Global, GlobalImplementation}; pub use event_loop::{EventLoop, LoopSignal, LoopToken}; pub use resource::{NewResource, Resource}; -struct Client; - #[cfg(feature = "native_lib")] pub use generated::c_api as protocol; diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index 0537a82a33c..348e9ef5c65 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -1,6 +1,6 @@ use wayland_commons::{Implementation, Interface, MessageGroup}; -use LoopToken; +use {Client, LoopToken}; #[cfg(feature = "native_lib")] use wayland_sys::server::*; @@ -89,6 +89,52 @@ impl Resource { } } + 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); + } + } + } + + 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() + } + } + } + + 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")] pub fn c_ptr(&self) -> *mut wl_resource { self.ptr @@ -127,7 +173,7 @@ pub struct NewResource { impl NewResource { /// Implement this resource using given function, destructor, and implementation data. - fn implement( + pub fn implement( self, idata: ID, implementation: Implementation, I::Requests, ID>, From e93620c33792ec916fd525e20c6db8e2e0428e8e Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 2 Apr 2018 16:12:03 +0200 Subject: [PATCH 21/60] Make Implementation a trait --- wayland-client/examples/list_globals.rs | 2 +- wayland-client/src/proxy.rs | 76 +++++++--------- wayland-commons/src/lib.rs | 13 ++- wayland-server/src/resource.rs | 113 +++++++++++------------- 4 files changed, 95 insertions(+), 109 deletions(-) diff --git a/wayland-client/examples/list_globals.rs b/wayland-client/examples/list_globals.rs index 471282bfd8f..7e33bd8ea05 100644 --- a/wayland-client/examples/list_globals.rs +++ b/wayland-client/examples/list_globals.rs @@ -11,7 +11,7 @@ fn main() { let registry = display .get_registry() .unwrap() - .implement((), |_, evt, _| match evt { + .implement(|evt, _| match evt { RegistryEvents::Global { name, interface, diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs index 8d8d41eee67..63b793fbb7e 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -138,7 +138,7 @@ impl Proxy { }; 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; + as *mut self::native_machinery::ProxyUserData; Some((*user_data).internal.clone()) } else { None @@ -237,17 +237,16 @@ pub struct NewProxy { impl NewProxy { /// Implement this proxy using given function and implementation data. - pub fn implement( - self, - idata: ID, - implementation: Implementation, I::Events, ID>, - ) -> Proxy { - unsafe { self.implement_inner(idata, implementation) } + pub fn implement(self, implementation: Impl) -> Proxy + where + Impl: Implementation, I::Events> + Send + 'static, + { + unsafe { self.implement_inner(implementation) } } /// Implement this proxy using given function and implementation data. /// - /// This method allows the implementation data to not be `Send`, but requires for + /// 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. @@ -258,26 +257,23 @@ impl NewProxy { /// old queue is being dispatched from an other thread. /// /// To ensure safety, see `Proxy::make_wrapper`. - pub unsafe fn implement_nonsend( - self, - idata: ID, - implementation: Implementation, I::Events, ID>, - queue: &QueueToken, - ) -> Proxy { + pub unsafe fn implement_nonsend(self, implementation: Impl, queue: &QueueToken) -> Proxy + where + Impl: Implementation, I::Events> + 'static, + { #[cfg(not(feature = "native_lib"))] {} #[cfg(feature = "native_lib")] { queue.assign_proxy(self.c_ptr()); - self.implement_inner(idata, implementation) + self.implement_inner(implementation) } } - unsafe fn implement_inner( - self, - idata: ID, - implementation: Implementation, I::Events, ID>, - ) -> Proxy { + unsafe fn implement_inner(self, implementation: Impl) -> Proxy + where + Impl: Implementation, I::Events> + 'static, + { #[cfg(not(feature = "native_lib"))] { unimplemented!() @@ -286,17 +282,14 @@ impl NewProxy { { use wayland_sys::client::*; - let new_user_data = Box::new(self::native_machinery::ProxyUserData::new( - idata, - implementation, - )); + 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::, + self::native_machinery::proxy_dispatcher::, &::wayland_sys::RUST_MANAGED as *const _ as *const _, Box::into_raw(new_user_data) as *mut _ ); @@ -329,7 +322,6 @@ mod native_machinery { use wayland_sys::common::*; use wayland_sys::client::*; - use std::any::Any; use std::sync::Arc; use std::sync::atomic::Ordering; use std::os::raw::{c_int, c_void}; @@ -338,24 +330,24 @@ mod native_machinery { use wayland_commons::{Implementation, Interface, MessageGroup}; - pub(crate) struct ProxyUserData { + pub(crate) struct ProxyUserData { pub(crate) internal: Arc, - implem: Option>, + implem: Option, I::Events>>>, } - impl ProxyUserData { - pub(crate) fn new( - idata: ID, - implem: Implementation, I::Events, ID>, - ) -> ProxyUserData { + impl ProxyUserData { + pub(crate) fn new(implem: Impl) -> ProxyUserData + where + Impl: Implementation, I::Events> + 'static, + { ProxyUserData { internal: Arc::new(super::ProxyInternal::new()), - implem: Some(Box::new((implem, idata)) as Box), + implem: Some(Box::new(implem)), } } } - pub(crate) unsafe extern "C" fn proxy_dispatcher( + pub(crate) unsafe extern "C" fn proxy_dispatcher( _implem: *const c_void, proxy: *mut c_void, opcode: u32, @@ -378,23 +370,17 @@ mod native_machinery { // 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() - .downcast_mut::<(Implementation, I::Events, ID>, ID)>() - .unwrap(); - let &mut (ref implem_func, ref mut idata) = implem; + 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_func(proxy_obj, msg, idata); + implem.receive(msg, proxy_obj); } if must_destroy { // final cleanup - let _ = Box::from_raw(user_data as *mut ProxyUserData); + let _ = Box::from_raw(user_data as *mut ProxyUserData); ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy, proxy); } Ok(()) diff --git a/wayland-commons/src/lib.rs b/wayland-commons/src/lib.rs index a91d9535240..38023cfaa2f 100644 --- a/wayland-commons/src/lib.rs +++ b/wayland-commons/src/lib.rs @@ -26,7 +26,18 @@ pub trait Interface: 'static { fn c_interface() -> *const ::syscom::wl_interface; } -pub type Implementation = fn(Meta, M, &mut ID); +pub trait Implementation { + fn receive(&mut self, msg: Msg, meta: Meta); +} + +impl Implementation for F +where + F: FnMut(Msg, Meta), +{ + fn receive(&mut self, msg: Msg, meta: Meta) { + (self)(msg, meta) + } +} pub struct AnonymousObject; diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index 348e9ef5c65..14e0fe548f3 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -153,7 +153,7 @@ impl Resource { }; 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; + as *mut self::native_machinery::ResourceUserData; Some((*user_data).internal.clone()) } else { None @@ -173,13 +173,12 @@ pub struct NewResource { impl NewResource { /// Implement this resource using given function, destructor, and implementation data. - pub fn implement( - self, - idata: ID, - implementation: Implementation, I::Requests, ID>, - destructor: Option, (), ID>>, - ) -> Resource { - unsafe { self.implement_inner(idata, implementation, destructor) } + pub fn implement(self, implementation: Impl, destructor: Option) -> Resource + where + Impl: Implementation, I::Requests> + Send + 'static, + Dest: FnMut(Resource, Box, I::Requests>>) + Send + 'static, + { + unsafe { self.implement_inner(implementation, destructor) } } /// Implement this resource using given function and implementation data. @@ -193,23 +192,25 @@ impl NewResource { /// /// This function is unsafe if you create several wayland event loops and do not /// provide a token to the right one. - pub unsafe fn implement_nonsend( + pub unsafe fn implement_nonsend( self, - idata: ID, - implementation: Implementation, I::Requests, ID>, - destructor: Option, (), ID>>, + implementation: Impl, + destructor: Option, token: &LoopToken, - ) -> Resource { + ) -> Resource + where + Impl: Implementation, I::Requests> + 'static, + Dest: FnMut(Resource, Box, I::Requests>>) + 'static, + { let _ = token; - self.implement_inner(idata, implementation, destructor) + self.implement_inner(implementation, destructor) } - unsafe fn implement_inner( - self, - idata: ID, - implementation: Implementation, I::Requests, ID>, - destructor: Option, (), ID>>, - ) -> Resource { + unsafe fn implement_inner(self, implementation: Impl, destructor: Option) -> Resource + where + Impl: Implementation, I::Requests> + 'static, + Dest: FnMut(Resource, Box, I::Requests>>) + 'static, + { #[cfg(not(feature = "native_lib"))] { unimplemented!() @@ -217,7 +218,6 @@ impl NewResource { #[cfg(feature = "native_lib")] { let new_user_data = Box::new(self::native_machinery::ResourceUserData::new( - idata, implementation, destructor, )); @@ -227,10 +227,10 @@ impl NewResource { WAYLAND_SERVER_HANDLE, wl_resource_set_dispatcher, self.ptr, - self::native_machinery::resource_dispatcher::, + 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::) + Some(self::native_machinery::resource_destroy::) ); Resource { @@ -255,7 +255,6 @@ mod native_machinery { use wayland_sys::common::*; use wayland_sys::server::*; - use std::any::Any; use std::sync::Arc; use std::sync::atomic::Ordering; use std::os::raw::{c_int, c_void}; @@ -264,25 +263,32 @@ mod native_machinery { use wayland_commons::{Implementation, Interface, MessageGroup}; - pub(crate) struct ResourceUserData { + pub(crate) struct ResourceUserData { + _i: ::std::marker::PhantomData<*const I>, pub(crate) internal: Arc, - implem: Option>, + implem: Option< + ( + Box, I::Requests>>, + Option, Box, I::Requests>>)>>, + ), + >, } - impl ResourceUserData { - pub(crate) fn new( - idata: ID, - implem: Implementation, I::Requests, ID>, - destructor: Option, (), ID>>, - ) -> ResourceUserData { + impl ResourceUserData { + pub(crate) fn new(implem: Impl, destructor: Option) -> ResourceUserData + where + Impl: Implementation, I::Requests> + 'static, + Dest: FnMut(Resource, Box, I::Requests>>) + 'static, + { ResourceUserData { + _i: ::std::marker::PhantomData, internal: Arc::new(super::ResourceInternal::new()), - implem: Some(Box::new((implem, idata, destructor)) as Box), + implem: Some((Box::new(implem), destructor.map(|d| Box::new(d) as Box<_>))), } } } - pub(crate) unsafe extern "C" fn resource_dispatcher( + pub(crate) unsafe extern "C" fn resource_dispatcher( _implem: *const c_void, resource: *mut c_void, opcode: u32, @@ -305,23 +311,14 @@ mod native_machinery { // 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() - .downcast_mut::<( - Implementation, I::Requests, ID>, - ID, - Option, (), ID>>, - )>() - .unwrap(); - let &mut (ref implem_func, ref mut idata, _) = implem; + 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(resource_obj, msg, idata); + implem_func.receive(msg, resource_obj); } if must_destroy { // final cleanup @@ -347,28 +344,20 @@ mod native_machinery { } } - pub(crate) unsafe extern "C" fn resource_destroy(resource: *mut wl_resource) { + 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); + 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() - .downcast_mut::<( - Implementation, I::Events, ID>, - ID, - Option, (), ID>>, - )>() - .unwrap(); - let &mut (_, ref mut idata, ref destructor) = implem; - if let &Some(dest_func) = destructor { + 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, (), idata); + dest_func(resource_obj, retrieved_implem); } }); From a8bdf3576d54c2c4c5e01653738e9333dd66edf0 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 2 Apr 2018 16:30:24 +0200 Subject: [PATCH 22/60] server: use Implementation for globals --- wayland-server/src/display.rs | 21 ++++++++++++--------- wayland-server/src/globals.rs | 31 +++++++++++-------------------- wayland-server/src/lib.rs | 2 +- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/wayland-server/src/display.rs b/wayland-server/src/display.rs index 36ab7c8bf82..f9fcbda58b4 100644 --- a/wayland-server/src/display.rs +++ b/wayland-server/src/display.rs @@ -5,9 +5,9 @@ use std::os::raw::c_void; use std::os::unix::ffi::OsStringExt; use std::sync::Arc; -use wayland_commons::Interface; +use wayland_commons::{Implementation, Interface}; -use {Client, EventLoop, Global, GlobalImplementation, LoopToken}; +use {Client, EventLoop, Global, LoopToken, NewResource}; use globals::global_bind; #[cfg(feature = "native_lib")] @@ -63,13 +63,15 @@ impl Display { (display, evq) } - pub fn create_global( + pub fn create_global( &mut self, token: &LoopToken, version: u32, - implementation: GlobalImplementation, - idata: ID, - ) -> Global { + implementation: Impl, + ) -> Global + where + Impl: Implementation, u32> + 'static, + { let token_inner = token .inner .inner @@ -80,7 +82,8 @@ impl Display { "Display::create_global requires the token associated with the display event loop." ); - let data = Box::new((implementation, idata)); + let data = Box::new(Box::new(implementation) + as Box, u32>>); unsafe { let ptr = ffi_dispatch!( @@ -89,8 +92,8 @@ impl Display { self.inner.ptr, I::c_interface(), version as i32, - &*data as *const (GlobalImplementation, ID) as *mut _, - global_bind:: + &*data as *const Box<_> as *mut _, + global_bind:: ); Global::create(ptr, data) diff --git a/wayland-server/src/globals.rs b/wayland-server/src/globals.rs index dbb36854bce..508697f44e7 100644 --- a/wayland-server/src/globals.rs +++ b/wayland-server/src/globals.rs @@ -1,21 +1,12 @@ use std::os::raw::c_void; -use wayland_commons::Interface; +use wayland_commons::{Implementation, Interface}; use NewResource; #[cfg(feature = "native_lib")] use wayland_sys::server::*; -/// Callback function called when a global is instanciated by a client -/// -/// Arguments are: -/// -/// - handle to the eventloop -/// - implementation data you provided to `register_global` -/// - the newly instanciated global -pub type GlobalImplementation = fn(&mut ID, NewResource, u32); - /// A handle to a global object /// /// This is given to you when you register a global to the event loop. @@ -24,18 +15,20 @@ pub type GlobalImplementation = fn(&mut ID, NewResource, u3 /// /// If you know you will never destroy this global, you can let this /// handle go out of scope. -pub struct Global { +pub struct Global { + _i: ::std::marker::PhantomData<*const I>, #[cfg(feature = "native_lib")] ptr: *mut wl_global, - #[cfg(feature = "native_lib")] data: *mut (GlobalImplementation, ID), + #[cfg(feature = "native_lib")] data: *mut Box, u32>>, } -impl Global { +impl Global { #[cfg(feature = "native_lib")] pub(crate) unsafe fn create( ptr: *mut wl_global, - data: Box<(GlobalImplementation, ID)>, - ) -> Global { + data: Box, u32>>>, + ) -> Global { Global { + _i: ::std::marker::PhantomData, ptr: ptr, data: Box::into_raw(data), } @@ -56,7 +49,7 @@ impl Global { } } -pub(crate) unsafe extern "C" fn global_bind( +pub(crate) unsafe extern "C" fn global_bind( client: *mut wl_client, data: *mut c_void, version: u32, @@ -64,9 +57,7 @@ pub(crate) unsafe extern "C" fn global_bind( ) { // safety of this function is the same as dispatch_func let ret = ::std::panic::catch_unwind(move || { - let data = &mut *(data as *mut (GlobalImplementation, ID)); - let cb = data.0; - let idata = &mut data.1; + let implem = &mut *(data as *mut Box, u32>>); let ptr = ffi_dispatch!( WAYLAND_SERVER_HANDLE, wl_resource_create, @@ -76,7 +67,7 @@ pub(crate) unsafe extern "C" fn global_bind( id ); let resource = NewResource::from_c_ptr(ptr as *mut wl_resource); - cb(idata, resource, version) + implem.receive(version, resource); }); match ret { Ok(()) => (), // all went well diff --git a/wayland-server/src/lib.rs b/wayland-server/src/lib.rs index d0bbca7915f..011d5a9d18a 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -15,7 +15,7 @@ mod resource; pub use client::Client; pub use display::Display; -pub use globals::{Global, GlobalImplementation}; +pub use globals::Global; pub use event_loop::{EventLoop, LoopSignal, LoopToken}; pub use resource::{NewResource, Resource}; From 94de054f742289a8980e377a5b08e91024b3d5d9 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 2 Apr 2018 16:39:37 +0200 Subject: [PATCH 23/60] server: listening socket management --- wayland-server/src/display.rs | 193 +++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 5 deletions(-) diff --git a/wayland-server/src/display.rs b/wayland-server/src/display.rs index f9fcbda58b4..642e947340d 100644 --- a/wayland-server/src/display.rs +++ b/wayland-server/src/display.rs @@ -1,8 +1,11 @@ -#[cfg(feature = "native_lib")] -use std::ffi::{CString, OsString}; +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; -#[cfg(feature = "native_lib")] -use std::os::unix::ffi::OsStringExt; +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}; @@ -99,10 +102,190 @@ impl Display { 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) { +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 + /// + /// Wayland clients will be able to connect to your compositor from this socket. + /// + /// Socket will be created in the directory specified by the environment variable + /// `XDG_RUNTIME_DIR`. + /// + /// If a name is provided, it is used. Otherwise, if `WAYLAND_DISPLAY` environment + /// variable is set, its contents are used as socket name. Otherwise, `wayland-0` is used. + /// + /// Errors if `name` contains an interior null, or if `XDG_RUNTIME_DIR` is not set, + /// or if specified could not be bound (either it is already used or the compositor + /// does not have the rights to create it). + pub fn add_socket(&mut self, name: Option) -> IoResult<()> + where + S: AsRef, + { + #[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(()) + } + } + } + + /// Add an automatically named listening socket to this display + /// + /// Wayland clients will be able to connect to your compositor from this socket. + /// + /// Socket will be created in the directory specified by the environment variable + /// `XDG_RUNTIME_DIR`. The directory is scanned for any name in the form `wayland-$d` with + /// `0 <= $d < 32` and the first available one is used. + /// + /// Errors if `XDG_RUNTIME_DIR` is not set, or all 32 names are already in use. + pub fn add_socket_auto(&mut self) -> IoResult { + #[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(), + )) + } + } + } + + /// Add existing listening socket to this display + /// + /// Wayland clients will be able to connect to your compositor from this socket. + /// + /// The existing socket fd must already be created, opened, and locked. + /// The fd must be properly set to CLOEXEC and bound to a socket file + /// with both bind() and listen() already called. An error is returned + /// otherwise. + pub fn add_socket_from(&mut self, socket: T) -> IoResult<()> + where + T: IntoRawFd, + { + unsafe { self.add_socket_fd(socket.into_raw_fd()) } + } + + /// Add existing listening socket to this display from a raw file descriptor + /// + /// Wayland clients will be able to connect to your compositor from this socket. + /// + /// The library takes ownership of the provided socket if this method returns + /// successfully. + /// + /// The existing socket fd must already be created, opened, and locked. + /// The fd must be properly set to CLOEXEC and bound to a socket file + /// with both bind() and listen() already called. An error is returned + /// otherwise. + pub unsafe fn add_socket_fd(&mut self, fd: RawFd) -> IoResult<()> { + #[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")) + } + } + } +} + +fn get_runtime_dir() -> IoResult { + match env::var_os("XDG_RUNTIME_DIR") { + Some(s) => Ok(s.into()), + None => Err(IoError::new( + ErrorKind::NotFound, + "XDG_RUNTIME_DIR env variable is not set", + )), + } +} From 1b3c81d2d710d753840fce3d8401bc8458116b6e Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 2 Apr 2018 23:14:37 +0200 Subject: [PATCH 24/60] server: event sources --- wayland-commons/Cargo.toml | 1 + wayland-commons/src/lib.rs | 23 ++- wayland-server/Cargo.toml | 1 + wayland-server/src/event_loop.rs | 162 ++++++++++++++++++ wayland-server/src/lib.rs | 3 + wayland-server/src/sources.rs | 280 +++++++++++++++++++++++++++++++ 6 files changed, 468 insertions(+), 2 deletions(-) create mode 100644 wayland-server/src/sources.rs diff --git a/wayland-commons/Cargo.toml b/wayland-commons/Cargo.toml index eddab49724e..d2c33865efe 100644 --- a/wayland-commons/Cargo.toml +++ b/wayland-commons/Cargo.toml @@ -14,6 +14,7 @@ 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"] diff --git a/wayland-commons/src/lib.rs b/wayland-commons/src/lib.rs index 38023cfaa2f..f2998dda5b5 100644 --- a/wayland-commons/src/lib.rs +++ b/wayland-commons/src/lib.rs @@ -1,3 +1,5 @@ +#[macro_use] +extern crate downcast_rs as downcast; #[cfg(feature = "native_lib")] extern crate wayland_sys; #[cfg(feature = "native_lib")] @@ -5,6 +7,8 @@ use wayland_sys::common as syscom; use std::os::raw::c_void; +use downcast::Downcast; + pub trait MessageGroup: Sized { fn is_destructor(&self) -> bool; #[cfg(feature = "native_lib")] @@ -26,19 +30,34 @@ pub trait Interface: 'static { fn c_interface() -> *const ::syscom::wl_interface; } -pub trait Implementation { +pub trait Implementation: Downcast { fn receive(&mut self, msg: Msg, meta: Meta); } +impl_downcast!(Implementation); + impl Implementation for F where - F: FnMut(Msg, Meta), + F: FnMut(Msg, Meta) + 'static, { fn receive(&mut self, msg: Msg, meta: Meta) { (self)(msg, meta) } } +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) + } +} + pub struct AnonymousObject; pub enum NoMessage { diff --git a/wayland-server/Cargo.toml b/wayland-server/Cargo.toml index 477c085d9a2..079232f6b07 100644 --- a/wayland-server/Cargo.toml +++ b/wayland-server/Cargo.toml @@ -18,6 +18,7 @@ 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" libc = "0.2" +nix = "0.10" [build-dependencies] wayland-scanner = { version = "0.20.0", path = "../wayland-scanner" } diff --git a/wayland-server/src/event_loop.rs b/wayland-server/src/event_loop.rs index dbfac4c836b..2fb7e762982 100644 --- a/wayland-server/src/event_loop.rs +++ b/wayland-server/src/event_loop.rs @@ -1,9 +1,15 @@ +use std::cell::RefCell; use std::io::{Error as IoError, Result as IoResult}; +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; +use wayland_commons::{downcast_impl, Implementation}; + use display::DisplayInner; +use {FdEvent, FdInterest, IdleSource, SignalEvent, Source, TimerEvent}; #[cfg(feature = "native_lib")] use wayland_sys::server::*; @@ -34,6 +40,24 @@ impl LoopSignal { } impl EventLoop { + 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)), + } + } + } + #[cfg(feature = "native_lib")] pub(crate) unsafe fn display_new(disp_inner: Arc, ptr: *mut wl_event_loop) -> EventLoop { EventLoop { @@ -122,6 +146,144 @@ impl EventLoop { } } +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 implementation will be called whenever these capabilities are + /// satisfied, during the dispatching of this event loop. + 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.inner.wlevl, + fd, + interest.bits(), + ::sources::event_source_fd_dispatcher, + &*data as *const _ as *mut c_void + ) + }; + if ret.is_null() { + Err(( + IoError::last_os_error(), + *(downcast_impl(*data).map_err(|_| ()).unwrap()), + )) + } else { + Ok(Source::make(ret, data)) + } + } + + /// Add a timer event source to this event loop + /// + /// It is a countdown, which can be reset using the struct + /// returned by this function. When the countdown reaches 0, + /// the implementation is called in the dispatching of + /// this event loop. + pub fn add_timer_event_source( + &self, + implementation: Impl, + ) -> Result, (IoError, Impl)> + where + Impl: Implementation<(), TimerEvent> + 'static, + { + let data = Box::new(Box::new(implementation) + as Box>); + let ret = unsafe { + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_event_loop_add_timer, + self.inner.wlevl, + ::sources::event_source_timer_dispatcher, + &*data as *const _ as *mut c_void + ) + }; + if ret.is_null() { + Err(( + IoError::last_os_error(), + *(downcast_impl(*data).map_err(|_| ()).unwrap()), + )) + } else { + 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 implementation whenever + /// the program receives this signal. Calls are made during the + /// dispatching of this event loop. + pub fn add_signal_event_source( + &self, + signal: ::nix::sys::signal::Signal, + implementation: Impl, + ) -> Result, (IoError, Impl)> + where + Impl: Implementation<(), SignalEvent> + 'static, + { + let data = Box::new(Box::new(implementation) + as Box>); + let ret = unsafe { + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_event_loop_add_signal, + self.inner.wlevl, + signal as c_int, + ::sources::event_source_signal_dispatcher, + &*data as *const _ as *mut c_void + ) + }; + if ret.is_null() { + Err(( + IoError::last_os_error(), + *(downcast_impl(*data).map_err(|_| ()).unwrap()), + )) + } else { + Ok(Source::make(ret, data)) + } + } + + /// Add an idle event source to this event loop + /// + /// This is a kind of "defer this computation for when there is nothing else to do". + /// + /// The provided implementation callback will be called when the event loop has finished + /// 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(&self, implementation: Impl) -> IdleSource + where + Impl: Implementation<(), ()> + 'static, + { + let data = Rc::new(RefCell::new(( + Box::new(implementation) as Box>, + false, + ))); + let ret = unsafe { + ffi_dispatch!( + WAYLAND_SERVER_HANDLE, + wl_event_loop_add_idle, + self.inner.wlevl, + ::sources::event_source_idle_dispatcher, + Rc::into_raw(data.clone()) as *mut _ + ) + }; + IdleSource::make(ret, data) + } +} + impl Drop for EventLoopInner { fn drop(&mut self) { #[cfg(feature = "native_lib")] diff --git a/wayland-server/src/lib.rs b/wayland-server/src/lib.rs index 011d5a9d18a..4f5acb62dc0 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -1,6 +1,7 @@ #[macro_use] extern crate bitflags; extern crate libc; +extern crate nix; extern crate wayland_commons; #[cfg(feature = "native_lib")] @@ -12,12 +13,14 @@ mod display; mod event_loop; mod globals; mod resource; +mod sources; pub use client::Client; pub use display::Display; pub use globals::Global; pub use event_loop::{EventLoop, LoopSignal, LoopToken}; pub use resource::{NewResource, Resource}; +pub use sources::{FdEvent, FdInterest, IdleSource, SignalEvent, Source, TimerEvent}; #[cfg(feature = "native_lib")] pub use generated::c_api as protocol; diff --git a/wayland-server/src/sources.rs b/wayland-server/src/sources.rs new file mode 100644 index 00000000000..15a1b23542e --- /dev/null +++ b/wayland-server/src/sources.rs @@ -0,0 +1,280 @@ +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; + +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), + } + } + + 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; + } +} + +pub enum FdEvent { + Ready { fd: RawFd, mask: FdInterest }, + Error { fd: RawFd, 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 + +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 + +pub struct SignalEvent(::nix::sys::signal::Signal); + +pub 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 +/// +/// 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, + } + } + + 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(); + } + } +} From acb0643a1ff25e6fe168e35a267c7619cc6f5c4c Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Wed, 4 Apr 2018 16:41:27 +0200 Subject: [PATCH 25/60] commons: API docs --- wayland-commons/src/lib.rs | 59 +++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/wayland-commons/src/lib.rs b/wayland-commons/src/lib.rs index f2998dda5b5..e8c75073e89 100644 --- a/wayland-commons/src/lib.rs +++ b/wayland-commons/src/lib.rs @@ -1,3 +1,5 @@ +#![warn(missing_docs)] + #[macro_use] extern crate downcast_rs as downcast; #[cfg(feature = "native_lib")] @@ -9,28 +11,70 @@ 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 Requests: MessageGroup + 'static; + /// Set of events associated to this interface + /// + /// Events are messages from the server to the client type Events: 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); } @@ -45,6 +89,9 @@ where } } +/// Attempt to downcast a boxed `Implementation` trait object. +/// +/// Similar to `Box::::downcast()`. pub fn downcast_impl>( b: Box>, ) -> Result, Box>> { @@ -58,10 +105,14 @@ pub fn downcast_impl>( } } +/// Anonymous interface +/// +/// A special Interface implementation representing an +/// handle to an object for which the interface is not known. pub struct AnonymousObject; -pub enum NoMessage { -} +/// An empty enum representing a MessageGroup with no messages +pub enum NoMessage {} impl Interface for AnonymousObject { type Requests = NoMessage; From b091117bb63b8671b3c5011f6c9eadb7c34639d7 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Wed, 4 Apr 2018 16:41:54 +0200 Subject: [PATCH 26/60] client: API docs --- wayland-client/src/display.rs | 49 +++++++++++++ wayland-client/src/event_queue.rs | 37 ++++++++++ wayland-client/src/lib.rs | 22 +++++- wayland-client/src/proxy.rs | 114 +++++++++++++++++++++++++++++- 4 files changed, 219 insertions(+), 3 deletions(-) diff --git a/wayland-client/src/display.rs b/wayland-client/src/display.rs index 5dd565689e6..c58bfac63cc 100644 --- a/wayland-client/src/display.rs +++ b/wayland-client/src/display.rs @@ -1,5 +1,6 @@ #[cfg(feature = "native_lib")] use std::ffi::{CString, OsString}; +use std::io; #[cfg(feature = "native_lib")] use std::os::unix::ffi::OsStringExt; use std::sync::Arc; @@ -22,6 +23,7 @@ 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, } @@ -55,6 +57,13 @@ impl Drop for DisplayInner { } } +/// A connection to a wayland server +/// +/// 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, } @@ -77,6 +86,15 @@ impl Display { 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"))] { @@ -96,6 +114,12 @@ impl Display { } } + /// 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"))] { @@ -117,6 +141,31 @@ impl Display { } } + /// 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.inner.ptr() + ) + }; + if ret >= 0 { + Ok(ret) + } else { + Err(io::Error::last_os_error()) + } + } + + /// Create a new event queue associated with this wayland connection pub fn create_event_queue(&self) -> EventQueue { #[cfg(not(feature = "native_lib"))] { diff --git a/wayland-client/src/event_queue.rs b/wayland-client/src/event_queue.rs index 1f1a41bf2e3..914145708a3 100644 --- a/wayland-client/src/event_queue.rs +++ b/wayland-client/src/event_queue.rs @@ -13,11 +13,45 @@ struct EventQueueInner { inner: Arc, } +/// An event queue for protocol messages +/// +/// 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. +/// +/// 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. +/// +/// 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. +/// +/// 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!"); +/// } +/// # } +/// ``` pub struct EventQueue { // 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, } @@ -148,6 +182,9 @@ impl EventQueue { } } + /// 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(), diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index 6823a168ce4..196b7596779 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -1,3 +1,5 @@ +#![warn(missing_docs)] + #[macro_use] extern crate bitflags; extern crate libc; @@ -14,12 +16,28 @@ mod proxy; pub use proxy::{NewProxy, Proxy}; pub use display::Display; pub use event_queue::{EventQueue, QueueToken}; -#[cfg(feature = "native_lib")] -pub use generated::c_api as protocol; + +/// Re-export of wayland-commons +/// +/// Common traits and functions to work with wayland objects +pub mod commons { + pub use wayland_commons::*; +} #[cfg(feature = "native_lib")] +/// C-associated types +/// +/// Required for plugging wayland-server 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,client}; +} + +/// Generated interfaces for the core wayland protocol +pub mod protocol { + #[cfg(feature = "native_lib")] + pub use generated::c_api::*; } mod generated { diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs index 63b793fbb7e..eab910e915b 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -22,6 +22,18 @@ impl ProxyInternal { } } +/// 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, @@ -34,6 +46,16 @@ 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::Requests) { #[cfg(not(feature = "native_lib"))] { @@ -64,7 +86,12 @@ impl Proxy { } } - // returns false is external + /// 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"))] { @@ -79,6 +106,15 @@ impl Proxy { } } + /// 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"))] { @@ -92,6 +128,9 @@ impl Proxy { } } + /// 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"))] { @@ -108,10 +147,17 @@ impl Proxy { } #[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, @@ -124,11 +170,32 @@ impl Proxy { } #[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. pub unsafe fn from_c_ptr(ptr: *mut wl_proxy) -> Self { use wayland_sys::client::*; @@ -163,6 +230,16 @@ impl Proxy { } #[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(()); @@ -182,6 +259,16 @@ impl Proxy { }) } + /// 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"))] { @@ -230,6 +317,17 @@ impl Drop for Proxy { } } +/// 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, @@ -304,11 +402,25 @@ impl NewProxy { } #[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, From 62a51e5287f41efda8116999fbd5da3cc1a47048 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Wed, 4 Apr 2018 19:10:51 +0200 Subject: [PATCH 27/60] travis: rework & use codecov --- .travis.yml | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 72b813fdc59..e0bfce45aca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,6 @@ addons: sources: - sourceline: 'ppa:wayland.admin/daily-builds' packages: - - libssl-dev - libwayland-dev rust: @@ -23,6 +22,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: @@ -33,29 +39,37 @@ before_script: - mkdir $(pwd)/socket - export XDG_RUNTIME_DIR="$(pwd)/socket" - | - if [ $TRAVIS_RUST_VERSION = "nightly" ]; then + if [ -n "$BUILD_FMT" ]; then rustup component add rustfmt-preview fi - - which cargo-tarpaulin || cargo install cargo-tarpaulin - - which cargo-install-update || cargo install cargo-update - - cargo install-update -a + - | + if [ -n "$TARPAULIN" ]; then + bash <(curl https://raw.githubusercontent.com/xd009642/tarpaulin/master/travis-install.sh) + fi 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 + 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 @@ -65,6 +79,7 @@ deploy: on: branch: master rust: stable + env: BUILD_DOC=1 notifications: webhooks: From 80585d03f91d14631453da742925770d834e2639 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Wed, 4 Apr 2018 19:55:10 +0200 Subject: [PATCH 28/60] test: force dlopen feature in tests --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3a6a25d3381..e25a49c7506 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,8 @@ 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] From f7c90d34ad987e43b30245fdda4a45660c83cbd6 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Wed, 4 Apr 2018 19:58:31 +0200 Subject: [PATCH 29/60] cargo fmt --- wayland-client/src/display.rs | 8 +------- wayland-client/src/event_queue.rs | 3 ++- wayland-client/src/lib.rs | 2 +- wayland-client/src/proxy.rs | 15 ++++++++++----- wayland-server/src/client.rs | 6 +++--- wayland-server/src/display.rs | 6 +++--- wayland-server/src/event_loop.rs | 11 +++++------ wayland-server/src/globals.rs | 6 ++++-- wayland-server/src/lib.rs | 7 +++++-- wayland-server/src/resource.rs | 12 ++++++++---- wayland-server/src/sources.rs | 8 +++++--- 11 files changed, 47 insertions(+), 37 deletions(-) diff --git a/wayland-client/src/display.rs b/wayland-client/src/display.rs index c58bfac63cc..9f7e15ccd6e 100644 --- a/wayland-client/src/display.rs +++ b/wayland-client/src/display.rs @@ -151,13 +151,7 @@ impl Display { /// /// 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.inner.ptr() - ) - }; + let ret = unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_flush, self.inner.ptr()) }; if ret >= 0 { Ok(ret) } else { diff --git a/wayland-client/src/event_queue.rs b/wayland-client/src/event_queue.rs index 914145708a3..44122c665fa 100644 --- a/wayland-client/src/event_queue.rs +++ b/wayland-client/src/event_queue.rs @@ -9,7 +9,8 @@ use display::DisplayInner; use wayland_sys::client::*; struct EventQueueInner { - #[cfg(feature = "native_lib")] wlevq: Option<*mut wl_event_queue>, + #[cfg(feature = "native_lib")] + wlevq: Option<*mut wl_event_queue>, inner: Arc, } diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index 196b7596779..c1f5da97a78 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -31,7 +31,7 @@ pub mod commons { /// 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,client}; + pub use wayland_sys::{client, common}; } /// Generated interfaces for the core wayland protocol diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs index eab910e915b..31c62d7aafd 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -36,10 +36,14 @@ impl ProxyInternal { /// 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, + #[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 {} @@ -330,7 +334,8 @@ impl Drop for Proxy { /// closures. pub struct NewProxy { _i: ::std::marker::PhantomData<*const I>, - #[cfg(feature = "native_lib")] ptr: *mut wl_proxy, + #[cfg(feature = "native_lib")] + ptr: *mut wl_proxy, } impl NewProxy { diff --git a/wayland-server/src/client.rs b/wayland-server/src/client.rs index 0a59c7ebb3e..5d817635817 100644 --- a/wayland-server/src/client.rs +++ b/wayland-server/src/client.rs @@ -24,7 +24,8 @@ impl ClientInternal { pub struct Client { internal: Arc, - #[cfg(feature = "native_lib")] ptr: *mut wl_client, + #[cfg(feature = "native_lib")] + ptr: *mut wl_client, } impl Client { @@ -90,8 +91,7 @@ impl Client { } 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); + 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); diff --git a/wayland-server/src/display.rs b/wayland-server/src/display.rs index 642e947340d..e221c9324c8 100644 --- a/wayland-server/src/display.rs +++ b/wayland-server/src/display.rs @@ -17,7 +17,8 @@ use globals::global_bind; use wayland_sys::server::*; pub(crate) struct DisplayInner { - #[cfg(feature = "native_lib")] pub(crate) ptr: *mut wl_display, + #[cfg(feature = "native_lib")] + pub(crate) ptr: *mut wl_display, } impl Drop for DisplayInner { @@ -85,8 +86,7 @@ impl Display { "Display::create_global requires the token associated with the display event loop." ); - let data = Box::new(Box::new(implementation) - as Box, u32>>); + let data = Box::new(Box::new(implementation) as Box, u32>>); unsafe { let ptr = ffi_dispatch!( diff --git a/wayland-server/src/event_loop.rs b/wayland-server/src/event_loop.rs index 2fb7e762982..0a1f5e07c8d 100644 --- a/wayland-server/src/event_loop.rs +++ b/wayland-server/src/event_loop.rs @@ -9,13 +9,14 @@ use std::sync::atomic; use wayland_commons::{downcast_impl, Implementation}; use display::DisplayInner; -use {FdEvent, FdInterest, IdleSource, SignalEvent, Source, TimerEvent}; +use sources::{FdEvent, FdInterest, IdleSource, SignalEvent, Source, TimerEvent}; #[cfg(feature = "native_lib")] use wayland_sys::server::*; pub(crate) struct EventLoopInner { - #[cfg(feature = "native_lib")] wlevl: *mut wl_event_loop, + #[cfg(feature = "native_lib")] + wlevl: *mut wl_event_loop, pub(crate) inner: Option>, } @@ -197,8 +198,7 @@ impl LoopToken { where Impl: Implementation<(), TimerEvent> + 'static, { - let data = Box::new(Box::new(implementation) - as Box>); + let data = Box::new(Box::new(implementation) as Box>); let ret = unsafe { ffi_dispatch!( WAYLAND_SERVER_HANDLE, @@ -232,8 +232,7 @@ impl LoopToken { where Impl: Implementation<(), SignalEvent> + 'static, { - let data = Box::new(Box::new(implementation) - as Box>); + let data = Box::new(Box::new(implementation) as Box>); let ret = unsafe { ffi_dispatch!( WAYLAND_SERVER_HANDLE, diff --git a/wayland-server/src/globals.rs b/wayland-server/src/globals.rs index 508697f44e7..b57364d73e2 100644 --- a/wayland-server/src/globals.rs +++ b/wayland-server/src/globals.rs @@ -17,8 +17,10 @@ use wayland_sys::server::*; /// 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>>, + #[cfg(feature = "native_lib")] + ptr: *mut wl_global, + #[cfg(feature = "native_lib")] + data: *mut Box, u32>>, } impl Global { diff --git a/wayland-server/src/lib.rs b/wayland-server/src/lib.rs index 4f5acb62dc0..8685ab042fd 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -13,14 +13,17 @@ mod display; mod event_loop; mod globals; mod resource; -mod sources; +pub mod sources; pub use client::Client; pub use display::Display; pub use globals::Global; pub use event_loop::{EventLoop, LoopSignal, LoopToken}; pub use resource::{NewResource, Resource}; -pub use sources::{FdEvent, FdInterest, IdleSource, SignalEvent, Source, TimerEvent}; + +pub mod commons { + pub use wayland_commons::*; +} #[cfg(feature = "native_lib")] pub use generated::c_api as protocol; diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index 14e0fe548f3..141c6d63405 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -24,9 +24,12 @@ impl ResourceInternal { 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, + #[cfg(not(feature = "native_lib"))] + internal: Arc, + #[cfg(feature = "native_lib")] + internal: Option>, + #[cfg(feature = "native_lib")] + ptr: *mut wl_resource, } impl Resource { @@ -168,7 +171,8 @@ impl Resource { pub struct NewResource { _i: ::std::marker::PhantomData<*const I>, - #[cfg(feature = "native_lib")] ptr: *mut wl_resource, + #[cfg(feature = "native_lib")] + ptr: *mut wl_resource, } impl NewResource { diff --git a/wayland-server/src/sources.rs b/wayland-server/src/sources.rs index 15a1b23542e..9272c315cdd 100644 --- a/wayland-server/src/sources.rs +++ b/wayland-server/src/sources.rs @@ -11,8 +11,10 @@ use wayland_commons::Implementation; 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>, + #[cfg(feature = "native_lib")] + ptr: *mut wl_event_source, + #[cfg(feature = "native_lib")] + data: *mut Box>, } impl Source { @@ -183,7 +185,7 @@ pub(crate) unsafe extern "C" fn event_source_timer_dispatcher(data: *mut c_void) pub struct SignalEvent(::nix::sys::signal::Signal); -pub unsafe extern "C" fn event_source_signal_dispatcher(signal: c_int, data: *mut c_void) -> c_int { +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 || { From 00c59c882f83dea61623947fc1d9a80fcc09723c Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Wed, 4 Apr 2018 20:15:10 +0200 Subject: [PATCH 30/60] client: prepare_read() --- wayland-client/src/event_queue.rs | 95 +++++++++++++++++++++++++++++++ wayland-client/src/lib.rs | 2 +- 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/wayland-client/src/event_queue.rs b/wayland-client/src/event_queue.rs index 44122c665fa..296e2fa087f 100644 --- a/wayland-client/src/event_queue.rs +++ b/wayland-client/src/event_queue.rs @@ -191,6 +191,50 @@ impl EventQueue { inner: self.inner.clone(), } } + + /// Prepare an conccurent read + /// + /// Will declare your intention to read events from the server socket. + /// + /// Will return `None` if there are still some events awaiting dispatch on this EventIterator. + /// In this case, you need to call `dispatch_pending()` before calling this method again. + /// + /// As long as the returned guard is in scope, no events can be dispatched to any event iterator. + /// + /// The guard can then be destroyed by two means: + /// + /// - Calling its `cancel()` method (or letting it go out of scope): the read intention will + /// be cancelled + /// - Calling its `read_events()` method: will block until all existing guards are destroyed + /// by one of these methods, then events will be read and all blocked `read_events()` calls + /// will return. + /// + /// This call will otherwise not block on the server socket if it is empty, and return + /// an io error `WouldBlock` in such cases. + pub fn prepare_read(&self) -> Option { + let ret = unsafe { + match self.inner.wlevq { + Some(evtq) => ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_prepare_read_queue, + self.inner.inner.ptr(), + evtq + ), + None => ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_prepare_read, + self.inner.inner.ptr() + ), + } + }; + if ret >= 0 { + Some(ReadEventsGuard { + inner: self.inner.clone(), + }) + } else { + None + } + } } impl QueueToken { @@ -221,3 +265,54 @@ impl Drop for EventQueueInner { } } } + +/// A guard over a read intention. +/// +/// See `EventQueue::prepare_read()` for details about its use. +pub struct ReadEventsGuard { + inner: Rc, +} + +impl ReadEventsGuard { + /// Read events + /// + /// Reads events from the server socket. If other `ReadEventsGuard` exists, will block + /// until they are all consumed or destroyed. + pub fn read_events(self) -> IoResult { + 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 { + Ok(ret) + } else { + Err(IoError::last_os_error()) + } + } + + /// Cancel the read + /// + /// Will cancel the read intention associated with this guard. Never blocks. + /// + /// Has the same effet as letting the guard go out of scope. + pub fn cancel(self) { + // just run the destructor + } +} + +impl Drop for ReadEventsGuard { + fn drop(&mut self) { + unsafe { + ffi_dispatch!( + WAYLAND_CLIENT_HANDLE, + wl_display_cancel_read, + self.inner.inner.ptr() + ) + } + } +} diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index c1f5da97a78..b36e279b108 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -15,7 +15,7 @@ mod proxy; pub use proxy::{NewProxy, Proxy}; pub use display::Display; -pub use event_queue::{EventQueue, QueueToken}; +pub use event_queue::{EventQueue, QueueToken, ReadEventsGuard}; /// Re-export of wayland-commons /// From 02e81d6893c90f4ebfa93c70991fa3ab1fd12ff7 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Wed, 4 Apr 2018 20:15:28 +0200 Subject: [PATCH 31/60] tests: skeletton --- tests/helpers/mod.rs | 69 ++++++++++++++++++++++++++++++++++++++++++++ tests/skel.rs | 22 +++++++++++++- 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 tests/helpers/mod.rs diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs new file mode 100644 index 00000000000..4d5ee0753dc --- /dev/null +++ b/tests/helpers/mod.rs @@ -0,0 +1,69 @@ +// This module contains helpers functions and types that +// are not test in themselves, but are used by several tests. + +#![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: self::ways::Display, + pub event_loop: self::ways::EventLoop, + pub socket_name: OsString, +} + +impl TestServer { + pub fn new() -> TestServer { + let (mut display, event_loop) = self::ways::Display::new(); + let socket_name = display + .add_socket_auto() + .expect("Failed to create a server socket."); + + TestServer { + display: display, + event_loop: event_loop, + socket_name: socket_name, + } + } + + pub fn answer(&mut self) { + // for some reason, two dispatches are needed + self.event_loop.dispatch(Some(10)).unwrap(); + self.event_loop.dispatch(Some(10)).unwrap(); + self.display.flush_clients(); + } +} + +pub struct TestClient { + pub display: self::wayc::Display, + pub event_queue: self::wayc::EventQueue, +} + +impl TestClient { + pub fn new(socket_name: &OsStr) -> TestClient { + 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, + } + } +} + +pub fn roundtrip(client: &mut TestClient, server: &mut TestServer) { + // send to the server + client.display.flush().unwrap(); + // make it answer messages + server.answer(); + // dispatch all client-side + client.event_queue.dispatch_pending().unwrap(); + client + .event_queue + .prepare_read() + .unwrap() + .read_events() + .unwrap(); + client.event_queue.dispatch_pending().unwrap(); +} diff --git a/tests/skel.rs b/tests/skel.rs index 2002ef17dd6..7abca0e3f8a 100644 --- a/tests/skel.rs +++ b/tests/skel.rs @@ -1,2 +1,22 @@ +mod helpers; + +use helpers::{roundtrip, TestClient, TestServer}; + #[test] -fn it_works() {} +fn skel() { + // Server setup + // + let mut server = TestServer::new(); + + // Client setup + // + let mut client = TestClient::new(&server.socket_name); + + // Some message passing + // + roundtrip(&mut client, &mut server); + + // Final asserts + // + assert!(true); +} From ae096ed617342ca7e90e3f46b7f2fedf0a132977 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Wed, 4 Apr 2018 20:31:09 +0200 Subject: [PATCH 32/60] tests: import all from helpers in skel --- tests/skel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/skel.rs b/tests/skel.rs index 7abca0e3f8a..839e60298f9 100644 --- a/tests/skel.rs +++ b/tests/skel.rs @@ -1,6 +1,6 @@ mod helpers; -use helpers::{roundtrip, TestClient, TestServer}; +use helpers::{roundtrip, wayc, ways, TestClient, TestServer}; #[test] fn skel() { From 8dc32be5a126af2b09a17ea2c1470e14804e847d Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Thu, 5 Apr 2018 15:58:18 +0200 Subject: [PATCH 33/60] tests: server dispatch_idle --- tests/server.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/server.rs 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()); +} From 2c55b3f05fdda7e953ee1c6d57261d7b6beddc85 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Thu, 5 Apr 2018 16:13:02 +0200 Subject: [PATCH 34/60] server: api docs --- wayland-client/src/lib.rs | 2 +- wayland-scanner/src/c_code_gen.rs | 2 +- wayland-server/src/client.rs | 33 +++++++++++ wayland-server/src/display.rs | 23 ++++++++ wayland-server/src/event_loop.rs | 32 ++++++++++- wayland-server/src/lib.rs | 19 ++++++- wayland-server/src/resource.rs | 95 ++++++++++++++++++++++++++++++- wayland-server/src/sources.rs | 49 +++++++++++++++- 8 files changed, 245 insertions(+), 10 deletions(-) diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index b36e279b108..c34f02a6ec5 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -27,7 +27,7 @@ pub mod commons { #[cfg(feature = "native_lib")] /// C-associated types /// -/// Required for plugging wayland-server generated protocols +/// 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; diff --git a/wayland-scanner/src/c_code_gen.rs b/wayland-scanner/src/c_code_gen.rs index fbcd45c3223..af183f2d41f 100644 --- a/wayland-scanner/src/c_code_gen.rs +++ b/wayland-scanner/src/c_code_gen.rs @@ -526,7 +526,7 @@ fn write_client_methods(name: &str, messages: &[Message], out: &mut O) if has_newp { for a in &msg.args { if a.typ == Type::NewId { - if let Some(ref iface) = a.interface { + if a.interface.is_some() { writeln!(out, " Ok(_arg_{}_newproxy)", a.name)?; } } diff --git a/wayland-server/src/client.rs b/wayland-server/src/client.rs index 5d817635817..4c1b0132f11 100644 --- a/wayland-server/src/client.rs +++ b/wayland-server/src/client.rs @@ -22,6 +22,9 @@ impl ClientInternal { } } +/// 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")] @@ -30,6 +33,7 @@ pub struct Client { impl 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!( @@ -67,22 +71,51 @@ impl Client { } #[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 } + /// Check whether this client is still connected to the server pub fn alive(&self) -> bool { self.internal.alive.load(Ordering::Acquire) } + /// 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 + /// + /// 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); } + /// 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 diff --git a/wayland-server/src/display.rs b/wayland-server/src/display.rs index e221c9324c8..5ea1b2ec5fe 100644 --- a/wayland-server/src/display.rs +++ b/wayland-server/src/display.rs @@ -36,12 +36,24 @@ impl Drop for DisplayInner { } } +/// The wayland display +/// +/// 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 { inner: Arc, } 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,) }; @@ -67,6 +79,17 @@ impl Display { (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, diff --git a/wayland-server/src/event_loop.rs b/wayland-server/src/event_loop.rs index 0a1f5e07c8d..72ba22322b0 100644 --- a/wayland-server/src/event_loop.rs +++ b/wayland-server/src/event_loop.rs @@ -20,27 +20,54 @@ pub(crate) struct EventLoopInner { pub(crate) inner: Option>, } +/// An 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. +/// +/// 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. +/// +/// 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, } +/// 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, } +/// An event loop signal +/// +/// 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 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"))] { @@ -70,12 +97,14 @@ impl EventLoop { } } + /// Retrieve a `LoopToken` associated to this event loop pub fn token(&self) -> LoopToken { LoopToken { inner: self.inner.clone(), } } + /// Retrieve a `LoopSignal` associated to this event loop pub fn signal(&self) -> LoopSignal { LoopSignal { inner: self.stop_signal.clone(), @@ -261,7 +290,8 @@ impl LoopToken { /// 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`. + /// 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 Impl: Implementation<(), ()> + 'static, diff --git a/wayland-server/src/lib.rs b/wayland-server/src/lib.rs index 8685ab042fd..66842988688 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -1,3 +1,5 @@ +#![warn(missing_docs)] + #[macro_use] extern crate bitflags; extern crate libc; @@ -21,16 +23,27 @@ pub use globals::Global; pub use event_loop::{EventLoop, LoopSignal, LoopToken}; pub use resource::{NewResource, Resource}; +/// Re-export of wayland-commons +/// +/// Common traits and functions to work with wayland objects pub mod commons { pub use wayland_commons::*; } #[cfg(feature = "native_lib")] -pub use generated::c_api as protocol; - -#[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}; +} + +/// Generated interfaces for the core wayland protocol +pub mod protocol { + #[cfg(feature = "native_lib")] + pub use generated::c_api::*; } mod generated { diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index 141c6d63405..d3f1e9def71 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -22,6 +22,17 @@ impl ResourceInternal { } } +/// 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"))] @@ -33,6 +44,10 @@ pub struct 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::Events) { #[cfg(not(feature = "native_lib"))] { @@ -63,7 +78,12 @@ impl Resource { } } - // returns false if external + /// 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"))] { @@ -79,10 +99,17 @@ impl Resource { } #[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, @@ -92,6 +119,15 @@ impl Resource { } } + /// 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"))] { @@ -105,6 +141,9 @@ impl Resource { } } + /// 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"))] { @@ -120,6 +159,9 @@ impl Resource { } } + /// 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"))] @@ -139,11 +181,32 @@ impl Resource { } #[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. pub unsafe fn from_c_ptr(ptr: *mut wl_resource) -> Self { let is_managed = { ffi_dispatch!( @@ -169,6 +232,17 @@ impl Resource { } } +/// 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")] @@ -246,6 +320,25 @@ impl NewResource { } #[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, diff --git a/wayland-server/src/sources.rs b/wayland-server/src/sources.rs index 9272c315cdd..a824e4c2272 100644 --- a/wayland-server/src/sources.rs +++ b/wayland-server/src/sources.rs @@ -1,3 +1,12 @@ +//! 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; @@ -9,6 +18,15 @@ 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")] @@ -27,6 +45,10 @@ impl Source { } } + /// 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"))] { @@ -55,9 +77,22 @@ bitflags!{ } } +/// An event generated by an FD event source pub enum FdEvent { - Ready { fd: RawFd, mask: FdInterest }, - Error { fd: RawFd, error: IoError }, + /// 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 { @@ -142,6 +177,7 @@ pub(crate) unsafe extern "C" fn event_source_fd_dispatcher(fd: c_int, mask: u32, // Timer event source +/// A timer generated event. pub struct TimerEvent; impl Source { @@ -183,6 +219,9 @@ pub(crate) unsafe extern "C" fn event_source_timer_dispatcher(data: *mut c_void) // 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 { @@ -218,7 +257,7 @@ pub(crate) unsafe extern "C" fn event_source_signal_dispatcher(signal: c_int, da /// Idle event source /// -/// A handle to an 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. @@ -239,6 +278,10 @@ impl IdleSource { } } + /// 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 { From bd92443d99f554d78667347bb123a7b347a6cd76 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Thu, 5 Apr 2018 16:45:23 +0200 Subject: [PATCH 35/60] travis: use containers --- .travis.yml | 20 ++++++++------------ travis_install_wayland.sh | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 12 deletions(-) create mode 100755 travis_install_wayland.sh diff --git a/.travis.yml b/.travis.yml index e0bfce45aca..fe1d73f033b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,14 +4,7 @@ cache: cargo dist: trusty -sudo: required - -addons: - apt: - sources: - - sourceline: 'ppa:wayland.admin/daily-builds' - packages: - - libwayland-dev +sudo: false rust: - 1.20.0 @@ -41,10 +34,13 @@ before_script: - | if [ -n "$BUILD_FMT" ]; then rustup component add rustfmt-preview - fi - - | - if [ -n "$TARPAULIN" ]; then + 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 os: @@ -59,7 +55,7 @@ script: elif [ -n "$BUILD_DOC" ]; then cargo doc --all --no-deps --all-features else - cargo test --all --all-features + env LD_LIBRARY_PATH=~/install/lib:$LD_LIBRARY_PATH cargo test --all --all-features fi after_success: 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 From 09805064d5732790558087dddb04517bbb9d1fdd Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Thu, 5 Apr 2018 19:08:33 +0200 Subject: [PATCH 36/60] travis: actually we need sudo for coverage --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fe1d73f033b..10d4cef8ff0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ cache: cargo dist: trusty -sudo: false +sudo: required rust: - 1.20.0 From 914d6d0ad7b393d8cf0b17e65f8847137b84ae70 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 6 Apr 2018 08:55:00 +0200 Subject: [PATCH 37/60] commons: crate doc --- wayland-commons/src/lib.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/wayland-commons/src/lib.rs b/wayland-commons/src/lib.rs index e8c75073e89..c4492179ffb 100644 --- a/wayland-commons/src/lib.rs +++ b/wayland-commons/src/lib.rs @@ -1,3 +1,18 @@ +//! 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] From 608d80344a8342367fad461ff1786a341f31a1fd Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 6 Apr 2018 11:46:01 +0200 Subject: [PATCH 38/60] client: crate-level doc --- wayland-client/src/cursor.rs | 1 + wayland-client/src/egl.rs | 1 + wayland-client/src/event_queue.rs | 4 + wayland-client/src/lib.rs | 128 ++++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+) create mode 100644 wayland-client/src/cursor.rs create mode 100644 wayland-client/src/egl.rs diff --git a/wayland-client/src/cursor.rs b/wayland-client/src/cursor.rs new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/wayland-client/src/cursor.rs @@ -0,0 +1 @@ + diff --git a/wayland-client/src/egl.rs b/wayland-client/src/egl.rs new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/wayland-client/src/egl.rs @@ -0,0 +1 @@ + diff --git a/wayland-client/src/event_queue.rs b/wayland-client/src/event_queue.rs index 296e2fa087f..4d46d2edde4 100644 --- a/wayland-client/src/event_queue.rs +++ b/wayland-client/src/event_queue.rs @@ -42,6 +42,10 @@ struct EventQueueInner { /// } /// # } /// ``` +/// +/// 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 { // EventQueue is *not* Send inner: Rc, diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index c34f02a6ec5..22ee0a69736 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -1,3 +1,125 @@ +//! Client-side Wayland connector +//! +//! ## Overview +//! +//! This crate provides the interfaces and machinnery 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 be-directionnal, 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 `Rroxy` 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::Events>` +//! trait, where `I` is the interface of the considered object: +//! +//! ``` +//! // Example implementation for the wl_surface interface +//! use wayland_client::Proxy; +//! use wayland_client::protocol::wl_surface; +//! use wayland_client::commons::Implementation; +//! +//! struct MyImpl { +//! // ... +//! } +//! +//! impl Implementation, wl_surface::Events> for MyImpl { +//! fn receive(&mut self, msg: wl_surface::Events, proxy: Proxy) { +//! // process the message... +//! } +//! } +//! # fn main() {} +//! ``` +//! +//! The trait is also automatically implemented for `FnMut(I::Events, Proxy)` closures, +//! so you can use them for simplicity if a full struct would be too cumbersome. +//! +//! ## Event Queues +//! +//! 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. +//! +//! 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 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. +//! +//! 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. +//! +//! ## Dynamic linking with `libwayland-client.so` +//! +//! If you need to gracefully handle the case of a system on which wayland is not installed (by +//! fallbacking to X11 for example), you can do so by activating the `dlopen` cargo feature. +//! +//! When this is done, the library will be loaded a runtime rather than directly linked. And trying +//! to create a `Display` on a system that does not have this library will return a `NoWaylandLib` +//! error. +//! +//! ## Auxiliary libraries +//! +//! Two auxiliary libraries are also available behind cargo features: +//! +//! - 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. +//! +//! 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] @@ -17,6 +139,12 @@ pub use proxy::{NewProxy, Proxy}; pub use display::Display; pub use event_queue::{EventQueue, QueueToken, ReadEventsGuard}; +#[cfg(feature = "cursor")] +pub mod cursor; + +#[cfg(feature = "egl")] +pub mod egl; + /// Re-export of wayland-commons /// /// Common traits and functions to work with wayland objects From bf5adcea3f5c127585eed1531b9a6d62b2f01f8c Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 6 Apr 2018 14:53:17 +0200 Subject: [PATCH 39/60] Doc typos. --- wayland-client/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index 22ee0a69736..0b3424d1187 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -2,7 +2,7 @@ //! //! ## Overview //! -//! This crate provides the interfaces and machinnery to safely create +//! 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. //! @@ -13,7 +13,7 @@ //! //! ## Protocol and messages handling model //! -//! The protocol being be-directionnal, you can send and receive messages. +//! 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. //! @@ -51,7 +51,7 @@ //! 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 `Rroxy` object. +//! 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 From 76ee33b5e914dc8e006af25aa6be4323fd15ac07 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 6 Apr 2018 15:06:31 +0200 Subject: [PATCH 40/60] client: cursor & EGL --- wayland-client/src/cursor.rs | 243 +++++++++++++++++++++++++++++++++++ wayland-client/src/egl.rs | 108 ++++++++++++++++ 2 files changed, 351 insertions(+) diff --git a/wayland-client/src/cursor.rs b/wayland-client/src/cursor.rs index 8b137891791..d1b4a53b4a8 100644 --- a/wayland-client/src/cursor.rs +++ b/wayland-client/src/cursor.rs @@ -1 +1,244 @@ +//! Cursor utilities +//! +//! This module contains bindings to the `libwayland-cursor.so` library. +//! +//! These utilities allows you to laod cursor images in order to match +//! your cursors to the ones of the system. +//! +//! First of all, the function `load_theme` will allow you to load a +//! `CursorTheme`, which represents the full cursor theme. +//! +//! From this theme, you can load a specific `Cursor`, which can each +//! contain several images if the cursor is animated. It provides you +//! with the means of knowing which frame of the animation shoudl be +//! 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; +use std::ffi::{CStr, CString}; +use std::marker::PhantomData; +use std::ops::Deref; +use std::os::raw::c_int; +use std::ptr; +use wayland_sys::cursor::*; + +/// Checks if the wayland-cursor lib is available and can be used +/// +/// Trying to call any function of this module if the lib cannot +/// be used will result in a panic. +pub fn is_available() -> bool { + is_lib_available() +} + +/// Represents a cursor theme loaded from the system. +pub struct CursorTheme { + theme: *mut wl_cursor_theme, +} + +unsafe impl Send for CursorTheme {} + +/// Attempts to load a cursor theme from given name. +/// +/// If no name is given or the requested theme is not found, will +/// load the default theme. +/// +/// Other arguments are the requested size for the cursor images (ex: 16) +/// and a handle to the global `WlShm` object. +/// +/// Panics: +/// +/// - If the `wayland-cursor` lib is not available (see `is_available()` function) +/// 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: &Proxy) -> CursorTheme { + let ptr = if let Some(theme) = name { + let cstr = CString::new(theme).expect("Theme name contained an interior null."); + unsafe { + ffi_dispatch!( + WAYLAND_CURSOR_HANDLE, + wl_cursor_theme_load, + cstr.as_ptr(), + size as c_int, + shm.c_ptr() + ) + } + } else { + unsafe { + ffi_dispatch!( + WAYLAND_CURSOR_HANDLE, + wl_cursor_theme_load, + ptr::null(), + size as c_int, + shm.c_ptr() + ) + } + }; + + assert!( + !ptr.is_null(), + "Memory allocation failure while loading a theme." + ); + + CursorTheme { theme: ptr } +} + +impl CursorTheme { + /// Retrieve a cursor from the theme. + /// + /// Returns `None` if this cursor is not provided by the theme. + /// + /// Panics if the name contains an interior null. + pub fn get_cursor(&self, name: &str) -> Option { + let cstr = CString::new(name).expect("Cursor name contained an interior null."); + let ptr = unsafe { + ffi_dispatch!( + WAYLAND_CURSOR_HANDLE, + wl_cursor_theme_get_cursor, + self.theme, + cstr.as_ptr() + ) + }; + if ptr.is_null() { + None + } else { + Some(Cursor { + _theme: PhantomData, + cursor: ptr, + }) + } + } +} + +impl Drop for CursorTheme { + fn drop(&mut self) { + unsafe { + ffi_dispatch!(WAYLAND_CURSOR_HANDLE, wl_cursor_theme_destroy, self.theme); + } + } +} + +/// A cursor from a theme. Can contain several images if animated. +pub struct Cursor<'a> { + _theme: PhantomData<&'a CursorTheme>, + cursor: *mut wl_cursor, +} + +unsafe impl<'a> Send for Cursor<'a> {} + +impl<'a> Cursor<'a> { + /// Retrieve the name of this cursor. + pub fn name(&self) -> String { + let name = unsafe { CStr::from_ptr((*self.cursor).name) }; + name.to_string_lossy().into_owned() + } + + /// Retrieve the number of images contained in this + /// animated cursor + pub fn image_count(&self) -> usize { + let count = unsafe { (*self.cursor).image_count }; + count as usize + } + + /// Retrieve the image number of cursor animation. + /// + /// Returns the image number of the animation that should be displayed + /// after a given amount of time since the beginning of the animation, + /// in milliseconds. + pub fn frame(&self, duration: u32) -> usize { + let frame = unsafe { + ffi_dispatch!( + WAYLAND_CURSOR_HANDLE, + wl_cursor_frame, + self.cursor, + duration + ) + }; + frame as usize + } + + /// Retrieve the image number and its duration. + /// + /// Same as `frame()`, but also returns the number of milliseconds this + /// frame should still be displayed. + pub fn frame_and_duration(&self, duration: u32) -> (usize, u32) { + let mut out_duration = 0u32; + let frame = unsafe { + ffi_dispatch!( + WAYLAND_CURSOR_HANDLE, + wl_cursor_frame_and_duration, + self.cursor, + duration, + &mut out_duration as *mut u32 + ) + } as usize; + (frame, out_duration) + } + + /// Retrieve a `CursorImageBuffer` containing the given image of an animation. + /// + /// It can be used to be attached to a surface as a classic `WlBuffer`. + /// + /// Returns `None` if the frame is out of bounds. + /// + /// Note: destroying this buffer (using the `destroy` method) will corrupt + /// your theme data, so you might not want to do it. + pub fn frame_buffer(&self, frame: usize) -> Option { + if frame >= self.image_count() { + None + } else { + unsafe { + let image = *(*self.cursor).images.offset(frame as isize); + let ptr = ffi_dispatch!(WAYLAND_CURSOR_HANDLE, wl_cursor_image_get_buffer, image); + let buffer = Proxy::from_c_ptr(ptr); + + Some(CursorImageBuffer { + _cursor: PhantomData, + buffer: buffer, + }) + } + } + } + + /// Retrive the metadate associated with given frame of the animation. + /// + /// The tuple contains: `(width, height, hotspot_x, hotspot_y, delay)` + /// + /// Returns `None` if the frame is out of bounds. + pub fn frame_info(&self, frame: usize) -> Option<(u32, u32, u32, u32, u32)> { + if frame >= self.image_count() { + None + } else { + let image = unsafe { &**(*self.cursor).images.offset(frame as isize) }; + Some(( + image.width, + image.height, + image.hotspot_x, + image.hotspot_y, + image.delay, + )) + } + } +} + +/// 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: Proxy, +} + +unsafe impl<'a> Send for CursorImageBuffer<'a> {} + +impl<'a> Deref for CursorImageBuffer<'a> { + type Target = Proxy; + fn deref(&self) -> &Proxy { + &self.buffer + } +} diff --git a/wayland-client/src/egl.rs b/wayland-client/src/egl.rs index 8b137891791..24476a33a80 100644 --- a/wayland-client/src/egl.rs +++ b/wayland-client/src/egl.rs @@ -1 +1,109 @@ +//! EGL utilities +//! +//! This module contains bindings to the `libwayland-egl.so` library. +//! +//! This library is used to interface with the OpenGL stack, and creating +//! EGL surfaces from a wayland surface. +//! +//! See WlEglSurface documentation for details. +use Proxy; +use protocol::wl_surface::WlSurface; +use std::os::raw::c_void; +use wayland_sys::client::wl_proxy; +use wayland_sys::egl::*; + +/// Checks if the wayland-egl lib is available and can be used +/// +/// Trying to create an `WlEglSurface` while this function returns +/// `false` will result in a panic. +pub fn is_available() -> bool { + is_lib_available() +} + +unsafe impl Send for WlEglSurface {} +unsafe impl Sync for WlEglSurface {} + +/// EGL surface +/// +/// This object is a simple wrapper around a `WlSurface` to add the EGL +/// capabilities. Just use the `ptr` method once this object is created +/// to get the window pointer your OpenGL library is needing to initialize the +/// EGL context (you'll most likely need the display ptr as well, that you can +/// get via the `ptr` method of the `Proxy` trait on the `WlDisplay` object). +pub struct WlEglSurface { + ptr: *mut wl_egl_window, +} + +impl WlEglSurface { + /// Create an EGL surface from a wayland surface + 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 + /// + /// This function is unsafe because `surface` must be a valid wl_surface pointer + pub unsafe fn new_from_raw(surface: *mut wl_proxy, width: i32, height: i32) -> WlEglSurface { + let ptr = ffi_dispatch!( + WAYLAND_EGL_HANDLE, + wl_egl_window_create, + surface, + width, + height + ); + WlEglSurface { ptr: ptr } + } + + /// Fetch current size of the EGL surface + pub fn get_size(&self) -> (i32, i32) { + let mut w = 0i32; + let mut h = 0i32; + unsafe { + ffi_dispatch!( + WAYLAND_EGL_HANDLE, + wl_egl_window_get_attached_size, + self.ptr, + &mut w as *mut i32, + &mut h as *mut i32 + ); + } + (w, h) + } + + /// Resize the EGL surface + /// + /// The two first arguments `(width, height)` are the new size of + /// the surface, the two others `(dx, dy)` represent the displacement + /// of the top-left corner of the surface. It allows you to control the + /// direction of the resizing if necessary. + pub fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) { + unsafe { + ffi_dispatch!( + WAYLAND_EGL_HANDLE, + wl_egl_window_resize, + self.ptr, + width, + height, + dx, + dy + ) + } + } + + /// Raw pointer to the EGL surface + /// + /// You'll need this pointer to initialize the EGL context in your + /// favourite OpenGL lib. + pub fn ptr(&self) -> *const c_void { + self.ptr as *const c_void + } +} + +impl Drop for WlEglSurface { + fn drop(&mut self) { + unsafe { + ffi_dispatch!(WAYLAND_EGL_HANDLE, wl_egl_window_destroy, self.ptr); + } + } +} From 882a6ea1da974a966239e252a30dcbd8bb4efee2 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 6 Apr 2018 15:32:38 +0200 Subject: [PATCH 41/60] server: crate docs --- wayland-server/src/lib.rs | 94 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/wayland-server/src/lib.rs b/wayland-server/src/lib.rs index 66842988688..7cfa6328153 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -1,3 +1,97 @@ +//! Server-side Wayland connector +//! +//! ## Overview +//! +//! This crate provides the interfaces and machinery to safely create server +//! for the wayland protocol. It is a rust wrapper around the `libwayland-server.so` +//! C library. +//! +//! 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. +//! +//! ## Protocol and messages handling model +//! +//! 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. +//! +//! ### Resources +//! +//! 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. +//! +//! These resources are used to senf messages to the clients (they are called "events" in the +//! wayland context). This is done by the `Resource::::send(..)` method. +//! +//! 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. +//! +//! 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. +//! +//! ### Implementations +//! +//! 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. +//! +//! **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. +//! +//! An implementation is just a struct implementing the `Implementation, I::Requests>` +//! trait, where `I` is the interface of the considered object: +//! +//! ``` +//! // Example implementation for the wl_surface interface +//! use wayland_server::Resource; +//! use wayland_server::protocol::wl_surface; +//! use wayland_server::commons::Implementation; +//! +//! struct MyImpl { +//! // ... +//! } +//! +//! impl Implementation, wl_surface::Requests> for MyImpl { +//! fn receive(&mut self, msg: wl_surface::Requests, resource: Resource) { +//! // process the message... +//! } +//! } +//! # fn main() {} +//! ``` +//! +//! The trait is also automatically implemented for `FnMut(I::Requests, Resource)` closures, +//! so you can use them for simplicity if a full struct would be too cumbersome. +//! +//! 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). +//! +//! ## Event loops and general structure +//! +//! 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 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)] #[macro_use] From 49cdbd1cceb821ad61b20e0fdad375ab6b244277 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 6 Apr 2018 16:24:04 +0200 Subject: [PATCH 42/60] client: minimal GlobalManager --- wayland-client/src/globals.rs | 81 +++++++++++++++++++++++++++++++++++ wayland-client/src/lib.rs | 2 + 2 files changed, 83 insertions(+) create mode 100644 wayland-client/src/globals.rs diff --git a/wayland-client/src/globals.rs b/wayland-client/src/globals.rs new file mode 100644 index 00000000000..00863a2d0b9 --- /dev/null +++ b/wayland-client/src/globals.rs @@ -0,0 +1,81 @@ +use std::sync::{Arc, Mutex}; + +use commons::Interface; +use {NewProxy, Proxy}; +use protocol::wl_registry::{self, RequestsTrait}; + +/// 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 { + global_list: 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), +} + +impl GlobalManager { + /// Create a global manager handling a registry + pub fn new(registry: NewProxy) -> GlobalManager { + let global_list = Arc::new(Mutex::new(Vec::new())); + let global_list_clone = global_list.clone(); + + let registry = registry.implement(move |msg, _| match msg { + wl_registry::Events::Global { + name, + interface, + version, + } => { + global_list_clone + .lock() + .unwrap() + .push((name, interface, version)); + } + wl_registry::Events::GlobalRemove { name } => { + global_list_clone + .lock() + .unwrap() + .retain(|&(n, _, _)| n != name); + } + }); + + GlobalManager { + global_list: global_list, + registry: registry, + } + } + + /// Instanciate a specific global + /// + /// 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(&self, version: u32) -> Result, GlobalError> { + let list = self.global_list.lock().unwrap(); + for &(id, ref interface, server_version) in &*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.global_list.lock().unwrap().clone() + } +} diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index 0b3424d1187..af905ed0b17 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -133,10 +133,12 @@ extern crate wayland_sys; mod display; mod event_queue; +mod globals; mod proxy; pub use proxy::{NewProxy, Proxy}; pub use display::Display; +pub use globals::{GlobalError, GlobalManager}; pub use event_queue::{EventQueue, QueueToken, ReadEventsGuard}; #[cfg(feature = "cursor")] From 798bcc72c25b1d2a0e8a23746ffa54a80309564c Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sat, 7 Apr 2018 09:29:30 +0200 Subject: [PATCH 43/60] more tests --- tests/attach_null_to_surface.rs | 78 +++++++++++++++++++++++++++++++++ tests/basic_global.rs | 54 +++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 tests/attach_null_to_surface.rs create mode 100644 tests/basic_global.rs diff --git a/tests/attach_null_to_surface.rs b/tests/attach_null_to_surface.rs new file mode 100644 index 00000000000..20ce4f1146e --- /dev/null +++ b/tests/attach_null_to_surface.rs @@ -0,0 +1,78 @@ +use std::sync::{Arc, Mutex}; + +mod helpers; + +use helpers::{roundtrip, wayc, ways, TestClient, TestServer}; + +use wayc::protocol::wl_display::RequestsTrait as DisplayRequests; +use wayc::protocol::wl_compositor::RequestsTrait as CompositorRequests; +use wayc::protocol::wl_surface::RequestsTrait as SurfaceRequests; + +fn insert_compositor(server: &mut TestServer) -> Arc> { + use ways::protocol::{wl_compositor, wl_surface}; + use ways::NewResource; + + let seen_surface = Arc::new(Mutex::new(false)); + let seen_surface2 = seen_surface.clone(); + + let loop_token = server.event_loop.token(); + server + .display + .create_global::( + &loop_token, + 1, + move |version, compositor: NewResource<_>| { + let compositor_seen_surface = seen_surface.clone(); + compositor.implement( + move |event, _| { + if let wl_compositor::Requests::CreateSurface { id: surface } = event { + let my_seen_surface = compositor_seen_surface.clone(); + surface.implement( + move |event, _| { + if let wl_surface::Requests::Attach { buffer, x, y } = event { + assert!(buffer.is_none()); + *(my_seen_surface.lock().unwrap()) = true; + } else { + panic!("Unexpected event on surface!"); + } + }, + None::, + ); + } else { + panic!("Unexpected event on compositor!"); + } + }, + None::, + ); + }, + ); + + seen_surface2 +} + +#[test] +fn attach_null() { + // Server setup + // + let mut server = TestServer::new(); + let seen_surface = insert_compositor(&mut server); + + // Client setup + // + let mut client = TestClient::new(&server.socket_name); + let manager = wayc::GlobalManager::new(client.display.get_registry().unwrap()); + + // Initial sync + roundtrip(&mut client, &mut server); + + let compositor = manager + .instanciate::(1) + .unwrap() + .implement(|_, _| {}); + let surface = compositor.create_surface().unwrap().implement(|_, _| {}); + surface.attach(None, 0, 0); + + roundtrip(&mut client, &mut server); + + assert!(*seen_surface.lock().unwrap()); +} diff --git a/tests/basic_global.rs b/tests/basic_global.rs new file mode 100644 index 00000000000..bc91d14b813 --- /dev/null +++ b/tests/basic_global.rs @@ -0,0 +1,54 @@ +mod helpers; + +use helpers::{roundtrip, wayc, ways, TestClient, TestServer}; + +use ways::protocol::wl_compositor::WlCompositor as ServerCompositor; +use wayc::protocol::wl_display::RequestsTrait; + +#[test] +fn simple_global() { + let mut server = TestServer::new(); + let loop_token = server.event_loop.token(); + server + .display + .create_global::(&loop_token, 1, |_, _| {}); + + let mut client = TestClient::new(&server.socket_name); + let manager = wayc::GlobalManager::new(client.display.get_registry().unwrap()); + + roundtrip(&mut client, &mut server); + let globals = manager.list(); + assert!(globals.len() == 1); + assert_eq!(globals[0], (1, "wl_compositor".into(), 1)); +} + +#[test] +fn multi_versions() { + let mut server = TestServer::new(); + 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, |_, _| {}); + + let mut client = TestClient::new(&server.socket_name); + let manager = wayc::GlobalManager::new(client.display.get_registry().unwrap()); + + roundtrip(&mut client, &mut server); + let globals = manager.list(); + assert!(globals.len() == 4); + let mut seen = [false; 4]; + for &(_, ref interface, version) in &globals { + assert!(interface == "wl_compositor"); + seen[version as usize - 1] = true; + } + assert_eq!(seen, [true, true, true, true]); +} From 81deaa9501e152892c203656eace4284a76b76a9 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sat, 7 Apr 2018 17:25:30 +0200 Subject: [PATCH 44/60] server: doc typos --- wayland-server/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wayland-server/src/lib.rs b/wayland-server/src/lib.rs index 7cfa6328153..813786df5b6 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -2,7 +2,7 @@ //! //! ## Overview //! -//! This crate provides the interfaces and machinery to safely create server +//! 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. //! @@ -23,7 +23,7 @@ //! main difference being that the handles to objects are represented by the `Resource` //! type. //! -//! These resources are used to senf messages to the clients (they are called "events" in the +//! 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. //! //! There is not a 1 to 1 mapping between `Resource` instances and protocol objects. Rather, From 81bbd60594b47e847bd46699dd044b3f7794f16e Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sat, 7 Apr 2018 20:12:57 +0200 Subject: [PATCH 45/60] client: callback for GlobalManager --- wayland-client/src/globals.rs | 235 ++++++++++++++++++++++++++++++---- wayland-client/src/lib.rs | 2 +- 2 files changed, 213 insertions(+), 24 deletions(-) diff --git a/wayland-client/src/globals.rs b/wayland-client/src/globals.rs index 00863a2d0b9..ce260a48264 100644 --- a/wayland-client/src/globals.rs +++ b/wayland-client/src/globals.rs @@ -1,16 +1,21 @@ use std::sync::{Arc, Mutex}; -use commons::Interface; +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 { - global_list: Arc>>, + inner: Arc>, registry: Proxy, } @@ -24,33 +29,119 @@ pub enum GlobalError { VersionTooLow(u32), } +/// Events 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 global_list = Arc::new(Mutex::new(Vec::new())); - let global_list_clone = global_list.clone(); + let inner = Arc::new(Mutex::new(Inner { + list: Vec::new(), + callback: Box::new(|_, _| {}), + })); + let inner_clone = inner.clone(); - let registry = registry.implement(move |msg, _| match msg { - wl_registry::Events::Global { - name, - interface, - version, - } => { - global_list_clone - .lock() - .unwrap() - .push((name, interface, version)); + let registry = registry.implement(move |msg, _proxy| { + let mut inner = inner.lock().unwrap(); + match msg { + wl_registry::Events::Global { + name, + interface, + version, + } => { + inner.list.push((name, interface, version)); + } + wl_registry::Events::GlobalRemove { name } => { + inner.list.retain(|&(n, _, _)| n != name); + } } - wl_registry::Events::GlobalRemove { name } => { - global_list_clone - .lock() - .unwrap() - .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::Events::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::Events::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 { - global_list: global_list, + inner: inner_clone, registry: registry, } } @@ -61,8 +152,8 @@ impl GlobalManager { /// not exist with multiplicity (sur as `wl_compositor` or `wl_shm`), /// as it will only bind a single one. pub fn instanciate(&self, version: u32) -> Result, GlobalError> { - let list = self.global_list.lock().unwrap(); - for &(id, ref interface, server_version) in &*list { + 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)); @@ -76,6 +167,104 @@ impl GlobalManager { /// Retrieve the list of currently known globals pub fn list(&self) -> Vec<(u32, String, u32)> { - self.global_list.lock().unwrap().clone() + 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 af905ed0b17..a76c5f2cd2c 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -138,7 +138,7 @@ mod proxy; pub use proxy::{NewProxy, Proxy}; pub use display::Display; -pub use globals::{GlobalError, GlobalManager}; +pub use globals::{GlobalError, GlobalEvent, GlobalManager}; pub use event_queue::{EventQueue, QueueToken, ReadEventsGuard}; #[cfg(feature = "cursor")] From a26976241b86403b16be64f4385e2dec72d361d8 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sat, 7 Apr 2018 20:13:38 +0200 Subject: [PATCH 46/60] client: examples --- wayland-client/Cargo.toml | 4 + wayland-client/examples/dynamic_globals.rs | 123 ++++++++++++++++ wayland-client/examples/list_globals.rs | 35 ++--- wayland-client/examples/simple_window.rs | 161 +++++++++++++++++++++ 4 files changed, 304 insertions(+), 19 deletions(-) create mode 100644 wayland-client/examples/dynamic_globals.rs create mode 100644 wayland-client/examples/simple_window.rs diff --git a/wayland-client/Cargo.toml b/wayland-client/Cargo.toml index 9f96f1e5281..952024647df 100644 --- a/wayland-client/Cargo.toml +++ b/wayland-client/Cargo.toml @@ -22,6 +22,10 @@ libc = "0.2" [build-dependencies] wayland-scanner = { version = "0.20.0", path = "../wayland-scanner" } +[dev-dependencies] +byteorder = "1.0" +tempfile = "2.0" + [features] default = ["egl", "cursor", "native_lib"] native_lib = [ "wayland-sys", "wayland-commons/native_lib" ] diff --git a/wayland-client/examples/dynamic_globals.rs b/wayland-client/examples/dynamic_globals.rs new file mode 100644 index 00000000000..8efc6078706 --- /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 necessayr 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 happer 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::Events; + match event { + Events::Name { name } => { + seat_name = Some(name); + } + Events::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::Events; + match event { + Events::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); + } + Events::Mode { + flags, + width, + height, + refresh, + } => { + modes.push((flags, width, height, refresh)); + } + Events::Scale { factor } => { + scale = factor; + } + Events::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 index 7e33bd8ea05..6c22a5d8fb4 100644 --- a/wayland-client/examples/list_globals.rs +++ b/wayland-client/examples/list_globals.rs @@ -1,30 +1,27 @@ extern crate wayland_client; -use wayland_client::Display; +use wayland_client::{Display, GlobalManager}; use wayland_client::protocol::wl_display::RequestsTrait; -use wayland_client::protocol::wl_registry::Events as RegistryEvents; + +// A minimal example printing the list of globals abvertized by the server and +// then exiting fn main() { + // Connect to the server let (display, mut event_queue) = Display::connect_to_env().unwrap(); - let registry = display - .get_registry() - .unwrap() - .implement(|evt, _| match evt { - RegistryEvents::Global { - name, - interface, - version, - } => { - println!( - "New global with id {} and version {} of interface '{}'.", - name, version, interface - ); - } - _ => {} - }); + // 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(); - 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_window.rs b/wayland-client/examples/simple_window.rs new file mode 100644 index 00000000000..f94e45a3403 --- /dev/null +++ b/wayland-client/examples/simple_window.rs @@ -0,0 +1,161 @@ +extern crate byteorder; +extern crate tempfile; +extern crate wayland_client; + +use std::cmp::min; +use std::io::Write; +use std::os::unix::io::AsRawFd; + +use byteorder::{NativeEndian, WriteBytesExt}; + +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; + +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; + + // create a tempfile to write the conents of the window on + let mut tmp = tempfile::tempfile() + .ok() + .expect("Unable to create a tempfile."); + // write the contents to it, lets put a nice color gradient + for i in 0..(buf_x * buf_y) { + let x = (i % buf_x) as u32; + let y = (i / buf_x) as u32; + let r: u32 = min(((buf_x - x) * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y); + let g: u32 = min((x * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y); + let b: u32 = min(((buf_x - x) * 0xFF) / buf_x, (y * 0xFF) / buf_y); + let _ = tmp.write_u32::((0xFF << 24) + (r << 16) + (g << 8) + b); + } + let _ = tmp.flush(); + + /* + * Init wayland objects + */ + + // The compositor allows us to creates surfaces + let compositor = globals + .instanciate::(1) + .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::(1) + .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, // 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::(1) + .unwrap() + .implement(|_, _| {}); + let shell_surface = shell.get_shell_surface(&surface).unwrap().implement( + |event, shell_surface: Proxy| { + use wayland_client::protocol::wl_shell_surface::{Events, RequestsTrait}; + // This ping/pong mechanism is used by the wayland server to detect + // unresponsive applications + if let Events::Ping { serial } = event { + shell_surface.pong(serial); + } + }, + ); + + // Set our surface as toplevel and define its contents + shell_surface.set_toplevel(); + surface.attach(Some(&buffer), 0, 0); + surface.commit(); + + // 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::(1) + .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, Events as SeatEvents, + RequestsTrait as SeatRequests}; + use wayland_client::protocol::wl_pointer::Events as PointerEvents; + + if let SeatEvents::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 { + PointerEvents::Enter { + surface_x, + surface_y, + .. + } => { + println!("Pointer entered at ({}, {}).", surface_x, surface_y); + } + PointerEvents::Leave { .. } => { + println!("Pointer left."); + } + PointerEvents::Motion { + surface_x, + surface_y, + .. + } => { + println!("Pointer moved to ({}, {}).", surface_x, surface_y); + } + PointerEvents::Button { button, state, .. } => { + println!("Button {} was {:?}.", button, state); + } + _ => {} + }); + } + } + }); + + loop { + display.flush().unwrap(); + event_queue.dispatch().unwrap(); + } +} From 1d0e09e44cb48d810f335d58d3df852fa7863698 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 9 Apr 2018 14:26:17 +0200 Subject: [PATCH 47/60] Use singular for Event and Request types. --- tests/attach_null_to_surface.rs | 4 +- tests/scanner_assets/client_c_code.rs | 128 ++++++++++----------- tests/scanner_assets/server_c_code.rs | 76 ++++++------ wayland-client/examples/dynamic_globals.rs | 16 +-- wayland-client/examples/simple_window.rs | 18 +-- wayland-client/src/globals.rs | 10 +- wayland-client/src/lib.rs | 8 +- wayland-client/src/proxy.rs | 14 +-- wayland-commons/src/lib.rs | 8 +- wayland-scanner/src/c_code_gen.rs | 22 ++-- wayland-server/src/lib.rs | 8 +- wayland-server/src/resource.rs | 24 ++-- 12 files changed, 168 insertions(+), 168 deletions(-) diff --git a/tests/attach_null_to_surface.rs b/tests/attach_null_to_surface.rs index 20ce4f1146e..9e15a9c467b 100644 --- a/tests/attach_null_to_surface.rs +++ b/tests/attach_null_to_surface.rs @@ -25,11 +25,11 @@ fn insert_compositor(server: &mut TestServer) -> Arc> { let compositor_seen_surface = seen_surface.clone(); compositor.implement( move |event, _| { - if let wl_compositor::Requests::CreateSurface { id: surface } = 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::Requests::Attach { buffer, x, y } = event { + if let wl_surface::Request::Attach { buffer, x, y } = event { assert!(buffer.is_none()); *(my_seen_surface.lock().unwrap()) = true; } else { diff --git a/tests/scanner_assets/client_c_code.rs b/tests/scanner_assets/client_c_code.rs index 9c437b2b980..035e62519eb 100644 --- a/tests/scanner_assets/client_c_code.rs +++ b/tests/scanner_assets/client_c_code.rs @@ -71,7 +71,7 @@ pub mod wl_foo { } } - pub enum Requests { + pub enum Request { /// do some foo /// /// This will do some foo with its args. @@ -82,20 +82,20 @@ pub mod wl_foo { CreateBar {id: Proxy, }, } - impl super::MessageGroup for Requests { + 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!("Requests::from_raw_c can not be used Client-side.") + 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 { - Requests::FooIt { number, unumber, text, float, file, } => { + 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; @@ -105,7 +105,7 @@ pub mod wl_foo { _args_array[4].h = file; f(0, &mut _args_array) }, - Requests::CreateBar { id, } => { + 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) @@ -114,7 +114,7 @@ pub mod wl_foo { } } - pub enum Events { + pub enum Event { /// a cake is possible /// /// The server advertizes that a kind of cake is available @@ -123,18 +123,18 @@ pub mod wl_foo { Cake {kind: CakeKind, amount: u32, }, } - impl super::MessageGroup for Events { + 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 { + 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(Events::Cake { + Ok(Event::Cake { kind: CakeKind::from_raw(_args[0].u).ok_or(())?, amount: _args[1].u, }) }, @@ -143,7 +143,7 @@ pub mod wl_foo { } fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { - panic!("Events::as_raw_c_in can not be used Client-side.") + panic!("Event::as_raw_c_in can not be used Client-side.") } } @@ -151,8 +151,8 @@ pub mod wl_foo { pub struct WlFoo; impl Interface for WlFoo { - type Requests = Requests; - type Events = Events; + 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 } @@ -175,7 +175,7 @@ pub mod wl_foo { if !self.is_external() && !self.is_alive() { return; } - let msg = Requests::FooIt { + let msg = Request::FooIt { number: number, unumber: unumber, text: text, @@ -190,7 +190,7 @@ pub mod wl_foo { return Err(()); } let _arg_id_newproxy = self.child::(); - let msg = Requests::CreateBar { + let msg = Request::CreateBar { id: unsafe { Proxy::::from_c_ptr(_arg_id_newproxy.c_ptr()) }, }; self.send(msg); @@ -208,7 +208,7 @@ pub mod wl_bar { use super::sys::common::{wl_argument, wl_interface, wl_array}; use super::sys::client::*; - pub enum Requests { + pub enum Request { /// ask for a bar delivery /// /// Proceed to a bar delivery of given foo. @@ -223,21 +223,21 @@ pub mod wl_bar { Release, } - impl super::MessageGroup for Requests { + impl super::MessageGroup for Request { fn is_destructor(&self) -> bool { match *self { - Requests::Release => true, + Request::Release => true, _ => false } } - unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { - panic!("Requests::from_raw_c can not be used Client-side.") + 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 { - Requests::BarDelivery { kind, target, metadata, } => { + 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 _; @@ -245,7 +245,7 @@ pub mod wl_bar { _args_array[2].a = &_arg_2; f(0, &mut _args_array) }, - Requests::Release => { + Request::Release => { let mut _args_array: [wl_argument; 0] = unsafe { ::std::mem::zeroed() }; f(1, &mut _args_array) }, @@ -253,23 +253,23 @@ pub mod wl_bar { } } - pub enum Events { + pub enum Event { } - impl super::MessageGroup for Events { + 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 { + 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!("Events::as_raw_c_in can not be used Client-side.") + panic!("Event::as_raw_c_in can not be used Client-side.") } } @@ -277,8 +277,8 @@ pub mod wl_bar { pub struct WlBar; impl Interface for WlBar { - type Requests = Requests; - type Events = Events; + 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 } @@ -306,7 +306,7 @@ pub mod wl_bar { return; } - let msg = Requests::BarDelivery { + let msg = Request::BarDelivery { kind: kind, target: target.clone(), metadata: metadata, @@ -318,7 +318,7 @@ pub mod wl_bar { if !self.is_external() && !self.is_alive() { return; } - let msg = Requests::Release; + let msg = Request::Release; self.send(msg); } } @@ -333,17 +333,17 @@ pub mod wl_display { use super::sys::common::{wl_argument, wl_interface, wl_array}; use super::sys::client::*; - pub enum Requests { + pub enum Request { } - impl super::MessageGroup for Requests { + 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!("Requests::from_raw_c can not be used Client-side.") + 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 { @@ -352,23 +352,23 @@ pub mod wl_display { } } - pub enum Events { + pub enum Event { } - impl super::MessageGroup for Events { + 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 { + 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!("Events::as_raw_c_in can not be used Client-side.") + panic!("Event::as_raw_c_in can not be used Client-side.") } } @@ -376,8 +376,8 @@ pub mod wl_display { pub struct WlDisplay; impl Interface for WlDisplay { - type Requests = Requests; - type Events = Events; + 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 } @@ -400,27 +400,27 @@ pub mod wl_registry { use super::sys::common::{wl_argument, wl_interface, wl_array}; use super::sys::client::*; - pub enum Requests { + 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 Requests { + 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!("Requests::from_raw_c can not be used Client-side.") + 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 { - Requests::Bind { name, id, } => { + 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(); @@ -433,23 +433,23 @@ pub mod wl_registry { } } - pub enum Events { + pub enum Event { } - impl super::MessageGroup for Events { + 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 { + 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!("Events::as_raw_c_in can not be used Client-side.") + panic!("Event::as_raw_c_in can not be used Client-side.") } } @@ -457,8 +457,8 @@ pub mod wl_registry { pub struct WlRegistry; impl Interface for WlRegistry { - type Requests = Requests; - type Events = Events; + 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 } @@ -477,7 +477,7 @@ pub mod wl_registry { if !self.is_external() && !self.is_alive() { return Err(()); } - let msg = Requests::Bind { + let msg = Request::Bind { name: name, id: (T::NAME.into(), version, unsafe { Proxy::::new_null() }), }; @@ -509,17 +509,17 @@ pub mod wl_callback { use super::sys::common::{wl_argument, wl_interface, wl_array}; use super::sys::client::*; - pub enum Requests { + pub enum Request { } - impl super::MessageGroup for Requests { + 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!("Requests::from_raw_c can not be used Client-side.") + 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 { @@ -528,7 +528,7 @@ pub mod wl_callback { } } - pub enum Events { + pub enum Event { /// done event /// /// This event is actually a destructor, but the protocol XML has no wait of specifying it. @@ -538,18 +538,18 @@ pub mod wl_callback { Done {callback_data: u32, }, } - impl super::MessageGroup for Events { + impl super::MessageGroup for Event { fn is_destructor(&self) -> bool { match *self { - Events::Done { .. } => true, + Event::Done { .. } => true, } } - unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + 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(Events::Done { + Ok(Event::Done { callback_data: _args[0].u, }) }, _ => return Err(()) @@ -557,7 +557,7 @@ pub mod wl_callback { } fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { - panic!("Events::as_raw_c_in can not be used Client-side.") + panic!("Event::as_raw_c_in can not be used Client-side.") } } @@ -565,8 +565,8 @@ pub mod wl_callback { pub struct WlCallback; impl Interface for WlCallback { - type Requests = Requests; - type Events = Events; + 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_c_code.rs b/tests/scanner_assets/server_c_code.rs index cabdf4bec75..0f60aa65391 100644 --- a/tests/scanner_assets/server_c_code.rs +++ b/tests/scanner_assets/server_c_code.rs @@ -70,7 +70,7 @@ pub mod wl_foo { } } - pub enum Requests { + pub enum Request { /// do some foo /// /// This will do some foo with its args. @@ -81,18 +81,18 @@ pub mod wl_foo { CreateBar {id: NewResource, }, } - impl super::MessageGroup for Requests { + 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 { + 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(Requests::FooIt { + 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(), @@ -101,7 +101,7 @@ pub mod wl_foo { }) }, 1 => { let _args = ::std::slice::from_raw_parts(args, 1); - Ok(Requests::CreateBar { + 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(()) @@ -109,11 +109,11 @@ pub mod wl_foo { } fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { - panic!("Requests::as_raw_c_in can not be used Server-side.") + panic!("Request::as_raw_c_in can not be used Server-side.") } } - pub enum Events { + pub enum Event { /// a cake is possible /// /// The server advertizes that a kind of cake is available @@ -122,20 +122,20 @@ pub mod wl_foo { Cake {kind: CakeKind, amount: u32, }, } - impl super::MessageGroup for Events { + 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!("Events::from_raw_c can not be used Server-side.") + 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 { - Events::Cake { kind, amount, } => { + 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; @@ -149,8 +149,8 @@ pub mod wl_foo { pub struct WlFoo; impl Interface for WlFoo { - type Requests = Requests; - type Events = Events; + 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 } @@ -168,7 +168,7 @@ pub mod wl_bar { use super::sys::common::{wl_argument, wl_interface, wl_array}; use super::sys::server::*; - pub enum Requests { + pub enum Request { /// ask for a bar delivery /// /// Proceed to a bar delivery of given foo. @@ -183,45 +183,45 @@ pub mod wl_bar { Release, } - impl super::MessageGroup for Requests { + impl super::MessageGroup for Request { fn is_destructor(&self) -> bool { match *self { - Requests::Release => true, + Request::Release => true, _ => false } } - unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { + 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(Requests::BarDelivery { + 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(Requests::Release) }, + Ok(Request::Release) }, _ => return Err(()) } } fn as_raw_c_in(self, f: F) -> T where F: FnOnce(u32, &mut [wl_argument]) -> T { - panic!("Requests::as_raw_c_in can not be used Server-side.") + panic!("Request::as_raw_c_in can not be used Server-side.") } } - pub enum Events { + pub enum Event { } - impl super::MessageGroup for Events { + 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!("Events::from_raw_c can not be used Server-side.") + 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 { @@ -234,8 +234,8 @@ pub mod wl_bar { pub struct WlBar; impl Interface for WlBar { - type Requests = Requests; - type Events = Events; + 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 } @@ -253,27 +253,27 @@ pub mod wl_callback { use super::sys::common::{wl_argument, wl_interface, wl_array}; use super::sys::server::*; - pub enum Requests { + pub enum Request { } - impl super::MessageGroup for Requests { + 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 { + 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!("Requests::as_raw_c_in can not be used Server-side.") + panic!("Request::as_raw_c_in can not be used Server-side.") } } - pub enum Events { + pub enum Event { /// done event /// /// This event is actually a destructor, but the protocol XML has no wait of specifying it. @@ -283,20 +283,20 @@ pub mod wl_callback { Done {callback_data: u32, }, } - impl super::MessageGroup for Events { + impl super::MessageGroup for Event { fn is_destructor(&self) -> bool { match *self { - Events::Done { .. } => true, + Event::Done { .. } => true, } } - unsafe fn from_raw_c(obj: *mut ::std::os::raw::c_void, opcode: u32, args: *const wl_argument) -> Result { - panic!("Events::from_raw_c can not be used Server-side.") + 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 { - Events::Done { callback_data, } => { + 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) @@ -309,8 +309,8 @@ pub mod wl_callback { pub struct WlCallback; impl Interface for WlCallback { - type Requests = Requests; - type Events = Events; + 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/wayland-client/examples/dynamic_globals.rs b/wayland-client/examples/dynamic_globals.rs index 8efc6078706..8db3a72115f 100644 --- a/wayland-client/examples/dynamic_globals.rs +++ b/wayland-client/examples/dynamic_globals.rs @@ -37,12 +37,12 @@ fn main() { let mut seat_name = None; let mut caps = None; seat.implement(move |event, _| { - use wayland_client::protocol::wl_seat::Events; + use wayland_client::protocol::wl_seat::Event; match event { - Events::Name { name } => { + Event::Name { name } => { seat_name = Some(name); } - Events::Capabilities { capabilities } => { + Event::Capabilities { capabilities } => { // We *should* have received the "name" event first caps = Some(capabilities); } @@ -63,9 +63,9 @@ fn main() { let mut modes = Vec::new(); let mut scale = 1; output.implement(move |event, _| { - use wayland_client::protocol::wl_output::Events; + use wayland_client::protocol::wl_output::Event; match event { - Events::Geometry { + Event::Geometry { x, y, physical_width, @@ -85,7 +85,7 @@ fn main() { println!(" -> subpixel orientation: {:?}", subpixel); name = format!("{} ({})", make, model); } - Events::Mode { + Event::Mode { flags, width, height, @@ -93,10 +93,10 @@ fn main() { } => { modes.push((flags, width, height, refresh)); } - Events::Scale { factor } => { + Event::Scale { factor } => { scale = factor; } - Events::Done => { + Event::Done => { println!("Modesetting information for output \"{}\"", name); println!(" -> scaling factor: {}", scale); println!(" -> mode list:"); diff --git a/wayland-client/examples/simple_window.rs b/wayland-client/examples/simple_window.rs index f94e45a3403..4261ec043ea 100644 --- a/wayland-client/examples/simple_window.rs +++ b/wayland-client/examples/simple_window.rs @@ -90,10 +90,10 @@ fn main() { .implement(|_, _| {}); let shell_surface = shell.get_shell_surface(&surface).unwrap().implement( |event, shell_surface: Proxy| { - use wayland_client::protocol::wl_shell_surface::{Events, RequestsTrait}; + 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 Events::Ping { serial } = event { + if let Event::Ping { serial } = event { shell_surface.pong(serial); } }, @@ -117,35 +117,35 @@ fn main() { // 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, Events as SeatEvents, + use wayland_client::protocol::wl_seat::{Capability, Event as SeatEvent, RequestsTrait as SeatRequests}; - use wayland_client::protocol::wl_pointer::Events as PointerEvents; + use wayland_client::protocol::wl_pointer::Event as PointerEvent; - if let SeatEvents::Capabilities { capabilities } = event { + 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 { - PointerEvents::Enter { + PointerEvent::Enter { surface_x, surface_y, .. } => { println!("Pointer entered at ({}, {}).", surface_x, surface_y); } - PointerEvents::Leave { .. } => { + PointerEvent::Leave { .. } => { println!("Pointer left."); } - PointerEvents::Motion { + PointerEvent::Motion { surface_x, surface_y, .. } => { println!("Pointer moved to ({}, {}).", surface_x, surface_y); } - PointerEvents::Button { button, state, .. } => { + PointerEvent::Button { button, state, .. } => { println!("Button {} was {:?}.", button, state); } _ => {} diff --git a/wayland-client/src/globals.rs b/wayland-client/src/globals.rs index ce260a48264..97c1b21adf0 100644 --- a/wayland-client/src/globals.rs +++ b/wayland-client/src/globals.rs @@ -29,7 +29,7 @@ pub enum GlobalError { VersionTooLow(u32), } -/// Events provided to the user callback of GlobalManager +/// Event provided to the user callback of GlobalManager pub enum GlobalEvent { /// A new global was created New { @@ -61,14 +61,14 @@ impl GlobalManager { let registry = registry.implement(move |msg, _proxy| { let mut inner = inner.lock().unwrap(); match msg { - wl_registry::Events::Global { + wl_registry::Event::Global { name, interface, version, } => { inner.list.push((name, interface, version)); } - wl_registry::Events::GlobalRemove { name } => { + wl_registry::Event::GlobalRemove { name } => { inner.list.retain(|&(n, _, _)| n != name); } } @@ -100,7 +100,7 @@ impl GlobalManager { let registry = registry.implement(move |msg, proxy| { let mut inner = inner.lock().unwrap(); match msg { - wl_registry::Events::Global { + wl_registry::Event::Global { name, interface, version, @@ -115,7 +115,7 @@ impl GlobalManager { proxy, ); } - wl_registry::Events::GlobalRemove { name } => { + wl_registry::Event::GlobalRemove { name } => { if let Some((i, _)) = inner .list .iter() diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs index a76c5f2cd2c..6aa9717c0fd 100644 --- a/wayland-client/src/lib.rs +++ b/wayland-client/src/lib.rs @@ -57,7 +57,7 @@ //! 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::Events>` +//! An implementation is just a struct implementing the `Implementation, I::Event>` //! trait, where `I` is the interface of the considered object: //! //! ``` @@ -70,15 +70,15 @@ //! // ... //! } //! -//! impl Implementation, wl_surface::Events> for MyImpl { -//! fn receive(&mut self, msg: wl_surface::Events, proxy: Proxy) { +//! impl Implementation, wl_surface::Event> for MyImpl { +//! fn receive(&mut self, msg: wl_surface::Event, proxy: Proxy) { //! // process the message... //! } //! } //! # fn main() {} //! ``` //! -//! The trait is also automatically implemented for `FnMut(I::Events, Proxy)` closures, +//! 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. //! //! ## Event Queues diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs index 31c62d7aafd..520e03685a6 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -60,7 +60,7 @@ impl Proxy { /// 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::Requests) { + pub fn send(&self, msg: I::Request) { #[cfg(not(feature = "native_lib"))] { if !self.internal.alive.load(Ordering::Acquire) { @@ -342,7 +342,7 @@ impl NewProxy { /// Implement this proxy using given function and implementation data. pub fn implement(self, implementation: Impl) -> Proxy where - Impl: Implementation, I::Events> + Send + 'static, + Impl: Implementation, I::Event> + Send + 'static, { unsafe { self.implement_inner(implementation) } } @@ -362,7 +362,7 @@ impl NewProxy { /// To ensure safety, see `Proxy::make_wrapper`. pub unsafe fn implement_nonsend(self, implementation: Impl, queue: &QueueToken) -> Proxy where - Impl: Implementation, I::Events> + 'static, + Impl: Implementation, I::Event> + 'static, { #[cfg(not(feature = "native_lib"))] {} @@ -375,7 +375,7 @@ impl NewProxy { unsafe fn implement_inner(self, implementation: Impl) -> Proxy where - Impl: Implementation, I::Events> + 'static, + Impl: Implementation, I::Event> + 'static, { #[cfg(not(feature = "native_lib"))] { @@ -449,13 +449,13 @@ mod native_machinery { pub(crate) struct ProxyUserData { pub(crate) internal: Arc, - implem: Option, I::Events>>>, + implem: Option, I::Event>>>, } impl ProxyUserData { pub(crate) fn new(implem: Impl) -> ProxyUserData where - Impl: Implementation, I::Events> + 'static, + Impl: Implementation, I::Event> + 'static, { ProxyUserData { internal: Arc::new(super::ProxyInternal::new()), @@ -480,7 +480,7 @@ mod native_machinery { // 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::Events::from_raw_c(proxy as *mut _, opcode, args)?; + 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); diff --git a/wayland-commons/src/lib.rs b/wayland-commons/src/lib.rs index c4492179ffb..82a4de54eb6 100644 --- a/wayland-commons/src/lib.rs +++ b/wayland-commons/src/lib.rs @@ -60,11 +60,11 @@ pub trait Interface: 'static { /// Set of requests associated to this interface /// /// Requests are messages from the client to the server - type Requests: MessageGroup + 'static; + type Request: MessageGroup + 'static; /// Set of events associated to this interface /// /// Events are messages from the server to the client - type Events: MessageGroup + 'static; + type Event: MessageGroup + 'static; /// Name of this interface const NAME: &'static str; #[cfg(feature = "native_lib")] @@ -130,8 +130,8 @@ pub struct AnonymousObject; pub enum NoMessage {} impl Interface for AnonymousObject { - type Requests = NoMessage; - type Events = NoMessage; + type Request = NoMessage; + type Event = NoMessage; const NAME: &'static str = ""; #[cfg(feature = "native_lib")] fn c_interface() -> *const ::syscom::wl_interface { diff --git a/wayland-scanner/src/c_code_gen.rs b/wayland-scanner/src/c_code_gen.rs index af183f2d41f..7c2c79e5799 100644 --- a/wayland-scanner/src/c_code_gen.rs +++ b/wayland-scanner/src/c_code_gen.rs @@ -29,10 +29,10 @@ pub(crate) fn write_protocol_client(protocol: Protocol, out: &mut O) - let iface_name = snake_to_camel(&iface.name); write_enums(&iface.enums, out)?; - write_messagegroup("Requests", Side::Client, false, &iface.requests, out)?; - write_messagegroup_impl("Requests", Side::Client, false, &iface.requests, out)?; - write_messagegroup("Events", Side::Client, true, &iface.events, out)?; - write_messagegroup_impl("Events", Side::Client, true, &iface.events, 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)?; @@ -70,10 +70,10 @@ pub(crate) fn write_protocol_server(protocol: Protocol, out: &mut O) - let iface_name = snake_to_camel(&iface.name); write_enums(&iface.enums, out)?; - write_messagegroup("Requests", Side::Server, true, &iface.requests, out)?; - write_messagegroup_impl("Requests", Side::Server, true, &iface.requests, out)?; - write_messagegroup("Events", Side::Server, false, &iface.events, out)?; - write_messagegroup_impl("Events", Side::Server, false, &iface.events, 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")?; @@ -396,8 +396,8 @@ fn write_interface(name: &str, low_name: &str, out: &mut O) -> IOResul pub struct {name}; impl Interface for {name} {{ - type Requests = Requests; - type Events = Events; + 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 }} @@ -468,7 +468,7 @@ fn write_client_methods(name: &str, messages: &[Message], out: &mut O) // actually send the stuff write!( out, - " let msg = Requests::{}", + " let msg = Request::{}", snake_to_camel(&msg.name) )?; if msg.args.len() > 0 { diff --git a/wayland-server/src/lib.rs b/wayland-server/src/lib.rs index 813786df5b6..7bd5380dea2 100644 --- a/wayland-server/src/lib.rs +++ b/wayland-server/src/lib.rs @@ -48,7 +48,7 @@ //! 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. //! -//! An implementation is just a struct implementing the `Implementation, I::Requests>` +//! An implementation is just a struct implementing the `Implementation, I::Request>` //! trait, where `I` is the interface of the considered object: //! //! ``` @@ -61,15 +61,15 @@ //! // ... //! } //! -//! impl Implementation, wl_surface::Requests> for MyImpl { -//! fn receive(&mut self, msg: wl_surface::Requests, resource: Resource) { +//! impl Implementation, wl_surface::Request> for MyImpl { +//! fn receive(&mut self, msg: wl_surface::Request, resource: Resource) { //! // process the message... //! } //! } //! # fn main() {} //! ``` //! -//! The trait is also automatically implemented for `FnMut(I::Requests, Resource)` closures, +//! 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. //! //! The `Resource` passed to your implementation is garanteed to be alive (as it just received diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index d3f1e9def71..bd0a09098db 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -48,7 +48,7 @@ impl Resource { /// /// The event will be send to the client associated to this /// object. - pub fn send(&self, msg: I::Events) { + pub fn send(&self, msg: I::Event) { #[cfg(not(feature = "native_lib"))] { if !self.internal.alive.load(Ordering::Acquire) { @@ -253,8 +253,8 @@ 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::Requests> + Send + 'static, - Dest: FnMut(Resource, Box, I::Requests>>) + Send + 'static, + Impl: Implementation, I::Request> + Send + 'static, + Dest: FnMut(Resource, Box, I::Request>>) + Send + 'static, { unsafe { self.implement_inner(implementation, destructor) } } @@ -277,8 +277,8 @@ impl NewResource { token: &LoopToken, ) -> Resource where - Impl: Implementation, I::Requests> + 'static, - Dest: FnMut(Resource, Box, I::Requests>>) + 'static, + Impl: Implementation, I::Request> + 'static, + Dest: FnMut(Resource, Box, I::Request>>) + 'static, { let _ = token; self.implement_inner(implementation, destructor) @@ -286,8 +286,8 @@ impl NewResource { unsafe fn implement_inner(self, implementation: Impl, destructor: Option) -> Resource where - Impl: Implementation, I::Requests> + 'static, - Dest: FnMut(Resource, Box, I::Requests>>) + 'static, + Impl: Implementation, I::Request> + 'static, + Dest: FnMut(Resource, Box, I::Request>>) + 'static, { #[cfg(not(feature = "native_lib"))] { @@ -365,8 +365,8 @@ mod native_machinery { pub(crate) internal: Arc, implem: Option< ( - Box, I::Requests>>, - Option, Box, I::Requests>>)>>, + Box, I::Request>>, + Option, Box, I::Request>>)>>, ), >, } @@ -374,8 +374,8 @@ mod native_machinery { impl ResourceUserData { pub(crate) fn new(implem: Impl, destructor: Option) -> ResourceUserData where - Impl: Implementation, I::Requests> + 'static, - Dest: FnMut(Resource, Box, I::Requests>>) + 'static, + Impl: Implementation, I::Request> + 'static, + Dest: FnMut(Resource, Box, I::Request>>) + 'static, { ResourceUserData { _i: ::std::marker::PhantomData, @@ -401,7 +401,7 @@ mod native_machinery { // 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::Requests::from_raw_c(resource as *mut _, opcode, args)?; + 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); From e381b87f1117cee6cf6d478aaba69726b8f60b12 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 9 Apr 2018 14:28:16 +0200 Subject: [PATCH 48/60] fix typos in comments --- wayland-client/examples/dynamic_globals.rs | 4 ++-- wayland-client/examples/list_globals.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/wayland-client/examples/dynamic_globals.rs b/wayland-client/examples/dynamic_globals.rs index 8db3a72115f..3d99fbd6367 100644 --- a/wayland-client/examples/dynamic_globals.rs +++ b/wayland-client/examples/dynamic_globals.rs @@ -24,7 +24,7 @@ fn main() { // with version 1 when advertized, and provide a callback that // will handle the created wl_seat to implement them // - // NOTE: the type annotations are necessayr because rustc's + // NOTE: the type annotations are necessary because rustc's // inference is apparently not smart enough [ wl_seat::WlSeat, @@ -32,7 +32,7 @@ fn main() { |seat: Result, _>, ()| { // here seat is a result, as failure can happen if the server // advertized an lower version than we requested. - // This cannot happer here as we requested version 1 + // This cannot happen here as we requested version 1 let seat = seat.unwrap(); let mut seat_name = None; let mut caps = None; diff --git a/wayland-client/examples/list_globals.rs b/wayland-client/examples/list_globals.rs index 6c22a5d8fb4..71d15a2a58a 100644 --- a/wayland-client/examples/list_globals.rs +++ b/wayland-client/examples/list_globals.rs @@ -4,7 +4,7 @@ use wayland_client::{Display, GlobalManager}; use wayland_client::protocol::wl_display::RequestsTrait; -// A minimal example printing the list of globals abvertized by the server and +// A minimal example printing the list of globals advertized by the server and // then exiting fn main() { From 69b25fc0e2f7336d0412623f11c4b8a28d20cb34 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 9 Apr 2018 17:18:26 +0200 Subject: [PATCH 49/60] Expose version of Proxy/Resource --- wayland-client/src/proxy.rs | 18 ++++++++++++++++++ wayland-server/src/resource.rs | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs index 520e03685a6..5b3aea9e7da 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -110,6 +110,24 @@ impl Proxy { } } + /// 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 diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index bd0a09098db..b32dd2006ab 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -98,6 +98,24 @@ impl Resource { } } + /// 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 /// From 508e8a4fb238e9168782219217a735752233cbac Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 9 Apr 2018 17:23:48 +0200 Subject: [PATCH 50/60] client: differentiate instanciate_auto/exact in GlobalManager --- tests/attach_null_to_surface.rs | 5 ++++- wayland-client/examples/simple_window.rs | 8 ++++---- wayland-client/src/globals.rs | 18 ++++++++++++++++-- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/tests/attach_null_to_surface.rs b/tests/attach_null_to_surface.rs index 9e15a9c467b..92e977158a4 100644 --- a/tests/attach_null_to_surface.rs +++ b/tests/attach_null_to_surface.rs @@ -22,6 +22,7 @@ fn insert_compositor(server: &mut TestServer) -> Arc> { &loop_token, 1, move |version, compositor: NewResource<_>| { + assert!(version == 1); let compositor_seen_surface = seen_surface.clone(); compositor.implement( move |event, _| { @@ -31,6 +32,8 @@ fn insert_compositor(server: &mut TestServer) -> Arc> { 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!"); @@ -66,7 +69,7 @@ fn attach_null() { roundtrip(&mut client, &mut server); let compositor = manager - .instanciate::(1) + .instanciate_exact::(1) .unwrap() .implement(|_, _| {}); let surface = compositor.create_surface().unwrap().implement(|_, _| {}); diff --git a/wayland-client/examples/simple_window.rs b/wayland-client/examples/simple_window.rs index 4261ec043ea..2b8c6d7de44 100644 --- a/wayland-client/examples/simple_window.rs +++ b/wayland-client/examples/simple_window.rs @@ -54,7 +54,7 @@ fn main() { // The compositor allows us to creates surfaces let compositor = globals - .instanciate::(1) + .instanciate_auto::() .unwrap() .implement(|_, _| {}); let surface = compositor.create_surface().unwrap().implement(|_, _| {}); @@ -62,7 +62,7 @@ fn main() { // 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::(1) + .instanciate_auto::() .unwrap() .implement(|_, _| {}); let pool = shm.create_pool( @@ -85,7 +85,7 @@ fn main() { // 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::(1) + .instanciate_auto::() .unwrap() .implement(|_, _| {}); let shell_surface = shell.get_shell_surface(&surface).unwrap().implement( @@ -111,7 +111,7 @@ fn main() { // seat, so we'll keep it simple here let mut pointer_created = false; let _seat = globals - .instanciate::(1) + .instanciate_auto::() .unwrap() .implement(move |event, seat: Proxy| { // The capabilities of a seat are known at runtime and we retrieve diff --git a/wayland-client/src/globals.rs b/wayland-client/src/globals.rs index 97c1b21adf0..5e9683ff7ad 100644 --- a/wayland-client/src/globals.rs +++ b/wayland-client/src/globals.rs @@ -146,12 +146,26 @@ impl GlobalManager { } } - /// Instanciate a specific global + /// 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(&self, version: u32) -> Result, GlobalError> { + 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 { From 445ce974aa17803f4e4b055386b5b8f7eecc417f Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Thu, 12 Apr 2018 12:05:10 +0200 Subject: [PATCH 51/60] Proxy/Resource::is_implemented_with --- wayland-client/src/globals.rs | 2 +- wayland-client/src/proxy.rs | 35 ++++++++++++++++++++++++++++++++++ wayland-server/src/resource.rs | 35 ++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/wayland-client/src/globals.rs b/wayland-client/src/globals.rs index 5e9683ff7ad..86f777a3b0c 100644 --- a/wayland-client/src/globals.rs +++ b/wayland-client/src/globals.rs @@ -155,7 +155,7 @@ impl GlobalManager { 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()); + return Ok(self.registry.bind::(version, id).unwrap()); } } Err(GlobalError::Missing) diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs index 5b3aea9e7da..589946e88c2 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -312,6 +312,31 @@ impl Proxy { } } } + + /// 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")] @@ -480,6 +505,16 @@ mod native_machinery { 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( diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index b32dd2006ab..b094b9002b2 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -248,6 +248,31 @@ impl Resource { 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 @@ -401,6 +426,16 @@ mod native_machinery { 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( From 9961a68841b49bca382d7c4d2cdbbe069c0c9f87 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Thu, 12 Apr 2018 12:17:13 +0200 Subject: [PATCH 52/60] server: add Resource::post_error --- wayland-server/src/resource.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index b094b9002b2..401cc2e1cf5 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -137,6 +137,33 @@ impl Resource { } } + /// 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 From 2cf922de18651f274b5aebd48815a2cda76dbc19 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 13 Apr 2018 12:15:54 +0200 Subject: [PATCH 53/60] server: make Resource::implement_nonsend safe --- wayland-server/src/event_loop.rs | 22 ++++++++++++++++++++++ wayland-server/src/resource.rs | 12 +++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/wayland-server/src/event_loop.rs b/wayland-server/src/event_loop.rs index 72ba22322b0..5437008f3d7 100644 --- a/wayland-server/src/event_loop.rs +++ b/wayland-server/src/event_loop.rs @@ -311,6 +311,28 @@ impl LoopToken { }; IdleSource::make(ret, data) } + + /// 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_display_get_event_loop, + display_ptr + ); + return event_loop_ptr == self.inner.wlevl + } } impl Drop for EventLoopInner { diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index 401cc2e1cf5..35cd331f484 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -336,11 +336,11 @@ impl NewResource { /// is not `Send`, this ensures you are implementing the resource from the same thread /// as the event loop runs on. /// - /// ** Unsafety ** + /// ** Panics ** /// - /// This function is unsafe if you create several wayland event loops and do not + /// This function will panic if you create several wayland event loops and do not /// provide a token to the right one. - pub unsafe fn implement_nonsend( + pub fn implement_nonsend( self, implementation: Impl, destructor: Option, @@ -350,8 +350,10 @@ impl NewResource { Impl: Implementation, I::Request> + 'static, Dest: FnMut(Resource, Box, I::Request>>) + 'static, { - let _ = token; - self.implement_inner(implementation, destructor) + 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 From 32fecbd4597febdf8ddea262be1c02408b38af50 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 13 Apr 2018 16:10:16 +0200 Subject: [PATCH 54/60] server: Resource is Send+Sync --- wayland-server/src/resource.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index 35cd331f484..f0507819e12 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -43,6 +43,9 @@ pub struct Resource { ptr: *mut wl_resource, } +unsafe impl Send for Resource {} +unsafe impl Sync for Resource {} + impl Resource { /// Send an event through this object /// From 7e9f5a705a6fd0796249b4298d330732e3ab1ff6 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sat, 14 Apr 2018 11:13:44 +0200 Subject: [PATCH 55/60] Proxy/Resource::equals --- wayland-client/src/proxy.rs | 16 ++++++++++++++++ wayland-server/src/event_loop.rs | 14 +++----------- wayland-server/src/resource.rs | 21 ++++++++++++++++++++- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs index 589946e88c2..7fb98d63267 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -191,6 +191,22 @@ impl Proxy { } } + /// 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 /// diff --git a/wayland-server/src/event_loop.rs b/wayland-server/src/event_loop.rs index 5437008f3d7..9f61642fa9d 100644 --- a/wayland-server/src/event_loop.rs +++ b/wayland-server/src/event_loop.rs @@ -316,22 +316,14 @@ impl LoopToken { /// 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 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_display_get_event_loop, display_ptr ); - return event_loop_ptr == self.inner.wlevl + return event_loop_ptr == self.inner.wlevl; } } diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index f0507819e12..9d52a75ae5c 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -140,6 +140,22 @@ impl Resource { } } + /// 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, + } + } + } + /// Posts a protocol error to this resource /// /// The error code can be obtained from the various `Error` enums of the protocols. @@ -354,7 +370,10 @@ impl NewResource { Dest: FnMut(Resource, Box, I::Request>>) + 'static, { unsafe { - assert!(token.matches(self.ptr), "Tried to implement a Resource with the wrong LoopToken."); + assert!( + token.matches(self.ptr), + "Tried to implement a Resource with the wrong LoopToken." + ); self.implement_inner(implementation, destructor) } } From 196d923c81900db89ed4ae26b92b8c74006f8c79 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sun, 15 Apr 2018 11:20:58 +0200 Subject: [PATCH 56/60] server: Client::equals(..) --- wayland-server/src/client.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wayland-server/src/client.rs b/wayland-server/src/client.rs index 4c1b0132f11..544164ecd93 100644 --- a/wayland-server/src/client.rs +++ b/wayland-server/src/client.rs @@ -81,6 +81,12 @@ impl Client { 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() { From d264ad02a3f4e0064765f0b4a14561c95d8c2be9 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sun, 15 Apr 2018 11:33:47 +0200 Subject: [PATCH 57/60] server: Resource::same_client_as(..) --- wayland-server/src/resource.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index 9d52a75ae5c..627a6ca5ec5 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -156,6 +156,28 @@ impl Resource { } } + /// 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. From df1b6324cadce55015fb9790c75ec69986d32e4a Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Tue, 17 Apr 2018 19:02:51 +0200 Subject: [PATCH 58/60] server: Display:c_ptr() --- wayland-server/src/display.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/wayland-server/src/display.rs b/wayland-server/src/display.rs index 5ea1b2ec5fe..45db9cde8bc 100644 --- a/wayland-server/src/display.rs +++ b/wayland-server/src/display.rs @@ -303,6 +303,14 @@ impl Display { } } +#[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 + } +} + fn get_runtime_dir() -> IoResult { match env::var_os("XDG_RUNTIME_DIR") { Some(s) => Ok(s.into()), From 18d1e457647499debff505d27cbeef9a6fd9e0b3 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 20 Apr 2018 10:10:17 +0200 Subject: [PATCH 59/60] Gracefully handle NULL objects from protocol races. --- wayland-client/src/proxy.rs | 15 +++++++++++++++ wayland-server/src/resource.rs | 14 ++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/wayland-client/src/proxy.rs b/wayland-client/src/proxy.rs index 7fb98d63267..3ca7328681d 100644 --- a/wayland-client/src/proxy.rs +++ b/wayland-client/src/proxy.rs @@ -234,9 +234,24 @@ impl Proxy { /// 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 _ diff --git a/wayland-server/src/resource.rs b/wayland-server/src/resource.rs index 627a6ca5ec5..d427b52d425 100644 --- a/wayland-server/src/resource.rs +++ b/wayland-server/src/resource.rs @@ -293,7 +293,21 @@ impl Resource { /// 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, From fb92d72821391eda6746849f9dbf0757ce74964a Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 20 Apr 2018 10:53:44 +0200 Subject: [PATCH 60/60] Update README and CHANGELOG --- CHANGELOG.md | 4 +++- README.md | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) 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/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.