Skip to content

Commit

Permalink
client-toolkit: Add suport for cosmic-toplevel-info-v1 version 2
Browse files Browse the repository at this point in the history
This keeps the API compatible, adding fields to `ToplevelInfo` for the
foreign handle, identifier, and geometry. While also supporting version
1 if that's all the compositor exposes.
  • Loading branch information
ids1024 committed Oct 21, 2024
1 parent 91aeb55 commit 6c35e92
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 9 deletions.
1 change: 1 addition & 0 deletions client-toolkit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ sctk = { package = "smithay-client-toolkit", version = "0.19.1" }
wayland-client = { version = "0.31.1" }
smithay = { git = "https://github.com/Smithay/smithay", rev = "c35bc3e", default-features = false, features = ["backend_egl", "renderer_gl", "renderer_multi"], optional = true }
libc = "0.2.153"
wayland-protocols = { version = "0.32.4", features = ["client", "staging"] }

[build-dependencies]
gl_generator = { version = "0.14.0", optional = true }
Expand Down
4 changes: 4 additions & 0 deletions client-toolkit/examples/toplevel-monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ impl ToplevelInfoHandler for AppData {
self.toplevel_info_state.info(toplevel).unwrap()
);
}

fn info_done(&mut self, conn: &Connection, qh: &QueueHandle<Self>) {
println!("Info done");
}
}

fn main() {
Expand Down
1 change: 1 addition & 0 deletions client-toolkit/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub use cosmic_protocols;
pub use sctk;
pub use wayland_client;
pub use wayland_protocols;

#[cfg(feature = "gl")]
pub mod egl;
Expand Down
177 changes: 168 additions & 9 deletions client-toolkit/src/toplevel_info.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};

use cosmic_protocols::{
toplevel_info::v1::client::{zcosmic_toplevel_handle_v1, zcosmic_toplevel_info_v1},
workspace::v1::client::zcosmic_workspace_handle_v1,
};
use sctk::registry::RegistryState;
use wayland_client::{protocol::wl_output, Connection, Dispatch, QueueHandle};
use wayland_client::{protocol::wl_output, Connection, Dispatch, Proxy, QueueHandle};
use wayland_protocols::ext::foreign_toplevel_list::v1::client::{
ext_foreign_toplevel_handle_v1, ext_foreign_toplevel_list_v1,
};

#[derive(Clone, Debug, Default)]
pub struct ToplevelGeometry {
pub x: i32,
pub y: i32,
pub width: i32,
pub height: i32,
}

#[derive(Clone, Debug, Default)]
pub struct ToplevelInfo {
pub title: String,
pub app_id: String,
/// Requires zcosmic_toplevel_info_v1 version 2
pub identifier: Option<String>,
pub state: HashSet<zcosmic_toplevel_handle_v1::State>,
pub output: HashSet<wl_output::WlOutput>,
/// Requires zcosmic_toplevel_info_v1 version 2
pub geometry: HashMap<wl_output::WlOutput, ToplevelGeometry>,
pub workspace: HashSet<zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1>,
/// Requires zcosmic_toplevel_info_v1 version 2
pub foreign_toplevel: Option<ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1>,
}

