Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/overlap notify #1007

Merged
merged 2 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use anyhow::{Context, Result};
use state::State;
use std::{env, ffi::OsString, os::unix::process::CommandExt, process, sync::Arc};
use tracing::{error, info, warn};
use wayland::protocols::overlap_notify::OverlapNotifyState;

use crate::wayland::handlers::compositor::client_compositor_state;

Expand Down Expand Up @@ -131,6 +132,7 @@ fn main() -> Result<()> {
}
state.common.refresh();
state::Common::refresh_focus(state);
OverlapNotifyState::refresh(state);
state.common.update_x11_stacking_order();

{
Expand Down
5 changes: 5 additions & 0 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::{
image_source::ImageSourceState,
output_configuration::OutputConfigurationState,
output_power::OutputPowerState,
overlap_notify::OverlapNotifyState,
screencopy::ScreencopyState,
toplevel_info::ToplevelInfoState,
toplevel_management::{ManagementCapabilities, ToplevelManagementState},
Expand Down Expand Up @@ -219,6 +220,7 @@ pub struct Common {
pub viewporter_state: ViewporterState,
pub kde_decoration_state: KdeDecorationState,
pub xdg_decoration_state: XdgDecorationState,
pub overlap_notify_state: OverlapNotifyState,

// shell-related wayland state
pub xdg_shell_state: XdgShellState,
Expand Down Expand Up @@ -499,6 +501,8 @@ impl State {
let output_state = OutputManagerState::new_with_xdg_output::<Self>(dh);
let output_configuration_state = OutputConfigurationState::new(dh, client_is_privileged);
let output_power_state = OutputPowerState::new::<Self, _>(dh, client_is_privileged);
let overlap_notify_state =
OverlapNotifyState::new::<Self, _>(dh, client_has_no_security_context);
let presentation_state = PresentationState::new::<Self>(dh, clock.id() as u32);
let primary_selection_state = PrimarySelectionState::new::<Self>(dh);
let image_source_state = ImageSourceState::new::<Self, _>(dh, client_is_privileged);
Expand Down Expand Up @@ -609,6 +613,7 @@ impl State {
output_state,
output_configuration_state,
output_power_state,
overlap_notify_state,
presentation_state,
primary_selection_state,
data_control_state,
Expand Down
1 change: 1 addition & 0 deletions src/wayland/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub mod layer_shell;
pub mod output;
pub mod output_configuration;
pub mod output_power;
pub mod overlap_notify;
pub mod pointer_constraints;
pub mod pointer_gestures;
pub mod presentation;
Expand Down
42 changes: 42 additions & 0 deletions src/wayland/handlers/overlap_notify.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use smithay::{
desktop::{layer_map_for_output, LayerSurface, WindowSurfaceType},
output::Output,
reexports::wayland_protocols_wlr::layer_shell::v1::server::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
};

use crate::{
state::State,
wayland::protocols::overlap_notify::{
delegate_overlap_notify, OverlapNotifyHandler, OverlapNotifyState,
},
};

impl OverlapNotifyHandler for State {
fn overlap_notify_state(&mut self) -> &mut OverlapNotifyState {
&mut self.common.overlap_notify_state
}

fn layer_surface_from_resource(&self, resource: ZwlrLayerSurfaceV1) -> Option<LayerSurface> {
self.common
.layer_shell_state
.layer_surfaces()
.find(|l| l.shell_surface() == &resource)
.and_then(|l| {
let shell = self.common.shell.read().unwrap();
let outputs = shell.outputs();
let ret = outputs.map(|o| layer_map_for_output(o)).find_map(|s| {
s.layer_for_surface(l.wl_surface(), WindowSurfaceType::ALL)
.cloned()
});
drop(shell);
ret
})
}

fn outputs(&self) -> impl Iterator<Item = Output> {
let shell = self.common.shell.read().unwrap();
shell.outputs().cloned().collect::<Vec<_>>().into_iter()
}
}

delegate_overlap_notify!(State);
140 changes: 96 additions & 44 deletions src/wayland/protocols/overlap_notify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use cosmic_protocols::{
zcosmic_toplevel_info_v1::ZcosmicToplevelInfoV1,
},
};
use rand::distributions::{Alphanumeric, DistString};
use smithay::{
desktop::{layer_map_for_output, LayerSurface},
output::Output,
Expand All @@ -29,14 +28,15 @@ use smithay::{
shell::wlr_layer::{ExclusiveZone, Layer},
},
};
use wayland_backend::server::GlobalId;
use wayland_backend::server::{GlobalId, ObjectId};

use crate::utils::prelude::{RectExt, RectGlobalExt, RectLocalExt};

use super::toplevel_info::{
ToplevelHandleState, ToplevelInfoGlobalData, ToplevelInfoHandler, ToplevelState, Window,
};

#[derive(Debug)]
pub struct OverlapNotifyState {
instances: Vec<ZcosmicOverlapNotifyV1>,
global: GlobalId,
Expand Down Expand Up @@ -83,7 +83,7 @@ impl OverlapNotifyState {
W: Window + 'static,
{
for output in state.outputs() {
let map = layer_map_for_output(output);
let map = layer_map_for_output(&output);
for layer_surface in map.layers() {
if let Some(data) = layer_surface
.user_data()
Expand All @@ -94,14 +94,18 @@ impl OverlapNotifyState {
if inner.has_active_notifications() {
let mut new_snapshot = OverlapSnapshot::default();

let layer_geo = layer_surface.bbox().as_local().to_global(output);
let layer_geo = map
.layer_geometry(layer_surface)
.unwrap_or_default()
.as_local()
.to_global(&output);

for window in state.toplevel_info_state().registered_toplevels() {
if let Some(window_geo) = window.global_geometry() {
if let Some(intersection) = layer_geo.intersection(window_geo) {
// relative to window location
// relative to layer location
let region = Rectangle::from_loc_and_size(
intersection.loc - window_geo.loc,
intersection.loc - layer_geo.loc,
intersection.size,
)
.as_logical();
Expand All @@ -111,11 +115,18 @@ impl OverlapNotifyState {
}

for other_surface in map.layers().filter(|s| *s != layer_surface) {
let other_geo = other_surface.bbox().as_local().to_global(output);
if other_surface.wl_surface().id() == layer_surface.wl_surface().id() {
continue;
}
let other_geo = map
.layer_geometry(other_surface)
.unwrap_or_default()
.as_local()
.to_global(&output);
if let Some(intersection) = layer_geo.intersection(other_geo) {
// relative to window location
// relative to layer location
let region = Rectangle::from_loc_and_size(
intersection.loc - other_geo.loc,
intersection.loc - layer_geo.loc,
intersection.size,
)
.as_logical();
Expand All @@ -134,7 +145,7 @@ impl OverlapNotifyState {
pub trait OverlapNotifyHandler: ToplevelInfoHandler {
fn overlap_notify_state(&mut self) -> &mut OverlapNotifyState;
fn layer_surface_from_resource(&self, resource: ZwlrLayerSurfaceV1) -> Option<LayerSurface>;
fn outputs(&self) -> impl Iterator<Item = &Output>;
fn outputs(&self) -> impl Iterator<Item = Output>;
}

pub struct OverlapNotifyGlobalData {
Expand All @@ -156,20 +167,26 @@ impl LayerOverlapNotificationDataInternal {
}

pub fn add_notification(&mut self, new_notification: ZcosmicOverlapNotificationV1) {
for (toplevel, overlap) in &self.last_snapshot.toplevel_overlaps {
if let Ok(toplevel) = toplevel.upgrade() {
new_notification.toplevel_enter(
&toplevel,
overlap.loc.x,
overlap.loc.y,
overlap.size.w,
overlap.size.h,
);
if let Some(client) = new_notification.client() {
for (toplevel, overlap) in &self.last_snapshot.toplevel_overlaps {
if let Some(toplevel) = toplevel
.upgrade()
.ok()
.filter(|handle| handle.client().is_some_and(|c| c == client))
{
new_notification.toplevel_enter(
&toplevel,
overlap.loc.x,
overlap.loc.y,
overlap.size.w,
overlap.size.h,
);
}
}
}
for (layer_surface, (exclusive, layer, overlap)) in &self.last_snapshot.layer_overlaps {
for (_, (identifier, exclusive, layer, overlap)) in &self.last_snapshot.layer_overlaps {
new_notification.layer_enter(
layer_surface.clone(),
identifier.clone(),
if *exclusive { 1 } else { 0 },
match layer {
Layer::Background => WlrLayer::Background,
Expand All @@ -196,44 +213,61 @@ impl LayerOverlapNotificationDataInternal {
for toplevel in self.last_snapshot.toplevel_overlaps.keys() {
if !new_snapshot.toplevel_overlaps.contains_key(toplevel) {
if let Ok(toplevel) = toplevel.upgrade() {
for notification in &notifications {
notification.toplevel_leave(&toplevel);
if let Some(client) = toplevel.client() {
for notification in notifications
.iter()
.filter(|n| n.client().is_some_and(|c| c == client))
{
notification.toplevel_leave(&toplevel);
}
}
}
}
}
for (toplevel, overlap) in &new_snapshot.toplevel_overlaps {
if !self.last_snapshot.toplevel_overlaps.contains_key(toplevel) {
if !self
.last_snapshot
.toplevel_overlaps
.get(toplevel)
.is_some_and(|old_overlap| old_overlap == overlap)
{
if let Ok(toplevel) = toplevel.upgrade() {
for notification in &notifications {
notification.toplevel_enter(
&toplevel,
overlap.loc.x,
overlap.loc.y,
overlap.size.w,
overlap.size.h,
);
if let Some(client) = toplevel.client() {
for notification in notifications
.iter()
.filter(|n| n.client().is_some_and(|c| c == client))
{
notification.toplevel_enter(
&toplevel,
overlap.loc.x,
overlap.loc.y,
overlap.size.w,
overlap.size.h,
);
}
}
}
}
}

for layer_surface in self.last_snapshot.layer_overlaps.keys() {
if new_snapshot.layer_overlaps.contains_key(layer_surface) {
for (layer_surface, (identifier, ..)) in &self.last_snapshot.layer_overlaps {
if !new_snapshot.layer_overlaps.contains_key(layer_surface) {
for notification in &notifications {
notification.layer_leave(layer_surface.clone());
notification.layer_leave(identifier.clone());
}
}
}
for (layer_surface, (exclusive, layer, overlap)) in &new_snapshot.layer_overlaps {
for (layer_surface, (identifier, exclusive, layer, overlap)) in &new_snapshot.layer_overlaps
{
if !self
.last_snapshot
.layer_overlaps
.contains_key(layer_surface)
.get(layer_surface)
.is_some_and(|(_, _, _, old_overlap)| old_overlap == overlap)
{
for notification in &notifications {
notification.layer_enter(
layer_surface.clone(),
identifier.clone(),
if *exclusive { 1 } else { 0 },
match layer {
Layer::Background => WlrLayer::Background,
Expand All @@ -257,7 +291,7 @@ impl LayerOverlapNotificationDataInternal {
#[derive(Debug, Default, Clone)]
struct OverlapSnapshot {
toplevel_overlaps: HashMap<Weak<ExtForeignToplevelHandleV1>, Rectangle<i32, Logical>>,
layer_overlaps: HashMap<String, (bool, Layer, Rectangle<i32, Logical>)>,
layer_overlaps: HashMap<ObjectId, (String, bool, Layer, Rectangle<i32, Logical>)>,
}

impl OverlapSnapshot {
Expand All @@ -283,18 +317,21 @@ impl OverlapSnapshot {
ExclusiveZone::Exclusive(_)
);
let layer = layer_surface.layer();
let identifier = layer_surface.user_data().get_or_insert(Identifier::default);
let id = layer_surface.wl_surface().id();
let identifier = layer_surface
.user_data()
.get_or_insert(|| Identifier::from(id.clone()));

self.layer_overlaps
.insert(identifier.0.clone(), (exclusive, layer, overlap));
.insert(id, (identifier.0.clone(), exclusive, layer, overlap));
}
}

struct Identifier(String);

impl Default for Identifier {
fn default() -> Self {
Identifier(Alphanumeric.sample_string(&mut rand::thread_rng(), 32))
impl From<ObjectId> for Identifier {
fn from(value: ObjectId) -> Self {
Identifier(value.to_string())
}
}

Expand Down Expand Up @@ -392,3 +429,18 @@ where
}
}
}

macro_rules! delegate_overlap_notify {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::overlap_notify::v1::server::zcosmic_overlap_notify_v1::ZcosmicOverlapNotifyV1: $crate::wayland::protocols::overlap_notify::OverlapNotifyGlobalData
] => $crate::wayland::protocols::overlap_notify::OverlapNotifyState);
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::overlap_notify::v1::server::zcosmic_overlap_notify_v1::ZcosmicOverlapNotifyV1: ()
] => $crate::wayland::protocols::overlap_notify::OverlapNotifyState);
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::overlap_notify::v1::server::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1: ()
] => $crate::wayland::protocols::overlap_notify::OverlapNotifyState);
};
}
pub(crate) use delegate_overlap_notify;