Compare commits

...
2 Commits
Author SHA1 Message Date
neo 9ece322dc7 defining first Display and Registry structs. Added Callback support for Registry. 2026-08-27 17:52:14 +02:00
neo ef2051c07f cargo init lib 2026-08-27 14:23:21 +02:00
8 changed files with 290 additions and 0 deletions
+5
View File
@@ -16,3 +16,8 @@ target/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Added by cargo
/target
Generated
+7
View File
@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "wayland_client"
version = "0.1.0"
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "wayland_client"
version = "0.1.0"
edition = "2024"
publish = ["tcraft"]
license = "GPL-2.0-only"
keywords = ["ffi", "wayland"]
[dependencies]
+26
View File
@@ -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(())
}
+17
View File
@@ -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(())
}
+97
View File
@@ -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<wl_display>,
}
impl Display {
pub fn new(name: Option<String>) -> Result<Self, std::io::Error> {
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::<wl_display>::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<i32> {
let events = unsafe {wl_display_dispatch(self.raw.as_ptr()) };
if events == -1 {
None
} else {
Some(events)
}
}
pub fn roundtrip(&self) -> std::io::Result<i32> {
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()) };
}
}
+4
View File
@@ -0,0 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-only
pub mod display;
pub mod registry;
+125
View File
@@ -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<registry_global_callback>,
pub global_remove: Option<registry_global_remove_callback>
}
#[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<Box<dyn FnMut(String, u32, u32) + 'static>>,
global_remove: Option<Box<dyn FnMut(u32) + 'static>>,
}
// the main registry cell
pub struct Registry {
raw: NonNull<wl_registry>,
callbacks: Box<RegistryListener>,
listener: Box<wl_registry_listener>,
}
impl Registry {
pub fn on_global<F>(&mut self, global: F)
where
F: FnMut(String, u32, u32) + 'static
{
self.callbacks.global = Some(Box::new(global));
}
pub fn on_global_remove<F>(&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),
}),
}
}
}