#[derive(Debug, Default)]
Expand All @@ -24,6 +41,7 @@ struct ToplevelData {

#[derive(Debug)]
pub struct ToplevelInfoState {
pub cosmic_toplevel_info: zcosmic_toplevel_info_v1::ZcosmicToplevelInfoV1,
toplevels: Vec<(
zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
ToplevelData,
Expand All @@ -33,13 +51,24 @@ pub struct ToplevelInfoState {
impl ToplevelInfoState {
pub fn new<D>(registry: &RegistryState, qh: &QueueHandle<D>) -> Self
where
D: Dispatch<zcosmic_toplevel_info_v1::ZcosmicToplevelInfoV1, ()> + 'static,
D: Dispatch<zcosmic_toplevel_info_v1::ZcosmicToplevelInfoV1, ()>
+ Dispatch<ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, ()>
+ 'static,
{
registry
.bind_one::<zcosmic_toplevel_info_v1::ZcosmicToplevelInfoV1, _, _>(qh, 1..=1, ())
let cosmic_toplevel_info = registry
.bind_one::<zcosmic_toplevel_info_v1::ZcosmicToplevelInfoV1, _, _>(qh, 1..=2, ())
.unwrap();
if cosmic_toplevel_info.version() >= 2 {
let _ = registry
.bind_one::<ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, _, _>(
qh,
1..=1,
(),
);
}

Self {
cosmic_toplevel_info,
toplevels: Vec::new(),
}
}
Expand Down Expand Up @@ -93,6 +122,11 @@ pub trait ToplevelInfoHandler: Sized {
qh: &QueueHandle<Self>,
toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
);

/// Only sent for zcosmic_toplevel_info_v1 version 2
fn info_done(&mut self, conn: &Connection, qh: &QueueHandle<Self>) {}

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

impl<D> Dispatch<zcosmic_toplevel_info_v1::ZcosmicToplevelInfoV1, (), D> for ToplevelInfoState
Expand All @@ -104,11 +138,11 @@ where
{
fn event(
state: &mut D,
_: &zcosmic_toplevel_info_v1::ZcosmicToplevelInfoV1,
proxy: &zcosmic_toplevel_info_v1::ZcosmicToplevelInfoV1,
event: zcosmic_toplevel_info_v1::Event,
_: &(),
_: &Connection,
_: &QueueHandle<D>,
conn: &Connection,
qh: &QueueHandle<D>,
) {
match event {
zcosmic_toplevel_info_v1::Event::Toplevel { toplevel } => {
Expand All @@ -117,7 +151,12 @@ where
.toplevels
.push((toplevel, ToplevelData::default()));
}
zcosmic_toplevel_info_v1::Event::Finished => {}
zcosmic_toplevel_info_v1::Event::Done => {
state.info_done(conn, qh);
}
zcosmic_toplevel_info_v1::Event::Finished => {
state.finished(conn, qh);
}
_ => unreachable!(),
}
}
Expand Down Expand Up @@ -160,6 +199,7 @@ where
}
zcosmic_toplevel_handle_v1::Event::OutputLeave { output } => {
data.pending_info.output.remove(&output);
data.pending_info.geometry.remove(&output);
}
zcosmic_toplevel_handle_v1::Event::WorkspaceEnter { workspace } => {
data.pending_info.workspace.insert(workspace);
Expand All @@ -179,6 +219,23 @@ where
}
}
}
zcosmic_toplevel_handle_v1::Event::Geometry {
output,
x,
y,
width,
height,
} => {
data.pending_info.geometry.insert(
output,
ToplevelGeometry {
x,
y,
width,
height,
},
);
}
zcosmic_toplevel_handle_v1::Event::Done => {
let is_new = data.current_info.is_none();
data.current_info = Some(data.pending_info.clone());
Expand All @@ -201,6 +258,102 @@ where
}
}

impl<D> Dispatch<ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, (), D>
for ToplevelInfoState
where
D: Dispatch<ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, ()>
+ Dispatch<ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, ()>
+ Dispatch<zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, ()>
+ ToplevelInfoHandler
+ 'static,
{
fn event(
state: &mut D,
proxy: &ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
event: ext_foreign_toplevel_list_v1::Event,
_: &(),
conn: &Connection,
qh: &QueueHandle<D>,
) {
match event {
ext_foreign_toplevel_list_v1::Event::Toplevel { toplevel } => {
let info_state = state.toplevel_info_state();
let cosmic_toplevel =
info_state
.cosmic_toplevel_info
.get_cosmic_toplevel(&toplevel, qh, ());
let mut toplevel_data = ToplevelData::default();
toplevel_data.pending_info.foreign_toplevel = Some(toplevel);
info_state.toplevels.push((cosmic_toplevel, toplevel_data));
}
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, (), D>
for ToplevelInfoState
where
D: Dispatch<ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, ()>
+ ToplevelInfoHandler,
{
fn event(
state: &mut D,
handle: &ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
event: ext_foreign_toplevel_handle_v1::Event,
data: &(),
conn: &Connection,
qh: &QueueHandle<D>,
) {
let (toplevel, data) = &mut state
.toplevel_info_state()
.toplevels
.iter_mut()
.find(|(_, data)| data.pending_info.foreign_toplevel.as_ref() == Some(handle))
.expect("Received event for dead toplevel");
match event {
ext_foreign_toplevel_handle_v1::Event::Closed => {
let toplevel = toplevel.clone();
state.toplevel_closed(conn, qh, &toplevel);

let toplevels = &mut state.toplevel_info_state().toplevels;
if let Some(idx) = toplevels.iter().position(|(handle, _)| handle == &toplevel) {
toplevels.remove(idx);
}
}
ext_foreign_toplevel_handle_v1::Event::Done => {
// XXX
let is_new = data.current_info.is_none();
data.current_info = Some(data.pending_info.clone());
let toplevel = toplevel.clone();
if is_new {
state.new_toplevel(conn, qh, &toplevel);
} else {
state.update_toplevel(conn, qh, &toplevel);
}
}
ext_foreign_toplevel_handle_v1::Event::Title { title } => {
data.pending_info.title = title;
}
ext_foreign_toplevel_handle_v1::Event::AppId { app_id } => {
data.pending_info.app_id = app_id;
}
ext_foreign_toplevel_handle_v1::Event::Identifier { identifier } => {
data.pending_info.identifier = Some(identifier);
}
_ => unreachable!(),
}
}
}

#[macro_export]
macro_rules! delegate_toplevel_info {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
Expand All @@ -210,5 +363,11 @@ macro_rules! delegate_toplevel_info {
$crate::wayland_client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1: ()
] => $crate::toplevel_info::ToplevelInfoState);
$crate::wayland_client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1: ()
] => $crate::toplevel_info::ToplevelInfoState);
$crate::wayland_client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
$crate::wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1: ()
] => $crate::toplevel_info::ToplevelInfoState);
};
}

0 comments on commit 6c35e92

Please sign in to comment.