Compare commits
12 commits
e65b97abb8
...
1dda5a558e
| Author | SHA1 | Date | |
|---|---|---|---|
| 1dda5a558e | |||
| 11e229fa44 | |||
|
|
84f9dcbe0e | ||
|
|
4a22c9f3dc | ||
| 647a672b25 | |||
| d038b6f89f | |||
| e3d5e4668f | |||
| 0ae782638c | |||
| 7b05b2e768 | |||
| 267fcd80b3 | |||
| 56230f0ade | |||
| 934db82775 |
54 changed files with 1600 additions and 2387 deletions
818
Cargo.lock
generated
818
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
14
Cargo.toml
14
Cargo.toml
|
|
@ -12,12 +12,13 @@ members = ["cosmic-comp-config"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { version = "1.0.102", features = ["backtrace"] }
|
anyhow = { version = "1.0.102", features = ["backtrace"] }
|
||||||
bitflags = "2.11.0"
|
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 = [
|
cosmic-comp-config = { path = "cosmic-comp-config", features = [
|
||||||
"libdisplay-info",
|
"libdisplay-info",
|
||||||
"output",
|
"output",
|
||||||
] }
|
] }
|
||||||
cosmic-config = { path = "../libcosmic/cosmic-config", features = [
|
cosmic-config = { path = "../libcosmic/cosmic-config", features = [
|
||||||
|
"async-std",
|
||||||
"calloop",
|
"calloop",
|
||||||
"macro",
|
"macro",
|
||||||
] }
|
] }
|
||||||
|
|
@ -43,7 +44,7 @@ iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" }
|
||||||
indexmap = "2.13"
|
indexmap = "2.13"
|
||||||
keyframe = "1.1.1"
|
keyframe = "1.1.1"
|
||||||
cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [
|
cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [
|
||||||
"tokio",
|
"async-std",
|
||||||
"wayland",
|
"wayland",
|
||||||
"multi-window",
|
"multi-window",
|
||||||
] }
|
] }
|
||||||
|
|
@ -77,7 +78,6 @@ xdg = "^3.0"
|
||||||
xdg-user = "0.2.1"
|
xdg-user = "0.2.1"
|
||||||
xkbcommon = "0.9"
|
xkbcommon = "0.9"
|
||||||
zbus = "5.14.0"
|
zbus = "5.14.0"
|
||||||
tokio = { version = "1", features = ["rt-multi-thread"] }
|
|
||||||
profiling = { version = "1.0" }
|
profiling = { version = "1.0" }
|
||||||
rustix = { version = "1.1.4", features = ["process"] }
|
rustix = { version = "1.1.4", features = ["process"] }
|
||||||
rand = "0.10"
|
rand = "0.10"
|
||||||
|
|
@ -85,9 +85,10 @@ rand = "0.10"
|
||||||
clap_lex = "1.0"
|
clap_lex = "1.0"
|
||||||
parking_lot = "0.12.5"
|
parking_lot = "0.12.5"
|
||||||
logind-zbus = { version = "5.3.2", optional = true }
|
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"
|
futures-util = "0.3.32"
|
||||||
cgmath = "0.18.0"
|
cgmath = "0.18.0"
|
||||||
|
smallvec = "1.15.1"
|
||||||
|
|
||||||
[dependencies.id_tree]
|
[dependencies.id_tree]
|
||||||
branch = "feature/copy_clone"
|
branch = "feature/copy_clone"
|
||||||
|
|
@ -122,8 +123,7 @@ optional = true
|
||||||
[features]
|
[features]
|
||||||
debug = ["egui", "egui_plot", "smithay-egui", "anyhow/backtrace"]
|
debug = ["egui", "egui_plot", "smithay-egui", "anyhow/backtrace"]
|
||||||
default = ["systemd"]
|
default = ["systemd"]
|
||||||
logind = ["logind-zbus"]
|
systemd = ["libsystemd", "logind-zbus"]
|
||||||
systemd = ["libsystemd", "logind"]
|
|
||||||
profile-with-tracy = ["profiling/profile-with-tracy", "tracy-client/default"]
|
profile-with-tracy = ["profiling/profile-with-tracy", "tracy-client/default"]
|
||||||
profile-with-tracy-gpu = ["profile-with-tracy", "smithay/tracy_gpu_profiling"]
|
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" }
|
mime = { path = "../window_clipboard/mime" }
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
smithay = { git = "https://github.com/smithay/smithay.git", rev = "8eb4076" }
|
smithay = { git = "https://github.com/smithay/smithay.git", rev = "774f2ab" }
|
||||||
|
|
|
||||||
1
Makefile
1
Makefile
|
|
@ -39,7 +39,6 @@ vendor:
|
||||||
mkdir -p .cargo
|
mkdir -p .cargo
|
||||||
cargo vendor | head -n -1 > .cargo/config
|
cargo vendor | head -n -1 > .cargo/config
|
||||||
echo 'directory = "vendor"' >> .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
|
tar pcf vendor.tar vendor
|
||||||
rm -rf vendor
|
rm -rf vendor
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,6 @@ pub struct CosmicCompConfig {
|
||||||
pub appearance_settings: AppearanceConfig,
|
pub appearance_settings: AppearanceConfig,
|
||||||
/// Hide the cursor after this many seconds of pointer inactivity (None disables)
|
/// Hide the cursor after this many seconds of pointer inactivity (None disables)
|
||||||
pub cursor_hide_timeout: Option<u32>,
|
pub cursor_hide_timeout: Option<u32>,
|
||||||
pub activation_policy: ActivationPolicy,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CosmicCompConfig {
|
impl Default for CosmicCompConfig {
|
||||||
|
|
@ -139,7 +138,6 @@ impl Default for CosmicCompConfig {
|
||||||
accessibility_zoom: ZoomConfig::default(),
|
accessibility_zoom: ZoomConfig::default(),
|
||||||
appearance_settings: AppearanceConfig::default(),
|
appearance_settings: AppearanceConfig::default(),
|
||||||
cursor_hide_timeout: None,
|
cursor_hide_timeout: None,
|
||||||
activation_policy: ActivationPolicy::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -229,14 +227,6 @@ pub enum EavesdroppingKeyboardMode {
|
||||||
All,
|
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)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum XwaylandDescaling {
|
pub enum XwaylandDescaling {
|
||||||
|
|
|
||||||
|
|
@ -63,5 +63,5 @@ pub struct PinnedWorkspace {
|
||||||
pub output: OutputMatch,
|
pub output: OutputMatch,
|
||||||
pub tiling_enabled: bool,
|
pub tiling_enabled: bool,
|
||||||
pub id: Option<String>,
|
pub id: Option<String>,
|
||||||
pub name: Option<String>,
|
// TODO: name
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,5 @@
|
||||||
(appid: "zoom", titles: [".*"]),
|
(appid: "zoom", titles: [".*"]),
|
||||||
(appid: "Tor Browser", titles: [".*"]),
|
(appid: "Tor Browser", titles: [".*"]),
|
||||||
(appid: "^.*?action=join.*$", titles: [".*"]),
|
(appid: "^.*?action=join.*$", titles: [".*"]),
|
||||||
(appid: "^(slack|com.slack.Slack)", titles: ["^.*?(Huddle Preview).*"]),
|
|
||||||
(appid: "^(thunderbird|org.mozilla.thunderbird)(-esr|_esr)*", titles: ["^(Write:).*"]),
|
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@ pub struct Device {
|
||||||
pub inner: InnerDevice,
|
pub inner: InnerDevice,
|
||||||
pub drm: GbmDrmOutputManager,
|
pub drm: GbmDrmOutputManager,
|
||||||
|
|
||||||
|
supports_atomic: bool,
|
||||||
pub texture_formats: FormatSet,
|
pub texture_formats: FormatSet,
|
||||||
event_token: Option<RegistrationToken>,
|
event_token: Option<RegistrationToken>,
|
||||||
pub socket: Option<Socket>,
|
pub socket: Option<Socket>,
|
||||||
|
|
@ -710,6 +711,7 @@ impl Device {
|
||||||
let (drm, notifier) = DrmDevice::new(fd.clone(), false)
|
let (drm, notifier) = DrmDevice::new(fd.clone(), false)
|
||||||
.with_context(|| format!("Failed to initialize drm device for: {}", path.display()))?;
|
.with_context(|| format!("Failed to initialize drm device for: {}", path.display()))?;
|
||||||
let dev_node = DrmNode::from_dev_id(dev)?;
|
let dev_node = DrmNode::from_dev_id(dev)?;
|
||||||
|
let supports_atomic = drm.is_atomic();
|
||||||
|
|
||||||
let gbm = GbmDevice::new(fd)
|
let gbm = GbmDevice::new(fd)
|
||||||
.with_context(|| format!("Failed to initialize GBM device for {}", path.display()))?;
|
.with_context(|| format!("Failed to initialize GBM device for {}", path.display()))?;
|
||||||
|
|
@ -829,6 +831,7 @@ impl Device {
|
||||||
active_clients,
|
active_clients,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
supports_atomic,
|
||||||
texture_formats,
|
texture_formats,
|
||||||
event_token: Some(token),
|
event_token: Some(token),
|
||||||
socket,
|
socket,
|
||||||
|
|
@ -837,7 +840,8 @@ impl Device {
|
||||||
|
|
||||||
pub fn enumerate_surfaces(&mut self) -> Result<OutputChanges> {
|
pub fn enumerate_surfaces(&mut self) -> Result<OutputChanges> {
|
||||||
// enumerate our outputs
|
// 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
|
let surfaces = self
|
||||||
.inner
|
.inner
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
use anyhow::{Context, Result, anyhow};
|
use anyhow::{Context, Result, anyhow};
|
||||||
use libdisplay_info::{edid::DisplayDescriptorTag, info::Info};
|
use libdisplay_info::{edid::DisplayDescriptorTag, info::Info};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::drm::DrmDevice,
|
|
||||||
reexports::drm::control::{
|
reexports::drm::control::{
|
||||||
AtomicCommitFlags, Device as ControlDevice, Mode, ModeFlags, PlaneType, ResourceHandle,
|
AtomicCommitFlags, Device as ControlDevice, Mode, ModeFlags, PlaneType, ResourceHandle,
|
||||||
atomic::AtomicModeReq,
|
atomic::AtomicModeReq,
|
||||||
|
|
@ -17,7 +16,8 @@ use smithay::{
|
||||||
use std::{collections::HashMap, ops::Range};
|
use std::{collections::HashMap, ops::Range};
|
||||||
|
|
||||||
pub fn display_configuration(
|
pub fn display_configuration(
|
||||||
device: &mut DrmDevice,
|
device: &mut impl ControlDevice,
|
||||||
|
supports_atomic: bool,
|
||||||
) -> Result<HashMap<connector::Handle, Option<crtc::Handle>>> {
|
) -> Result<HashMap<connector::Handle, Option<crtc::Handle>>> {
|
||||||
let res_handles = device.resource_handles()?;
|
let res_handles = device.resource_handles()?;
|
||||||
let connectors = res_handles.connectors();
|
let connectors = res_handles.connectors();
|
||||||
|
|
@ -73,10 +73,28 @@ pub fn display_configuration(
|
||||||
}
|
}
|
||||||
|
|
||||||
// And then cleanup
|
// And then cleanup
|
||||||
if device.is_atomic() {
|
if supports_atomic {
|
||||||
let mut req = AtomicModeReq::new();
|
let mut req = AtomicModeReq::new();
|
||||||
let plane_handles = device.plane_handles()?;
|
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.
|
// 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.)
|
// (Technically a device does not need to be atomic for planes to be used, but nobody does this otherwise.)
|
||||||
for plane in plane_handles {
|
for plane in plane_handles {
|
||||||
|
|
@ -90,7 +108,7 @@ pub fn display_configuration(
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
if !is_primary && !cleanup.contains(&crtc) {
|
if cleanup.contains(&crtc) || !is_primary {
|
||||||
let crtc_id = get_prop(device, plane, "CRTC_ID")?;
|
let crtc_id = get_prop(device, plane, "CRTC_ID")?;
|
||||||
let fb_id = get_prop(device, plane, "FB_ID")?;
|
let fb_id = get_prop(device, plane, "FB_ID")?;
|
||||||
req.add_property(plane, crtc_id, property::Value::CRTC(None));
|
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)?;
|
device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?;
|
||||||
} else {
|
} else {
|
||||||
for crtc in res_handles.crtcs() {
|
for crtc in res_handles.crtcs() {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
let _ = device.set_cursor(*crtc, Option::<&DumbBuffer>::None);
|
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)
|
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> {
|
pub fn interface_name(device: &impl ControlDevice, connector: connector::Handle) -> Result<String> {
|
||||||
let conn_info = device.get_connector(connector, false)?;
|
let conn_info = device.get_connector(connector, false)?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -374,7 +374,6 @@ impl State {
|
||||||
for device in backend.drm_devices.values_mut() {
|
for device in backend.drm_devices.values_mut() {
|
||||||
if let Err(err) = device.drm.lock().activate(true) {
|
if let Err(err) = device.drm.lock().activate(true) {
|
||||||
error!(?err, "Failed to resume drm device");
|
error!(?err, "Failed to resume drm device");
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if let Some(lease_state) = device.inner.leasing_global.as_mut() {
|
if let Some(lease_state) = device.inner.leasing_global.as_mut() {
|
||||||
lease_state.resume::<State>();
|
lease_state.resume::<State>();
|
||||||
|
|
@ -852,23 +851,11 @@ impl KmsGuard<'_> {
|
||||||
|
|
||||||
// first drop old surfaces
|
// first drop old surfaces
|
||||||
if !test_only {
|
if !test_only {
|
||||||
let mut disabled_crtcs = Vec::new();
|
for output in outputs.iter().filter(|o| !o.is_enabled()) {
|
||||||
device.inner.surfaces.retain(|crtc, surface| {
|
device
|
||||||
if outputs
|
.inner
|
||||||
.iter()
|
.surfaces
|
||||||
.any(|o| !o.is_enabled() && surface.output == *o)
|
.retain(|_, surface| surface.output != *output);
|
||||||
{
|
|
||||||
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}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use clap_lex::OsStrExt;
|
use smithay::backend::{
|
||||||
use rustix::path::Arg;
|
|
||||||
use smithay::{
|
|
||||||
backend::{
|
|
||||||
SwapBuffersError,
|
SwapBuffersError,
|
||||||
allocator::{
|
allocator::{
|
||||||
Allocator,
|
Allocator,
|
||||||
|
|
@ -13,18 +10,12 @@ use smithay::{
|
||||||
drm::{CreateDrmNodeError, DrmNode},
|
drm::{CreateDrmNodeError, DrmNode},
|
||||||
renderer::{
|
renderer::{
|
||||||
RendererSuper,
|
RendererSuper,
|
||||||
gles::{GlesError, GlesRenderer, ffi},
|
gles::{GlesError, GlesRenderer},
|
||||||
glow::GlowRenderer,
|
glow::GlowRenderer,
|
||||||
multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
|
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::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt,
|
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 Device = GbmGlowDevice;
|
||||||
type Error = Error;
|
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())
|
.any(|renderer| renderer.node.dev_id() == node.dev_id())
|
||||||
})
|
})
|
||||||
.flat_map(|(node, (allocator, renderer))| {
|
.flat_map(|(node, (allocator, renderer))| {
|
||||||
let mut renderer = renderer.replace(None)?;
|
let 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")
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
Some(GbmGlowDevice {
|
Some(GbmGlowDevice {
|
||||||
node: *node,
|
node: *node,
|
||||||
renderer,
|
renderer,
|
||||||
intel_export_quirk,
|
|
||||||
allocator: Box::new(DmabufAllocator(allocator.clone())),
|
allocator: Box::new(DmabufAllocator(allocator.clone())),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -177,7 +150,6 @@ impl<A: AsFd + Device + Clone + 'static> GraphicsApi for GbmGlowBackend<A> {
|
||||||
pub struct GbmGlowDevice {
|
pub struct GbmGlowDevice {
|
||||||
node: DrmNode,
|
node: DrmNode,
|
||||||
renderer: GlowRenderer,
|
renderer: GlowRenderer,
|
||||||
intel_export_quirk: bool,
|
|
||||||
allocator: Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
|
allocator: Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,14 +180,9 @@ impl ApiDevice for GbmGlowDevice {
|
||||||
fn can_do_cross_device_imports(&self) -> bool {
|
fn can_do_cross_device_imports(&self) -> bool {
|
||||||
!Borrow::<GlesRenderer>::borrow(&self.renderer).is_software()
|
!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
|
impl<T: GraphicsApi, A: AsFd + Clone + 'static> FromGlesError for MultiError<GbmGlowBackend<A>, T>
|
||||||
for MultiError<GbmGlowBackend<A>, T>
|
|
||||||
where
|
where
|
||||||
T::Error: 'static,
|
T::Error: 'static,
|
||||||
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
|
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
|
||||||
|
|
|
||||||
|
|
@ -1024,15 +1024,11 @@ impl SurfaceThreadState {
|
||||||
let animations_going = shell.animations_going();
|
let animations_going = shell.animations_going();
|
||||||
let output = self.mirroring.as_ref().unwrap_or(&self.output);
|
let output = self.mirroring.as_ref().unwrap_or(&self.output);
|
||||||
if let Some((_, workspace)) = shell.workspaces.active(output) {
|
if let Some((_, workspace)) = shell.workspaces.active(output) {
|
||||||
let seat = shell.seats.last_active();
|
if let Some(fullscreen_surface) = workspace.get_fullscreen() {
|
||||||
if let Some(fullscreen_surface) = workspace.get_fullscreen(seat) {
|
|
||||||
const _30_FPS: Duration = Duration::from_nanos(1_000_000_000 / 30);
|
const _30_FPS: Duration = Duration::from_nanos(1_000_000_000 / 30);
|
||||||
(
|
(
|
||||||
true,
|
true,
|
||||||
fullscreen_surface
|
fullscreen_surface.wl_surface().is_some_and(|surface| {
|
||||||
.surface
|
|
||||||
.wl_surface()
|
|
||||||
.is_some_and(|surface| {
|
|
||||||
recursive_frame_time_estimation(&self.clock, &surface)
|
recursive_frame_time_estimation(&self.clock, &surface)
|
||||||
.is_some_and(|dur| dur <= _30_FPS)
|
.is_some_and(|dur| dur <= _30_FPS)
|
||||||
}),
|
}),
|
||||||
|
|
@ -1494,18 +1490,15 @@ fn render_node_for_output(
|
||||||
let Some(workspace) = shell.active_space(output) else {
|
let Some(workspace) = shell.active_space(output) else {
|
||||||
return *target_node;
|
return *target_node;
|
||||||
};
|
};
|
||||||
let fullscreens: Vec<_> = workspace
|
let nodes = workspace
|
||||||
.get_fullscreen_surfaces()
|
.get_fullscreen()
|
||||||
.map(|f| f.surface.clone())
|
.map(|w| vec![w.clone()])
|
||||||
.collect();
|
.unwrap_or_else(|| {
|
||||||
let nodes = if !fullscreens.is_empty() {
|
|
||||||
fullscreens
|
|
||||||
} else {
|
|
||||||
workspace
|
workspace
|
||||||
.mapped()
|
.mapped()
|
||||||
.map(|mapped| mapped.active_window())
|
.map(|mapped| mapped.active_window())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
})
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s)))
|
.flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ use smithay::{
|
||||||
calloop::{EventLoop, ping},
|
calloop::{EventLoop, ping},
|
||||||
wayland_protocols::wp::presentation_time::server::wp_presentation_feedback,
|
wayland_protocols::wp::presentation_time::server::wp_presentation_feedback,
|
||||||
wayland_server::DisplayHandle,
|
wayland_server::DisplayHandle,
|
||||||
winit::event_loop::pump_events::PumpStatus,
|
winit::platform::pump_events::PumpStatus,
|
||||||
},
|
},
|
||||||
utils::Transform,
|
utils::Transform,
|
||||||
wayland::{dmabuf::DmabufFeedbackBuilder, presentation::Refresh},
|
wayland::{dmabuf::DmabufFeedbackBuilder, presentation::Refresh},
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ mod types;
|
||||||
use cosmic::config::CosmicTk;
|
use cosmic::config::CosmicTk;
|
||||||
pub use cosmic_comp_config::EdidProduct;
|
pub use cosmic_comp_config::EdidProduct;
|
||||||
use cosmic_comp_config::{
|
use cosmic_comp_config::{
|
||||||
ActivationPolicy, AppearanceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior, XkbConfig,
|
AppearanceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior, XkbConfig, XwaylandDescaling,
|
||||||
XwaylandDescaling, XwaylandEavesdropping, ZoomConfig,
|
XwaylandEavesdropping, ZoomConfig,
|
||||||
input::{DeviceState as InputDeviceState, InputConfig, TouchpadOverride},
|
input::{DeviceState as InputDeviceState, InputConfig, TouchpadOverride},
|
||||||
output::comp::{
|
output::comp::{
|
||||||
OutputConfig, OutputInfo, OutputState, OutputsConfig, TransformDef, load_outputs,
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
// https://gitlab.gnome.org/GNOME/mutter/-/blob/main/data/dbus-interfaces/org.freedesktop.a11y.xml
|
// https://gitlab.gnome.org/GNOME/mutter/-/blob/main/data/dbus-interfaces/org.freedesktop.a11y.xml
|
||||||
|
|
||||||
|
use futures_executor::ThreadPool;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::input::KeyState,
|
backend::input::KeyState,
|
||||||
input::keyboard::{KeysymHandle, ModifiersState},
|
input::keyboard::{KeysymHandle, ModifiersState},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex, OnceLock},
|
||||||
};
|
};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use xkbcommon::xkb::Keysym;
|
use xkbcommon::xkb::Keysym;
|
||||||
|
|
@ -71,37 +72,40 @@ impl Clients {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct A11yKeyboardMonitorState {
|
pub struct A11yKeyboardMonitorState {
|
||||||
executor: calloop::futures::Scheduler<()>,
|
executor: ThreadPool,
|
||||||
clients: Arc<Mutex<Clients>>,
|
clients: Arc<Mutex<Clients>>,
|
||||||
active_virtual_mods: HashSet<Keysym>,
|
active_virtual_mods: HashSet<Keysym>,
|
||||||
conn: zbus::Connection,
|
conn: Arc<OnceLock<zbus::Connection>>,
|
||||||
name_owners: NameOwners,
|
name_owners: Arc<OnceLock<NameOwners>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl A11yKeyboardMonitorState {
|
impl A11yKeyboardMonitorState {
|
||||||
pub async fn new(
|
pub fn new(executor: &ThreadPool) -> Self {
|
||||||
conn: &zbus::Connection,
|
|
||||||
name_owners: &NameOwners,
|
|
||||||
executor: &calloop::futures::Scheduler<()>,
|
|
||||||
) -> zbus::Result<Self> {
|
|
||||||
let clients = Arc::new(Mutex::new(Clients::default()));
|
let clients = Arc::new(Mutex::new(Clients::default()));
|
||||||
|
let clients_clone = clients.clone();
|
||||||
let keyboard_monitor = KeyboardMonitor {
|
let conn_cell = Arc::new(OnceLock::new());
|
||||||
clients: clients.clone(),
|
let conn_cell_clone = conn_cell.clone();
|
||||||
name_owners: name_owners.clone(),
|
let name_owners_cell = Arc::new(OnceLock::new());
|
||||||
};
|
let name_owners_cell_clone = name_owners_cell.clone();
|
||||||
conn.object_server()
|
let executor_clone = executor.clone();
|
||||||
.at("/org/freedesktop/a11y/Manager", keyboard_monitor)
|
executor.spawn_ok(async move {
|
||||||
.await?;
|
match serve(clients_clone, &executor_clone).await {
|
||||||
conn.request_name("org.freedesktop.a11y.Manager").await?;
|
Ok((conn, name_owners)) => {
|
||||||
|
conn_cell_clone.set(conn).unwrap();
|
||||||
Ok(Self {
|
name_owners_cell_clone.set(name_owners).unwrap();
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
tracing::error!("Failed to serve `org.freedesktop.a11y.Manager`: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Self {
|
||||||
executor: executor.clone(),
|
executor: executor.clone(),
|
||||||
clients,
|
clients,
|
||||||
active_virtual_mods: HashSet::new(),
|
active_virtual_mods: HashSet::new(),
|
||||||
conn: conn.clone(),
|
conn: conn_cell,
|
||||||
name_owners: name_owners.clone(),
|
name_owners: name_owners_cell,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_virtual_mod(&self, keysym: Keysym) -> bool {
|
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) {
|
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();
|
let clients = self.clients.lock().unwrap();
|
||||||
for (unique_name, client) in clients.0.iter() {
|
for (unique_name, client) in clients.0.iter() {
|
||||||
if !client.watched && !self.has_key_grab(modifiers, keysym.modified_sym()) {
|
if !client.watched && !self.has_key_grab(modifiers, keysym.modified_sym()) {
|
||||||
|
|
@ -157,7 +165,7 @@ impl A11yKeyboardMonitorState {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut signal_context =
|
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
|
// Instead of sending signal to all clients, send only to authorized
|
||||||
// clients with registed watches.
|
// clients with registed watches.
|
||||||
signal_context = signal_context.set_destination(unique_name.clone().into());
|
signal_context = signal_context.set_destination(unique_name.clone().into());
|
||||||
|
|
@ -178,7 +186,7 @@ impl A11yKeyboardMonitorState {
|
||||||
unichar,
|
unichar,
|
||||||
keysym.raw_code().raw() as u16,
|
keysym.raw_code().raw() as u16,
|
||||||
);
|
);
|
||||||
let _ = self.executor.schedule(async {
|
self.executor.spawn_ok(async {
|
||||||
let _ = future.await;
|
let _ = future.await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -187,11 +195,13 @@ impl A11yKeyboardMonitorState {
|
||||||
pub fn refresh(&mut self) {
|
pub fn refresh(&mut self) {
|
||||||
// Remove clients and associated grabs when unique names are no longer
|
// Remove clients and associated grabs when unique names are no longer
|
||||||
// present on bus, or no longer hold approved name on bus.
|
// present on bus, or no longer hold approved name on bus.
|
||||||
|
if let Some(name_owners) = self.name_owners.get() {
|
||||||
self.clients
|
self.clients
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.0
|
.0
|
||||||
.retain(|k, _| self.name_owners.check_owner_no_poll(k, ALLOWED_NAMES))
|
.retain(|k, _| name_owners.check_owner_no_poll(k, ALLOWED_NAMES))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,3 +311,20 @@ impl KeyboardMonitor {
|
||||||
keycode: u16,
|
keycode: u16,
|
||||||
) -> zbus::Result<()>;
|
) -> 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))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,24 @@
|
||||||
use std::os::fd::OwnedFd;
|
use std::os::fd::OwnedFd;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
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() -> Result<OwnedFd> {
|
||||||
|
let conn = Connection::system()?;
|
||||||
pub fn inhibit_lid(common: &Common) -> Result<OwnedFd> {
|
let proxy = ManagerProxyBlocking::new(&conn)?;
|
||||||
let fd = futures_executor::block_on(async {
|
let fd = proxy.inhibit(
|
||||||
let conn = common.dbus_state.system_conn().await?;
|
|
||||||
let manager = ManagerProxy::new(conn).await?;
|
|
||||||
manager
|
|
||||||
.inhibit(
|
|
||||||
HandleLidSwitch,
|
HandleLidSwitch,
|
||||||
"cosmic-comp",
|
"cosmic-comp",
|
||||||
"External output connected",
|
"External output connected",
|
||||||
"block",
|
"block",
|
||||||
)
|
)?;
|
||||||
.await
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(fd.into())
|
Ok(fd.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lid_closed(common: &Common) -> Result<bool> {
|
pub fn lid_closed() -> Result<bool> {
|
||||||
futures_executor::block_on(async {
|
let conn = Connection::system()?;
|
||||||
let conn = common.dbus_state.system_conn().await?;
|
let proxy = ManagerProxyBlocking::new(&conn)?;
|
||||||
let manager = ManagerProxy::new(conn).await?;
|
proxy.lid_closed().context("Failed to talk to logind")
|
||||||
manager
|
|
||||||
.lid_closed()
|
|
||||||
.await
|
|
||||||
.context("Failed to talk to logind")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
141
src/dbus/mod.rs
141
src/dbus/mod.rs
|
|
@ -3,111 +3,33 @@ use crate::{
|
||||||
utils::prelude::OutputExt,
|
utils::prelude::OutputExt,
|
||||||
};
|
};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use calloop::{InsertError, LoopHandle, stream::StreamSource};
|
use calloop::{InsertError, LoopHandle, RegistrationToken};
|
||||||
use cosmic_comp_config::output::comp::OutputState;
|
use cosmic_comp_config::output::comp::OutputState;
|
||||||
use std::{
|
use futures_executor::{ThreadPool, block_on};
|
||||||
cell::{RefCell, RefMut},
|
use futures_util::stream::StreamExt;
|
||||||
collections::HashMap,
|
use std::collections::HashMap;
|
||||||
rc::Rc,
|
|
||||||
};
|
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
|
use zbus::blocking::{Connection, fdo::DBusProxy};
|
||||||
|
|
||||||
pub mod a11y_keyboard_monitor;
|
pub mod a11y_keyboard_monitor;
|
||||||
use a11y_keyboard_monitor::A11yKeyboardMonitorState;
|
#[cfg(feature = "systemd")]
|
||||||
#[cfg(feature = "logind")]
|
|
||||||
pub mod logind;
|
pub mod logind;
|
||||||
mod name_owners;
|
mod name_owners;
|
||||||
mod power;
|
mod power;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
pub fn init(
|
||||||
pub struct DBusState(Rc<DBusStateInner>);
|
evlh: &LoopHandle<'static, State>,
|
||||||
|
executor: &ThreadPool,
|
||||||
|
) -> Result<Vec<RegistrationToken>> {
|
||||||
|
let mut tokens = Vec::new();
|
||||||
|
|
||||||
#[derive(Debug)]
|
match block_on(power::init()) {
|
||||||
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 {
|
|
||||||
Ok(power_daemon) => {
|
Ok(power_daemon) => {
|
||||||
if let Ok(stream) = power_daemon.receive_hot_plug_detect().await {
|
let (tx, rx) = calloop::channel::channel();
|
||||||
let source = StreamSource::new(stream).unwrap();
|
|
||||||
evlh.insert_source(source, |_, _, state| {
|
let token = evlh
|
||||||
|
.insert_source(rx, |event, _, state| match event {
|
||||||
|
calloop::channel::Event::Msg(_) => {
|
||||||
let nodes = match &mut state.backend {
|
let nodes = match &mut state.backend {
|
||||||
BackendData::Kms(kms) => {
|
BackendData::Kms(kms) => {
|
||||||
kms.drm_devices.keys().cloned().collect::<Vec<_>>()
|
kms.drm_devices.keys().cloned().collect::<Vec<_>>()
|
||||||
|
|
@ -134,26 +56,40 @@ async fn power_hot_plug_task(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
calloop::channel::Event::Closed => (),
|
||||||
})
|
})
|
||||||
.map_err(|InsertError { error, .. }| error)
|
.map_err(|InsertError { error, .. }| error)
|
||||||
.with_context(|| "Failed to add channel to event_loop")?;
|
.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) => {
|
Err(err) => {
|
||||||
tracing::info!(?err, "Failed to connect to com.system76.PowerDaemon");
|
tracing::info!(?err, "Failed to connect to com.system76.PowerDaemon");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(())
|
|
||||||
|
Ok(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updated the D-Bus activation environment with `WAYLAND_DISPLAY` and
|
/// Updated the D-Bus activation environment with `WAYLAND_DISPLAY` and
|
||||||
/// `DISPLAY` variables.
|
/// `DISPLAY` variables.
|
||||||
pub fn ready(common: &Common) -> Result<()> {
|
pub fn ready(common: &Common) -> Result<()> {
|
||||||
futures_executor::block_on(async {
|
let conn = Connection::session()?;
|
||||||
let conn = common.dbus_state.session_conn().await?;
|
let proxy = DBusProxy::new(&conn)?;
|
||||||
let dbus = zbus::fdo::DBusProxy::new(conn).await?;
|
|
||||||
|
|
||||||
dbus.update_activation_environment(HashMap::from([
|
proxy.update_activation_environment(HashMap::from([
|
||||||
("WAYLAND_DISPLAY", common.socket.to_str().unwrap()),
|
("WAYLAND_DISPLAY", common.socket.to_str().unwrap()),
|
||||||
(
|
(
|
||||||
"DISPLAY",
|
"DISPLAY",
|
||||||
|
|
@ -163,8 +99,7 @@ pub fn ready(common: &Common) -> Result<()> {
|
||||||
.map(|s| format!(":{}", s.display))
|
.map(|s| format!(":{}", s.display))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
),
|
),
|
||||||
]))
|
]))?;
|
||||||
.await
|
|
||||||
})?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,9 @@
|
||||||
//!
|
//!
|
||||||
//! Compare to Mutter's `MetaDbusAccessChecker`
|
//! Compare to Mutter's `MetaDbusAccessChecker`
|
||||||
|
|
||||||
use futures_util::{
|
use futures_executor::ThreadPool;
|
||||||
StreamExt,
|
use futures_util::stream::FusedStream;
|
||||||
stream::{FusedStream, FuturesUnordered},
|
use futures_util::{StreamExt, stream::FuturesUnordered};
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
future::{Future, poll_fn},
|
future::{Future, poll_fn},
|
||||||
|
|
@ -95,10 +94,7 @@ fn update_task(inner: Weak<Mutex<Inner>>) -> impl Future<Output = ()> {
|
||||||
pub struct NameOwners(Arc<Mutex<Inner>>);
|
pub struct NameOwners(Arc<Mutex<Inner>>);
|
||||||
|
|
||||||
impl NameOwners {
|
impl NameOwners {
|
||||||
pub async fn new(
|
pub async fn new(connection: &zbus::Connection, executor: &ThreadPool) -> zbus::Result<Self> {
|
||||||
connection: &zbus::Connection,
|
|
||||||
executor: &calloop::futures::Scheduler<()>,
|
|
||||||
) -> zbus::Result<Self> {
|
|
||||||
let dbus = fdo::DBusProxy::new(connection).await?;
|
let dbus = fdo::DBusProxy::new(connection).await?;
|
||||||
let stream = dbus.receive_name_owner_changed().await?;
|
let stream = dbus.receive_name_owner_changed().await?;
|
||||||
|
|
||||||
|
|
@ -130,7 +126,7 @@ impl NameOwners {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if enforce {
|
if enforce {
|
||||||
let _ = executor.schedule(update_task(Arc::downgrade(&inner)));
|
executor.spawn_ok(update_task(Arc::downgrade(&inner)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(NameOwners(inner))
|
Ok(NameOwners(inner))
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,9 @@ pub trait PowerDaemon {
|
||||||
fn power_profile_switch(&self, profile: &str) -> zbus::Result<()>;
|
fn power_profile_switch(&self, profile: &str) -> zbus::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn init(conn: &Connection) -> anyhow::Result<PowerDaemonProxy<'static>> {
|
pub async fn init() -> anyhow::Result<PowerDaemonProxy<'static>> {
|
||||||
let proxy = PowerDaemonProxy::new(conn).await?;
|
let conn = Connection::system().await?;
|
||||||
|
let proxy = PowerDaemonProxy::new(&conn).await?;
|
||||||
proxy.0.introspect().await?;
|
proxy.0.introspect().await?;
|
||||||
Ok(proxy)
|
Ok(proxy)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
312
src/input/mod.rs
312
src/input/mod.rs
|
|
@ -52,21 +52,16 @@ use smithay::{
|
||||||
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent,
|
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent,
|
||||||
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,
|
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,
|
||||||
GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent,
|
GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent,
|
||||||
PointerGrab, PointerHandle, RelativeMotionEvent,
|
PointerGrab, RelativeMotionEvent,
|
||||||
},
|
},
|
||||||
touch::{DownEvent, MotionEvent as TouchMotionEvent, UpEvent},
|
touch::{DownEvent, MotionEvent as TouchMotionEvent, UpEvent},
|
||||||
},
|
},
|
||||||
output::Output,
|
output::Output,
|
||||||
reexports::{
|
reexports::{
|
||||||
input::Device as InputDevice,
|
input::Device as InputDevice, wayland_server::protocol::wl_shm::Format as ShmFormat,
|
||||||
wayland_server::{
|
|
||||||
Resource as _,
|
|
||||||
protocol::{wl_shm::Format as ShmFormat, wl_surface::WlSurface},
|
|
||||||
},
|
},
|
||||||
},
|
utils::{Point, Rectangle, SERIAL_COUNTER, Serial},
|
||||||
utils::{Logical, Point, Rectangle, SERIAL_COUNTER, Serial, Size},
|
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::CompositorHandler,
|
|
||||||
image_copy_capture::{BufferConstraints, CursorSessionRef},
|
image_copy_capture::{BufferConstraints, CursorSessionRef},
|
||||||
keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat,
|
keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat,
|
||||||
pointer_constraints::{PointerConstraint, with_pointer_constraint},
|
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);
|
std::mem::drop(shell);
|
||||||
ptr.relative_motion(
|
ptr.relative_motion(
|
||||||
|
|
@ -371,25 +387,6 @@ impl State {
|
||||||
return;
|
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 ptr.is_grabbed() {
|
||||||
if seat
|
if seat
|
||||||
.user_data()
|
.user_data()
|
||||||
|
|
@ -497,21 +494,14 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If confined, help user to update valid coordinates can improve user experience
|
// If confined, don't move pointer if it would go outside surface or region
|
||||||
let shell = self.common.shell.read();
|
if pointer_confined && let Some((surface, surface_loc)) = &under {
|
||||||
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())
|
if new_under.as_ref().and_then(|(under, _)| under.wl_surface())
|
||||||
!= surface.wl_surface()
|
!= surface.wl_surface()
|
||||||
{
|
{
|
||||||
return (false, None);
|
ptr.frame(self);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match surface {
|
match surface {
|
||||||
PointerFocusTarget::WlSurface { surface, .. } => {
|
PointerFocusTarget::WlSurface { surface, .. } => {
|
||||||
if under_from_surface_tree(
|
if under_from_surface_tree(
|
||||||
|
|
@ -522,7 +512,8 @@ impl State {
|
||||||
)
|
)
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
return (false, None);
|
ptr.frame(self);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PointerFocusTarget::X11Surface { surface, .. } => {
|
PointerFocusTarget::X11Surface { surface, .. } => {
|
||||||
|
|
@ -534,54 +525,20 @@ impl State {
|
||||||
)
|
)
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
return (false, None);
|
ptr.frame(self);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
if let Some(region) = confine_region
|
||||||
if let Some(region) = &confine_region
|
|
||||||
&& !region
|
&& !region
|
||||||
.contains((pos.as_logical() - *surface_loc).to_i32_round())
|
.contains((position.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
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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);
|
ptr.frame(self);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let serial = SERIAL_COUNTER.next_serial();
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
ptr.motion(
|
ptr.motion(
|
||||||
|
|
@ -595,17 +552,10 @@ impl State {
|
||||||
);
|
);
|
||||||
ptr.frame(self);
|
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
|
if let Some((under, surface_location)) = new_under
|
||||||
.and_then(|(target, loc)| Some((target.wl_surface()?.into_owned(), loc)))
|
.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 {
|
with_pointer_constraint(&under, &ptr, |constraint| match constraint {
|
||||||
Some(constraint) if !constraint.is_active() => {
|
Some(constraint) if !constraint.is_active() => {
|
||||||
let region = match &*constraint {
|
let region = match &*constraint {
|
||||||
|
|
@ -621,7 +571,6 @@ impl State {
|
||||||
_ => {}
|
_ => {}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let mut shell = self.common.shell.write();
|
let mut shell = self.common.shell.write();
|
||||||
shell.update_pointer_position(position.to_local(&output), &output);
|
shell.update_pointer_position(position.to_local(&output), &output);
|
||||||
|
|
@ -640,9 +589,7 @@ impl State {
|
||||||
|
|
||||||
for session in cursor_sessions_for_output(&shell, &output) {
|
for session in cursor_sessions_for_output(&shell, &output) {
|
||||||
if let Some((geometry, offset)) = seat.cursor_geometry(
|
if let Some((geometry, offset)) = seat.cursor_geometry(
|
||||||
(position - output_geometry.loc.to_f64())
|
position.as_logical().to_buffer(
|
||||||
.as_logical()
|
|
||||||
.to_buffer(
|
|
||||||
output.current_scale().fractional_scale(),
|
output.current_scale().fractional_scale(),
|
||||||
output.current_transform(),
|
output.current_transform(),
|
||||||
&output_geometry.size.to_f64().as_logical(),
|
&output_geometry.size.to_f64().as_logical(),
|
||||||
|
|
@ -654,13 +601,8 @@ impl State {
|
||||||
.map(|constraint| constraint.size != geometry.size)
|
.map(|constraint| constraint.size != geometry.size)
|
||||||
.unwrap_or(true)
|
.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 {
|
session.update_constraints(BufferConstraints {
|
||||||
size: cursor_size,
|
size: geometry.size,
|
||||||
shm: vec![ShmFormat::Argb8888],
|
shm: vec![ShmFormat::Argb8888],
|
||||||
dma: None,
|
dma: None,
|
||||||
});
|
});
|
||||||
|
|
@ -683,11 +625,11 @@ impl State {
|
||||||
self.common.idle_notifier_state.notify_activity(&seat);
|
self.common.idle_notifier_state.notify_activity(&seat);
|
||||||
notify_cursor_activity(self, &seat);
|
notify_cursor_activity(self, &seat);
|
||||||
let output = seat.active_output();
|
let output = seat.active_output();
|
||||||
let output_geometry = output.geometry();
|
let geometry = output.geometry();
|
||||||
let position = output_geometry.loc.to_f64()
|
let position = geometry.loc.to_f64()
|
||||||
+ smithay::backend::input::AbsolutePositionEvent::position_transformed(
|
+ smithay::backend::input::AbsolutePositionEvent::position_transformed(
|
||||||
&event,
|
&event,
|
||||||
output_geometry.size.as_logical(),
|
geometry.size.as_logical(),
|
||||||
)
|
)
|
||||||
.as_global();
|
.as_global();
|
||||||
let serial = SERIAL_COUNTER.next_serial();
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
|
|
@ -709,12 +651,10 @@ impl State {
|
||||||
let shell = self.common.shell.read();
|
let shell = self.common.shell.read();
|
||||||
for session in cursor_sessions_for_output(&shell, &output) {
|
for session in cursor_sessions_for_output(&shell, &output) {
|
||||||
if let Some((geometry, offset)) = seat.cursor_geometry(
|
if let Some((geometry, offset)) = seat.cursor_geometry(
|
||||||
(position - output_geometry.loc.to_f64())
|
position.as_logical().to_buffer(
|
||||||
.as_logical()
|
|
||||||
.to_buffer(
|
|
||||||
output.current_scale().fractional_scale(),
|
output.current_scale().fractional_scale(),
|
||||||
output.current_transform(),
|
output.current_transform(),
|
||||||
&output_geometry.size.to_f64().as_logical(),
|
&geometry.size.to_f64().as_logical(),
|
||||||
),
|
),
|
||||||
self.common.clock.now(),
|
self.common.clock.now(),
|
||||||
) {
|
) {
|
||||||
|
|
@ -723,13 +663,8 @@ impl State {
|
||||||
.map(|constraint| constraint.size != geometry.size)
|
.map(|constraint| constraint.size != geometry.size)
|
||||||
.unwrap_or(true)
|
.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 {
|
session.update_constraints(BufferConstraints {
|
||||||
size: cursor_size,
|
size: geometry.size,
|
||||||
shm: vec![ShmFormat::Argb8888],
|
shm: vec![ShmFormat::Argb8888],
|
||||||
dma: None,
|
dma: None,
|
||||||
});
|
});
|
||||||
|
|
@ -1600,7 +1535,7 @@ impl State {
|
||||||
}
|
}
|
||||||
InputEvent::Special(_) => {}
|
InputEvent::Special(_) => {}
|
||||||
InputEvent::SwitchToggle { event } => {
|
InputEvent::SwitchToggle { event } => {
|
||||||
#[cfg(feature = "logind")]
|
#[cfg(feature = "systemd")]
|
||||||
if event.switch() == Some(Switch::Lid) && self.common.inhibit_lid_fd.is_some() {
|
if event.switch() == Some(Switch::Lid) && self.common.inhibit_lid_fd.is_some() {
|
||||||
let backend = self.backend.lock();
|
let backend = self.backend.lock();
|
||||||
let output = backend
|
let output = backend
|
||||||
|
|
@ -1682,9 +1617,9 @@ impl State {
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor() {
|
self.common
|
||||||
a11y_keyboard_monitor.key_event(modifiers, &handle, event.state());
|
.a11y_keyboard_monitor_state
|
||||||
}
|
.key_event(modifiers, &handle, event.state());
|
||||||
|
|
||||||
// Leave move overview mode, if any modifier was released
|
// Leave move overview mode, if any modifier was released
|
||||||
if let Some(Trigger::KeyboardMove(action_modifiers)) =
|
if let Some(Trigger::KeyboardMove(action_modifiers)) =
|
||||||
|
|
@ -1844,10 +1779,11 @@ impl State {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mut a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor() {
|
|
||||||
if event.state() == KeyState::Released {
|
if event.state() == KeyState::Released {
|
||||||
let removed =
|
let removed = self
|
||||||
a11y_keyboard_monitor.remove_active_virtual_mod(handle.modified_sym());
|
.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 `Caps_Lock` is a virtual modifier, and is in locked state, clear it
|
||||||
if removed
|
if removed
|
||||||
&& handle.modified_sym() == Keysym::Caps_Lock
|
&& handle.modified_sym() == Keysym::Caps_Lock
|
||||||
|
|
@ -1880,19 +1816,25 @@ impl State {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if event.state() == KeyState::Pressed
|
} else if event.state() == KeyState::Pressed
|
||||||
&& a11y_keyboard_monitor.has_virtual_mod(handle.modified_sym())
|
&& self
|
||||||
|
.common
|
||||||
|
.a11y_keyboard_monitor_state
|
||||||
|
.has_virtual_mod(handle.modified_sym())
|
||||||
{
|
{
|
||||||
a11y_keyboard_monitor.add_active_virtual_mod(handle.modified_sym());
|
self.common
|
||||||
|
.a11y_keyboard_monitor_state
|
||||||
|
.add_active_virtual_mod(handle.modified_sym());
|
||||||
|
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"active virtual mods: {:?}",
|
"active virtual mods: {:?}",
|
||||||
a11y_keyboard_monitor.active_virtual_mods()
|
self.common
|
||||||
|
.a11y_keyboard_monitor_state
|
||||||
|
.active_virtual_mods()
|
||||||
);
|
);
|
||||||
seat.supressed_keys().add(&handle, None);
|
seat.supressed_keys().add(&handle, None);
|
||||||
|
|
||||||
return FilterResult::Intercept(None);
|
return FilterResult::Intercept(None);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Skip released events for initially surpressed keys
|
// Skip released events for initially surpressed keys
|
||||||
if event.state() == KeyState::Released
|
if event.state() == KeyState::Released
|
||||||
|
|
@ -1918,10 +1860,12 @@ impl State {
|
||||||
return FilterResult::Intercept(None);
|
return FilterResult::Intercept(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor()
|
if event.state() == KeyState::Pressed
|
||||||
&& event.state() == KeyState::Pressed
|
&& (self.common.a11y_keyboard_monitor_state.has_keyboard_grab()
|
||||||
&& (a11y_keyboard_monitor.has_keyboard_grab()
|
|| self
|
||||||
|| a11y_keyboard_monitor.has_key_grab(modifiers, handle.modified_sym()))
|
.common
|
||||||
|
.a11y_keyboard_monitor_state
|
||||||
|
.has_key_grab(modifiers, handle.modified_sym()))
|
||||||
{
|
{
|
||||||
let modifiers_queue = seat.modifiers_shortcut_queue();
|
let modifiers_queue = seat.modifiers_shortcut_queue();
|
||||||
modifiers_queue.clear();
|
modifiers_queue.clear();
|
||||||
|
|
@ -2173,14 +2117,14 @@ impl State {
|
||||||
}
|
}
|
||||||
Stage::StickyPopups(layout) => {
|
Stage::StickyPopups(layout) => {
|
||||||
if let Some(element) =
|
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)));
|
return ControlFlow::Break(Ok(Some(element)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stage::Sticky(layout) => {
|
Stage::Sticky(layout) => {
|
||||||
if let Some(element) =
|
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)));
|
return ControlFlow::Break(Ok(Some(element)));
|
||||||
}
|
}
|
||||||
|
|
@ -2335,7 +2279,7 @@ impl State {
|
||||||
}
|
}
|
||||||
Stage::StickyPopups(floating_layer) => {
|
Stage::StickyPopups(floating_layer) => {
|
||||||
if let Some(under) = 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)))
|
.map(|(target, point)| (target, point.to_global(output)))
|
||||||
{
|
{
|
||||||
return ControlFlow::Break(Ok(Some(under)));
|
return ControlFlow::Break(Ok(Some(under)));
|
||||||
|
|
@ -2343,7 +2287,7 @@ impl State {
|
||||||
}
|
}
|
||||||
Stage::Sticky(floating_layer) => {
|
Stage::Sticky(floating_layer) => {
|
||||||
if let Some(under) = 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)))
|
.map(|(target, point)| (target, point.to_global(output)))
|
||||||
{
|
{
|
||||||
return ControlFlow::Break(Ok(Some(under)));
|
return ControlFlow::Break(Ok(Some(under)));
|
||||||
|
|
@ -2373,113 +2317,8 @@ impl State {
|
||||||
.ok()
|
.ok()
|
||||||
.flatten()
|
.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>(
|
fn cursor_sessions_for_output<'a>(
|
||||||
shell: &'a Shell,
|
shell: &'a Shell,
|
||||||
output: &'a Output,
|
output: &'a Output,
|
||||||
|
|
@ -2488,9 +2327,16 @@ fn cursor_sessions_for_output<'a>(
|
||||||
.active_space(output)
|
.active_space(output)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|workspace| {
|
.flat_map(|workspace| {
|
||||||
|
let maybe_fullscreen = workspace.get_fullscreen();
|
||||||
workspace
|
workspace
|
||||||
.cursor_sessions()
|
.cursor_sessions()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
.chain(
|
||||||
|
maybe_fullscreen
|
||||||
|
.map(|w| w.cursor_sessions())
|
||||||
|
.into_iter()
|
||||||
|
.flatten(),
|
||||||
|
)
|
||||||
.chain(output.cursor_sessions())
|
.chain(output.cursor_sessions())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,6 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
fn main() {
|
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()) {
|
if let Err(err) = cosmic_comp::run(Default::default()) {
|
||||||
tracing::error!("Error occured in main(): {}", err);
|
tracing::error!("Error occured in main(): {}", err);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
|
|
|
||||||
|
|
@ -254,14 +254,6 @@ impl CosmicMapped {
|
||||||
.any(|(w, _)| w.has_surface(surface, surface_type))
|
.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.
|
/// Give the pointer target under a relative offset into this element.
|
||||||
///
|
///
|
||||||
/// Returns Target + Offset relative to the target
|
/// Returns Target + Offset relative to the target
|
||||||
|
|
@ -269,13 +261,10 @@ impl CosmicMapped {
|
||||||
&self,
|
&self,
|
||||||
relative_pos: Point<f64, Logical>,
|
relative_pos: Point<f64, Logical>,
|
||||||
surface_type: WindowSurfaceType,
|
surface_type: WindowSurfaceType,
|
||||||
seat: &Seat<State>,
|
|
||||||
) -> Option<(PointerFocusTarget, Point<f64, Logical>)> {
|
) -> Option<(PointerFocusTarget, Point<f64, Logical>)> {
|
||||||
match &self.element {
|
match &self.element {
|
||||||
CosmicMappedInternal::Stack(stack) => stack.focus_under(relative_pos, surface_type),
|
CosmicMappedInternal::Stack(stack) => stack.focus_under(relative_pos, surface_type),
|
||||||
CosmicMappedInternal::Window(window) => {
|
CosmicMappedInternal::Window(window) => window.focus_under(relative_pos, surface_type),
|
||||||
window.focus_under(relative_pos, surface_type, Some(seat))
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,8 @@ use smithay::{
|
||||||
PointerTarget, RelativeMotionEvent,
|
PointerTarget, RelativeMotionEvent,
|
||||||
},
|
},
|
||||||
touch::{
|
touch::{
|
||||||
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
|
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
|
||||||
TouchTarget, UpEvent,
|
UpEvent,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output::Output,
|
output::Output,
|
||||||
|
|
@ -79,7 +79,7 @@ use std::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
sync::{
|
sync::{
|
||||||
Arc, LazyLock, Mutex,
|
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,
|
scroll_to_focus: AtomicBool,
|
||||||
previous_keyboard: AtomicUsize,
|
previous_keyboard: AtomicUsize,
|
||||||
pointer_entered: AtomicU8,
|
pointer_entered: AtomicU8,
|
||||||
touch_serial: AtomicU32,
|
|
||||||
reenter: AtomicBool,
|
reenter: AtomicBool,
|
||||||
potential_drag: Mutex<Option<usize>>,
|
potential_drag: Mutex<Option<usize>>,
|
||||||
override_alive: AtomicBool,
|
override_alive: AtomicBool,
|
||||||
|
|
@ -172,7 +171,6 @@ impl CosmicStack {
|
||||||
scroll_to_focus: AtomicBool::new(false),
|
scroll_to_focus: AtomicBool::new(false),
|
||||||
previous_keyboard: AtomicUsize::new(0),
|
previous_keyboard: AtomicUsize::new(0),
|
||||||
pointer_entered: AtomicU8::new(0),
|
pointer_entered: AtomicU8::new(0),
|
||||||
touch_serial: AtomicU32::new(0),
|
|
||||||
reenter: AtomicBool::new(false),
|
reenter: AtomicBool::new(false),
|
||||||
potential_drag: Mutex::new(None),
|
potential_drag: Mutex::new(None),
|
||||||
override_alive: AtomicBool::new(true),
|
override_alive: AtomicBool::new(true),
|
||||||
|
|
@ -251,10 +249,6 @@ impl CosmicStack {
|
||||||
window.set_tiled(false);
|
window.set_tiled(false);
|
||||||
|
|
||||||
p.active.fetch_min(windows.len() - 1, Ordering::SeqCst);
|
p.active.fetch_min(windows.len() - 1, Ordering::SeqCst);
|
||||||
p.previous_index
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.take_if(|(_, idx)| *idx >= windows.len());
|
|
||||||
});
|
});
|
||||||
self.0
|
self.0
|
||||||
.resize(Size::from((self.active().geometry().size.w, TAB_HEIGHT)));
|
.resize(Size::from((self.active().geometry().size.w, TAB_HEIGHT)));
|
||||||
|
|
@ -282,10 +276,6 @@ impl CosmicStack {
|
||||||
window.set_tiled(false);
|
window.set_tiled(false);
|
||||||
|
|
||||||
p.active.fetch_min(windows.len() - 1, Ordering::SeqCst);
|
p.active.fetch_min(windows.len() - 1, Ordering::SeqCst);
|
||||||
p.previous_index
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.take_if(|(_, idx)| *idx >= windows.len());
|
|
||||||
|
|
||||||
Some(window)
|
Some(window)
|
||||||
});
|
});
|
||||||
|
|
@ -1817,62 +1807,55 @@ impl PointerTarget<State> for CosmicStack {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TouchTarget<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 mut event = event.clone();
|
||||||
let active_window_geo = self.0.with_program(|p| {
|
let active_window_geo = self.0.with_program(|p| {
|
||||||
p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry()
|
p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry()
|
||||||
});
|
});
|
||||||
event.location -= active_window_geo.loc.to_f64();
|
event.location -= active_window_geo.loc.to_f64();
|
||||||
self.0
|
TouchTarget::down(&self.0, seat, data, &event, seq)
|
||||||
.with_program(|p| p.touch_serial.store(event.serial.into(), Ordering::Release));
|
|
||||||
TouchTarget::down(&self.0, seat, data, &event)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent) {
|
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent, seq: Serial) {
|
||||||
TouchTarget::up(&self.0, seat, data, event)
|
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 mut event = event.clone();
|
||||||
let active_window_geo = self.0.with_program(|p| {
|
let active_window_geo = self.0.with_program(|p| {
|
||||||
p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry()
|
p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry()
|
||||||
});
|
});
|
||||||
event.location -= active_window_geo.loc.to_f64();
|
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
|
if event.location.y < 0.0
|
||||||
|| event.location.y > TAB_HEIGHT as f64
|
|| event.location.y > TAB_HEIGHT as f64
|
||||||
|| event.location.x < 64.0
|
|| event.location.x < 64.0
|
||||||
|| event.location.x > (active_window_geo.size.w as f64 - 64.0)
|
|| event.location.x > (active_window_geo.size.w as f64 - 64.0)
|
||||||
{
|
{
|
||||||
self.start_drag(
|
self.start_drag(data, seat, seq);
|
||||||
data,
|
|
||||||
seat,
|
|
||||||
self.0
|
|
||||||
.with_program(|p| p.touch_serial.load(Ordering::Acquire))
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frame(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
|
fn frame(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
|
||||||
TouchTarget::frame(&self.0, seat, data, frame)
|
TouchTarget::frame(&self.0, 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) {
|
||||||
TouchTarget::cancel(&self.0, seat, data, frame)
|
TouchTarget::cancel(&self.0, 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) {
|
||||||
TouchTarget::shape(&self.0, seat, data, event)
|
TouchTarget::shape(&self.0, seat, data, event, seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn orientation(&self, seat: &Seat<State>, data: &mut State, event: &OrientationEvent) {
|
fn orientation(
|
||||||
TouchTarget::orientation(&self.0, seat, data, event)
|
&self,
|
||||||
}
|
_seat: &Seat<State>,
|
||||||
|
_data: &mut State,
|
||||||
fn last_frame(&self, seat: &Seat<State>, data: &mut State) -> Option<FrameMarker> {
|
_event: &OrientationEvent,
|
||||||
TouchTarget::last_frame(&self.0, seat, data)
|
_seq: Serial,
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,10 +45,7 @@ use smithay::{
|
||||||
IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, user_data::UserDataMap,
|
IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, user_data::UserDataMap,
|
||||||
},
|
},
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::{
|
compositor::{SurfaceData, TraversalAction, with_states, with_surface_tree_downward},
|
||||||
SubsurfaceCachedState, SurfaceData, TraversalAction, get_parent, with_states,
|
|
||||||
with_surface_tree_downward,
|
|
||||||
},
|
|
||||||
seat::WaylandFocus,
|
seat::WaylandFocus,
|
||||||
shell::xdg::{
|
shell::xdg::{
|
||||||
SurfaceCachedState, ToplevelCachedState, ToplevelSurface, XdgToplevelSurfaceData,
|
SurfaceCachedState, ToplevelCachedState, ToplevelSurface, XdgToplevelSurfaceData,
|
||||||
|
|
@ -238,8 +235,7 @@ impl CosmicSurface {
|
||||||
toplevel.with_pending_state(|state| state.size = Some(geo.size.as_logical()))
|
toplevel.with_pending_state(|state| state.size = Some(geo.size.as_logical()))
|
||||||
}
|
}
|
||||||
WindowSurface::X11(surface) => {
|
WindowSurface::X11(surface) => {
|
||||||
let _ =
|
let _ = surface.configure_with_sync(geo.as_logical(), None);
|
||||||
surface.configure_with_sync(geo.as_logical() + surface.frame_extents(), None);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -346,7 +342,7 @@ impl CosmicSurface {
|
||||||
state.is_some_and(|state| state.states.contains(ToplevelState::Resizing))
|
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(
|
pub fn focus_under(
|
||||||
&self,
|
&self,
|
||||||
relative_pos: Point<f64, Logical>,
|
relative_pos: Point<f64, Logical>,
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,8 @@ use smithay::{
|
||||||
GestureSwipeUpdateEvent, MotionEvent, PointerTarget, RelativeMotionEvent,
|
GestureSwipeUpdateEvent, MotionEvent, PointerTarget, RelativeMotionEvent,
|
||||||
},
|
},
|
||||||
touch::{
|
touch::{
|
||||||
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
|
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
|
||||||
TouchTarget, UpEvent,
|
UpEvent,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output::Output,
|
output::Output,
|
||||||
|
|
@ -57,7 +57,7 @@ use smithay::{
|
||||||
Buffer, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform,
|
Buffer, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform,
|
||||||
user_data::UserDataMap,
|
user_data::UserDataMap,
|
||||||
},
|
},
|
||||||
wayland::{pointer_constraints::with_pointer_constraint, seat::WaylandFocus},
|
wayland::seat::WaylandFocus,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
|
|
@ -274,25 +274,12 @@ impl CosmicWindow {
|
||||||
&self,
|
&self,
|
||||||
mut relative_pos: Point<f64, Logical>,
|
mut relative_pos: Point<f64, Logical>,
|
||||||
surface_type: WindowSurfaceType,
|
surface_type: WindowSurfaceType,
|
||||||
seat: Option<&Seat<State>>,
|
|
||||||
) -> Option<(PointerFocusTarget, Point<f64, Logical>)> {
|
) -> 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| {
|
self.0.with_program(|p| {
|
||||||
let mut offset = Point::from((0., 0.));
|
let mut offset = Point::from((0., 0.));
|
||||||
let mut window_ui = None;
|
let mut window_ui = None;
|
||||||
let has_ssd = p.has_ssd(false);
|
let has_ssd = p.has_ssd(false);
|
||||||
|
if (has_ssd || p.has_tiled_state())
|
||||||
if (!has_constraint && (has_ssd || p.has_tiled_state()))
|
|
||||||
&& surface_type.contains(WindowSurfaceType::TOPLEVEL)
|
&& surface_type.contains(WindowSurfaceType::TOPLEVEL)
|
||||||
{
|
{
|
||||||
let geo = p.window.geometry();
|
let geo = p.window.geometry();
|
||||||
|
|
@ -890,8 +877,7 @@ impl SpaceElement for CosmicWindow {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
|
fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
|
||||||
self.focus_under(*point, WindowSurfaceType::ALL, None)
|
self.focus_under(*point, WindowSurfaceType::ALL).is_some()
|
||||||
.is_some()
|
|
||||||
}
|
}
|
||||||
fn set_activate(&self, activated: bool) {
|
fn set_activate(&self, activated: bool) {
|
||||||
if self
|
if self
|
||||||
|
|
@ -1190,42 +1176,43 @@ impl PointerTarget<State> for CosmicWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TouchTarget<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();
|
let mut event = event.clone();
|
||||||
self.0.with_program(|p| {
|
self.0.with_program(|p| {
|
||||||
event.location -= p.window.geometry().loc.to_f64();
|
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) {
|
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent, seq: Serial) {
|
||||||
TouchTarget::up(&self.0, seat, data, event)
|
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 mut event = event.clone();
|
||||||
event.location -= self.0.with_program(|p| p.window.geometry().loc.to_f64());
|
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) {
|
fn frame(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
|
||||||
TouchTarget::frame(&self.0, seat, data, frame)
|
TouchTarget::frame(&self.0, 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) {
|
||||||
TouchTarget::cancel(&self.0, seat, data, frame)
|
TouchTarget::cancel(&self.0, 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) {
|
||||||
TouchTarget::shape(&self.0, seat, data, event)
|
TouchTarget::shape(&self.0, seat, data, event, seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn orientation(&self, seat: &Seat<State>, data: &mut State, event: &OrientationEvent) {
|
fn orientation(
|
||||||
TouchTarget::orientation(&self.0, seat, data, event)
|
&self,
|
||||||
}
|
_seat: &Seat<State>,
|
||||||
|
_data: &mut State,
|
||||||
fn last_frame(&self, seat: &Seat<State>, data: &mut State) -> Option<FrameMarker> {
|
_event: &OrientationEvent,
|
||||||
TouchTarget::last_frame(&self.0, seat, data)
|
_seq: Serial,
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
shell::{CosmicSurface, MinimizedWindow, Shell, Trigger, element::CosmicMapped},
|
shell::{CosmicSurface, MinimizedWindow, Shell, Trigger, element::CosmicMapped},
|
||||||
state::{Common, State},
|
state::Common,
|
||||||
utils::prelude::*,
|
utils::prelude::*,
|
||||||
wayland::handlers::{xdg_shell::PopupGrabData, xwayland_keyboard_grab::XWaylandGrabSeatData},
|
wayland::handlers::{xdg_shell::PopupGrabData, xwayland_keyboard_grab::XWaylandGrabSeatData},
|
||||||
};
|
};
|
||||||
|
|
@ -12,7 +12,6 @@ use smithay::{
|
||||||
reexports::wayland_server::{Resource, protocol::wl_surface::WlSurface},
|
reexports::wayland_server::{Resource, protocol::wl_surface::WlSurface},
|
||||||
utils::{IsAlive, Point, SERIAL_COUNTER, Serial},
|
utils::{IsAlive, Point, SERIAL_COUNTER, Serial},
|
||||||
wayland::{
|
wayland::{
|
||||||
pointer_constraints::with_pointer_constraint,
|
|
||||||
seat::WaylandFocus,
|
seat::WaylandFocus,
|
||||||
selection::{data_device::set_data_device_focus, primary_selection::set_primary_focus},
|
selection::{data_device::set_data_device_focus, primary_selection::set_primary_focus},
|
||||||
shell::wlr_layer::{KeyboardInteractivity, Layer},
|
shell::wlr_layer::{KeyboardInteractivity, Layer},
|
||||||
|
|
@ -288,18 +287,22 @@ impl Shell {
|
||||||
}
|
}
|
||||||
|
|
||||||
let workspace = &mut set.workspaces[set.active];
|
let workspace = &mut set.workspaces[set.active];
|
||||||
for fs in workspace.get_fullscreen_surfaces() {
|
if let Some(fullscreen) = workspace.get_fullscreen() {
|
||||||
let is_focused = self.seats.iter().any(|seat| {
|
if self.seats.iter().any(|seat| {
|
||||||
if let Some(KeyboardFocusTarget::Fullscreen(s)) =
|
if let Some(KeyboardFocusTarget::Fullscreen(s)) =
|
||||||
seat.get_keyboard().unwrap().current_focus()
|
seat.get_keyboard().unwrap().current_focus()
|
||||||
{
|
{
|
||||||
s == fs.surface
|
&s == fullscreen
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
});
|
}) {
|
||||||
fs.surface.set_activated(is_focused);
|
fullscreen.set_activated(true);
|
||||||
fs.surface.send_configure();
|
fullscreen.send_configure();
|
||||||
|
} else {
|
||||||
|
fullscreen.set_activated(false);
|
||||||
|
fullscreen.send_configure();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for focused in focused_windows.iter() {
|
for focused in focused_windows.iter() {
|
||||||
raise_with_children(&mut workspace.floating_layer, focused);
|
raise_with_children(&mut workspace.floating_layer, focused);
|
||||||
|
|
@ -344,20 +347,6 @@ fn update_focus_state(
|
||||||
) {
|
) {
|
||||||
// update keyboard focus
|
// update keyboard focus
|
||||||
if let Some(keyboard) = seat.get_keyboard() {
|
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
|
if should_update_cursor
|
||||||
&& state.common.config.cosmic_conf.cursor_follows_focus
|
&& state.common.config.cosmic_conf.cursor_follows_focus
|
||||||
&& target.is_some()
|
&& target.is_some()
|
||||||
|
|
@ -657,9 +646,7 @@ fn focus_target_is_valid(
|
||||||
.has_node(&node),
|
.has_node(&node),
|
||||||
KeyboardFocusTarget::Fullscreen(window) => {
|
KeyboardFocusTarget::Fullscreen(window) => {
|
||||||
let workspace = shell.active_space(output).unwrap();
|
let workspace = shell.active_space(output).unwrap();
|
||||||
workspace
|
workspace.get_fullscreen().is_some_and(|w| w == &window)
|
||||||
.get_fullscreen_surfaces()
|
|
||||||
.any(|f| f.surface == window)
|
|
||||||
}
|
}
|
||||||
KeyboardFocusTarget::Popup(_) => true,
|
KeyboardFocusTarget::Popup(_) => true,
|
||||||
KeyboardFocusTarget::LockSurface(_) => false,
|
KeyboardFocusTarget::LockSurface(_) => false,
|
||||||
|
|
@ -712,9 +699,9 @@ fn update_focus_target(
|
||||||
.map(KeyboardFocusTarget::Element)
|
.map(KeyboardFocusTarget::Element)
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
workspace
|
workspace
|
||||||
.get_fullscreen(seat)
|
.get_fullscreen()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|fs| KeyboardFocusTarget::Fullscreen(fs.surface))
|
.map(KeyboardFocusTarget::Fullscreen)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,8 +112,8 @@ fn render_input_order_internal<R: 'static>(
|
||||||
let output_size = output.geometry().size;
|
let output_size = output.geometry().size;
|
||||||
|
|
||||||
// this is more hacky than I would like..
|
// 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 seat = shell.seats.last_active();
|
||||||
let fullscreen = workspace.get_fullscreen(seat);
|
|
||||||
let is_active_workspace = seat.focused_output().is_some_and(|output| {
|
let is_active_workspace = seat.focused_output().is_some_and(|output| {
|
||||||
shell
|
shell
|
||||||
.active_space(&output)
|
.active_space(&output)
|
||||||
|
|
@ -144,7 +144,7 @@ fn render_input_order_internal<R: 'static>(
|
||||||
let Some(workspace) = shell.workspaces.space_for_handle(previous) else {
|
let Some(workspace) = shell.workspaces.space_for_handle(previous) else {
|
||||||
return ControlFlow::Break(Err(OutputNoMode));
|
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 {
|
let (forward, percentage) = match start {
|
||||||
WorkspaceDelta::Shortcut(st) => (
|
WorkspaceDelta::Shortcut(st) => (
|
||||||
|
|
@ -220,19 +220,21 @@ fn render_input_order_internal<R: 'static>(
|
||||||
.rev()
|
.rev()
|
||||||
.filter(|or| {
|
.filter(|or| {
|
||||||
(*or)
|
(*or)
|
||||||
.last_configure()
|
.geometry()
|
||||||
.as_global()
|
.as_global()
|
||||||
.intersection(output.geometry())
|
.intersection(output.geometry())
|
||||||
.is_some()
|
.is_some()
|
||||||
})
|
})
|
||||||
.map(|or| (or, or.last_configure().loc.as_global()))
|
.map(|or| (or, or.geometry().loc.as_global()))
|
||||||
{
|
{
|
||||||
callback(Stage::OverrideRedirect { surface, location })?;
|
callback(Stage::OverrideRedirect { surface, location })?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sticky window popups
|
// sticky window popups
|
||||||
|
if !has_focused_fullscreen {
|
||||||
callback(Stage::StickyPopups(&set.sticky_layer))?;
|
callback(Stage::StickyPopups(&set.sticky_layer))?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if element_filter != ElementFilter::LayerShellOnly {
|
if element_filter != ElementFilter::LayerShellOnly {
|
||||||
// previous workspace popups
|
// previous workspace popups
|
||||||
|
|
@ -311,12 +313,12 @@ fn render_input_order_internal<R: 'static>(
|
||||||
for (layer, location) in layer_surfaces(output, Layer::Top, element_filter) {
|
for (layer, location) in layer_surfaces(output, Layer::Top, element_filter) {
|
||||||
callback(Stage::LayerSurface { layer, location })?;
|
callback(Stage::LayerSurface { layer, location })?;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// sticky windows
|
// sticky windows
|
||||||
if element_filter != ElementFilter::LayerShellOnly {
|
if element_filter != ElementFilter::LayerShellOnly {
|
||||||
callback(Stage::Sticky(&set.sticky_layer))?;
|
callback(Stage::Sticky(&set.sticky_layer))?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if element_filter != ElementFilter::LayerShellOnly {
|
if element_filter != ElementFilter::LayerShellOnly {
|
||||||
// workspace windows
|
// workspace windows
|
||||||
|
|
|
||||||
|
|
@ -29,18 +29,15 @@ use smithay::{
|
||||||
MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent,
|
MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent,
|
||||||
},
|
},
|
||||||
touch::{
|
touch::{
|
||||||
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
|
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
|
||||||
TouchTarget, UpEvent,
|
UpEvent,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
reexports::wayland_server::{
|
reexports::wayland_server::{
|
||||||
Client, DisplayHandle, Resource, backend::ObjectId, protocol::wl_surface::WlSurface,
|
Client, DisplayHandle, Resource, backend::ObjectId, protocol::wl_surface::WlSurface,
|
||||||
},
|
},
|
||||||
utils::{IsAlive, Logical, Point, Serial, Transform},
|
utils::{IsAlive, Logical, Point, Serial, Transform},
|
||||||
wayland::{
|
wayland::{seat::WaylandFocus, selection::data_device::WlOfferData, session_lock::LockSurface},
|
||||||
compositor::with_states, seat::WaylandFocus, selection::data_device::WlOfferData,
|
|
||||||
session_lock::LockSurface, shell::xdg::SurfaceCachedState,
|
|
||||||
},
|
|
||||||
xwayland::{
|
xwayland::{
|
||||||
X11Surface,
|
X11Surface,
|
||||||
xwm::{XwmId, XwmOfferData},
|
xwm::{XwmId, XwmOfferData},
|
||||||
|
|
@ -228,63 +225,6 @@ impl PointerFocusTarget {
|
||||||
_ => false,
|
_ => 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 {
|
impl KeyboardFocusTarget {
|
||||||
|
|
@ -341,18 +281,6 @@ impl KeyboardFocusTarget {
|
||||||
false
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -397,11 +325,47 @@ impl IsAlive for KeyboardFocusTarget {
|
||||||
|
|
||||||
impl PointerTarget<State> for PointerFocusTarget {
|
impl PointerTarget<State> for PointerFocusTarget {
|
||||||
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &PointerMotionEvent) {
|
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);
|
self.inner_pointer_target().enter(seat, data, event);
|
||||||
}
|
}
|
||||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &PointerMotionEvent) {
|
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);
|
self.inner_pointer_target().motion(seat, data, event);
|
||||||
}
|
}
|
||||||
fn relative_motion(&self, seat: &Seat<State>, data: &mut State, event: &RelativeMotionEvent) {
|
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 {
|
impl TouchTarget<State> for PointerFocusTarget {
|
||||||
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent) {
|
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent, seq: Serial) {
|
||||||
self.inner_touch_target().down(seat, data, event);
|
self.inner_touch_target().down(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) {
|
||||||
self.inner_touch_target().up(seat, data, event);
|
self.inner_touch_target().up(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) {
|
||||||
self.inner_touch_target().motion(seat, data, event);
|
self.inner_touch_target().motion(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) {
|
||||||
self.inner_touch_target().frame(seat, data, frame);
|
self.inner_touch_target().frame(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) {
|
||||||
self.inner_touch_target().cancel(seat, data, frame);
|
self.inner_touch_target().cancel(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) {
|
||||||
self.inner_touch_target().shape(seat, data, event);
|
self.inner_touch_target().shape(seat, data, event, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn orientation(&self, seat: &Seat<State>, data: &mut State, event: &OrientationEvent) {
|
fn orientation(
|
||||||
self.inner_touch_target().orientation(seat, data, event);
|
&self,
|
||||||
}
|
seat: &Seat<State>,
|
||||||
|
data: &mut State,
|
||||||
fn last_frame(&self, seat: &Seat<State>, data: &mut State) -> Option<FrameMarker> {
|
event: &OrientationEvent,
|
||||||
self.inner_touch_target().last_frame(seat, data)
|
seq: Serial,
|
||||||
|
) {
|
||||||
|
self.inner_touch_target()
|
||||||
|
.orientation(seat, data, event, seq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -202,12 +202,19 @@ impl<G: TouchGrab<State>> TouchGrab<State> for DelayGrab<G> {
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
|
focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
|
||||||
event: &DownEvent,
|
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) {
|
fn up(
|
||||||
handle.up(data, event);
|
&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 {
|
if event.slot == TouchGrab::start_data(self).slot {
|
||||||
handle.unset_grab(self, data);
|
handle.unset_grab(self, data);
|
||||||
|
|
@ -220,8 +227,9 @@ impl<G: TouchGrab<State>> TouchGrab<State> for DelayGrab<G> {
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
|
focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
|
||||||
event: &TouchMotionEvent,
|
event: &TouchMotionEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
handle.motion(data, focus, event);
|
handle.motion(data, focus, event, seq);
|
||||||
|
|
||||||
let distance = self.start_data.distance(event.location);
|
let distance = self.start_data.distance(event.location);
|
||||||
if distance >= 1.
|
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>) {
|
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||||
handle.frame(data)
|
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.cancel(data);
|
handle.cancel(data, seq);
|
||||||
handle.unset_grab(self, data);
|
handle.unset_grab(self, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,8 +259,9 @@ impl<G: TouchGrab<State>> TouchGrab<State> for DelayGrab<G> {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &ShapeEvent,
|
event: &ShapeEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
handle.shape(data, event)
|
handle.shape(data, event, seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn orientation(
|
fn orientation(
|
||||||
|
|
@ -260,8 +269,9 @@ impl<G: TouchGrab<State>> TouchGrab<State> for DelayGrab<G> {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &OrientationEvent,
|
event: &OrientationEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
handle.orientation(data, event);
|
handle.orientation(data, event, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_data(&self) -> &TouchGrabStartData<State> {
|
fn start_data(&self) -> &TouchGrabStartData<State> {
|
||||||
|
|
|
||||||
|
|
@ -720,6 +720,7 @@ impl TouchGrab<State> for MenuGrab {
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||||
event: &DownEvent,
|
event: &DownEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
{
|
{
|
||||||
let mut guard = self.elements.lock().unwrap();
|
let mut guard = self.elements.lock().unwrap();
|
||||||
|
|
@ -754,15 +755,21 @@ impl TouchGrab<State> for MenuGrab {
|
||||||
time: event.time,
|
time: event.time,
|
||||||
};
|
};
|
||||||
if element.touch_entered.is_none() {
|
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);
|
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();
|
let elements = self.elements.lock().unwrap();
|
||||||
for element in elements.iter().filter(|elem| {
|
for element in elements.iter().filter(|elem| {
|
||||||
|
|
@ -770,7 +777,7 @@ impl TouchGrab<State> for MenuGrab {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|slot| *slot == event.slot)
|
.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);
|
handle.unset_grab(self, data);
|
||||||
|
|
@ -782,6 +789,7 @@ impl TouchGrab<State> for MenuGrab {
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||||
event: &TouchMotionEvent,
|
event: &TouchMotionEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
{
|
{
|
||||||
let elements = self.elements.lock().unwrap();
|
let elements = self.elements.lock().unwrap();
|
||||||
|
|
@ -790,24 +798,24 @@ impl TouchGrab<State> for MenuGrab {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|slot| *slot == event.slot)
|
.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>) {
|
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||||
handle.frame(data);
|
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();
|
let mut elements = self.elements.lock().unwrap();
|
||||||
for element in elements.iter_mut() {
|
for element in elements.iter_mut() {
|
||||||
let _ = element.touch_entered.take();
|
let _ = element.touch_entered.take();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handle.cancel(data);
|
handle.cancel(data, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shape(
|
fn shape(
|
||||||
|
|
@ -815,8 +823,9 @@ impl TouchGrab<State> for MenuGrab {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &smithay::input::touch::ShapeEvent,
|
event: &smithay::input::touch::ShapeEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
handle.shape(data, event);
|
handle.shape(data, event, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn orientation(
|
fn orientation(
|
||||||
|
|
@ -824,8 +833,9 @@ impl TouchGrab<State> for MenuGrab {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &smithay::input::touch::OrientationEvent,
|
event: &smithay::input::touch::OrientationEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
handle.orientation(data, event);
|
handle.orientation(data, event, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_data(&self) -> &TouchGrabStartData<State> {
|
fn start_data(&self) -> &TouchGrabStartData<State> {
|
||||||
|
|
|
||||||
|
|
@ -387,17 +387,24 @@ impl TouchGrab<State> for ResizeGrab {
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||||
event: &DownEvent,
|
event: &DownEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
ResizeGrab::Floating(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),
|
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 {
|
match self {
|
||||||
ResizeGrab::Floating(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),
|
ResizeGrab::Tiling(grab) => TouchGrab::up(grab, data, handle, event, seq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -407,24 +414,25 @@ impl TouchGrab<State> for ResizeGrab {
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||||
event: &TouchMotionEvent,
|
event: &TouchMotionEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
ResizeGrab::Floating(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),
|
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 {
|
match self {
|
||||||
ResizeGrab::Floating(grab) => TouchGrab::frame(grab, data, handle),
|
ResizeGrab::Floating(grab) => TouchGrab::frame(grab, data, handle, seq),
|
||||||
ResizeGrab::Tiling(grab) => TouchGrab::frame(grab, data, handle),
|
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 {
|
match self {
|
||||||
ResizeGrab::Floating(grab) => TouchGrab::cancel(grab, data, handle),
|
ResizeGrab::Floating(grab) => TouchGrab::cancel(grab, data, handle, seq),
|
||||||
ResizeGrab::Tiling(grab) => TouchGrab::cancel(grab, data, handle),
|
ResizeGrab::Tiling(grab) => TouchGrab::cancel(grab, data, handle, seq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -433,10 +441,11 @@ impl TouchGrab<State> for ResizeGrab {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &ShapeEvent,
|
event: &ShapeEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
ResizeGrab::Floating(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),
|
ResizeGrab::Tiling(grab) => TouchGrab::shape(grab, data, handle, event, seq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,10 +454,11 @@ impl TouchGrab<State> for ResizeGrab {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &OrientationEvent,
|
event: &OrientationEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
ResizeGrab::Floating(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),
|
ResizeGrab::Tiling(grab) => TouchGrab::orientation(grab, data, handle, event, seq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -719,17 +729,24 @@ impl TouchGrab<State> for MoveGrab {
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||||
event: &DownEvent,
|
event: &DownEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
MoveGrab::Move(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),
|
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 {
|
match self {
|
||||||
MoveGrab::Move(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),
|
MoveGrab::Delayed(grab) => TouchGrab::up(grab, data, handle, event, seq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -739,24 +756,25 @@ impl TouchGrab<State> for MoveGrab {
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||||
event: &TouchMotionEvent,
|
event: &TouchMotionEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
MoveGrab::Move(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),
|
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 {
|
match self {
|
||||||
MoveGrab::Move(grab) => TouchGrab::frame(grab, data, handle),
|
MoveGrab::Move(grab) => TouchGrab::frame(grab, data, handle, seq),
|
||||||
MoveGrab::Delayed(grab) => TouchGrab::frame(grab, data, handle),
|
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 {
|
match self {
|
||||||
MoveGrab::Move(grab) => TouchGrab::cancel(grab, data, handle),
|
MoveGrab::Move(grab) => TouchGrab::cancel(grab, data, handle, seq),
|
||||||
MoveGrab::Delayed(grab) => TouchGrab::cancel(grab, data, handle),
|
MoveGrab::Delayed(grab) => TouchGrab::cancel(grab, data, handle, seq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -765,10 +783,11 @@ impl TouchGrab<State> for MoveGrab {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &ShapeEvent,
|
event: &ShapeEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
MoveGrab::Move(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),
|
MoveGrab::Delayed(grab) => TouchGrab::shape(grab, data, handle, event, seq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -777,10 +796,11 @@ impl TouchGrab<State> for MoveGrab {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &OrientationEvent,
|
event: &OrientationEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
MoveGrab::Move(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),
|
MoveGrab::Delayed(grab) => TouchGrab::orientation(grab, data, handle, event, seq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ use smithay::{
|
||||||
touch::{self, GrabStartData as TouchGrabStartData, TouchGrab, TouchInnerHandle},
|
touch::{self, GrabStartData as TouchGrabStartData, TouchGrab, TouchInnerHandle},
|
||||||
},
|
},
|
||||||
output::Output,
|
output::Output,
|
||||||
utils::{IsAlive, Logical, Point, Rectangle, SERIAL_COUNTER, Scale},
|
utils::{IsAlive, Logical, Point, Rectangle, SERIAL_COUNTER, Scale, Serial},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
|
|
@ -652,8 +652,9 @@ impl TouchGrab<State> for MoveGrab {
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||||
event: &touch::DownEvent,
|
event: &touch::DownEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
handle.down(data, None, event)
|
handle.down(data, None, event, seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn up(
|
fn up(
|
||||||
|
|
@ -661,12 +662,13 @@ impl TouchGrab<State> for MoveGrab {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &touch::UpEvent,
|
event: &touch::UpEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
|
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
|
||||||
handle.unset_grab(self, data);
|
handle.unset_grab(self, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle.up(data, event);
|
handle.up(data, event, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn motion(
|
fn motion(
|
||||||
|
|
@ -675,19 +677,20 @@ impl TouchGrab<State> for MoveGrab {
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||||
event: &touch::MotionEvent,
|
event: &touch::MotionEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
|
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
|
||||||
self.update_location(data, event.location);
|
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>) {
|
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||||
handle.frame(data)
|
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);
|
handle.unset_grab(self, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -696,8 +699,9 @@ impl TouchGrab<State> for MoveGrab {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &touch::ShapeEvent,
|
event: &touch::ShapeEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
handle.shape(data, event)
|
handle.shape(data, event, seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn orientation(
|
fn orientation(
|
||||||
|
|
@ -705,8 +709,9 @@ impl TouchGrab<State> for MoveGrab {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &touch::OrientationEvent,
|
event: &touch::OrientationEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
handle.orientation(data, event)
|
handle.orientation(data, event, seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_data(&self) -> &TouchGrabStartData<State> {
|
fn start_data(&self) -> &TouchGrabStartData<State> {
|
||||||
|
|
@ -948,7 +953,6 @@ impl Drop for MoveGrab {
|
||||||
if let Some((target, offset)) = mapped.focus_under(
|
if let Some((target, offset)) = mapped.focus_under(
|
||||||
current_location - position.as_logical().to_f64(),
|
current_location - position.as_logical().to_f64(),
|
||||||
WindowSurfaceType::ALL,
|
WindowSurfaceType::ALL,
|
||||||
&seat,
|
|
||||||
) {
|
) {
|
||||||
pointer.motion(
|
pointer.motion(
|
||||||
state,
|
state,
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ use smithay::{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output::Output,
|
output::Output,
|
||||||
utils::{IsAlive, Logical, Point, Rectangle, Size},
|
utils::{IsAlive, Logical, Point, Rectangle, Serial, Size},
|
||||||
};
|
};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
|
|
@ -330,16 +330,23 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||||
event: &DownEvent,
|
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 {
|
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
|
||||||
handle.unset_grab(self, data);
|
handle.unset_grab(self, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle.up(data, event);
|
handle.up(data, event, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn motion(
|
fn motion(
|
||||||
|
|
@ -348,6 +355,7 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||||
event: &TouchMotionEvent,
|
event: &TouchMotionEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot
|
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot
|
||||||
&& self.update_location(event.location.as_global())
|
&& self.update_location(event.location.as_global())
|
||||||
|
|
@ -355,14 +363,14 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
|
||||||
handle.unset_grab(self, data);
|
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>) {
|
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||||
handle.frame(data)
|
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);
|
handle.unset_grab(self, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -371,8 +379,9 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &ShapeEvent,
|
event: &ShapeEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
handle.shape(data, event)
|
handle.shape(data, event, seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn orientation(
|
fn orientation(
|
||||||
|
|
@ -380,8 +389,9 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &OrientationEvent,
|
event: &OrientationEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
handle.orientation(data, event)
|
handle.orientation(data, event, seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_data(&self) -> &TouchGrabStartData<State> {
|
fn start_data(&self) -> &TouchGrabStartData<State> {
|
||||||
|
|
@ -533,7 +543,7 @@ impl ResizeSurfaceGrab {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
WindowSurface::X11(surface) => {
|
WindowSurface::X11(surface) => {
|
||||||
let mut geometry = surface.last_configure();
|
let mut geometry = surface.geometry();
|
||||||
geometry.loc += (location - new_location).as_logical();
|
geometry.loc += (location - new_location).as_logical();
|
||||||
let _ = surface.configure(geometry);
|
let _ = surface.configure(geometry);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -295,7 +295,6 @@ impl FloatingLayout {
|
||||||
for mapped in self
|
for mapped in self
|
||||||
.space
|
.space
|
||||||
.elements()
|
.elements()
|
||||||
.filter(|w| w.alive())
|
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
@ -310,10 +309,7 @@ impl FloatingLayout {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let Some(geometry) = self.space.element_geometry(&mapped) else {
|
let geometry = self.space.element_geometry(&mapped).unwrap().to_f64();
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let geometry = geometry.to_f64();
|
|
||||||
let new_loc = (
|
let new_loc = (
|
||||||
((geometry.loc.x - old_output_geometry.loc.x).max(0.)
|
((geometry.loc.x - old_output_geometry.loc.x).max(0.)
|
||||||
/ old_output_geometry.size.w
|
/ old_output_geometry.size.w
|
||||||
|
|
@ -623,10 +619,6 @@ impl FloatingLayout {
|
||||||
from: Rectangle<i32, Local>,
|
from: Rectangle<i32, Local>,
|
||||||
position: Point<i32, Local>,
|
position: Point<i32, Local>,
|
||||||
) {
|
) {
|
||||||
if !mapped.alive() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = self.space.outputs().next().unwrap().clone();
|
let output = self.space.outputs().next().unwrap().clone();
|
||||||
let layers = layer_map_for_output(&output);
|
let layers = layer_map_for_output(&output);
|
||||||
let geometry = layers.non_exclusive_zone().as_local();
|
let geometry = layers.non_exclusive_zone().as_local();
|
||||||
|
|
@ -739,11 +731,7 @@ impl FloatingLayout {
|
||||||
self.space.element_geometry(elem).map(RectExt::as_local)
|
self.space.element_geometry(elem).map(RectExt::as_local)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn popup_element_under(
|
pub fn popup_element_under(&self, location: Point<f64, Local>) -> Option<KeyboardFocusTarget> {
|
||||||
&self,
|
|
||||||
location: Point<f64, Local>,
|
|
||||||
seat: &Seat<State>,
|
|
||||||
) -> Option<KeyboardFocusTarget> {
|
|
||||||
self.space
|
self.space
|
||||||
.elements()
|
.elements()
|
||||||
.rev()
|
.rev()
|
||||||
|
|
@ -764,7 +752,6 @@ impl FloatingLayout {
|
||||||
if e.focus_under(
|
if e.focus_under(
|
||||||
point.as_logical(),
|
point.as_logical(),
|
||||||
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
|
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
|
||||||
seat,
|
|
||||||
)
|
)
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
|
|
@ -778,7 +765,6 @@ impl FloatingLayout {
|
||||||
pub fn toplevel_element_under(
|
pub fn toplevel_element_under(
|
||||||
&self,
|
&self,
|
||||||
location: Point<f64, Local>,
|
location: Point<f64, Local>,
|
||||||
seat: &Seat<State>,
|
|
||||||
) -> Option<KeyboardFocusTarget> {
|
) -> Option<KeyboardFocusTarget> {
|
||||||
self.space
|
self.space
|
||||||
.elements()
|
.elements()
|
||||||
|
|
@ -800,7 +786,6 @@ impl FloatingLayout {
|
||||||
if e.focus_under(
|
if e.focus_under(
|
||||||
point.as_logical(),
|
point.as_logical(),
|
||||||
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
||||||
seat,
|
|
||||||
)
|
)
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
|
|
@ -814,7 +799,6 @@ impl FloatingLayout {
|
||||||
pub fn popup_surface_under(
|
pub fn popup_surface_under(
|
||||||
&self,
|
&self,
|
||||||
location: Point<f64, Local>,
|
location: Point<f64, Local>,
|
||||||
seat: &Seat<State>,
|
|
||||||
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
|
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
|
||||||
self.space
|
self.space
|
||||||
.elements()
|
.elements()
|
||||||
|
|
@ -836,7 +820,6 @@ impl FloatingLayout {
|
||||||
e.focus_under(
|
e.focus_under(
|
||||||
point.as_logical(),
|
point.as_logical(),
|
||||||
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
|
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
|
||||||
seat,
|
|
||||||
)
|
)
|
||||||
.map(|(surface, surface_offset)| {
|
.map(|(surface, surface_offset)| {
|
||||||
(surface, render_location + surface_offset.as_local())
|
(surface, render_location + surface_offset.as_local())
|
||||||
|
|
@ -847,7 +830,6 @@ impl FloatingLayout {
|
||||||
pub fn toplevel_surface_under(
|
pub fn toplevel_surface_under(
|
||||||
&self,
|
&self,
|
||||||
location: Point<f64, Local>,
|
location: Point<f64, Local>,
|
||||||
seat: &Seat<State>,
|
|
||||||
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
|
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
|
||||||
self.space
|
self.space
|
||||||
.elements()
|
.elements()
|
||||||
|
|
@ -869,7 +851,6 @@ impl FloatingLayout {
|
||||||
e.focus_under(
|
e.focus_under(
|
||||||
point.as_logical(),
|
point.as_logical(),
|
||||||
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
||||||
seat,
|
|
||||||
)
|
)
|
||||||
.map(|(surface, surface_offset)| {
|
.map(|(surface, surface_offset)| {
|
||||||
(surface, render_location + surface_offset.as_local())
|
(surface, render_location + surface_offset.as_local())
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ pub fn is_dialog(window: &CosmicSurface) -> bool {
|
||||||
}
|
}
|
||||||
WindowSurface::X11(surface) => {
|
WindowSurface::X11(surface) => {
|
||||||
if surface.is_override_redirect()
|
if surface.is_override_redirect()
|
||||||
|| surface.is_modal()
|
|| surface.is_popup()
|
||||||
|| !matches!(
|
|| !matches!(
|
||||||
surface.window_type(),
|
surface.window_type(),
|
||||||
None | Some(WmWindowType::Normal) | Some(WmWindowType::Utility)
|
None | Some(WmWindowType::Normal) | Some(WmWindowType::Utility)
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,12 @@ use smithay::{
|
||||||
PointerTarget, RelativeMotionEvent,
|
PointerTarget, RelativeMotionEvent,
|
||||||
},
|
},
|
||||||
touch::{
|
touch::{
|
||||||
DownEvent, FrameMarker, GrabStartData as TouchGrabStartData,
|
DownEvent, GrabStartData as TouchGrabStartData, MotionEvent as TouchMotionEvent,
|
||||||
MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchGrab,
|
OrientationEvent, ShapeEvent, TouchGrab, TouchInnerHandle, TouchTarget, UpEvent,
|
||||||
TouchInnerHandle, TouchTarget, UpEvent,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output::WeakOutput,
|
output::WeakOutput,
|
||||||
utils::{IsAlive, Logical, Point},
|
utils::{IsAlive, Logical, Point, Serial},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::super::{Data, TilingLayout};
|
use super::super::{Data, TilingLayout};
|
||||||
|
|
@ -127,7 +126,7 @@ impl PointerTarget<State> for ResizeForkTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TouchTarget<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 seat = seat.clone();
|
||||||
let node = self.node.clone();
|
let node = self.node.clone();
|
||||||
let output = self.output.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 up(&self, _seat: &Seat<State>, _data: &mut State, _event: &UpEvent, _seq: Serial) {}
|
||||||
fn motion(&self, _seat: &Seat<State>, _data: &mut State, _event: &TouchMotionEvent) {}
|
fn motion(
|
||||||
fn frame(&self, _seat: &Seat<State>, _data: &mut State, _frame: FrameMarker) {}
|
&self,
|
||||||
fn cancel(&self, _seat: &Seat<State>, _data: &mut State, _frame: FrameMarker) {}
|
_seat: &Seat<State>,
|
||||||
fn shape(&self, _seat: &Seat<State>, _data: &mut State, _event: &ShapeEvent) {}
|
_data: &mut State,
|
||||||
fn orientation(&self, _seat: &Seat<State>, _data: &mut State, _event: &OrientationEvent) {}
|
_event: &TouchMotionEvent,
|
||||||
fn last_frame(&self, _seat: &Seat<State>, _data: &mut State) -> Option<FrameMarker> {
|
_seq: Serial,
|
||||||
None
|
) {
|
||||||
|
}
|
||||||
|
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>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||||
event: &DownEvent,
|
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 {
|
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
|
||||||
handle.unset_grab(self, data);
|
handle.unset_grab(self, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle.up(data, event);
|
handle.up(data, event, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn motion(
|
fn motion(
|
||||||
|
|
@ -516,6 +533,7 @@ impl TouchGrab<State> for ResizeForkGrab {
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||||
event: &TouchMotionEvent,
|
event: &TouchMotionEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot
|
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot
|
||||||
&& self.update_location(data, event.location, false)
|
&& self.update_location(data, event.location, false)
|
||||||
|
|
@ -523,14 +541,14 @@ impl TouchGrab<State> for ResizeForkGrab {
|
||||||
handle.unset_grab(self, data);
|
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>) {
|
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||||
handle.frame(data)
|
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);
|
handle.unset_grab(self, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -539,8 +557,9 @@ impl TouchGrab<State> for ResizeForkGrab {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &ShapeEvent,
|
event: &ShapeEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
handle.shape(data, event)
|
handle.shape(data, event, seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_data(&self) -> &TouchGrabStartData<State> {
|
fn start_data(&self) -> &TouchGrabStartData<State> {
|
||||||
|
|
@ -555,8 +574,9 @@ impl TouchGrab<State> for ResizeForkGrab {
|
||||||
data: &mut State,
|
data: &mut State,
|
||||||
handle: &mut TouchInnerHandle<'_, State>,
|
handle: &mut TouchInnerHandle<'_, State>,
|
||||||
event: &OrientationEvent,
|
event: &OrientationEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
handle.orientation(data, event)
|
handle.orientation(data, event, seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unset(&mut self, data: &mut State) {
|
fn unset(&mut self, data: &mut State) {
|
||||||
|
|
|
||||||
|
|
@ -3145,7 +3145,6 @@ impl TilingLayout {
|
||||||
pub fn popup_element_under(
|
pub fn popup_element_under(
|
||||||
&self,
|
&self,
|
||||||
location_f64: Point<f64, Local>,
|
location_f64: Point<f64, Local>,
|
||||||
seat: &Seat<State>,
|
|
||||||
) -> Option<KeyboardFocusTarget> {
|
) -> Option<KeyboardFocusTarget> {
|
||||||
let location = location_f64.to_i32_round();
|
let location = location_f64.to_i32_round();
|
||||||
|
|
||||||
|
|
@ -3158,7 +3157,6 @@ impl TilingLayout {
|
||||||
.focus_under(
|
.focus_under(
|
||||||
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
|
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
|
||||||
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
|
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
|
||||||
seat,
|
|
||||||
)
|
)
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
|
|
@ -3172,7 +3170,6 @@ impl TilingLayout {
|
||||||
pub fn toplevel_element_under(
|
pub fn toplevel_element_under(
|
||||||
&self,
|
&self,
|
||||||
location_f64: Point<f64, Local>,
|
location_f64: Point<f64, Local>,
|
||||||
seat: &Seat<State>,
|
|
||||||
) -> Option<KeyboardFocusTarget> {
|
) -> Option<KeyboardFocusTarget> {
|
||||||
let location = location_f64.to_i32_round();
|
let location = location_f64.to_i32_round();
|
||||||
|
|
||||||
|
|
@ -3185,7 +3182,6 @@ impl TilingLayout {
|
||||||
.focus_under(
|
.focus_under(
|
||||||
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
|
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
|
||||||
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
||||||
seat,
|
|
||||||
)
|
)
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
|
|
@ -3200,7 +3196,6 @@ impl TilingLayout {
|
||||||
&self,
|
&self,
|
||||||
location_f64: Point<f64, Local>,
|
location_f64: Point<f64, Local>,
|
||||||
overview: OverviewMode,
|
overview: OverviewMode,
|
||||||
seat: &Seat<State>,
|
|
||||||
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
|
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
|
||||||
let location = location_f64.to_i32_round();
|
let location = location_f64.to_i32_round();
|
||||||
|
|
||||||
|
|
@ -3215,7 +3210,6 @@ impl TilingLayout {
|
||||||
if let Some((target, surface_offset)) = mapped.focus_under(
|
if let Some((target, surface_offset)) = mapped.focus_under(
|
||||||
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
|
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
|
||||||
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
|
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
|
||||||
seat,
|
|
||||||
) {
|
) {
|
||||||
return Some((
|
return Some((
|
||||||
target,
|
target,
|
||||||
|
|
@ -3233,7 +3227,6 @@ impl TilingLayout {
|
||||||
&self,
|
&self,
|
||||||
location_f64: Point<f64, Local>,
|
location_f64: Point<f64, Local>,
|
||||||
overview: OverviewMode,
|
overview: OverviewMode,
|
||||||
seat: &Seat<State>,
|
|
||||||
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
|
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
|
||||||
let tree = &self.queue.trees.back().unwrap().0;
|
let tree = &self.queue.trees.back().unwrap().0;
|
||||||
let root = tree.root_node_id()?;
|
let root = tree.root_node_id()?;
|
||||||
|
|
@ -3250,7 +3243,6 @@ impl TilingLayout {
|
||||||
if let Some((target, surface_offset)) = mapped.focus_under(
|
if let Some((target, surface_offset)) = mapped.focus_under(
|
||||||
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
|
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
|
||||||
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
||||||
seat,
|
|
||||||
) {
|
) {
|
||||||
return Some((
|
return Some((
|
||||||
target,
|
target,
|
||||||
|
|
@ -3301,7 +3293,6 @@ impl TilingLayout {
|
||||||
.focus_under(
|
.focus_under(
|
||||||
test_point,
|
test_point,
|
||||||
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
||||||
seat,
|
|
||||||
)
|
)
|
||||||
.map(|(surface, surface_offset)| {
|
.map(|(surface, surface_offset)| {
|
||||||
(
|
(
|
||||||
|
|
|
||||||
207
src/shell/mod.rs
207
src/shell/mod.rs
|
|
@ -50,13 +50,10 @@ use smithay::{
|
||||||
},
|
},
|
||||||
utils::{IsAlive, Logical, Point, Rectangle, Serial, Size},
|
utils::{IsAlive, Logical, Point, Rectangle, Serial, Size},
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::{SurfaceAttributes, get_parent, with_states},
|
compositor::{SurfaceAttributes, with_states},
|
||||||
seat::WaylandFocus,
|
seat::WaylandFocus,
|
||||||
session_lock::LockSurface,
|
session_lock::LockSurface,
|
||||||
shell::{
|
shell::wlr_layer::{KeyboardInteractivity, Layer, LayerSurfaceCachedState},
|
||||||
wlr_layer::{KeyboardInteractivity, Layer, LayerSurfaceCachedState},
|
|
||||||
xdg::{XDG_POPUP_ROLE, XdgPopupSurfaceData},
|
|
||||||
},
|
|
||||||
xdg_activation::XdgActivationState,
|
xdg_activation::XdgActivationState,
|
||||||
xwayland_keyboard_grab::XWaylandKeyboardGrab,
|
xwayland_keyboard_grab::XWaylandKeyboardGrab,
|
||||||
},
|
},
|
||||||
|
|
@ -439,11 +436,6 @@ fn create_workspace_from_pinned(
|
||||||
| WorkspaceCapabilities::Pin
|
| WorkspaceCapabilities::Pin
|
||||||
| WorkspaceCapabilities::Move,
|
| WorkspaceCapabilities::Move,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(ref name) = pinned.name {
|
|
||||||
state.set_workspace_name(&workspace_handle, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
Workspace::from_pinned(
|
Workspace::from_pinned(
|
||||||
pinned,
|
pinned,
|
||||||
workspace_handle,
|
workspace_handle,
|
||||||
|
|
@ -624,7 +616,6 @@ impl WorkspaceSet {
|
||||||
state,
|
state,
|
||||||
self.workspaces.len() as u8 + 1,
|
self.workspaces.len() as u8 + 1,
|
||||||
&workspace.handle,
|
&workspace.handle,
|
||||||
workspace.name.as_deref(),
|
|
||||||
// this method is only used by code paths related to dynamic workspaces, so this should be fine
|
// this method is only used by code paths related to dynamic workspaces, so this should be fine
|
||||||
);
|
);
|
||||||
self.workspaces.push(workspace);
|
self.workspaces.push(workspace);
|
||||||
|
|
@ -686,12 +677,7 @@ impl WorkspaceSet {
|
||||||
|
|
||||||
fn update_workspace_idxs(&self, state: &mut WorkspaceUpdateGuard<'_, State>) {
|
fn update_workspace_idxs(&self, state: &mut WorkspaceUpdateGuard<'_, State>) {
|
||||||
for (i, workspace) in self.workspaces.iter().enumerate() {
|
for (i, workspace) in self.workspaces.iter().enumerate() {
|
||||||
workspace_set_idx(
|
workspace_set_idx(state, i as u8 + 1, &workspace.handle);
|
||||||
state,
|
|
||||||
i as u8 + 1,
|
|
||||||
&workspace.handle,
|
|
||||||
workspace.name.as_deref(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -742,78 +728,6 @@ impl WorkspaceSet {
|
||||||
self.post_remove_workspace(workspace_state, &previous_active_handle);
|
self.post_remove_workspace(workspace_state, &previous_active_handle);
|
||||||
prefers
|
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)]
|
#[derive(Debug)]
|
||||||
|
|
@ -1588,9 +1502,7 @@ impl Common {
|
||||||
self.popups.cleanup();
|
self.popups.cleanup();
|
||||||
self.toplevel_info_state.refresh(&self.workspace_state);
|
self.toplevel_info_state.refresh(&self.workspace_state);
|
||||||
self.refresh_idle_inhibit();
|
self.refresh_idle_inhibit();
|
||||||
if let Some(mut a11y_keyboard_monitor) = self.dbus_state.a11y_keyboard_monitor() {
|
self.a11y_keyboard_monitor_state.refresh();
|
||||||
a11y_keyboard_monitor.refresh();
|
|
||||||
}
|
|
||||||
self.image_copy_capture_state.cleanup();
|
self.image_copy_capture_state.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1650,13 +1562,12 @@ impl Common {
|
||||||
if let Some(mapped) = shell.element_for_surface(surface) {
|
if let Some(mapped) = shell.element_for_surface(surface) {
|
||||||
mapped.on_commit(surface);
|
mapped.on_commit(surface);
|
||||||
}
|
}
|
||||||
if let Some(fs) = shell
|
if let Some(surface) = shell
|
||||||
.workspaces
|
.workspaces
|
||||||
.spaces()
|
.spaces()
|
||||||
.flat_map(|w| w.get_fullscreen_surfaces())
|
.find_map(|w| w.get_fullscreen().filter(|s| *s == surface))
|
||||||
.find(|f| f.surface == *surface)
|
|
||||||
{
|
{
|
||||||
fs.surface.on_commit()
|
surface.on_commit()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
self.popups.commit(surface);
|
self.popups.commit(surface);
|
||||||
|
|
@ -1917,9 +1828,7 @@ impl Shell {
|
||||||
.outputs()
|
.outputs()
|
||||||
.find(|output| {
|
.find(|output| {
|
||||||
let workspace = self.active_space(output).unwrap();
|
let workspace = self.active_space(output).unwrap();
|
||||||
workspace
|
workspace.get_fullscreen() == Some(&elem)
|
||||||
.get_fullscreen_surfaces()
|
|
||||||
.any(|f| f.surface == elem)
|
|
||||||
})
|
})
|
||||||
.cloned(),
|
.cloned(),
|
||||||
KeyboardFocusTarget::Group(WindowGroup { node, .. }) => self
|
KeyboardFocusTarget::Group(WindowGroup { node, .. }) => self
|
||||||
|
|
@ -2052,8 +1961,8 @@ impl Shell {
|
||||||
let workspace = self.active_space(o).unwrap();
|
let workspace = self.active_space(o).unwrap();
|
||||||
|
|
||||||
workspace
|
workspace
|
||||||
.get_fullscreen_surfaces()
|
.get_fullscreen()
|
||||||
.any(|f| f.surface.has_surface(surface, WindowSurfaceType::ALL))
|
.is_some_and(|s| s.has_surface(surface, WindowSurfaceType::ALL))
|
||||||
|| workspace
|
|| workspace
|
||||||
.mapped()
|
.mapped()
|
||||||
.any(|e| e.has_surface(surface, WindowSurfaceType::ALL))
|
.any(|e| e.has_surface(surface, WindowSurfaceType::ALL))
|
||||||
|
|
@ -2108,8 +2017,8 @@ impl Shell {
|
||||||
.workspaces
|
.workspaces
|
||||||
.spaces()
|
.spaces()
|
||||||
.find(|w| {
|
.find(|w| {
|
||||||
w.get_fullscreen_surfaces()
|
w.get_fullscreen()
|
||||||
.any(|f| f.surface.has_surface(surface, WindowSurfaceType::ALL))
|
.is_some_and(|s| s.has_surface(surface, WindowSurfaceType::ALL))
|
||||||
|| w.mapped()
|
|| w.mapped()
|
||||||
.any(|e| e.has_surface(surface, WindowSurfaceType::ALL))
|
.any(|e| e.has_surface(surface, WindowSurfaceType::ALL))
|
||||||
|| w.minimized_windows.iter().any(|m| {
|
|| w.minimized_windows.iter().any(|m| {
|
||||||
|
|
@ -2156,7 +2065,7 @@ impl Shell {
|
||||||
.mapped()
|
.mapped()
|
||||||
.any(|m| m.windows().any(|(s, _)| &s == surface))
|
.any(|m| m.windows().any(|(s, _)| &s == surface))
|
||||||
|| set.workspaces.iter().any(|w| {
|
|| 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
|
|| w.minimized_windows
|
||||||
.iter()
|
.iter()
|
||||||
.any(|m| m.windows().any(|s| &s == surface))
|
.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);
|
let floating_exception = layout::has_floating_exception(&self.tiling_exceptions, &window);
|
||||||
|
|
||||||
if should_be_fullscreen {
|
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 {
|
if was_activated {
|
||||||
workspace_state.add_workspace_state(&workspace_handle, WState::Urgent);
|
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 from_workspace = self.workspaces.space_for_handle_mut(from).unwrap(); // checked above
|
||||||
|
|
||||||
let is_minimized = window.is_minimized();
|
let is_minimized = window.is_minimized();
|
||||||
let is_fullscreen = from_workspace
|
let is_fullscreen = from_workspace.get_fullscreen().is_some_and(|f| f == window);
|
||||||
.get_fullscreen_surfaces()
|
|
||||||
.any(|f| &f.surface == window);
|
|
||||||
let mut window_state = if is_fullscreen {
|
let mut window_state = if is_fullscreen {
|
||||||
let (_, previous_state, previous_geometry) =
|
let (_, previous_state, previous_geometry) = from_workspace.take_fullscreen().unwrap();
|
||||||
from_workspace.take_fullscreen(window).unwrap();
|
|
||||||
WorkspaceRestoreData::Fullscreen(previous_state.zip(previous_geometry).map(
|
WorkspaceRestoreData::Fullscreen(previous_state.zip(previous_geometry).map(
|
||||||
|(previous_state, previous_geometry)| FullscreenRestoreData {
|
|(previous_state, previous_geometry)| FullscreenRestoreData {
|
||||||
previous_state,
|
previous_state,
|
||||||
|
|
@ -3377,12 +3288,14 @@ impl Shell {
|
||||||
);
|
);
|
||||||
mapped.into()
|
mapped.into()
|
||||||
} else if let WorkspaceRestoreData::Fullscreen(previous) = window_state {
|
} else if let WorkspaceRestoreData::Fullscreen(previous) = window_state {
|
||||||
to_workspace.map_fullscreen(
|
if let Some((old_surface, previous_state, _)) = to_workspace.map_fullscreen(
|
||||||
window,
|
window,
|
||||||
None,
|
None,
|
||||||
previous.clone().map(|p| p.previous_state),
|
previous.clone().map(|p| p.previous_state),
|
||||||
previous.map(|p| p.previous_geometry),
|
previous.map(|p| p.previous_geometry),
|
||||||
);
|
) {
|
||||||
|
self.remap_unfullscreened_window(old_surface, previous_state, evlh);
|
||||||
|
}
|
||||||
window.clone().into()
|
window.clone().into()
|
||||||
} else {
|
} else {
|
||||||
unreachable!() // TODO: sticky
|
unreachable!() // TODO: sticky
|
||||||
|
|
@ -3600,12 +3513,8 @@ impl Shell {
|
||||||
} else if let Some((workspace, output)) = self.workspace_for_surface(surface) {
|
} else if let Some((workspace, output)) = self.workspace_for_surface(surface) {
|
||||||
let workspace = self.workspaces.space_for_handle(&workspace).unwrap();
|
let workspace = self.workspaces.space_for_handle(&workspace).unwrap();
|
||||||
|
|
||||||
if let Some(fs) = workspace
|
if let Some(window) = workspace.get_fullscreen().filter(|s| *s == surface) {
|
||||||
.get_fullscreen_surfaces()
|
let global_position = (workspace.fullscreen_geometry().unwrap().loc
|
||||||
.find(|f| &f.surface == surface)
|
|
||||||
{
|
|
||||||
let window = &fs.surface;
|
|
||||||
let global_position = (workspace.fullscreen_geometry_for(fs).loc
|
|
||||||
+ location.as_local())
|
+ location.as_local())
|
||||||
.to_global(&output);
|
.to_global(&output);
|
||||||
|
|
||||||
|
|
@ -3698,14 +3607,10 @@ impl Shell {
|
||||||
let maybe_fullscreen_workspace = self
|
let maybe_fullscreen_workspace = self
|
||||||
.workspaces
|
.workspaces
|
||||||
.spaces_mut()
|
.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 {
|
if let Some(workspace) = maybe_fullscreen_workspace {
|
||||||
let fs = workspace
|
element_geo = Some(workspace.fullscreen_geometry().unwrap());
|
||||||
.get_fullscreen_surfaces()
|
let (surface, state, _) = workspace.remove_fullscreen().unwrap();
|
||||||
.find(|f| &f.surface == surface)
|
|
||||||
.unwrap();
|
|
||||||
element_geo = Some(workspace.fullscreen_geometry_for(fs));
|
|
||||||
let (surface, state, _) = workspace.remove_fullscreen_surface(surface).unwrap();
|
|
||||||
self.remap_unfullscreened_window(surface, state, evlh);
|
self.remap_unfullscreened_window(surface, state, evlh);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -3730,11 +3635,7 @@ impl Shell {
|
||||||
if old_mapped.is_maximized(false) {
|
if old_mapped.is_maximized(false) {
|
||||||
new_mapped.set_maximized(false);
|
new_mapped.set_maximized(false);
|
||||||
}
|
}
|
||||||
start_data.set_focus(new_mapped.focus_under(
|
start_data.set_focus(new_mapped.focus_under((0., 0.).into(), WindowSurfaceType::ALL));
|
||||||
(0., 0.).into(),
|
|
||||||
WindowSurfaceType::ALL,
|
|
||||||
seat,
|
|
||||||
));
|
|
||||||
new_mapped
|
new_mapped
|
||||||
} else {
|
} else {
|
||||||
old_mapped.clone()
|
old_mapped.clone()
|
||||||
|
|
@ -3918,15 +3819,19 @@ impl Shell {
|
||||||
/// Get the window geometry of a keyboard focus target
|
/// Get the window geometry of a keyboard focus target
|
||||||
pub fn focused_geometry(&self, target: &KeyboardFocusTarget) -> Option<Rectangle<i32, Global>> {
|
pub fn focused_geometry(&self, target: &KeyboardFocusTarget) -> Option<Rectangle<i32, Global>> {
|
||||||
match target {
|
match target {
|
||||||
KeyboardFocusTarget::Fullscreen(surface) => surface
|
KeyboardFocusTarget::Fullscreen(surface) => {
|
||||||
|
if let Some(workspace) = surface
|
||||||
.wl_surface()
|
.wl_surface()
|
||||||
.and_then(|s| self.workspace_for_surface(&s))
|
.and_then(|s| self.workspace_for_surface(&s))
|
||||||
.and_then(|(handle, _)| self.workspaces.space_for_handle(&handle))
|
.and_then(|(handle, _)| self.workspaces.space_for_handle(&handle))
|
||||||
.map(|workspace| {
|
{
|
||||||
workspace
|
workspace
|
||||||
.fullscreen_geometry_for_surface(surface)
|
.fullscreen_geometry()
|
||||||
.to_global(workspace.output())
|
.map(|f| f.to_global(workspace.output()))
|
||||||
}),
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if let Some(element) = self.focused_element(target) {
|
if let Some(element) = self.focused_element(target) {
|
||||||
self.element_geometry(&element)
|
self.element_geometry(&element)
|
||||||
|
|
@ -4201,7 +4106,7 @@ impl Shell {
|
||||||
|
|
||||||
let element_offset = (new_loc - geometry.loc).as_logical();
|
let element_offset = (new_loc - geometry.loc).as_logical();
|
||||||
let focus = mapped
|
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())));
|
.map(|(target, surface_offset)| (target, (surface_offset + element_offset.to_f64())));
|
||||||
start_data.set_location(new_loc.as_logical().to_f64());
|
start_data.set_location(new_loc.as_logical().to_f64());
|
||||||
start_data.set_focus(focus.clone());
|
start_data.set_focus(focus.clone());
|
||||||
|
|
@ -4278,8 +4183,9 @@ impl Shell {
|
||||||
self.workspaces.sets.values_mut().find_map(|set| {
|
self.workspaces.sets.values_mut().find_map(|set| {
|
||||||
set.workspaces.iter_mut().find_map(|workspace| {
|
set.workspaces.iter_mut().find_map(|workspace| {
|
||||||
let window = workspace
|
let window = workspace
|
||||||
.get_fullscreen_surfaces()
|
.get_fullscreen()
|
||||||
.map(|f| f.surface.clone())
|
.cloned()
|
||||||
|
.into_iter()
|
||||||
.chain(workspace.mapped().map(|m| m.active_window()))
|
.chain(workspace.mapped().map(|m| m.active_window()))
|
||||||
.find(|s| s == surface);
|
.find(|s| s == surface);
|
||||||
window.map(|s| (workspace, s))
|
window.map(|s| (workspace, s))
|
||||||
|
|
@ -4779,16 +4685,15 @@ impl Shell {
|
||||||
&mut self,
|
&mut self,
|
||||||
surface: &S,
|
surface: &S,
|
||||||
output: Output,
|
output: Output,
|
||||||
_loop_handle: &LoopHandle<'static, State>,
|
loop_handle: &LoopHandle<'static, State>,
|
||||||
) -> Option<KeyboardFocusTarget>
|
) -> Option<KeyboardFocusTarget>
|
||||||
where
|
where
|
||||||
CosmicSurface: PartialEq<S>,
|
CosmicSurface: PartialEq<S>,
|
||||||
{
|
{
|
||||||
let mapped = self.element_for_surface(surface).cloned()?;
|
let mapped = self.element_for_surface(surface).cloned()?;
|
||||||
let seat = self.seats.last_active().clone();
|
|
||||||
let window;
|
let window;
|
||||||
|
|
||||||
if let Some((old_output, set)) = self
|
let old_fullscreen = if let Some((old_output, set)) = self
|
||||||
.workspaces
|
.workspaces
|
||||||
.sets
|
.sets
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
|
|
@ -4835,7 +4740,7 @@ impl Shell {
|
||||||
let workspace = self.active_space_mut(&output).unwrap();
|
let workspace = self.active_space_mut(&output).unwrap();
|
||||||
workspace.map_fullscreen(
|
workspace.map_fullscreen(
|
||||||
&window,
|
&window,
|
||||||
&seat,
|
None,
|
||||||
Some(FullscreenRestoreState::Sticky {
|
Some(FullscreenRestoreState::Sticky {
|
||||||
output: old_output,
|
output: old_output,
|
||||||
state: FloatingRestoreData {
|
state: FloatingRestoreData {
|
||||||
|
|
@ -4846,7 +4751,7 @@ impl Shell {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
Some(from),
|
Some(from),
|
||||||
);
|
)
|
||||||
} else if let Some(workspace) = self.space_for_mut(&mapped) {
|
} else if let Some(workspace) = self.space_for_mut(&mapped) {
|
||||||
if mapped.is_minimized() {
|
if mapped.is_minimized() {
|
||||||
// TODO: Rewrite the `MinimizedWindow` to restore to fullscreen
|
// TODO: Rewrite the `MinimizedWindow` to restore to fullscreen
|
||||||
|
|
@ -4872,7 +4777,7 @@ impl Shell {
|
||||||
|
|
||||||
workspace.map_fullscreen(
|
workspace.map_fullscreen(
|
||||||
&window,
|
&window,
|
||||||
&seat,
|
None,
|
||||||
match state {
|
match state {
|
||||||
WorkspaceRestoreData::Floating(floating_state) => {
|
WorkspaceRestoreData::Floating(floating_state) => {
|
||||||
floating_state.map(|state| FullscreenRestoreState::Floating {
|
floating_state.map(|state| FullscreenRestoreState::Floating {
|
||||||
|
|
@ -4889,11 +4794,15 @@ impl Shell {
|
||||||
WorkspaceRestoreData::Fullscreen(_) => unreachable!(),
|
WorkspaceRestoreData::Fullscreen(_) => unreachable!(),
|
||||||
},
|
},
|
||||||
Some(from),
|
Some(from),
|
||||||
);
|
)
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some((old_fullscreen, restore, _)) = old_fullscreen {
|
||||||
|
self.remap_unfullscreened_window(old_fullscreen, restore, loop_handle);
|
||||||
|
}
|
||||||
|
|
||||||
Some(KeyboardFocusTarget::Fullscreen(window))
|
Some(KeyboardFocusTarget::Fullscreen(window))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4908,12 +4817,11 @@ impl Shell {
|
||||||
let maybe_workspace = self.workspaces.iter_mut().find_map(|(_, s)| {
|
let maybe_workspace = self.workspaces.iter_mut().find_map(|(_, s)| {
|
||||||
s.workspaces
|
s.workspaces
|
||||||
.iter_mut()
|
.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 {
|
if let Some(workspace) = maybe_workspace {
|
||||||
let (old_fullscreen, restore, _) =
|
let (old_fullscreen, restore, _) = workspace.remove_fullscreen().unwrap();
|
||||||
workspace.remove_fullscreen_surface(surface).unwrap();
|
|
||||||
toplevel_leave_output(&old_fullscreen, &workspace.output);
|
toplevel_leave_output(&old_fullscreen, &workspace.output);
|
||||||
toplevel_leave_workspace(&old_fullscreen, &workspace.handle);
|
toplevel_leave_workspace(&old_fullscreen, &workspace.handle);
|
||||||
|
|
||||||
|
|
@ -5036,9 +4944,8 @@ fn workspace_set_idx(
|
||||||
state: &mut WorkspaceUpdateGuard<'_, State>,
|
state: &mut WorkspaceUpdateGuard<'_, State>,
|
||||||
idx: u8,
|
idx: u8,
|
||||||
handle: &WorkspaceHandle,
|
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]);
|
state.set_workspace_coordinates(handle, &[idx as u32]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,8 @@ use smithay::{
|
||||||
pointer::{CursorImageAttributes, CursorImageStatus},
|
pointer::{CursorImageAttributes, CursorImageStatus},
|
||||||
},
|
},
|
||||||
output::Output,
|
output::Output,
|
||||||
reexports::{
|
reexports::{input::Device as InputDevice, wayland_server::DisplayHandle},
|
||||||
input::Device as InputDevice,
|
utils::{Buffer, IsAlive, Monotonic, Point, Rectangle, Serial, Time, Transform},
|
||||||
wayland_server::{DisplayHandle, protocol::wl_surface::WlSurface},
|
|
||||||
},
|
|
||||||
utils::{Buffer, IsAlive, Logical, Monotonic, Point, Rectangle, Serial, Time, Transform},
|
|
||||||
wayland::compositor::with_states,
|
wayland::compositor::with_states,
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
@ -181,9 +178,6 @@ struct ActiveOutput(pub Mutex<Output>);
|
||||||
/// The output which currently has keyboard focus
|
/// The output which currently has keyboard focus
|
||||||
struct FocusedOutput(pub Mutex<Option<Output>>);
|
struct FocusedOutput(pub Mutex<Option<Output>>);
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct PointerConstraintHint(pub Mutex<Option<(WlSurface, Point<f64, Logical>)>>);
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct LastModifierChange(pub Mutex<Option<Serial>>);
|
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(CursorState::default);
|
||||||
userdata.insert_if_missing_threadsafe(|| ActiveOutput(Mutex::new(output.clone())));
|
userdata.insert_if_missing_threadsafe(|| ActiveOutput(Mutex::new(output.clone())));
|
||||||
userdata.insert_if_missing_threadsafe(|| FocusedOutput(Mutex::new(None)));
|
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()));
|
userdata.insert_if_missing_threadsafe(|| Mutex::new(CursorImageStatus::default_named()));
|
||||||
|
|
||||||
// A lot of clients bind keyboard and pointer unconditionally once on launch..
|
// 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 supressed_buttons(&self) -> &SupressedButtons;
|
||||||
fn modifiers_shortcut_queue(&self) -> &ModifiersShortcutQueue;
|
fn modifiers_shortcut_queue(&self) -> &ModifiersShortcutQueue;
|
||||||
fn last_modifier_change(&self) -> Option<Serial>;
|
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(
|
fn cursor_geometry(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -346,23 +337,6 @@ impl SeatExt for Seat<State> {
|
||||||
.unwrap()
|
.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(
|
fn cursor_geometry(
|
||||||
&self,
|
&self,
|
||||||
loc: impl Into<Point<f64, Buffer>>,
|
loc: impl Into<Point<f64, Buffer>>,
|
||||||
|
|
|
||||||
|
|
@ -108,10 +108,9 @@ pub struct Workspace {
|
||||||
pub floating_layer: FloatingLayout,
|
pub floating_layer: FloatingLayout,
|
||||||
pub minimized_windows: Vec<MinimizedWindow>,
|
pub minimized_windows: Vec<MinimizedWindow>,
|
||||||
pub tiling_enabled: bool,
|
pub tiling_enabled: bool,
|
||||||
pub fullscreen_surfaces: Vec<FullscreenSurface>,
|
pub fullscreen: Option<FullscreenSurface>,
|
||||||
pub pinned: bool,
|
pub pinned: bool,
|
||||||
pub id: Option<String>,
|
pub id: Option<String>,
|
||||||
pub name: Option<String>,
|
|
||||||
|
|
||||||
pub handle: WorkspaceHandle,
|
pub handle: WorkspaceHandle,
|
||||||
pub focus_stack: FocusStacks,
|
pub focus_stack: FocusStacks,
|
||||||
|
|
@ -384,10 +383,9 @@ impl Workspace {
|
||||||
floating_layer,
|
floating_layer,
|
||||||
tiling_enabled,
|
tiling_enabled,
|
||||||
minimized_windows: Vec::new(),
|
minimized_windows: Vec::new(),
|
||||||
fullscreen_surfaces: Vec::new(),
|
fullscreen: None,
|
||||||
pinned: false,
|
pinned: false,
|
||||||
id: None,
|
id: None,
|
||||||
name: None,
|
|
||||||
handle,
|
handle,
|
||||||
focus_stack: FocusStacks::default(),
|
focus_stack: FocusStacks::default(),
|
||||||
image_copy: ImageCopySessions::default(),
|
image_copy: ImageCopySessions::default(),
|
||||||
|
|
@ -418,10 +416,9 @@ impl Workspace {
|
||||||
floating_layer,
|
floating_layer,
|
||||||
tiling_enabled: pinned.tiling_enabled,
|
tiling_enabled: pinned.tiling_enabled,
|
||||||
minimized_windows: Vec::new(),
|
minimized_windows: Vec::new(),
|
||||||
fullscreen_surfaces: Vec::new(),
|
fullscreen: None,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
id: pinned.id.clone(),
|
id: pinned.id.clone(),
|
||||||
name: pinned.name.clone(),
|
|
||||||
handle,
|
handle,
|
||||||
focus_stack: FocusStacks::default(),
|
focus_stack: FocusStacks::default(),
|
||||||
image_copy: ImageCopySessions::default(),
|
image_copy: ImageCopySessions::default(),
|
||||||
|
|
@ -439,9 +436,9 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_pinned(&self) -> Option<PinnedWorkspace> {
|
pub fn to_pinned(&self) -> Option<PinnedWorkspace> {
|
||||||
|
debug_assert!(self.id.is_some());
|
||||||
let output = self.explicit_output().clone();
|
let output = self.explicit_output().clone();
|
||||||
if self.pinned {
|
if self.pinned {
|
||||||
debug_assert!(self.id.is_some());
|
|
||||||
Some(PinnedWorkspace {
|
Some(PinnedWorkspace {
|
||||||
output: cosmic_comp_config::workspace::OutputMatch {
|
output: cosmic_comp_config::workspace::OutputMatch {
|
||||||
name: output.name,
|
name: output.name,
|
||||||
|
|
@ -449,7 +446,6 @@ impl Workspace {
|
||||||
},
|
},
|
||||||
tiling_enabled: self.tiling_enabled,
|
tiling_enabled: self.tiling_enabled,
|
||||||
id: self.id.clone(),
|
id: self.id.clone(),
|
||||||
name: self.name.clone(),
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
@ -458,10 +454,7 @@ impl Workspace {
|
||||||
|
|
||||||
#[profiling::function]
|
#[profiling::function]
|
||||||
pub fn refresh(&mut self) {
|
pub fn refresh(&mut self) {
|
||||||
// seems it removes dead windows
|
self.fullscreen.take_if(|w| !w.alive());
|
||||||
// self.fullscreen.take_if(|w| !w.alive());
|
|
||||||
self.fullscreen_surfaces.retain(|w| w.alive());
|
|
||||||
|
|
||||||
self.floating_layer.refresh();
|
self.floating_layer.refresh();
|
||||||
self.tiling_layer.refresh();
|
self.tiling_layer.refresh();
|
||||||
}
|
}
|
||||||
|
|
@ -485,15 +478,14 @@ impl Workspace {
|
||||||
self.is_empty() && !self.has_activation_token(xdg_activation_state) && !self.pinned
|
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) {
|
pub fn refresh_focus_stack(&mut self) {
|
||||||
for (seat, stack) in self.focus_stack.0.iter_mut() {
|
for (seat, stack) in self.focus_stack.0.iter_mut() {
|
||||||
let fullscreen_surfaces: Vec<&CosmicSurface> = self
|
let fullscreen = self
|
||||||
.fullscreen_surfaces
|
.fullscreen
|
||||||
.iter()
|
.as_ref()
|
||||||
.filter(|f| f.alive() && f.ended_at.is_none())
|
.filter(|f| f.alive())
|
||||||
.map(|f| &f.surface)
|
.filter(|f| f.ended_at.is_none())
|
||||||
.collect();
|
.map(|f| &f.surface);
|
||||||
|
|
||||||
// Move grab is treated as focused, so don't change focus to a
|
// Move grab is treated as focused, so don't change focus to a
|
||||||
// window while grab exists.
|
// window while grab exists.
|
||||||
|
|
@ -514,7 +506,7 @@ impl Workspace {
|
||||||
.chain(move_mapped.iter())
|
.chain(move_mapped.iter())
|
||||||
};
|
};
|
||||||
stack.retain(|w| match w {
|
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),
|
FocusTarget::Window(w) => mapped().any(|m| w == m),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -523,12 +515,15 @@ impl Workspace {
|
||||||
pub fn animations_going(&self) -> bool {
|
pub fn animations_going(&self) -> bool {
|
||||||
self.tiling_layer.animations_going()
|
self.tiling_layer.animations_going()
|
||||||
|| self.floating_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)
|
|| self.dirty.swap(false, Ordering::SeqCst)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_animations(&mut self) -> HashMap<ClientId, Client> {
|
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() {
|
if let Some(start) = f.start_at.as_ref() {
|
||||||
let duration_since = Instant::now().duration_since(*start);
|
let duration_since = Instant::now().duration_since(*start);
|
||||||
if duration_since > FULLSCREEN_ANIMATION_DURATION {
|
if duration_since > FULLSCREEN_ANIMATION_DURATION {
|
||||||
|
|
@ -536,17 +531,15 @@ impl Workspace {
|
||||||
self.dirty.store(true, Ordering::SeqCst);
|
self.dirty.store(true, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
self.fullscreen_surfaces.retain(|f| {
|
if let Some(end) = f.ended_at {
|
||||||
if let Some(end) = f.ended_at
|
let duration_since = Instant::now().duration_since(end);
|
||||||
&& Instant::now().duration_since(end) >= FULLSCREEN_ANIMATION_DURATION
|
if duration_since >= FULLSCREEN_ANIMATION_DURATION {
|
||||||
{
|
let _ = self.fullscreen.take();
|
||||||
self.dirty.store(true, Ordering::SeqCst);
|
self.dirty.store(true, Ordering::SeqCst);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
true
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
let clients = self.tiling_layer.update_animation_state();
|
let clients = self.tiling_layer.update_animation_state();
|
||||||
self.floating_layer.update_animation_state();
|
self.floating_layer.update_animation_state();
|
||||||
|
|
@ -581,11 +574,7 @@ impl Workspace {
|
||||||
toplevel_enter_output(&surface, output);
|
toplevel_enter_output(&surface, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for f in self
|
if let Some(f) = self.fullscreen.as_ref().filter(|f| f.ended_at.is_none()) {
|
||||||
.fullscreen_surfaces
|
|
||||||
.iter()
|
|
||||||
.filter(|f| f.ended_at.is_none())
|
|
||||||
{
|
|
||||||
toplevel_leave_output(&f.surface, &self.output);
|
toplevel_leave_output(&f.surface, &self.output);
|
||||||
toplevel_enter_output(&f.surface, output);
|
toplevel_enter_output(&f.surface, output);
|
||||||
}
|
}
|
||||||
|
|
@ -676,13 +665,12 @@ impl Workspace {
|
||||||
where
|
where
|
||||||
CosmicSurface: PartialEq<S>,
|
CosmicSurface: PartialEq<S>,
|
||||||
{
|
{
|
||||||
if let Some(idx) = self
|
if self
|
||||||
.fullscreen_surfaces
|
.fullscreen
|
||||||
.iter()
|
.as_ref()
|
||||||
.position(|f| f.ended_at.is_none() && &f.surface == surface)
|
.is_some_and(|f| f.ended_at.is_none() && &f.surface == surface)
|
||||||
{
|
{
|
||||||
let (surface, previous_state, previous_geometry) =
|
let (surface, previous_state, previous_geometry) = self.remove_fullscreen().unwrap();
|
||||||
self.remove_fullscreen_at(idx).unwrap();
|
|
||||||
return Some((
|
return Some((
|
||||||
surface,
|
surface,
|
||||||
WorkspaceRestoreData::Fullscreen(previous_state.zip(previous_geometry).map(
|
WorkspaceRestoreData::Fullscreen(previous_state.zip(previous_geometry).map(
|
||||||
|
|
@ -735,11 +723,9 @@ impl Workspace {
|
||||||
Some((mapped.active_window(), layer))
|
Some((mapped.active_window(), layer))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fullscreen_geometry_for_surface(
|
pub fn fullscreen_geometry(&self) -> Option<Rectangle<i32, Local>> {
|
||||||
&self,
|
self.fullscreen.as_ref().map(|fullscreen| {
|
||||||
surface: &CosmicSurface,
|
let bbox = fullscreen.surface.bbox().as_local();
|
||||||
) -> Rectangle<i32, Local> {
|
|
||||||
let bbox = surface.bbox().as_local();
|
|
||||||
|
|
||||||
let mut full_geo = Rectangle::from_size(self.output.geometry().size.as_local());
|
let mut full_geo = Rectangle::from_size(self.output.geometry().size.as_local());
|
||||||
if bbox != full_geo {
|
if bbox != full_geo {
|
||||||
|
|
@ -754,9 +740,7 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
|
|
||||||
full_geo
|
full_geo
|
||||||
}
|
})
|
||||||
pub fn fullscreen_geometry_for(&self, fullscreen: &FullscreenSurface) -> Rectangle<i32, Local> {
|
|
||||||
self.fullscreen_geometry_for_surface(&fullscreen.surface)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn element_for_surface<S>(&self, surface: &S) -> Option<&CosmicMapped>
|
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 stack = self.focus_stack.get(seat);
|
||||||
let last_focused = stack.last();
|
let last_focused = stack.last();
|
||||||
|
|
||||||
if let Some(fullscreen) = self.fullscreen_surfaces.iter().find(|f| {
|
if let Some(fullscreen) = self.fullscreen.as_ref()
|
||||||
!f.is_animating()
|
&& last_focused.is_some_and(
|
||||||
&& last_focused
|
|t| matches!(t, FocusTarget::Fullscreen(f) if f == &fullscreen.surface),
|
||||||
.is_some_and(|t| matches!(t, FocusTarget::Fullscreen(s) if s == &f.surface))
|
)
|
||||||
}) {
|
&& !fullscreen.is_animating()
|
||||||
let geometry = self.fullscreen_geometry_for(fullscreen);
|
{
|
||||||
|
let geometry = self.fullscreen_geometry().unwrap();
|
||||||
return fullscreen_element_under(fullscreen, geometry);
|
return fullscreen_element_under(fullscreen, geometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.floating_layer
|
self.floating_layer
|
||||||
.popup_element_under(location, seat)
|
.popup_element_under(location)
|
||||||
.or_else(|| self.tiling_layer.popup_element_under(location, seat))
|
.or_else(|| self.tiling_layer.popup_element_under(location))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
if last_focused.is_none_or(|t| !matches!(t, FocusTarget::Fullscreen(_)))
|
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);
|
return fullscreen_element_under(fullscreen, geometry);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|
@ -845,23 +830,24 @@ impl Workspace {
|
||||||
let stack = self.focus_stack.get(seat);
|
let stack = self.focus_stack.get(seat);
|
||||||
let last_focused = stack.last();
|
let last_focused = stack.last();
|
||||||
|
|
||||||
if let Some(fullscreen) = self.fullscreen_surfaces.iter().find(|fs| {
|
if let Some(fullscreen) = self.fullscreen.as_ref()
|
||||||
!fs.is_animating()
|
&& last_focused.is_some_and(
|
||||||
&& last_focused
|
|t| matches!(t, FocusTarget::Fullscreen(f) if f == &fullscreen.surface),
|
||||||
.is_some_and(|t| matches!(t, FocusTarget::Fullscreen(f) if f == &fs.surface))
|
)
|
||||||
}) {
|
&& !fullscreen.is_animating()
|
||||||
let geometry = self.fullscreen_geometry_for(fullscreen);
|
{
|
||||||
|
let geometry = self.fullscreen_geometry().unwrap();
|
||||||
return fullscreen_element_under(fullscreen, geometry);
|
return fullscreen_element_under(fullscreen, geometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.floating_layer
|
self.floating_layer
|
||||||
.toplevel_element_under(location, seat)
|
.toplevel_element_under(location)
|
||||||
.or_else(|| self.tiling_layer.toplevel_element_under(location, seat))
|
.or_else(|| self.tiling_layer.toplevel_element_under(location))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
if last_focused.is_none_or(|t| !matches!(t, FocusTarget::Fullscreen(_)))
|
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);
|
return fullscreen_element_under(fullscreen, geometry);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|
@ -881,7 +867,7 @@ impl Workspace {
|
||||||
|
|
||||||
let check_fullscreen = |fullscreen: &FullscreenSurface| {
|
let check_fullscreen = |fullscreen: &FullscreenSurface| {
|
||||||
if !fullscreen.is_animating() {
|
if !fullscreen.is_animating() {
|
||||||
let geometry = self.fullscreen_geometry_for(fullscreen);
|
let geometry = self.fullscreen_geometry().unwrap();
|
||||||
return fullscreen
|
return fullscreen
|
||||||
.surface
|
.surface
|
||||||
.0
|
.0
|
||||||
|
|
@ -905,16 +891,18 @@ impl Workspace {
|
||||||
let stack = self.focus_stack.get(seat);
|
let stack = self.focus_stack.get(seat);
|
||||||
let last_focused = stack.last();
|
let last_focused = stack.last();
|
||||||
|
|
||||||
self.fullscreen_surfaces
|
self.fullscreen
|
||||||
.iter()
|
.as_ref()
|
||||||
.find(|f| last_focused.is_some_and(|t| t == &f.surface))
|
.filter(|f| last_focused.is_some_and(|t| t == &f.surface))
|
||||||
.and_then(check_fullscreen)
|
.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(|| {
|
.or_else(|| {
|
||||||
self.tiling_layer
|
self.fullscreen
|
||||||
.popup_surface_under(location, overview, seat)
|
.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)))
|
.map(|(m, p)| (m, p.to_global(&self.output)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -931,7 +919,7 @@ impl Workspace {
|
||||||
|
|
||||||
let check_fullscreen = |fullscreen: &FullscreenSurface| {
|
let check_fullscreen = |fullscreen: &FullscreenSurface| {
|
||||||
if !fullscreen.is_animating() {
|
if !fullscreen.is_animating() {
|
||||||
let geometry = self.fullscreen_geometry_for(fullscreen);
|
let geometry = self.fullscreen_geometry().unwrap();
|
||||||
return fullscreen
|
return fullscreen
|
||||||
.surface
|
.surface
|
||||||
.focus_under(
|
.focus_under(
|
||||||
|
|
@ -949,16 +937,18 @@ impl Workspace {
|
||||||
let stack = self.focus_stack.get(seat);
|
let stack = self.focus_stack.get(seat);
|
||||||
let last_focused = stack.last();
|
let last_focused = stack.last();
|
||||||
|
|
||||||
self.fullscreen_surfaces
|
self.fullscreen
|
||||||
.iter()
|
.as_ref()
|
||||||
.find(|f| last_focused.is_some_and(|t| t == &f.surface))
|
.filter(|f| last_focused.is_some_and(|t| t == &f.surface))
|
||||||
.and_then(check_fullscreen)
|
.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(|| {
|
.or_else(|| {
|
||||||
self.tiling_layer
|
self.fullscreen
|
||||||
.toplevel_surface_under(location, overview, seat)
|
.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)))
|
.map(|(m, p)| (m, p.to_global(&self.output)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1030,14 +1020,10 @@ impl Workspace {
|
||||||
where
|
where
|
||||||
CosmicSurface: PartialEq<S>,
|
CosmicSurface: PartialEq<S>,
|
||||||
{
|
{
|
||||||
if let Some(idx) = self
|
if self.get_fullscreen().is_some_and(|s| s == surface) {
|
||||||
.fullscreen_surfaces
|
let fullscreen_state = self.fullscreen.clone().unwrap();
|
||||||
.iter()
|
|
||||||
.position(|f| f.ended_at.is_none() && &f.surface == surface)
|
|
||||||
{
|
{
|
||||||
let fullscreen_state = self.fullscreen_surfaces.get(idx)?.clone();
|
let f = self.fullscreen.as_mut().unwrap();
|
||||||
{
|
|
||||||
let f = self.fullscreen_surfaces.get_mut(idx)?;
|
|
||||||
f.previous_geometry = Some(to);
|
f.previous_geometry = Some(to);
|
||||||
f.ended_at = Some(
|
f.ended_at = Some(
|
||||||
Instant::now()
|
Instant::now()
|
||||||
|
|
@ -1141,20 +1127,16 @@ impl Workspace {
|
||||||
)> {
|
)> {
|
||||||
match window {
|
match window {
|
||||||
MinimizedWindow::Fullscreen { previous, surface } => {
|
MinimizedWindow::Fullscreen { previous, surface } => {
|
||||||
|
let old_fullscreen = self.remove_fullscreen();
|
||||||
surface.set_minimized(false);
|
surface.set_minimized(false);
|
||||||
// focus it so it's the top fullscreen window
|
self.fullscreen = Some(FullscreenSurface {
|
||||||
self.focus_stack
|
|
||||||
.get_mut(seat)
|
|
||||||
.append(FocusTarget::Fullscreen(surface.clone()));
|
|
||||||
self.fullscreen_surfaces.push(FullscreenSurface {
|
|
||||||
surface,
|
surface,
|
||||||
previous_state: previous.clone().map(|p| p.previous_state),
|
previous_state: previous.clone().map(|p| p.previous_state),
|
||||||
previous_geometry: previous.map(|p| p.previous_geometry),
|
previous_geometry: previous.map(|p| p.previous_geometry),
|
||||||
start_at: None,
|
start_at: None,
|
||||||
ended_at: None,
|
ended_at: None,
|
||||||
});
|
});
|
||||||
self.dirty.store(true, Ordering::SeqCst);
|
old_fullscreen
|
||||||
None
|
|
||||||
}
|
}
|
||||||
MinimizedWindow::Floating { window, previous } => {
|
MinimizedWindow::Floating { window, previous } => {
|
||||||
let current_output_size = self.output.geometry().size.as_logical();
|
let current_output_size = self.output.geometry().size.as_logical();
|
||||||
|
|
@ -1242,7 +1224,13 @@ impl Workspace {
|
||||||
seat: impl Into<Option<&'a Seat<State>>>,
|
seat: impl Into<Option<&'a Seat<State>>>,
|
||||||
restore: Option<FullscreenRestoreState>,
|
restore: Option<FullscreenRestoreState>,
|
||||||
previous_geometry: Option<Rectangle<i32, Local>>,
|
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_fullscreen(true);
|
||||||
window.set_geometry(self.output.geometry(), 0);
|
window.set_geometry(self.output.geometry(), 0);
|
||||||
window.send_configure();
|
window.send_configure();
|
||||||
|
|
@ -1255,53 +1243,47 @@ impl Workspace {
|
||||||
self.focus_stack.get_mut(seat).append(window.clone());
|
self.focus_stack.get_mut(seat).append(window.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dirty.store(true, Ordering::SeqCst);
|
self.fullscreen = Some(FullscreenSurface {
|
||||||
self.fullscreen_surfaces.push(FullscreenSurface {
|
|
||||||
surface: window.clone(),
|
surface: window.clone(),
|
||||||
previous_state: restore,
|
previous_state: restore,
|
||||||
previous_geometry,
|
previous_geometry,
|
||||||
start_at: Some(Instant::now()),
|
start_at: Some(Instant::now()),
|
||||||
ended_at: None,
|
ended_at: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn take_fullscreen<S>(
|
pub fn take_fullscreen(
|
||||||
&mut self,
|
&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<(
|
) -> Option<(
|
||||||
CosmicSurface,
|
CosmicSurface,
|
||||||
Option<FullscreenRestoreState>,
|
Option<FullscreenRestoreState>,
|
||||||
Option<Rectangle<i32, Local>>,
|
Option<Rectangle<i32, Local>>,
|
||||||
)> {
|
)> {
|
||||||
// if it doesn't exist we move on.
|
let surface = self.fullscreen.take_if(|s| s.ended_at.is_none())?;
|
||||||
let surface = self.fullscreen_surfaces.get_mut(idx)?;
|
|
||||||
// if already being removed, do nothing
|
for focus_stack in self.focus_stack.0.values_mut() {
|
||||||
|
focus_stack.retain(|t| t != &surface.surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((
|
||||||
|
surface.surface,
|
||||||
|
surface.previous_state,
|
||||||
|
surface.previous_geometry,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn remove_fullscreen(
|
||||||
|
&mut self,
|
||||||
|
) -> Option<(
|
||||||
|
CosmicSurface,
|
||||||
|
Option<FullscreenRestoreState>,
|
||||||
|
Option<Rectangle<i32, Local>>,
|
||||||
|
)> {
|
||||||
|
if let Some(surface) = self.fullscreen.as_mut() {
|
||||||
if surface.ended_at.is_some() {
|
if surface.ended_at.is_some() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
@ -1340,52 +1322,17 @@ impl Workspace {
|
||||||
surface.previous_state.clone(),
|
surface.previous_state.clone(),
|
||||||
surface.previous_geometry,
|
surface.previous_geometry,
|
||||||
))
|
))
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn remove_fullscreen_surface<S>(
|
|
||||||
&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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.or_else(|| {
|
|
||||||
self.fullscreen_surfaces
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.find(|f| f.alive() && f.ended_at.is_none())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_fullscreen_surfaces(&self) -> impl Iterator<Item = &FullscreenSurface> {
|
pub fn get_fullscreen(&self) -> Option<&CosmicSurface> {
|
||||||
self.fullscreen_surfaces
|
self.fullscreen
|
||||||
.iter()
|
.as_ref()
|
||||||
.filter(|f| f.alive() && f.ended_at.is_none())
|
.filter(|f| f.alive())
|
||||||
|
.filter(|f| f.ended_at.is_none())
|
||||||
|
.map(|f| &f.surface)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(
|
pub fn resize(
|
||||||
|
|
@ -1512,18 +1459,14 @@ impl Workspace {
|
||||||
self.floating_layer.mapped().count()
|
self.floating_layer.mapped().count()
|
||||||
+ self.tiling_layer.mapped().count()
|
+ self.tiling_layer.mapped().count()
|
||||||
+ self.minimized_windows.len()
|
+ self.minimized_windows.len()
|
||||||
+ self
|
+ if self.fullscreen.is_some() { 1 } else { 0 }
|
||||||
.fullscreen_surfaces
|
|
||||||
.iter()
|
|
||||||
.filter(|f| f.ended_at.is_none())
|
|
||||||
.count()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.floating_layer.mapped().next().is_none()
|
self.floating_layer.mapped().next().is_none()
|
||||||
&& self.tiling_layer.mapped().next().is_none()
|
&& self.tiling_layer.mapped().next().is_none()
|
||||||
&& self.minimized_windows.is_empty()
|
&& self.minimized_windows.is_empty()
|
||||||
&& self.fullscreen_surfaces.is_empty()
|
&& self.fullscreen.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_floating<S>(&self, surface: &S) -> bool
|
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 focused = self.focus_stack.get(last_active_seat).last().cloned();
|
||||||
|
|
||||||
let render_fullscreen = |fullscreen: &FullscreenSurface,
|
let mut fullscreen_elements = if let Some(fullscreen) = self.fullscreen.as_ref() {
|
||||||
renderer: &mut R,
|
let fullscreen_geo = self.fullscreen_geometry().unwrap();
|
||||||
output_scale: f64|
|
|
||||||
-> Vec<WorkspaceRenderElement<R>> {
|
|
||||||
let fullscreen_geo = self.fullscreen_geometry_for(fullscreen);
|
|
||||||
let previous_geo = fullscreen
|
let previous_geo = fullscreen
|
||||||
.previous_geometry
|
.previous_geometry
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -1704,35 +1644,20 @@ impl Workspace {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(animation_rescale)
|
.map(animation_rescale)
|
||||||
.collect::<Vec<_>>()
|
.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(_))) {
|
if matches!(focused, Some(FocusTarget::Fullscreen(_))) {
|
||||||
elements.append(&mut fullscreen_elements);
|
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(_)))
|
if !matches!(focused, Some(FocusTarget::Fullscreen(_)))
|
||||||
|| any_fullscreen_animating
|
|
||||||
|| self
|
|| self
|
||||||
.fullscreen_surfaces
|
.fullscreen
|
||||||
.iter()
|
.as_ref()
|
||||||
.all(|f| !f.alive() || f.ended_at.is_some())
|
.map(|f| f.start_at.is_some() || f.ended_at.is_some())
|
||||||
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
// floating surfaces
|
// floating surfaces
|
||||||
let alpha = match &overview.0 {
|
let alpha = match &overview.0 {
|
||||||
|
|
@ -1757,17 +1682,13 @@ impl Workspace {
|
||||||
self.floating_layer
|
self.floating_layer
|
||||||
.render::<R>(
|
.render::<R>(
|
||||||
renderer,
|
renderer,
|
||||||
render_focus
|
|
||||||
.then(|| {
|
|
||||||
focused.as_ref().and_then(|target| {
|
focused.as_ref().and_then(|target| {
|
||||||
if let FocusTarget::Window(mapped) = target {
|
if let FocusTarget::Window(mapped) = target {
|
||||||
Some(mapped)
|
Some(mapped)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
})
|
|
||||||
.flatten(),
|
|
||||||
resize_indicator.clone(),
|
resize_indicator.clone(),
|
||||||
indicator_thickness,
|
indicator_thickness,
|
||||||
alpha,
|
alpha,
|
||||||
|
|
@ -1853,12 +1774,8 @@ impl Workspace {
|
||||||
layer_map.non_exclusive_zone().as_local()
|
layer_map.non_exclusive_zone().as_local()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render popups for the top (most recently focused) fullscreen
|
if let Some(fullscreen) = self.fullscreen.as_ref() {
|
||||||
let focus_stack = self.focus_stack.get(last_active_seat);
|
let fullscreen_geo = self.fullscreen_geometry().unwrap();
|
||||||
let top_fullscreen = self.get_fullscreen(last_active_seat);
|
|
||||||
|
|
||||||
if let Some(fullscreen) = top_fullscreen {
|
|
||||||
let fullscreen_geo = self.fullscreen_geometry_for(fullscreen);
|
|
||||||
let previous_geo = fullscreen
|
let previous_geo = fullscreen
|
||||||
.previous_geometry
|
.previous_geometry
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -1915,16 +1832,13 @@ impl Workspace {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let any_fullscreen_animating = self
|
let focus_stack = self.focus_stack.get(last_active_seat);
|
||||||
.fullscreen_surfaces
|
|
||||||
.iter()
|
|
||||||
.any(|f| f.start_at.is_some() || f.ended_at.is_some());
|
|
||||||
if !matches!(focus_stack.last(), Some(FocusTarget::Fullscreen(_)))
|
if !matches!(focus_stack.last(), Some(FocusTarget::Fullscreen(_)))
|
||||||
|| any_fullscreen_animating
|
|
||||||
|| self
|
|| self
|
||||||
.fullscreen_surfaces
|
.fullscreen
|
||||||
.iter()
|
.as_ref()
|
||||||
.all(|f| !f.alive() || f.ended_at.is_some())
|
.map(|f| f.start_at.is_some() || f.ended_at.is_some())
|
||||||
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
// floating surfaces
|
// floating surfaces
|
||||||
let alpha = match &overview.0 {
|
let alpha = match &overview.0 {
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ use smithay::{
|
||||||
MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent,
|
MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent,
|
||||||
},
|
},
|
||||||
touch::{
|
touch::{
|
||||||
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
|
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
|
||||||
TouchTarget, UpEvent,
|
UpEvent,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output::Output,
|
output::Output,
|
||||||
|
|
@ -259,8 +259,10 @@ impl ZoomState {
|
||||||
original_position: Point<f64, Global>,
|
original_position: Point<f64, Global>,
|
||||||
movement: ZoomMovement,
|
movement: ZoomMovement,
|
||||||
) {
|
) {
|
||||||
let output_geometry = output.geometry().to_f64();
|
let cursor_position = cursor_position.to_i32_round();
|
||||||
let mut zoomed_output_geometry = output.zoomed_geometry().unwrap().to_f64();
|
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 output_state = output.user_data().get::<Mutex<OutputZoomState>>().unwrap();
|
||||||
let mut output_state_ref = output_state.lock().unwrap();
|
let mut output_state_ref = output_state.lock().unwrap();
|
||||||
|
|
@ -273,40 +275,44 @@ impl ZoomState {
|
||||||
|
|
||||||
let cursor_position = cursor_position.to_local(output);
|
let cursor_position = cursor_position.to_local(output);
|
||||||
match movement {
|
match movement {
|
||||||
ZoomMovement::Continuously => output_state_ref.focal_point = cursor_position,
|
ZoomMovement::Continuously => output_state_ref.focal_point = cursor_position.to_f64(),
|
||||||
ZoomMovement::OnEdge => {
|
ZoomMovement::OnEdge => {
|
||||||
if !zoomed_output_geometry
|
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.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
|
let mut focal_point = zoomed_output_geometry
|
||||||
.loc
|
.loc
|
||||||
.to_local(output)
|
.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);
|
.to_global(output);
|
||||||
focal_point.x = focal_point.x.clamp(
|
focal_point.x = focal_point.x.clamp(
|
||||||
output_geometry.loc.x,
|
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(
|
focal_point.y = focal_point.y.clamp(
|
||||||
output_geometry.loc.y,
|
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 =
|
output_state_ref.previous_point =
|
||||||
Some((output_state_ref.focal_point, Instant::now()));
|
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)) {
|
} else if !zoomed_output_geometry.contains(cursor_position.to_global(output)) {
|
||||||
let mut diff = output_state_ref.focal_point.to_global(output)
|
let mut diff = output_state_ref.focal_point.to_global(output)
|
||||||
+ (cursor_position.to_global(output) - original_position)
|
+ (cursor_position.to_global(output) - original_position)
|
||||||
|
.to_f64()
|
||||||
.upscale(output_state_ref.level);
|
.upscale(output_state_ref.level);
|
||||||
diff.x = diff.x.clamp(
|
diff.x = diff.x.clamp(
|
||||||
output_geometry.loc.x,
|
output_geometry.loc.x as f64,
|
||||||
(output_geometry.loc.x + output_geometry.size.w).next_down(),
|
((output_geometry.loc.x + output_geometry.size.w) as f64).next_down(),
|
||||||
);
|
);
|
||||||
diff.y = diff.y.clamp(
|
diff.y = diff.y.clamp(
|
||||||
output_geometry.loc.y,
|
output_geometry.loc.y as f64,
|
||||||
(output_geometry.loc.y + output_geometry.size.h).next_down(),
|
((output_geometry.loc.y + output_geometry.size.h) as f64).next_down(),
|
||||||
);
|
);
|
||||||
diff -= output_state_ref.focal_point.to_global(output);
|
diff -= output_state_ref.focal_point.to_global(output);
|
||||||
|
|
||||||
|
|
@ -315,27 +321,28 @@ impl ZoomState {
|
||||||
}
|
}
|
||||||
ZoomMovement::Centered => {
|
ZoomMovement::Centered => {
|
||||||
zoomed_output_geometry.loc = cursor_position.to_global(output)
|
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
|
let mut focal_point = zoomed_output_geometry
|
||||||
.loc
|
.loc
|
||||||
.to_local(output)
|
.to_local(output)
|
||||||
.upscale(
|
.upscale(
|
||||||
(output_geometry.size.w
|
output_geometry
|
||||||
/ (output_geometry.size.w - zoomed_output_geometry.size.w)
|
.size
|
||||||
.max(f64::EPSILON))
|
.w
|
||||||
.max(1.),
|
.checked_div(output_geometry.size.w - zoomed_output_geometry.size.w)
|
||||||
|
.unwrap_or(1),
|
||||||
)
|
)
|
||||||
.to_global(output);
|
.to_global(output);
|
||||||
focal_point.x = focal_point.x.clamp(
|
focal_point.x = focal_point.x.clamp(
|
||||||
output_geometry.loc.x,
|
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(
|
focal_point.y = focal_point.y.clamp(
|
||||||
output_geometry.loc.y,
|
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 {
|
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 {
|
match self {
|
||||||
ZoomFocusTarget::Main(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),
|
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 {
|
match self {
|
||||||
ZoomFocusTarget::Main(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),
|
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 {
|
match self {
|
||||||
ZoomFocusTarget::Main(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),
|
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 {
|
match self {
|
||||||
ZoomFocusTarget::Main(elem) => TouchTarget::frame(elem, seat, data, frame),
|
ZoomFocusTarget::Main(elem) => TouchTarget::frame(elem, seat, data, seq),
|
||||||
ZoomFocusTarget::Menu(elem) => TouchTarget::frame(elem, seat, data, frame),
|
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 {
|
match self {
|
||||||
ZoomFocusTarget::Main(elem) => TouchTarget::cancel(elem, seat, data, frame),
|
ZoomFocusTarget::Main(elem) => TouchTarget::cancel(elem, seat, data, seq),
|
||||||
ZoomFocusTarget::Menu(elem) => TouchTarget::cancel(elem, seat, data, frame),
|
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 {
|
match self {
|
||||||
ZoomFocusTarget::Main(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),
|
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 {
|
match self {
|
||||||
ZoomFocusTarget::Main(elem) => TouchTarget::orientation(elem, seat, data, event),
|
ZoomFocusTarget::Main(elem) => TouchTarget::orientation(elem, seat, data, event, seq),
|
||||||
ZoomFocusTarget::Menu(elem) => TouchTarget::orientation(elem, seat, data, event),
|
ZoomFocusTarget::Menu(elem) => TouchTarget::orientation(elem, seat, data, event, seq),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
54
src/state.rs
54
src/state.rs
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
x11::X11State,
|
x11::X11State,
|
||||||
},
|
},
|
||||||
config::{CompOutputConfig, Config, ScreenFilter},
|
config::{CompOutputConfig, Config, ScreenFilter},
|
||||||
dbus::DBusState,
|
dbus::a11y_keyboard_monitor::A11yKeyboardMonitorState,
|
||||||
input::{PointerFocusState, gestures::GestureState},
|
input::{PointerFocusState, gestures::GestureState},
|
||||||
shell::{CosmicSurface, SeatExt, Shell, grabs::SeatMoveGrabState},
|
shell::{CosmicSurface, SeatExt, Shell, grabs::SeatMoveGrabState},
|
||||||
utils::prelude::OutputExt,
|
utils::prelude::OutputExt,
|
||||||
|
|
@ -32,6 +32,7 @@ use crate::{
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use calloop::RegistrationToken;
|
use calloop::RegistrationToken;
|
||||||
use cosmic_comp_config::output::comp::{OutputConfig, OutputState};
|
use cosmic_comp_config::output::comp::{OutputConfig, OutputState};
|
||||||
|
use futures_executor::ThreadPool;
|
||||||
use i18n_embed::{
|
use i18n_embed::{
|
||||||
DesktopLanguageRequester,
|
DesktopLanguageRequester,
|
||||||
fluent::{FluentLanguageLoader, fluent_language_loader},
|
fluent::{FluentLanguageLoader, fluent_language_loader},
|
||||||
|
|
@ -86,7 +87,6 @@ use smithay::{
|
||||||
output::OutputManagerState,
|
output::OutputManagerState,
|
||||||
pointer_constraints::PointerConstraintsState,
|
pointer_constraints::PointerConstraintsState,
|
||||||
pointer_gestures::PointerGesturesState,
|
pointer_gestures::PointerGesturesState,
|
||||||
pointer_warp::PointerWarpManager,
|
|
||||||
presentation::PresentationState,
|
presentation::PresentationState,
|
||||||
seat::WaylandFocus,
|
seat::WaylandFocus,
|
||||||
security_context::{SecurityContext, SecurityContextState},
|
security_context::{SecurityContext, SecurityContextState},
|
||||||
|
|
@ -117,7 +117,7 @@ use smithay::{
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
#[cfg(feature = "logind")]
|
#[cfg(feature = "systemd")]
|
||||||
use std::os::fd::OwnedFd;
|
use std::os::fd::OwnedFd;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -234,6 +234,7 @@ pub struct Common {
|
||||||
pub display_handle: DisplayHandle,
|
pub display_handle: DisplayHandle,
|
||||||
pub event_loop_handle: LoopHandle<'static, State>,
|
pub event_loop_handle: LoopHandle<'static, State>,
|
||||||
pub event_loop_signal: LoopSignal,
|
pub event_loop_signal: LoopSignal,
|
||||||
|
pub async_executor: ThreadPool,
|
||||||
|
|
||||||
pub popups: PopupManager,
|
pub popups: PopupManager,
|
||||||
pub shell: Arc<parking_lot::RwLock<Shell>>,
|
pub shell: Arc<parking_lot::RwLock<Shell>>,
|
||||||
|
|
@ -278,7 +279,7 @@ pub struct Common {
|
||||||
pub xdg_decoration_state: XdgDecorationState,
|
pub xdg_decoration_state: XdgDecorationState,
|
||||||
pub overlap_notify_state: OverlapNotifyState,
|
pub overlap_notify_state: OverlapNotifyState,
|
||||||
pub a11y_state: A11yState,
|
pub a11y_state: A11yState,
|
||||||
pub dbus_state: DBusState,
|
pub a11y_keyboard_monitor_state: A11yKeyboardMonitorState,
|
||||||
|
|
||||||
// shell-related wayland state
|
// shell-related wayland state
|
||||||
pub xdg_shell_state: XdgShellState,
|
pub xdg_shell_state: XdgShellState,
|
||||||
|
|
@ -293,7 +294,7 @@ pub struct Common {
|
||||||
pub xwayland_shell_state: XWaylandShellState,
|
pub xwayland_shell_state: XWaylandShellState,
|
||||||
pub pointer_focus_state: Option<PointerFocusState>,
|
pub pointer_focus_state: Option<PointerFocusState>,
|
||||||
|
|
||||||
#[cfg(feature = "logind")]
|
#[cfg(feature = "systemd")]
|
||||||
pub inhibit_lid_fd: Option<OwnedFd>,
|
pub inhibit_lid_fd: Option<OwnedFd>,
|
||||||
|
|
||||||
pub with_xwayland: bool,
|
pub with_xwayland: bool,
|
||||||
|
|
@ -672,7 +673,6 @@ impl State {
|
||||||
XWaylandKeyboardGrabState::new::<Self>(dh);
|
XWaylandKeyboardGrabState::new::<Self>(dh);
|
||||||
let xwayland_shell_state = XWaylandShellState::new::<Self>(dh);
|
let xwayland_shell_state = XWaylandShellState::new::<Self>(dh);
|
||||||
PointerConstraintsState::new::<Self>(dh);
|
PointerConstraintsState::new::<Self>(dh);
|
||||||
PointerWarpManager::new::<Self>(dh);
|
|
||||||
PointerGesturesState::new::<Self>(dh);
|
PointerGesturesState::new::<Self>(dh);
|
||||||
TabletManagerState::new::<Self>(dh);
|
TabletManagerState::new::<Self>(dh);
|
||||||
SecurityContextState::new::<Self, _>(dh, client_has_no_security_context);
|
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 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 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 {
|
State {
|
||||||
common: Common {
|
common: Common {
|
||||||
|
|
@ -738,6 +744,7 @@ impl State {
|
||||||
display_handle: dh.clone(),
|
display_handle: dh.clone(),
|
||||||
event_loop_handle: handle,
|
event_loop_handle: handle,
|
||||||
event_loop_signal: signal,
|
event_loop_signal: signal,
|
||||||
|
async_executor,
|
||||||
|
|
||||||
popups: PopupManager::default(),
|
popups: PopupManager::default(),
|
||||||
shell,
|
shell,
|
||||||
|
|
@ -787,13 +794,13 @@ impl State {
|
||||||
xdg_foreign_state,
|
xdg_foreign_state,
|
||||||
workspace_state,
|
workspace_state,
|
||||||
a11y_state,
|
a11y_state,
|
||||||
|
a11y_keyboard_monitor_state,
|
||||||
xwayland_scale: None,
|
xwayland_scale: None,
|
||||||
xwayland_state: None,
|
xwayland_state: None,
|
||||||
xwayland_shell_state,
|
xwayland_shell_state,
|
||||||
pointer_focus_state: None,
|
pointer_focus_state: None,
|
||||||
dbus_state,
|
|
||||||
|
|
||||||
#[cfg(feature = "logind")]
|
#[cfg(feature = "systemd")]
|
||||||
inhibit_lid_fd: None,
|
inhibit_lid_fd: None,
|
||||||
|
|
||||||
with_xwayland,
|
with_xwayland,
|
||||||
|
|
@ -818,7 +825,7 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_inhibitor_locks(&mut self) {
|
fn update_inhibitor_locks(&mut self) {
|
||||||
#[cfg(feature = "logind")]
|
#[cfg(feature = "systemd")]
|
||||||
{
|
{
|
||||||
use smithay::backend::session::Session;
|
use smithay::backend::session::Session;
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
|
|
@ -834,7 +841,7 @@ impl State {
|
||||||
|
|
||||||
if should_handle_lid {
|
if should_handle_lid {
|
||||||
if self.common.inhibit_lid_fd.is_none() {
|
if self.common.inhibit_lid_fd.is_none() {
|
||||||
match crate::dbus::logind::inhibit_lid(&self.common) {
|
match crate::dbus::logind::inhibit_lid() {
|
||||||
Ok(fd) => {
|
Ok(fd) => {
|
||||||
debug!("Inhibiting lid switch");
|
debug!("Inhibiting lid switch");
|
||||||
self.common.inhibit_lid_fd = Some(fd);
|
self.common.inhibit_lid_fd = Some(fd);
|
||||||
|
|
@ -845,8 +852,7 @@ impl State {
|
||||||
.iter()
|
.iter()
|
||||||
.find(|o| o.is_internal())
|
.find(|o| o.is_internal())
|
||||||
.cloned();
|
.cloned();
|
||||||
let closed =
|
let closed = crate::dbus::logind::lid_closed().unwrap_or(false);
|
||||||
crate::dbus::logind::lid_closed(&self.common).unwrap_or(false);
|
|
||||||
|
|
||||||
if closed {
|
if closed {
|
||||||
backend.disable_internal_output(
|
backend.disable_internal_output(
|
||||||
|
|
@ -990,8 +996,8 @@ impl Common {
|
||||||
|
|
||||||
// normal windows
|
// normal windows
|
||||||
for space in shell.workspaces.spaces() {
|
for space in shell.workspaces.spaces() {
|
||||||
if let Some(fs) = space.get_fullscreen(shell.seats.last_active()) {
|
if let Some(window) = space.get_fullscreen() {
|
||||||
fs.surface.with_surfaces(processor);
|
window.with_surfaces(processor);
|
||||||
}
|
}
|
||||||
space.mapped().for_each(|mapped| {
|
space.mapped().for_each(|mapped| {
|
||||||
for (window, _) in mapped.windows() {
|
for (window, _) in mapped.windows() {
|
||||||
|
|
@ -1156,16 +1162,15 @@ impl Common {
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(active) = shell.active_space(output) {
|
if let Some(active) = shell.active_space(output) {
|
||||||
if let Some(fs) = active.get_fullscreen(shell.seats.last_active())
|
if let Some(window) = active.get_fullscreen()
|
||||||
&& let Some(feedback) = fs
|
&& let Some(feedback) = window
|
||||||
.surface
|
|
||||||
.wl_surface()
|
.wl_surface()
|
||||||
.and_then(|wl_surface| {
|
.and_then(|wl_surface| {
|
||||||
advertised_node_for_surface(&wl_surface, &self.display_handle)
|
advertised_node_for_surface(&wl_surface, &self.display_handle)
|
||||||
})
|
})
|
||||||
.and_then(&mut dmabuf_feedback)
|
.and_then(&mut dmabuf_feedback)
|
||||||
{
|
{
|
||||||
fs.surface.send_dmabuf_feedback(
|
window.send_dmabuf_feedback(
|
||||||
output,
|
output,
|
||||||
&feedback,
|
&feedback,
|
||||||
render_element_states,
|
render_element_states,
|
||||||
|
|
@ -1351,9 +1356,8 @@ impl Common {
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(active) = shell.active_space(output) {
|
if let Some(active) = shell.active_space(output) {
|
||||||
if let Some(fs) = active.get_fullscreen(shell.seats.last_active()) {
|
if let Some(window) = active.get_fullscreen() {
|
||||||
fs.surface
|
window.send_frame(output, time, throttle(window), should_send);
|
||||||
.send_frame(output, time, throttle(&fs.surface), should_send);
|
|
||||||
}
|
}
|
||||||
active.mapped().for_each(|mapped| {
|
active.mapped().for_each(|mapped| {
|
||||||
for (window, _) in mapped.windows() {
|
for (window, _) in mapped.windows() {
|
||||||
|
|
@ -1373,9 +1377,9 @@ impl Common {
|
||||||
.spaces_for_output(output)
|
.spaces_for_output(output)
|
||||||
.filter(|w| w.handle != active.handle)
|
.filter(|w| w.handle != active.handle)
|
||||||
{
|
{
|
||||||
if let Some(fs) = space.get_fullscreen(shell.seats.last_active()) {
|
if let Some(window) = space.get_fullscreen() {
|
||||||
let throttle = min(throttle(space), throttle(&fs.surface));
|
let throttle = min(throttle(space), throttle(window));
|
||||||
fs.surface.send_frame(output, time, throttle, |_, _| None);
|
window.send_frame(output, time, throttle, |_, _| None);
|
||||||
}
|
}
|
||||||
space.mapped().for_each(|mapped| {
|
space.mapped().for_each(|mapped| {
|
||||||
for (window, _) in mapped.windows() {
|
for (window, _) in mapped.windows() {
|
||||||
|
|
|
||||||
|
|
@ -51,12 +51,13 @@ use smithay::{
|
||||||
PointerTarget, RelativeMotionEvent,
|
PointerTarget, RelativeMotionEvent,
|
||||||
},
|
},
|
||||||
touch::{
|
touch::{
|
||||||
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
|
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
|
||||||
TouchTarget, UpEvent,
|
UpEvent,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output::Output,
|
output::Output,
|
||||||
reexports::calloop::{self, LoopHandle, RegistrationToken, futures::Scheduler},
|
reexports::calloop::RegistrationToken,
|
||||||
|
reexports::calloop::{self, LoopHandle, futures::Scheduler},
|
||||||
utils::{
|
utils::{
|
||||||
Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size,
|
Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size,
|
||||||
Transform,
|
Transform,
|
||||||
|
|
@ -179,8 +180,6 @@ pub(crate) struct IcedElementInternal<P: Program + Send + 'static> {
|
||||||
last_seat: Arc<Mutex<Option<(Seat<crate::state::State>, Serial)>>>,
|
last_seat: Arc<Mutex<Option<(Seat<crate::state::State>, Serial)>>>,
|
||||||
cursor_pos: Option<Point<f64, Logical>>,
|
cursor_pos: Option<Point<f64, Logical>>,
|
||||||
touch_map: HashMap<Finger, IcedPoint>,
|
touch_map: HashMap<Finger, IcedPoint>,
|
||||||
last_touch_frame: Option<FrameMarker>,
|
|
||||||
last_touch_serial: Option<Serial>,
|
|
||||||
|
|
||||||
// iced
|
// iced
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
|
|
@ -229,8 +228,6 @@ impl<P: Program + Send + Clone + 'static> Clone for IcedElementInternal<P> {
|
||||||
last_seat: self.last_seat.clone(),
|
last_seat: self.last_seat.clone(),
|
||||||
cursor_pos: self.cursor_pos,
|
cursor_pos: self.cursor_pos,
|
||||||
touch_map: self.touch_map.clone(),
|
touch_map: self.touch_map.clone(),
|
||||||
last_touch_frame: None,
|
|
||||||
last_touch_serial: None,
|
|
||||||
theme: self.theme.clone(),
|
theme: self.theme.clone(),
|
||||||
renderer,
|
renderer,
|
||||||
state,
|
state,
|
||||||
|
|
@ -256,8 +253,6 @@ impl<P: Program + Send + 'static> fmt::Debug for IcedElementInternal<P> {
|
||||||
.field("last_seat", &self.last_seat)
|
.field("last_seat", &self.last_seat)
|
||||||
.field("cursor_pos", &self.cursor_pos)
|
.field("cursor_pos", &self.cursor_pos)
|
||||||
.field("touch_map", &self.touch_map)
|
.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("theme", &"...")
|
||||||
.field("renderer", &"...")
|
.field("renderer", &"...")
|
||||||
.field("state", &"...")
|
.field("state", &"...")
|
||||||
|
|
@ -314,8 +309,6 @@ impl<P: Program + Send + 'static> IcedElement<P> {
|
||||||
cursor_pos: None,
|
cursor_pos: None,
|
||||||
last_seat,
|
last_seat,
|
||||||
touch_map: HashMap::new(),
|
touch_map: HashMap::new(),
|
||||||
last_touch_frame: None,
|
|
||||||
last_touch_serial: None,
|
|
||||||
theme,
|
theme,
|
||||||
renderer,
|
renderer,
|
||||||
state,
|
state,
|
||||||
|
|
@ -640,6 +633,7 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
|
||||||
seat: &Seat<crate::state::State>,
|
seat: &Seat<crate::state::State>,
|
||||||
_data: &mut crate::state::State,
|
_data: &mut crate::state::State,
|
||||||
event: &DownEvent,
|
event: &DownEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
let mut internal = self.0.lock().unwrap();
|
let mut internal = self.0.lock().unwrap();
|
||||||
let id = Finger(i32::from(event.slot) as u64);
|
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 }));
|
.queue_event(Event::Touch(TouchEvent::FingerPressed { id, position }));
|
||||||
internal.touch_map.insert(id, position);
|
internal.touch_map.insert(id, position);
|
||||||
internal.cursor_pos = Some(event_location);
|
internal.cursor_pos = Some(event_location);
|
||||||
internal.last_touch_serial = Some(event.serial);
|
*internal.last_seat.lock().unwrap() = Some((seat.clone(), seq));
|
||||||
*internal.last_seat.lock().unwrap() = Some((seat.clone(), event.serial));
|
|
||||||
internal.update(false);
|
internal.update(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -660,12 +653,12 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
|
||||||
seat: &Seat<crate::state::State>,
|
seat: &Seat<crate::state::State>,
|
||||||
_data: &mut crate::state::State,
|
_data: &mut crate::state::State,
|
||||||
event: &UpEvent,
|
event: &UpEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
let mut internal = self.0.lock().unwrap();
|
let mut internal = self.0.lock().unwrap();
|
||||||
let id = Finger(i32::from(event.slot) as u64);
|
let id = Finger(i32::from(event.slot) as u64);
|
||||||
if let Some(position) = internal.touch_map.remove(&id) {
|
if let Some(position) = internal.touch_map.remove(&id) {
|
||||||
*internal.last_seat.lock().unwrap() =
|
*internal.last_seat.lock().unwrap() = Some((seat.clone(), seq));
|
||||||
Some((seat.clone(), internal.last_touch_serial.unwrap()));
|
|
||||||
internal
|
internal
|
||||||
.state
|
.state
|
||||||
.queue_event(Event::Touch(TouchEvent::FingerLifted { id, position }));
|
.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>,
|
seat: &Seat<crate::state::State>,
|
||||||
_data: &mut crate::state::State,
|
_data: &mut crate::state::State,
|
||||||
event: &TouchMotionEvent,
|
event: &TouchMotionEvent,
|
||||||
|
seq: Serial,
|
||||||
) {
|
) {
|
||||||
let mut internal = self.0.lock().unwrap();
|
let mut internal = self.0.lock().unwrap();
|
||||||
let id = Finger(i32::from(event.slot) as u64);
|
let id = Finger(i32::from(event.slot) as u64);
|
||||||
let event_location = event.location.downscale(internal.additional_scale);
|
let event_location = event.location.downscale(internal.additional_scale);
|
||||||
let position = IcedPoint::new(event_location.x as f32, event_location.y as f32);
|
let position = IcedPoint::new(event_location.x as f32, event_location.y as f32);
|
||||||
*internal.last_seat.lock().unwrap() =
|
*internal.last_seat.lock().unwrap() = Some((seat.clone(), seq));
|
||||||
Some((seat.clone(), internal.last_touch_serial.unwrap()));
|
|
||||||
internal
|
internal
|
||||||
.state
|
.state
|
||||||
.queue_event(Event::Touch(TouchEvent::FingerMoved { id, position }));
|
.queue_event(Event::Touch(TouchEvent::FingerMoved { id, position }));
|
||||||
|
|
@ -697,19 +690,17 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
|
||||||
&self,
|
&self,
|
||||||
_seat: &Seat<crate::state::State>,
|
_seat: &Seat<crate::state::State>,
|
||||||
_data: &mut crate::state::State,
|
_data: &mut crate::state::State,
|
||||||
frame: FrameMarker,
|
_seq: Serial,
|
||||||
) {
|
) {
|
||||||
self.0.lock().unwrap().last_touch_frame = Some(frame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cancel(
|
fn cancel(
|
||||||
&self,
|
&self,
|
||||||
_seat: &Seat<crate::state::State>,
|
_seat: &Seat<crate::state::State>,
|
||||||
_data: &mut crate::state::State,
|
_data: &mut crate::state::State,
|
||||||
frame: FrameMarker,
|
_seq: Serial,
|
||||||
) {
|
) {
|
||||||
let mut internal = self.0.lock().unwrap();
|
let mut internal = self.0.lock().unwrap();
|
||||||
internal.last_touch_frame = Some(frame);
|
|
||||||
for (id, position) in std::mem::take(&mut internal.touch_map) {
|
for (id, position) in std::mem::take(&mut internal.touch_map) {
|
||||||
internal
|
internal
|
||||||
.state
|
.state
|
||||||
|
|
@ -723,6 +714,7 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
|
||||||
_seat: &Seat<crate::state::State>,
|
_seat: &Seat<crate::state::State>,
|
||||||
_data: &mut crate::state::State,
|
_data: &mut crate::state::State,
|
||||||
_event: &ShapeEvent,
|
_event: &ShapeEvent,
|
||||||
|
_seq: Serial,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -731,16 +723,9 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
|
||||||
_seat: &Seat<crate::state::State>,
|
_seat: &Seat<crate::state::State>,
|
||||||
_data: &mut crate::state::State,
|
_data: &mut crate::state::State,
|
||||||
_event: &OrientationEvent,
|
_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> {
|
impl<P: Program + Send + 'static> KeyboardTarget<crate::state::State> for IcedElement<P> {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{desktop::layer_map_for_output, output::Output};
|
||||||
backend::renderer::utils::with_renderer_surface_state, desktop::layer_map_for_output,
|
|
||||||
output::Output,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Layer shell namespace used by `cosmic-workspaces`
|
/// Layer shell namespace used by `cosmic-workspaces`
|
||||||
// TODO: Avoid special case, or add protocol to expose required behavior
|
// 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 {
|
pub fn workspace_overview_is_open(output: &Output) -> bool {
|
||||||
layer_map_for_output(output)
|
layer_map_for_output(output)
|
||||||
.layers()
|
.layers()
|
||||||
.filter(|s| s.namespace() == WORKSPACE_OVERVIEW_NAMESPACE)
|
.any(|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)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,9 @@ impl DmabufHandler for State {
|
||||||
let is_fullscreen = shell
|
let is_fullscreen = shell
|
||||||
.workspaces
|
.workspaces
|
||||||
.space_for_handle(&handle)?
|
.space_for_handle(&handle)?
|
||||||
.fullscreen_surfaces
|
.fullscreen
|
||||||
.iter()
|
.as_ref()
|
||||||
.any(|f| {
|
.is_some_and(|f| f.surface.has_surface(surface, WindowSurfaceType::all()));
|
||||||
f.ended_at.is_none() && f.surface.has_surface(surface, WindowSurfaceType::all())
|
|
||||||
});
|
|
||||||
|
|
||||||
let node = kms
|
let node = kms
|
||||||
.drm_devices
|
.drm_devices
|
||||||
|
|
|
||||||
|
|
@ -753,15 +753,11 @@ pub fn render_cursor_to_buffer(
|
||||||
seat: &Seat<State>,
|
seat: &Seat<State>,
|
||||||
) {
|
) {
|
||||||
let buffer = frame.buffer();
|
let buffer = frame.buffer();
|
||||||
let mut cursor_size = seat
|
let cursor_size = seat
|
||||||
.cursor_geometry((0.0, 0.0), state.common.clock.now())
|
.cursor_geometry((0.0, 0.0), state.common.clock.now())
|
||||||
.map(|(geo, _hotspot)| geo.size)
|
.map(|(geo, _hotspot)| geo.size)
|
||||||
.unwrap_or_else(|| Size::from((64, 64)));
|
.unwrap_or_else(|| Size::from((64, 64)));
|
||||||
let buffer_size = buffer_dimensions(&buffer).unwrap();
|
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 {
|
if buffer_size != cursor_size {
|
||||||
let constraints = BufferConstraints {
|
let constraints = BufferConstraints {
|
||||||
size: cursor_size,
|
size: cursor_size,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ pub mod output_configuration;
|
||||||
pub mod output_power;
|
pub mod output_power;
|
||||||
pub mod overlap_notify;
|
pub mod overlap_notify;
|
||||||
pub mod pointer_constraints;
|
pub mod pointer_constraints;
|
||||||
pub mod pointer_warp;
|
|
||||||
pub mod primary_selection;
|
pub mod primary_selection;
|
||||||
pub mod seat;
|
pub mod seat;
|
||||||
pub mod security_context;
|
pub mod security_context;
|
||||||
|
|
|
||||||
|
|
@ -1,123 +1,35 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use crate::{shell::CosmicSurface, state::State, utils::prelude::*};
|
use crate::state::State;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
input::pointer::PointerHandle,
|
input::pointer::PointerHandle,
|
||||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||||
utils::{Logical, Point},
|
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 {
|
impl PointerConstraintsHandler for State {
|
||||||
fn new_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle<Self>) {
|
fn new_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle<Self>) {
|
||||||
let seat = self
|
// XXX region
|
||||||
.common
|
if pointer
|
||||||
.shell
|
.current_focus()
|
||||||
.read()
|
.is_some_and(|x| x.wl_surface().as_deref() == Some(surface))
|
||||||
.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, ¤t_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 {
|
|
||||||
with_pointer_constraint(surface, pointer, |constraint| {
|
with_pointer_constraint(surface, pointer, |constraint| {
|
||||||
if let Some(constraint) = constraint {
|
constraint.unwrap().activate();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
fn cursor_position_hint(
|
||||||
&mut self,
|
&mut self,
|
||||||
surface: &WlSurface,
|
_surface: &WlSurface,
|
||||||
pointer: &PointerHandle<Self>,
|
_pointer: &PointerHandle<Self>,
|
||||||
location: Point<f64, Logical>,
|
_location: Point<f64, Logical>,
|
||||||
) {
|
) {
|
||||||
if with_pointer_constraint(surface, pointer, |constraint| {
|
// TODO
|
||||||
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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -42,7 +42,7 @@ impl ToplevelManagementHandler for State {
|
||||||
.spaces_for_output(output)
|
.spaces_for_output(output)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find(|(_, w)| {
|
.find(|(_, w)| {
|
||||||
w.get_fullscreen_surfaces().any(|f| &f.surface == window)
|
w.get_fullscreen().is_some_and(|f| f == window)
|
||||||
|| w.mapped()
|
|| w.mapped()
|
||||||
.flat_map(|m| m.windows().map(|(s, _)| s))
|
.flat_map(|m| m.windows().map(|(s, _)| s))
|
||||||
.any(|w| &w == window)
|
.any(|w| &w == window)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ use crate::{
|
||||||
state::State,
|
state::State,
|
||||||
wayland::protocols::workspace::{State as WState, WorkspaceHandle},
|
wayland::protocols::workspace::{State as WState, WorkspaceHandle},
|
||||||
};
|
};
|
||||||
use cosmic_comp_config::ActivationPolicy;
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
input::Seat,
|
input::Seat,
|
||||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||||
|
|
@ -21,10 +20,6 @@ pub enum ActivationContext {
|
||||||
Workspace(WorkspaceHandle),
|
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 {
|
impl XdgActivationHandler for State {
|
||||||
fn activation_state(&mut self) -> &mut XdgActivationState {
|
fn activation_state(&mut self) -> &mut XdgActivationState {
|
||||||
&mut self.common.xdg_activation_state
|
&mut self.common.xdg_activation_state
|
||||||
|
|
@ -56,12 +51,7 @@ impl XdgActivationHandler for State {
|
||||||
});
|
});
|
||||||
let output = seat.active_output();
|
let output = seat.active_output();
|
||||||
let mut shell = self.common.shell.write();
|
let mut shell = self.common.shell.write();
|
||||||
let Some(workspace) = shell.active_space_mut(&output) else {
|
let workspace = shell.active_space_mut(&output).unwrap();
|
||||||
debug!(?token, "created urgent token for privileged client");
|
|
||||||
data.user_data
|
|
||||||
.insert_if_missing(move || ActivationContext::UrgentOnly);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
let handle = workspace.handle;
|
let handle = workspace.handle;
|
||||||
data.user_data
|
data.user_data
|
||||||
.insert_if_missing(move || ActivationContext::Workspace(handle));
|
.insert_if_missing(move || ActivationContext::Workspace(handle));
|
||||||
|
|
@ -95,11 +85,7 @@ impl XdgActivationHandler for State {
|
||||||
if valid {
|
if valid {
|
||||||
let output = seat.active_output();
|
let output = seat.active_output();
|
||||||
let mut shell = self.common.shell.write();
|
let mut shell = self.common.shell.write();
|
||||||
let Some(workspace) = shell.active_space_mut(&output) else {
|
let workspace = shell.active_space_mut(&output).unwrap();
|
||||||
data.user_data
|
|
||||||
.insert_if_missing(|| ActivationContext::UrgentOnly);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
let handle = workspace.handle;
|
let handle = workspace.handle;
|
||||||
data.user_data
|
data.user_data
|
||||||
.insert_if_missing(move || ActivationContext::Workspace(handle));
|
.insert_if_missing(move || ActivationContext::Workspace(handle));
|
||||||
|
|
@ -129,45 +115,11 @@ impl XdgActivationHandler for State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ActivationContext::Workspace(_) => {
|
ActivationContext::Workspace(_) => {
|
||||||
match self.common.config.cosmic_conf.activation_policy {
|
|
||||||
ActivationPolicy::Focus => {
|
|
||||||
self.activate_surface(
|
self.activate_surface(
|
||||||
&surface,
|
&surface,
|
||||||
Some((ActivationKey::Wayland(surface.clone()), *context)),
|
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(¤t_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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -267,8 +219,8 @@ impl State {
|
||||||
.workspaces
|
.workspaces
|
||||||
.space_for_handle(&workspace)
|
.space_for_handle(&workspace)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get_fullscreen(&seat)
|
.get_fullscreen()
|
||||||
.map(|f| f.surface.clone())
|
.cloned()
|
||||||
.map(KeyboardFocusTarget::Fullscreen)
|
.map(KeyboardFocusTarget::Fullscreen)
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
|
|
@ -280,8 +232,8 @@ impl State {
|
||||||
if let Some(surface) = shell
|
if let Some(surface) = shell
|
||||||
.workspaces
|
.workspaces
|
||||||
.space_for_handle(&workspace)
|
.space_for_handle(&workspace)
|
||||||
.and_then(|w| w.get_fullscreen(&seat))
|
.and_then(|w| w.get_fullscreen())
|
||||||
.map(|f| f.surface.clone())
|
.cloned()
|
||||||
{
|
{
|
||||||
shell.append_focus_stack(surface, &seat)
|
shell.append_focus_stack(surface, &seat)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ impl Shell {
|
||||||
unconstrain_xdg_popup(surface, window_loc, output.geometry());
|
unconstrain_xdg_popup(surface, window_loc, output.geometry());
|
||||||
}
|
}
|
||||||
} else if let Some(output) = self.workspaces.spaces().find_map(|w| {
|
} 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())
|
(f.surface.wl_surface().as_deref() == Some(&parent)).then_some(w.output())
|
||||||
})
|
})
|
||||||
}) {
|
}) {
|
||||||
|
|
|
||||||
|
|
@ -495,18 +495,35 @@ where
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let foreign_toplevel_handle = state.foreign_handle.as_ref();
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
|
|
||||||
let new_title = (handle_state.title != window.title()).then(|| window.title());
|
if handle_state.title != window.title() {
|
||||||
let new_app_id = (handle_state.app_id != window.app_id()).then(|| window.app_id());
|
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::Maximized) != window.is_maximized())
|
||||||
|| (states.contains(&States::Fullscreen) != window.is_fullscreen())
|
|| (states.contains(&States::Fullscreen) != window.is_fullscreen())
|
||||||
|| (states.contains(&States::Activated) != window.is_activated())
|
|| (states.contains(&States::Activated) != window.is_activated())
|
||||||
|| (states.contains(&States::Minimized) != window.is_minimized())
|
|| (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();
|
let mut states = Vec::new();
|
||||||
if window.is_maximized() {
|
if window.is_maximized() {
|
||||||
|
|
@ -526,64 +543,8 @@ where
|
||||||
{
|
{
|
||||||
states.push(States::Sticky);
|
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());
|
handle_state.states = Some(states.clone());
|
||||||
|
|
||||||
let states = states
|
let states = states
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|state| (*state as u32).to_ne_bytes())
|
.flat_map(|state| (*state as u32).to_ne_bytes())
|
||||||
|
|
@ -592,9 +553,17 @@ where
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputs_changed || geometry_changed)
|
let mut geometry_changed = false;
|
||||||
&& let Ok(client) = dh.get_client(instance.id())
|
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();
|
handle_state.outputs = state.outputs.clone();
|
||||||
|
|
||||||
let handle_state = &mut *handle_state;
|
let handle_state = &mut *handle_state;
|
||||||
|
|
@ -630,7 +599,6 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if workspaces_changed {
|
|
||||||
for new_workspace in state
|
for new_workspace in state
|
||||||
.workspaces
|
.workspaces
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -652,7 +620,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handle_state.workspaces = state.workspaces.clone();
|
handle_state.workspaces = state.workspaces.clone();
|
||||||
}
|
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
|
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
|
||||||
|
|
|
||||||
|
|
@ -237,10 +237,11 @@ where
|
||||||
window_from_handle::<<D as ToplevelInfoHandler>::Window>(toplevel).unwrap();
|
window_from_handle::<<D as ToplevelInfoHandler>::Window>(toplevel).unwrap();
|
||||||
if let Some(toplevel_state) = window.user_data().get::<ToplevelState>() {
|
if let Some(toplevel_state) = window.user_data().get::<ToplevelState>() {
|
||||||
let mut toplevel_state = toplevel_state.lock().unwrap();
|
let mut toplevel_state = toplevel_state.lock().unwrap();
|
||||||
|
if width == 0 && height == 0 {
|
||||||
toplevel_state
|
toplevel_state
|
||||||
.rectangles
|
.rectangles
|
||||||
.retain(|(s, _)| s.id() != surface.id());
|
.retain(|(s, _)| s.id() != surface.id());
|
||||||
if width != 0 || height != 0 {
|
} else {
|
||||||
toplevel_state.rectangles.push((
|
toplevel_state.rectangles.push((
|
||||||
surface.downgrade(),
|
surface.downgrade(),
|
||||||
Rectangle::new((x, y).into(), (width, height).into()),
|
Rectangle::new((x, y).into(), (width, height).into()),
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,6 @@ impl State {
|
||||||
&self.common.display_handle,
|
&self.common.display_handle,
|
||||||
None,
|
None,
|
||||||
std::iter::empty::<(OsString, OsString)>(),
|
std::iter::empty::<(OsString, OsString)>(),
|
||||||
std::iter::empty::<OsString>(),
|
|
||||||
true,
|
true,
|
||||||
Stdio::null(),
|
Stdio::null(),
|
||||||
Stdio::null(),
|
Stdio::null(),
|
||||||
|
|
@ -576,15 +575,16 @@ impl Common {
|
||||||
.filter(|(i, _)| *i != set.active),
|
.filter(|(i, _)| *i != set.active),
|
||||||
)
|
)
|
||||||
.flat_map(|(_, workspace)| {
|
.flat_map(|(_, workspace)| {
|
||||||
let focus_last =
|
|
||||||
workspace.focus_stack.get(seat).last().cloned();
|
|
||||||
workspace
|
workspace
|
||||||
.get_fullscreen_surfaces()
|
.get_fullscreen()
|
||||||
.filter(|f| {
|
.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())
|
.cloned()
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(workspace.mapped().flat_map(|mapped| {
|
.chain(workspace.mapped().flat_map(|mapped| {
|
||||||
let active = mapped.active_window();
|
let active = mapped.active_window();
|
||||||
|
|
@ -603,14 +603,15 @@ impl Common {
|
||||||
}))
|
}))
|
||||||
.chain(
|
.chain(
|
||||||
workspace
|
workspace
|
||||||
.get_fullscreen_surfaces()
|
.get_fullscreen()
|
||||||
.filter(|f| {
|
.filter(|f| {
|
||||||
focus_last
|
workspace
|
||||||
.as_ref()
|
.focus_stack
|
||||||
.is_none_or(|t| t != &f.surface)
|
.get(seat)
|
||||||
|
.last()
|
||||||
|
.is_none_or(|t| &t != f)
|
||||||
})
|
})
|
||||||
.map(|f| f.surface.clone())
|
.cloned()
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
)
|
)
|
||||||
.chain(
|
.chain(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue