diff --git a/examples/globals.rs b/examples/globals.rs new file mode 100644 index 0000000..139494f --- /dev/null +++ b/examples/globals.rs @@ -0,0 +1,26 @@ +// SPDIX-Licenes-Identifier: GPL-2.0-later + +use std::io; +use wayland_client::display::Display; + +fn main() -> io::Result<()> { + let display = Display::new(None)?; + let mut registry = display.get_registry(); + + // define on_global callback + registry.on_global(|interface, name, version|{ + println!("interface: {interface}, version: {version}, name: {name}") + }); + // define on_global_remove callback + registry.on_global_remove(|_name| { + // left blank + }); + // register the callbacks in wayland + registry.registry_listener(); + + // take a look around + let _ = display.roundtrip(); + + drop(display); + Ok(()) +} \ No newline at end of file diff --git a/examples/simple_client.rs b/examples/simple_client.rs new file mode 100644 index 0000000..584470a --- /dev/null +++ b/examples/simple_client.rs @@ -0,0 +1,17 @@ +// SPDX-License-Identifer: GPL-2.0-only + +use std::io; + +use wayland_client::display::Display; + +fn main() -> io::Result<()> { + let display = Display::new(None)?; + println!("Connection established!"); + + while let Some(_events) = display.dispatch() { + + } + + drop(display); + Ok(()) +} \ No newline at end of file diff --git a/src/display.rs b/src/display.rs new file mode 100644 index 0000000..f9ae218 --- /dev/null +++ b/src/display.rs @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only +use std::{env, fmt, io::{Error, ErrorKind}, ptr::NonNull}; +use crate::{display::ffi::{wl_display_dispatch, wl_proxy, wl_proxy_get_version, wl_proxy_marshal_flags, wl_registry_interface}, registry::Registry}; + +mod ffi { + use std::ffi::{c_char, c_int,c_void}; + + // relevant type definitions + #[repr(C)] + #[allow(non_camel_case_types)] + pub(super) struct wl_display; + #[repr(C)] + #[allow(non_camel_case_types)] + pub(super) struct wl_proxy; + #[repr(C)] + #[allow(non_camel_case_types)] + pub(super) struct wl_interface; + + #[link(name = "wayland-client")] + unsafe extern "C" { + // relevant public static + pub(crate) unsafe static wl_registry_interface: wl_interface; + + // relevant function definitions + pub(super) unsafe fn wl_display_connect(name: *const c_char) -> *mut c_void; + pub(super) unsafe fn wl_display_disconnect(disp: *mut wl_display); + pub(super) unsafe fn wl_display_dispatch(disp: *mut wl_display) -> c_int; + pub(super) unsafe fn wl_display_roundtrip(disp: *mut wl_display) -> c_int; + pub(super) unsafe fn wl_proxy_marshal_flags(proxy: *mut wl_proxy, opcode: u32, interface: *const wl_interface, version: u32, flags: u32, ptr: *mut c_void) -> *mut c_void; + pub(super) unsafe fn wl_proxy_get_version(proxy: *mut wl_proxy) -> u32; + } +} + +use ffi::wl_display; + +// core wayland elemenent: Display +pub struct Display { + raw: NonNull, +} +impl Display { + pub fn new(name: Option) -> Result { + let sock_name: String = if name.is_some() { + name.unwrap() + } else { + let env_name = env::var("WAYLAND_DISPLAY"); + if env_name.is_ok() { + env_name.unwrap() + } else { + "wayland-0".into() + } + }; + let binding = std::ffi::CString::new(sock_name).unwrap(); + let str = binding.as_c_str(); + let raw = unsafe { ffi::wl_display_connect(str.as_ptr()) }; + + let raw = NonNull::::new(raw as *mut wl_display) + .expect("Failed to connect to Wayland display."); + + Ok(Self{raw}) + } + pub fn get_registry(&self) -> Registry { + let registry = unsafe { + wl_proxy_marshal_flags(self.raw.as_ptr() as *mut wl_proxy, + 1, + &wl_registry_interface, + wl_proxy_get_version(self.raw.as_ptr() as *mut wl_proxy), + 0, + std::ptr::null_mut() + )}; + + return Registry::from(registry); + } + pub fn dispatch(&self) -> Option { + let events = unsafe {wl_display_dispatch(self.raw.as_ptr()) }; + + if events == -1 { + None + } else { + Some(events) + } + } + pub fn roundtrip(&self) -> std::io::Result { + let r = unsafe { ffi::wl_display_roundtrip(self.raw.as_ptr()) }; + + if r >= 0 { + Ok(r) + } else { + Err(Error::new(ErrorKind::Other, "unable to process server messages")) + } + } +} + +impl Drop for Display { + fn drop(&mut self) { + unsafe { ffi::wl_display_disconnect(self.raw.as_ptr()) }; + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index e0d02d0..b3be0a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,4 @@ // SPDX-License-Identifier: GPL-2.0-only -pub fn add(left: u64, right: u64) -> u64 { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +pub mod display; +pub mod registry; \ No newline at end of file diff --git a/src/registry.rs b/src/registry.rs new file mode 100644 index 0000000..c6f6afc --- /dev/null +++ b/src/registry.rs @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0-only + +use std::ffi::CStr; +use std::{ffi::{c_char,c_void}, ptr::NonNull}; + +use crate::registry::ffi::{wl_proxy, wl_proxy_add_listener, wl_registry, wl_registry_listener}; + +mod ffi { + use std::ffi::{c_char, c_void}; + + #[repr(C)] + #[allow(non_camel_case_types)] + pub(super) struct wl_registry; + #[repr(C)] + #[allow(non_camel_case_types)] + pub(super) struct wl_proxy; + + // function types + #[allow(non_camel_case_types)] + pub(super) type registry_global_callback = unsafe extern "C" fn(*mut c_void, *mut wl_registry, u32, *const c_char, u32); + #[allow(non_camel_case_types)] + pub(super) type registry_global_remove_callback = unsafe extern "C" fn(*mut c_void, *mut wl_registry, u32); + #[allow(non_camel_case_types)] + pub(super) type cb_implementation = unsafe extern "C" fn(); + + #[repr(C)] + pub(super) struct wl_registry_listener { + pub global: Option, + pub global_remove: Option + } + #[link(name = "wayland-client")] + unsafe extern "C" { + pub(super) unsafe fn wl_proxy_add_listener(proxy: *mut wl_proxy, implementation: *const cb_implementation, data: *mut c_void); + } +} + +// C->Rust trampoline functions +unsafe extern "C" fn registry_handle_global( + data: *mut c_void, + _registry: *mut wl_registry, + name: u32, + interface: *const c_char, + version: u32, +) { + let registry = unsafe { + &mut *(data as *mut RegistryListener) + }; + let interface = unsafe { + CStr::from_ptr(interface) + .to_string_lossy() + .into_owned() + }; + + if let Some(callback) = registry.global.as_mut() { + callback(interface, name, version) + } +} + +unsafe extern "C" fn registry_handle_global_remove( + data: *mut c_void, + _registry: *mut wl_registry, + name: u32, +) { + let registry = unsafe { + &mut *(data as *mut RegistryListener) + }; + + if let Some(callback) = registry.global_remove.as_mut() { + callback(name) + } +} + +// callback holder cell +pub struct RegistryListener { + global: Option>, + global_remove: Option>, +} + +// the main registry cell +pub struct Registry { + raw: NonNull, + + callbacks: Box, + + listener: Box, +} +impl Registry { + pub fn on_global(&mut self, global: F) + where + F: FnMut(String, u32, u32) + 'static + { + self.callbacks.global = Some(Box::new(global)); + } + pub fn on_global_remove(&mut self, global_remove: F) + where + F: FnMut(u32) + 'static + { + self.callbacks.global_remove = Some(Box::new(global_remove)); + } + pub fn registry_listener(&mut self) { + unsafe { + wl_proxy_add_listener( + self.raw.as_ptr() as *mut wl_proxy, + &*self.listener as *const _ as *const ffi::cb_implementation, + self.callbacks.as_mut() as *mut _ as *mut c_void) + }; + } +} +impl From<*mut c_void> for Registry { + fn from(value: *mut c_void) -> Self { + let raw = NonNull::new(value as *mut wl_registry) + .expect("unable to get display registry"); + + Self { + raw, + callbacks: Box::new( + RegistryListener { global: None, global_remove: None } + ), + listener: Box::new(ffi::wl_registry_listener { + global: Some(registry_handle_global), + global_remove: Some(registry_handle_global_remove), + }), + } + } +}