Skip to content

Commit

Permalink
Fix wayland window identifier
Browse files Browse the repository at this point in the history
  • Loading branch information
A6GibKm committed Jul 26, 2022
1 parent c26ec5d commit 46e80e4
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 80 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ futures = "0.3"
tracing = {version = "0.1", optional = true}
libc = {version = "0.2.94", optional = true}
raw-window-handle = {version = "0.4", optional = true}
wayland-client = {version = "0.30.0-beta.3", optional = true}
wayland-protocols = {version = "0.30.0-beta.3", optional = true, features = ["unstable", "client"]}
wayland-backend = {version = "0.1.0-beta.3", optional = true, features = ["client_system"]}
wayland-client = {version = "0.30.0-beta.8", optional = true}
wayland-protocols = {version = "0.30.0-beta.8", optional = true, features = ["unstable", "client"]}
wayland-backend = {version = "0.1.0-beta.8", optional = true, features = ["client_system"]}
async-std = {version = "1.11", optional = true}
tokio = {version = "1.17", features = ["fs", "io-util"], optional = true, default-features = false}
once_cell = "1.9"
Expand Down
17 changes: 11 additions & 6 deletions src/window_identifier/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,20 +215,25 @@ impl WindowIdentifier {
#[cfg(feature = "wayland")]
/// Create an instance of [`WindowIdentifier`] from a Wayland surface.
///
/// ## Safety
/// # Safety
///
/// The surface has to be a valid Wayland surface pointer.
pub unsafe fn from_wayland_raw(surface_ptr: *mut std::ffi::c_void) -> Self {
match WaylandWindowIdentifier::from_raw(surface_ptr) {
/// Both pointers have to be valid surface and display pointers. You must
/// ensure the `display_ptr` lives longer than the returned
/// `WindowIdentifier`.
pub async unsafe fn from_wayland_raw(
surface_ptr: *mut std::ffi::c_void,
display_ptr: *mut std::ffi::c_void,
) -> Self {
match WaylandWindowIdentifier::from_raw(surface_ptr, display_ptr).await {
Some(identifier) => Self::Wayland(identifier),
None => Self::default(),
}
}

#[cfg(feature = "wayland")]
/// Create an instance of [`WindowIdentifier`] from a Wayland surface.
pub fn from_wayland(surface: &wayland_client::protocol::wl_surface::WlSurface) -> Self {
match WaylandWindowIdentifier::new(surface) {
pub async fn from_wayland(surface: &wayland_client::protocol::wl_surface::WlSurface) -> Self {
match WaylandWindowIdentifier::new(surface).await {
Some(identifier) => Self::Wayland(identifier),
None => Self::default(),
}
Expand Down
285 changes: 214 additions & 71 deletions src/window_identifier/wayland.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,99 @@
use std::fmt;

use wayland_backend::sys::client::Backend;
use wayland_client::{
protocol::{__interfaces::WL_SURFACE_INTERFACE, wl_surface::WlSurface},
protocol::{wl_registry, wl_surface::WlSurface},
Proxy, QueueHandle,
};
use wayland_protocols::xdg::foreign::zv2::client::{
zxdg_exported_v2::{Event, ZxdgExportedV2},
zxdg_exporter_v2::ZxdgExporterV2,
use wayland_protocols::xdg::foreign::{
zv1::client::{
zxdg_exported_v1::{self, ZxdgExportedV1},
zxdg_exporter_v1::ZxdgExporterV1,
},
zv2::client::{
zxdg_exported_v2::{self, ZxdgExportedV2},
zxdg_exporter_v2::ZxdgExporterV2,
},
};

use super::WindowIdentifierType;

// Supported versions.
const ZXDG_EXPORTER_V1: u32 = 1;
const ZXDG_EXPORTER_V2: u32 = 1;

#[derive(Debug)]
pub struct WaylandWindowIdentifier {
exported: ZxdgExportedV2,
exported: Exported,
type_: WindowIdentifierType,
}

impl WaylandWindowIdentifier {
pub fn new(surface: &WlSurface) -> Option<Self> {
match wayland_handle_export_from_surface(surface) {
Ok((exported, handle)) => Some(Self {
exported,
type_: WindowIdentifierType::Wayland(handle),
}),
_ => None,
#[derive(Debug)]
enum Exported {
V1(ZxdgExportedV1),
V2(ZxdgExportedV2),
}

impl Exported {
fn destroy(&self) {
match self {
Self::V1(exported) => exported.destroy(),
Self::V2(exported) => exported.destroy(),
}
}
}

#[derive(Debug)]
enum Exporter {
V1(ZxdgExporterV1),
V2(ZxdgExporterV2),
}

impl WaylandWindowIdentifier {
pub async fn new(surface: &WlSurface) -> Option<Self> {
let backend = surface.backend().upgrade()?;
let conn = wayland_client::Connection::from_backend(backend);

Self::new_inner(conn, surface).await
}

pub fn from_raw(surface_ptr: *mut std::ffi::c_void) -> Option<Self> {
match wayland_handle_export(surface_ptr) {
Ok((exported, handle)) => Some(Self {
exported,
type_: WindowIdentifierType::Wayland(handle),
}),
_ => None,
pub async unsafe fn from_raw(
surface_ptr: *mut std::ffi::c_void,
display_ptr: *mut std::ffi::c_void,
) -> Option<Self> {
if surface_ptr.is_null() || display_ptr.is_null() {
return None;
}

let backend = Backend::from_foreign_display(display_ptr as *mut _);
let conn = wayland_client::Connection::from_backend(backend);
let obj_id = wayland_backend::sys::client::ObjectId::from_ptr(
WlSurface::interface(),
surface_ptr as *mut _,
)
.ok()?;

let surface = WlSurface::from_id(&conn, obj_id).ok()?;

Self::new_inner(conn, &surface).await
}

async fn new_inner(conn: wayland_client::Connection, surface: &WlSurface) -> Option<Self> {
let (sender, receiver) =
futures::channel::oneshot::channel::<Option<WaylandWindowIdentifier>>();

// Cheap clone, protocol objects are essentially smart pointers
let surface = surface.clone();
std::thread::spawn(move || match wayland_export_handle(conn, &surface) {
Ok(window_handle) => sender.send(Some(window_handle)).unwrap(),
Err(_err) => {
#[cfg(feature = "tracing")]
tracing::info!("Could not get wayland window identifier: {_err}");
sender.send(None).unwrap();
}
});

receiver.await.unwrap()
}
}

Expand All @@ -46,81 +105,165 @@ impl fmt::Display for WaylandWindowIdentifier {

impl Drop for WaylandWindowIdentifier {
fn drop(&mut self) {
if let Err(_err) = wayland_handle_unexport(&self.exported) {
#[cfg(feature = "tracing")]
tracing::error!("Failed to unexport wayland handle {}", _err);
}
self.exported.destroy();
}
}

#[derive(Default, Debug)]
struct ExportedWaylandHandle(String);
struct ExportedWaylandHandle {
handle: String,
exporter: Option<Exporter>,
}

impl wayland_client::Dispatch<ZxdgExportedV1, ()> for ExportedWaylandHandle {
fn event(
state: &mut ExportedWaylandHandle,
_proxy: &ZxdgExportedV1,
event: <ZxdgExportedV1 as Proxy>::Event,
_data: &(),
_connhandle: &wayland_client::Connection,
_qhandle: &QueueHandle<Self>,
) {
if let zxdg_exported_v1::Event::Handle { handle } = event {
state.handle = handle;
}
}
}

impl wayland_client::Dispatch<ZxdgExportedV2, ()> for ExportedWaylandHandle {
fn event(
&mut self,
state: &mut ExportedWaylandHandle,
_proxy: &ZxdgExportedV2,
event: <ZxdgExportedV2 as Proxy>::Event,
_data: &(),
_connhandle: &wayland_client::Connection,
_qhandle: &QueueHandle<Self>,
) {
match event {
Event::Handle { handle } => {
self.0 = handle;
}
_ => unreachable!(),
if let zxdg_exported_v2::Event::Handle { handle } = event {
state.handle = handle;
}
}
}

/// A helper to export a wayland handle from a WLSurface
///
/// Needed for converting a RawWindowHandle to a WindowIdentifier
fn wayland_handle_export(
surface_ptr: *mut std::ffi::c_void,
) -> Result<(ZxdgExportedV2, String), Box<dyn std::error::Error>> {
let cnx = wayland_client::Connection::connect_to_env()?;
let surface_id = unsafe {
wayland_backend::sys::client::ObjectId::from_ptr(
&WL_SURFACE_INTERFACE,
surface_ptr as *mut _,
)?
};
let surface = WlSurface::from_id(&cnx, surface_id)?;
impl wayland_client::Dispatch<ZxdgExporterV1, ()> for ExportedWaylandHandle {
fn event(
_state: &mut ExportedWaylandHandle,
_proxy: &ZxdgExporterV1,
_event: <ZxdgExporterV1 as Proxy>::Event,
_data: &(),
_connhandle: &wayland_client::Connection,
_qhandle: &QueueHandle<Self>,
) {
}
}

let exporter = ZxdgExporterV2::from_id(&cnx, surface.id())?;
let mut queue = cnx.new_event_queue();
let mut wl_handle = ExportedWaylandHandle::default();
impl wayland_client::Dispatch<ZxdgExporterV2, ()> for ExportedWaylandHandle {
fn event(
_state: &mut ExportedWaylandHandle,
_proxy: &ZxdgExporterV2,
_event: <ZxdgExporterV2 as Proxy>::Event,
_data: &(),
_connhandle: &wayland_client::Connection,
_qhandle: &QueueHandle<Self>,
) {
}
}

let queue_handle = queue.handle();
let exported = exporter.export_toplevel(&surface, &queue_handle, ())?;
queue.blocking_dispatch(&mut wl_handle)?;
Ok((exported, wl_handle.0))
impl wayland_client::Dispatch<wl_registry::WlRegistry, ()> for ExportedWaylandHandle {
fn event(
state: &mut ExportedWaylandHandle,
registry: &wl_registry::WlRegistry,
event: wl_registry::Event,
_: &(),
_: &wayland_client::Connection,
qhandle: &QueueHandle<Self>,
) {
if let wl_registry::Event::Global {
name,
interface,
version,
} = event
{
match interface.as_str() {
"zxdg_exporter_v1" => {
#[cfg(feature = "tracing")]
tracing::info!("Found wayland interface {interface} v{version}");
let exporter = registry
.bind::<ZxdgExporterV1, (), ExportedWaylandHandle>(
name,
version.min(ZXDG_EXPORTER_V1),
qhandle,
(),
)
.unwrap();
match state.exporter {
Some(Exporter::V2(_)) => (),
_ => state.exporter = Some(Exporter::V1(exporter)),
}
}
"zxdg_exporter_v2" => {
#[cfg(feature = "tracing")]
tracing::info!("Found wayland interface {interface} v{version}");
let exporter = registry
.bind::<ZxdgExporterV2, (), ExportedWaylandHandle>(
name,
version.min(ZXDG_EXPORTER_V2),
qhandle,
(),
)
.unwrap();
state.exporter = Some(Exporter::V2(exporter));
}
_ => (),
}
}
}
}

/// A helper to export a wayland handle from a WLSurface
/// A helper to export a wayland handle from a surface and a connection
///
/// Needed for converting a RawWindowHandle to a WindowIdentifier
fn wayland_handle_export_from_surface(
/// Needed for converting a RawWindowHandle to a WindowIdentifier.
fn wayland_export_handle(
conn: wayland_client::Connection,
surface: &WlSurface,
) -> Result<(ZxdgExportedV2, String), Box<dyn std::error::Error>> {
let cnx = wayland_client::Connection::connect_to_env()?;
) -> Result<WaylandWindowIdentifier, Box<dyn std::error::Error>> {
let display = conn.display();
let mut event_queue = conn.new_event_queue();
let mut state = ExportedWaylandHandle::default();
let qhandle = event_queue.handle();
display.get_registry(&qhandle, ())?;
event_queue.roundtrip(&mut state)?;

let exporter = ZxdgExporterV2::from_id(&cnx, surface.id())?;
let mut queue = cnx.new_event_queue();
let mut wl_handle = ExportedWaylandHandle::default();
let exported = match state.exporter.take() {
Some(Exporter::V2(exporter)) => {
let exp = exporter.export_toplevel(surface, &qhandle, ())?;
event_queue.roundtrip(&mut state)?;
exporter.destroy();

let queue_handle = queue.handle();
let exported = exporter.export_toplevel(surface, &queue_handle, ())?;
queue.blocking_dispatch(&mut wl_handle)?;
Ok((exported, wl_handle.0))
}
/// A helper to unexport a wayland handle from a previously exported one
///
/// Needed for converting a RawWindowHandle to a WindowIdentifier
fn wayland_handle_unexport(exported: &ZxdgExportedV2) -> Result<(), Box<dyn std::error::Error>> {
exported.destroy();
Some(Exported::V2(exp))
}
Some(Exporter::V1(exporter)) => {
let exp = exporter.export(surface, &qhandle, ())?;
event_queue.roundtrip(&mut state)?;
exporter.destroy();

Some(Exported::V1(exp))
}
None => {
#[cfg(feature = "tracing")]
tracing::error!(
"The compositor does not support the zxdg_exporter_v1 nor zxdg_exporter_v2 protocols"
);
None
}
};

Ok(())
if let Some(exported) = exported {
Ok(WaylandWindowIdentifier {
exported,
type_: WindowIdentifierType::Wayland(state.handle),
})
} else {
Err(Box::new(crate::Error::NoResponse))
}
}

0 comments on commit 46e80e4

Please sign in to comment.