Compare commits
3
Commits
a9780070f3
..
v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e54373717b | ||
|
|
9ece322dc7 | ||
|
|
ef2051c07f |
@@ -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
@@ -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"
|
||||
@@ -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]
|
||||
@@ -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.register_listener();
|
||||
|
||||
// take a look around
|
||||
let _ = display.roundtrip();
|
||||
|
||||
drop(display);
|
||||
Ok(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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()) };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
pub mod display;
|
||||
pub mod registry;
|
||||
+125
@@ -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 register_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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user