Compare commits

..

12 commits

Author SHA1 Message Date
1dda5a558e fix: absorb libcosmic theme-v2 API (private containers, text::Style, wgpu 28)
Leyoda 2026 – GPLv3
2026-07-03 12:36:51 +02:00
11e229fa44 Refresh Cargo lockfile after upstream rebase 2026-06-13 08:39:57 +02:00
devpa
84f9dcbe0e input: allow layout switching and brightness/volume control under session lock 2026-06-13 08:39:57 +02:00
Hosted Weblate
4a22c9f3dc i18n: translation updates from weblate
Co-authored-by: therealmate <hellogaming91@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-comp/hu/
Translation: Pop OS/COSMIC Comp
2026-06-13 08:39:57 +02:00
647a672b25 chore: resolve local graphics dependencies 2026-05-25 19:37:09 +02:00
d038b6f89f chore: use local glyphon lockfile 2026-05-25 18:28:56 +02:00
e3d5e4668f chore: refresh lockfile and bump rust-toolchain to 1.93
Required to absorb the libcosmic-yoda squash rebase on 2026-05-25.
libcosmic-yoda now declares rust-version = "1.93".

Leyoda 2026 – GPLv3
2026-05-25 14:27:10 +02:00
0ae782638c chore: use local cosmic-text checkout 2026-05-25 11:34:38 +02:00
7b05b2e768 yoda: refresh local dbus binding lock 2026-05-25 09:55:23 +02:00
267fcd80b3 fix: keep compositor dbus off tokio runtime 2026-05-24 18:18:51 +02:00
56230f0ade fix: keep tokio runtime available for compositor 2026-05-24 16:08:29 +02:00
934db82775 chore: align compositor with local cosmic stack 2026-05-24 11:40:51 +02:00
54 changed files with 1600 additions and 2387 deletions

818
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -12,12 +12,13 @@ members = ["cosmic-comp-config"]
[dependencies]
anyhow = { version = "1.0.102", features = ["backtrace"] }
bitflags = "2.11.0"
calloop = { version = "0.14.4", features = ["executor", "stream"] }
calloop = { version = "0.14.4", features = ["executor"] }
cosmic-comp-config = { path = "cosmic-comp-config", features = [
"libdisplay-info",
"output",
] }
cosmic-config = { path = "../libcosmic/cosmic-config", features = [
"async-std",
"calloop",
"macro",
] }
@ -43,7 +44,7 @@ iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" }
indexmap = "2.13"
keyframe = "1.1.1"
cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [
"tokio",
"async-std",
"wayland",
"multi-window",
] }
@ -77,7 +78,6 @@ xdg = "^3.0"
xdg-user = "0.2.1"
xkbcommon = "0.9"
zbus = "5.14.0"
tokio = { version = "1", features = ["rt-multi-thread"] }
profiling = { version = "1.0" }
rustix = { version = "1.1.4", features = ["process"] }
rand = "0.10"
@ -85,9 +85,10 @@ rand = "0.10"
clap_lex = "1.0"
parking_lot = "0.12.5"
logind-zbus = { version = "5.3.2", optional = true }
futures-executor = { version = "0.3.32" }
futures-executor = { version = "0.3.32", features = ["thread-pool"] }
futures-util = "0.3.32"
cgmath = "0.18.0"
smallvec = "1.15.1"
[dependencies.id_tree]
branch = "feature/copy_clone"
@ -122,8 +123,7 @@ optional = true
[features]
debug = ["egui", "egui_plot", "smithay-egui", "anyhow/backtrace"]
default = ["systemd"]
logind = ["logind-zbus"]
systemd = ["libsystemd", "logind"]
systemd = ["libsystemd", "logind-zbus"]
profile-with-tracy = ["profiling/profile-with-tracy", "tracy-client/default"]
profile-with-tracy-gpu = ["profile-with-tracy", "smithay/tracy_gpu_profiling"]
@ -175,4 +175,4 @@ dnd = { path = "../window_clipboard/dnd" }
mime = { path = "../window_clipboard/mime" }
[patch.crates-io]
smithay = { git = "https://github.com/smithay/smithay.git", rev = "8eb4076" }
smithay = { git = "https://github.com/smithay/smithay.git", rev = "774f2ab" }

View file

@ -39,7 +39,6 @@ vendor:
mkdir -p .cargo
cargo vendor | head -n -1 > .cargo/config
echo 'directory = "vendor"' >> .cargo/config
[ -n "$(SOURCE_GIT_HASH)" ] && printf '\n[env]\nGIT_HASH = "%s"\n' "$(SOURCE_GIT_HASH)" >> .cargo/config || true
tar pcf vendor.tar vendor
rm -rf vendor

View file

