Skip to content

Commit

Permalink
Add ext-foreign-toplevel-list-v1 protocol
Browse files Browse the repository at this point in the history
Provides a handler API similar to the output API.
  • Loading branch information
ids1024 committed Aug 28, 2024
1 parent 0233ee6 commit 614b007
Show file tree
Hide file tree
Showing 3 changed files with 275 additions and 0 deletions.
81 changes: 81 additions & 0 deletions examples/foreign-toplevel-monitor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use std::error::Error;

use smithay_client_toolkit::{
delegate_foreign_toplevel_list, delegate_registry,
foreign_toplevel_list::{ForeignToplevelList, ForeignToplevelListHandler},
registry::{ProvidesRegistryState, RegistryState},
registry_handlers,
};
use wayland_client::{globals::registry_queue_init, Connection, QueueHandle};
use wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1;

struct State {
registry_state: RegistryState,
foreign_toplevel_list: ForeignToplevelList,
}

fn main() -> Result<(), Box<dyn Error>> {
let conn = Connection::connect_to_env()?;
let (globals, mut event_queue) = registry_queue_init(&conn)?;
let qh = event_queue.handle();
let registry_state = RegistryState::new(&globals);
let foreign_toplevel_list = ForeignToplevelList::new(&globals, &qh);

let mut state = State { registry_state, foreign_toplevel_list };
loop {
event_queue.blocking_dispatch(&mut state)?;
}
}

impl ProvidesRegistryState for State {
fn registry(&mut self) -> &mut RegistryState {
&mut self.registry_state
}

registry_handlers! {}
}

impl ForeignToplevelListHandler for State {
fn foreign_toplevel_list_state(&mut self) -> &mut ForeignToplevelList {
&mut self.foreign_toplevel_list
}

fn new_toplevel(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
toplevel_handle: ExtForeignToplevelHandleV1,
) {
let Some(info) = self.foreign_toplevel_list.info(&toplevel_handle) else {
return;
};
println!("New toplevel: {:?}", info);
}

fn update_toplevel(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
toplevel_handle: ExtForeignToplevelHandleV1,
) {
let Some(info) = self.foreign_toplevel_list.info(&toplevel_handle) else {
return;
};
println!("Update toplevel: {:?}", info);
}

fn toplevel_closed(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
toplevel_handle: ExtForeignToplevelHandleV1,
) {
let Some(info) = self.foreign_toplevel_list.info(&toplevel_handle) else {
return;
};
println!("Close toplevel: {:?}", info);
}
}

delegate_foreign_toplevel_list!(State);
delegate_registry!(State);
193 changes: 193 additions & 0 deletions src/foreign_toplevel_list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
use crate::{globals::GlobalData, registry::GlobalProxy};
use std::sync::{Arc, Mutex};
use wayland_client::{globals::GlobalList, Connection, Dispatch, Proxy, QueueHandle};
use wayland_protocols::ext::foreign_toplevel_list::v1::client::{
ext_foreign_toplevel_handle_v1, ext_foreign_toplevel_list_v1,
};

/// Information about a toplevel.
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct ForeignToplevelInfo {
/// Title
pub title: String,
/// App id
pub app_id: String,
/// Identifier to check if two toplevel handles refer to same toplevel
pub identifier: String,
}

#[derive(Debug, Default)]
struct ForeignToplevelInner {
current_info: Option<ForeignToplevelInfo>,
pending_info: ForeignToplevelInfo,
}

#[doc(hidden)]
#[derive(Debug, Default, Clone)]
pub struct ForeignToplevelData(Arc<Mutex<ForeignToplevelInner>>);

#[derive(Debug)]
pub struct ForeignToplevelList {
foreign_toplevel_list: GlobalProxy<ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1>,
toplevels: Vec<ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1>,
}

impl ForeignToplevelList {
pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Self
where
D: Dispatch<ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, GlobalData> + 'static,
{
let foreign_toplevel_list = GlobalProxy::from(globals.bind(qh, 1..=1, GlobalData));
Self { foreign_toplevel_list, toplevels: Vec::new() }
}

/// Returns list of toplevels.
pub fn toplevels(&self) -> &[ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1] {
&self.toplevels
}

/// Returns information about a toplevel.
///
/// This may be none if the toplevel has been destroyed or the compositor has not sent
/// information about the toplevel yet.
pub fn info(
&self,
toplevel: &ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
) -> Option<ForeignToplevelInfo> {
toplevel.data::<ForeignToplevelData>()?.0.lock().unwrap().current_info.clone()
}

pub fn stop(&self) {
if let Ok(toplevel_list) = self.foreign_toplevel_list.get() {
toplevel_list.stop();
}
}
}

