xdg-activation: Initial implementation

This commit is contained in:
Victoria Brekenfeld 2023-11-07 18:46:25 +01:00
parent 9ced8c346d
commit f7cc1985e3
11 changed files with 419 additions and 66 deletions

2
Cargo.lock generated
View file

@ -3964,7 +3964,7 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
[[package]]
name = "smithay"
version = "0.3.0"
source = "git+https://github.com/smithay//smithay?rev=a8f3c46#a8f3c46f0bd0153160a3ba117502ba47c38ab0dc"
source = "git+https://github.com/smithay//smithay?rev=d5b352b#d5b352b33525d21b38ad8d7ebd54c99d39246464"
dependencies = [
"appendlist",
"ash",

View file

@ -88,4 +88,4 @@ debug = true
lto = "fat"
[patch."https://github.com/Smithay/smithay.git"]
smithay = { git = "https://github.com/smithay//smithay", rev = "a8f3c46" }
smithay = { git = "https://github.com/smithay//smithay", rev = "d5b352b" }

View file

@ -15,7 +15,10 @@ use crate::{
},
state::{Common, SessionLock},
utils::prelude::*,
wayland::{handlers::screencopy::ScreencopySessions, protocols::screencopy::Session},
wayland::{
handlers::{screencopy::ScreencopySessions, xdg_activation::ActivationContext},
protocols::screencopy::Session,
},
};
use calloop::{timer::Timer, RegistrationToken};
use cosmic_comp_config::workspace::WorkspaceLayout;
@ -1688,8 +1691,22 @@ impl State {
workspace.toggle_floating_window(seat);
}
Action::Spawn(command) => {
let wayland_display = self.common.socket.clone();
let (token, data) = self
.common
.shell
.xdg_activation_state
.create_external_token(None);
let (token, data) = (token.clone(), data.clone());
let seat = self.common.last_active_seat();
let output = seat.active_output();
let workspace = self.common.shell.active_space_mut(&output);
workspace.pending_tokens.insert(token.clone());
let handle = workspace.handle;
data.user_data
.insert_if_missing(move || ActivationContext::Workspace(handle));
let wayland_display = self.common.socket.clone();
let display = self
.common
.xwayland_state
@ -1697,22 +1714,22 @@ impl State {
.map(|s| format!(":{}", s.display))
.unwrap_or_default();
std::thread::spawn(move || {
let mut cmd = std::process::Command::new("/bin/sh");
let mut cmd = std::process::Command::new("/bin/sh");
cmd.arg("-c")
.arg(command.clone())
.env("WAYLAND_DISPLAY", &wayland_display)
.env("DISPLAY", &display)
.env_remove("COSMIC_SESSION_SOCK");
cmd.arg("-c")
.arg(command.clone())
.env("WAYLAND_DISPLAY", &wayland_display)
.env("DISPLAY", &display)
.env("XDG_ACTIVATION_TOKEN", &*token)
.env("DESKTOP_STARTUP_ID", &*token)
.env_remove("COSMIC_SESSION_SOCK");
match cmd.spawn() {
Ok(mut child) => {
let _res = child.wait();
}
Err(err) => {
tracing::warn!(?err, "Failed to spawn \"{}\"", command);
}
std::thread::spawn(move || match cmd.spawn() {
Ok(mut child) => {
let _res = child.wait();
}
Err(err) => {
tracing::warn!(?err, "Failed to spawn \"{}\"", command);
}
});
}

View file

@ -94,11 +94,10 @@ impl ActiveFocus {
}
impl Shell {
pub fn set_focus<'a>(
pub fn append_focus_stack(
state: &mut State,
target: Option<&KeyboardFocusTarget>,
active_seat: &Seat<State>,
serial: Option<Serial>,
) {
// update FocusStack and notify layouts about new focus (if any window)
let element = match target {
@ -128,6 +127,15 @@ impl Shell {
}
}
}
}
pub fn set_focus(
state: &mut State,
target: Option<&KeyboardFocusTarget>,
active_seat: &Seat<State>,
serial: Option<Serial>,
) {
Self::append_focus_stack(state, target, active_seat);
// update keyboard focus
if let Some(keyboard) = active_seat.get_keyboard() {

View file

@ -35,7 +35,7 @@ pub use self::grabs::*;
#[derive(Debug, Default)]
pub struct FloatingLayout {
pub(in crate::shell) space: Space<CosmicMapped>,
pub(crate) space: Space<CosmicMapped>,
}
impl FloatingLayout {

View file

@ -30,6 +30,7 @@ use smithay::{
},
xdg::XdgShellState,
},
xdg_activation::XdgActivationState,
},
xwayland::X11Surface,
};
@ -38,12 +39,15 @@ use crate::{
config::{Config, KeyModifiers, KeyPattern},
state::client_should_see_privileged_protocols,
utils::prelude::*,
wayland::protocols::{
toplevel_info::ToplevelInfoState,
toplevel_management::{ManagementCapabilities, ToplevelManagementState},
workspace::{
WorkspaceCapabilities, WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState,
WorkspaceUpdateGuard,
wayland::{
handlers::xdg_activation::ActivationContext,
protocols::{
toplevel_info::ToplevelInfoState,
toplevel_management::{ManagementCapabilities, ToplevelManagementState},
workspace::{
WorkspaceCapabilities, WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState,
WorkspaceUpdateGuard,
},
},
},
};
@ -145,6 +149,22 @@ pub enum MaximizeMode {
OnTop,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ActivationKey {
Wayland(WlSurface),
X11(u32),
}
impl From<&CosmicSurface> for ActivationKey {
fn from(value: &CosmicSurface) -> Self {
match value {
CosmicSurface::Wayland(w) => ActivationKey::Wayland(w.toplevel().wl_surface().clone()),
CosmicSurface::X11(s) => ActivationKey::X11(s.window_id()),
_ => unreachable!(),
}
}
}
#[derive(Debug)]
pub struct Shell {
pub workspaces: Workspaces,
@ -153,6 +173,7 @@ pub struct Shell {
pub maximize_mode: MaximizeMode,
pub pending_windows: Vec<(CosmicSurface, Seat<State>, Option<Output>)>,
pub pending_layers: Vec<(LayerSurface, Output, Seat<State>)>,
pub pending_activations: HashMap<ActivationKey, ActivationContext>,
pub override_redirect_windows: Vec<X11Surface>,
// wayland_state
@ -160,6 +181,7 @@ pub struct Shell {
pub toplevel_info_state: ToplevelInfoState<State, CosmicSurface>,
pub toplevel_management_state: ToplevelManagementState,
pub xdg_shell_state: XdgShellState,
pub xdg_activation_state: XdgActivationState,
pub workspace_state: WorkspaceState<State>,
theme: cosmic::Theme,
@ -279,6 +301,8 @@ impl WorkspaceSet {
if self.active != idx {
let old_active = self.active;
state.remove_workspace_state(&self.workspaces[old_active].handle, WState::Active);
state.remove_workspace_state(&self.workspaces[old_active].handle, WState::Urgent);
state.remove_workspace_state(&self.workspaces[idx].handle, WState::Urgent);
state.add_workspace_state(&self.workspaces[idx].handle, WState::Active);
self.previously_active = Some((old_active, Instant::now()));
self.active = idx;
@ -299,13 +323,13 @@ impl WorkspaceSet {
self.output = new_output.clone();
}
fn refresh<'a>(&mut self) {
fn refresh<'a>(&mut self, xdg_activation_state: &XdgActivationState) {
if let Some((_, start)) = self.previously_active {
if Instant::now().duration_since(start).as_millis() >= ANIMATION_DURATION.as_millis() {
self.previously_active = None;
}
} else {
self.workspaces[self.active].refresh();
self.workspaces[self.active].refresh(xdg_activation_state);
}
}
@ -332,7 +356,7 @@ impl WorkspaceSet {
if self
.workspaces
.last()
.map(|last| last.windows().next().is_some())
.map(|last| !last.pending_tokens.is_empty() || last.windows().next().is_some())
.unwrap_or(true)
{
self.add_empty_workspace(state);
@ -342,7 +366,8 @@ impl WorkspaceSet {
let len = self.workspaces.len();
let mut keep = vec![true; len];
for (i, workspace) in self.workspaces.iter().enumerate() {
let has_windows = workspace.windows().next().is_some();
let has_windows =
!workspace.pending_tokens.is_empty() || workspace.windows().next().is_some();
if !has_windows && i != self.active && i != len - 1 {
state.remove_workspace(workspace.handle);
@ -370,6 +395,7 @@ impl WorkspaceSet {
amount: usize,
state: &mut WorkspaceUpdateGuard<State>,
toplevel_info: &mut ToplevelInfoState<State, CosmicSurface>,
xdg_activation_state: &XdgActivationState,
) {
if amount < self.workspaces.len() {
// merge last ones
@ -401,7 +427,7 @@ impl WorkspaceSet {
state.remove_workspace(workspace.handle);
}
last_space.refresh();
last_space.refresh(xdg_activation_state);
} else if amount > self.workspaces.len() {
// add empty ones
while amount > self.workspaces.len() {
@ -508,6 +534,7 @@ impl Workspaces {
seats: impl Iterator<Item = Seat<State>>,
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
xdg_activation_state: &XdgActivationState,
) {
if !self.sets.contains_key(output) {
return;
@ -557,7 +584,7 @@ impl Workspaces {
// update mapping
workspace.set_output(&new_output, toplevel_info_state);
workspace.refresh();
workspace.refresh(xdg_activation_state);
// TODO: merge if mode = static
new_set.workspaces.push(workspace);
@ -576,7 +603,7 @@ impl Workspaces {
self.backup_set = Some(set);
}
self.refresh(workspace_state, toplevel_info_state)
self.refresh(workspace_state, toplevel_info_state, xdg_activation_state)
}
}
@ -585,6 +612,7 @@ impl Workspaces {
config: &Config,
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
xdg_activation_state: &XdgActivationState,
) {
let old_mode = self.mode;
@ -650,13 +678,14 @@ impl Workspaces {
_ => {}
};
self.refresh(workspace_state, toplevel_info_state)
self.refresh(workspace_state, toplevel_info_state, xdg_activation_state)
}
pub fn refresh(
&mut self,
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
xdg_activation_state: &XdgActivationState,
) {
match self.mode {
WorkspaceMode::Global => {
@ -696,10 +725,10 @@ impl Workspaces {
let mut active = self.sets[0].active;
let mut keep = vec![true; len];
for i in 0..len {
let has_windows = self
.sets
.values()
.any(|s| s.workspaces[i].windows().next().is_some());
let has_windows = self.sets.values().any(|s| {
!s.workspaces[i].pending_tokens.is_empty()
|| s.workspaces[i].windows().next().is_some()
});
if !has_windows && i != active && i != len - 1 {
for workspace in self.sets.values().map(|s| &s.workspaces[i]) {
@ -733,7 +762,12 @@ impl Workspaces {
}
WorkspaceAmount::Static(amount) => {
for set in self.sets.values_mut() {
set.ensure_static(amount as usize, workspace_state, toplevel_info_state)
set.ensure_static(
amount as usize,
workspace_state,
toplevel_info_state,
xdg_activation_state,
)
}
}
}
@ -746,14 +780,19 @@ impl Workspaces {
}
WorkspaceAmount::Static(amount) => {
for set in self.sets.values_mut() {
set.ensure_static(amount as usize, workspace_state, toplevel_info_state)
set.ensure_static(
amount as usize,
workspace_state,
toplevel_info_state,
xdg_activation_state,
)
}
}
},
}
for set in self.sets.values_mut() {
set.refresh()
set.refresh(xdg_activation_state)
}
}
@ -826,7 +865,7 @@ impl Workspaces {
}
}
pub fn set_theme(&mut self, theme: cosmic::Theme) {
pub fn set_theme(&mut self, theme: cosmic::Theme, xdg_activation_state: &XdgActivationState) {
for (_, s) in &mut self.sets {
s.theme = theme.clone();
for mut w in &mut s.workspaces {
@ -837,7 +876,7 @@ impl Workspaces {
m.force_redraw();
});
w.refresh();
w.refresh(xdg_activation_state);
w.dirty.store(true, Ordering::Relaxed);
w.recalculate();
}
@ -854,6 +893,7 @@ impl Shell {
client_should_see_privileged_protocols,
);
let xdg_shell_state = XdgShellState::new::<State>(dh);
let xdg_activation_state = XdgActivationState::new::<State>(dh);
let toplevel_info_state =
ToplevelInfoState::new(dh, client_should_see_privileged_protocols);
let toplevel_management_state = ToplevelManagementState::new::<State, _>(
@ -874,12 +914,14 @@ impl Shell {
pending_windows: Vec::new(),
pending_layers: Vec::new(),
pending_activations: HashMap::new(),
override_redirect_windows: Vec::new(),
layer_shell_state,
toplevel_info_state,
toplevel_management_state,
xdg_shell_state,
xdg_activation_state,
workspace_state,
theme,
@ -905,6 +947,7 @@ impl Shell {
seats,
&mut self.workspace_state.update(),
&mut self.toplevel_info_state,
&self.xdg_activation_state,
);
self.refresh(); // cleans up excess of workspaces and empty workspaces
}
@ -912,8 +955,12 @@ impl Shell {
pub fn update_config(&mut self, config: &Config) {
let mut workspace_state = self.workspace_state.update();
let toplevel_info_state = &mut self.toplevel_info_state;
self.workspaces
.update_config(config, &mut workspace_state, toplevel_info_state);
self.workspaces.update_config(
config,
&mut workspace_state,
toplevel_info_state,
&self.xdg_activation_state,
);
}
pub fn activate(
@ -958,6 +1005,12 @@ impl Shell {
self.workspaces.active_mut(output)
}
pub fn refresh_active_space(&mut self, output: &Output) {
self.workspaces
.active_mut(output)
.refresh(&self.xdg_activation_state)
}
pub fn visible_outputs_for_surface<'a>(
&'a self,
surface: &'a WlSurface,
@ -1190,9 +1243,13 @@ impl Shell {
self.popups.cleanup();
self.xdg_activation_state.retain_tokens(|_, data| {
Instant::now().duration_since(data.timestamp) < Duration::from_secs(5)
});
self.workspaces.refresh(
&mut self.workspace_state.update(),
&mut self.toplevel_info_state,
&self.xdg_activation_state,
);
for output in self.outputs() {
@ -1258,10 +1315,42 @@ impl Shell {
.unwrap();
let (window, seat, output) = state.common.shell.pending_windows.remove(pos);
let should_be_fullscreen = output.is_some();
let output = output.unwrap_or_else(|| seat.active_output());
let pending_activation = state
.common
.shell
.pending_activations
.remove(&(&window).into());
let workspace_handle = match pending_activation {
Some(ActivationContext::Workspace(handle)) => Some(handle),
_ => None,
};
let should_be_fullscreen = output.is_some();
let mut output = output.unwrap_or_else(|| seat.active_output());
// this is beyond stupid, just to make the borrow checker happy
let workspace = if let Some(handle) = workspace_handle.filter(|handle| {
state
.common
.shell
.workspaces
.spaces()
.any(|space| &space.handle == handle)
}) {
state
.common
.shell
.workspaces
.spaces_mut()
.find(|space| space.handle == handle)
.unwrap()
} else {
state.common.shell.workspaces.active_mut(&output)
};
if output != workspace.output {
output = workspace.output.clone();
}
let workspace = state.common.shell.workspaces.active_mut(&output);
if let Some((mapped, layer, previous_workspace)) = workspace.remove_fullscreen() {
let old_handle = workspace.handle.clone();
let new_workspace_handle = state
@ -1280,7 +1369,26 @@ impl Shell {
);
};
let workspace = state.common.shell.workspaces.active_mut(&output);
let active_handle = state.common.shell.workspaces.active(&output).1.handle;
let workspace = if let Some(handle) = workspace_handle.filter(|handle| {
state
.common
.shell
.workspaces
.spaces()
.any(|space| &space.handle == handle)
}) {
state
.common
.shell
.workspaces
.spaces_mut()
.find(|space| space.handle == handle)
.unwrap()
} else {
state.common.shell.workspaces.active_mut(&output)
};
state.common.shell.toplevel_info_state.new_toplevel(&window);
state
.common
@ -1302,6 +1410,9 @@ impl Shell {
{
mapped.set_debug(state.common.egui.active);
}
let workspace_empty = workspace.mapped().next().is_none();
if layout::should_be_floating(&window) || !workspace.tiling_enabled {
workspace.floating_layer.map(mapped.clone(), None);
} else {
@ -1324,7 +1435,14 @@ impl Shell {
workspace.fullscreen_request(&mapped.active_window(), None);
}
Shell::set_focus(state, Some(&KeyboardFocusTarget::from(mapped)), &seat, None);
if workspace.output == seat.active_output() && active_handle == workspace.handle {
// TODO: enforce focus stealing prevention by also checking the same rules as for the else case.
Shell::set_focus(state, Some(&KeyboardFocusTarget::from(mapped)), &seat, None);
} else if workspace_empty || workspace_handle.is_some() || should_be_fullscreen {
let handle = workspace.handle;
Shell::append_focus_stack(state, Some(&KeyboardFocusTarget::from(mapped)), &seat);
state.common.shell.set_urgent(&handle);
}
let active_space = state.common.shell.active_space(&output);
for mapped in active_space.mapped() {
@ -1666,7 +1784,13 @@ impl Shell {
pub(crate) fn set_theme(&mut self, theme: cosmic::Theme) {
self.theme = theme.clone();
self.refresh();
self.workspaces.set_theme(theme.clone());
self.workspaces
.set_theme(theme.clone(), &self.xdg_activation_state);
}
pub fn set_urgent(&mut self, workspace: &WorkspaceHandle) {
let mut workspace_guard = self.workspace_state.update();
workspace_guard.add_workspace_state(workspace, WState::Urgent);
}
}

View file

@ -43,11 +43,12 @@ use smithay::{
wayland::{
compositor::{add_blocker, Blocker, BlockerState},
seat::WaylandFocus,
xdg_activation::{XdgActivationState, XdgActivationToken},
},
xwayland::X11Surface,
};
use std::{
collections::{HashMap, VecDeque},
collections::{HashMap, HashSet, VecDeque},
sync::{
atomic::{AtomicBool, Ordering},
Arc,
@ -87,6 +88,7 @@ pub struct Workspace {
pub pending_buffers: Vec<(ScreencopySession, BufferParams)>,
pub screencopy_sessions: Vec<DropableSession>,
pub output_stack: VecDeque<String>,
pub pending_tokens: HashSet<XdgActivationToken>,
pub(super) backdrop_id: Id,
pub dirty: AtomicBool,
}
@ -227,12 +229,13 @@ impl Workspace {
pending_buffers: Vec::new(),
screencopy_sessions: Vec::new(),
output_stack: VecDeque::new(),
pending_tokens: HashSet::new(),
backdrop_id: Id::new(),
dirty: AtomicBool::new(false),
}
}
pub fn refresh(&mut self) {
pub fn refresh(&mut self, xdg_activation_state: &XdgActivationState) {
#[cfg(feature = "debug")]
puffin::profile_function!();
@ -243,6 +246,9 @@ impl Workspace {
self.floating_layer.refresh();
self.tiling_layer.refresh();
self.pending_tokens
.retain(|token| xdg_activation_state.data_for_token(token).is_some());
}
pub fn refresh_focus_stack(&mut self) {

View file

@ -30,5 +30,6 @@ pub mod viewporter;
pub mod virtual_keyboard;
pub mod wl_drm;
pub mod workspace;
pub mod xdg_activation;
pub mod xdg_shell;
pub mod xwayland_keyboard_grab;

View file

@ -0,0 +1,150 @@
use smithay::{
delegate_xdg_activation,
input::Seat,
reexports::wayland_server::protocol::wl_surface::WlSurface,
wayland::xdg_activation::{
XdgActivationHandler, XdgActivationState, XdgActivationToken, XdgActivationTokenData,
},
};
use tracing::debug;
use crate::{shell::ActivationKey, state::ClientState, utils::prelude::*};
use crate::{state::State, wayland::protocols::workspace::WorkspaceHandle};
#[derive(Debug, Clone, Copy)]
pub enum ActivationContext {
UrgentOnly,
Workspace(WorkspaceHandle),
}
impl XdgActivationHandler for State {
fn activation_state(&mut self) -> &mut XdgActivationState {
&mut self.common.shell.xdg_activation_state
}
fn token_created(&mut self, token: XdgActivationToken, data: XdgActivationTokenData) -> bool {
// Privileged clients always get valid tokens
if data
.client_id
.and_then(|client_id| {
self.common
.display_handle
.backend_handle()
.get_client_data(client_id)
.ok()
})
.and_then(|data| {
data.downcast_ref::<ClientState>()
.map(|data| data.privileged)
})
.unwrap_or(false)
{
if let Some(seat) = data.serial.and_then(|(_, seat)| Seat::from_resource(&seat)) {
let output = seat.active_output();
let workspace = self.common.shell.active_space_mut(&output);
workspace.pending_tokens.insert(token.clone());
let handle = workspace.handle;
data.user_data
.insert_if_missing(move || ActivationContext::Workspace(handle));
debug!(?token, "created workspace token for privileged client");
} else {
data.user_data
.insert_if_missing(|| ActivationContext::UrgentOnly);
debug!(
?token,
"created urgent-only token for privileged client without seat"
);
}
return true;
};
// Tokens without validation aren't allowed to steal focus
let Some((serial, seat)) = data.serial else {
data.user_data.insert_if_missing(|| ActivationContext::UrgentOnly);
debug!(?token, "created urgent-only token for missing seat/serial");
return true
};
let Some(seat) = Seat::from_resource(&seat) else {
data.user_data.insert_if_missing(|| ActivationContext::UrgentOnly);
debug!(?token, "created urgent-only token for unknown seat");
return true
};
// At this point we don't bother with urgent-only tokens.
// If the client provides a bad serial, it should be fixed.
let keyboard = seat.get_keyboard().unwrap();
let valid = keyboard
.last_enter()
.map(|last_enter| serial.is_no_older_than(&last_enter))
.unwrap_or(false);
if valid {
let output = seat.active_output();
let workspace = self.common.shell.active_space_mut(&output);
workspace.pending_tokens.insert(token.clone());
let handle = workspace.handle;
data.user_data
.insert_if_missing(move || ActivationContext::Workspace(handle));
debug!(?token, "created workspace token");
} else {
debug!(?token, "created urgent-only token for invalid serial");
}
valid
}
fn request_activation(
&mut self,
_token: XdgActivationToken,
token_data: XdgActivationTokenData,
surface: WlSurface,
) {
if let Some(context) = token_data.user_data.get::<ActivationContext>() {
if let Some(element) = self.common.shell.element_for_wl_surface(&surface).cloned() {
match context {
ActivationContext::UrgentOnly => {
if let Some((workspace, _output)) =
self.common.shell.workspace_for_surface(&surface)
{
self.common.shell.set_urgent(&workspace);
}
}
ActivationContext::Workspace(workspace) => {
let seat = self.common.last_active_seat().clone();
let current_output = seat.active_output();
let current_workspace = self.common.shell.active_space_mut(&current_output);
if current_workspace
.floating_layer
.mapped()
.any(|m| m == &element)
{
current_workspace
.floating_layer
.space
.raise_element(&element, true);
}
let target = element.into();
if workspace == &current_workspace.handle {
Shell::set_focus(self, Some(&target), &seat, None);
} else {
Shell::append_focus_stack(self, Some(&target), &seat);
self.common.shell.set_urgent(workspace);
}
}
}
} else {
self.common
.shell
.pending_activations
.insert(ActivationKey::Wayland(surface), context.clone());
}
}
}
}
delegate_xdg_activation!(State);

View file

@ -312,7 +312,7 @@ impl XdgShellHandler for State {
.visible_outputs_for_surface(surface.wl_surface())
.collect::<Vec<_>>();
for output in outputs.iter() {
self.common.shell.active_space_mut(output).refresh();
self.common.shell.refresh_active_space(output);
}
// animations might be unblocked now

View file

@ -5,23 +5,29 @@ use crate::{
shell::{focus::target::KeyboardFocusTarget, CosmicSurface, Shell},
state::State,
utils::prelude::*,
wayland::{handlers::screencopy::PendingScreencopyBuffers, protocols::screencopy::SessionType},
wayland::{
handlers::{screencopy::PendingScreencopyBuffers, xdg_activation::ActivationContext},
protocols::screencopy::SessionType,
},
};
use smithay::{
backend::drm::DrmNode,
desktop::space::SpaceElement,
reexports::x11rb::protocol::xproto::Window as X11Window,
utils::{Logical, Point, Rectangle, Size},
wayland::selection::{
data_device::{
clear_data_device_selection, current_data_device_selection_userdata,
request_data_device_client_selection, set_data_device_selection,
wayland::{
selection::{
data_device::{
clear_data_device_selection, current_data_device_selection_userdata,
request_data_device_client_selection, set_data_device_selection,
},
primary_selection::{
clear_primary_selection, current_primary_selection_userdata,
request_primary_client_selection, set_primary_selection,
},
SelectionTarget,
},
primary_selection::{
clear_primary_selection, current_primary_selection_userdata,
request_primary_client_selection, set_primary_selection,
},
SelectionTarget,
xdg_activation::XdgActivationToken,
},
xwayland::{
xwm::{Reorder, XwmId},
@ -145,12 +151,29 @@ impl XwmHandler for State {
warn!(?window, ?err, "Failed to send Xwayland Mapped-Event",);
}
let startup_id = window.startup_id();
let surface = CosmicSurface::X11(window.clone());
if self.common.shell.element_for_surface(&surface).is_some() {
return;
}
let seat = self.common.last_active_seat().clone();
if let Some(context) = startup_id
.map(XdgActivationToken::from)
.and_then(|token| {
self.common
.shell
.xdg_activation_state
.data_for_token(&token)
})
.and_then(|data| data.user_data.get::<ActivationContext>())
{
self.common.shell.pending_activations.insert(
crate::shell::ActivationKey::X11(window.window_id()),
context.clone(),
);
}
self.common
.shell
.pending_windows
@ -172,6 +195,30 @@ impl XwmHandler for State {
})
.cloned()
{
if !self
.common
.shell
.pending_activations
.contains_key(&crate::shell::ActivationKey::X11(surface.window_id()))
{
if let Some(startup_id) = match &window {
CosmicSurface::X11(x11) => x11.startup_id(),
_ => None,
} {
if let Some(context) = self
.common
.shell
.xdg_activation_state
.data_for_token(&XdgActivationToken::from(startup_id))
.and_then(|data| data.user_data.get::<ActivationContext>())
{
self.common.shell.pending_activations.insert(
crate::shell::ActivationKey::X11(surface.window_id()),
context.clone(),
);
}
}
}
Shell::map_window(self, &window);
}
}
@ -224,7 +271,7 @@ impl XwmHandler for State {
self.common.shell.outputs().cloned().collect::<Vec<_>>()
};
for output in outputs.iter() {
self.common.shell.active_space_mut(output).refresh();
self.common.shell.refresh_active_space(output);
}
// screencopy