wayland: toplevel management protocol

This commit is contained in:
Victoria Brekenfeld 2022-07-18 21:26:02 +02:00
parent 4f142d50b3
commit 6b659eb107
9 changed files with 340 additions and 54 deletions

42
Cargo.lock generated
View file

@ -361,11 +361,10 @@ dependencies = [
[[package]]
name = "cosmic-protocols"
version = "0.1.0"
source = "git+https://github.com/pop-os/cosmic-protocols?branch=main#55f15e8b05fc983ab36b65b4c027b59f5876a181"
source = "git+https://github.com/pop-os/cosmic-protocols?branch=main#81d6a50bdc91af5968f87785fc19a16cf261c96b"
dependencies = [
"bitflags",
"wayland-backend",
"wayland-client 0.30.0-beta.8",
"wayland-protocols 0.30.0-beta.8",
"wayland-scanner 0.30.0-beta.8",
"wayland-server",
@ -649,21 +648,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "futures-channel"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
[[package]]
name = "gbm"
version = "0.8.0"
@ -1569,7 +1553,7 @@ dependencies = [
"memmap2",
"nix 0.22.3",
"pkg-config",
"wayland-client 0.29.4",
"wayland-client",
"wayland-cursor",
"wayland-protocols 0.29.4",
]
@ -1844,21 +1828,6 @@ dependencies = [
"wayland-sys 0.29.4",
]
[[package]]
name = "wayland-client"
version = "0.30.0-beta.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f9e0d862c23f07b2c4b49de66b0680948af5dd1d2def17f1ddc16520352bf14"
dependencies = [
"bitflags",
"futures-channel",
"futures-core",
"nix 0.24.2",
"thiserror",
"wayland-backend",
"wayland-scanner 0.30.0-beta.8",
]
[[package]]
name = "wayland-commons"
version = "0.29.4"
@ -1878,7 +1847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c52758f13d5e7861fc83d942d3d99bf270c83269575e52ac29e5b73cb956a6bd"
dependencies = [
"nix 0.22.3",
"wayland-client 0.29.4",
"wayland-client",
"xcursor",
]
@ -1900,7 +1869,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60147ae23303402e41fe034f74fb2c35ad0780ee88a1c40ac09a3be1e7465741"
dependencies = [
"bitflags",
"wayland-client 0.29.4",
"wayland-client",
"wayland-commons",
"wayland-scanner 0.29.4",
]
@ -1913,7 +1882,6 @@ checksum = "e47c45a60d531d5a513601f47f51a4743901836778ddae208ae9124606be1719"
dependencies = [
"bitflags",
"wayland-backend",
"wayland-client 0.30.0-beta.8",
"wayland-scanner 0.30.0-beta.8",
"wayland-server",
]
@ -2102,7 +2070,7 @@ dependencies = [
"raw-window-handle",
"smithay-client-toolkit",
"wasm-bindgen",
"wayland-client 0.29.4",
"wayland-client",
"wayland-protocols 0.29.4",
"web-sys",
"winapi",

View file

@ -31,7 +31,7 @@ atomic_float = "0.1"
libsystemd = "0.5"
wayland-backend = "=0.1.0-beta.8"
wayland-scanner = "=0.30.0-beta.8"
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", branch = "main" }
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", branch = "main", default-features = false, features = ["server"] }
[dependencies.smithay]
version = "0.3"

View file

@ -22,9 +22,11 @@ use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State
use crate::{
config::{Config, WorkspaceMode as ConfigMode},
//state::ClientState,
utils::prelude::*,
wayland::protocols::{
toplevel_info::ToplevelInfoState,
toplevel_management::{ToplevelManagementState, ManagementCapabilities},
workspace::{
WorkspaceCapabilities, WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState,
WorkspaceUpdateGuard,
@ -52,6 +54,7 @@ pub struct Shell {
// wayland_state
pub layer_shell_state: WlrLayerShellState,
pub toplevel_info_state: ToplevelInfoState<State>,
pub toplevel_management_state: ToplevelManagementState,
pub xdg_shell_state: XdgShellState,
pub workspace_state: WorkspaceState<State>,
}
@ -81,10 +84,24 @@ const UNINIT_SPACE: MaybeUninit<Workspace> = MaybeUninit::uninit();
impl Shell {
pub fn new(config: &Config, dh: &DisplayHandle) -> Self {
// TODO: Privileged protocols
let layer_shell_state = WlrLayerShellState::new::<State, _>(dh, None);
let toplevel_info_state = ToplevelInfoState::new(dh, |_| true);
let xdg_shell_state = XdgShellState::new::<State, _>(dh, None);
let mut workspace_state = WorkspaceState::new(dh, |_| true);
let toplevel_info_state = ToplevelInfoState::new(
dh,
//|client| client.get_data::<ClientState>().unwrap().privileged,
|_| true);
let toplevel_management_state = ToplevelManagementState::new::<State, _>(
dh,
vec![ManagementCapabilities::Close, ManagementCapabilities::Activate],
//|client| client.get_data::<ClientState>().unwrap().privileged,
|_| true,
);
let mut workspace_state = WorkspaceState::new(
dh,
//|client| client.get_data::<ClientState>().unwrap().privileged,
|_| true,
);
let mut spaces = unsafe {
let mut spaces = [UNINIT_SPACE; MAX_WORKSPACES];
@ -116,6 +133,7 @@ impl Shell {
layer_shell_state,
toplevel_info_state,
toplevel_management_state,
xdg_shell_state,
workspace_state,
}

View file

@ -20,7 +20,7 @@ use smithay::{
use std::collections::HashMap;
pub struct Workspace {
pub(super) idx: u8,
pub idx: u8,
pub space: Space,
pub tiling_layer: TilingLayout,
pub floating_layer: FloatingLayout,

View file

@ -11,6 +11,7 @@ pub mod primary_selection;
pub mod seat;
pub mod shm;
pub mod toplevel_info;
pub mod toplevel_management;
pub mod viewporter;
pub mod wl_drm;
pub mod workspace;

View file

@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::{
desktop::{Kind, Window},
reexports::wayland_server::DisplayHandle,
wayland::seat::Seat,
};
use crate::{
utils::prelude::*,
wayland::protocols::toplevel_management::{
delegate_toplevel_management, ToplevelManagementHandler, ToplevelManagementState,
},
};
impl ToplevelManagementHandler for State {
fn toplevel_management_state(&mut self) -> &mut ToplevelManagementState {
&mut self.common.shell.toplevel_management_state
}
fn activate(&mut self, dh: &DisplayHandle, window: &Window, seat: Option<Seat<Self>>) {
if let Some(idx) = self.common.shell.space_for_window(window.toplevel().wl_surface()).map(|w| w.idx) {
let seat = seat.unwrap_or(self.common.last_active_seat.clone());
let output = active_output(&seat, &self.common);
if self.common.shell.active_space(&output).idx != idx {
self.common.shell.activate(&seat, &output, idx as usize);
}
self.common.set_focus(
dh,
Some(window.toplevel().wl_surface()),
&seat,
None,
);
}
}
fn close(&mut self, _dh: &DisplayHandle, window: &Window) {
#[allow(irrefutable_let_patterns)]
if let Kind::Xdg(xdg) = &window.toplevel() {
xdg.send_close();
}
}
}
delegate_toplevel_management!(State);

View file

@ -3,4 +3,5 @@
pub mod drm;
pub mod output_configuration;
pub mod toplevel_info;
pub mod toplevel_management;
pub mod workspace;

View file

@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only
use std::sync::Mutex;
use std::{
collections::HashMap,
sync::Mutex,
};
use smithay::{
desktop::Window,
@ -8,11 +11,12 @@ use smithay::{
wayland_protocols::xdg::shell::server::xdg_toplevel,
wayland_server::{
backend::{ClientId, GlobalId, ObjectId},
protocol::wl_surface::WlSurface,
Client, DataInit, Dispatch, DisplayHandle,
GlobalDispatch, New, Resource,
},
},
utils::IsAlive,
utils::{IsAlive, Rectangle, Logical},
wayland::{
compositor::with_states, output::Output, shell::xdg::XdgToplevelSurfaceRoleAttributes,
},
@ -27,7 +31,7 @@ use cosmic_protocols::toplevel_info::v1::server::{
pub struct ToplevelInfoState<D> {
dh: DisplayHandle,
toplevels: Vec<Window>,
pub(super) toplevels: Vec<Window>,
instances: Vec<ZcosmicToplevelInfoV1>,
global: GlobalId,
_dispatch_data: std::marker::PhantomData<D>,
@ -43,24 +47,40 @@ pub struct ToplevelInfoGlobalData {
}
#[derive(Default)]
struct ToplevelStateInner {
pub(super) struct ToplevelStateInner {
instances: Vec<ZcosmicToplevelHandleV1>,
outputs: Vec<Output>,
workspaces: Vec<WorkspaceHandle>,
minimized: bool,
pub(super) rectangles: HashMap<ClientId, (WlSurface, Rectangle<i32, Logical>)>,
}
type ToplevelState = Mutex<ToplevelStateInner>;
pub(super) type ToplevelState = Mutex<ToplevelStateInner>;
#[derive(Default)]
pub struct ToplevelHandleStateInner {
outputs: Vec<Output>,
workspaces: Vec<WorkspaceHandle>,
title: String,
app_id: String,
states: Vec<States>,
pub(super) window: Window,
}
pub type ToplevelHandleState = Mutex<ToplevelHandleStateInner>;
impl ToplevelHandleStateInner {
fn from_window(window: &Window) -> ToplevelHandleState {
ToplevelHandleState::new(
ToplevelHandleStateInner {
outputs: Vec::new(),
workspaces: Vec::new(),
title: String::new(),
app_id: String::new(),
states: Vec::new(),
window: window.clone(),
}
)
}
}
impl<D> GlobalDispatch<ZcosmicToplevelInfoV1, ToplevelInfoGlobalData, D>
for ToplevelInfoState<D>
where
@ -82,6 +102,7 @@ where
for window in &state.toplevel_info_state().toplevels {
send_toplevel_to_client::<D>(dh, Some(state.workspace_state()), &instance, window);
}
state.toplevel_info_state_mut().instances.push(instance);
}
fn can_view(client: Client, global_data: &ToplevelInfoGlobalData) -> bool {
@ -229,18 +250,21 @@ where
pub fn refresh(&mut self, workspace_state: Option<&WorkspaceState<D>>) {
self.toplevels.retain(|window| {
let mut state = window
.user_data()
.get::<ToplevelState>()
.unwrap()
.lock()
.unwrap();
state.rectangles
.retain(|_, (surface, _)| surface.alive());
if window.alive() {
std::mem::drop(state);
for instance in &self.instances {
send_toplevel_to_client::<D>(&self.dh, workspace_state, instance, window);
}
true
} else {
let state = window
.user_data()
.get::<ToplevelState>()
.unwrap()
.lock()
.unwrap();
for handle in &state.instances {
// don't send events to stopped instances
if self
@ -291,7 +315,7 @@ fn send_toplevel_to_client<D>(
.create_resource::<ZcosmicToplevelHandleV1, _, D>(
dh,
info.version(),
ToplevelHandleState::default(),
ToplevelHandleStateInner::from_window(window),
)
{
state.instances.push(toplevel_handle);
@ -448,6 +472,16 @@ fn send_toplevel_to_client<D>(
}
}
pub(super) fn window_from_handle(handle: ZcosmicToplevelHandleV1) -> Window {
handle
.data::<ToplevelHandleState>()
.unwrap()
.lock()
.unwrap()
.window
.clone()
}
macro_rules! delegate_toplevel_info {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [

View file

@ -0,0 +1,219 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::{
desktop::Window,
reexports::{
wayland_server::{
backend::{ClientId, GlobalId, ObjectId},
protocol::wl_surface::WlSurface,
Client, DataInit, Dispatch, DisplayHandle,
GlobalDispatch, New, Resource,
},
},
utils::{Logical, Rectangle},
wayland::{
output::Output,
seat::Seat,
},
};
use cosmic_protocols::toplevel_management::v1::server::{
zcosmic_toplevel_manager_v1::{self, ZcosmicToplevelManagerV1},
};
pub use cosmic_protocols::toplevel_management::v1::server::zcosmic_toplevel_manager_v1::ZcosmicToplelevelManagementCapabilitiesV1 as ManagementCapabilities;
use super::toplevel_info::{ToplevelInfoHandler, ToplevelHandleState, ToplevelState, window_from_handle};
pub struct ToplevelManagementState {
instances: Vec<ZcosmicToplevelManagerV1>,
capabilities: Vec<ManagementCapabilities>,
global: GlobalId,
}
#[allow(unused_variables)]
pub trait ToplevelManagementHandler: ToplevelInfoHandler {
fn toplevel_management_state(&mut self) -> &mut ToplevelManagementState;
fn activate(&mut self, dh: &DisplayHandle, window: &Window, seat: Option<Seat<Self>>) {}
fn close(&mut self, dh: &DisplayHandle, window: &Window) {}
fn fullscreen(&mut self, dh: &DisplayHandle, window: &Window, output: Option<Output>) {}
fn unfullscreen(&mut self, dh: &DisplayHandle, window: &Window) {}
fn maximize(&mut self, dh: &DisplayHandle, window: &Window) {}
fn unmaximize(&mut self, dh: &DisplayHandle, window: &Window) {}
fn minimize(&mut self, dh: &DisplayHandle, window: &Window) {}
fn unminimize(&mut self, dh: &DisplayHandle, window: &Window) {}
}
pub struct ToplevelManagerGlobalData {
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
}
impl ToplevelManagementState {
pub fn new<D, F>(dh: &DisplayHandle, capabilities: Vec<ManagementCapabilities>, client_filter: F) -> ToplevelManagementState
where
D: GlobalDispatch<ZcosmicToplevelManagerV1, ToplevelManagerGlobalData>
+ Dispatch<ZcosmicToplevelManagerV1, ()>
+ ToplevelManagementHandler
+ 'static,
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static
{
let global = dh.create_global::<D, ZcosmicToplevelManagerV1, _>(
1,
ToplevelManagerGlobalData {
filter: Box::new(client_filter),
},
);
ToplevelManagementState {
capabilities,
instances: Vec::new(),
global,
}
}
pub fn rectangle_for(&mut self, window: &Window, client: &ClientId) -> Option<(WlSurface, Rectangle<i32, Logical>)> {
if let Some(state) = window.user_data().get::<ToplevelState>() {
state.lock().unwrap().rectangles.get(client).cloned()
} else {
None
}
}
pub fn global_id(&self) -> GlobalId {
self.global.clone()
}
}
impl<D> GlobalDispatch<ZcosmicToplevelManagerV1, ToplevelManagerGlobalData, D> for ToplevelManagementState
where
D: GlobalDispatch<ZcosmicToplevelManagerV1, ToplevelManagerGlobalData>
+ Dispatch<ZcosmicToplevelManagerV1, ()>
+ ToplevelManagementHandler
+ 'static
{
fn bind(
state: &mut D,
_dh: &DisplayHandle,
_client: &Client,
resource: New<ZcosmicToplevelManagerV1>,
_global_data: &ToplevelManagerGlobalData,
data_init: &mut DataInit<'_, D>,
) {
let instance = data_init.init(resource, ());
let capabilities = {
let mut caps = state.toplevel_management_state().capabilities.clone();
let ratio = std::mem::size_of::<ManagementCapabilities>() / std::mem::size_of::<u8>();
let ptr = caps.as_mut_ptr() as *mut u8;
let len = caps.len() * ratio;
let cap = caps.capacity() * ratio;
std::mem::forget(caps);
unsafe { Vec::from_raw_parts(ptr, len, cap) }
};
instance.capabilities(capabilities);
state.toplevel_management_state().instances.push(instance);
}
fn can_view(client: Client, global_data: &ToplevelManagerGlobalData) -> bool {
(global_data.filter)(&client)
}
}
impl<D> Dispatch<ZcosmicToplevelManagerV1, (), D> for ToplevelManagementState
where
D: GlobalDispatch<ZcosmicToplevelManagerV1, ToplevelManagerGlobalData>
+ Dispatch<ZcosmicToplevelManagerV1, ()>
+ ToplevelManagementHandler
+ 'static
{
fn request(
state: &mut D,
_client: &Client,
_obj: &ZcosmicToplevelManagerV1,
request: zcosmic_toplevel_manager_v1::Request,
_data: &(),
dh: &DisplayHandle,
_data_init: &mut DataInit<'_, D>,
) {
match request {
zcosmic_toplevel_manager_v1::Request::Activate { toplevel, seat } => {
let window = window_from_handle(toplevel);
state.activate(dh, &window, Seat::from_resource(&seat));
},
zcosmic_toplevel_manager_v1::Request::Close { toplevel } => {
let window = window_from_handle(toplevel);
state.close(dh, &window);
},
zcosmic_toplevel_manager_v1::Request::SetFullscreen { toplevel, output } => {
let window = window_from_handle(toplevel);
state.fullscreen(dh, &window, output.as_ref().and_then(Output::from_resource))
},
zcosmic_toplevel_manager_v1::Request::UnsetFullscreen { toplevel } => {
let window = window_from_handle(toplevel);
state.unfullscreen(dh, &window);
},
zcosmic_toplevel_manager_v1::Request::SetMaximized { toplevel } => {
let window = window_from_handle(toplevel);
state.maximize(dh, &window);
},
zcosmic_toplevel_manager_v1::Request::UnsetMaximized { toplevel } => {
let window = window_from_handle(toplevel);
state.unmaximize(dh, &window);
},
zcosmic_toplevel_manager_v1::Request::SetMinimized { toplevel } => {
let window = window_from_handle(toplevel);
state.minimize(dh, &window);
},
zcosmic_toplevel_manager_v1::Request::UnsetMinimized { toplevel } => {
let window = window_from_handle(toplevel);
state.unminimize(dh, &window);
},
zcosmic_toplevel_manager_v1::Request::SetRectangle { toplevel, surface, x, y, width, height } => {
let window = toplevel
.data::<ToplevelHandleState>()
.unwrap()
.lock()
.unwrap()
.window
.clone();
if let Some(toplevel_state) = window.user_data().get::<ToplevelState>() {
let mut toplevel_state = toplevel_state.lock().unwrap();
if let Some(client) = surface.client_id() {
if width == 0 && height == 0 {
toplevel_state.rectangles.remove(&client);
} else {
toplevel_state.rectangles.insert(client, (surface, Rectangle::from_loc_and_size((x, y), (width, height))));
}
}
}
},
_ => unreachable!(),
}
}
fn destroyed(state: &mut D, client: ClientId, resource: ObjectId, _data: &()) {
let mng_state = state.toplevel_management_state();
mng_state.instances.retain(|i| i.id() != resource);
if !mng_state.instances.iter().any(|i| i.client_id().map(|c| c == client).unwrap_or(false)) {
for toplevel in state.toplevel_info_state_mut().toplevels.iter() {
if let Some(toplevel_state) = toplevel.user_data().get::<ToplevelState>() {
toplevel_state.lock().unwrap().rectangles.remove(&client);
}
}
}
}
}
macro_rules! delegate_toplevel_management {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::toplevel_management::v1::server::zcosmic_toplevel_manager_v1::ZcosmicToplevelManagerV1: $crate::wayland::protocols::toplevel_management::ToplevelManagerGlobalData
] => $crate::wayland::protocols::toplevel_management::ToplevelManagementState);
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::toplevel_management::v1::server::zcosmic_toplevel_manager_v1::ZcosmicToplevelManagerV1: ()
] => $crate::wayland::protocols::toplevel_management::ToplevelManagementState);
};
}
pub(crate) use delegate_toplevel_management;