@ -101,7 +101,6 @@ pub struct CosmicCompConfig {
pub appearance_settings: AppearanceConfig,
/// Hide the cursor after this many seconds of pointer inactivity (None disables)
pub cursor_hide_timeout: Option<u32>,
pub activation_policy: ActivationPolicy,
}
impl Default for CosmicCompConfig {
@ -139,7 +138,6 @@ impl Default for CosmicCompConfig {
accessibility_zoom: ZoomConfig::default(),
appearance_settings: AppearanceConfig::default(),
cursor_hide_timeout: None,
activation_policy: ActivationPolicy::default(),
}
}
}
@ -229,14 +227,6 @@ pub enum EavesdroppingKeyboardMode {
All,
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialEq, Eq)]
pub enum ActivationPolicy {
#[default]
Focus,
FocusIfActiveWorkspace,
Urgent,
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum XwaylandDescaling {

View file

@ -63,5 +63,5 @@ pub struct PinnedWorkspace {
pub output: OutputMatch,
pub tiling_enabled: bool,
pub id: Option<String>,
pub name: Option<String>,
// TODO: name
}

View file

@ -48,6 +48,5 @@
(appid: "zoom", titles: [".*"]),
(appid: "Tor Browser", titles: [".*"]),
(appid: "^.*?action=join.*$", titles: [".*"]),
(appid: "^(slack|com.slack.Slack)", titles: ["^.*?(Huddle Preview).*"]),
(appid: "^(thunderbird|org.mozilla.thunderbird)(-esr|_esr)*", titles: ["^(Write:).*"]),
]

View file

@ -96,6 +96,7 @@ pub struct Device {
pub inner: InnerDevice,
pub drm: GbmDrmOutputManager,
supports_atomic: bool,
pub texture_formats: FormatSet,
event_token: Option<RegistrationToken>,
pub socket: Option<Socket>,
@ -710,6 +711,7 @@ impl Device {
let (drm, notifier) = DrmDevice::new(fd.clone(), false)
.with_context(|| format!("Failed to initialize drm device for: {}", path.display()))?;
let dev_node = DrmNode::from_dev_id(dev)?;
let supports_atomic = drm.is_atomic();
let gbm = GbmDevice::new(fd)
.with_context(|| format!("Failed to initialize GBM device for {}", path.display()))?;
@ -829,6 +831,7 @@ impl Device {
active_clients,
},
supports_atomic,
texture_formats,
event_token: Some(token),
socket,
@ -837,7 +840,8 @@ impl Device {
pub fn enumerate_surfaces(&mut self) -> Result<OutputChanges> {
// enumerate our outputs
let config = drm_helpers::display_configuration(self.drm.device_mut())?;
let config =
drm_helpers::display_configuration(self.drm.device_mut(), self.supports_atomic)?;
let surfaces = self
.inner

View file

@ -3,7 +3,6 @@
use anyhow::{Context, Result, anyhow};
use libdisplay_info::{edid::DisplayDescriptorTag, info::Info};
use smithay::{
backend::drm::DrmDevice,
reexports::drm::control::{
AtomicCommitFlags, Device as ControlDevice, Mode, ModeFlags, PlaneType, ResourceHandle,
atomic::AtomicModeReq,
@ -17,7 +16,8 @@ use smithay::{
use std::{collections::HashMap, ops::Range};
pub fn display_configuration(
device: &mut DrmDevice,
device: &mut impl ControlDevice,
supports_atomic: bool,
) -> Result<HashMap<connector::Handle, Option<crtc::Handle>>> {
let res_handles = device.resource_handles()?;
let connectors = res_handles.connectors();
@ -73,10 +73,28 @@ pub fn display_configuration(
}
// And then cleanup
if device.is_atomic() {
if supports_atomic {
let mut req = AtomicModeReq::new();
let plane_handles = device.plane_handles()?;
for conn in connectors
.iter()
.flat_map(|conn| device.get_connector(*conn, false).ok())
.filter(|conn| {
if let Some(enc) = conn.current_encoder()
&& let Ok(enc) = device.get_encoder(enc)
&& let Some(crtc) = enc.crtc()
{
return cleanup.contains(&crtc);
}
false
})
.map(|info| info.handle())
{
let crtc_id = get_prop(device, conn, "CRTC_ID")?;
req.add_property(conn, crtc_id, property::Value::CRTC(None));
}
// We cannot just shortcut and use the legacy api for all cleanups because of this.
// (Technically a device does not need to be atomic for planes to be used, but nobody does this otherwise.)
for plane in plane_handles {
@ -90,7 +108,7 @@ pub fn display_configuration(
_ => false,
},
)?;
if !is_primary && !cleanup.contains(&crtc) {
if cleanup.contains(&crtc) || !is_primary {
let crtc_id = get_prop(device, plane, "CRTC_ID")?;
let fb_id = get_prop(device, plane, "FB_ID")?;
req.add_property(plane, crtc_id, property::Value::CRTC(None));
@ -98,78 +116,29 @@ pub fn display_configuration(
}
}
}
for crtc in cleanup {
let mode_id = get_prop(device, crtc, "MODE_ID")?;
let active = get_prop(device, crtc, "ACTIVE")?;
req.add_property(crtc, active, property::Value::Boolean(false));
req.add_property(crtc, mode_id, property::Value::Unknown(0));
}
device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?;
} else {
for crtc in res_handles.crtcs() {
#[allow(deprecated)]
let _ = device.set_cursor(*crtc, Option::<&DumbBuffer>::None);
}
for crtc in cleanup {
// null commit (necessary to trigger removal on the kernel side with the legacy api.)
let _ = device.set_crtc(crtc, None, (0, 0), &[], None);
}
}
disable_crtcs(device, &cleanup)?;
Ok(map)
}
/// Disables the given CRTCs and detaches their connectors/planes.
pub fn disable_crtcs(device: &mut DrmDevice, crtcs: &[crtc::Handle]) -> Result<()> {
if crtcs.is_empty() {
return Ok(());
}
let res_handles = device.resource_handles()?;
if device.is_atomic() {
let mut req = AtomicModeReq::new();
for conn in res_handles
.connectors()
.iter()
.flat_map(|conn| device.get_connector(*conn, false).ok())
.filter(|conn| {
if let Some(enc) = conn.current_encoder()
&& let Ok(enc) = device.get_encoder(enc)
&& let Some(crtc) = enc.crtc()
{
return crtcs.contains(&crtc);
}
false
})
.map(|info| info.handle())
{
let crtc_id = get_prop(device, conn, "CRTC_ID")?;
req.add_property(conn, crtc_id, property::Value::CRTC(None));
}
for plane in device.plane_handles()? {
let info = device.get_plane(plane)?;
if let Some(crtc) = info.crtc()
&& crtcs.contains(&crtc)
{
let crtc_id = get_prop(device, plane, "CRTC_ID")?;
let fb_id = get_prop(device, plane, "FB_ID")?;
req.add_property(plane, crtc_id, property::Value::CRTC(None));
req.add_property(plane, fb_id, property::Value::Framebuffer(None));
}
}
for crtc in crtcs {
let mode_id = get_prop(device, *crtc, "MODE_ID")?;
let active = get_prop(device, *crtc, "ACTIVE")?;
req.add_property(*crtc, active, property::Value::Boolean(false));
req.add_property(*crtc, mode_id, property::Value::Unknown(0));
}
device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?;
} else {
for crtc in crtcs {
// null commit (necessary to trigger removal on the kernel side with the legacy api.)
let _ = device.set_crtc(*crtc, None, (0, 0), &[], None);
}
}
Ok(())
}
pub fn interface_name(device: &impl ControlDevice, connector: connector::Handle) -> Result<String> {
let conn_info = device.get_connector(connector, false)?;

View file

@ -374,7 +374,6 @@ impl State {
for device in backend.drm_devices.values_mut() {
if let Err(err) = device.drm.lock().activate(true) {
error!(?err, "Failed to resume drm device");
continue;
}
if let Some(lease_state) = device.inner.leasing_global.as_mut() {
lease_state.resume::<State>();
@ -852,23 +851,11 @@ impl KmsGuard<'_> {
// first drop old surfaces
if !test_only {
let mut disabled_crtcs = Vec::new();
device.inner.surfaces.retain(|crtc, surface| {
if outputs
.iter()
.any(|o| !o.is_enabled() && surface.output == *o)
{
disabled_crtcs.push(*crtc);
false
} else {
true
}
});
if let Err(err) =
drm_helpers::disable_crtcs(device.drm.device_mut(), &disabled_crtcs)
{
warn!("Failed to disable crtcs for disabled outputs: {err}");
for output in outputs.iter().filter(|o| !o.is_enabled()) {
device
.inner
.surfaces
.retain(|_, surface| surface.output != *output);
}
}

View file

@ -1,30 +1,21 @@
// SPDX-License-Identifier: GPL-3.0-only
use clap_lex::OsStrExt;
use rustix::path::Arg;
use smithay::{
backend::{
SwapBuffersError,
allocator::{
Allocator,
dmabuf::{AnyError, Dmabuf, DmabufAllocator},
gbm::GbmAllocator,
},
drm::{CreateDrmNodeError, DrmNode},
renderer::{
RendererSuper,
gles::{GlesError, GlesRenderer, ffi},
glow::GlowRenderer,
multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
},
use smithay::backend::{
SwapBuffersError,
allocator::{
Allocator,
dmabuf::{AnyError, Dmabuf, DmabufAllocator},
gbm::GbmAllocator,
},
drm::{CreateDrmNodeError, DrmNode},
renderer::{
RendererSuper,
gles::{GlesError, GlesRenderer},
glow::GlowRenderer,
multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
},
reexports::drm::control::Device,
};
use std::{
borrow::{Borrow, BorrowMut},
cell::Cell,
ffi::{CStr, c_char},
};
use std::{borrow::Borrow, cell::Cell};
use std::{
collections::HashMap,
fmt,
@ -107,7 +98,7 @@ impl<A: AsFd + Clone + Send + 'static> GbmGlowBackend<A> {
}
}
impl<A: AsFd + Device + Clone + 'static> GraphicsApi for GbmGlowBackend<A> {
impl<A: AsFd + Clone + 'static> GraphicsApi for GbmGlowBackend<A> {
type Device = GbmGlowDevice;
type Error = Error;
@ -132,29 +123,11 @@ impl<A: AsFd + Device + Clone + 'static> GraphicsApi for GbmGlowBackend<A> {
.any(|renderer| renderer.node.dev_id() == node.dev_id())
})
.flat_map(|(node, (allocator, renderer))| {
let mut renderer = renderer.replace(None)?;
let is_intel = allocator
.as_ref()
.get_driver()
.is_ok_and(|drv| drv.name().contains("i915"));
let intel_export_quirk = is_intel
&& BorrowMut::<GlesRenderer>::borrow_mut(&mut renderer)
.with_context(|gl| unsafe {
CStr::from_ptr(gl.GetString(ffi::RENDERER) as *const c_char)
})
.is_ok_and(|name| {
name.as_str().is_ok_and(|name| {
name.contains("TGL")
|| name.contains("MTL")
|| name.contains("ARL")
|| name.contains("LNL")
})
});
let renderer = renderer.replace(None)?;
Some(GbmGlowDevice {
node: *node,
renderer,
intel_export_quirk,
allocator: Box::new(DmabufAllocator(allocator.clone())),
})
})
@ -177,7 +150,6 @@ impl<A: AsFd + Device + Clone + 'static> GraphicsApi for GbmGlowBackend<A> {
pub struct GbmGlowDevice {
node: DrmNode,
renderer: GlowRenderer,
intel_export_quirk: bool,
allocator: Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
}
@ -208,14 +180,9 @@ impl ApiDevice for GbmGlowDevice {
fn can_do_cross_device_imports(&self) -> bool {
!Borrow::<GlesRenderer>::borrow(&self.renderer).is_software()
}
fn should_do_cross_device_exports(&self) -> bool {
!self.intel_export_quirk
}
}
impl<T: GraphicsApi, A: AsFd + Device + Clone + 'static> FromGlesError
for MultiError<GbmGlowBackend<A>, T>
impl<T: GraphicsApi, A: AsFd + Clone + 'static> FromGlesError for MultiError<GbmGlowBackend<A>, T>
where
T::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,

View file

@ -1024,18 +1024,14 @@ impl SurfaceThreadState {
let animations_going = shell.animations_going();
let output = self.mirroring.as_ref().unwrap_or(&self.output);
if let Some((_, workspace)) = shell.workspaces.active(output) {
let seat = shell.seats.last_active();
if let Some(fullscreen_surface) = workspace.get_fullscreen(seat) {
if let Some(fullscreen_surface) = workspace.get_fullscreen() {
const _30_FPS: Duration = Duration::from_nanos(1_000_000_000 / 30);
(
true,
fullscreen_surface
.surface
.wl_surface()
.is_some_and(|surface| {
recursive_frame_time_estimation(&self.clock, &surface)
.is_some_and(|dur| dur <= _30_FPS)
}),
fullscreen_surface.wl_surface().is_some_and(|surface| {
recursive_frame_time_estimation(&self.clock, &surface)
.is_some_and(|dur| dur <= _30_FPS)
}),
animations_going,
)
} else {
@ -1494,21 +1490,18 @@ fn render_node_for_output(
let Some(workspace) = shell.active_space(output) else {
return *target_node;
};
let fullscreens: Vec<_> = workspace
.get_fullscreen_surfaces()
.map(|f| f.surface.clone())
.collect();
let nodes = if !fullscreens.is_empty() {
fullscreens
} else {
workspace
.mapped()
.map(|mapped| mapped.active_window())
.collect::<Vec<_>>()
}
.into_iter()
.flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s)))
.collect::<Vec<_>>();
let nodes = workspace
.get_fullscreen()
.map(|w| vec![w.clone()])
.unwrap_or_else(|| {
workspace
.mapped()
.map(|mapped| mapped.active_window())
.collect::<Vec<_>>()
})
.into_iter()
.flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s)))
.collect::<Vec<_>>();
if nodes.contains(target_node) || nodes.is_empty() {
*target_node

View file

@ -26,7 +26,7 @@ use smithay::{
calloop::{EventLoop, ping},
wayland_protocols::wp::presentation_time::server::wp_presentation_feedback,
wayland_server::DisplayHandle,
winit::event_loop::pump_events::PumpStatus,
winit::platform::pump_events::PumpStatus,
},
utils::Transform,
wayland::{dmabuf::DmabufFeedbackBuilder, presentation::Refresh},

View file

@ -45,8 +45,8 @@ mod types;
use cosmic::config::CosmicTk;
pub use cosmic_comp_config::EdidProduct;
use cosmic_comp_config::{
ActivationPolicy, AppearanceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior, XkbConfig,
XwaylandDescaling, XwaylandEavesdropping, ZoomConfig,
AppearanceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior, XkbConfig, XwaylandDescaling,
XwaylandEavesdropping, ZoomConfig,
input::{DeviceState as InputDeviceState, InputConfig, TouchpadOverride},
output::comp::{
OutputConfig, OutputInfo, OutputState, OutputsConfig, TransformDef, load_outputs,
@ -966,12 +966,6 @@ fn config_changed(config: cosmic_config::Config, keys: Vec<String>, state: &mut
}
}
}
"activation_policy" => {
let new = get_config::<ActivationPolicy>(&config, "activation_policy");
if new != state.common.config.cosmic_conf.activation_policy {
state.common.config.cosmic_conf.activation_policy = new;
}
}
_ => {}
}
}

View file

@ -1,12 +1,13 @@
// https://gitlab.gnome.org/GNOME/mutter/-/blob/main/data/dbus-interfaces/org.freedesktop.a11y.xml
use futures_executor::ThreadPool;
use smithay::{
backend::input::KeyState,
input::keyboard::{KeysymHandle, ModifiersState},
};
use std::{
collections::{HashMap, HashSet},
sync::{Arc, Mutex},
sync::{Arc, Mutex, OnceLock},
};
use tracing::debug;
use xkbcommon::xkb::Keysym;
@ -71,37 +72,40 @@ impl Clients {
#[derive(Debug)]
pub struct A11yKeyboardMonitorState {
executor: calloop::futures::Scheduler<()>,
executor: ThreadPool,
clients: Arc<Mutex<Clients>>,
active_virtual_mods: HashSet<Keysym>,
conn: zbus::Connection,
name_owners: NameOwners,
conn: Arc<OnceLock<zbus::Connection>>,
name_owners: Arc<OnceLock<NameOwners>>,
}
impl A11yKeyboardMonitorState {
pub async fn new(
conn: &zbus::Connection,
name_owners: &NameOwners,
executor: &calloop::futures::Scheduler<()>,
) -> zbus::Result<Self> {
pub fn new(executor: &ThreadPool) -> Self {
let clients = Arc::new(Mutex::new(Clients::default()));
let keyboard_monitor = KeyboardMonitor {
clients: clients.clone(),
name_owners: name_owners.clone(),
};
conn.object_server()
.at("/org/freedesktop/a11y/Manager", keyboard_monitor)
.await?;
conn.request_name("org.freedesktop.a11y.Manager").await?;
Ok(Self {
let clients_clone = clients.clone();
let conn_cell = Arc::new(OnceLock::new());
let conn_cell_clone = conn_cell.clone();
let name_owners_cell = Arc::new(OnceLock::new());
let name_owners_cell_clone = name_owners_cell.clone();
let executor_clone = executor.clone();
executor.spawn_ok(async move {
match serve(clients_clone, &executor_clone).await {
Ok((conn, name_owners)) => {
conn_cell_clone.set(conn).unwrap();
name_owners_cell_clone.set(name_owners).unwrap();
}
Err(err) => {
tracing::error!("Failed to serve `org.freedesktop.a11y.Manager`: {err}");
}
}
});
Self {
executor: executor.clone(),
clients,
active_virtual_mods: HashSet::new(),
conn: conn.clone(),
name_owners: name_owners.clone(),
})
conn: conn_cell,
name_owners: name_owners_cell,
}
}
pub fn has_virtual_mod(&self, keysym: Keysym) -> bool {
@ -150,6 +154,10 @@ impl A11yKeyboardMonitorState {
}
pub fn key_event(&self, modifiers: &ModifiersState, keysym: &KeysymHandle, state: KeyState) {
let Some(conn) = self.conn.get() else {
return;
};
let clients = self.clients.lock().unwrap();
for (unique_name, client) in clients.0.iter() {
if !client.watched && !self.has_key_grab(modifiers, keysym.modified_sym()) {
@ -157,7 +165,7 @@ impl A11yKeyboardMonitorState {
}
let mut signal_context =
SignalEmitter::new(&self.conn, "/org/freedesktop/a11y/Manager").unwrap();
SignalEmitter::new(conn, "/org/freedesktop/a11y/Manager").unwrap();
// Instead of sending signal to all clients, send only to authorized
// clients with registed watches.
signal_context = signal_context.set_destination(unique_name.clone().into());
@ -178,7 +186,7 @@ impl A11yKeyboardMonitorState {
unichar,
keysym.raw_code().raw() as u16,
);
let _ = self.executor.schedule(async {
self.executor.spawn_ok(async {
let _ = future.await;
});
}
@ -187,11 +195,13 @@ impl A11yKeyboardMonitorState {
pub fn refresh(&mut self) {
// Remove clients and associated grabs when unique names are no longer
// present on bus, or no longer hold approved name on bus.
self.clients
.lock()
.unwrap()
.0
.retain(|k, _| self.name_owners.check_owner_no_poll(k, ALLOWED_NAMES))
if let Some(name_owners) = self.name_owners.get() {
self.clients
.lock()
.unwrap()
.0
.retain(|k, _| name_owners.check_owner_no_poll(k, ALLOWED_NAMES))
}
}
}
@ -301,3 +311,20 @@ impl KeyboardMonitor {
keycode: u16,
) -> zbus::Result<()>;
}
async fn serve(
clients: Arc<Mutex<Clients>>,
executor: &ThreadPool,
) -> zbus::Result<(zbus::Connection, NameOwners)> {
let conn = zbus::Connection::session().await?;
let name_owners = NameOwners::new(&conn, executor).await?;
let keyboard_monitor = KeyboardMonitor {
clients,
name_owners: name_owners.clone(),
};
conn.object_server()
.at("/org/freedesktop/a11y/Manager", keyboard_monitor)
.await?;
conn.request_name("org.freedesktop.a11y.Manager").await?;
Ok((conn, name_owners))
}

View file

@ -1,34 +1,24 @@
use std::os::fd::OwnedFd;
use anyhow::{Context, Result};
use logind_zbus::manager::{InhibitType::HandleLidSwitch, ManagerProxy};
use logind_zbus::manager::{InhibitType::HandleLidSwitch, ManagerProxyBlocking};
use zbus::blocking::Connection;
use crate::state::Common;
pub fn inhibit_lid(common: &Common) -> Result<OwnedFd> {
let fd = futures_executor::block_on(async {
let conn = common.dbus_state.system_conn().await?;
let manager = ManagerProxy::new(conn).await?;
manager
.inhibit(
HandleLidSwitch,
"cosmic-comp",
"External output connected",
"block",
)
.await
})?;
pub fn inhibit_lid() -> Result<OwnedFd> {
let conn = Connection::system()?;
let proxy = ManagerProxyBlocking::new(&conn)?;
let fd = proxy.inhibit(
HandleLidSwitch,
"cosmic-comp",
"External output connected",
"block",
)?;
Ok(fd.into())
}
pub fn lid_closed(common: &Common) -> Result<bool> {
futures_executor::block_on(async {
let conn = common.dbus_state.system_conn().await?;
let manager = ManagerProxy::new(conn).await?;
manager
.lid_closed()
.await
.context("Failed to talk to logind")
})
pub fn lid_closed() -> Result<bool> {
let conn = Connection::system()?;
let proxy = ManagerProxyBlocking::new(&conn)?;
proxy.lid_closed().context("Failed to talk to logind")
}

View file

@ -3,168 +3,103 @@ use crate::{
utils::prelude::OutputExt,
};
use anyhow::{Context, Result};
use calloop::{InsertError, LoopHandle, stream::StreamSource};
use calloop::{InsertError, LoopHandle, RegistrationToken};
use cosmic_comp_config::output::comp::OutputState;
use std::{
cell::{RefCell, RefMut},
collections::HashMap,
rc::Rc,
};
use futures_executor::{ThreadPool, block_on};
use futures_util::stream::StreamExt;
use std::collections::HashMap;
use tracing::{error, warn};
use zbus::blocking::{Connection, fdo::DBusProxy};
pub mod a11y_keyboard_monitor;
use a11y_keyboard_monitor::A11yKeyboardMonitorState;
#[cfg(feature = "logind")]
#[cfg(feature = "systemd")]
pub mod logind;
mod name_owners;
mod power;
#[derive(Clone, Debug)]
pub struct DBusState(Rc<DBusStateInner>);
pub fn init(
evlh: &LoopHandle<'static, State>,
executor: &ThreadPool,
) -> Result<Vec<RegistrationToken>> {
let mut tokens = Vec::new();
#[derive(Debug)]
struct DBusStateInner {
evlh: LoopHandle<'static, State>,
executor: calloop::futures::Scheduler<()>,
session_conn: zbus::Result<zbus::Connection>,
system_conn: zbus::Result<zbus::Connection>,
a11y_keyboard_monitor: RefCell<Option<a11y_keyboard_monitor::A11yKeyboardMonitorState>>,
}
impl DBusState {
pub fn init(evlh: &LoopHandle<'static, State>) -> Self {
let (source, executor) = calloop::futures::executor().unwrap();
let session_conn = futures_executor::block_on(zbus::Connection::session());
let system_conn = futures_executor::block_on(zbus::Connection::system());
let state = Self(Rc::new(DBusStateInner {
evlh: evlh.clone(),
executor,
session_conn,
system_conn,
a11y_keyboard_monitor: RefCell::new(None),
}));
evlh.insert_source(source, |_, _, _| {}).unwrap();
let state_clone = state.clone();
state.spawn(async move {
if let Err(err) = init_session(&state_clone).await {
tracing::error!("Failed to initialize session DBus connection: {}", err);
}
});
let state_clone = state.clone();
state.spawn(async move {
if let Err(err) = init_system(&state_clone).await {
tracing::error!("Failed to initialize system DBus connection: {}", err);
}
});
state
}
pub fn a11y_keyboard_monitor(
&self,
) -> Option<RefMut<'_, a11y_keyboard_monitor::A11yKeyboardMonitorState>> {
RefMut::filter_map(self.0.a11y_keyboard_monitor.borrow_mut(), |x| x.as_mut()).ok()
}
// TODO Lazy async init when we don't have anything blocking main thread
async fn session_conn(&self) -> zbus::Result<&zbus::Connection> {
self.0.session_conn.as_ref().map_err(|err| err.clone())
}
async fn system_conn(&self) -> zbus::Result<&zbus::Connection> {
self.0.system_conn.as_ref().map_err(|err| err.clone())
}
fn spawn(&self, fut: impl Future<Output = ()> + 'static) {
let _ = self.0.executor.schedule(fut);
}
}
async fn init_session(state: &DBusState) -> zbus::Result<()> {
let conn = state.session_conn().await?;
let name_owners = name_owners::NameOwners::new(conn, &state.0.executor).await?;
let a11y_keyboard_monitor_state =
A11yKeyboardMonitorState::new(conn, &name_owners, &state.0.executor).await?;
*state.0.a11y_keyboard_monitor.borrow_mut() = Some(a11y_keyboard_monitor_state);
Ok(())
}
async fn init_system(state: &DBusState) -> zbus::Result<()> {
let conn = state.system_conn().await?.clone();
let evlh = state.0.evlh.clone();
state.spawn(async move {
if let Err(err) = power_hot_plug_task(conn, evlh).await {
tracing::warn!(?err, "Failed to initialize dbus handlers");
}
});
Ok(())
}
async fn power_hot_plug_task(
conn: zbus::Connection,
evlh: LoopHandle<'static, State>,
) -> Result<()> {
match power::init(&conn).await {
match block_on(power::init()) {
Ok(power_daemon) => {
if let Ok(stream) = power_daemon.receive_hot_plug_detect().await {
let source = StreamSource::new(stream).unwrap();
evlh.insert_source(source, |_, _, state| {
let nodes = match &mut state.backend {
BackendData::Kms(kms) => {
kms.drm_devices.keys().cloned().collect::<Vec<_>>()
let (tx, rx) = calloop::channel::channel();
let token = evlh
.insert_source(rx, |event, _, state| match event {
calloop::channel::Event::Msg(_) => {
let nodes = match &mut state.backend {
BackendData::Kms(kms) => {
kms.drm_devices.keys().cloned().collect::<Vec<_>>()
}
_ => Vec::new(),
};
let mut added = Vec::new();
for node in nodes {
match state.device_changed(node.dev_id()) {
Ok(outputs) => added.extend(outputs),
Err(err) => {
tracing::error!(?err, "Failed to update drm device {}.", node)
}
}
}
_ => Vec::new(),
};
let mut added = Vec::new();
for node in nodes {
match state.device_changed(node.dev_id()) {
Ok(outputs) => added.extend(outputs),
Err(err) => {
tracing::error!(?err, "Failed to update drm device {}.", node)
}
}
}
if let Err(err) = state.refresh_output_config() {
warn!("Unable to load output config: {}", err);
if !added.is_empty() {
for output in added {
output.config_mut().enabled = OutputState::Disabled;
}
if let Err(err) = state.refresh_output_config() {
error!("Unrecoverable config error: {}", err);
if let Err(err) = state.refresh_output_config() {
warn!("Unable to load output config: {}", err);
if !added.is_empty() {
for output in added {
output.config_mut().enabled = OutputState::Disabled;
}
if let Err(err) = state.refresh_output_config() {
error!("Unrecoverable config error: {}", err);
}
}
}
}
calloop::channel::Event::Closed => (),
})
.map_err(|InsertError { error, .. }| error)
.with_context(|| "Failed to add channel to event_loop")?;
}
// start helper thread
executor.spawn_ok(async move {
if let Ok(mut msg_iter) = power_daemon.receive_hot_plug_detect().await {
while let Some(msg) = msg_iter.next().await {
if tx.send(msg).is_err() {
break;
}
}
}
});
tokens.push(token);
}
Err(err) => {
tracing::info!(?err, "Failed to connect to com.system76.PowerDaemon");
}
};
Ok(())
Ok(tokens)
}
/// Updated the D-Bus activation environment with `WAYLAND_DISPLAY` and
/// `DISPLAY` variables.
pub fn ready(common: &Common) -> Result<()> {
futures_executor::block_on(async {
let conn = common.dbus_state.session_conn().await?;
let dbus = zbus::fdo::DBusProxy::new(conn).await?;
let conn = Connection::session()?;
let proxy = DBusProxy::new(&conn)?;
proxy.update_activation_environment(HashMap::from([
("WAYLAND_DISPLAY", common.socket.to_str().unwrap()),
(
"DISPLAY",
&common
.xwayland_state
.as_ref()
.map(|s| format!(":{}", s.display))
.unwrap_or_default(),
),
]))?;
dbus.update_activation_environment(HashMap::from([
("WAYLAND_DISPLAY", common.socket.to_str().unwrap()),
(
"DISPLAY",
&common
.xwayland_state
.as_ref()
.map(|s| format!(":{}", s.display))
.unwrap_or_default(),
),
]))
.await
})?;
Ok(())
}

View file

@ -2,10 +2,9 @@
//!
//! Compare to Mutter's `MetaDbusAccessChecker`
use futures_util::{
StreamExt,
stream::{FusedStream, FuturesUnordered},
};
use futures_executor::ThreadPool;
use futures_util::stream::FusedStream;
use futures_util::{StreamExt, stream::FuturesUnordered};
use std::{
collections::{HashMap, HashSet},
future::{Future, poll_fn},
@ -95,10 +94,7 @@ fn update_task(inner: Weak<Mutex<Inner>>) -> impl Future<Output = ()> {
pub struct NameOwners(Arc<Mutex<Inner>>);
impl NameOwners {
pub async fn new(
connection: &zbus::Connection,
executor: &calloop::futures::Scheduler<()>,
) -> zbus::Result<Self> {
pub async fn new(connection: &zbus::Connection, executor: &ThreadPool) -> zbus::Result<Self> {
let dbus = fdo::DBusProxy::new(connection).await?;
let stream = dbus.receive_name_owner_changed().await?;
@ -130,7 +126,7 @@ impl NameOwners {
}));
if enforce {
let _ = executor.schedule(update_task(Arc::downgrade(&inner)));
executor.spawn_ok(update_task(Arc::downgrade(&inner)));
}
Ok(NameOwners(inner))

View file

@ -79,8 +79,9 @@ pub trait PowerDaemon {
fn power_profile_switch(&self, profile: &str) -> zbus::Result<()>;
}
pub async fn init(conn: &Connection) -> anyhow::Result<PowerDaemonProxy<'static>> {
let proxy = PowerDaemonProxy::new(conn).await?;
pub async fn init() -> anyhow::Result<PowerDaemonProxy<'static>> {
let conn = Connection::system().await?;
let proxy = PowerDaemonProxy::new(&conn).await?;
proxy.0.introspect().await?;
Ok(proxy)
}

View file

@ -52,21 +52,16 @@ use smithay::{
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent,
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,
GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent,
PointerGrab, PointerHandle, RelativeMotionEvent,
PointerGrab, RelativeMotionEvent,
},
touch::{DownEvent, MotionEvent as TouchMotionEvent, UpEvent},
},
output::Output,
reexports::{
input::Device as InputDevice,
wayland_server::{
Resource as _,
protocol::{wl_shm::Format as ShmFormat, wl_surface::WlSurface},
},
input::Device as InputDevice, wayland_server::protocol::wl_shm::Format as ShmFormat,
},
utils::{Logical, Point, Rectangle, SERIAL_COUNTER, Serial, Size},
utils::{Point, Rectangle, SERIAL_COUNTER, Serial},
wayland::{
compositor::CompositorHandler,
image_copy_capture::{BufferConstraints, CursorSessionRef},
keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat,
pointer_constraints::{PointerConstraint, with_pointer_constraint},
@ -354,6 +349,27 @@ impl State {
_ => {}
});
}
let original_position = position;
position += event.delta().as_global();
let output = shell
.outputs()
.find(|output| output.geometry().to_f64().contains(position))
.cloned()
.unwrap_or(current_output.clone());
let output_geometry = output.geometry();
position.x = position.x.clamp(
output_geometry.loc.x as f64,
(output_geometry.loc.x + output_geometry.size.w - 1) as f64,
);
position.y = position.y.clamp(
output_geometry.loc.y as f64,
(output_geometry.loc.y + output_geometry.size.h - 1) as f64,
);
let new_under = State::surface_under(position, &output, &shell)
.map(|(target, pos)| (target, pos.as_logical()));
std::mem::drop(shell);
ptr.relative_motion(
@ -371,25 +387,6 @@ impl State {
return;
}
let original_position = position;
position += event.delta().as_global();
let shell = self.common.shell.read();
let output = shell
.outputs()
.find(|output| output.geometry().to_f64().contains(position))
.cloned()
.unwrap_or(current_output.clone());
drop(shell);
let output_geometry = output.geometry();
position.x = position.x.clamp(
output_geometry.loc.x as f64,
(output_geometry.loc.x + output_geometry.size.w - 1) as f64,
);
position.y = position.y.clamp(
output_geometry.loc.y as f64,
(output_geometry.loc.y + output_geometry.size.h - 1) as f64,
);
if ptr.is_grabbed() {
if seat
.user_data()
@ -497,90 +494,50 @@ impl State {
}
}
// If confined, help user to update valid coordinates can improve user experience
let shell = self.common.shell.read();
let new_under = if pointer_confined && let Some((surface, surface_loc)) = &under
{
let is_legal = |pos: Point<f64, Global>, shell: &Shell| {
let new_under = State::surface_under(pos, &output, shell)
.map(|(target, pos)| (target, pos.as_logical()));
// TODO: We might need a solution that allows constraints to bypass the surface without affecting the constraints themselves
if new_under.as_ref().and_then(|(under, _)| under.wl_surface())
!= surface.wl_surface()
{
return (false, None);
// If confined, don't move pointer if it would go outside surface or region
if pointer_confined && let Some((surface, surface_loc)) = &under {
if new_under.as_ref().and_then(|(under, _)| under.wl_surface())
!= surface.wl_surface()
{
ptr.frame(self);
return;
}
match surface {
PointerFocusTarget::WlSurface { surface, .. } => {
if under_from_surface_tree(
surface,
position.as_logical() - surface_loc.to_f64(),
(0, 0),
WindowSurfaceType::ALL,
)
.is_none()
{
ptr.frame(self);
return;
}
}
match surface {
PointerFocusTarget::WlSurface { surface, .. } => {
if under_from_surface_tree(
surface,
PointerFocusTarget::X11Surface { surface, .. } => {
if surface
.surface_under(
position.as_logical() - surface_loc.to_f64(),
(0, 0),
WindowSurfaceType::ALL,
)
.is_none()
{
return (false, None);
}
}
PointerFocusTarget::X11Surface { surface, .. } => {
if surface
.surface_under(
position.as_logical() - surface_loc.to_f64(),
(0, 0),
WindowSurfaceType::ALL,
)
.is_none()
{
return (false, None);
}
}
_ => {}
}
if let Some(region) = &confine_region
&& !region
.contains((pos.as_logical() - *surface_loc).to_i32_round())
{
return (false, None);
}
(true, new_under)
};
match is_legal(position, &shell) {
(true, under) => under,
_ => {
let y_only_pos = Point::new(original_position.x, position.y);
let x_only_pos = Point::new(position.x, original_position.y);
match is_legal(y_only_pos, &shell) {
(true, under) => {
position = y_only_pos;
under
}
_ => match is_legal(x_only_pos, &shell) {
(true, under) => {
position = x_only_pos;
under
}
_ => {
position = original_position;
None
}
},
{
ptr.frame(self);
return;
}
}
_ => {}
}
if let Some(region) = confine_region
&& !region
.contains((position.as_logical() - *surface_loc).to_i32_round())
{
ptr.frame(self);
return;
}
} else {
State::surface_under(position, &output, &shell)
.map(|(target, pos)| (target, pos.as_logical()))
};
drop(shell);
if pointer_confined && new_under.is_none() {
ptr.frame(self);
return;
}
let serial = SERIAL_COUNTER.next_serial();
@ -595,32 +552,24 @@ impl State {
);
ptr.frame(self);
// If pointer is now in a constraint region and window is in focused, activate it
// If pointer is now in a constraint region, activate it
if let Some((under, surface_location)) = new_under
.and_then(|(target, loc)| Some((target.wl_surface()?.into_owned(), loc)))
{
let shell = self.common.shell.read();
let is_focused = seat
.get_keyboard()
.and_then(|k| k.current_focus())
.is_some_and(|f| f.has_surface(&shell, &under));
if is_focused {
with_pointer_constraint(&under, &ptr, |constraint| match constraint {
Some(constraint) if !constraint.is_active() => {
let region = match &*constraint {
PointerConstraint::Locked(locked) => locked.region(),
PointerConstraint::Confined(confined) => confined.region(),
};
let point =
(ptr.current_location() - surface_location).to_i32_round();
if region.is_none_or(|region| region.contains(point)) {
constraint.activate();
}
with_pointer_constraint(&under, &ptr, |constraint| match constraint {
Some(constraint) if !constraint.is_active() => {
let region = match &*constraint {
PointerConstraint::Locked(locked) => locked.region(),
PointerConstraint::Confined(confined) => confined.region(),
};
let point =
(ptr.current_location() - surface_location).to_i32_round();
if region.is_none_or(|region| region.contains(point)) {
constraint.activate();
}
_ => {}
});
}
}
_ => {}
});
}
let mut shell = self.common.shell.write();
@ -640,13 +589,11 @@ impl State {
for session in cursor_sessions_for_output(&shell, &output) {
if let Some((geometry, offset)) = seat.cursor_geometry(
(position - output_geometry.loc.to_f64())
.as_logical()
.to_buffer(
output.current_scale().fractional_scale(),
output.current_transform(),
&output_geometry.size.to_f64().as_logical(),
),
position.as_logical().to_buffer(
output.current_scale().fractional_scale(),
output.current_transform(),
&output_geometry.size.to_f64().as_logical(),
),
self.common.clock.now(),
) {
if session
@ -654,13 +601,8 @@ impl State {
.map(|constraint| constraint.size != geometry.size)
.unwrap_or(true)
{
let mut cursor_size = geometry.size;
// Client shouldn't try to allocate 0x0 buffer
if cursor_size == Size::new(0, 0) {
cursor_size = Size::new(1, 1);
}
session.update_constraints(BufferConstraints {
size: cursor_size,
size: geometry.size,
shm: vec![ShmFormat::Argb8888],
dma: None,
});
@ -683,11 +625,11 @@ impl State {
self.common.idle_notifier_state.notify_activity(&seat);
notify_cursor_activity(self, &seat);
let output = seat.active_output();
let output_geometry = output.geometry();
let position = output_geometry.loc.to_f64()
let geometry = output.geometry();
let position = geometry.loc.to_f64()
+ smithay::backend::input::AbsolutePositionEvent::position_transformed(
&event,
output_geometry.size.as_logical(),
geometry.size.as_logical(),
)
.as_global();
let serial = SERIAL_COUNTER.next_serial();
@ -709,13 +651,11 @@ impl State {
let shell = self.common.shell.read();
for session in cursor_sessions_for_output(&shell, &output) {
if let Some((geometry, offset)) = seat.cursor_geometry(
(position - output_geometry.loc.to_f64())
.as_logical()
.to_buffer(
output.current_scale().fractional_scale(),
output.current_transform(),
&output_geometry.size.to_f64().as_logical(),
),
position.as_logical().to_buffer(
output.current_scale().fractional_scale(),
output.current_transform(),
&geometry.size.to_f64().as_logical(),
),
self.common.clock.now(),
) {
if session
@ -723,13 +663,8 @@ impl State {
.map(|constraint| constraint.size != geometry.size)
.unwrap_or(true)
{
let mut cursor_size = geometry.size;
// Client shouldn't try to allocate 0x0 buffer
if cursor_size == Size::new(0, 0) {
cursor_size = Size::new(1, 1);
}
session.update_constraints(BufferConstraints {
size: cursor_size,
size: geometry.size,
shm: vec![ShmFormat::Argb8888],
dma: None,
});
@ -1600,7 +1535,7 @@ impl State {
}
InputEvent::Special(_) => {}
InputEvent::SwitchToggle { event } => {
#[cfg(feature = "logind")]
#[cfg(feature = "systemd")]
if event.switch() == Some(Switch::Lid) && self.common.inhibit_lid_fd.is_some() {
let backend = self.backend.lock();
let output = backend
@ -1682,9 +1617,9 @@ impl State {
.unwrap_or(false)
});
if let Some(a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor() {
a11y_keyboard_monitor.key_event(modifiers, &handle, event.state());
}
self.common
.a11y_keyboard_monitor_state
.key_event(modifiers, &handle, event.state());
// Leave move overview mode, if any modifier was released
if let Some(Trigger::KeyboardMove(action_modifiers)) =
@ -1844,54 +1779,61 @@ impl State {
)));
}
if let Some(mut a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor() {
if event.state() == KeyState::Released {
let removed =
a11y_keyboard_monitor.remove_active_virtual_mod(handle.modified_sym());
// If `Caps_Lock` is a virtual modifier, and is in locked state, clear it
if removed
&& handle.modified_sym() == Keysym::Caps_Lock
&& (modifiers.serialized.locked & 2) != 0
{
let seat = seat.clone();
let key_code = event.key_code();
self.common.event_loop_handle.insert_idle(move |state| {
if let Some(keyboard) = seat.get_keyboard() {
let serial = SERIAL_COUNTER.next_serial();
let time = state.common.clock.now().as_millis();
keyboard.input(
state,
key_code,
KeyState::Pressed,
serial,
time,
|_, _, _| FilterResult::<()>::Forward,
);
let serial = SERIAL_COUNTER.next_serial();
keyboard.input(
state,
key_code,
KeyState::Released,
serial,
time,
|_, _, _| FilterResult::<()>::Forward,
);
}
});
}
} else if event.state() == KeyState::Pressed
&& a11y_keyboard_monitor.has_virtual_mod(handle.modified_sym())
if event.state() == KeyState::Released {
let removed = self
.common
.a11y_keyboard_monitor_state
.remove_active_virtual_mod(handle.modified_sym());
// If `Caps_Lock` is a virtual modifier, and is in locked state, clear it
if removed
&& handle.modified_sym() == Keysym::Caps_Lock
&& (modifiers.serialized.locked & 2) != 0
{
a11y_keyboard_monitor.add_active_virtual_mod(handle.modified_sym());
tracing::debug!(
"active virtual mods: {:?}",
a11y_keyboard_monitor.active_virtual_mods()
);
seat.supressed_keys().add(&handle, None);
return FilterResult::Intercept(None);
let seat = seat.clone();
let key_code = event.key_code();
self.common.event_loop_handle.insert_idle(move |state| {
if let Some(keyboard) = seat.get_keyboard() {
let serial = SERIAL_COUNTER.next_serial();
let time = state.common.clock.now().as_millis();
keyboard.input(
state,
key_code,
KeyState::Pressed,
serial,
time,
|_, _, _| FilterResult::<()>::Forward,
);
let serial = SERIAL_COUNTER.next_serial();
keyboard.input(
state,
key_code,
KeyState::Released,
serial,
time,
|_, _, _| FilterResult::<()>::Forward,
);
}
});
}
} else if event.state() == KeyState::Pressed
&& self
.common
.a11y_keyboard_monitor_state
.has_virtual_mod(handle.modified_sym())
{
self.common
.a11y_keyboard_monitor_state
.add_active_virtual_mod(handle.modified_sym());
tracing::debug!(
"active virtual mods: {:?}",
self.common
.a11y_keyboard_monitor_state
.active_virtual_mods()
);
seat.supressed_keys().add(&handle, None);
return FilterResult::Intercept(None);
}
// Skip released events for initially surpressed keys
@ -1918,10 +1860,12 @@ impl State {
return FilterResult::Intercept(None);
}
if let Some(a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor()
&& event.state() == KeyState::Pressed
&& (a11y_keyboard_monitor.has_keyboard_grab()
|| a11y_keyboard_monitor.has_key_grab(modifiers, handle.modified_sym()))
if event.state() == KeyState::Pressed
&& (self.common.a11y_keyboard_monitor_state.has_keyboard_grab()
|| self
.common
.a11y_keyboard_monitor_state
.has_key_grab(modifiers, handle.modified_sym()))
{
let modifiers_queue = seat.modifiers_shortcut_queue();
modifiers_queue.clear();
@ -2173,14 +2117,14 @@ impl State {
}
Stage::StickyPopups(layout) => {
if let Some(element) =
layout.popup_element_under(global_pos.to_local(output), seat)
layout.popup_element_under(global_pos.to_local(output))
{
return ControlFlow::Break(Ok(Some(element)));
}
}
Stage::Sticky(layout) => {
if let Some(element) =
layout.toplevel_element_under(global_pos.to_local(output), seat)
layout.toplevel_element_under(global_pos.to_local(output))
{
return ControlFlow::Break(Ok(Some(element)));
}
@ -2335,7 +2279,7 @@ impl State {
}
Stage::StickyPopups(floating_layer) => {
if let Some(under) = floating_layer
.popup_surface_under(relative_pos, seat)
.popup_surface_under(relative_pos)
.map(|(target, point)| (target, point.to_global(output)))
{
return ControlFlow::Break(Ok(Some(under)));
@ -2343,7 +2287,7 @@ impl State {
}
Stage::Sticky(floating_layer) => {
if let Some(under) = floating_layer
.toplevel_surface_under(relative_pos, seat)
.toplevel_surface_under(relative_pos)
.map(|(target, point)| (target, point.to_global(output)))
{
return ControlFlow::Break(Ok(Some(under)));
@ -2373,113 +2317,8 @@ impl State {
.ok()
.flatten()
}
pub fn apply_cursor_hint(
&mut self,
surface: &WlSurface,
pointer: &PointerHandle<Self>,
mut location: Point<f64, Logical>,
) {
let Some(client) = surface.client() else {
return;
};
location = location.downscale(self.client_compositor_state(&client).client_scale());
let point_and_output = {
let shell = self.common.shell.read();
let found = shell.workspaces.sets.iter().find_map(|(out, set)| {
set.surface_geometry_offset_from_toplevel(surface)
.map(|(geometry, surface_offset)| (out, geometry, surface_offset))
});
if let Some((output, geometry, surface_offset)) = found {
let pos_in_element = location + surface_offset.to_f64();
let window_size = geometry.size.to_f64();
let is_legal = |p: Point<f64, Logical>| {
let in_window =
p.x >= 0.0 && p.y >= 0.0 && p.x < window_size.w && p.y < window_size.h;
if !in_window {
return false;
}
with_pointer_constraint(surface, pointer, |constraint| {
if let Some(constraint) = constraint
&& let Some(region) = constraint.region()
{
let point_in_surface = (p - surface_offset.to_f64()).to_i32_round();
return region.contains(point_in_surface);
}
true
})
};
let workspace_origin = output.geometry().loc.to_f64();
let origin = geometry.loc.to_f64();
if is_legal(pos_in_element) {
let x = workspace_origin.x + origin.x + pos_in_element.x;
let y = workspace_origin.y + origin.y + pos_in_element.y;
Some((Point::new(x, y), output.clone()))
} else {
None
}
} else {
None
}
};
if let Some((point, output)) = point_and_output {
let original_position = pointer.current_location();
pointer.set_location(point);
let mut shell = self.common.shell.write();
shell.update_pointer_position(point.as_global().to_local(&output), &output);
let seat = shell
.seats
.iter()
.find(|s| s.get_pointer().as_ref() == Some(pointer))
.cloned();
if let Some(seat) = seat {
shell.update_focal_point(
&seat,
original_position.as_global(),
self.common.config.cosmic_conf.accessibility_zoom.view_moves,
);
let output_geometry = output.geometry();
for session in cursor_sessions_for_output(&shell, &output) {
if let Some((geometry, offset)) = seat.cursor_geometry(
point.to_buffer(
output.current_scale().fractional_scale(),
output.current_transform(),
&output_geometry.size.to_f64().as_logical(),
),
self.common.clock.now(),
) {
if session
.current_constraints()
.map(|constraint| constraint.size != geometry.size)
.unwrap_or(true)
{
session.update_constraints(BufferConstraints {
size: geometry.size,
shm: vec![ShmFormat::Argb8888],
dma: None,
});
}
session.set_cursor_hotspot(offset);
session.set_cursor_pos(Some(geometry.loc));
}
}
}
}
}
}
// Output and workspace sessions for the given output
fn cursor_sessions_for_output<'a>(
shell: &'a Shell,
output: &'a Output,
@ -2488,9 +2327,16 @@ fn cursor_sessions_for_output<'a>(
.active_space(output)
.into_iter()
.flat_map(|workspace| {
let maybe_fullscreen = workspace.get_fullscreen();
workspace
.cursor_sessions()
.into_iter()
.chain(
maybe_fullscreen
.map(|w| w.cursor_sessions())
.into_iter()
.flatten(),
)
.chain(output.cursor_sessions())
})
}

View file

@ -1,15 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
fn main() {
// libcosmic-yoda enables zbus' `tokio` feature, so zbus 5.14 expects an
// ambient Tokio runtime via Handle::current(). cosmic-comp's loop is not
// async, so hold a runtime guard for the lifetime of run().
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("failed to build tokio runtime");
let _guard = runtime.enter();
if let Err(err) = cosmic_comp::run(Default::default()) {
tracing::error!("Error occured in main(): {}", err);
std::process::exit(1);

View file

@ -254,14 +254,6 @@ impl CosmicMapped {
.any(|(w, _)| w.has_surface(surface, surface_type))
}
pub fn surface_offset(&self, surface: &WlSurface) -> Option<Point<i32, Logical>> {
self.windows().find_map(|(window, window_offset)| {
window
.surface_offset(surface)
.map(|offset| window_offset + offset)
})
}
/// Give the pointer target under a relative offset into this element.
///
/// Returns Target + Offset relative to the target
@ -269,13 +261,10 @@ impl CosmicMapped {
&self,
relative_pos: Point<f64, Logical>,
surface_type: WindowSurfaceType,
seat: &Seat<State>,
) -> Option<(PointerFocusTarget, Point<f64, Logical>)> {
match &self.element {
CosmicMappedInternal::Stack(stack) => stack.focus_under(relative_pos, surface_type),
CosmicMappedInternal::Window(window) => {
window.focus_under(relative_pos, surface_type, Some(seat))
}
CosmicMappedInternal::Window(window) => window.focus_under(relative_pos, surface_type),
_ => unreachable!(),
}
}

View file

@ -61,8 +61,8 @@ use smithay::{
PointerTarget, RelativeMotionEvent,
},
touch::{
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
TouchTarget, UpEvent,
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
UpEvent,
},
},
output::Output,
@ -79,7 +79,7 @@ use std::{
hash::Hash,
sync::{
Arc, LazyLock, Mutex,
atomic::{AtomicBool, AtomicU8, AtomicU32, AtomicUsize, Ordering},
atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering},
},
};
@ -114,7 +114,6 @@ pub struct CosmicStackInternal {
scroll_to_focus: AtomicBool,
previous_keyboard: AtomicUsize,
pointer_entered: AtomicU8,
touch_serial: AtomicU32,
reenter: AtomicBool,
potential_drag: Mutex<Option<usize>>,
override_alive: AtomicBool,
@ -172,7 +171,6 @@ impl CosmicStack {
scroll_to_focus: AtomicBool::new(false),
previous_keyboard: AtomicUsize::new(0),
pointer_entered: AtomicU8::new(0),
touch_serial: AtomicU32::new(0),
reenter: AtomicBool::new(false),
potential_drag: Mutex::new(None),
override_alive: AtomicBool::new(true),
@ -251,10 +249,6 @@ impl CosmicStack {
window.set_tiled(false);
p.active.fetch_min(windows.len() - 1, Ordering::SeqCst);
p.previous_index
.lock()
.unwrap()
.take_if(|(_, idx)| *idx >= windows.len());
});
self.0
.resize(Size::from((self.active().geometry().size.w, TAB_HEIGHT)));
@ -282,10 +276,6 @@ impl CosmicStack {
window.set_tiled(false);
p.active.fetch_min(windows.len() - 1, Ordering::SeqCst);
p.previous_index
.lock()
.unwrap()
.take_if(|(_, idx)| *idx >= windows.len());
Some(window)
});
@ -1817,62 +1807,55 @@ impl PointerTarget<State> for CosmicStack {
}
impl TouchTarget<State> for CosmicStack {
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent) {
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent, seq: Serial) {
let mut event = event.clone();
let active_window_geo = self.0.with_program(|p| {
p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry()
});
event.location -= active_window_geo.loc.to_f64();
self.0
.with_program(|p| p.touch_serial.store(event.serial.into(), Ordering::Release));
TouchTarget::down(&self.0, seat, data, &event)
TouchTarget::down(&self.0, seat, data, &event, seq)
}
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent) {
TouchTarget::up(&self.0, seat, data, event)
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent, seq: Serial) {
TouchTarget::up(&self.0, seat, data, event, seq)
}
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent) {
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent, seq: Serial) {
let mut event = event.clone();
let active_window_geo = self.0.with_program(|p| {
p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry()
});
event.location -= active_window_geo.loc.to_f64();
TouchTarget::motion(&self.0, seat, data, &event);
TouchTarget::motion(&self.0, seat, data, &event, seq);
if event.location.y < 0.0
|| event.location.y > TAB_HEIGHT as f64
|| event.location.x < 64.0
|| event.location.x > (active_window_geo.size.w as f64 - 64.0)
{
self.start_drag(
data,
seat,
self.0
.with_program(|p| p.touch_serial.load(Ordering::Acquire))
.into(),
);
self.start_drag(data, seat, seq);
}
}
fn frame(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
TouchTarget::frame(&self.0, seat, data, frame)
fn frame(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
TouchTarget::frame(&self.0, seat, data, seq)
}
fn cancel(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
TouchTarget::cancel(&self.0, seat, data, frame)
fn cancel(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
TouchTarget::cancel(&self.0, seat, data, seq)
}
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent) {
TouchTarget::shape(&self.0, seat, data, event)
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent, seq: Serial) {
TouchTarget::shape(&self.0, seat, data, event, seq)
}
fn orientation(&self, seat: &Seat<State>, data: &mut State, event: &OrientationEvent) {
TouchTarget::orientation(&self.0, seat, data, event)
}
fn last_frame(&self, seat: &Seat<State>, data: &mut State) -> Option<FrameMarker> {
TouchTarget::last_frame(&self.0, seat, data)
fn orientation(
&self,
_seat: &Seat<State>,
_data: &mut State,
_event: &OrientationEvent,
_seq: Serial,
) {
}
}

View file

@ -45,10 +45,7 @@ use smithay::{
IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, user_data::UserDataMap,
},
wayland::{
compositor::{
SubsurfaceCachedState, SurfaceData, TraversalAction, get_parent, with_states,
with_surface_tree_downward,
},
compositor::{SurfaceData, TraversalAction, with_states, with_surface_tree_downward},
seat::WaylandFocus,
shell::xdg::{
SurfaceCachedState, ToplevelCachedState, ToplevelSurface, XdgToplevelSurfaceData,
@ -238,8 +235,7 @@ impl CosmicSurface {
toplevel.with_pending_state(|state| state.size = Some(geo.size.as_logical()))
}
WindowSurface::X11(surface) => {
let _ =
surface.configure_with_sync(geo.as_logical() + surface.frame_extents(), None);
let _ = surface.configure_with_sync(geo.as_logical(), None);
}
}
}
@ -346,7 +342,7 @@ impl CosmicSurface {
state.is_some_and(|state| state.states.contains(ToplevelState::Resizing))
}))
}
WindowSurface::X11(surface) => surface.pending_configure().map(|_| true),
WindowSurface::X11(surface) => surface.pending_geometry().map(|_| true),
}
}
@ -592,7 +588,7 @@ impl CosmicSurface {
}
})
}
WindowSurface::X11(surface) => surface.pending_configure().is_none(),
WindowSurface::X11(surface) => surface.pending_geometry().is_none(),
}
}
@ -656,54 +652,6 @@ impl CosmicSurface {
}
}
pub fn surface_offset(&self, surface: &WlSurface) -> Option<Point<i32, Logical>> {
match self.0.underlying_surface() {
WindowSurface::Wayland(toplevel) => {
Self::surface_tree_offset(toplevel.wl_surface(), surface)
}
WindowSurface::X11(surface_x11) => {
if surface_x11.wl_surface().as_ref() == Some(surface) {
Some(Point::default())
} else {
None
}
}
}
}
pub fn surface_tree_offset(
root: &WlSurface,
surface: &WlSurface,
) -> Option<Point<i32, Logical>> {
let mut offset = Point::<i32, Logical>::default();
let mut parent = surface.clone();
loop {
if parent == *root {
return Some(offset);
} else if let Some(s) = get_parent(&parent) {
offset += with_states(&parent, |states| {
states
.cached_state
.get::<SubsurfaceCachedState>()
.current()
.location
});
parent = s;
} else {
// `parent` is now root of subsurface tree; `surface` is not a subsurface child of `root`
break;
}
}
for (popup, popup_offset) in PopupManager::popups_for_surface(root) {
if let Some(offset) = Self::surface_tree_offset(popup.wl_surface(), surface) {
return Some(popup_offset + offset);
}
}
None
}
pub fn focus_under(
&self,
relative_pos: Point<f64, Logical>,

View file

@ -47,8 +47,8 @@ use smithay::{
GestureSwipeUpdateEvent, MotionEvent, PointerTarget, RelativeMotionEvent,
},
touch::{
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
TouchTarget, UpEvent,
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
UpEvent,
},
},
output::Output,
@ -57,7 +57,7 @@ use smithay::{
Buffer, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform,
user_data::UserDataMap,
},
wayland::{pointer_constraints::with_pointer_constraint, seat::WaylandFocus},
wayland::seat::WaylandFocus,
};
use std::{
borrow::Cow,
@ -274,25 +274,12 @@ impl CosmicWindow {
&self,
mut relative_pos: Point<f64, Logical>,
surface_type: WindowSurfaceType,
seat: Option<&Seat<State>>,
) -> Option<(PointerFocusTarget, Point<f64, Logical>)> {
let has_constraint = if let Some(seat) = seat
&& let Some(pointer) = seat.get_pointer()
&& let Some(surface) = self.wl_surface()
&& with_pointer_constraint(&surface, &pointer, |constraint| {
constraint.is_some_and(|c| c.is_active())
}) {
true
} else {
false
};
self.0.with_program(|p| {
let mut offset = Point::from((0., 0.));
let mut window_ui = None;
let has_ssd = p.has_ssd(false);
if (!has_constraint && (has_ssd || p.has_tiled_state()))
if (has_ssd || p.has_tiled_state())
&& surface_type.contains(WindowSurfaceType::TOPLEVEL)
{
let geo = p.window.geometry();
@ -890,8 +877,7 @@ impl SpaceElement for CosmicWindow {
})
}
fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
self.focus_under(*point, WindowSurfaceType::ALL, None)
.is_some()
self.focus_under(*point, WindowSurfaceType::ALL).is_some()
}
fn set_activate(&self, activated: bool) {
if self
@ -1190,42 +1176,43 @@ impl PointerTarget<State> for CosmicWindow {
}
impl TouchTarget<State> for CosmicWindow {
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent) {
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent, seq: Serial) {
let mut event = event.clone();
self.0.with_program(|p| {
event.location -= p.window.geometry().loc.to_f64();
});
TouchTarget::down(&self.0, seat, data, &event)
TouchTarget::down(&self.0, seat, data, &event, seq)
}
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent) {
TouchTarget::up(&self.0, seat, data, event)
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent, seq: Serial) {
TouchTarget::up(&self.0, seat, data, event, seq)
}
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent) {
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent, seq: Serial) {
let mut event = event.clone();
event.location -= self.0.with_program(|p| p.window.geometry().loc.to_f64());
TouchTarget::motion(&self.0, seat, data, &event)
TouchTarget::motion(&self.0, seat, data, &event, seq)
}
fn frame(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
TouchTarget::frame(&self.0, seat, data, frame)
fn frame(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
TouchTarget::frame(&self.0, seat, data, seq)
}
fn cancel(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
TouchTarget::cancel(&self.0, seat, data, frame)
fn cancel(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
TouchTarget::cancel(&self.0, seat, data, seq)
}
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent) {
TouchTarget::shape(&self.0, seat, data, event)
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent, seq: Serial) {
TouchTarget::shape(&self.0, seat, data, event, seq)
}
fn orientation(&self, seat: &Seat<State>, data: &mut State, event: &OrientationEvent) {
TouchTarget::orientation(&self.0, seat, data, event)
}
fn last_frame(&self, seat: &Seat<State>, data: &mut State) -> Option<FrameMarker> {
TouchTarget::last_frame(&self.0, seat, data)
fn orientation(
&self,
_seat: &Seat<State>,
_data: &mut State,
_event: &OrientationEvent,
_seq: Serial,
) {
}
}

View file

@ -1,6 +1,6 @@
use crate::{
shell::{CosmicSurface, MinimizedWindow, Shell, Trigger, element::CosmicMapped},
state::{Common, State},
state::Common,
utils::prelude::*,
wayland::handlers::{xdg_shell::PopupGrabData, xwayland_keyboard_grab::XWaylandGrabSeatData},
};
@ -12,7 +12,6 @@ use smithay::{
reexports::wayland_server::{Resource, protocol::wl_surface::WlSurface},
utils::{IsAlive, Point, SERIAL_COUNTER, Serial},
wayland::{
pointer_constraints::with_pointer_constraint,
seat::WaylandFocus,
selection::{data_device::set_data_device_focus, primary_selection::set_primary_focus},
shell::wlr_layer::{KeyboardInteractivity, Layer},
@ -288,18 +287,22 @@ impl Shell {
}
let workspace = &mut set.workspaces[set.active];
for fs in workspace.get_fullscreen_surfaces() {
let is_focused = self.seats.iter().any(|seat| {
if let Some(fullscreen) = workspace.get_fullscreen() {
if self.seats.iter().any(|seat| {
if let Some(KeyboardFocusTarget::Fullscreen(s)) =
seat.get_keyboard().unwrap().current_focus()
{
s == fs.surface
&s == fullscreen
} else {
false
}
});
fs.surface.set_activated(is_focused);
fs.surface.send_configure();
}) {
fullscreen.set_activated(true);
fullscreen.send_configure();
} else {
fullscreen.set_activated(false);
fullscreen.send_configure();
}
}
for focused in focused_windows.iter() {
raise_with_children(&mut workspace.floating_layer, focused);
@ -344,20 +347,6 @@ fn update_focus_state(
) {
// update keyboard focus
if let Some(keyboard) = seat.get_keyboard() {
// remove constraint when target changed
let old_focus = keyboard.current_focus();
if let Some(old_target) = old_focus
&& target != Some(&old_target)
&& let Some(surface) = old_target.wl_surface()
&& let Some(pointer) = seat.get_pointer()
{
with_pointer_constraint(&surface, &pointer, |constraint| {
if let Some(constraint) = constraint {
constraint.deactivate();
}
});
}
if should_update_cursor
&& state.common.config.cosmic_conf.cursor_follows_focus
&& target.is_some()
@ -657,9 +646,7 @@ fn focus_target_is_valid(
.has_node(&node),
KeyboardFocusTarget::Fullscreen(window) => {
let workspace = shell.active_space(output).unwrap();
workspace
.get_fullscreen_surfaces()
.any(|f| f.surface == window)
workspace.get_fullscreen().is_some_and(|w| w == &window)
}
KeyboardFocusTarget::Popup(_) => true,
KeyboardFocusTarget::LockSurface(_) => false,
@ -712,9 +699,9 @@ fn update_focus_target(
.map(KeyboardFocusTarget::Element)
.or_else(|| {
workspace
.get_fullscreen(seat)
.get_fullscreen()
.cloned()
.map(|fs| KeyboardFocusTarget::Fullscreen(fs.surface))
.map(KeyboardFocusTarget::Fullscreen)
})
})
}

View file

@ -112,8 +112,8 @@ fn render_input_order_internal<R: 'static>(
let output_size = output.geometry().size;
// this is more hacky than I would like..
let fullscreen = workspace.fullscreen.as_ref().filter(|f| !f.is_animating());
let seat = shell.seats.last_active();
let fullscreen = workspace.get_fullscreen(seat);
let is_active_workspace = seat.focused_output().is_some_and(|output| {
shell
.active_space(&output)
@ -144,7 +144,7 @@ fn render_input_order_internal<R: 'static>(
let Some(workspace) = shell.workspaces.space_for_handle(previous) else {
return ControlFlow::Break(Err(OutputNoMode));
};
let has_fullscreen = workspace.get_fullscreen(seat).is_some();
let has_fullscreen = workspace.fullscreen.is_some();
let (forward, percentage) = match start {
WorkspaceDelta::Shortcut(st) => (
@ -220,18 +220,20 @@ fn render_input_order_internal<R: 'static>(
.rev()
.filter(|or| {
(*or)
.last_configure()
.geometry()
.as_global()
.intersection(output.geometry())
.is_some()
})
.map(|or| (or, or.last_configure().loc.as_global()))
.map(|or| (or, or.geometry().loc.as_global()))
{
callback(Stage::OverrideRedirect { surface, location })?;
}
// sticky window popups
callback(Stage::StickyPopups(&set.sticky_layer))?;
if !has_focused_fullscreen {
callback(Stage::StickyPopups(&set.sticky_layer))?;
}
}
if element_filter != ElementFilter::LayerShellOnly {
@ -311,11 +313,11 @@ fn render_input_order_internal<R: 'static>(
for (layer, location) in layer_surfaces(output, Layer::Top, element_filter) {
callback(Stage::LayerSurface { layer, location })?;
}
}
// sticky windows
if element_filter != ElementFilter::LayerShellOnly {
callback(Stage::Sticky(&set.sticky_layer))?;
// sticky windows
if element_filter != ElementFilter::LayerShellOnly {
callback(Stage::Sticky(&set.sticky_layer))?;
}
}
if element_filter != ElementFilter::LayerShellOnly {

View file

@ -29,18 +29,15 @@ use smithay::{
MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent,
},
touch::{
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
TouchTarget, UpEvent,
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
UpEvent,
},
},
reexports::wayland_server::{
Client, DisplayHandle, Resource, backend::ObjectId, protocol::wl_surface::WlSurface,
},
utils::{IsAlive, Logical, Point, Serial, Transform},
wayland::{
compositor::with_states, seat::WaylandFocus, selection::data_device::WlOfferData,
session_lock::LockSurface, shell::xdg::SurfaceCachedState,
},
wayland::{seat::WaylandFocus, selection::data_device::WlOfferData, session_lock::LockSurface},
xwayland::{
X11Surface,
xwm::{XwmId, XwmOfferData},
@ -228,63 +225,6 @@ impl PointerFocusTarget {
_ => false,
}
}
// Update image copy cursor position/hotspot for enter/motion event
fn update_image_copy_cursor_position(
&self,
seat: &Seat<State>,
data: &mut State,
event: &PointerMotionEvent,
) {
let Some(toplevel) = self.toplevel(&data.common.shell.read()) else {
return;
};
let cursor_sessions = toplevel.cursor_sessions();
if cursor_sessions.is_empty() {
return;
}
let cursor_pos = if let Some(wl_surface) = self.wl_surface() {
let surface_offset = toplevel
.surface_offset(&wl_surface)
.unwrap_or(Point::from((0, 0)))
.to_f64();
let geometry_loc = toplevel
.wl_surface()
.and_then(|s| {
with_states(&s, |states| {
states
.cached_state
.get::<SurfaceCachedState>()
.current()
.geometry
.map(|g| g.loc.to_f64())
})
})
.unwrap_or_default();
Some(
(event.location - geometry_loc + surface_offset)
.to_buffer(1.0, Transform::Normal, &toplevel.geometry().size.to_f64())
.to_i32_round(),
)
} else {
// If cursor is in SSD, instead of a `wl_surface`, it is outside the captured bounds
None
};
let cursor_hotspot = if let Some((_, hotspot)) =
seat.cursor_geometry((0.0, 0.0), Duration::from_millis(event.time as u64).into())
{
hotspot
} else {
Point::from((0, 0))
};
for session in cursor_sessions {
session.set_cursor_pos(cursor_pos);
session.set_cursor_hotspot(cursor_hotspot);
}
}
}
impl KeyboardFocusTarget {
@ -341,18 +281,6 @@ impl KeyboardFocusTarget {
false
}
}
pub fn has_surface(&self, shell: &Shell, surface: &WlSurface) -> bool {
if let Some(fe) = shell.focused_element(self) {
fe.has_surface(surface, WindowSurfaceType::ALL)
} else if let KeyboardFocusTarget::Fullscreen(s) = self {
s.has_surface(surface, WindowSurfaceType::ALL)
} else if let Some(root) = WaylandFocus::wl_surface(self) {
CosmicSurface::surface_tree_offset(&root, surface).is_some()
} else {
false
}
}
}
#[derive(Debug, Clone)]
@ -397,11 +325,47 @@ impl IsAlive for KeyboardFocusTarget {
impl PointerTarget<State> for PointerFocusTarget {
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &PointerMotionEvent) {
self.update_image_copy_cursor_position(seat, data, event);
let toplevel = self.toplevel(&data.common.shell.read());
if let Some(element) = toplevel {
for session in element.cursor_sessions() {
session.set_cursor_pos(Some(
event
.location
.to_buffer(1.0, Transform::Normal, &element.geometry().size.to_f64())
.to_i32_round(),
));
if let Some((_, hotspot)) = seat
.cursor_geometry((0.0, 0.0), Duration::from_millis(event.time as u64).into())
{
session.set_cursor_hotspot(hotspot);
} else {
session.set_cursor_hotspot((0, 0));
}
}
}
self.inner_pointer_target().enter(seat, data, event);
}
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &PointerMotionEvent) {
self.update_image_copy_cursor_position(seat, data, event);
let toplevel = self.toplevel(&data.common.shell.read());
if let Some(element) = toplevel {
for session in element.cursor_sessions() {
session.set_cursor_pos(Some(
event
.location
.to_buffer(1.0, Transform::Normal, &element.geometry().size.to_f64())
.to_i32_round(),
));
if let Some((_, hotspot)) = seat
.cursor_geometry((0.0, 0.0), Duration::from_millis(event.time as u64).into())
{
session.set_cursor_hotspot(hotspot);
} else {
session.set_cursor_hotspot((0, 0));
}
}
}
self.inner_pointer_target().motion(seat, data, event);
}
fn relative_motion(&self, seat: &Seat<State>, data: &mut State, event: &RelativeMotionEvent) {
@ -498,36 +462,39 @@ impl PointerTarget<State> for PointerFocusTarget {
}
impl TouchTarget<State> for PointerFocusTarget {
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent) {
self.inner_touch_target().down(seat, data, event);
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent, seq: Serial) {
self.inner_touch_target().down(seat, data, event, seq);
}
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent) {
self.inner_touch_target().up(seat, data, event);
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent, seq: Serial) {
self.inner_touch_target().up(seat, data, event, seq);
}
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent) {
self.inner_touch_target().motion(seat, data, event);
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent, seq: Serial) {
self.inner_touch_target().motion(seat, data, event, seq);
}
fn frame(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
self.inner_touch_target().frame(seat, data, frame);
fn frame(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
self.inner_touch_target().frame(seat, data, seq);
}
fn cancel(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
self.inner_touch_target().cancel(seat, data, frame);
fn cancel(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
self.inner_touch_target().cancel(seat, data, seq);
}
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent) {
self.inner_touch_target().shape(seat, data, event);
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent, seq: Serial) {
self.inner_touch_target().shape(seat, data, event, seq);
}
fn orientation(&self, seat: &Seat<State>, data: &mut State, event: &OrientationEvent) {
self.inner_touch_target().orientation(seat, data, event);
}
fn last_frame(&self, seat: &Seat<State>, data: &mut State) -> Option<FrameMarker> {
self.inner_touch_target().last_frame(seat, data)
fn orientation(
&self,
seat: &Seat<State>,
data: &mut State,
event: &OrientationEvent,
seq: Serial,
) {
self.inner_touch_target()
.orientation(seat, data, event, seq);
}
}

View file

@ -202,12 +202,19 @@ impl<G: TouchGrab<State>> TouchGrab<State> for DelayGrab<G> {
handle: &mut TouchInnerHandle<'_, State>,
focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
event: &DownEvent,
seq: Serial,
) {
handle.down(data, focus, event);
handle.down(data, focus, event, seq);
}
fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) {
handle.up(data, event);
fn up(
&mut self,
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &UpEvent,
seq: Serial,
) {
handle.up(data, event, seq);
if event.slot == TouchGrab::start_data(self).slot {
handle.unset_grab(self, data);
@ -220,8 +227,9 @@ impl<G: TouchGrab<State>> TouchGrab<State> for DelayGrab<G> {
handle: &mut TouchInnerHandle<'_, State>,
focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
event: &TouchMotionEvent,
seq: Serial,
) {
handle.motion(data, focus, event);
handle.motion(data, focus, event, seq);
let distance = self.start_data.distance(event.location);
if distance >= 1.
@ -237,12 +245,12 @@ impl<G: TouchGrab<State>> TouchGrab<State> for DelayGrab<G> {
}
}
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
handle.frame(data)
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
handle.frame(data, seq)
}
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
handle.cancel(data);
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
handle.cancel(data, seq);
handle.unset_grab(self, data);
}
@ -251,8 +259,9 @@ impl<G: TouchGrab<State>> TouchGrab<State> for DelayGrab<G> {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &ShapeEvent,
seq: Serial,
) {
handle.shape(data, event)
handle.shape(data, event, seq)
}
fn orientation(
@ -260,8 +269,9 @@ impl<G: TouchGrab<State>> TouchGrab<State> for DelayGrab<G> {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &OrientationEvent,
seq: Serial,
) {
handle.orientation(data, event);
handle.orientation(data, event, seq);
}
fn start_data(&self) -> &TouchGrabStartData<State> {

View file

@ -720,6 +720,7 @@ impl TouchGrab<State> for MenuGrab {
handle: &mut TouchInnerHandle<'_, State>,
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &DownEvent,
seq: Serial,
) {
{
let mut guard = self.elements.lock().unwrap();
@ -754,15 +755,21 @@ impl TouchGrab<State> for MenuGrab {
time: event.time,
};
if element.touch_entered.is_none() {
TouchTarget::down(&element.iced, &self.seat, data, &new_event);
TouchTarget::down(&element.iced, &self.seat, data, &new_event, seq);
element.touch_entered = Some(event.slot);
}
}
}
handle.down(data, None, event);
handle.down(data, None, event, seq);
}
fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) {
fn up(
&mut self,
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &UpEvent,
seq: Serial,
) {
{
let elements = self.elements.lock().unwrap();
for element in elements.iter().filter(|elem| {
@ -770,7 +777,7 @@ impl TouchGrab<State> for MenuGrab {
.as_ref()
.is_some_and(|slot| *slot == event.slot)
}) {
TouchTarget::up(&element.iced, &self.seat, data, event);
TouchTarget::up(&element.iced, &self.seat, data, event, seq);
}
}
handle.unset_grab(self, data);
@ -782,6 +789,7 @@ impl TouchGrab<State> for MenuGrab {
handle: &mut TouchInnerHandle<'_, State>,
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &TouchMotionEvent,
seq: Serial,
) {
{
let elements = self.elements.lock().unwrap();
@ -790,24 +798,24 @@ impl TouchGrab<State> for MenuGrab {
.as_ref()
.is_some_and(|slot| *slot == event.slot)
}) {
TouchTarget::motion(&element.iced, &self.seat, data, event);
TouchTarget::motion(&element.iced, &self.seat, data, event, seq);
}
}
handle.motion(data, None, event);
handle.motion(data, None, event, seq);
}
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
handle.frame(data);
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
handle.frame(data, seq);
}
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
{
let mut elements = self.elements.lock().unwrap();
for element in elements.iter_mut() {
let _ = element.touch_entered.take();
}
}
handle.cancel(data);
handle.cancel(data, seq);
}
fn shape(
@ -815,8 +823,9 @@ impl TouchGrab<State> for MenuGrab {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &smithay::input::touch::ShapeEvent,
seq: Serial,
) {
handle.shape(data, event);
handle.shape(data, event, seq);
}
fn orientation(
@ -824,8 +833,9 @@ impl TouchGrab<State> for MenuGrab {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &smithay::input::touch::OrientationEvent,
seq: Serial,
) {
handle.orientation(data, event);
handle.orientation(data, event, seq);
}
fn start_data(&self) -> &TouchGrabStartData<State> {

View file

@ -387,17 +387,24 @@ impl TouchGrab<State> for ResizeGrab {
handle: &mut TouchInnerHandle<'_, State>,
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &DownEvent,
seq: Serial,
) {
match self {
ResizeGrab::Floating(grab) => TouchGrab::down(grab, data, handle, focus, event),
ResizeGrab::Tiling(grab) => TouchGrab::down(grab, data, handle, focus, event),
ResizeGrab::Floating(grab) => TouchGrab::down(grab, data, handle, focus, event, seq),
ResizeGrab::Tiling(grab) => TouchGrab::down(grab, data, handle, focus, event, seq),
}
}
fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) {
fn up(
&mut self,
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &UpEvent,
seq: Serial,
) {
match self {
ResizeGrab::Floating(grab) => TouchGrab::up(grab, data, handle, event),
ResizeGrab::Tiling(grab) => TouchGrab::up(grab, data, handle, event),
ResizeGrab::Floating(grab) => TouchGrab::up(grab, data, handle, event, seq),
ResizeGrab::Tiling(grab) => TouchGrab::up(grab, data, handle, event, seq),
}
}
@ -407,24 +414,25 @@ impl TouchGrab<State> for ResizeGrab {
handle: &mut TouchInnerHandle<'_, State>,
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &TouchMotionEvent,
seq: Serial,
) {
match self {
ResizeGrab::Floating(grab) => TouchGrab::motion(grab, data, handle, focus, event),
ResizeGrab::Tiling(grab) => TouchGrab::motion(grab, data, handle, focus, event),
ResizeGrab::Floating(grab) => TouchGrab::motion(grab, data, handle, focus, event, seq),
ResizeGrab::Tiling(grab) => TouchGrab::motion(grab, data, handle, focus, event, seq),
}
}
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
match self {
ResizeGrab::Floating(grab) => TouchGrab::frame(grab, data, handle),
ResizeGrab::Tiling(grab) => TouchGrab::frame(grab, data, handle),
ResizeGrab::Floating(grab) => TouchGrab::frame(grab, data, handle, seq),
ResizeGrab::Tiling(grab) => TouchGrab::frame(grab, data, handle, seq),
}
}
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
match self {
ResizeGrab::Floating(grab) => TouchGrab::cancel(grab, data, handle),
ResizeGrab::Tiling(grab) => TouchGrab::cancel(grab, data, handle),
ResizeGrab::Floating(grab) => TouchGrab::cancel(grab, data, handle, seq),
ResizeGrab::Tiling(grab) => TouchGrab::cancel(grab, data, handle, seq),
}
}
@ -433,10 +441,11 @@ impl TouchGrab<State> for ResizeGrab {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &ShapeEvent,
seq: Serial,
) {
match self {
ResizeGrab::Floating(grab) => TouchGrab::shape(grab, data, handle, event),
ResizeGrab::Tiling(grab) => TouchGrab::shape(grab, data, handle, event),
ResizeGrab::Floating(grab) => TouchGrab::shape(grab, data, handle, event, seq),
ResizeGrab::Tiling(grab) => TouchGrab::shape(grab, data, handle, event, seq),
}
}
@ -445,10 +454,11 @@ impl TouchGrab<State> for ResizeGrab {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &OrientationEvent,
seq: Serial,
) {
match self {
ResizeGrab::Floating(grab) => TouchGrab::orientation(grab, data, handle, event),
ResizeGrab::Tiling(grab) => TouchGrab::orientation(grab, data, handle, event),
ResizeGrab::Floating(grab) => TouchGrab::orientation(grab, data, handle, event, seq),
ResizeGrab::Tiling(grab) => TouchGrab::orientation(grab, data, handle, event, seq),
}
}
@ -719,17 +729,24 @@ impl TouchGrab<State> for MoveGrab {
handle: &mut TouchInnerHandle<'_, State>,
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &DownEvent,
seq: Serial,
) {
match self {
MoveGrab::Move(grab) => TouchGrab::down(grab, data, handle, focus, event),
MoveGrab::Delayed(grab) => TouchGrab::down(grab, data, handle, focus, event),
MoveGrab::Move(grab) => TouchGrab::down(grab, data, handle, focus, event, seq),
MoveGrab::Delayed(grab) => TouchGrab::down(grab, data, handle, focus, event, seq),
}
}
fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) {
fn up(
&mut self,
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &UpEvent,
seq: Serial,
) {
match self {
MoveGrab::Move(grab) => TouchGrab::up(grab, data, handle, event),
MoveGrab::Delayed(grab) => TouchGrab::up(grab, data, handle, event),
MoveGrab::Move(grab) => TouchGrab::up(grab, data, handle, event, seq),
MoveGrab::Delayed(grab) => TouchGrab::up(grab, data, handle, event, seq),
}
}
@ -739,24 +756,25 @@ impl TouchGrab<State> for MoveGrab {
handle: &mut TouchInnerHandle<'_, State>,
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &TouchMotionEvent,
seq: Serial,
) {
match self {
MoveGrab::Move(grab) => TouchGrab::motion(grab, data, handle, focus, event),
MoveGrab::Delayed(grab) => TouchGrab::motion(grab, data, handle, focus, event),
MoveGrab::Move(grab) => TouchGrab::motion(grab, data, handle, focus, event, seq),
MoveGrab::Delayed(grab) => TouchGrab::motion(grab, data, handle, focus, event, seq),
}
}
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
match self {
MoveGrab::Move(grab) => TouchGrab::frame(grab, data, handle),
MoveGrab::Delayed(grab) => TouchGrab::frame(grab, data, handle),
MoveGrab::Move(grab) => TouchGrab::frame(grab, data, handle, seq),
MoveGrab::Delayed(grab) => TouchGrab::frame(grab, data, handle, seq),
}
}
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
match self {
MoveGrab::Move(grab) => TouchGrab::cancel(grab, data, handle),
MoveGrab::Delayed(grab) => TouchGrab::cancel(grab, data, handle),
MoveGrab::Move(grab) => TouchGrab::cancel(grab, data, handle, seq),
MoveGrab::Delayed(grab) => TouchGrab::cancel(grab, data, handle, seq),
}
}
@ -765,10 +783,11 @@ impl TouchGrab<State> for MoveGrab {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &ShapeEvent,
seq: Serial,
) {
match self {
MoveGrab::Move(grab) => TouchGrab::shape(grab, data, handle, event),
MoveGrab::Delayed(grab) => TouchGrab::shape(grab, data, handle, event),
MoveGrab::Move(grab) => TouchGrab::shape(grab, data, handle, event, seq),
MoveGrab::Delayed(grab) => TouchGrab::shape(grab, data, handle, event, seq),
}
}
@ -777,10 +796,11 @@ impl TouchGrab<State> for MoveGrab {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &OrientationEvent,
seq: Serial,
) {
match self {
MoveGrab::Move(grab) => TouchGrab::orientation(grab, data, handle, event),
MoveGrab::Delayed(grab) => TouchGrab::orientation(grab, data, handle, event),
MoveGrab::Move(grab) => TouchGrab::orientation(grab, data, handle, event, seq),
MoveGrab::Delayed(grab) => TouchGrab::orientation(grab, data, handle, event, seq),
}
}

View file

@ -40,7 +40,7 @@ use smithay::{
touch::{self, GrabStartData as TouchGrabStartData, TouchGrab, TouchInnerHandle},
},
output::Output,
utils::{IsAlive, Logical, Point, Rectangle, SERIAL_COUNTER, Scale},
utils::{IsAlive, Logical, Point, Rectangle, SERIAL_COUNTER, Scale, Serial},
};
use std::{
collections::HashSet,
@ -652,8 +652,9 @@ impl TouchGrab<State> for MoveGrab {
handle: &mut TouchInnerHandle<'_, State>,
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &touch::DownEvent,
seq: Serial,
) {
handle.down(data, None, event)
handle.down(data, None, event, seq)
}
fn up(
@ -661,12 +662,13 @@ impl TouchGrab<State> for MoveGrab {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &touch::UpEvent,
seq: Serial,
) {
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
handle.unset_grab(self, data);
}
handle.up(data, event);
handle.up(data, event, seq);
}
fn motion(
@ -675,19 +677,20 @@ impl TouchGrab<State> for MoveGrab {
handle: &mut TouchInnerHandle<'_, State>,
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &touch::MotionEvent,
seq: Serial,
) {
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
self.update_location(data, event.location);
}
handle.motion(data, None, event);
handle.motion(data, None, event, seq);
}
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
handle.frame(data)
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
handle.frame(data, seq)
}
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _seq: Serial) {
handle.unset_grab(self, data);
}
@ -696,8 +699,9 @@ impl TouchGrab<State> for MoveGrab {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &touch::ShapeEvent,
seq: Serial,
) {
handle.shape(data, event)
handle.shape(data, event, seq)
}
fn orientation(
@ -705,8 +709,9 @@ impl TouchGrab<State> for MoveGrab {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &touch::OrientationEvent,
seq: Serial,
) {
handle.orientation(data, event)
handle.orientation(data, event, seq)
}
fn start_data(&self) -> &TouchGrabStartData<State> {
@ -948,7 +953,6 @@ impl Drop for MoveGrab {
if let Some((target, offset)) = mapped.focus_under(
current_location - position.as_logical().to_f64(),
WindowSurfaceType::ALL,
&seat,
) {
pointer.motion(
state,

View file

@ -29,7 +29,7 @@ use smithay::{
},
},
output::Output,
utils::{IsAlive, Logical, Point, Rectangle, Size},
utils::{IsAlive, Logical, Point, Rectangle, Serial, Size},
};
use tracing::debug;
@ -330,16 +330,23 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
handle: &mut TouchInnerHandle<'_, State>,
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &DownEvent,
seq: Serial,
) {
handle.down(data, None, event)
handle.down(data, None, event, seq)
}
fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) {
fn up(
&mut self,
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &UpEvent,
seq: Serial,
) {
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
handle.unset_grab(self, data);
}
handle.up(data, event);
handle.up(data, event, seq);
}
fn motion(
@ -348,6 +355,7 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
handle: &mut TouchInnerHandle<'_, State>,
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &TouchMotionEvent,
seq: Serial,
) {
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot
&& self.update_location(event.location.as_global())
@ -355,14 +363,14 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
handle.unset_grab(self, data);
}
handle.motion(data, None, event);
handle.motion(data, None, event, seq);
}
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
handle.frame(data)
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
handle.frame(data, seq)
}
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _seq: Serial) {
handle.unset_grab(self, data);
}
@ -371,8 +379,9 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &ShapeEvent,
seq: Serial,
) {
handle.shape(data, event)
handle.shape(data, event, seq)
}
fn orientation(
@ -380,8 +389,9 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &OrientationEvent,
seq: Serial,
) {
handle.orientation(data, event)
handle.orientation(data, event, seq)
}
fn start_data(&self) -> &TouchGrabStartData<State> {
@ -533,7 +543,7 @@ impl ResizeSurfaceGrab {
);
}
WindowSurface::X11(surface) => {
let mut geometry = surface.last_configure();
let mut geometry = surface.geometry();
geometry.loc += (location - new_location).as_logical();
let _ = surface.configure(geometry);
}

View file

@ -295,7 +295,6 @@ impl FloatingLayout {
for mapped in self
.space
.elements()
.filter(|w| w.alive())
.cloned()
.collect::<Vec<_>>()
.into_iter()
@ -310,10 +309,7 @@ impl FloatingLayout {
None,
);
} else {
let Some(geometry) = self.space.element_geometry(&mapped) else {
continue;
};
let geometry = geometry.to_f64();
let geometry = self.space.element_geometry(&mapped).unwrap().to_f64();
let new_loc = (
((geometry.loc.x - old_output_geometry.loc.x).max(0.)
/ old_output_geometry.size.w
@ -623,10 +619,6 @@ impl FloatingLayout {
from: Rectangle<i32, Local>,
position: Point<i32, Local>,
) {
if !mapped.alive() {
return;
}
let output = self.space.outputs().next().unwrap().clone();
let layers = layer_map_for_output(&output);
let geometry = layers.non_exclusive_zone().as_local();
@ -739,11 +731,7 @@ impl FloatingLayout {
self.space.element_geometry(elem).map(RectExt::as_local)
}
pub fn popup_element_under(
&self,
location: Point<f64, Local>,
seat: &Seat<State>,
) -> Option<KeyboardFocusTarget> {
pub fn popup_element_under(&self, location: Point<f64, Local>) -> Option<KeyboardFocusTarget> {
self.space
.elements()
.rev()
@ -764,7 +752,6 @@ impl FloatingLayout {
if e.focus_under(
point.as_logical(),
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
seat,
)
.is_some()
{
@ -778,7 +765,6 @@ impl FloatingLayout {
pub fn toplevel_element_under(
&self,
location: Point<f64, Local>,
seat: &Seat<State>,
) -> Option<KeyboardFocusTarget> {
self.space
.elements()
@ -800,7 +786,6 @@ impl FloatingLayout {
if e.focus_under(
point.as_logical(),
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
seat,
)
.is_some()
{
@ -814,7 +799,6 @@ impl FloatingLayout {
pub fn popup_surface_under(
&self,
location: Point<f64, Local>,
seat: &Seat<State>,
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
self.space
.elements()
@ -836,7 +820,6 @@ impl FloatingLayout {
e.focus_under(
point.as_logical(),
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
seat,
)
.map(|(surface, surface_offset)| {
(surface, render_location + surface_offset.as_local())
@ -847,7 +830,6 @@ impl FloatingLayout {
pub fn toplevel_surface_under(
&self,
location: Point<f64, Local>,
seat: &Seat<State>,
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
self.space
.elements()
@ -869,7 +851,6 @@ impl FloatingLayout {
e.focus_under(
point.as_logical(),
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
seat,
)
.map(|(surface, surface_offset)| {
(surface, render_location + surface_offset.as_local())

View file

@ -32,7 +32,7 @@ pub fn is_dialog(window: &CosmicSurface) -> bool {
}
WindowSurface::X11(surface) => {
if surface.is_override_redirect()
|| surface.is_modal()
|| surface.is_popup()
|| !matches!(
surface.window_type(),
None | Some(WmWindowType::Normal) | Some(WmWindowType::Utility)

View file

@ -22,13 +22,12 @@ use smithay::{
PointerTarget, RelativeMotionEvent,
},
touch::{
DownEvent, FrameMarker, GrabStartData as TouchGrabStartData,
MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchGrab,
TouchInnerHandle, TouchTarget, UpEvent,
DownEvent, GrabStartData as TouchGrabStartData, MotionEvent as TouchMotionEvent,
OrientationEvent, ShapeEvent, TouchGrab, TouchInnerHandle, TouchTarget, UpEvent,
},
},
output::WeakOutput,
utils::{IsAlive, Logical, Point},
utils::{IsAlive, Logical, Point, Serial},
};
use super::super::{Data, TilingLayout};
@ -127,7 +126,7 @@ impl PointerTarget<State> for ResizeForkTarget {
}
impl TouchTarget<State> for ResizeForkTarget {
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent) {
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent, _seq: Serial) {
let seat = seat.clone();
let node = self.node.clone();
let output = self.output.clone();
@ -158,14 +157,25 @@ impl TouchTarget<State> for ResizeForkTarget {
});
}
fn up(&self, _seat: &Seat<State>, _data: &mut State, _event: &UpEvent) {}
fn motion(&self, _seat: &Seat<State>, _data: &mut State, _event: &TouchMotionEvent) {}
fn frame(&self, _seat: &Seat<State>, _data: &mut State, _frame: FrameMarker) {}
fn cancel(&self, _seat: &Seat<State>, _data: &mut State, _frame: FrameMarker) {}
fn shape(&self, _seat: &Seat<State>, _data: &mut State, _event: &ShapeEvent) {}
fn orientation(&self, _seat: &Seat<State>, _data: &mut State, _event: &OrientationEvent) {}
fn last_frame(&self, _seat: &Seat<State>, _data: &mut State) -> Option<FrameMarker> {
None
fn up(&self, _seat: &Seat<State>, _data: &mut State, _event: &UpEvent, _seq: Serial) {}
fn motion(
&self,
_seat: &Seat<State>,
_data: &mut State,
_event: &TouchMotionEvent,
_seq: Serial,
) {
}
fn frame(&self, _seat: &Seat<State>, _data: &mut State, _seq: Serial) {}
fn cancel(&self, _seat: &Seat<State>, _data: &mut State, _seq: Serial) {}
fn shape(&self, _seat: &Seat<State>, _data: &mut State, _event: &ShapeEvent, _seq: Serial) {}
fn orientation(
&self,
_seat: &Seat<State>,
_data: &mut State,
_event: &OrientationEvent,
_seq: Serial,
) {
}
}
@ -498,16 +508,23 @@ impl TouchGrab<State> for ResizeForkGrab {
handle: &mut TouchInnerHandle<'_, State>,
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &DownEvent,
seq: Serial,
) {
handle.down(data, None, event)
handle.down(data, None, event, seq)
}
fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) {
fn up(
&mut self,
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &UpEvent,
seq: Serial,
) {
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
handle.unset_grab(self, data);
}
handle.up(data, event);
handle.up(data, event, seq);
}
fn motion(
@ -516,6 +533,7 @@ impl TouchGrab<State> for ResizeForkGrab {
handle: &mut TouchInnerHandle<'_, State>,
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &TouchMotionEvent,
seq: Serial,
) {
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot
&& self.update_location(data, event.location, false)
@ -523,14 +541,14 @@ impl TouchGrab<State> for ResizeForkGrab {
handle.unset_grab(self, data);
}
handle.motion(data, None, event);
handle.motion(data, None, event, seq);
}
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
handle.frame(data)
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
handle.frame(data, seq)
}
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _seq: Serial) {
handle.unset_grab(self, data);
}
@ -539,8 +557,9 @@ impl TouchGrab<State> for ResizeForkGrab {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &ShapeEvent,
seq: Serial,
) {
handle.shape(data, event)
handle.shape(data, event, seq)
}
fn start_data(&self) -> &TouchGrabStartData<State> {
@ -555,8 +574,9 @@ impl TouchGrab<State> for ResizeForkGrab {
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &OrientationEvent,
seq: Serial,
) {
handle.orientation(data, event)
handle.orientation(data, event, seq)
}
fn unset(&mut self, data: &mut State) {

View file

@ -3145,7 +3145,6 @@ impl TilingLayout {
pub fn popup_element_under(
&self,
location_f64: Point<f64, Local>,
seat: &Seat<State>,
) -> Option<KeyboardFocusTarget> {
let location = location_f64.to_i32_round();
@ -3158,7 +3157,6 @@ impl TilingLayout {
.focus_under(
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
seat,
)
.is_some()
{
@ -3172,7 +3170,6 @@ impl TilingLayout {
pub fn toplevel_element_under(
&self,
location_f64: Point<f64, Local>,
seat: &Seat<State>,
) -> Option<KeyboardFocusTarget> {
let location = location_f64.to_i32_round();
@ -3185,7 +3182,6 @@ impl TilingLayout {
.focus_under(
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
seat,
)
.is_some()
{
@ -3200,7 +3196,6 @@ impl TilingLayout {
&self,
location_f64: Point<f64, Local>,
overview: OverviewMode,
seat: &Seat<State>,
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
let location = location_f64.to_i32_round();
@ -3215,7 +3210,6 @@ impl TilingLayout {
if let Some((target, surface_offset)) = mapped.focus_under(
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
seat,
) {
return Some((
target,
@ -3233,7 +3227,6 @@ impl TilingLayout {
&self,
location_f64: Point<f64, Local>,
overview: OverviewMode,
seat: &Seat<State>,
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
let tree = &self.queue.trees.back().unwrap().0;
let root = tree.root_node_id()?;
@ -3250,7 +3243,6 @@ impl TilingLayout {
if let Some((target, surface_offset)) = mapped.focus_under(
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
seat,
) {
return Some((
target,
@ -3301,7 +3293,6 @@ impl TilingLayout {
.focus_under(
test_point,
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
seat,
)
.map(|(surface, surface_offset)| {
(

View file

@ -50,13 +50,10 @@ use smithay::{
},
utils::{IsAlive, Logical, Point, Rectangle, Serial, Size},
wayland::{
compositor::{SurfaceAttributes, get_parent, with_states},
compositor::{SurfaceAttributes, with_states},
seat::WaylandFocus,
session_lock::LockSurface,
shell::{
wlr_layer::{KeyboardInteractivity, Layer, LayerSurfaceCachedState},
xdg::{XDG_POPUP_ROLE, XdgPopupSurfaceData},
},
shell::wlr_layer::{KeyboardInteractivity, Layer, LayerSurfaceCachedState},
xdg_activation::XdgActivationState,
xwayland_keyboard_grab::XWaylandKeyboardGrab,
},
@ -439,11 +436,6 @@ fn create_workspace_from_pinned(
| WorkspaceCapabilities::Pin
| WorkspaceCapabilities::Move,
);
if let Some(ref name) = pinned.name {
state.set_workspace_name(&workspace_handle, name);
}
Workspace::from_pinned(
pinned,
workspace_handle,
@ -624,7 +616,6 @@ impl WorkspaceSet {
state,
self.workspaces.len() as u8 + 1,
&workspace.handle,
workspace.name.as_deref(),
// this method is only used by code paths related to dynamic workspaces, so this should be fine
);
self.workspaces.push(workspace);
@ -686,12 +677,7 @@ impl WorkspaceSet {
fn update_workspace_idxs(&self, state: &mut WorkspaceUpdateGuard<'_, State>) {
for (i, workspace) in self.workspaces.iter().enumerate() {
workspace_set_idx(
state,
i as u8 + 1,
&workspace.handle,
workspace.name.as_deref(),
);
workspace_set_idx(state, i as u8 + 1, &workspace.handle);
}
}
@ -742,78 +728,6 @@ impl WorkspaceSet {
self.post_remove_workspace(workspace_state, &previous_active_handle);
prefers
}
pub fn surface_geometry_offset_from_toplevel(
&self,
surface: &WlSurface,
) -> Option<(Rectangle<i32, Local>, Point<i32, Logical>)> {
let mut root = surface.clone();
while let Some(parent) = get_parent(&root) {
root = parent;
}
while smithay::wayland::compositor::get_role(&root) == Some(XDG_POPUP_ROLE) {
let parent = with_states(&root, |states| {
states
.data_map
.get::<XdgPopupSurfaceData>()
.and_then(|m| m.lock().unwrap().parent.as_ref().cloned())
});
if let Some(parent) = parent {
root = parent;
} else {
break;
}
}
self.sticky_layer
.mapped()
.find(|w| {
w.windows()
.any(|(w, _)| w.wl_surface().as_deref() == Some(&root))
})
.and_then(|w| {
self.sticky_layer
.element_geometry(w)
.zip(w.surface_offset(surface))
})
.or_else(|| {
self.workspaces.iter().find_map(|workspace| {
workspace
.get_fullscreen_surfaces()
.find_map(|fs| {
(fs.surface.wl_surface().as_deref() == Some(&root))
.then(|| {
fs.surface.surface_offset(surface).map(|offset| {
(workspace.fullscreen_geometry_for(fs), offset)
})
})
.flatten()
})
.or_else(|| {
workspace.mapped().find_map(|w| {
w.windows()
.any(|(w, _)| w.wl_surface().as_deref() == Some(&root))
.then(|| {
workspace.element_geometry(w).zip(w.surface_offset(surface))
})
.flatten()
})
})
})
})
.or_else(|| {
layer_map_for_output(&self.output).layers().find_map(|l| {
(l.wl_surface() == &root)
.then(|| {
CosmicSurface::surface_tree_offset(l.wl_surface(), surface)
.map(|offset| (l.geometry().as_local(), offset))
})
.flatten()
})
})
}
}
#[derive(Debug)]
@ -1588,9 +1502,7 @@ impl Common {
self.popups.cleanup();
self.toplevel_info_state.refresh(&self.workspace_state);
self.refresh_idle_inhibit();
if let Some(mut a11y_keyboard_monitor) = self.dbus_state.a11y_keyboard_monitor() {
a11y_keyboard_monitor.refresh();
}
self.a11y_keyboard_monitor_state.refresh();
self.image_copy_capture_state.cleanup();
}
@ -1650,13 +1562,12 @@ impl Common {
if let Some(mapped) = shell.element_for_surface(surface) {
mapped.on_commit(surface);
}
if let Some(fs) = shell
if let Some(surface) = shell
.workspaces
.spaces()
.flat_map(|w| w.get_fullscreen_surfaces())
.find(|f| f.surface == *surface)
.find_map(|w| w.get_fullscreen().filter(|s| *s == surface))
{
fs.surface.on_commit()
surface.on_commit()
};
}
self.popups.commit(surface);
@ -1917,9 +1828,7 @@ impl Shell {
.outputs()
.find(|output| {
let workspace = self.active_space(output).unwrap();
workspace
.get_fullscreen_surfaces()
.any(|f| f.surface == elem)
workspace.get_fullscreen() == Some(&elem)
})
.cloned(),
KeyboardFocusTarget::Group(WindowGroup { node, .. }) => self
@ -2052,8 +1961,8 @@ impl Shell {
let workspace = self.active_space(o).unwrap();
workspace
.get_fullscreen_surfaces()
.any(|f| f.surface.has_surface(surface, WindowSurfaceType::ALL))
.get_fullscreen()
.is_some_and(|s| s.has_surface(surface, WindowSurfaceType::ALL))
|| workspace
.mapped()
.any(|e| e.has_surface(surface, WindowSurfaceType::ALL))
@ -2108,8 +2017,8 @@ impl Shell {
.workspaces
.spaces()
.find(|w| {
w.get_fullscreen_surfaces()
.any(|f| f.surface.has_surface(surface, WindowSurfaceType::ALL))
w.get_fullscreen()
.is_some_and(|s| s.has_surface(surface, WindowSurfaceType::ALL))
|| w.mapped()
.any(|e| e.has_surface(surface, WindowSurfaceType::ALL))
|| w.minimized_windows.iter().any(|m| {
@ -2156,7 +2065,7 @@ impl Shell {
.mapped()
.any(|m| m.windows().any(|(s, _)| &s == surface))
|| set.workspaces.iter().any(|w| {
w.get_fullscreen_surfaces().any(|f| &f.surface == surface)
w.get_fullscreen().is_some_and(|s| s == surface)
|| w.minimized_windows
.iter()
.any(|m| m.windows().any(|s| &s == surface))
@ -2845,7 +2754,12 @@ impl Shell {
let floating_exception = layout::has_floating_exception(&self.tiling_exceptions, &window);
if should_be_fullscreen {
workspace.map_fullscreen(&window, &seat, None, None);
if let Some((surface, state, _)) = workspace.map_fullscreen(&window, &seat, None, None)
{
toplevel_leave_output(&surface, &workspace.output);
toplevel_leave_workspace(&surface, &workspace.handle);
self.remap_unfullscreened_window(surface, state, loop_handle);
}
if was_activated {
workspace_state.add_workspace_state(&workspace_handle, WState::Urgent);
}
@ -3216,12 +3130,9 @@ impl Shell {
let from_workspace = self.workspaces.space_for_handle_mut(from).unwrap(); // checked above
let is_minimized = window.is_minimized();
let is_fullscreen = from_workspace
.get_fullscreen_surfaces()
.any(|f| &f.surface == window);
let is_fullscreen = from_workspace.get_fullscreen().is_some_and(|f| f == window);
let mut window_state = if is_fullscreen {
let (_, previous_state, previous_geometry) =
from_workspace.take_fullscreen(window).unwrap();
let (_, previous_state, previous_geometry) = from_workspace.take_fullscreen().unwrap();
WorkspaceRestoreData::Fullscreen(previous_state.zip(previous_geometry).map(
|(previous_state, previous_geometry)| FullscreenRestoreData {
previous_state,
@ -3377,12 +3288,14 @@ impl Shell {
);
mapped.into()
} else if let WorkspaceRestoreData::Fullscreen(previous) = window_state {
to_workspace.map_fullscreen(
if let Some((old_surface, previous_state, _)) = to_workspace.map_fullscreen(
window,
None,
previous.clone().map(|p| p.previous_state),
previous.map(|p| p.previous_geometry),
);
) {
self.remap_unfullscreened_window(old_surface, previous_state, evlh);
}
window.clone().into()
} else {
unreachable!() // TODO: sticky
@ -3600,12 +3513,8 @@ impl Shell {
} else if let Some((workspace, output)) = self.workspace_for_surface(surface) {
let workspace = self.workspaces.space_for_handle(&workspace).unwrap();
if let Some(fs) = workspace
.get_fullscreen_surfaces()
.find(|f| &f.surface == surface)
{
let window = &fs.surface;
let global_position = (workspace.fullscreen_geometry_for(fs).loc
if let Some(window) = workspace.get_fullscreen().filter(|s| *s == surface) {
let global_position = (workspace.fullscreen_geometry().unwrap().loc
+ location.as_local())
.to_global(&output);
@ -3698,14 +3607,10 @@ impl Shell {
let maybe_fullscreen_workspace = self
.workspaces
.spaces_mut()
.find(|w| w.get_fullscreen_surfaces().any(|f| &f.surface == surface));
.find(|w| w.get_fullscreen().is_some_and(|s| s == surface));
if let Some(workspace) = maybe_fullscreen_workspace {
let fs = workspace
.get_fullscreen_surfaces()
.find(|f| &f.surface == surface)
.unwrap();
element_geo = Some(workspace.fullscreen_geometry_for(fs));
let (surface, state, _) = workspace.remove_fullscreen_surface(surface).unwrap();
element_geo = Some(workspace.fullscreen_geometry().unwrap());
let (surface, state, _) = workspace.remove_fullscreen().unwrap();
self.remap_unfullscreened_window(surface, state, evlh);
};
@ -3730,11 +3635,7 @@ impl Shell {
if old_mapped.is_maximized(false) {
new_mapped.set_maximized(false);
}
start_data.set_focus(new_mapped.focus_under(
(0., 0.).into(),
WindowSurfaceType::ALL,
seat,
));
start_data.set_focus(new_mapped.focus_under((0., 0.).into(), WindowSurfaceType::ALL));
new_mapped
} else {
old_mapped.clone()
@ -3918,15 +3819,19 @@ impl Shell {
/// Get the window geometry of a keyboard focus target
pub fn focused_geometry(&self, target: &KeyboardFocusTarget) -> Option<Rectangle<i32, Global>> {
match target {
KeyboardFocusTarget::Fullscreen(surface) => surface
.wl_surface()
.and_then(|s| self.workspace_for_surface(&s))
.and_then(|(handle, _)| self.workspaces.space_for_handle(&handle))
.map(|workspace| {
KeyboardFocusTarget::Fullscreen(surface) => {
if let Some(workspace) = surface
.wl_surface()
.and_then(|s| self.workspace_for_surface(&s))
.and_then(|(handle, _)| self.workspaces.space_for_handle(&handle))
{
workspace
.fullscreen_geometry_for_surface(surface)
.to_global(workspace.output())
}),
.fullscreen_geometry()
.map(|f| f.to_global(workspace.output()))
} else {
None
}
}
_ => {
if let Some(element) = self.focused_element(target) {
self.element_geometry(&element)
@ -4201,7 +4106,7 @@ impl Shell {
let element_offset = (new_loc - geometry.loc).as_logical();
let focus = mapped
.focus_under(element_offset.to_f64(), WindowSurfaceType::ALL, seat)
.focus_under(element_offset.to_f64(), WindowSurfaceType::ALL)
.map(|(target, surface_offset)| (target, (surface_offset + element_offset.to_f64())));
start_data.set_location(new_loc.as_logical().to_f64());
start_data.set_focus(focus.clone());
@ -4278,8 +4183,9 @@ impl Shell {
self.workspaces.sets.values_mut().find_map(|set| {
set.workspaces.iter_mut().find_map(|workspace| {
let window = workspace
.get_fullscreen_surfaces()
.map(|f| f.surface.clone())
.get_fullscreen()
.cloned()
.into_iter()
.chain(workspace.mapped().map(|m| m.active_window()))
.find(|s| s == surface);
window.map(|s| (workspace, s))
@ -4779,16 +4685,15 @@ impl Shell {
&mut self,
surface: &S,
output: Output,
_loop_handle: &LoopHandle<'static, State>,
loop_handle: &LoopHandle<'static, State>,
) -> Option<KeyboardFocusTarget>
where
CosmicSurface: PartialEq<S>,
{
let mapped = self.element_for_surface(surface).cloned()?;
let seat = self.seats.last_active().clone();
let window;
if let Some((old_output, set)) = self
let old_fullscreen = if let Some((old_output, set)) = self
.workspaces
.sets
.iter_mut()
@ -4835,7 +4740,7 @@ impl Shell {
let workspace = self.active_space_mut(&output).unwrap();
workspace.map_fullscreen(
&window,
&seat,
None,
Some(FullscreenRestoreState::Sticky {
output: old_output,
state: FloatingRestoreData {
@ -4846,7 +4751,7 @@ impl Shell {
},
}),
Some(from),
);
)
} else if let Some(workspace) = self.space_for_mut(&mapped) {
if mapped.is_minimized() {
// TODO: Rewrite the `MinimizedWindow` to restore to fullscreen
@ -4872,7 +4777,7 @@ impl Shell {
workspace.map_fullscreen(
&window,
&seat,
None,
match state {
WorkspaceRestoreData::Floating(floating_state) => {
floating_state.map(|state| FullscreenRestoreState::Floating {
@ -4889,11 +4794,15 @@ impl Shell {
WorkspaceRestoreData::Fullscreen(_) => unreachable!(),
},
Some(from),
);
)
} else {
return None;
};
if let Some((old_fullscreen, restore, _)) = old_fullscreen {
self.remap_unfullscreened_window(old_fullscreen, restore, loop_handle);
}
Some(KeyboardFocusTarget::Fullscreen(window))
}
@ -4908,12 +4817,11 @@ impl Shell {
let maybe_workspace = self.workspaces.iter_mut().find_map(|(_, s)| {
s.workspaces
.iter_mut()
.find(|w| w.get_fullscreen_surfaces().any(|f| &f.surface == surface))
.find(|w| w.get_fullscreen().is_some_and(|f| f == surface))
});
if let Some(workspace) = maybe_workspace {
let (old_fullscreen, restore, _) =
workspace.remove_fullscreen_surface(surface).unwrap();
let (old_fullscreen, restore, _) = workspace.remove_fullscreen().unwrap();
toplevel_leave_output(&old_fullscreen, &workspace.output);
toplevel_leave_workspace(&old_fullscreen, &workspace.handle);
@ -5036,9 +4944,8 @@ fn workspace_set_idx(
state: &mut WorkspaceUpdateGuard<'_, State>,
idx: u8,
handle: &WorkspaceHandle,
name: Option<&str>,
) {
state.set_workspace_name(handle, name.unwrap_or(&format!("{}", idx)));
state.set_workspace_name(handle, format!("{}", idx));
state.set_workspace_coordinates(handle, &[idx as u32]);
}

View file

@ -17,11 +17,8 @@ use smithay::{
pointer::{CursorImageAttributes, CursorImageStatus},
},
output::Output,
reexports::{
input::Device as InputDevice,
wayland_server::{DisplayHandle, protocol::wl_surface::WlSurface},
},
utils::{Buffer, IsAlive, Logical, Monotonic, Point, Rectangle, Serial, Time, Transform},
reexports::{input::Device as InputDevice, wayland_server::DisplayHandle},
utils::{Buffer, IsAlive, Monotonic, Point, Rectangle, Serial, Time, Transform},
wayland::compositor::with_states,
};
use tracing::warn;
@ -181,9 +178,6 @@ struct ActiveOutput(pub Mutex<Output>);
/// The output which currently has keyboard focus
struct FocusedOutput(pub Mutex<Option<Output>>);
#[derive(Default)]
pub struct PointerConstraintHint(pub Mutex<Option<(WlSurface, Point<f64, Logical>)>>);
#[derive(Default)]
pub struct LastModifierChange(pub Mutex<Option<Serial>>);
@ -207,7 +201,6 @@ pub fn create_seat(
userdata.insert_if_missing_threadsafe(CursorState::default);
userdata.insert_if_missing_threadsafe(|| ActiveOutput(Mutex::new(output.clone())));
userdata.insert_if_missing_threadsafe(|| FocusedOutput(Mutex::new(None)));
userdata.insert_if_missing_threadsafe(PointerConstraintHint::default);
userdata.insert_if_missing_threadsafe(|| Mutex::new(CursorImageStatus::default_named()));
// A lot of clients bind keyboard and pointer unconditionally once on launch..
@ -259,8 +252,6 @@ pub trait SeatExt {
fn supressed_buttons(&self) -> &SupressedButtons;
fn modifiers_shortcut_queue(&self) -> &ModifiersShortcutQueue;
fn last_modifier_change(&self) -> Option<Serial>;
fn pointer_constraint_hint(&self) -> Option<(WlSurface, Point<f64, Logical>)>;
fn set_pointer_constraint_hint(&self, hint: Option<(WlSurface, Point<f64, Logical>)>);
fn cursor_geometry(
&self,
@ -346,23 +337,6 @@ impl SeatExt for Seat<State> {
.unwrap()
}
fn pointer_constraint_hint(&self) -> Option<(WlSurface, Point<f64, Logical>)> {
let lock = self.user_data().get::<PointerConstraintHint>().unwrap();
let mut hint = lock.0.lock().unwrap();
// Check if alive
if let Some((ref surface, _)) = *hint
&& !surface.alive()
{
*hint = None;
}
hint.clone()
}
fn set_pointer_constraint_hint(&self, hint: Option<(WlSurface, Point<f64, Logical>)>) {
let lock = self.user_data().get::<PointerConstraintHint>().unwrap();
*lock.0.lock().unwrap() = hint;
}
fn cursor_geometry(
&self,
loc: impl Into<Point<f64, Buffer>>,

View file

@ -108,10 +108,9 @@ pub struct Workspace {
pub floating_layer: FloatingLayout,
pub minimized_windows: Vec<MinimizedWindow>,
pub tiling_enabled: bool,
pub fullscreen_surfaces: Vec<FullscreenSurface>,
pub fullscreen: Option<FullscreenSurface>,
pub pinned: bool,
pub id: Option<String>,
pub name: Option<String>,
pub handle: WorkspaceHandle,
pub focus_stack: FocusStacks,
@ -384,10 +383,9 @@ impl Workspace {
floating_layer,
tiling_enabled,
minimized_windows: Vec::new(),
fullscreen_surfaces: Vec::new(),
fullscreen: None,
pinned: false,
id: None,
name: None,
handle,
focus_stack: FocusStacks::default(),
image_copy: ImageCopySessions::default(),
@ -418,10 +416,9 @@ impl Workspace {
floating_layer,
tiling_enabled: pinned.tiling_enabled,
minimized_windows: Vec::new(),
fullscreen_surfaces: Vec::new(),
fullscreen: None,
pinned: true,
id: pinned.id.clone(),
name: pinned.name.clone(),
handle,
focus_stack: FocusStacks::default(),
image_copy: ImageCopySessions::default(),
@ -439,9 +436,9 @@ impl Workspace {
}
pub fn to_pinned(&self) -> Option<PinnedWorkspace> {
debug_assert!(self.id.is_some());
let output = self.explicit_output().clone();
if self.pinned {
debug_assert!(self.id.is_some());
Some(PinnedWorkspace {
output: cosmic_comp_config::workspace::OutputMatch {
name: output.name,
@ -449,7 +446,6 @@ impl Workspace {
},
tiling_enabled: self.tiling_enabled,
id: self.id.clone(),
name: self.name.clone(),
})
} else {
None
@ -458,10 +454,7 @@ impl Workspace {
#[profiling::function]
pub fn refresh(&mut self) {
// seems it removes dead windows
// self.fullscreen.take_if(|w| !w.alive());
self.fullscreen_surfaces.retain(|w| w.alive());
self.fullscreen.take_if(|w| !w.alive());
self.floating_layer.refresh();
self.tiling_layer.refresh();
}
@ -485,15 +478,14 @@ impl Workspace {
self.is_empty() && !self.has_activation_token(xdg_activation_state) && !self.pinned
}
/// cleans up any window that is not alive anymore
pub fn refresh_focus_stack(&mut self) {
for (seat, stack) in self.focus_stack.0.iter_mut() {
let fullscreen_surfaces: Vec<&CosmicSurface> = self
.fullscreen_surfaces
.iter()
.filter(|f| f.alive() && f.ended_at.is_none())
.map(|f| &f.surface)
.collect();
let fullscreen = self
.fullscreen
.as_ref()
.filter(|f| f.alive())
.filter(|f| f.ended_at.is_none())
.map(|f| &f.surface);
// Move grab is treated as focused, so don't change focus to a
// window while grab exists.
@ -514,7 +506,7 @@ impl Workspace {
.chain(move_mapped.iter())
};
stack.retain(|w| match w {
FocusTarget::Fullscreen(s) => fullscreen_surfaces.contains(&s),
FocusTarget::Fullscreen(s) => fullscreen.is_some_and(|f| f == s),
FocusTarget::Window(w) => mapped().any(|m| w == m),
});
}
@ -523,12 +515,15 @@ impl Workspace {
pub fn animations_going(&self) -> bool {
self.tiling_layer.animations_going()
|| self.floating_layer.animations_going()
|| self.fullscreen_surfaces.iter().any(|f| f.is_animating())
|| self
.fullscreen
.as_ref()
.is_some_and(|f| f.start_at.is_some() || f.ended_at.is_some())
|| self.dirty.swap(false, Ordering::SeqCst)
}
pub fn update_animations(&mut self) -> HashMap<ClientId, Client> {
for f in self.fullscreen_surfaces.iter_mut() {
if let Some(f) = self.fullscreen.as_mut() {
if let Some(start) = f.start_at.as_ref() {
let duration_since = Instant::now().duration_since(*start);
if duration_since > FULLSCREEN_ANIMATION_DURATION {
@ -536,17 +531,15 @@ impl Workspace {
self.dirty.store(true, Ordering::SeqCst);
}
}
}
self.fullscreen_surfaces.retain(|f| {
if let Some(end) = f.ended_at
&& Instant::now().duration_since(end) >= FULLSCREEN_ANIMATION_DURATION
{
self.dirty.store(true, Ordering::SeqCst);
return false;
if let Some(end) = f.ended_at {
let duration_since = Instant::now().duration_since(end);
if duration_since >= FULLSCREEN_ANIMATION_DURATION {
let _ = self.fullscreen.take();
self.dirty.store(true, Ordering::SeqCst);
}
}
true
});
}
let clients = self.tiling_layer.update_animation_state();
self.floating_layer.update_animation_state();
@ -581,11 +574,7 @@ impl Workspace {
toplevel_enter_output(&surface, output);
}
}
for f in self
.fullscreen_surfaces
.iter()
.filter(|f| f.ended_at.is_none())
{
if let Some(f) = self.fullscreen.as_ref().filter(|f| f.ended_at.is_none()) {
toplevel_leave_output(&f.surface, &self.output);
toplevel_enter_output(&f.surface, output);
}
@ -676,13 +665,12 @@ impl Workspace {
where
CosmicSurface: PartialEq<S>,
{
if let Some(idx) = self
.fullscreen_surfaces
.iter()
.position(|f| f.ended_at.is_none() && &f.surface == surface)
if self
.fullscreen
.as_ref()
.is_some_and(|f| f.ended_at.is_none() && &f.surface == surface)
{
let (surface, previous_state, previous_geometry) =
self.remove_fullscreen_at(idx).unwrap();
let (surface, previous_state, previous_geometry) = self.remove_fullscreen().unwrap();
return Some((
surface,
WorkspaceRestoreData::Fullscreen(previous_state.zip(previous_geometry).map(
@ -735,28 +723,24 @@ impl Workspace {
Some((mapped.active_window(), layer))
}
pub fn fullscreen_geometry_for_surface(
&self,
surface: &CosmicSurface,
) -> Rectangle<i32, Local> {
let bbox = surface.bbox().as_local();
pub fn fullscreen_geometry(&self) -> Option<Rectangle<i32, Local>> {
self.fullscreen.as_ref().map(|fullscreen| {
let bbox = fullscreen.surface.bbox().as_local();
let mut full_geo = Rectangle::from_size(self.output.geometry().size.as_local());
if bbox != full_geo {
if bbox.size.w < full_geo.size.w {
full_geo.loc.x += (full_geo.size.w - bbox.size.w) / 2;
full_geo.size.w = bbox.size.w;
let mut full_geo = Rectangle::from_size(self.output.geometry().size.as_local());
if bbox != full_geo {
if bbox.size.w < full_geo.size.w {
full_geo.loc.x += (full_geo.size.w - bbox.size.w) / 2;
full_geo.size.w = bbox.size.w;
}
if bbox.size.h < full_geo.size.h {
full_geo.loc.y += (full_geo.size.h - bbox.size.h) / 2;
full_geo.size.h = bbox.size.h;
}
}
if bbox.size.h < full_geo.size.h {
full_geo.loc.y += (full_geo.size.h - bbox.size.h) / 2;
full_geo.size.h = bbox.size.h;
}
}
full_geo
}
pub fn fullscreen_geometry_for(&self, fullscreen: &FullscreenSurface) -> Rectangle<i32, Local> {
self.fullscreen_geometry_for_surface(&fullscreen.surface)
full_geo
})
}
pub fn element_for_surface<S>(&self, surface: &S) -> Option<&CosmicMapped>
@ -796,23 +780,24 @@ impl Workspace {
let stack = self.focus_stack.get(seat);
let last_focused = stack.last();
if let Some(fullscreen) = self.fullscreen_surfaces.iter().find(|f| {
!f.is_animating()
&& last_focused
.is_some_and(|t| matches!(t, FocusTarget::Fullscreen(s) if s == &f.surface))
}) {
let geometry = self.fullscreen_geometry_for(fullscreen);
if let Some(fullscreen) = self.fullscreen.as_ref()
&& last_focused.is_some_and(
|t| matches!(t, FocusTarget::Fullscreen(f) if f == &fullscreen.surface),
)
&& !fullscreen.is_animating()
{
let geometry = self.fullscreen_geometry().unwrap();
return fullscreen_element_under(fullscreen, geometry);
}
self.floating_layer
.popup_element_under(location, seat)
.or_else(|| self.tiling_layer.popup_element_under(location, seat))
.popup_element_under(location)
.or_else(|| self.tiling_layer.popup_element_under(location))
.or_else(|| {
if last_focused.is_none_or(|t| !matches!(t, FocusTarget::Fullscreen(_)))
&& let Some(fullscreen) = self.get_fullscreen(seat)
&& let Some(fullscreen) = self.fullscreen.as_ref()
{
let geometry = self.fullscreen_geometry_for(fullscreen);
let geometry = self.fullscreen_geometry().unwrap();
return fullscreen_element_under(fullscreen, geometry);
}
None
@ -845,23 +830,24 @@ impl Workspace {
let stack = self.focus_stack.get(seat);
let last_focused = stack.last();
if let Some(fullscreen) = self.fullscreen_surfaces.iter().find(|fs| {
!fs.is_animating()
&& last_focused
.is_some_and(|t| matches!(t, FocusTarget::Fullscreen(f) if f == &fs.surface))
}) {
let geometry = self.fullscreen_geometry_for(fullscreen);
if let Some(fullscreen) = self.fullscreen.as_ref()
&& last_focused.is_some_and(
|t| matches!(t, FocusTarget::Fullscreen(f) if f == &fullscreen.surface),
)
&& !fullscreen.is_animating()
{
let geometry = self.fullscreen_geometry().unwrap();
return fullscreen_element_under(fullscreen, geometry);
}
self.floating_layer
.toplevel_element_under(location, seat)
.or_else(|| self.tiling_layer.toplevel_element_under(location, seat))
.toplevel_element_under(location)
.or_else(|| self.tiling_layer.toplevel_element_under(location))
.or_else(|| {
if last_focused.is_none_or(|t| !matches!(t, FocusTarget::Fullscreen(_)))
&& let Some(fullscreen) = self.get_fullscreen(seat)
&& let Some(fullscreen) = self.fullscreen.as_ref()
{
let geometry = self.fullscreen_geometry_for(fullscreen);
let geometry = self.fullscreen_geometry().unwrap();
return fullscreen_element_under(fullscreen, geometry);
}
None
@ -881,7 +867,7 @@ impl Workspace {
let check_fullscreen = |fullscreen: &FullscreenSurface| {
if !fullscreen.is_animating() {
let geometry = self.fullscreen_geometry_for(fullscreen);
let geometry = self.fullscreen_geometry().unwrap();
return fullscreen
.surface
.0
@ -905,16 +891,18 @@ impl Workspace {
let stack = self.focus_stack.get(seat);
let last_focused = stack.last();
self.fullscreen_surfaces
.iter()
.find(|f| last_focused.is_some_and(|t| t == &f.surface))
self.fullscreen
.as_ref()
.filter(|f| last_focused.is_some_and(|t| t == &f.surface))
.and_then(check_fullscreen)
.or_else(|| self.floating_layer.popup_surface_under(location, seat))
.or_else(|| self.floating_layer.popup_surface_under(location))
.or_else(|| self.tiling_layer.popup_surface_under(location, overview))
.or_else(|| {
self.tiling_layer
.popup_surface_under(location, overview, seat)
self.fullscreen
.as_ref()
.filter(|f| last_focused.is_none_or(|t| t != &f.surface))
.and_then(check_fullscreen)
})
.or_else(|| self.get_fullscreen(seat).and_then(check_fullscreen))
.map(|(m, p)| (m, p.to_global(&self.output)))
}
@ -931,7 +919,7 @@ impl Workspace {
let check_fullscreen = |fullscreen: &FullscreenSurface| {
if !fullscreen.is_animating() {
let geometry = self.fullscreen_geometry_for(fullscreen);
let geometry = self.fullscreen_geometry().unwrap();
return fullscreen
.surface
.focus_under(
@ -949,16 +937,18 @@ impl Workspace {
let stack = self.focus_stack.get(seat);
let last_focused = stack.last();
self.fullscreen_surfaces
.iter()
.find(|f| last_focused.is_some_and(|t| t == &f.surface))
self.fullscreen
.as_ref()
.filter(|f| last_focused.is_some_and(|t| t == &f.surface))
.and_then(check_fullscreen)
.or_else(|| self.floating_layer.toplevel_surface_under(location, seat))
.or_else(|| self.floating_layer.toplevel_surface_under(location))
.or_else(|| self.tiling_layer.toplevel_surface_under(location, overview))
.or_else(|| {
self.tiling_layer
.toplevel_surface_under(location, overview, seat)
self.fullscreen
.as_ref()
.filter(|f| last_focused.is_none_or(|t| t != &f.surface))
.and_then(check_fullscreen)
})
.or_else(|| self.get_fullscreen(seat).and_then(check_fullscreen))
.map(|(m, p)| (m, p.to_global(&self.output)))
}
@ -1030,14 +1020,10 @@ impl Workspace {
where
CosmicSurface: PartialEq<S>,
{
if let Some(idx) = self
.fullscreen_surfaces
.iter()
.position(|f| f.ended_at.is_none() && &f.surface == surface)
{
let fullscreen_state = self.fullscreen_surfaces.get(idx)?.clone();
if self.get_fullscreen().is_some_and(|s| s == surface) {
let fullscreen_state = self.fullscreen.clone().unwrap();
{
let f = self.fullscreen_surfaces.get_mut(idx)?;
let f = self.fullscreen.as_mut().unwrap();
f.previous_geometry = Some(to);
f.ended_at = Some(
Instant::now()
@ -1141,20 +1127,16 @@ impl Workspace {
)> {
match window {
MinimizedWindow::Fullscreen { previous, surface } => {
let old_fullscreen = self.remove_fullscreen();
surface.set_minimized(false);
// focus it so it's the top fullscreen window
self.focus_stack
.get_mut(seat)
.append(FocusTarget::Fullscreen(surface.clone()));
self.fullscreen_surfaces.push(FullscreenSurface {
self.fullscreen = Some(FullscreenSurface {
surface,
previous_state: previous.clone().map(|p| p.previous_state),
previous_geometry: previous.map(|p| p.previous_geometry),
start_at: None,
ended_at: None,
});
self.dirty.store(true, Ordering::SeqCst);
None
old_fullscreen
}
MinimizedWindow::Floating { window, previous } => {
let current_output_size = self.output.geometry().size.as_logical();
@ -1242,7 +1224,13 @@ impl Workspace {
seat: impl Into<Option<&'a Seat<State>>>,
restore: Option<FullscreenRestoreState>,
previous_geometry: Option<Rectangle<i32, Local>>,
) {
) -> Option<(
CosmicSurface,
Option<FullscreenRestoreState>,
Option<Rectangle<i32, Local>>,
)> {
let res = self.remove_fullscreen();
window.set_fullscreen(true);
window.set_geometry(self.output.geometry(), 0);
window.send_configure();
@ -1255,137 +1243,96 @@ impl Workspace {
self.focus_stack.get_mut(seat).append(window.clone());
}
self.dirty.store(true, Ordering::SeqCst);
self.fullscreen_surfaces.push(FullscreenSurface {
self.fullscreen = Some(FullscreenSurface {
surface: window.clone(),
previous_state: restore,
previous_geometry,
start_at: Some(Instant::now()),
ended_at: None,
});
res
}
#[must_use]
pub fn take_fullscreen<S>(
pub fn take_fullscreen(
&mut self,
surface: &S,
) -> Option<(
CosmicSurface,
Option<FullscreenRestoreState>,
Option<Rectangle<i32, Local>>,
)>
where
CosmicSurface: PartialEq<S>,
{
let idx = self
.fullscreen_surfaces
.iter()
.position(|f| f.ended_at.is_none() && &f.surface == surface)?;
let fs = self.fullscreen_surfaces.remove(idx);
for focus_stack in self.focus_stack.0.values_mut() {
focus_stack.retain(|t| t != &fs.surface);
}
Some((fs.surface, fs.previous_state, fs.previous_geometry))
}
#[must_use]
pub fn remove_fullscreen_at(
&mut self,
idx: usize,
) -> Option<(
CosmicSurface,
Option<FullscreenRestoreState>,
Option<Rectangle<i32, Local>>,
)> {
// if it doesn't exist we move on.
let surface = self.fullscreen_surfaces.get_mut(idx)?;
// if already being removed, do nothing
if surface.ended_at.is_some() {
return None;
}
if surface.surface.alive() {
surface.surface.output_leave(&self.output);
surface.surface.set_fullscreen(false);
if let Some(previous_geometry) = surface.previous_geometry.as_ref() {
surface
.surface
.set_geometry(previous_geometry.to_global(&self.output), 0);
}
surface.surface.send_configure();
}
let surface = self.fullscreen.take_if(|s| s.ended_at.is_none())?;
for focus_stack in self.focus_stack.0.values_mut() {
focus_stack.retain(|t| t != &surface.surface);
}
surface.ended_at = Some(
Instant::now()
- (FULLSCREEN_ANIMATION_DURATION
- surface
.start_at
.take()
.map(|earlier| {
Instant::now()
.duration_since(earlier)
.min(FULLSCREEN_ANIMATION_DURATION)
})
.unwrap_or(FULLSCREEN_ANIMATION_DURATION)),
);
Some((
surface.surface.clone(),
surface.previous_state.clone(),
surface.surface,
surface.previous_state,
surface.previous_geometry,
))
}
#[must_use]
pub fn remove_fullscreen_surface<S>(
pub fn remove_fullscreen(
&mut self,
surface: &S,
) -> Option<(
CosmicSurface,
Option<FullscreenRestoreState>,
Option<Rectangle<i32, Local>>,
)>
where
CosmicSurface: PartialEq<S>,
{
let idx = self
.fullscreen_surfaces
.iter()
.position(|f| f.ended_at.is_none() && &f.surface == surface)?;
self.remove_fullscreen_at(idx)
}
)> {
if let Some(surface) = self.fullscreen.as_mut() {
if surface.ended_at.is_some() {
return None;
}
pub fn get_fullscreen(&self, seat: &Seat<State>) -> Option<&FullscreenSurface> {
let stack = self.focus_stack.get(seat);
stack
.iter()
.find_map(|t| {
if let FocusTarget::Fullscreen(s) = t {
self.fullscreen_surfaces
.iter()
.find(|f| f.alive() && f.ended_at.is_none() && &f.surface == s)
} else {
None
if surface.surface.alive() {
surface.surface.output_leave(&self.output);
surface.surface.set_fullscreen(false);
if let Some(previous_geometry) = surface.previous_geometry.as_ref() {
surface
.surface
.set_geometry(previous_geometry.to_global(&self.output), 0);
}
})
.or_else(|| {
self.fullscreen_surfaces
.iter()
.rev()
.find(|f| f.alive() && f.ended_at.is_none())
})
surface.surface.send_configure();
}
for focus_stack in self.focus_stack.0.values_mut() {
focus_stack.retain(|t| t != &surface.surface);
}
surface.ended_at = Some(
Instant::now()
- (FULLSCREEN_ANIMATION_DURATION
- surface
.start_at
.take()
.map(|earlier| {
Instant::now()
.duration_since(earlier)
.min(FULLSCREEN_ANIMATION_DURATION)
})
.unwrap_or(FULLSCREEN_ANIMATION_DURATION)),
);
Some((
surface.surface.clone(),
surface.previous_state.clone(),
surface.previous_geometry,
))
} else {
None
}
}
pub fn get_fullscreen_surfaces(&self) -> impl Iterator<Item = &FullscreenSurface> {
self.fullscreen_surfaces
.iter()
.filter(|f| f.alive() && f.ended_at.is_none())
pub fn get_fullscreen(&self) -> Option<&CosmicSurface> {
self.fullscreen
.as_ref()
.filter(|f| f.alive())
.filter(|f| f.ended_at.is_none())
.map(|f| &f.surface)
}
pub fn resize(
@ -1512,18 +1459,14 @@ impl Workspace {
self.floating_layer.mapped().count()
+ self.tiling_layer.mapped().count()
+ self.minimized_windows.len()
+ self
.fullscreen_surfaces
.iter()
.filter(|f| f.ended_at.is_none())
.count()
+ if self.fullscreen.is_some() { 1 } else { 0 }
}
pub fn is_empty(&self) -> bool {
self.floating_layer.mapped().next().is_none()
&& self.tiling_layer.mapped().next().is_none()
&& self.minimized_windows.is_empty()
&& self.fullscreen_surfaces.is_empty()
&& self.fullscreen.is_none()
}
pub fn is_floating<S>(&self, surface: &S) -> bool
@ -1630,11 +1573,8 @@ impl Workspace {
};
let focused = self.focus_stack.get(last_active_seat).last().cloned();
let render_fullscreen = |fullscreen: &FullscreenSurface,
renderer: &mut R,
output_scale: f64|
-> Vec<WorkspaceRenderElement<R>> {
let fullscreen_geo = self.fullscreen_geometry_for(fullscreen);
let mut fullscreen_elements = if let Some(fullscreen) = self.fullscreen.as_ref() {
let fullscreen_geo = self.fullscreen_geometry().unwrap();
let previous_geo = fullscreen
.previous_geometry
.as_ref()
@ -1704,35 +1644,20 @@ impl Workspace {
.into_iter()
.map(animation_rescale)
.collect::<Vec<_>>()
} else {
Vec::new()
};
let top_fullscreen = self.get_fullscreen(last_active_seat);
let mut fullscreen_elements: Vec<WorkspaceRenderElement<R>> = Vec::new();
if let Some(fs) = top_fullscreen {
fullscreen_elements.extend(render_fullscreen(fs, renderer, output_scale));
}
// Also render any animating (entering/exiting) fullscreens
for fs in self.fullscreen_surfaces.iter().filter(|f| f.is_animating()) {
if top_fullscreen.is_none_or(|top| top.surface != fs.surface) {
fullscreen_elements.extend(render_fullscreen(fs, renderer, output_scale));
};
}
if matches!(focused, Some(FocusTarget::Fullscreen(_))) {
elements.append(&mut fullscreen_elements);
}
let any_fullscreen_animating = self
.fullscreen_surfaces
.iter()
.any(|f| f.start_at.is_some() || f.ended_at.is_some());
if !matches!(focused, Some(FocusTarget::Fullscreen(_)))
|| any_fullscreen_animating
|| self
.fullscreen_surfaces
.iter()
.all(|f| !f.alive() || f.ended_at.is_some())
.fullscreen
.as_ref()
.map(|f| f.start_at.is_some() || f.ended_at.is_some())
.unwrap_or(true)
{
// floating surfaces
let alpha = match &overview.0 {
@ -1757,17 +1682,13 @@ impl Workspace {
self.floating_layer
.render::<R>(
renderer,
render_focus
.then(|| {
focused.as_ref().and_then(|target| {
if let FocusTarget::Window(mapped) = target {
Some(mapped)
} else {
None
}
})
})
.flatten(),
focused.as_ref().and_then(|target| {
if let FocusTarget::Window(mapped) = target {
Some(mapped)
} else {
None
}
}),
resize_indicator.clone(),
indicator_thickness,
alpha,
@ -1853,12 +1774,8 @@ impl Workspace {
layer_map.non_exclusive_zone().as_local()
};
// Render popups for the top (most recently focused) fullscreen
let focus_stack = self.focus_stack.get(last_active_seat);
let top_fullscreen = self.get_fullscreen(last_active_seat);
if let Some(fullscreen) = top_fullscreen {
let fullscreen_geo = self.fullscreen_geometry_for(fullscreen);
if let Some(fullscreen) = self.fullscreen.as_ref() {
let fullscreen_geo = self.fullscreen_geometry().unwrap();
let previous_geo = fullscreen
.previous_geometry
.as_ref()
@ -1915,16 +1832,13 @@ impl Workspace {
);
}
let any_fullscreen_animating = self
.fullscreen_surfaces
.iter()
.any(|f| f.start_at.is_some() || f.ended_at.is_some());
let focus_stack = self.focus_stack.get(last_active_seat);
if !matches!(focus_stack.last(), Some(FocusTarget::Fullscreen(_)))
|| any_fullscreen_animating
|| self
.fullscreen_surfaces
.iter()
.all(|f| !f.alive() || f.ended_at.is_some())
.fullscreen
.as_ref()
.map(|f| f.start_at.is_some() || f.ended_at.is_some())
.unwrap_or(true)
{
// floating surfaces
let alpha = match &overview.0 {

View file

@ -22,8 +22,8 @@ use smithay::{
MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent,
},
touch::{
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
TouchTarget, UpEvent,
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
UpEvent,
},
},
output::Output,
@ -259,8 +259,10 @@ impl ZoomState {
original_position: Point<f64, Global>,
movement: ZoomMovement,
) {
let output_geometry = output.geometry().to_f64();
let mut zoomed_output_geometry = output.zoomed_geometry().unwrap().to_f64();
let cursor_position = cursor_position.to_i32_round();
let original_position = original_position.to_i32_round();
let output_geometry = output.geometry();
let mut zoomed_output_geometry = output.zoomed_geometry().unwrap();
let output_state = output.user_data().get::<Mutex<OutputZoomState>>().unwrap();
let mut output_state_ref = output_state.lock().unwrap();
@ -273,40 +275,44 @@ impl ZoomState {
let cursor_position = cursor_position.to_local(output);
match movement {
ZoomMovement::Continuously => output_state_ref.focal_point = cursor_position,
ZoomMovement::Continuously => output_state_ref.focal_point = cursor_position.to_f64(),
ZoomMovement::OnEdge => {
if !zoomed_output_geometry
.overlaps_or_touches(Rectangle::new(original_position, Size::from((16., 16.))))
.overlaps_or_touches(Rectangle::new(original_position, Size::from((16, 16))))
{
zoomed_output_geometry.loc = cursor_position.to_global(output)
- zoomed_output_geometry.size.downscale(2.).to_point();
- zoomed_output_geometry.size.downscale(2).to_point();
let mut focal_point = zoomed_output_geometry
.loc
.to_local(output)
.upscale(output_state_ref.level)
.upscale(
output_geometry.size.w
/ (output_geometry.size.w - zoomed_output_geometry.size.w),
)
.to_global(output);
focal_point.x = focal_point.x.clamp(
output_geometry.loc.x,
output_geometry.loc.x + output_geometry.size.w - 1.,
output_geometry.loc.x + output_geometry.size.w - 1,
);
focal_point.y = focal_point.y.clamp(
output_geometry.loc.y,
output_geometry.loc.y + output_geometry.size.h - 1.,
output_geometry.loc.y + output_geometry.size.h - 1,
);
output_state_ref.previous_point =
Some((output_state_ref.focal_point, Instant::now()));
output_state_ref.focal_point = focal_point.to_local(output);
output_state_ref.focal_point = focal_point.to_local(output).to_f64();
} else if !zoomed_output_geometry.contains(cursor_position.to_global(output)) {
let mut diff = output_state_ref.focal_point.to_global(output)
+ (cursor_position.to_global(output) - original_position)
.to_f64()
.upscale(output_state_ref.level);
diff.x = diff.x.clamp(
output_geometry.loc.x,
(output_geometry.loc.x + output_geometry.size.w).next_down(),
output_geometry.loc.x as f64,
((output_geometry.loc.x + output_geometry.size.w) as f64).next_down(),
);
diff.y = diff.y.clamp(
output_geometry.loc.y,
(output_geometry.loc.y + output_geometry.size.h).next_down(),
output_geometry.loc.y as f64,
((output_geometry.loc.y + output_geometry.size.h) as f64).next_down(),
);
diff -= output_state_ref.focal_point.to_global(output);
@ -315,27 +321,28 @@ impl ZoomState {
}
ZoomMovement::Centered => {
zoomed_output_geometry.loc = cursor_position.to_global(output)
- zoomed_output_geometry.size.downscale(2.).to_point();
- zoomed_output_geometry.size.downscale(2).to_point();
let mut focal_point = zoomed_output_geometry
.loc
.to_local(output)
.upscale(
(output_geometry.size.w
/ (output_geometry.size.w - zoomed_output_geometry.size.w)
.max(f64::EPSILON))
.max(1.),
output_geometry
.size
.w
.checked_div(output_geometry.size.w - zoomed_output_geometry.size.w)
.unwrap_or(1),
)
.to_global(output);
focal_point.x = focal_point.x.clamp(
output_geometry.loc.x,
output_geometry.loc.x + output_geometry.size.w - 1.,
output_geometry.loc.x + output_geometry.size.w - 1,
);
focal_point.y = focal_point.y.clamp(
output_geometry.loc.y,
output_geometry.loc.y + output_geometry.size.h - 1.,
output_geometry.loc.y + output_geometry.size.h - 1,
);
output_state_ref.focal_point = focal_point.to_local(output);
output_state_ref.focal_point = focal_point.to_local(output).to_f64();
}
}
}
@ -1029,59 +1036,58 @@ impl PointerTarget<State> for ZoomFocusTarget {
}
impl TouchTarget<State> for ZoomFocusTarget {
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent) {
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent, seq: Serial) {
match self {
ZoomFocusTarget::Main(elem) => TouchTarget::down(elem, seat, data, event),
ZoomFocusTarget::Menu(elem) => TouchTarget::down(elem, seat, data, event),
ZoomFocusTarget::Main(elem) => TouchTarget::down(elem, seat, data, event, seq),
ZoomFocusTarget::Menu(elem) => TouchTarget::down(elem, seat, data, event, seq),
}
}
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent) {
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent, seq: Serial) {
match self {
ZoomFocusTarget::Main(elem) => TouchTarget::up(elem, seat, data, event),
ZoomFocusTarget::Menu(elem) => TouchTarget::up(elem, seat, data, event),
ZoomFocusTarget::Main(elem) => TouchTarget::up(elem, seat, data, event, seq),
ZoomFocusTarget::Menu(elem) => TouchTarget::up(elem, seat, data, event, seq),
}
}
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent) {
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent, seq: Serial) {
match self {
ZoomFocusTarget::Main(elem) => TouchTarget::motion(elem, seat, data, event),
ZoomFocusTarget::Menu(elem) => TouchTarget::motion(elem, seat, data, event),
ZoomFocusTarget::Main(elem) => TouchTarget::motion(elem, seat, data, event, seq),
ZoomFocusTarget::Menu(elem) => TouchTarget::motion(elem, seat, data, event, seq),
}
}
fn frame(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
fn frame(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
match self {
ZoomFocusTarget::Main(elem) => TouchTarget::frame(elem, seat, data, frame),
ZoomFocusTarget::Menu(elem) => TouchTarget::frame(elem, seat, data, frame),
ZoomFocusTarget::Main(elem) => TouchTarget::frame(elem, seat, data, seq),
ZoomFocusTarget::Menu(elem) => TouchTarget::frame(elem, seat, data, seq),
}
}
fn cancel(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
fn cancel(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
match self {
ZoomFocusTarget::Main(elem) => TouchTarget::cancel(elem, seat, data, frame),
ZoomFocusTarget::Menu(elem) => TouchTarget::cancel(elem, seat, data, frame),
ZoomFocusTarget::Main(elem) => TouchTarget::cancel(elem, seat, data, seq),
ZoomFocusTarget::Menu(elem) => TouchTarget::cancel(elem, seat, data, seq),
}
}
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent) {
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent, seq: Serial) {
match self {
ZoomFocusTarget::Main(elem) => TouchTarget::shape(elem, seat, data, event),
ZoomFocusTarget::Menu(elem) => TouchTarget::shape(elem, seat, data, event),
ZoomFocusTarget::Main(elem) => TouchTarget::shape(elem, seat, data, event, seq),
ZoomFocusTarget::Menu(elem) => TouchTarget::shape(elem, seat, data, event, seq),
}
}
fn orientation(&self, seat: &Seat<State>, data: &mut State, event: &OrientationEvent) {
fn orientation(
&self,
seat: &Seat<State>,
data: &mut State,
event: &OrientationEvent,
seq: Serial,
) {
match self {
ZoomFocusTarget::Main(elem) => TouchTarget::orientation(elem, seat, data, event),
ZoomFocusTarget::Menu(elem) => TouchTarget::orientation(elem, seat, data, event),
}
}
fn last_frame(&self, seat: &Seat<State>, data: &mut State) -> Option<FrameMarker> {
match self {
ZoomFocusTarget::Main(elem) => TouchTarget::last_frame(elem, seat, data),
ZoomFocusTarget::Menu(elem) => TouchTarget::last_frame(elem, seat, data),
ZoomFocusTarget::Main(elem) => TouchTarget::orientation(elem, seat, data, event, seq),
ZoomFocusTarget::Menu(elem) => TouchTarget::orientation(elem, seat, data, event, seq),
}
}
}

View file

@ -8,7 +8,7 @@ use crate::{
x11::X11State,
},
config::{CompOutputConfig, Config, ScreenFilter},
dbus::DBusState,
dbus::a11y_keyboard_monitor::A11yKeyboardMonitorState,
input::{PointerFocusState, gestures::GestureState},
shell::{CosmicSurface, SeatExt, Shell, grabs::SeatMoveGrabState},
utils::prelude::OutputExt,
@ -32,6 +32,7 @@ use crate::{
use anyhow::Context;
use calloop::RegistrationToken;
use cosmic_comp_config::output::comp::{OutputConfig, OutputState};
use futures_executor::ThreadPool;
use i18n_embed::{
DesktopLanguageRequester,
fluent::{FluentLanguageLoader, fluent_language_loader},
@ -86,7 +87,6 @@ use smithay::{
output::OutputManagerState,
pointer_constraints::PointerConstraintsState,
pointer_gestures::PointerGesturesState,
pointer_warp::PointerWarpManager,
presentation::PresentationState,
seat::WaylandFocus,
security_context::{SecurityContext, SecurityContextState},
@ -117,7 +117,7 @@ use smithay::{
};
use tracing::warn;
#[cfg(feature = "logind")]
#[cfg(feature = "systemd")]
use std::os::fd::OwnedFd;
use std::{
@ -234,6 +234,7 @@ pub struct Common {
pub display_handle: DisplayHandle,
pub event_loop_handle: LoopHandle<'static, State>,
pub event_loop_signal: LoopSignal,
pub async_executor: ThreadPool,
pub popups: PopupManager,
pub shell: Arc<parking_lot::RwLock<Shell>>,
@ -278,7 +279,7 @@ pub struct Common {
pub xdg_decoration_state: XdgDecorationState,
pub overlap_notify_state: OverlapNotifyState,
pub a11y_state: A11yState,
pub dbus_state: DBusState,
pub a11y_keyboard_monitor_state: A11yKeyboardMonitorState,
// shell-related wayland state
pub xdg_shell_state: XdgShellState,
@ -293,7 +294,7 @@ pub struct Common {
pub xwayland_shell_state: XWaylandShellState,
pub pointer_focus_state: Option<PointerFocusState>,
#[cfg(feature = "logind")]
#[cfg(feature = "systemd")]
pub inhibit_lid_fd: Option<OwnedFd>,
pub with_xwayland: bool,
@ -672,7 +673,6 @@ impl State {
XWaylandKeyboardGrabState::new::<Self>(dh);
let xwayland_shell_state = XWaylandShellState::new::<Self>(dh);
PointerConstraintsState::new::<Self>(dh);
PointerWarpManager::new::<Self>(dh);
PointerGesturesState::new::<Self>(dh);
TabletManagerState::new::<Self>(dh);
SecurityContextState::new::<Self, _>(dh, client_has_no_security_context);
@ -727,9 +727,15 @@ impl State {
);
let workspace_state = WorkspaceState::new(dh, client_not_sandboxed);
let async_executor = ThreadPool::builder().pool_size(1).create().unwrap();
if let Err(err) = crate::dbus::init(&handle, &async_executor) {
tracing::warn!(?err, "Failed to initialize dbus handlers");
}
let a11y_state = A11yState::new::<State, _>(dh, client_not_sandboxed);
let dbus_state = DBusState::init(&handle);
let a11y_keyboard_monitor_state = A11yKeyboardMonitorState::new(&async_executor);
State {
common: Common {
@ -738,6 +744,7 @@ impl State {
display_handle: dh.clone(),
event_loop_handle: handle,
event_loop_signal: signal,
async_executor,
popups: PopupManager::default(),
shell,
@ -787,13 +794,13 @@ impl State {
xdg_foreign_state,
workspace_state,
a11y_state,
a11y_keyboard_monitor_state,
xwayland_scale: None,
xwayland_state: None,
xwayland_shell_state,
pointer_focus_state: None,
dbus_state,
#[cfg(feature = "logind")]
#[cfg(feature = "systemd")]
inhibit_lid_fd: None,
with_xwayland,
@ -818,7 +825,7 @@ impl State {
}
fn update_inhibitor_locks(&mut self) {
#[cfg(feature = "logind")]
#[cfg(feature = "systemd")]
{
use smithay::backend::session::Session;
use tracing::{debug, error, warn};
@ -834,7 +841,7 @@ impl State {
if should_handle_lid {
if self.common.inhibit_lid_fd.is_none() {
match crate::dbus::logind::inhibit_lid(&self.common) {
match crate::dbus::logind::inhibit_lid() {
Ok(fd) => {
debug!("Inhibiting lid switch");
self.common.inhibit_lid_fd = Some(fd);
@ -845,8 +852,7 @@ impl State {
.iter()
.find(|o| o.is_internal())
.cloned();
let closed =
crate::dbus::logind::lid_closed(&self.common).unwrap_or(false);
let closed = crate::dbus::logind::lid_closed().unwrap_or(false);
if closed {
backend.disable_internal_output(
@ -990,8 +996,8 @@ impl Common {
// normal windows
for space in shell.workspaces.spaces() {
if let Some(fs) = space.get_fullscreen(shell.seats.last_active()) {
fs.surface.with_surfaces(processor);
if let Some(window) = space.get_fullscreen() {
window.with_surfaces(processor);
}
space.mapped().for_each(|mapped| {
for (window, _) in mapped.windows() {
@ -1156,16 +1162,15 @@ impl Common {
});
if let Some(active) = shell.active_space(output) {
if let Some(fs) = active.get_fullscreen(shell.seats.last_active())
&& let Some(feedback) = fs
.surface
if let Some(window) = active.get_fullscreen()
&& let Some(feedback) = window
.wl_surface()
.and_then(|wl_surface| {
advertised_node_for_surface(&wl_surface, &self.display_handle)
})
.and_then(&mut dmabuf_feedback)
{
fs.surface.send_dmabuf_feedback(
window.send_dmabuf_feedback(
output,
&feedback,
render_element_states,
@ -1351,9 +1356,8 @@ impl Common {
});
if let Some(active) = shell.active_space(output) {
if let Some(fs) = active.get_fullscreen(shell.seats.last_active()) {
fs.surface
.send_frame(output, time, throttle(&fs.surface), should_send);
if let Some(window) = active.get_fullscreen() {
window.send_frame(output, time, throttle(window), should_send);
}
active.mapped().for_each(|mapped| {
for (window, _) in mapped.windows() {
@ -1373,9 +1377,9 @@ impl Common {
.spaces_for_output(output)
.filter(|w| w.handle != active.handle)
{
if let Some(fs) = space.get_fullscreen(shell.seats.last_active()) {
let throttle = min(throttle(space), throttle(&fs.surface));
fs.surface.send_frame(output, time, throttle, |_, _| None);
if let Some(window) = space.get_fullscreen() {
let throttle = min(throttle(space), throttle(window));
window.send_frame(output, time, throttle, |_, _| None);
}
space.mapped().for_each(|mapped| {
for (window, _) in mapped.windows() {

View file

@ -51,12 +51,13 @@ use smithay::{
PointerTarget, RelativeMotionEvent,
},
touch::{
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
TouchTarget, UpEvent,
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
UpEvent,
},
},
output::Output,
reexports::calloop::{self, LoopHandle, RegistrationToken, futures::Scheduler},
reexports::calloop::RegistrationToken,
reexports::calloop::{self, LoopHandle, futures::Scheduler},
utils::{
Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size,
Transform,
@ -179,8 +180,6 @@ pub(crate) struct IcedElementInternal<P: Program + Send + 'static> {
last_seat: Arc<Mutex<Option<(Seat<crate::state::State>, Serial)>>>,
cursor_pos: Option<Point<f64, Logical>>,
touch_map: HashMap<Finger, IcedPoint>,
last_touch_frame: Option<FrameMarker>,
last_touch_serial: Option<Serial>,
// iced
theme: Theme,
@ -229,8 +228,6 @@ impl<P: Program + Send + Clone + 'static> Clone for IcedElementInternal<P> {
last_seat: self.last_seat.clone(),
cursor_pos: self.cursor_pos,
touch_map: self.touch_map.clone(),
last_touch_frame: None,
last_touch_serial: None,
theme: self.theme.clone(),
renderer,
state,
@ -256,8 +253,6 @@ impl<P: Program + Send + 'static> fmt::Debug for IcedElementInternal<P> {
.field("last_seat", &self.last_seat)
.field("cursor_pos", &self.cursor_pos)
.field("touch_map", &self.touch_map)
.field("last_touch_frame", &self.last_touch_frame)
.field("last_touch_serial", &self.last_touch_serial)
.field("theme", &"...")
.field("renderer", &"...")
.field("state", &"...")
@ -314,8 +309,6 @@ impl<P: Program + Send + 'static> IcedElement<P> {
cursor_pos: None,
last_seat,
touch_map: HashMap::new(),
last_touch_frame: None,
last_touch_serial: None,
theme,
renderer,
state,
@ -640,6 +633,7 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
seat: &Seat<crate::state::State>,
_data: &mut crate::state::State,
event: &DownEvent,
seq: Serial,
) {
let mut internal = self.0.lock().unwrap();
let id = Finger(i32::from(event.slot) as u64);
@ -650,8 +644,7 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
.queue_event(Event::Touch(TouchEvent::FingerPressed { id, position }));
internal.touch_map.insert(id, position);
internal.cursor_pos = Some(event_location);
internal.last_touch_serial = Some(event.serial);
*internal.last_seat.lock().unwrap() = Some((seat.clone(), event.serial));
*internal.last_seat.lock().unwrap() = Some((seat.clone(), seq));
internal.update(false);
}
@ -660,12 +653,12 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
seat: &Seat<crate::state::State>,
_data: &mut crate::state::State,
event: &UpEvent,
seq: Serial,
) {
let mut internal = self.0.lock().unwrap();
let id = Finger(i32::from(event.slot) as u64);
if let Some(position) = internal.touch_map.remove(&id) {
*internal.last_seat.lock().unwrap() =
Some((seat.clone(), internal.last_touch_serial.unwrap()));
*internal.last_seat.lock().unwrap() = Some((seat.clone(), seq));
internal
.state
.queue_event(Event::Touch(TouchEvent::FingerLifted { id, position }));
@ -678,13 +671,13 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
seat: &Seat<crate::state::State>,
_data: &mut crate::state::State,
event: &TouchMotionEvent,
seq: Serial,
) {
let mut internal = self.0.lock().unwrap();
let id = Finger(i32::from(event.slot) as u64);
let event_location = event.location.downscale(internal.additional_scale);
let position = IcedPoint::new(event_location.x as f32, event_location.y as f32);
*internal.last_seat.lock().unwrap() =
Some((seat.clone(), internal.last_touch_serial.unwrap()));
*internal.last_seat.lock().unwrap() = Some((seat.clone(), seq));
internal
.state
.queue_event(Event::Touch(TouchEvent::FingerMoved { id, position }));
@ -697,19 +690,17 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
&self,
_seat: &Seat<crate::state::State>,
_data: &mut crate::state::State,
frame: FrameMarker,
_seq: Serial,
) {
self.0.lock().unwrap().last_touch_frame = Some(frame);
}
fn cancel(
&self,
_seat: &Seat<crate::state::State>,
_data: &mut crate::state::State,
frame: FrameMarker,
_seq: Serial,
) {
let mut internal = self.0.lock().unwrap();
internal.last_touch_frame = Some(frame);
for (id, position) in std::mem::take(&mut internal.touch_map) {
internal
.state
@ -723,6 +714,7 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
_seat: &Seat<crate::state::State>,
_data: &mut crate::state::State,
_event: &ShapeEvent,
_seq: Serial,
) {
}
@ -731,16 +723,9 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
_seat: &Seat<crate::state::State>,
_data: &mut crate::state::State,
_event: &OrientationEvent,
_seq: Serial,
) {
}
fn last_frame(
&self,
_seat: &Seat<crate::state::State>,
_data: &mut crate::state::State,
) -> Option<FrameMarker> {
self.0.lock().unwrap().last_touch_frame
}
}
impl<P: Program + Send + 'static> KeyboardTarget<crate::state::State> for IcedElement<P> {

View file

@ -1,9 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::{
backend::renderer::utils::with_renderer_surface_state, desktop::layer_map_for_output,
output::Output,
};
use smithay::{desktop::layer_map_for_output, output::Output};
/// Layer shell namespace used by `cosmic-workspaces`
// TODO: Avoid special case, or add protocol to expose required behavior
@ -13,14 +10,5 @@ pub const WORKSPACE_OVERVIEW_NAMESPACE: &str = "cosmic-workspace-overview";
pub fn workspace_overview_is_open(output: &Output) -> bool {
layer_map_for_output(output)
.layers()
.filter(|s| s.namespace() == WORKSPACE_OVERVIEW_NAMESPACE)
// Only consider the overview open once it has committed a buffer. The
// surface is inserted into the layer map on its initial (bufferless)
// commit, so checking for the namespace alone hides all toplevels for a
// frame before the overview has anything to draw, briefly flashing the
// bare wallpaper.
.any(|s| {
with_renderer_surface_state(s.wl_surface(), |state| state.buffer().is_some())
.unwrap_or(false)
})
.any(|s| s.namespace() == WORKSPACE_OVERVIEW_NAMESPACE)
}

View file

@ -51,11 +51,9 @@ impl DmabufHandler for State {
let is_fullscreen = shell
.workspaces
.space_for_handle(&handle)?
.fullscreen_surfaces
.iter()
.any(|f| {
f.ended_at.is_none() && f.surface.has_surface(surface, WindowSurfaceType::all())
});
.fullscreen
.as_ref()
.is_some_and(|f| f.surface.has_surface(surface, WindowSurfaceType::all()));
let node = kms
.drm_devices

View file

@ -753,15 +753,11 @@ pub fn render_cursor_to_buffer(
seat: &Seat<State>,
) {
let buffer = frame.buffer();
let mut cursor_size = seat
let cursor_size = seat
.cursor_geometry((0.0, 0.0), state.common.clock.now())
.map(|(geo, _hotspot)| geo.size)
.unwrap_or_else(|| Size::from((64, 64)));
let buffer_size = buffer_dimensions(&buffer).unwrap();
// Client shouldn't try to allocate 0x0 buffer
if cursor_size == Size::new(0, 0) {
cursor_size = Size::new(1, 1);
}
if buffer_size != cursor_size {
let constraints = BufferConstraints {
size: cursor_size,

View file

@ -25,7 +25,6 @@ pub mod output_configuration;
pub mod output_power;
pub mod overlap_notify;
pub mod pointer_constraints;
pub mod pointer_warp;
pub mod primary_selection;
pub mod seat;
pub mod security_context;

View file

@ -1,123 +1,35 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{shell::CosmicSurface, state::State, utils::prelude::*};
use crate::state::State;
use smithay::{
input::pointer::PointerHandle,
reexports::wayland_server::protocol::wl_surface::WlSurface,
utils::{Logical, Point},
wayland::{pointer_constraints::PointerConstraintsHandler, seat::WaylandFocus},
wayland::{
pointer_constraints::{PointerConstraintsHandler, with_pointer_constraint},
seat::WaylandFocus,
},
};
pub use smithay::wayland::pointer_constraints::{PointerConstraintRef, with_pointer_constraint};
impl PointerConstraintsHandler for State {
fn new_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle<Self>) {
let seat = self
.common
.shell
.read()
.seats
.iter()
.find(|s| s.get_pointer().as_ref() == Some(pointer))
.cloned();
let (is_under, is_focused, surface_location) = if let Some(seat) = seat {
seat.set_pointer_constraint_hint(None);
let current_output = seat.active_output();
let position = seat.get_pointer().unwrap().current_location().as_global();
let shell = self.common.shell.read();
let under = State::surface_under(position, &current_output, &shell);
let mut surface_location = None;
let is_under = if let Some((target, target_loc)) = under
&& let Some(under_surface) = target.wl_surface()
{
if *under_surface == *surface {
surface_location = Some(target_loc);
true
} else {
CosmicSurface::surface_tree_offset(surface, &under_surface).is_some_and(
|offset| {
surface_location = Some(target_loc - offset.to_f64().as_global());
true
},
)
}
} else {
false
};
let is_focused = seat
.get_keyboard()
.and_then(|k| k.current_focus())
.is_some_and(|f| f.has_surface(&shell, surface));
(is_under, is_focused, surface_location)
} else {
(false, false, None)
};
if is_focused && is_under {
// XXX region
if pointer
.current_focus()
.is_some_and(|x| x.wl_surface().as_deref() == Some(surface))
{
with_pointer_constraint(surface, pointer, |constraint| {
if let Some(constraint) = constraint {
if let Some(region) = constraint.region() {
if let Some(surface_location) = surface_location
&& let position = pointer.current_location()
&& let point = (position - surface_location.as_logical()).to_i32_round()
&& region.contains(point)
{
constraint.activate();
}
} else {
constraint.activate();
}
}
constraint.unwrap().activate();
});
}
}
fn remove_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle<Self>) {
if with_pointer_constraint(surface, pointer, |constraint| constraint.is_none()) {
let seat = self
.common
.shell
.read()
.seats
.iter()
.find(|s| s.get_pointer().as_ref() == Some(pointer))
.cloned();
if let Some(seat) = seat
&& let Some((hint_surface, hint_location)) = seat.pointer_constraint_hint()
&& hint_surface == *surface
{
self.apply_cursor_hint(surface, pointer, hint_location);
seat.set_pointer_constraint_hint(None);
}
}
}
fn cursor_position_hint(
&mut self,
surface: &WlSurface,
pointer: &PointerHandle<Self>,
location: Point<f64, Logical>,
_surface: &WlSurface,
_pointer: &PointerHandle<Self>,
_location: Point<f64, Logical>,
) {
if with_pointer_constraint(surface, pointer, |constraint| {
constraint.is_some_and(|c| c.is_active())
}) {
let seat = self
.common
.shell
.read()
.seats
.iter()
.find(|s| s.get_pointer().as_ref() == Some(pointer))
.cloned();
if let Some(seat) = seat {
seat.set_pointer_constraint_hint(Some((surface.clone(), location)));
}
}
// TODO
}
}

View file

@ -1,44 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::state::State;
use smithay::{
input::pointer::PointerHandle,
reexports::wayland_server::protocol::{wl_pointer::WlPointer, wl_surface::WlSurface},
utils::{Logical, Point, Serial},
wayland::pointer_warp::PointerWarpHandler,
};
impl PointerWarpHandler for State {
fn warp_pointer(
&mut self,
surface: WlSurface,
pointer: WlPointer,
pos: Point<f64, Logical>,
serial: Serial,
) {
let Some(resource_handle) = PointerHandle::<State>::from_resource(&pointer) else {
return;
};
let shell = self.common.shell.read();
let pointer_handle = shell.seats.iter().find_map(|seat| {
if let Some(pointer_handle) = seat.get_pointer()
&& resource_handle == pointer_handle
&& pointer_handle.last_enter() == Some(serial)
&& let Some(keyboard) = seat.get_keyboard()
&& let Some(keyboard_focus) = keyboard.current_focus()
&& keyboard_focus.has_surface(&shell, &surface)
{
return Some(pointer_handle);
}
None
});
drop(shell);
if let Some(pointer_handle) = pointer_handle {
self.apply_cursor_hint(&surface, &pointer_handle, pos);
}
}
}

View file

@ -42,7 +42,7 @@ impl ToplevelManagementHandler for State {
.spaces_for_output(output)
.enumerate()
.find(|(_, w)| {
w.get_fullscreen_surfaces().any(|f| &f.surface == window)
w.get_fullscreen().is_some_and(|f| f == window)
|| w.mapped()
.flat_map(|m| m.windows().map(|(s, _)| s))
.any(|w| &w == window)

View file

@ -5,7 +5,6 @@ use crate::{
state::State,
wayland::protocols::workspace::{State as WState, WorkspaceHandle},
};
use cosmic_comp_config::ActivationPolicy;
use smithay::{
input::Seat,
reexports::wayland_server::protocol::wl_surface::WlSurface,
@ -21,10 +20,6 @@ pub enum ActivationContext {
Workspace(WorkspaceHandle),
}
// It may happen that we get activation-requests, while all outputs are disabled.
// In these cases we won't be able to determine workspaces for windows and thus
// need to handle the corresponding code paths defensively.
impl XdgActivationHandler for State {
fn activation_state(&mut self) -> &mut XdgActivationState {
&mut self.common.xdg_activation_state
@ -56,12 +51,7 @@ impl XdgActivationHandler for State {
});
let output = seat.active_output();
let mut shell = self.common.shell.write();
let Some(workspace) = shell.active_space_mut(&output) else {
debug!(?token, "created urgent token for privileged client");
data.user_data
.insert_if_missing(move || ActivationContext::UrgentOnly);
return true;
};
let workspace = shell.active_space_mut(&output).unwrap();
let handle = workspace.handle;
data.user_data
.insert_if_missing(move || ActivationContext::Workspace(handle));
@ -95,11 +85,7 @@ impl XdgActivationHandler for State {
if valid {
let output = seat.active_output();
let mut shell = self.common.shell.write();
let Some(workspace) = shell.active_space_mut(&output) else {
data.user_data
.insert_if_missing(|| ActivationContext::UrgentOnly);
return true;
};
let workspace = shell.active_space_mut(&output).unwrap();
let handle = workspace.handle;
data.user_data
.insert_if_missing(move || ActivationContext::Workspace(handle));
@ -129,44 +115,10 @@ impl XdgActivationHandler for State {
}
}
ActivationContext::Workspace(_) => {
match self.common.config.cosmic_conf.activation_policy {
ActivationPolicy::Focus => {
self.activate_surface(
&surface,
Some((ActivationKey::Wayland(surface.clone()), *context)),
);
}
ActivationPolicy::FocusIfActiveWorkspace => {
let shell = self.common.shell.write();
let Some((target_workspace, _)) = shell.workspace_for_surface(&surface)
else {
return;
};
let seat = shell.seats.last_active().clone();
let current_output = seat.active_output();
let current_workspace = shell.active_space(&current_output).unwrap().handle;
if target_workspace == current_workspace {
std::mem::drop(shell);
self.activate_surface(
&surface,
Some((ActivationKey::Wayland(surface.clone()), *context)),
);
} else {
let mut workspace_guard = self.common.workspace_state.update();
workspace_guard.add_workspace_state(&target_workspace, WState::Urgent);
}
}
ActivationPolicy::Urgent => {
let shell = self.common.shell.write();
if let Some((workspace, _output)) = shell.workspace_for_surface(&surface) {
let mut workspace_guard = self.common.workspace_state.update();
workspace_guard.add_workspace_state(&workspace, WState::Urgent);
}
}
}
self.activate_surface(
&surface,
Some((ActivationKey::Wayland(surface.clone()), *context)),
);
}
}
}
@ -267,8 +219,8 @@ impl State {
.workspaces
.space_for_handle(&workspace)
.unwrap()
.get_fullscreen(&seat)
.map(|f| f.surface.clone())
.get_fullscreen()
.cloned()
.map(KeyboardFocusTarget::Fullscreen)
else {
return;
@ -280,8 +232,8 @@ impl State {
if let Some(surface) = shell
.workspaces
.space_for_handle(&workspace)
.and_then(|w| w.get_fullscreen(&seat))
.map(|f| f.surface.clone())
.and_then(|w| w.get_fullscreen())
.cloned()
{
shell.append_focus_stack(surface, &seat)
}

View file

@ -68,7 +68,7 @@ impl Shell {
unconstrain_xdg_popup(surface, window_loc, output.geometry());
}
} else if let Some(output) = self.workspaces.spaces().find_map(|w| {
w.fullscreen_surfaces.iter().find_map(|f| {
w.fullscreen.as_ref().and_then(|f| {
(f.surface.wl_surface().as_deref() == Some(&parent)).then_some(w.output())
})
}) {

View file

@ -495,18 +495,35 @@ where
.unwrap()
.lock()
.unwrap();
let foreign_toplevel_handle = state.foreign_handle.as_ref();
let mut changed = false;
let new_title = (handle_state.title != window.title()).then(|| window.title());
let new_app_id = (handle_state.app_id != window.app_id()).then(|| window.app_id());
if handle_state.title != window.title() {
handle_state.title = window.title();
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
instance.title(handle_state.title.clone());
}
if let Some(handle) = foreign_toplevel_handle {
handle.send_title(&handle_state.title);
}
changed = true;
}
if handle_state.app_id != window.app_id() {
handle_state.app_id = window.app_id();
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
instance.app_id(handle_state.app_id.clone());
}
if let Some(handle) = foreign_toplevel_handle {
handle.send_app_id(&handle_state.app_id);
}
changed = true;
}
let new_states = if handle_state.states.as_ref().is_none_or(|states| {
if handle_state.states.as_ref().is_none_or(|states| {
(states.contains(&States::Maximized) != window.is_maximized())
|| (states.contains(&States::Fullscreen) != window.is_fullscreen())
|| (states.contains(&States::Activated) != window.is_activated())
|| (states.contains(&States::Minimized) != window.is_minimized())
|| (instance.version() >= zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE
&& states.contains(&States::Sticky) != window.is_sticky())
}) {
let mut states = Vec::new();
if window.is_maximized() {
@ -526,64 +543,8 @@ where
{
states.push(States::Sticky);
}
Some(states)
} else {
None
};
let geometry_changed = if !window.is_resizing() {
let geometry = window.global_geometry();
if handle_state.geometry != geometry {
handle_state.geometry = geometry;
true
} else {
false
}
} else {
false
};
let outputs_changed = state.outputs != handle_state.outputs
|| handle_state.wl_outputs.iter().any(|o| !o.is_alive());
let workspaces_changed = state.workspaces != handle_state.workspaces;
if new_title.is_none()
&& new_app_id.is_none()
&& new_states.is_none()
&& !geometry_changed
&& !outputs_changed
&& !workspaces_changed
{
return false;
}
let foreign_toplevel_handle = state.foreign_handle.as_ref();
if let Some(title) = new_title {
handle_state.title = title;
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
instance.title(handle_state.title.clone());
}
if let Some(handle) = foreign_toplevel_handle {
handle.send_title(&handle_state.title);
}
changed = true;
}
if let Some(app_id) = new_app_id {
handle_state.app_id = app_id;
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
instance.app_id(handle_state.app_id.clone());
}
if let Some(handle) = foreign_toplevel_handle {
handle.send_app_id(&handle_state.app_id);
}
changed = true;
}
if let Some(states) = new_states {
handle_state.states = Some(states.clone());
let states = states
.iter()
.flat_map(|state| (*state as u32).to_ne_bytes())
@ -592,9 +553,17 @@ where
changed = true;
}
if (outputs_changed || geometry_changed)
&& let Ok(client) = dh.get_client(instance.id())
{
let mut geometry_changed = false;
if !window.is_resizing() {
let geometry = window.global_geometry();
if handle_state.geometry != geometry {
handle_state.geometry = geometry;
changed = true;
geometry_changed = true;
}
}
if let Ok(client) = dh.get_client(instance.id()) {
handle_state.outputs = state.outputs.clone();
let handle_state = &mut *handle_state;
@ -630,29 +599,27 @@ where
});
}
if workspaces_changed {
for new_workspace in state
.workspaces
.iter()
.filter(|w| !handle_state.workspaces.contains(w))
{
for handle in workspace_state.raw_ext_workspace_handles(new_workspace, &instance.id()) {
instance.ext_workspace_enter(handle);
changed = true;
}
for new_workspace in state
.workspaces
.iter()
.filter(|w| !handle_state.workspaces.contains(w))
{
for handle in workspace_state.raw_ext_workspace_handles(new_workspace, &instance.id()) {
instance.ext_workspace_enter(handle);
changed = true;
}
for old_workspace in handle_state
.workspaces
.iter()
.filter(|w| !state.workspaces.contains(w))
{
for handle in workspace_state.raw_ext_workspace_handles(old_workspace, &instance.id()) {
instance.ext_workspace_leave(handle);
changed = true;
}
}
handle_state.workspaces = state.workspaces.clone();
}
for old_workspace in handle_state
.workspaces
.iter()
.filter(|w| !state.workspaces.contains(w))
{
for handle in workspace_state.raw_ext_workspace_handles(old_workspace, &instance.id()) {
instance.ext_workspace_leave(handle);
changed = true;
}
}
handle_state.workspaces = state.workspaces.clone();
if changed {
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {

View file

@ -237,10 +237,11 @@ where
window_from_handle::<<D as ToplevelInfoHandler>::Window>(toplevel).unwrap();
if let Some(toplevel_state) = window.user_data().get::<ToplevelState>() {
let mut toplevel_state = toplevel_state.lock().unwrap();
toplevel_state
.rectangles
.retain(|(s, _)| s.id() != surface.id());
if width != 0 || height != 0 {
if width == 0 && height == 0 {
toplevel_state
.rectangles
.retain(|(s, _)| s.id() != surface.id());
} else {
toplevel_state.rectangles.push((
surface.downgrade(),
Rectangle::new((x, y).into(), (width, height).into()),

View file

@ -115,7 +115,6 @@ impl State {
&self.common.display_handle,
None,
std::iter::empty::<(OsString, OsString)>(),
std::iter::empty::<OsString>(),
true,
Stdio::null(),
Stdio::null(),
@ -576,15 +575,16 @@ impl Common {
.filter(|(i, _)| *i != set.active),
)
.flat_map(|(_, workspace)| {
let focus_last =
workspace.focus_stack.get(seat).last().cloned();
workspace
.get_fullscreen_surfaces()
.get_fullscreen()
.filter(|f| {
focus_last.as_ref().is_some_and(|t| t == &f.surface)
workspace
.focus_stack
.get(seat)
.last()
.is_some_and(|t| &t == f)
})
.map(|f| f.surface.clone())
.collect::<Vec<_>>()
.cloned()
.into_iter()
.chain(workspace.mapped().flat_map(|mapped| {
let active = mapped.active_window();
@ -603,14 +603,15 @@ impl Common {
}))
.chain(
workspace
.get_fullscreen_surfaces()
.get_fullscreen()
.filter(|f| {
focus_last
.as_ref()
.is_none_or(|t| t != &f.surface)
workspace
.focus_stack
.get(seat)
.last()
.is_none_or(|t| &t != f)
})
.map(|f| f.surface.clone())
.collect::<Vec<_>>()
.cloned()
.into_iter(),
)
.chain(