/// Handler trait for foreign toplevel list protocol.
pub trait ForeignToplevelListHandler: Sized {
fn foreign_toplevel_list_state(&mut self) -> &mut ForeignToplevelList;

/// A new toplevel has been opened.
fn new_toplevel(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
toplevel_handle: ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
);

/// An existing toplevel has changed.
fn update_toplevel(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
toplevel_handle: ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
);

/// A toplevel has closed.
fn toplevel_closed(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
toplevel_handle: ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
);

fn finished(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>) {}
}

impl<D> Dispatch<ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, GlobalData, D>
for ForeignToplevelList
where
D: Dispatch<ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, GlobalData>
+ Dispatch<ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, ForeignToplevelData>
+ ForeignToplevelListHandler
+ 'static,
{
fn event(
state: &mut D,
proxy: &ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
event: ext_foreign_toplevel_list_v1::Event,
_: &GlobalData,
conn: &Connection,
qh: &QueueHandle<D>,
) {
match event {
ext_foreign_toplevel_list_v1::Event::Toplevel { toplevel: _ } => {}
ext_foreign_toplevel_list_v1::Event::Finished => {
state.finished(conn, qh);
proxy.destroy();
}
_ => unreachable!(),
}
}

wayland_client::event_created_child!(D, ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, [
ext_foreign_toplevel_list_v1::EVT_TOPLEVEL_OPCODE => (ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, Default::default())
]);
}

impl<D> Dispatch<ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, ForeignToplevelData, D>
for ForeignToplevelList
where
D: Dispatch<ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, ForeignToplevelData>
+ ForeignToplevelListHandler,
{
fn event(
state: &mut D,
handle: &ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
event: ext_foreign_toplevel_handle_v1::Event,
data: &ForeignToplevelData,
conn: &Connection,
qh: &QueueHandle<D>,
) {
match event {
ext_foreign_toplevel_handle_v1::Event::Closed => {
state.toplevel_closed(conn, qh, handle.clone());
let toplevels = &mut state.foreign_toplevel_list_state().toplevels;
if let Some(idx) = toplevels.iter().position(|x| x == handle) {
toplevels.remove(idx);
}
handle.destroy();
}
ext_foreign_toplevel_handle_v1::Event::Done => {
let mut inner = data.0.lock().unwrap();
let just_created = inner.current_info.is_none();
inner.current_info = Some(inner.pending_info.clone());
drop(inner);
if just_created {
state.foreign_toplevel_list_state().toplevels.push(handle.clone());
state.new_toplevel(conn, qh, handle.clone());
} else {
state.update_toplevel(conn, qh, handle.clone());
}
}
ext_foreign_toplevel_handle_v1::Event::Title { title } => {
data.0.lock().unwrap().pending_info.title = title;
}
ext_foreign_toplevel_handle_v1::Event::AppId { app_id } => {
data.0.lock().unwrap().pending_info.app_id = app_id;
}
ext_foreign_toplevel_handle_v1::Event::Identifier { identifier } => {
data.0.lock().unwrap().pending_info.identifier = identifier;
}
_ => unreachable!(),
}
}
}

#[macro_export]
macro_rules! delegate_foreign_toplevel_list {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
$crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
[
$crate::reexports::protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1: $crate::globals::GlobalData
] => $crate::foreign_toplevel_list::ForeignToplevelList
);
$crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
[
$crate::reexports::protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1: $crate::foreign_toplevel_list::ForeignToplevelData
] => $crate::foreign_toplevel_list::ForeignToplevelList
);
};
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub mod compositor;
pub mod data_device_manager;
pub mod dmabuf;
pub mod error;
pub mod foreign_toplevel_list;
pub mod globals;
pub mod output;
pub mod primary_selection;
Expand Down

0 comments on commit 614b007

Please sign in to comment.