xdg-activation: Initial implementation
This commit is contained in:
parent
9ced8c346d
commit
f7cc1985e3
11 changed files with 419 additions and 66 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -3964,7 +3964,7 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smithay"
|
name = "smithay"
|
||||||
version = "0.3.0"
|
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 = [
|
dependencies = [
|
||||||
"appendlist",
|
"appendlist",
|
||||||
"ash",
|
"ash",
|
||||||
|
|
|
||||||
|
|
@ -88,4 +88,4 @@ debug = true
|
||||||
lto = "fat"
|
lto = "fat"
|
||||||
|
|
||||||
[patch."https://github.com/Smithay/smithay.git"]
|
[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" }
|
||||||
|
|
@ -15,7 +15,10 @@ use crate::{
|
||||||
},
|
},
|
||||||
state::{Common, SessionLock},
|
state::{Common, SessionLock},
|
||||||
utils::prelude::*,
|
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 calloop::{timer::Timer, RegistrationToken};
|
||||||
use cosmic_comp_config::workspace::WorkspaceLayout;
|
use cosmic_comp_config::workspace::WorkspaceLayout;
|
||||||
|
|
@ -1688,8 +1691,22 @@ impl State {
|
||||||
workspace.toggle_floating_window(seat);
|
workspace.toggle_floating_window(seat);
|
||||||
}
|
}
|
||||||
Action::Spawn(command) => {
|
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
|
let display = self
|
||||||
.common
|
.common
|
||||||
.xwayland_state
|
.xwayland_state
|
||||||
|
|
@ -1697,22 +1714,22 @@ impl State {
|
||||||
.map(|s| format!(":{}", s.display))
|
.map(|s| format!(":{}", s.display))
|
||||||
.unwrap_or_default();
|
.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")
|
cmd.arg("-c")
|
||||||
.arg(command.clone())
|
.arg(command.clone())
|
||||||
.env("WAYLAND_DISPLAY", &wayland_display)
|
.env("WAYLAND_DISPLAY", &wayland_display)
|
||||||
.env("DISPLAY", &display)
|
.env("DISPLAY", &display)
|
||||||
.env_remove("COSMIC_SESSION_SOCK");
|
.env("XDG_ACTIVATION_TOKEN", &*token)
|
||||||
|
.env("DESKTOP_STARTUP_ID", &*token)
|
||||||
|
.env_remove("COSMIC_SESSION_SOCK");
|
||||||
|
|
||||||
match cmd.spawn() {
|
std::thread::spawn(move || match cmd.spawn() {
|
||||||
Ok(mut child) => {
|
Ok(mut child) => {
|
||||||
let _res = child.wait();
|
let _res = child.wait();
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::warn!(?err, "Failed to spawn \"{}\"", command);
|
tracing::warn!(?err, "Failed to spawn \"{}\"", command);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,11 +94,10 @@ impl ActiveFocus {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Shell {
|
impl Shell {
|
||||||
pub fn set_focus<'a>(
|
pub fn append_focus_stack(
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
target: Option<&KeyboardFocusTarget>,
|
target: Option<&KeyboardFocusTarget>,
|
||||||
active_seat: &Seat<State>,
|
active_seat: &Seat<State>,
|
||||||
serial: Option<Serial>,
|
|
||||||
) {
|
) {
|
||||||
// update FocusStack and notify layouts about new focus (if any window)
|
// update FocusStack and notify layouts about new focus (if any window)
|
||||||
let element = match target {
|
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
|
// update keyboard focus
|
||||||
if let Some(keyboard) = active_seat.get_keyboard() {
|
if let Some(keyboard) = active_seat.get_keyboard() {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ pub use self::grabs::*;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct FloatingLayout {
|
pub struct FloatingLayout {
|
||||||
pub(in crate::shell) space: Space<CosmicMapped>,
|
pub(crate) space: Space<CosmicMapped>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloatingLayout {
|
impl FloatingLayout {
|
||||||
|
|
|
||||||
186
src/shell/mod.rs
186
src/shell/mod.rs
|
|
@ -30,6 +30,7 @@ use smithay::{
|
||||||
},
|
},
|
||||||
xdg::XdgShellState,
|
xdg::XdgShellState,
|
||||||
},
|
},
|
||||||
|
xdg_activation::XdgActivationState,
|
||||||
},
|
},
|
||||||
xwayland::X11Surface,
|
xwayland::X11Surface,
|
||||||
};
|
};
|
||||||
|
|
@ -38,12 +39,15 @@ use crate::{
|
||||||
config::{Config, KeyModifiers, KeyPattern},
|
config::{Config, KeyModifiers, KeyPattern},
|
||||||
state::client_should_see_privileged_protocols,
|
state::client_should_see_privileged_protocols,
|
||||||
utils::prelude::*,
|
utils::prelude::*,
|
||||||
wayland::protocols::{
|
wayland::{
|
||||||
toplevel_info::ToplevelInfoState,
|
handlers::xdg_activation::ActivationContext,
|
||||||
toplevel_management::{ManagementCapabilities, ToplevelManagementState},
|
protocols::{
|
||||||
workspace::{
|
toplevel_info::ToplevelInfoState,
|
||||||
WorkspaceCapabilities, WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState,
|
toplevel_management::{ManagementCapabilities, ToplevelManagementState},
|
||||||
WorkspaceUpdateGuard,
|
workspace::{
|
||||||
|
WorkspaceCapabilities, WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState,
|
||||||
|
WorkspaceUpdateGuard,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -145,6 +149,22 @@ pub enum MaximizeMode {
|
||||||
OnTop,
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct Shell {
|
pub struct Shell {
|
||||||
pub workspaces: Workspaces,
|
pub workspaces: Workspaces,
|
||||||
|
|
@ -153,6 +173,7 @@ pub struct Shell {
|
||||||
pub maximize_mode: MaximizeMode,
|
pub maximize_mode: MaximizeMode,
|
||||||
pub pending_windows: Vec<(CosmicSurface, Seat<State>, Option<Output>)>,
|
pub pending_windows: Vec<(CosmicSurface, Seat<State>, Option<Output>)>,
|
||||||
pub pending_layers: Vec<(LayerSurface, Output, Seat<State>)>,
|
pub pending_layers: Vec<(LayerSurface, Output, Seat<State>)>,
|
||||||
|
pub pending_activations: HashMap<ActivationKey, ActivationContext>,
|
||||||
pub override_redirect_windows: Vec<X11Surface>,
|
pub override_redirect_windows: Vec<X11Surface>,
|
||||||
|
|
||||||
// wayland_state
|
// wayland_state
|
||||||
|
|
@ -160,6 +181,7 @@ pub struct Shell {
|
||||||
pub toplevel_info_state: ToplevelInfoState<State, CosmicSurface>,
|
pub toplevel_info_state: ToplevelInfoState<State, CosmicSurface>,
|
||||||
pub toplevel_management_state: ToplevelManagementState,
|
pub toplevel_management_state: ToplevelManagementState,
|
||||||
pub xdg_shell_state: XdgShellState,
|
pub xdg_shell_state: XdgShellState,
|
||||||
|
pub xdg_activation_state: XdgActivationState,
|
||||||
pub workspace_state: WorkspaceState<State>,
|
pub workspace_state: WorkspaceState<State>,
|
||||||
|
|
||||||
theme: cosmic::Theme,
|
theme: cosmic::Theme,
|
||||||
|
|
@ -279,6 +301,8 @@ impl WorkspaceSet {
|
||||||
if self.active != idx {
|
if self.active != idx {
|
||||||
let old_active = self.active;
|
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::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);
|
state.add_workspace_state(&self.workspaces[idx].handle, WState::Active);
|
||||||
self.previously_active = Some((old_active, Instant::now()));
|
self.previously_active = Some((old_active, Instant::now()));
|
||||||
self.active = idx;
|
self.active = idx;
|
||||||
|
|
@ -299,13 +323,13 @@ impl WorkspaceSet {
|
||||||
self.output = new_output.clone();
|
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 let Some((_, start)) = self.previously_active {
|
||||||
if Instant::now().duration_since(start).as_millis() >= ANIMATION_DURATION.as_millis() {
|
if Instant::now().duration_since(start).as_millis() >= ANIMATION_DURATION.as_millis() {
|
||||||
self.previously_active = None;
|
self.previously_active = None;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.workspaces[self.active].refresh();
|
self.workspaces[self.active].refresh(xdg_activation_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -332,7 +356,7 @@ impl WorkspaceSet {
|
||||||
if self
|
if self
|
||||||
.workspaces
|
.workspaces
|
||||||
.last()
|
.last()
|
||||||
.map(|last| last.windows().next().is_some())
|
.map(|last| !last.pending_tokens.is_empty() || last.windows().next().is_some())
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
self.add_empty_workspace(state);
|
self.add_empty_workspace(state);
|
||||||
|
|
@ -342,7 +366,8 @@ impl WorkspaceSet {
|
||||||
let len = self.workspaces.len();
|
let len = self.workspaces.len();
|
||||||
let mut keep = vec![true; len];
|
let mut keep = vec![true; len];
|
||||||
for (i, workspace) in self.workspaces.iter().enumerate() {
|
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 {
|
if !has_windows && i != self.active && i != len - 1 {
|
||||||
state.remove_workspace(workspace.handle);
|
state.remove_workspace(workspace.handle);
|
||||||
|
|
@ -370,6 +395,7 @@ impl WorkspaceSet {
|
||||||
amount: usize,
|
amount: usize,
|
||||||
state: &mut WorkspaceUpdateGuard<State>,
|
state: &mut WorkspaceUpdateGuard<State>,
|
||||||
toplevel_info: &mut ToplevelInfoState<State, CosmicSurface>,
|
toplevel_info: &mut ToplevelInfoState<State, CosmicSurface>,
|
||||||
|
xdg_activation_state: &XdgActivationState,
|
||||||
) {
|
) {
|
||||||
if amount < self.workspaces.len() {
|
if amount < self.workspaces.len() {
|
||||||
// merge last ones
|
// merge last ones
|
||||||
|
|
@ -401,7 +427,7 @@ impl WorkspaceSet {
|
||||||
state.remove_workspace(workspace.handle);
|
state.remove_workspace(workspace.handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
last_space.refresh();
|
last_space.refresh(xdg_activation_state);
|
||||||
} else if amount > self.workspaces.len() {
|
} else if amount > self.workspaces.len() {
|
||||||
// add empty ones
|
// add empty ones
|
||||||
while amount > self.workspaces.len() {
|
while amount > self.workspaces.len() {
|
||||||
|
|
@ -508,6 +534,7 @@ impl Workspaces {
|
||||||
seats: impl Iterator<Item = Seat<State>>,
|
seats: impl Iterator<Item = Seat<State>>,
|
||||||
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
||||||
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
|
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
|
||||||
|
xdg_activation_state: &XdgActivationState,
|
||||||
) {
|
) {
|
||||||
if !self.sets.contains_key(output) {
|
if !self.sets.contains_key(output) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -557,7 +584,7 @@ impl Workspaces {
|
||||||
|
|
||||||
// update mapping
|
// update mapping
|
||||||
workspace.set_output(&new_output, toplevel_info_state);
|
workspace.set_output(&new_output, toplevel_info_state);
|
||||||
workspace.refresh();
|
workspace.refresh(xdg_activation_state);
|
||||||
|
|
||||||
// TODO: merge if mode = static
|
// TODO: merge if mode = static
|
||||||
new_set.workspaces.push(workspace);
|
new_set.workspaces.push(workspace);
|
||||||
|
|
@ -576,7 +603,7 @@ impl Workspaces {
|
||||||
self.backup_set = Some(set);
|
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,
|
config: &Config,
|
||||||
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
||||||
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
|
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
|
||||||
|
xdg_activation_state: &XdgActivationState,
|
||||||
) {
|
) {
|
||||||
let old_mode = self.mode;
|
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(
|
pub fn refresh(
|
||||||
&mut self,
|
&mut self,
|
||||||
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
||||||
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
|
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
|
||||||
|
xdg_activation_state: &XdgActivationState,
|
||||||
) {
|
) {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
WorkspaceMode::Global => {
|
WorkspaceMode::Global => {
|
||||||
|
|
@ -696,10 +725,10 @@ impl Workspaces {
|
||||||
let mut active = self.sets[0].active;
|
let mut active = self.sets[0].active;
|
||||||
let mut keep = vec![true; len];
|
let mut keep = vec![true; len];
|
||||||
for i in 0..len {
|
for i in 0..len {
|
||||||
let has_windows = self
|
let has_windows = self.sets.values().any(|s| {
|
||||||
.sets
|
!s.workspaces[i].pending_tokens.is_empty()
|
||||||
.values()
|
|| s.workspaces[i].windows().next().is_some()
|
||||||
.any(|s| s.workspaces[i].windows().next().is_some());
|
});
|
||||||
|
|
||||||
if !has_windows && i != active && i != len - 1 {
|
if !has_windows && i != active && i != len - 1 {
|
||||||
for workspace in self.sets.values().map(|s| &s.workspaces[i]) {
|
for workspace in self.sets.values().map(|s| &s.workspaces[i]) {
|
||||||
|
|
@ -733,7 +762,12 @@ impl Workspaces {
|
||||||
}
|
}
|
||||||
WorkspaceAmount::Static(amount) => {
|
WorkspaceAmount::Static(amount) => {
|
||||||
for set in self.sets.values_mut() {
|
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) => {
|
WorkspaceAmount::Static(amount) => {
|
||||||
for set in self.sets.values_mut() {
|
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() {
|
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 {
|
for (_, s) in &mut self.sets {
|
||||||
s.theme = theme.clone();
|
s.theme = theme.clone();
|
||||||
for mut w in &mut s.workspaces {
|
for mut w in &mut s.workspaces {
|
||||||
|
|
@ -837,7 +876,7 @@ impl Workspaces {
|
||||||
m.force_redraw();
|
m.force_redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
w.refresh();
|
w.refresh(xdg_activation_state);
|
||||||
w.dirty.store(true, Ordering::Relaxed);
|
w.dirty.store(true, Ordering::Relaxed);
|
||||||
w.recalculate();
|
w.recalculate();
|
||||||
}
|
}
|
||||||
|
|
@ -854,6 +893,7 @@ impl Shell {
|
||||||
client_should_see_privileged_protocols,
|
client_should_see_privileged_protocols,
|
||||||
);
|
);
|
||||||
let xdg_shell_state = XdgShellState::new::<State>(dh);
|
let xdg_shell_state = XdgShellState::new::<State>(dh);
|
||||||
|
let xdg_activation_state = XdgActivationState::new::<State>(dh);
|
||||||
let toplevel_info_state =
|
let toplevel_info_state =
|
||||||
ToplevelInfoState::new(dh, client_should_see_privileged_protocols);
|
ToplevelInfoState::new(dh, client_should_see_privileged_protocols);
|
||||||
let toplevel_management_state = ToplevelManagementState::new::<State, _>(
|
let toplevel_management_state = ToplevelManagementState::new::<State, _>(
|
||||||
|
|
@ -874,12 +914,14 @@ impl Shell {
|
||||||
|
|
||||||
pending_windows: Vec::new(),
|
pending_windows: Vec::new(),
|
||||||
pending_layers: Vec::new(),
|
pending_layers: Vec::new(),
|
||||||
|
pending_activations: HashMap::new(),
|
||||||
override_redirect_windows: Vec::new(),
|
override_redirect_windows: Vec::new(),
|
||||||
|
|
||||||
layer_shell_state,
|
layer_shell_state,
|
||||||
toplevel_info_state,
|
toplevel_info_state,
|
||||||
toplevel_management_state,
|
toplevel_management_state,
|
||||||
xdg_shell_state,
|
xdg_shell_state,
|
||||||
|
xdg_activation_state,
|
||||||
workspace_state,
|
workspace_state,
|
||||||
|
|
||||||
theme,
|
theme,
|
||||||
|
|
@ -905,6 +947,7 @@ impl Shell {
|
||||||
seats,
|
seats,
|
||||||
&mut self.workspace_state.update(),
|
&mut self.workspace_state.update(),
|
||||||
&mut self.toplevel_info_state,
|
&mut self.toplevel_info_state,
|
||||||
|
&self.xdg_activation_state,
|
||||||
);
|
);
|
||||||
self.refresh(); // cleans up excess of workspaces and empty workspaces
|
self.refresh(); // cleans up excess of workspaces and empty workspaces
|
||||||
}
|
}
|
||||||
|
|
@ -912,8 +955,12 @@ impl Shell {
|
||||||
pub fn update_config(&mut self, config: &Config) {
|
pub fn update_config(&mut self, config: &Config) {
|
||||||
let mut workspace_state = self.workspace_state.update();
|
let mut workspace_state = self.workspace_state.update();
|
||||||
let toplevel_info_state = &mut self.toplevel_info_state;
|
let toplevel_info_state = &mut self.toplevel_info_state;
|
||||||
self.workspaces
|
self.workspaces.update_config(
|
||||||
.update_config(config, &mut workspace_state, toplevel_info_state);
|
config,
|
||||||
|
&mut workspace_state,
|
||||||
|
toplevel_info_state,
|
||||||
|
&self.xdg_activation_state,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate(
|
pub fn activate(
|
||||||
|
|
@ -958,6 +1005,12 @@ impl Shell {
|
||||||
self.workspaces.active_mut(output)
|
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>(
|
pub fn visible_outputs_for_surface<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
surface: &'a WlSurface,
|
surface: &'a WlSurface,
|
||||||
|
|
@ -1190,9 +1243,13 @@ impl Shell {
|
||||||
|
|
||||||
self.popups.cleanup();
|
self.popups.cleanup();
|
||||||
|
|
||||||
|
self.xdg_activation_state.retain_tokens(|_, data| {
|
||||||
|
Instant::now().duration_since(data.timestamp) < Duration::from_secs(5)
|
||||||
|
});
|
||||||
self.workspaces.refresh(
|
self.workspaces.refresh(
|
||||||
&mut self.workspace_state.update(),
|
&mut self.workspace_state.update(),
|
||||||
&mut self.toplevel_info_state,
|
&mut self.toplevel_info_state,
|
||||||
|
&self.xdg_activation_state,
|
||||||
);
|
);
|
||||||
|
|
||||||
for output in self.outputs() {
|
for output in self.outputs() {
|
||||||
|
|
@ -1258,10 +1315,42 @@ impl Shell {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (window, seat, output) = state.common.shell.pending_windows.remove(pos);
|
let (window, seat, output) = state.common.shell.pending_windows.remove(pos);
|
||||||
|
|
||||||
let should_be_fullscreen = output.is_some();
|
let pending_activation = state
|
||||||
let output = output.unwrap_or_else(|| seat.active_output());
|
.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() {
|
if let Some((mapped, layer, previous_workspace)) = workspace.remove_fullscreen() {
|
||||||
let old_handle = workspace.handle.clone();
|
let old_handle = workspace.handle.clone();
|
||||||
let new_workspace_handle = state
|
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.shell.toplevel_info_state.new_toplevel(&window);
|
||||||
state
|
state
|
||||||
.common
|
.common
|
||||||
|
|
@ -1302,6 +1410,9 @@ impl Shell {
|
||||||
{
|
{
|
||||||
mapped.set_debug(state.common.egui.active);
|
mapped.set_debug(state.common.egui.active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let workspace_empty = workspace.mapped().next().is_none();
|
||||||
|
|
||||||
if layout::should_be_floating(&window) || !workspace.tiling_enabled {
|
if layout::should_be_floating(&window) || !workspace.tiling_enabled {
|
||||||
workspace.floating_layer.map(mapped.clone(), None);
|
workspace.floating_layer.map(mapped.clone(), None);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1324,7 +1435,14 @@ impl Shell {
|
||||||
workspace.fullscreen_request(&mapped.active_window(), None);
|
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);
|
let active_space = state.common.shell.active_space(&output);
|
||||||
for mapped in active_space.mapped() {
|
for mapped in active_space.mapped() {
|
||||||
|
|
@ -1666,7 +1784,13 @@ impl Shell {
|
||||||
pub(crate) fn set_theme(&mut self, theme: cosmic::Theme) {
|
pub(crate) fn set_theme(&mut self, theme: cosmic::Theme) {
|
||||||
self.theme = theme.clone();
|
self.theme = theme.clone();
|
||||||
self.refresh();
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,12 @@ use smithay::{
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::{add_blocker, Blocker, BlockerState},
|
compositor::{add_blocker, Blocker, BlockerState},
|
||||||
seat::WaylandFocus,
|
seat::WaylandFocus,
|
||||||
|
xdg_activation::{XdgActivationState, XdgActivationToken},
|
||||||
},
|
},
|
||||||
xwayland::X11Surface,
|
xwayland::X11Surface,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, HashSet, VecDeque},
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
Arc,
|
Arc,
|
||||||
|
|
@ -87,6 +88,7 @@ pub struct Workspace {
|
||||||
pub pending_buffers: Vec<(ScreencopySession, BufferParams)>,
|
pub pending_buffers: Vec<(ScreencopySession, BufferParams)>,
|
||||||
pub screencopy_sessions: Vec<DropableSession>,
|
pub screencopy_sessions: Vec<DropableSession>,
|
||||||
pub output_stack: VecDeque<String>,
|
pub output_stack: VecDeque<String>,
|
||||||
|
pub pending_tokens: HashSet<XdgActivationToken>,
|
||||||
pub(super) backdrop_id: Id,
|
pub(super) backdrop_id: Id,
|
||||||
pub dirty: AtomicBool,
|
pub dirty: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
@ -227,12 +229,13 @@ impl Workspace {
|
||||||
pending_buffers: Vec::new(),
|
pending_buffers: Vec::new(),
|
||||||
screencopy_sessions: Vec::new(),
|
screencopy_sessions: Vec::new(),
|
||||||
output_stack: VecDeque::new(),
|
output_stack: VecDeque::new(),
|
||||||
|
pending_tokens: HashSet::new(),
|
||||||
backdrop_id: Id::new(),
|
backdrop_id: Id::new(),
|
||||||
dirty: AtomicBool::new(false),
|
dirty: AtomicBool::new(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh(&mut self) {
|
pub fn refresh(&mut self, xdg_activation_state: &XdgActivationState) {
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
puffin::profile_function!();
|
puffin::profile_function!();
|
||||||
|
|
||||||
|
|
@ -243,6 +246,9 @@ impl Workspace {
|
||||||
|
|
||||||
self.floating_layer.refresh();
|
self.floating_layer.refresh();
|
||||||
self.tiling_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) {
|
pub fn refresh_focus_stack(&mut self) {
|
||||||
|
|
|
||||||
|
|
@ -30,5 +30,6 @@ pub mod viewporter;
|
||||||
pub mod virtual_keyboard;
|
pub mod virtual_keyboard;
|
||||||
pub mod wl_drm;
|
pub mod wl_drm;
|
||||||
pub mod workspace;
|
pub mod workspace;
|
||||||
|
pub mod xdg_activation;
|
||||||
pub mod xdg_shell;
|
pub mod xdg_shell;
|
||||||
pub mod xwayland_keyboard_grab;
|
pub mod xwayland_keyboard_grab;
|
||||||
|
|
|
||||||
150
src/wayland/handlers/xdg_activation.rs
Normal file
150
src/wayland/handlers/xdg_activation.rs
Normal 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(¤t_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 == ¤t_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);
|
||||||
|
|
@ -312,7 +312,7 @@ impl XdgShellHandler for State {
|
||||||
.visible_outputs_for_surface(surface.wl_surface())
|
.visible_outputs_for_surface(surface.wl_surface())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
for output in outputs.iter() {
|
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
|
// animations might be unblocked now
|
||||||
|
|
|
||||||
|
|
@ -5,23 +5,29 @@ use crate::{
|
||||||
shell::{focus::target::KeyboardFocusTarget, CosmicSurface, Shell},
|
shell::{focus::target::KeyboardFocusTarget, CosmicSurface, Shell},
|
||||||
state::State,
|
state::State,
|
||||||
utils::prelude::*,
|
utils::prelude::*,
|
||||||
wayland::{handlers::screencopy::PendingScreencopyBuffers, protocols::screencopy::SessionType},
|
wayland::{
|
||||||
|
handlers::{screencopy::PendingScreencopyBuffers, xdg_activation::ActivationContext},
|
||||||
|
protocols::screencopy::SessionType,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::drm::DrmNode,
|
backend::drm::DrmNode,
|
||||||
desktop::space::SpaceElement,
|
desktop::space::SpaceElement,
|
||||||
reexports::x11rb::protocol::xproto::Window as X11Window,
|
reexports::x11rb::protocol::xproto::Window as X11Window,
|
||||||
utils::{Logical, Point, Rectangle, Size},
|
utils::{Logical, Point, Rectangle, Size},
|
||||||
wayland::selection::{
|
wayland::{
|
||||||
data_device::{
|
selection::{
|
||||||
clear_data_device_selection, current_data_device_selection_userdata,
|
data_device::{
|
||||||
request_data_device_client_selection, set_data_device_selection,
|
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::{
|
xdg_activation::XdgActivationToken,
|
||||||
clear_primary_selection, current_primary_selection_userdata,
|
|
||||||
request_primary_client_selection, set_primary_selection,
|
|
||||||
},
|
|
||||||
SelectionTarget,
|
|
||||||
},
|
},
|
||||||
xwayland::{
|
xwayland::{
|
||||||
xwm::{Reorder, XwmId},
|
xwm::{Reorder, XwmId},
|
||||||
|
|
@ -145,12 +151,29 @@ impl XwmHandler for State {
|
||||||
warn!(?window, ?err, "Failed to send Xwayland Mapped-Event",);
|
warn!(?window, ?err, "Failed to send Xwayland Mapped-Event",);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let startup_id = window.startup_id();
|
||||||
let surface = CosmicSurface::X11(window.clone());
|
let surface = CosmicSurface::X11(window.clone());
|
||||||
if self.common.shell.element_for_surface(&surface).is_some() {
|
if self.common.shell.element_for_surface(&surface).is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let seat = self.common.last_active_seat().clone();
|
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
|
self.common
|
||||||
.shell
|
.shell
|
||||||
.pending_windows
|
.pending_windows
|
||||||
|
|
@ -172,6 +195,30 @@ impl XwmHandler for State {
|
||||||
})
|
})
|
||||||
.cloned()
|
.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);
|
Shell::map_window(self, &window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -224,7 +271,7 @@ impl XwmHandler for State {
|
||||||
self.common.shell.outputs().cloned().collect::<Vec<_>>()
|
self.common.shell.outputs().cloned().collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
for output in outputs.iter() {
|
for output in outputs.iter() {
|
||||||
self.common.shell.active_space_mut(output).refresh();
|
self.common.shell.refresh_active_space(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
// screencopy
|
// screencopy
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue