Compare commits
65 commits
1dda5a558e
...
e65b97abb8
| Author | SHA1 | Date | |
|---|---|---|---|
| e65b97abb8 | |||
| 9141780b30 | |||
| 58b0598de8 | |||
| 7d24cd2e6a | |||
| 6e72e4c171 | |||
| a005fddefe | |||
| a5c0d7a17f | |||
|
|
9d52653d5c | ||
|
|
f5e0ba1594 | ||
|
|
a9f2427c00 | ||
|
|
dacdc3db39 | ||
|
|
c5775d2010 | ||
|
|
bb584aab7f | ||
|
|
ccc4f36065 | ||
|
|
650768211f | ||
|
|
aac1e19f08 | ||
|
|
f032e7dbd5 | ||
|
|
106b50293b | ||
|
|
296b6bee23 | ||
|
|
b6cab38528 | ||
|
|
821b431973 | ||
|
|
dddf51b441 | ||
|
|
aff506bb7b | ||
|
|
69bb35e336 | ||
|
|
836ccbaa0b | ||
|
|
d4c5714c10 | ||
|
|
db0b1afeb5 | ||
|
|
8f5f7c897d | ||
|
|
e576a87f5b | ||
|
|
52b3f930a8 | ||
|
|
31f317d16b | ||
|
|
f95b62635d | ||
|
|
5ca8cc2cdf | ||
|
|
0312f9a201 | ||
|
|
5153d497e7 | ||
|
|
94e9437c4e | ||
|
|
651877e622 | ||
|
|
84d2a9d297 | ||
|
|
7d0d374fd8 | ||
|
|
7a50625e78 | ||
|
|
8aea6cc158 | ||
|
|
82dcd9a46a | ||
|
|
4a8931896f | ||
|
|
b6b76e1d4a | ||
|
|
51dd3bc66f | ||
|
|
4eaaf4f55c | ||
|
|
c8d9ff1215 | ||
|
|
571565c28e | ||
|
|
3c834e9c85 | ||
|
|
2f7c34f29a | ||
|
|
56f84fba2d | ||
|
|
28ef6bdbc8 | ||
|
|
9f28a764a7 | ||
|
|
203cad8cbd | ||
|
|
208c2128cd | ||
|
|
4fb7d49316 | ||
|
|
81066d437a | ||
|
|
9702aae523 | ||
|
|
a533ac1ec3 | ||
|
|
e75c9c0c48 | ||
|
|
d829ec3815 | ||
|
|
32b54cc31f | ||
|
|
22fe419747 | ||
|
|
cefe8472b2 | ||
|
|
8c309d5231 |
59 changed files with 3435 additions and 1566 deletions
1746
Cargo.lock
generated
1746
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
62
Cargo.toml
62
Cargo.toml
|
|
@ -12,23 +12,23 @@ members = ["cosmic-comp-config"]
|
|||
[dependencies]
|
||||
anyhow = { version = "1.0.102", features = ["backtrace"] }
|
||||
bitflags = "2.11.0"
|
||||
calloop = { version = "0.14.4", features = ["executor"] }
|
||||
calloop = { version = "0.14.4", features = ["executor", "stream"] }
|
||||
cosmic-comp-config = { path = "cosmic-comp-config", features = [
|
||||
"libdisplay-info",
|
||||
"output",
|
||||
] }
|
||||
cosmic-config = { git = "https://github.com/pop-os/libcosmic", features = [
|
||||
cosmic-config = { path = "../libcosmic/cosmic-config", features = [
|
||||
"calloop",
|
||||
"macro",
|
||||
] }
|
||||
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", rev = "160b086", default-features = false, features = [
|
||||
cosmic-protocols = { path = "../cosmic-protocols", default-features = false, features = [
|
||||
"server",
|
||||
] }
|
||||
cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon" }
|
||||
cosmic-settings-daemon-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", features = [
|
||||
cosmic-settings-config = { path = "../cosmic-settings-daemon/config" }
|
||||
cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config", features = [
|
||||
"greeter",
|
||||
] }
|
||||
cosmic-text = { git = "https://github.com/pop-os/cosmic-text.git", features = [
|
||||
cosmic-text = { path = "../cosmic-text", features = [
|
||||
"shape-run-cache",
|
||||
] }
|
||||
libdisplay-info = "0.3.0"
|
||||
|
|
@ -39,10 +39,14 @@ i18n-embed = { version = "0.16", features = [
|
|||
"desktop-requester",
|
||||
] }
|
||||
i18n-embed-fl = "0.10"
|
||||
iced_tiny_skia = { git = "https://github.com/pop-os/libcosmic" }
|
||||
iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" }
|
||||
indexmap = "2.13"
|
||||
keyframe = "1.1.1"
|
||||
libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false }
|
||||
cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [
|
||||
"tokio",
|
||||
"wayland",
|
||||
"multi-window",
|
||||
] }
|
||||
libsystemd = { version = "0.7", optional = true }
|
||||
log-panics = { version = "2", features = ["with-backtrace"] }
|
||||
ordered-float = "5.1"
|
||||
|
|
@ -73,6 +77,7 @@ xdg = "^3.0"
|
|||
xdg-user = "0.2.1"
|
||||
xkbcommon = "0.9"
|
||||
zbus = "5.14.0"
|
||||
tokio = { version = "1", features = ["rt-multi-thread"] }
|
||||
profiling = { version = "1.0" }
|
||||
rustix = { version = "1.1.4", features = ["process"] }
|
||||
rand = "0.10"
|
||||
|
|
@ -80,10 +85,9 @@ rand = "0.10"
|
|||
clap_lex = "1.0"
|
||||
parking_lot = "0.12.5"
|
||||
logind-zbus = { version = "5.3.2", optional = true }
|
||||
futures-executor = { version = "0.3.32", features = ["thread-pool"] }
|
||||
futures-executor = { version = "0.3.32" }
|
||||
futures-util = "0.3.32"
|
||||
cgmath = "0.18.0"
|
||||
smallvec = "1.15.1"
|
||||
|
||||
[dependencies.id_tree]
|
||||
branch = "feature/copy_clone"
|
||||
|
|
@ -118,7 +122,8 @@ optional = true
|
|||
[features]
|
||||
debug = ["egui", "egui_plot", "smithay-egui", "anyhow/backtrace"]
|
||||
default = ["systemd"]
|
||||
systemd = ["libsystemd", "logind-zbus"]
|
||||
logind = ["logind-zbus"]
|
||||
systemd = ["libsystemd", "logind"]
|
||||
profile-with-tracy = ["profiling/profile-with-tracy", "tracy-client/default"]
|
||||
profile-with-tracy-gpu = ["profile-with-tracy", "smithay/tracy_gpu_profiling"]
|
||||
|
||||
|
|
@ -138,9 +143,36 @@ inherits = "release"
|
|||
[profile.release]
|
||||
lto = "fat"
|
||||
|
||||
[patch."https://github.com/pop-os/cosmic-protocols"]
|
||||
cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" }
|
||||
cosmic-client-toolkit = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" }
|
||||
[patch.'https://github.com/pop-os/libcosmic']
|
||||
cosmic-config = { path = "../libcosmic/cosmic-config" }
|
||||
cosmic-theme = { path = "../libcosmic/cosmic-theme" }
|
||||
iced = { path = "../libcosmic/iced" }
|
||||
iced_accessibility = { path = "../libcosmic/iced/accessibility" }
|
||||
iced_core = { path = "../libcosmic/iced/core" }
|
||||
iced_futures = { path = "../libcosmic/iced/futures" }
|
||||
iced_graphics = { path = "../libcosmic/iced/graphics" }
|
||||
iced_renderer = { path = "../libcosmic/iced/renderer" }
|
||||
iced_runtime = { path = "../libcosmic/iced/runtime" }
|
||||
iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" }
|
||||
iced_wgpu = { path = "../libcosmic/iced/wgpu" }
|
||||
iced_widget = { path = "../libcosmic/iced/widget" }
|
||||
iced_winit = { path = "../libcosmic/iced/winit" }
|
||||
|
||||
[patch.'https://github.com/pop-os/cosmic-protocols']
|
||||
cosmic-protocols = { path = "../cosmic-protocols" }
|
||||
cosmic-client-toolkit = { path = "../cosmic-protocols/client-toolkit" }
|
||||
|
||||
[patch.'https://github.com/pop-os/cosmic-settings-daemon']
|
||||
cosmic-settings-config = { path = "../cosmic-settings-daemon/config" }
|
||||
cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config" }
|
||||
|
||||
[patch.'https://github.com/pop-os/cosmic-text.git']
|
||||
cosmic-text = { path = "../cosmic-text" }
|
||||
|
||||
[patch.'https://forge.aditua.com/leyoda/window_clipboard.git']
|
||||
window_clipboard = { path = "../window_clipboard" }
|
||||
dnd = { path = "../window_clipboard/dnd" }
|
||||
mime = { path = "../window_clipboard/mime" }
|
||||
|
||||
[patch.crates-io]
|
||||
smithay = { git = "https://github.com/smithay/smithay.git", rev = "774f2ab" }
|
||||
smithay = { git = "https://github.com/smithay/smithay.git", rev = "8eb4076" }
|
||||
|
|
|
|||
1
Makefile
1
Makefile
|
|
@ -39,6 +39,7 @@ vendor:
|
|||
mkdir -p .cargo
|
||||
cargo vendor | head -n -1 > .cargo/config
|
||||
echo 'directory = "vendor"' >> .cargo/config
|
||||
[ -n "$(SOURCE_GIT_HASH)" ] && printf '\n[env]\nGIT_HASH = "%s"\n' "$(SOURCE_GIT_HASH)" >> .cargo/config || true
|
||||
tar pcf vendor.tar vendor
|
||||
rm -rf vendor
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ version = "1.0.0"
|
|||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
cosmic-config = { git = "https://github.com/pop-os/libcosmic/" }
|
||||
cosmic-randr-shell = { git = "https://github.com/pop-os/cosmic-randr/", optional = true }
|
||||
cosmic-config = { path = "../../libcosmic/cosmic-config" }
|
||||
cosmic-randr-shell = { path = "../../cosmic-randr/shell", optional = true }
|
||||
input = "0.10.0"
|
||||
libdisplay-info = { version = "0.3.0", optional = true }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ pub struct CosmicCompConfig {
|
|||
pub appearance_settings: AppearanceConfig,
|
||||
/// Hide the cursor after this many seconds of pointer inactivity (None disables)
|
||||
pub cursor_hide_timeout: Option<u32>,
|
||||
pub activation_policy: ActivationPolicy,
|
||||
}
|
||||
|
||||
impl Default for CosmicCompConfig {
|
||||
|
|
@ -138,6 +139,7 @@ impl Default for CosmicCompConfig {
|
|||
accessibility_zoom: ZoomConfig::default(),
|
||||
appearance_settings: AppearanceConfig::default(),
|
||||
cursor_hide_timeout: None,
|
||||
activation_policy: ActivationPolicy::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -227,6 +229,14 @@ pub enum EavesdroppingKeyboardMode {
|
|||
All,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub enum ActivationPolicy {
|
||||
#[default]
|
||||
Focus,
|
||||
FocusIfActiveWorkspace,
|
||||
Urgent,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum XwaylandDescaling {
|
||||
|
|
|
|||
|
|
@ -63,5 +63,5 @@ pub struct PinnedWorkspace {
|
|||
pub output: OutputMatch,
|
||||
pub tiling_enabled: bool,
|
||||
pub id: Option<String>,
|
||||
// TODO: name
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,5 +48,6 @@
|
|||
(appid: "zoom", titles: [".*"]),
|
||||
(appid: "Tor Browser", titles: [".*"]),
|
||||
(appid: "^.*?action=join.*$", titles: [".*"]),
|
||||
|
||||
(appid: "^(slack|com.slack.Slack)", titles: ["^.*?(Huddle Preview).*"]),
|
||||
(appid: "^(thunderbird|org.mozilla.thunderbird)(-esr|_esr)*", titles: ["^(Write:).*"]),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
a11y-zoom-move-continuously = A nézet folyamatosan követi a mutatót
|
||||
a11y-zoom-move-onedge = A nézet csak akkor mozog, ha a mutató a szélen van
|
||||
a11y-zoom-move-onedge = A nézet csak akkor mozog, ha a mutató eléri a szélét
|
||||
a11y-zoom-move-centered = A nézet úgy mozog, hogy a mutató középen maradjon
|
||||
a11y-zoom-settings = Nagyító beállításai…
|
||||
grow-window = Növelés
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "1.90"
|
||||
channel = "1.93"
|
||||
components = [ "rust-src" ]
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ pub struct Device {
|
|||
pub inner: InnerDevice,
|
||||
pub drm: GbmDrmOutputManager,
|
||||
|
||||
supports_atomic: bool,
|
||||
pub texture_formats: FormatSet,
|
||||
event_token: Option<RegistrationToken>,
|
||||
pub socket: Option<Socket>,
|
||||
|
|
@ -711,7 +710,6 @@ impl Device {
|
|||
let (drm, notifier) = DrmDevice::new(fd.clone(), false)
|
||||
.with_context(|| format!("Failed to initialize drm device for: {}", path.display()))?;
|
||||
let dev_node = DrmNode::from_dev_id(dev)?;
|
||||
let supports_atomic = drm.is_atomic();
|
||||
|
||||
let gbm = GbmDevice::new(fd)
|
||||
.with_context(|| format!("Failed to initialize GBM device for {}", path.display()))?;
|
||||
|
|
@ -831,7 +829,6 @@ impl Device {
|
|||
active_clients,
|
||||
},
|
||||
|
||||
supports_atomic,
|
||||
texture_formats,
|
||||
event_token: Some(token),
|
||||
socket,
|
||||
|
|
@ -840,8 +837,7 @@ impl Device {
|
|||
|
||||
pub fn enumerate_surfaces(&mut self) -> Result<OutputChanges> {
|
||||
// enumerate our outputs
|
||||
let config =
|
||||
drm_helpers::display_configuration(self.drm.device_mut(), self.supports_atomic)?;
|
||||
let config = drm_helpers::display_configuration(self.drm.device_mut())?;
|
||||
|
||||
let surfaces = self
|
||||
.inner
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
use anyhow::{Context, Result, anyhow};
|
||||
use libdisplay_info::{edid::DisplayDescriptorTag, info::Info};
|
||||
use smithay::{
|
||||
backend::drm::DrmDevice,
|
||||
reexports::drm::control::{
|
||||
AtomicCommitFlags, Device as ControlDevice, Mode, ModeFlags, PlaneType, ResourceHandle,
|
||||
atomic::AtomicModeReq,
|
||||
|
|
@ -16,8 +17,7 @@ use smithay::{
|
|||
use std::{collections::HashMap, ops::Range};
|
||||
|
||||
pub fn display_configuration(
|
||||
device: &mut impl ControlDevice,
|
||||
supports_atomic: bool,
|
||||
device: &mut DrmDevice,
|
||||
) -> Result<HashMap<connector::Handle, Option<crtc::Handle>>> {
|
||||
let res_handles = device.resource_handles()?;
|
||||
let connectors = res_handles.connectors();
|
||||
|
|
@ -73,28 +73,10 @@ pub fn display_configuration(
|
|||
}
|
||||
|
||||
// And then cleanup
|
||||
if supports_atomic {
|
||||
if device.is_atomic() {
|
||||
let mut req = AtomicModeReq::new();
|
||||
let plane_handles = device.plane_handles()?;
|
||||
|
||||
for conn in connectors
|
||||
.iter()
|
||||
.flat_map(|conn| device.get_connector(*conn, false).ok())
|
||||
.filter(|conn| {
|
||||
if let Some(enc) = conn.current_encoder()
|
||||
&& let Ok(enc) = device.get_encoder(enc)
|
||||
&& let Some(crtc) = enc.crtc()
|
||||
{
|
||||
return cleanup.contains(&crtc);
|
||||
}
|
||||
false
|
||||
})
|
||||
.map(|info| info.handle())
|
||||
{
|
||||
let crtc_id = get_prop(device, conn, "CRTC_ID")?;
|
||||
req.add_property(conn, crtc_id, property::Value::CRTC(None));
|
||||
}
|
||||
|
||||
// We cannot just shortcut and use the legacy api for all cleanups because of this.
|
||||
// (Technically a device does not need to be atomic for planes to be used, but nobody does this otherwise.)
|
||||
for plane in plane_handles {
|
||||
|
|
@ -108,7 +90,7 @@ pub fn display_configuration(
|
|||
_ => false,
|
||||
},
|
||||
)?;
|
||||
if cleanup.contains(&crtc) || !is_primary {
|
||||
if !is_primary && !cleanup.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));
|
||||
|
|
@ -116,27 +98,76 @@ pub fn display_configuration(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
for crtc in cleanup {
|
||||
let mode_id = get_prop(device, crtc, "MODE_ID")?;
|
||||
let active = get_prop(device, crtc, "ACTIVE")?;
|
||||
req.add_property(crtc, active, property::Value::Boolean(false));
|
||||
req.add_property(crtc, mode_id, property::Value::Unknown(0));
|
||||
}
|
||||
|
||||
device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?;
|
||||
} else {
|
||||
for crtc in res_handles.crtcs() {
|
||||
#[allow(deprecated)]
|
||||
let _ = device.set_cursor(*crtc, Option::<&DumbBuffer>::None);
|
||||
}
|
||||
for crtc in cleanup {
|
||||
}
|
||||
disable_crtcs(device, &cleanup)?;
|
||||
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
/// Disables the given CRTCs and detaches their connectors/planes.
|
||||
pub fn disable_crtcs(device: &mut DrmDevice, crtcs: &[crtc::Handle]) -> Result<()> {
|
||||
if crtcs.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let res_handles = device.resource_handles()?;
|
||||
|
||||
if device.is_atomic() {
|
||||
let mut req = AtomicModeReq::new();
|
||||
|
||||
for conn in res_handles
|
||||
.connectors()
|
||||
.iter()
|
||||
.flat_map(|conn| device.get_connector(*conn, false).ok())
|
||||
.filter(|conn| {
|
||||
if let Some(enc) = conn.current_encoder()
|
||||
&& let Ok(enc) = device.get_encoder(enc)
|
||||
&& let Some(crtc) = enc.crtc()
|
||||
{
|
||||
return crtcs.contains(&crtc);
|
||||
}
|
||||
false
|
||||
})
|
||||
.map(|info| info.handle())
|
||||
{
|
||||
let crtc_id = get_prop(device, conn, "CRTC_ID")?;
|
||||
req.add_property(conn, crtc_id, property::Value::CRTC(None));
|
||||
}
|
||||
|
||||
for plane in device.plane_handles()? {
|
||||
let info = device.get_plane(plane)?;
|
||||
if let Some(crtc) = info.crtc()
|
||||
&& crtcs.contains(&crtc)
|
||||
{
|
||||
let crtc_id = get_prop(device, plane, "CRTC_ID")?;
|
||||
let fb_id = get_prop(device, plane, "FB_ID")?;
|
||||
req.add_property(plane, crtc_id, property::Value::CRTC(None));
|
||||
req.add_property(plane, fb_id, property::Value::Framebuffer(None));
|
||||
}
|
||||
}
|
||||
|
||||
for crtc in crtcs {
|
||||
let mode_id = get_prop(device, *crtc, "MODE_ID")?;
|
||||
let active = get_prop(device, *crtc, "ACTIVE")?;
|
||||
req.add_property(*crtc, active, property::Value::Boolean(false));
|
||||
req.add_property(*crtc, mode_id, property::Value::Unknown(0));
|
||||
}
|
||||
|
||||
device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?;
|
||||
} else {
|
||||
for crtc in crtcs {
|
||||
// null commit (necessary to trigger removal on the kernel side with the legacy api.)
|
||||
let _ = device.set_crtc(crtc, None, (0, 0), &[], None);
|
||||
let _ = device.set_crtc(*crtc, None, (0, 0), &[], None);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn interface_name(device: &impl ControlDevice, connector: connector::Handle) -> Result<String> {
|
||||
|
|
|
|||
|
|
@ -374,6 +374,7 @@ impl State {
|
|||
for device in backend.drm_devices.values_mut() {
|
||||
if let Err(err) = device.drm.lock().activate(true) {
|
||||
error!(?err, "Failed to resume drm device");
|
||||
continue;
|
||||
}
|
||||
if let Some(lease_state) = device.inner.leasing_global.as_mut() {
|
||||
lease_state.resume::<State>();
|
||||
|
|
@ -851,11 +852,23 @@ impl KmsGuard<'_> {
|
|||
|
||||
// first drop old surfaces
|
||||
if !test_only {
|
||||
for output in outputs.iter().filter(|o| !o.is_enabled()) {
|
||||
device
|
||||
.inner
|
||||
.surfaces
|
||||
.retain(|_, surface| surface.output != *output);
|
||||
let mut disabled_crtcs = Vec::new();
|
||||
device.inner.surfaces.retain(|crtc, surface| {
|
||||
if outputs
|
||||
.iter()
|
||||
.any(|o| !o.is_enabled() && surface.output == *o)
|
||||
{
|
||||
disabled_crtcs.push(*crtc);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
if let Err(err) =
|
||||
drm_helpers::disable_crtcs(device.drm.device_mut(), &disabled_crtcs)
|
||||
{
|
||||
warn!("Failed to disable crtcs for disabled outputs: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,30 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use smithay::backend::{
|
||||
SwapBuffersError,
|
||||
allocator::{
|
||||
Allocator,
|
||||
dmabuf::{AnyError, Dmabuf, DmabufAllocator},
|
||||
gbm::GbmAllocator,
|
||||
},
|
||||
drm::{CreateDrmNodeError, DrmNode},
|
||||
renderer::{
|
||||
RendererSuper,
|
||||
gles::{GlesError, GlesRenderer},
|
||||
glow::GlowRenderer,
|
||||
multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
|
||||
use clap_lex::OsStrExt;
|
||||
use rustix::path::Arg;
|
||||
use smithay::{
|
||||
backend::{
|
||||
SwapBuffersError,
|
||||
allocator::{
|
||||
Allocator,
|
||||
dmabuf::{AnyError, Dmabuf, DmabufAllocator},
|
||||
gbm::GbmAllocator,
|
||||
},
|
||||
drm::{CreateDrmNodeError, DrmNode},
|
||||
renderer::{
|
||||
RendererSuper,
|
||||
gles::{GlesError, GlesRenderer, ffi},
|
||||
glow::GlowRenderer,
|
||||
multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
|
||||
},
|
||||
},
|
||||
reexports::drm::control::Device,
|
||||
};
|
||||
use std::{
|
||||
borrow::{Borrow, BorrowMut},
|
||||
cell::Cell,
|
||||
ffi::{CStr, c_char},
|
||||
};
|
||||
use std::{borrow::Borrow, cell::Cell};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
|
|
@ -98,7 +107,7 @@ impl<A: AsFd + Clone + Send + 'static> GbmGlowBackend<A> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<A: AsFd + Clone + 'static> GraphicsApi for GbmGlowBackend<A> {
|
||||
impl<A: AsFd + Device + Clone + 'static> GraphicsApi for GbmGlowBackend<A> {
|
||||
type Device = GbmGlowDevice;
|
||||
type Error = Error;
|
||||
|
||||
|
|
@ -123,11 +132,29 @@ impl<A: AsFd + Clone + 'static> GraphicsApi for GbmGlowBackend<A> {
|
|||
.any(|renderer| renderer.node.dev_id() == node.dev_id())
|
||||
})
|
||||
.flat_map(|(node, (allocator, renderer))| {
|
||||
let renderer = renderer.replace(None)?;
|
||||
let mut renderer = renderer.replace(None)?;
|
||||
let is_intel = allocator
|
||||
.as_ref()
|
||||
.get_driver()
|
||||
.is_ok_and(|drv| drv.name().contains("i915"));
|
||||
let intel_export_quirk = is_intel
|
||||
&& BorrowMut::<GlesRenderer>::borrow_mut(&mut renderer)
|
||||
.with_context(|gl| unsafe {
|
||||
CStr::from_ptr(gl.GetString(ffi::RENDERER) as *const c_char)
|
||||
})
|
||||
.is_ok_and(|name| {
|
||||
name.as_str().is_ok_and(|name| {
|
||||
name.contains("TGL")
|
||||
|| name.contains("MTL")
|
||||
|| name.contains("ARL")
|
||||
|| name.contains("LNL")
|
||||
})
|
||||
});
|
||||
|
||||
Some(GbmGlowDevice {
|
||||
node: *node,
|
||||
renderer,
|
||||
intel_export_quirk,
|
||||
allocator: Box::new(DmabufAllocator(allocator.clone())),
|
||||
})
|
||||
})
|
||||
|
|
@ -150,6 +177,7 @@ impl<A: AsFd + Clone + 'static> GraphicsApi for GbmGlowBackend<A> {
|
|||
pub struct GbmGlowDevice {
|
||||
node: DrmNode,
|
||||
renderer: GlowRenderer,
|
||||
intel_export_quirk: bool,
|
||||
allocator: Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
|
||||
}
|
||||
|
||||
|
|
@ -180,9 +208,14 @@ impl ApiDevice for GbmGlowDevice {
|
|||
fn can_do_cross_device_imports(&self) -> bool {
|
||||
!Borrow::<GlesRenderer>::borrow(&self.renderer).is_software()
|
||||
}
|
||||
|
||||
fn should_do_cross_device_exports(&self) -> bool {
|
||||
!self.intel_export_quirk
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: GraphicsApi, A: AsFd + Clone + 'static> FromGlesError for MultiError<GbmGlowBackend<A>, T>
|
||||
impl<T: GraphicsApi, A: AsFd + Device + Clone + 'static> FromGlesError
|
||||
for MultiError<GbmGlowBackend<A>, T>
|
||||
where
|
||||
T::Error: 'static,
|
||||
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
|
||||
|
|
|
|||
|
|
@ -1024,14 +1024,18 @@ impl SurfaceThreadState {
|
|||
let animations_going = shell.animations_going();
|
||||
let output = self.mirroring.as_ref().unwrap_or(&self.output);
|
||||
if let Some((_, workspace)) = shell.workspaces.active(output) {
|
||||
if let Some(fullscreen_surface) = workspace.get_fullscreen() {
|
||||
let seat = shell.seats.last_active();
|
||||
if let Some(fullscreen_surface) = workspace.get_fullscreen(seat) {
|
||||
const _30_FPS: Duration = Duration::from_nanos(1_000_000_000 / 30);
|
||||
(
|
||||
true,
|
||||
fullscreen_surface.wl_surface().is_some_and(|surface| {
|
||||
recursive_frame_time_estimation(&self.clock, &surface)
|
||||
.is_some_and(|dur| dur <= _30_FPS)
|
||||
}),
|
||||
fullscreen_surface
|
||||
.surface
|
||||
.wl_surface()
|
||||
.is_some_and(|surface| {
|
||||
recursive_frame_time_estimation(&self.clock, &surface)
|
||||
.is_some_and(|dur| dur <= _30_FPS)
|
||||
}),
|
||||
animations_going,
|
||||
)
|
||||
} else {
|
||||
|
|
@ -1490,18 +1494,21 @@ fn render_node_for_output(
|
|||
let Some(workspace) = shell.active_space(output) else {
|
||||
return *target_node;
|
||||
};
|
||||
let nodes = workspace
|
||||
.get_fullscreen()
|
||||
.map(|w| vec![w.clone()])
|
||||
.unwrap_or_else(|| {
|
||||
workspace
|
||||
.mapped()
|
||||
.map(|mapped| mapped.active_window())
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.into_iter()
|
||||
.flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s)))
|
||||
.collect::<Vec<_>>();
|
||||
let fullscreens: Vec<_> = workspace
|
||||
.get_fullscreen_surfaces()
|
||||
.map(|f| f.surface.clone())
|
||||
.collect();
|
||||
let nodes = if !fullscreens.is_empty() {
|
||||
fullscreens
|
||||
} else {
|
||||
workspace
|
||||
.mapped()
|
||||
.map(|mapped| mapped.active_window())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
.into_iter()
|
||||
.flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if nodes.contains(target_node) || nodes.is_empty() {
|
||||
*target_node
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ use smithay::{
|
|||
calloop::{EventLoop, ping},
|
||||
wayland_protocols::wp::presentation_time::server::wp_presentation_feedback,
|
||||
wayland_server::DisplayHandle,
|
||||
winit::platform::pump_events::PumpStatus,
|
||||
winit::event_loop::pump_events::PumpStatus,
|
||||
},
|
||||
utils::Transform,
|
||||
wayland::{dmabuf::DmabufFeedbackBuilder, presentation::Refresh},
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ mod types;
|
|||
use cosmic::config::CosmicTk;
|
||||
pub use cosmic_comp_config::EdidProduct;
|
||||
use cosmic_comp_config::{
|
||||
AppearanceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior, XkbConfig, XwaylandDescaling,
|
||||
XwaylandEavesdropping, ZoomConfig,
|
||||
ActivationPolicy, AppearanceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior, XkbConfig,
|
||||
XwaylandDescaling, XwaylandEavesdropping, ZoomConfig,
|
||||
input::{DeviceState as InputDeviceState, InputConfig, TouchpadOverride},
|
||||
output::comp::{
|
||||
OutputConfig, OutputInfo, OutputState, OutputsConfig, TransformDef, load_outputs,
|
||||
|
|
@ -966,6 +966,12 @@ 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,13 +1,12 @@
|
|||
// https://gitlab.gnome.org/GNOME/mutter/-/blob/main/data/dbus-interfaces/org.freedesktop.a11y.xml
|
||||
|
||||
use futures_executor::ThreadPool;
|
||||
use smithay::{
|
||||
backend::input::KeyState,
|
||||
input::keyboard::{KeysymHandle, ModifiersState},
|
||||
};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::{Arc, Mutex, OnceLock},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use tracing::debug;
|
||||
use xkbcommon::xkb::Keysym;
|
||||
|
|
@ -72,40 +71,37 @@ impl Clients {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct A11yKeyboardMonitorState {
|
||||
executor: ThreadPool,
|
||||
executor: calloop::futures::Scheduler<()>,
|
||||
clients: Arc<Mutex<Clients>>,
|
||||
active_virtual_mods: HashSet<Keysym>,
|
||||
conn: Arc<OnceLock<zbus::Connection>>,
|
||||
name_owners: Arc<OnceLock<NameOwners>>,
|
||||
conn: zbus::Connection,
|
||||
name_owners: NameOwners,
|
||||
}
|
||||
|
||||
impl A11yKeyboardMonitorState {
|
||||
pub fn new(executor: &ThreadPool) -> Self {
|
||||
pub async fn new(
|
||||
conn: &zbus::Connection,
|
||||
name_owners: &NameOwners,
|
||||
executor: &calloop::futures::Scheduler<()>,
|
||||
) -> zbus::Result<Self> {
|
||||
let clients = Arc::new(Mutex::new(Clients::default()));
|
||||
let clients_clone = clients.clone();
|
||||
let conn_cell = Arc::new(OnceLock::new());
|
||||
let conn_cell_clone = conn_cell.clone();
|
||||
let name_owners_cell = Arc::new(OnceLock::new());
|
||||
let name_owners_cell_clone = name_owners_cell.clone();
|
||||
let executor_clone = executor.clone();
|
||||
executor.spawn_ok(async move {
|
||||
match serve(clients_clone, &executor_clone).await {
|
||||
Ok((conn, name_owners)) => {
|
||||
conn_cell_clone.set(conn).unwrap();
|
||||
name_owners_cell_clone.set(name_owners).unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to serve `org.freedesktop.a11y.Manager`: {err}");
|
||||
}
|
||||
}
|
||||
});
|
||||
Self {
|
||||
|
||||
let keyboard_monitor = KeyboardMonitor {
|
||||
clients: clients.clone(),
|
||||
name_owners: name_owners.clone(),
|
||||
};
|
||||
conn.object_server()
|
||||
.at("/org/freedesktop/a11y/Manager", keyboard_monitor)
|
||||
.await?;
|
||||
conn.request_name("org.freedesktop.a11y.Manager").await?;
|
||||
|
||||
Ok(Self {
|
||||
executor: executor.clone(),
|
||||
clients,
|
||||
active_virtual_mods: HashSet::new(),
|
||||
conn: conn_cell,
|
||||
name_owners: name_owners_cell,
|
||||
}
|
||||
conn: conn.clone(),
|
||||
name_owners: name_owners.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn has_virtual_mod(&self, keysym: Keysym) -> bool {
|
||||
|
|
@ -154,10 +150,6 @@ impl A11yKeyboardMonitorState {
|
|||
}
|
||||
|
||||
pub fn key_event(&self, modifiers: &ModifiersState, keysym: &KeysymHandle, state: KeyState) {
|
||||
let Some(conn) = self.conn.get() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let clients = self.clients.lock().unwrap();
|
||||
for (unique_name, client) in clients.0.iter() {
|
||||
if !client.watched && !self.has_key_grab(modifiers, keysym.modified_sym()) {
|
||||
|
|
@ -165,7 +157,7 @@ impl A11yKeyboardMonitorState {
|
|||
}
|
||||
|
||||
let mut signal_context =
|
||||
SignalEmitter::new(conn, "/org/freedesktop/a11y/Manager").unwrap();
|
||||
SignalEmitter::new(&self.conn, "/org/freedesktop/a11y/Manager").unwrap();
|
||||
// Instead of sending signal to all clients, send only to authorized
|
||||
// clients with registed watches.
|
||||
signal_context = signal_context.set_destination(unique_name.clone().into());
|
||||
|
|
@ -186,7 +178,7 @@ impl A11yKeyboardMonitorState {
|
|||
unichar,
|
||||
keysym.raw_code().raw() as u16,
|
||||
);
|
||||
self.executor.spawn_ok(async {
|
||||
let _ = self.executor.schedule(async {
|
||||
let _ = future.await;
|
||||
});
|
||||
}
|
||||
|
|
@ -195,13 +187,11 @@ impl A11yKeyboardMonitorState {
|
|||
pub fn refresh(&mut self) {
|
||||
// Remove clients and associated grabs when unique names are no longer
|
||||
// present on bus, or no longer hold approved name on bus.
|
||||
if let Some(name_owners) = self.name_owners.get() {
|
||||
self.clients
|
||||
.lock()
|
||||
.unwrap()
|
||||
.0
|
||||
.retain(|k, _| name_owners.check_owner_no_poll(k, ALLOWED_NAMES))
|
||||
}
|
||||
self.clients
|
||||
.lock()
|
||||
.unwrap()
|
||||
.0
|
||||
.retain(|k, _| self.name_owners.check_owner_no_poll(k, ALLOWED_NAMES))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -311,20 +301,3 @@ impl KeyboardMonitor {
|
|||
keycode: u16,
|
||||
) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
async fn serve(
|
||||
clients: Arc<Mutex<Clients>>,
|
||||
executor: &ThreadPool,
|
||||
) -> zbus::Result<(zbus::Connection, NameOwners)> {
|
||||
let conn = zbus::Connection::session().await?;
|
||||
let name_owners = NameOwners::new(&conn, executor).await?;
|
||||
let keyboard_monitor = KeyboardMonitor {
|
||||
clients,
|
||||
name_owners: name_owners.clone(),
|
||||
};
|
||||
conn.object_server()
|
||||
.at("/org/freedesktop/a11y/Manager", keyboard_monitor)
|
||||
.await?;
|
||||
conn.request_name("org.freedesktop.a11y.Manager").await?;
|
||||
Ok((conn, name_owners))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,34 @@
|
|||
use std::os::fd::OwnedFd;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use logind_zbus::manager::{InhibitType::HandleLidSwitch, ManagerProxyBlocking};
|
||||
use zbus::blocking::Connection;
|
||||
use logind_zbus::manager::{InhibitType::HandleLidSwitch, ManagerProxy};
|
||||
|
||||
pub fn inhibit_lid() -> Result<OwnedFd> {
|
||||
let conn = Connection::system()?;
|
||||
let proxy = ManagerProxyBlocking::new(&conn)?;
|
||||
let fd = proxy.inhibit(
|
||||
HandleLidSwitch,
|
||||
"cosmic-comp",
|
||||
"External output connected",
|
||||
"block",
|
||||
)?;
|
||||
use crate::state::Common;
|
||||
|
||||
pub fn inhibit_lid(common: &Common) -> Result<OwnedFd> {
|
||||
let fd = futures_executor::block_on(async {
|
||||
let conn = common.dbus_state.system_conn().await?;
|
||||
let manager = ManagerProxy::new(conn).await?;
|
||||
manager
|
||||
.inhibit(
|
||||
HandleLidSwitch,
|
||||
"cosmic-comp",
|
||||
"External output connected",
|
||||
"block",
|
||||
)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(fd.into())
|
||||
}
|
||||
|
||||
pub fn lid_closed() -> Result<bool> {
|
||||
let conn = Connection::system()?;
|
||||
let proxy = ManagerProxyBlocking::new(&conn)?;
|
||||
proxy.lid_closed().context("Failed to talk to logind")
|
||||
pub fn lid_closed(common: &Common) -> Result<bool> {
|
||||
futures_executor::block_on(async {
|
||||
let conn = common.dbus_state.system_conn().await?;
|
||||
let manager = ManagerProxy::new(conn).await?;
|
||||
manager
|
||||
.lid_closed()
|
||||
.await
|
||||
.context("Failed to talk to logind")
|
||||
})
|
||||
}
|
||||
|
|
|
|||
205
src/dbus/mod.rs
205
src/dbus/mod.rs
|
|
@ -3,103 +3,168 @@ use crate::{
|
|||
utils::prelude::OutputExt,
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use calloop::{InsertError, LoopHandle, RegistrationToken};
|
||||
use calloop::{InsertError, LoopHandle, stream::StreamSource};
|
||||
use cosmic_comp_config::output::comp::OutputState;
|
||||
use futures_executor::{ThreadPool, block_on};
|
||||
use futures_util::stream::StreamExt;
|
||||
use std::collections::HashMap;
|
||||
use std::{
|
||||
cell::{RefCell, RefMut},
|
||||
collections::HashMap,
|
||||
rc::Rc,
|
||||
};
|
||||
use tracing::{error, warn};
|
||||
use zbus::blocking::{Connection, fdo::DBusProxy};
|
||||
|
||||
pub mod a11y_keyboard_monitor;
|
||||
#[cfg(feature = "systemd")]
|
||||
use a11y_keyboard_monitor::A11yKeyboardMonitorState;
|
||||
#[cfg(feature = "logind")]
|
||||
pub mod logind;
|
||||
mod name_owners;
|
||||
mod power;
|
||||
|
||||
pub fn init(
|
||||
evlh: &LoopHandle<'static, State>,
|
||||
executor: &ThreadPool,
|
||||
) -> Result<Vec<RegistrationToken>> {
|
||||
let mut tokens = Vec::new();
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DBusState(Rc<DBusStateInner>);
|
||||
|
||||
match block_on(power::init()) {
|
||||
#[derive(Debug)]
|
||||
struct DBusStateInner {
|
||||
evlh: LoopHandle<'static, State>,
|
||||
executor: calloop::futures::Scheduler<()>,
|
||||
session_conn: zbus::Result<zbus::Connection>,
|
||||
system_conn: zbus::Result<zbus::Connection>,
|
||||
a11y_keyboard_monitor: RefCell<Option<a11y_keyboard_monitor::A11yKeyboardMonitorState>>,
|
||||
}
|
||||
|
||||
impl DBusState {
|
||||
pub fn init(evlh: &LoopHandle<'static, State>) -> Self {
|
||||
let (source, executor) = calloop::futures::executor().unwrap();
|
||||
let session_conn = futures_executor::block_on(zbus::Connection::session());
|
||||
let system_conn = futures_executor::block_on(zbus::Connection::system());
|
||||
let state = Self(Rc::new(DBusStateInner {
|
||||
evlh: evlh.clone(),
|
||||
executor,
|
||||
session_conn,
|
||||
system_conn,
|
||||
a11y_keyboard_monitor: RefCell::new(None),
|
||||
}));
|
||||
evlh.insert_source(source, |_, _, _| {}).unwrap();
|
||||
let state_clone = state.clone();
|
||||
state.spawn(async move {
|
||||
if let Err(err) = init_session(&state_clone).await {
|
||||
tracing::error!("Failed to initialize session DBus connection: {}", err);
|
||||
}
|
||||
});
|
||||
let state_clone = state.clone();
|
||||
state.spawn(async move {
|
||||
if let Err(err) = init_system(&state_clone).await {
|
||||
tracing::error!("Failed to initialize system DBus connection: {}", err);
|
||||
}
|
||||
});
|
||||
state
|
||||
}
|
||||
|
||||
pub fn a11y_keyboard_monitor(
|
||||
&self,
|
||||
) -> Option<RefMut<'_, a11y_keyboard_monitor::A11yKeyboardMonitorState>> {
|
||||
RefMut::filter_map(self.0.a11y_keyboard_monitor.borrow_mut(), |x| x.as_mut()).ok()
|
||||
}
|
||||
|
||||
// TODO Lazy async init when we don't have anything blocking main thread
|
||||
async fn session_conn(&self) -> zbus::Result<&zbus::Connection> {
|
||||
self.0.session_conn.as_ref().map_err(|err| err.clone())
|
||||
}
|
||||
|
||||
async fn system_conn(&self) -> zbus::Result<&zbus::Connection> {
|
||||
self.0.system_conn.as_ref().map_err(|err| err.clone())
|
||||
}
|
||||
|
||||
fn spawn(&self, fut: impl Future<Output = ()> + 'static) {
|
||||
let _ = self.0.executor.schedule(fut);
|
||||
}
|
||||
}
|
||||
|
||||
async fn init_session(state: &DBusState) -> zbus::Result<()> {
|
||||
let conn = state.session_conn().await?;
|
||||
let name_owners = name_owners::NameOwners::new(conn, &state.0.executor).await?;
|
||||
let a11y_keyboard_monitor_state =
|
||||
A11yKeyboardMonitorState::new(conn, &name_owners, &state.0.executor).await?;
|
||||
*state.0.a11y_keyboard_monitor.borrow_mut() = Some(a11y_keyboard_monitor_state);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn init_system(state: &DBusState) -> zbus::Result<()> {
|
||||
let conn = state.system_conn().await?.clone();
|
||||
let evlh = state.0.evlh.clone();
|
||||
state.spawn(async move {
|
||||
if let Err(err) = power_hot_plug_task(conn, evlh).await {
|
||||
tracing::warn!(?err, "Failed to initialize dbus handlers");
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn power_hot_plug_task(
|
||||
conn: zbus::Connection,
|
||||
evlh: LoopHandle<'static, State>,
|
||||
) -> Result<()> {
|
||||
match power::init(&conn).await {
|
||||
Ok(power_daemon) => {
|
||||
let (tx, rx) = calloop::channel::channel();
|
||||
|
||||
let token = evlh
|
||||
.insert_source(rx, |event, _, state| match event {
|
||||
calloop::channel::Event::Msg(_) => {
|
||||
let nodes = match &mut state.backend {
|
||||
BackendData::Kms(kms) => {
|
||||
kms.drm_devices.keys().cloned().collect::<Vec<_>>()
|
||||
}
|
||||
_ => Vec::new(),
|
||||
};
|
||||
let mut added = Vec::new();
|
||||
for node in nodes {
|
||||
match state.device_changed(node.dev_id()) {
|
||||
Ok(outputs) => added.extend(outputs),
|
||||
Err(err) => {
|
||||
tracing::error!(?err, "Failed to update drm device {}.", node)
|
||||
}
|
||||
}
|
||||
if let Ok(stream) = power_daemon.receive_hot_plug_detect().await {
|
||||
let source = StreamSource::new(stream).unwrap();
|
||||
evlh.insert_source(source, |_, _, state| {
|
||||
let nodes = match &mut state.backend {
|
||||
BackendData::Kms(kms) => {
|
||||
kms.drm_devices.keys().cloned().collect::<Vec<_>>()
|
||||
}
|
||||
if let Err(err) = state.refresh_output_config() {
|
||||
warn!("Unable to load output config: {}", err);
|
||||
if !added.is_empty() {
|
||||
for output in added {
|
||||
output.config_mut().enabled = OutputState::Disabled;
|
||||
}
|
||||
if let Err(err) = state.refresh_output_config() {
|
||||
error!("Unrecoverable config error: {}", err);
|
||||
}
|
||||
_ => Vec::new(),
|
||||
};
|
||||
let mut added = Vec::new();
|
||||
for node in nodes {
|
||||
match state.device_changed(node.dev_id()) {
|
||||
Ok(outputs) => added.extend(outputs),
|
||||
Err(err) => {
|
||||
tracing::error!(?err, "Failed to update drm device {}.", node)
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Err(err) = state.refresh_output_config() {
|
||||
warn!("Unable to load output config: {}", err);
|
||||
if !added.is_empty() {
|
||||
for output in added {
|
||||
output.config_mut().enabled = OutputState::Disabled;
|
||||
}
|
||||
if let Err(err) = state.refresh_output_config() {
|
||||
error!("Unrecoverable config error: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
calloop::channel::Event::Closed => (),
|
||||
})
|
||||
.map_err(|InsertError { error, .. }| error)
|
||||
.with_context(|| "Failed to add channel to event_loop")?;
|
||||
|
||||
// start helper thread
|
||||
executor.spawn_ok(async move {
|
||||
if let Ok(mut msg_iter) = power_daemon.receive_hot_plug_detect().await {
|
||||
while let Some(msg) = msg_iter.next().await {
|
||||
if tx.send(msg).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tokens.push(token);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::info!(?err, "Failed to connect to com.system76.PowerDaemon");
|
||||
}
|
||||
};
|
||||
|
||||
Ok(tokens)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Updated the D-Bus activation environment with `WAYLAND_DISPLAY` and
|
||||
/// `DISPLAY` variables.
|
||||
pub fn ready(common: &Common) -> Result<()> {
|
||||
let conn = Connection::session()?;
|
||||
let proxy = DBusProxy::new(&conn)?;
|
||||
|
||||
proxy.update_activation_environment(HashMap::from([
|
||||
("WAYLAND_DISPLAY", common.socket.to_str().unwrap()),
|
||||
(
|
||||
"DISPLAY",
|
||||
&common
|
||||
.xwayland_state
|
||||
.as_ref()
|
||||
.map(|s| format!(":{}", s.display))
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
]))?;
|
||||
futures_executor::block_on(async {
|
||||
let conn = common.dbus_state.session_conn().await?;
|
||||
let dbus = zbus::fdo::DBusProxy::new(conn).await?;
|
||||
|
||||
dbus.update_activation_environment(HashMap::from([
|
||||
("WAYLAND_DISPLAY", common.socket.to_str().unwrap()),
|
||||
(
|
||||
"DISPLAY",
|
||||
&common
|
||||
.xwayland_state
|
||||
.as_ref()
|
||||
.map(|s| format!(":{}", s.display))
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
]))
|
||||
.await
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@
|
|||
//!
|
||||
//! Compare to Mutter's `MetaDbusAccessChecker`
|
||||
|
||||
use futures_executor::ThreadPool;
|
||||
use futures_util::stream::FusedStream;
|
||||
use futures_util::{StreamExt, stream::FuturesUnordered};
|
||||
use futures_util::{
|
||||
StreamExt,
|
||||
stream::{FusedStream, FuturesUnordered},
|
||||
};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
future::{Future, poll_fn},
|
||||
|
|
@ -94,7 +95,10 @@ fn update_task(inner: Weak<Mutex<Inner>>) -> impl Future<Output = ()> {
|
|||
pub struct NameOwners(Arc<Mutex<Inner>>);
|
||||
|
||||
impl NameOwners {
|
||||
pub async fn new(connection: &zbus::Connection, executor: &ThreadPool) -> zbus::Result<Self> {
|
||||
pub async fn new(
|
||||
connection: &zbus::Connection,
|
||||
executor: &calloop::futures::Scheduler<()>,
|
||||
) -> zbus::Result<Self> {
|
||||
let dbus = fdo::DBusProxy::new(connection).await?;
|
||||
let stream = dbus.receive_name_owner_changed().await?;
|
||||
|
||||
|
|
@ -126,7 +130,7 @@ impl NameOwners {
|
|||
}));
|
||||
|
||||
if enforce {
|
||||
executor.spawn_ok(update_task(Arc::downgrade(&inner)));
|
||||
let _ = executor.schedule(update_task(Arc::downgrade(&inner)));
|
||||
}
|
||||
|
||||
Ok(NameOwners(inner))
|
||||
|
|
|
|||
|
|
@ -79,9 +79,8 @@ pub trait PowerDaemon {
|
|||
fn power_profile_switch(&self, profile: &str) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
pub async fn init() -> anyhow::Result<PowerDaemonProxy<'static>> {
|
||||
let conn = Connection::system().await?;
|
||||
let proxy = PowerDaemonProxy::new(&conn).await?;
|
||||
pub async fn init(conn: &Connection) -> anyhow::Result<PowerDaemonProxy<'static>> {
|
||||
let proxy = PowerDaemonProxy::new(conn).await?;
|
||||
proxy.0.introspect().await?;
|
||||
Ok(proxy)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,14 @@ impl State {
|
|||
action,
|
||||
Action::Shortcut(shortcuts::Action::Terminate)
|
||||
| Action::Shortcut(shortcuts::Action::Debug)
|
||||
| Action::Shortcut(shortcuts::Action::System(
|
||||
shortcuts::action::System::InputSourceSwitch
|
||||
| shortcuts::action::System::BrightnessDown
|
||||
| shortcuts::action::System::BrightnessUp
|
||||
| shortcuts::action::System::VolumeLower
|
||||
| shortcuts::action::System::VolumeRaise
|
||||
| shortcuts::action::System::Mute,
|
||||
))
|
||||
)
|
||||
{
|
||||
return;
|
||||
|
|
|
|||
478
src/input/mod.rs
478
src/input/mod.rs
|
|
@ -52,16 +52,21 @@ use smithay::{
|
|||
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent,
|
||||
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,
|
||||
GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent,
|
||||
PointerGrab, RelativeMotionEvent,
|
||||
PointerGrab, PointerHandle, RelativeMotionEvent,
|
||||
},
|
||||
touch::{DownEvent, MotionEvent as TouchMotionEvent, UpEvent},
|
||||
},
|
||||
output::Output,
|
||||
reexports::{
|
||||
input::Device as InputDevice, wayland_server::protocol::wl_shm::Format as ShmFormat,
|
||||
input::Device as InputDevice,
|
||||
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::{
|
||||
compositor::CompositorHandler,
|
||||
image_copy_capture::{BufferConstraints, CursorSessionRef},
|
||||
keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat,
|
||||
pointer_constraints::{PointerConstraint, with_pointer_constraint},
|
||||
|
|
@ -349,27 +354,6 @@ impl State {
|
|||
_ => {}
|
||||
});
|
||||
}
|
||||
let original_position = position;
|
||||
position += event.delta().as_global();
|
||||
|
||||
let output = shell
|
||||
.outputs()
|
||||
.find(|output| output.geometry().to_f64().contains(position))
|
||||
.cloned()
|
||||
.unwrap_or(current_output.clone());
|
||||
|
||||
let output_geometry = output.geometry();
|
||||
position.x = position.x.clamp(
|
||||
output_geometry.loc.x as f64,
|
||||
(output_geometry.loc.x + output_geometry.size.w - 1) as f64,
|
||||
);
|
||||
position.y = position.y.clamp(
|
||||
output_geometry.loc.y as f64,
|
||||
(output_geometry.loc.y + output_geometry.size.h - 1) as f64,
|
||||
);
|
||||
|
||||
let new_under = State::surface_under(position, &output, &shell)
|
||||
.map(|(target, pos)| (target, pos.as_logical()));
|
||||
|
||||
std::mem::drop(shell);
|
||||
ptr.relative_motion(
|
||||
|
|
@ -387,6 +371,25 @@ impl State {
|
|||
return;
|
||||
}
|
||||
|
||||
let original_position = position;
|
||||
position += event.delta().as_global();
|
||||
let shell = self.common.shell.read();
|
||||
let output = shell
|
||||
.outputs()
|
||||
.find(|output| output.geometry().to_f64().contains(position))
|
||||
.cloned()
|
||||
.unwrap_or(current_output.clone());
|
||||
drop(shell);
|
||||
let output_geometry = output.geometry();
|
||||
position.x = position.x.clamp(
|
||||
output_geometry.loc.x as f64,
|
||||
(output_geometry.loc.x + output_geometry.size.w - 1) as f64,
|
||||
);
|
||||
position.y = position.y.clamp(
|
||||
output_geometry.loc.y as f64,
|
||||
(output_geometry.loc.y + output_geometry.size.h - 1) as f64,
|
||||
);
|
||||
|
||||
if ptr.is_grabbed() {
|
||||
if seat
|
||||
.user_data()
|
||||
|
|
@ -494,50 +497,90 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
// If confined, don't move pointer if it would go outside surface or region
|
||||
if pointer_confined && let Some((surface, surface_loc)) = &under {
|
||||
if new_under.as_ref().and_then(|(under, _)| under.wl_surface())
|
||||
!= surface.wl_surface()
|
||||
{
|
||||
ptr.frame(self);
|
||||
return;
|
||||
}
|
||||
match surface {
|
||||
PointerFocusTarget::WlSurface { surface, .. } => {
|
||||
if under_from_surface_tree(
|
||||
surface,
|
||||
position.as_logical() - surface_loc.to_f64(),
|
||||
(0, 0),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.is_none()
|
||||
{
|
||||
ptr.frame(self);
|
||||
return;
|
||||
}
|
||||
// If confined, help user to update valid coordinates can improve user experience
|
||||
let shell = self.common.shell.read();
|
||||
let new_under = if pointer_confined && let Some((surface, surface_loc)) = &under
|
||||
{
|
||||
let is_legal = |pos: Point<f64, Global>, shell: &Shell| {
|
||||
let new_under = State::surface_under(pos, &output, shell)
|
||||
.map(|(target, pos)| (target, pos.as_logical()));
|
||||
|
||||
// TODO: We might need a solution that allows constraints to bypass the surface without affecting the constraints themselves
|
||||
if new_under.as_ref().and_then(|(under, _)| under.wl_surface())
|
||||
!= surface.wl_surface()
|
||||
{
|
||||
return (false, None);
|
||||
}
|
||||
PointerFocusTarget::X11Surface { surface, .. } => {
|
||||
if surface
|
||||
.surface_under(
|
||||
|
||||
match surface {
|
||||
PointerFocusTarget::WlSurface { surface, .. } => {
|
||||
if under_from_surface_tree(
|
||||
surface,
|
||||
position.as_logical() - surface_loc.to_f64(),
|
||||
(0, 0),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.is_none()
|
||||
{
|
||||
ptr.frame(self);
|
||||
return;
|
||||
{
|
||||
return (false, None);
|
||||
}
|
||||
}
|
||||
PointerFocusTarget::X11Surface { surface, .. } => {
|
||||
if surface
|
||||
.surface_under(
|
||||
position.as_logical() - surface_loc.to_f64(),
|
||||
(0, 0),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.is_none()
|
||||
{
|
||||
return (false, None);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Some(region) = &confine_region
|
||||
&& !region
|
||||
.contains((pos.as_logical() - *surface_loc).to_i32_round())
|
||||
{
|
||||
return (false, None);
|
||||
}
|
||||
(true, new_under)
|
||||
};
|
||||
|
||||
match is_legal(position, &shell) {
|
||||
(true, under) => under,
|
||||
_ => {
|
||||
let y_only_pos = Point::new(original_position.x, position.y);
|
||||
let x_only_pos = Point::new(position.x, original_position.y);
|
||||
match is_legal(y_only_pos, &shell) {
|
||||
(true, under) => {
|
||||
position = y_only_pos;
|
||||
under
|
||||
}
|
||||
_ => match is_legal(x_only_pos, &shell) {
|
||||
(true, under) => {
|
||||
position = x_only_pos;
|
||||
under
|
||||
}
|
||||
_ => {
|
||||
position = original_position;
|
||||
None
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Some(region) = confine_region
|
||||
&& !region
|
||||
.contains((position.as_logical() - *surface_loc).to_i32_round())
|
||||
{
|
||||
ptr.frame(self);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
State::surface_under(position, &output, &shell)
|
||||
.map(|(target, pos)| (target, pos.as_logical()))
|
||||
};
|
||||
|
||||
drop(shell);
|
||||
if pointer_confined && new_under.is_none() {
|
||||
ptr.frame(self);
|
||||
return;
|
||||
}
|
||||
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
|
|
@ -552,24 +595,32 @@ impl State {
|
|||
);
|
||||
ptr.frame(self);
|
||||
|
||||
// If pointer is now in a constraint region, activate it
|
||||
// If pointer is now in a constraint region and window is in focused, activate it
|
||||
if let Some((under, surface_location)) = new_under
|
||||
.and_then(|(target, loc)| Some((target.wl_surface()?.into_owned(), loc)))
|
||||
{
|
||||
with_pointer_constraint(&under, &ptr, |constraint| match constraint {
|
||||
Some(constraint) if !constraint.is_active() => {
|
||||
let region = match &*constraint {
|
||||
PointerConstraint::Locked(locked) => locked.region(),
|
||||
PointerConstraint::Confined(confined) => confined.region(),
|
||||
};
|
||||
let point =
|
||||
(ptr.current_location() - surface_location).to_i32_round();
|
||||
if region.is_none_or(|region| region.contains(point)) {
|
||||
constraint.activate();
|
||||
let shell = self.common.shell.read();
|
||||
let is_focused = seat
|
||||
.get_keyboard()
|
||||
.and_then(|k| k.current_focus())
|
||||
.is_some_and(|f| f.has_surface(&shell, &under));
|
||||
|
||||
if is_focused {
|
||||
with_pointer_constraint(&under, &ptr, |constraint| match constraint {
|
||||
Some(constraint) if !constraint.is_active() => {
|
||||
let region = match &*constraint {
|
||||
PointerConstraint::Locked(locked) => locked.region(),
|
||||
PointerConstraint::Confined(confined) => confined.region(),
|
||||
};
|
||||
let point =
|
||||
(ptr.current_location() - surface_location).to_i32_round();
|
||||
if region.is_none_or(|region| region.contains(point)) {
|
||||
constraint.activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
_ => {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let mut shell = self.common.shell.write();
|
||||
|
|
@ -589,11 +640,13 @@ impl State {
|
|||
|
||||
for session in cursor_sessions_for_output(&shell, &output) {
|
||||
if let Some((geometry, offset)) = seat.cursor_geometry(
|
||||
position.as_logical().to_buffer(
|
||||
output.current_scale().fractional_scale(),
|
||||
output.current_transform(),
|
||||
&output_geometry.size.to_f64().as_logical(),
|
||||
),
|
||||
(position - output_geometry.loc.to_f64())
|
||||
.as_logical()
|
||||
.to_buffer(
|
||||
output.current_scale().fractional_scale(),
|
||||
output.current_transform(),
|
||||
&output_geometry.size.to_f64().as_logical(),
|
||||
),
|
||||
self.common.clock.now(),
|
||||
) {
|
||||
if session
|
||||
|
|
@ -601,8 +654,13 @@ impl State {
|
|||
.map(|constraint| constraint.size != geometry.size)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
let mut cursor_size = geometry.size;
|
||||
// Client shouldn't try to allocate 0x0 buffer
|
||||
if cursor_size == Size::new(0, 0) {
|
||||
cursor_size = Size::new(1, 1);
|
||||
}
|
||||
session.update_constraints(BufferConstraints {
|
||||
size: geometry.size,
|
||||
size: cursor_size,
|
||||
shm: vec![ShmFormat::Argb8888],
|
||||
dma: None,
|
||||
});
|
||||
|
|
@ -625,11 +683,11 @@ impl State {
|
|||
self.common.idle_notifier_state.notify_activity(&seat);
|
||||
notify_cursor_activity(self, &seat);
|
||||
let output = seat.active_output();
|
||||
let geometry = output.geometry();
|
||||
let position = geometry.loc.to_f64()
|
||||
let output_geometry = output.geometry();
|
||||
let position = output_geometry.loc.to_f64()
|
||||
+ smithay::backend::input::AbsolutePositionEvent::position_transformed(
|
||||
&event,
|
||||
geometry.size.as_logical(),
|
||||
output_geometry.size.as_logical(),
|
||||
)
|
||||
.as_global();
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
|
|
@ -651,11 +709,13 @@ impl State {
|
|||
let shell = self.common.shell.read();
|
||||
for session in cursor_sessions_for_output(&shell, &output) {
|
||||
if let Some((geometry, offset)) = seat.cursor_geometry(
|
||||
position.as_logical().to_buffer(
|
||||
output.current_scale().fractional_scale(),
|
||||
output.current_transform(),
|
||||
&geometry.size.to_f64().as_logical(),
|
||||
),
|
||||
(position - output_geometry.loc.to_f64())
|
||||
.as_logical()
|
||||
.to_buffer(
|
||||
output.current_scale().fractional_scale(),
|
||||
output.current_transform(),
|
||||
&output_geometry.size.to_f64().as_logical(),
|
||||
),
|
||||
self.common.clock.now(),
|
||||
) {
|
||||
if session
|
||||
|
|
@ -663,8 +723,13 @@ impl State {
|
|||
.map(|constraint| constraint.size != geometry.size)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
let mut cursor_size = geometry.size;
|
||||
// Client shouldn't try to allocate 0x0 buffer
|
||||
if cursor_size == Size::new(0, 0) {
|
||||
cursor_size = Size::new(1, 1);
|
||||
}
|
||||
session.update_constraints(BufferConstraints {
|
||||
size: geometry.size,
|
||||
size: cursor_size,
|
||||
shm: vec![ShmFormat::Argb8888],
|
||||
dma: None,
|
||||
});
|
||||
|
|
@ -1535,7 +1600,7 @@ impl State {
|
|||
}
|
||||
InputEvent::Special(_) => {}
|
||||
InputEvent::SwitchToggle { event } => {
|
||||
#[cfg(feature = "systemd")]
|
||||
#[cfg(feature = "logind")]
|
||||
if event.switch() == Some(Switch::Lid) && self.common.inhibit_lid_fd.is_some() {
|
||||
let backend = self.backend.lock();
|
||||
let output = backend
|
||||
|
|
@ -1617,9 +1682,9 @@ impl State {
|
|||
.unwrap_or(false)
|
||||
});
|
||||
|
||||
self.common
|
||||
.a11y_keyboard_monitor_state
|
||||
.key_event(modifiers, &handle, event.state());
|
||||
if let Some(a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor() {
|
||||
a11y_keyboard_monitor.key_event(modifiers, &handle, event.state());
|
||||
}
|
||||
|
||||
// Leave move overview mode, if any modifier was released
|
||||
if let Some(Trigger::KeyboardMove(action_modifiers)) =
|
||||
|
|
@ -1779,61 +1844,54 @@ impl State {
|
|||
)));
|
||||
}
|
||||
|
||||
if event.state() == KeyState::Released {
|
||||
let removed = self
|
||||
.common
|
||||
.a11y_keyboard_monitor_state
|
||||
.remove_active_virtual_mod(handle.modified_sym());
|
||||
// If `Caps_Lock` is a virtual modifier, and is in locked state, clear it
|
||||
if removed
|
||||
&& handle.modified_sym() == Keysym::Caps_Lock
|
||||
&& (modifiers.serialized.locked & 2) != 0
|
||||
if let Some(mut a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor() {
|
||||
if event.state() == KeyState::Released {
|
||||
let removed =
|
||||
a11y_keyboard_monitor.remove_active_virtual_mod(handle.modified_sym());
|
||||
// If `Caps_Lock` is a virtual modifier, and is in locked state, clear it
|
||||
if removed
|
||||
&& handle.modified_sym() == Keysym::Caps_Lock
|
||||
&& (modifiers.serialized.locked & 2) != 0
|
||||
{
|
||||
let seat = seat.clone();
|
||||
let key_code = event.key_code();
|
||||
self.common.event_loop_handle.insert_idle(move |state| {
|
||||
if let Some(keyboard) = seat.get_keyboard() {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let time = state.common.clock.now().as_millis();
|
||||
keyboard.input(
|
||||
state,
|
||||
key_code,
|
||||
KeyState::Pressed,
|
||||
serial,
|
||||
time,
|
||||
|_, _, _| FilterResult::<()>::Forward,
|
||||
);
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
keyboard.input(
|
||||
state,
|
||||
key_code,
|
||||
KeyState::Released,
|
||||
serial,
|
||||
time,
|
||||
|_, _, _| FilterResult::<()>::Forward,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if event.state() == KeyState::Pressed
|
||||
&& a11y_keyboard_monitor.has_virtual_mod(handle.modified_sym())
|
||||
{
|
||||
let seat = seat.clone();
|
||||
let key_code = event.key_code();
|
||||
self.common.event_loop_handle.insert_idle(move |state| {
|
||||
if let Some(keyboard) = seat.get_keyboard() {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let time = state.common.clock.now().as_millis();
|
||||
keyboard.input(
|
||||
state,
|
||||
key_code,
|
||||
KeyState::Pressed,
|
||||
serial,
|
||||
time,
|
||||
|_, _, _| FilterResult::<()>::Forward,
|
||||
);
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
keyboard.input(
|
||||
state,
|
||||
key_code,
|
||||
KeyState::Released,
|
||||
serial,
|
||||
time,
|
||||
|_, _, _| FilterResult::<()>::Forward,
|
||||
);
|
||||
}
|
||||
});
|
||||
a11y_keyboard_monitor.add_active_virtual_mod(handle.modified_sym());
|
||||
|
||||
tracing::debug!(
|
||||
"active virtual mods: {:?}",
|
||||
a11y_keyboard_monitor.active_virtual_mods()
|
||||
);
|
||||
seat.supressed_keys().add(&handle, None);
|
||||
|
||||
return FilterResult::Intercept(None);
|
||||
}
|
||||
} else if event.state() == KeyState::Pressed
|
||||
&& self
|
||||
.common
|
||||
.a11y_keyboard_monitor_state
|
||||
.has_virtual_mod(handle.modified_sym())
|
||||
{
|
||||
self.common
|
||||
.a11y_keyboard_monitor_state
|
||||
.add_active_virtual_mod(handle.modified_sym());
|
||||
|
||||
tracing::debug!(
|
||||
"active virtual mods: {:?}",
|
||||
self.common
|
||||
.a11y_keyboard_monitor_state
|
||||
.active_virtual_mods()
|
||||
);
|
||||
seat.supressed_keys().add(&handle, None);
|
||||
|
||||
return FilterResult::Intercept(None);
|
||||
}
|
||||
|
||||
// Skip released events for initially surpressed keys
|
||||
|
|
@ -1860,12 +1918,10 @@ impl State {
|
|||
return FilterResult::Intercept(None);
|
||||
}
|
||||
|
||||
if event.state() == KeyState::Pressed
|
||||
&& (self.common.a11y_keyboard_monitor_state.has_keyboard_grab()
|
||||
|| self
|
||||
.common
|
||||
.a11y_keyboard_monitor_state
|
||||
.has_key_grab(modifiers, handle.modified_sym()))
|
||||
if let Some(a11y_keyboard_monitor) = self.common.dbus_state.a11y_keyboard_monitor()
|
||||
&& event.state() == KeyState::Pressed
|
||||
&& (a11y_keyboard_monitor.has_keyboard_grab()
|
||||
|| a11y_keyboard_monitor.has_key_grab(modifiers, handle.modified_sym()))
|
||||
{
|
||||
let modifiers_queue = seat.modifiers_shortcut_queue();
|
||||
modifiers_queue.clear();
|
||||
|
|
@ -2117,14 +2173,14 @@ impl State {
|
|||
}
|
||||
Stage::StickyPopups(layout) => {
|
||||
if let Some(element) =
|
||||
layout.popup_element_under(global_pos.to_local(output))
|
||||
layout.popup_element_under(global_pos.to_local(output), seat)
|
||||
{
|
||||
return ControlFlow::Break(Ok(Some(element)));
|
||||
}
|
||||
}
|
||||
Stage::Sticky(layout) => {
|
||||
if let Some(element) =
|
||||
layout.toplevel_element_under(global_pos.to_local(output))
|
||||
layout.toplevel_element_under(global_pos.to_local(output), seat)
|
||||
{
|
||||
return ControlFlow::Break(Ok(Some(element)));
|
||||
}
|
||||
|
|
@ -2279,7 +2335,7 @@ impl State {
|
|||
}
|
||||
Stage::StickyPopups(floating_layer) => {
|
||||
if let Some(under) = floating_layer
|
||||
.popup_surface_under(relative_pos)
|
||||
.popup_surface_under(relative_pos, seat)
|
||||
.map(|(target, point)| (target, point.to_global(output)))
|
||||
{
|
||||
return ControlFlow::Break(Ok(Some(under)));
|
||||
|
|
@ -2287,7 +2343,7 @@ impl State {
|
|||
}
|
||||
Stage::Sticky(floating_layer) => {
|
||||
if let Some(under) = floating_layer
|
||||
.toplevel_surface_under(relative_pos)
|
||||
.toplevel_surface_under(relative_pos, seat)
|
||||
.map(|(target, point)| (target, point.to_global(output)))
|
||||
{
|
||||
return ControlFlow::Break(Ok(Some(under)));
|
||||
|
|
@ -2317,8 +2373,113 @@ impl State {
|
|||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub fn apply_cursor_hint(
|
||||
&mut self,
|
||||
surface: &WlSurface,
|
||||
pointer: &PointerHandle<Self>,
|
||||
mut location: Point<f64, Logical>,
|
||||
) {
|
||||
let Some(client) = surface.client() else {
|
||||
return;
|
||||
};
|
||||
location = location.downscale(self.client_compositor_state(&client).client_scale());
|
||||
|
||||
let point_and_output = {
|
||||
let shell = self.common.shell.read();
|
||||
let found = shell.workspaces.sets.iter().find_map(|(out, set)| {
|
||||
set.surface_geometry_offset_from_toplevel(surface)
|
||||
.map(|(geometry, surface_offset)| (out, geometry, surface_offset))
|
||||
});
|
||||
|
||||
if let Some((output, geometry, surface_offset)) = found {
|
||||
let pos_in_element = location + surface_offset.to_f64();
|
||||
let window_size = geometry.size.to_f64();
|
||||
|
||||
let is_legal = |p: Point<f64, Logical>| {
|
||||
let in_window =
|
||||
p.x >= 0.0 && p.y >= 0.0 && p.x < window_size.w && p.y < window_size.h;
|
||||
if !in_window {
|
||||
return false;
|
||||
}
|
||||
|
||||
with_pointer_constraint(surface, pointer, |constraint| {
|
||||
if let Some(constraint) = constraint
|
||||
&& let Some(region) = constraint.region()
|
||||
{
|
||||
let point_in_surface = (p - surface_offset.to_f64()).to_i32_round();
|
||||
return region.contains(point_in_surface);
|
||||
}
|
||||
true
|
||||
})
|
||||
};
|
||||
|
||||
let workspace_origin = output.geometry().loc.to_f64();
|
||||
let origin = geometry.loc.to_f64();
|
||||
|
||||
if is_legal(pos_in_element) {
|
||||
let x = workspace_origin.x + origin.x + pos_in_element.x;
|
||||
let y = workspace_origin.y + origin.y + pos_in_element.y;
|
||||
Some((Point::new(x, y), output.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some((point, output)) = point_and_output {
|
||||
let original_position = pointer.current_location();
|
||||
pointer.set_location(point);
|
||||
|
||||
let mut shell = self.common.shell.write();
|
||||
shell.update_pointer_position(point.as_global().to_local(&output), &output);
|
||||
|
||||
let seat = shell
|
||||
.seats
|
||||
.iter()
|
||||
.find(|s| s.get_pointer().as_ref() == Some(pointer))
|
||||
.cloned();
|
||||
|
||||
if let Some(seat) = seat {
|
||||
shell.update_focal_point(
|
||||
&seat,
|
||||
original_position.as_global(),
|
||||
self.common.config.cosmic_conf.accessibility_zoom.view_moves,
|
||||
);
|
||||
|
||||
let output_geometry = output.geometry();
|
||||
for session in cursor_sessions_for_output(&shell, &output) {
|
||||
if let Some((geometry, offset)) = seat.cursor_geometry(
|
||||
point.to_buffer(
|
||||
output.current_scale().fractional_scale(),
|
||||
output.current_transform(),
|
||||
&output_geometry.size.to_f64().as_logical(),
|
||||
),
|
||||
self.common.clock.now(),
|
||||
) {
|
||||
if session
|
||||
.current_constraints()
|
||||
.map(|constraint| constraint.size != geometry.size)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
session.update_constraints(BufferConstraints {
|
||||
size: geometry.size,
|
||||
shm: vec![ShmFormat::Argb8888],
|
||||
dma: None,
|
||||
});
|
||||
}
|
||||
session.set_cursor_hotspot(offset);
|
||||
session.set_cursor_pos(Some(geometry.loc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output and workspace sessions for the given output
|
||||
fn cursor_sessions_for_output<'a>(
|
||||
shell: &'a Shell,
|
||||
output: &'a Output,
|
||||
|
|
@ -2327,16 +2488,9 @@ fn cursor_sessions_for_output<'a>(
|
|||
.active_space(output)
|
||||
.into_iter()
|
||||
.flat_map(|workspace| {
|
||||
let maybe_fullscreen = workspace.get_fullscreen();
|
||||
workspace
|
||||
.cursor_sessions()
|
||||
.into_iter()
|
||||
.chain(
|
||||
maybe_fullscreen
|
||||
.map(|w| w.cursor_sessions())
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
.chain(output.cursor_sessions())
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
fn main() {
|
||||
// libcosmic-yoda enables zbus' `tokio` feature, so zbus 5.14 expects an
|
||||
// ambient Tokio runtime via Handle::current(). cosmic-comp's loop is not
|
||||
// async, so hold a runtime guard for the lifetime of run().
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("failed to build tokio runtime");
|
||||
let _guard = runtime.enter();
|
||||
|
||||
if let Err(err) = cosmic_comp::run(Default::default()) {
|
||||
tracing::error!("Error occured in main(): {}", err);
|
||||
std::process::exit(1);
|
||||
|
|
|
|||
|
|
@ -254,6 +254,14 @@ impl CosmicMapped {
|
|||
.any(|(w, _)| w.has_surface(surface, surface_type))
|
||||
}
|
||||
|
||||
pub fn surface_offset(&self, surface: &WlSurface) -> Option<Point<i32, Logical>> {
|
||||
self.windows().find_map(|(window, window_offset)| {
|
||||
window
|
||||
.surface_offset(surface)
|
||||
.map(|offset| window_offset + offset)
|
||||
})
|
||||
}
|
||||
|
||||
/// Give the pointer target under a relative offset into this element.
|
||||
///
|
||||
/// Returns Target + Offset relative to the target
|
||||
|
|
@ -261,10 +269,13 @@ impl CosmicMapped {
|
|||
&self,
|
||||
relative_pos: Point<f64, Logical>,
|
||||
surface_type: WindowSurfaceType,
|
||||
seat: &Seat<State>,
|
||||
) -> Option<(PointerFocusTarget, Point<f64, Logical>)> {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(stack) => stack.focus_under(relative_pos, surface_type),
|
||||
CosmicMappedInternal::Window(window) => window.focus_under(relative_pos, surface_type),
|
||||
CosmicMappedInternal::Window(window) => {
|
||||
window.focus_under(relative_pos, surface_type, Some(seat))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,8 +61,8 @@ use smithay::{
|
|||
PointerTarget, RelativeMotionEvent,
|
||||
},
|
||||
touch::{
|
||||
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
|
||||
UpEvent,
|
||||
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
|
||||
TouchTarget, UpEvent,
|
||||
},
|
||||
},
|
||||
output::Output,
|
||||
|
|
@ -79,7 +79,7 @@ use std::{
|
|||
hash::Hash,
|
||||
sync::{
|
||||
Arc, LazyLock, Mutex,
|
||||
atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering},
|
||||
atomic::{AtomicBool, AtomicU8, AtomicU32, AtomicUsize, Ordering},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -114,6 +114,7 @@ pub struct CosmicStackInternal {
|
|||
scroll_to_focus: AtomicBool,
|
||||
previous_keyboard: AtomicUsize,
|
||||
pointer_entered: AtomicU8,
|
||||
touch_serial: AtomicU32,
|
||||
reenter: AtomicBool,
|
||||
potential_drag: Mutex<Option<usize>>,
|
||||
override_alive: AtomicBool,
|
||||
|
|
@ -171,6 +172,7 @@ impl CosmicStack {
|
|||
scroll_to_focus: AtomicBool::new(false),
|
||||
previous_keyboard: AtomicUsize::new(0),
|
||||
pointer_entered: AtomicU8::new(0),
|
||||
touch_serial: AtomicU32::new(0),
|
||||
reenter: AtomicBool::new(false),
|
||||
potential_drag: Mutex::new(None),
|
||||
override_alive: AtomicBool::new(true),
|
||||
|
|
@ -249,6 +251,10 @@ impl CosmicStack {
|
|||
window.set_tiled(false);
|
||||
|
||||
p.active.fetch_min(windows.len() - 1, Ordering::SeqCst);
|
||||
p.previous_index
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take_if(|(_, idx)| *idx >= windows.len());
|
||||
});
|
||||
self.0
|
||||
.resize(Size::from((self.active().geometry().size.w, TAB_HEIGHT)));
|
||||
|
|
@ -276,6 +282,10 @@ impl CosmicStack {
|
|||
window.set_tiled(false);
|
||||
|
||||
p.active.fetch_min(windows.len() - 1, Ordering::SeqCst);
|
||||
p.previous_index
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take_if(|(_, idx)| *idx >= windows.len());
|
||||
|
||||
Some(window)
|
||||
});
|
||||
|
|
@ -1354,8 +1364,8 @@ impl Decorations<CosmicStackInternal, Message> for DefaultDecorations {
|
|||
|
||||
iced_widget::container::Style {
|
||||
snap: true,
|
||||
icon_color: Some(cosmic_theme.background.on.into()),
|
||||
text_color: Some(cosmic_theme.background.on.into()),
|
||||
icon_color: Some(cosmic_theme.background(false).on.into()),
|
||||
text_color: Some(cosmic_theme.background(false).on.into()),
|
||||
background: Some(Background::Color(background.into())),
|
||||
border: Border {
|
||||
radius,
|
||||
|
|
@ -1807,55 +1817,62 @@ impl PointerTarget<State> for CosmicStack {
|
|||
}
|
||||
|
||||
impl TouchTarget<State> for CosmicStack {
|
||||
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent, seq: Serial) {
|
||||
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent) {
|
||||
let mut event = event.clone();
|
||||
let active_window_geo = self.0.with_program(|p| {
|
||||
p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry()
|
||||
});
|
||||
event.location -= active_window_geo.loc.to_f64();
|
||||
TouchTarget::down(&self.0, seat, data, &event, seq)
|
||||
self.0
|
||||
.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, seq: Serial) {
|
||||
TouchTarget::up(&self.0, seat, data, event, seq)
|
||||
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent) {
|
||||
TouchTarget::up(&self.0, seat, data, event)
|
||||
}
|
||||
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent, seq: Serial) {
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent) {
|
||||
let mut event = event.clone();
|
||||
let active_window_geo = self.0.with_program(|p| {
|
||||
p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry()
|
||||
});
|
||||
event.location -= active_window_geo.loc.to_f64();
|
||||
TouchTarget::motion(&self.0, seat, data, &event, seq);
|
||||
TouchTarget::motion(&self.0, seat, data, &event);
|
||||
|
||||
if event.location.y < 0.0
|
||||
|| event.location.y > TAB_HEIGHT as f64
|
||||
|| event.location.x < 64.0
|
||||
|| event.location.x > (active_window_geo.size.w as f64 - 64.0)
|
||||
{
|
||||
self.start_drag(data, seat, seq);
|
||||
self.start_drag(
|
||||
data,
|
||||
seat,
|
||||
self.0
|
||||
.with_program(|p| p.touch_serial.load(Ordering::Acquire))
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn frame(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
|
||||
TouchTarget::frame(&self.0, seat, data, seq)
|
||||
fn frame(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
|
||||
TouchTarget::frame(&self.0, seat, data, frame)
|
||||
}
|
||||
|
||||
fn cancel(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
|
||||
TouchTarget::cancel(&self.0, seat, data, seq)
|
||||
fn cancel(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
|
||||
TouchTarget::cancel(&self.0, seat, data, frame)
|
||||
}
|
||||
|
||||
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent, seq: Serial) {
|
||||
TouchTarget::shape(&self.0, seat, data, event, seq)
|
||||
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent) {
|
||||
TouchTarget::shape(&self.0, seat, data, event)
|
||||
}
|
||||
|
||||
fn orientation(
|
||||
&self,
|
||||
_seat: &Seat<State>,
|
||||
_data: &mut State,
|
||||
_event: &OrientationEvent,
|
||||
_seq: Serial,
|
||||
) {
|
||||
fn orientation(&self, seat: &Seat<State>, data: &mut State, event: &OrientationEvent) {
|
||||
TouchTarget::orientation(&self.0, seat, data, event)
|
||||
}
|
||||
|
||||
fn last_frame(&self, seat: &Seat<State>, data: &mut State) -> Option<FrameMarker> {
|
||||
TouchTarget::last_frame(&self.0, seat, data)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ impl From<TabBackgroundTheme> for theme::Container<'_> {
|
|||
icon_color: Some(Color::from(theme.cosmic().accent_text_color())),
|
||||
text_color: Some(Color::from(theme.cosmic().accent_text_color())),
|
||||
background: Some(Background::Color(
|
||||
theme.cosmic().primary.component.selected.into(),
|
||||
theme.cosmic().primary(false).component.selected.into(),
|
||||
)),
|
||||
border: Border {
|
||||
radius: 0.0.into(),
|
||||
|
|
@ -81,7 +81,7 @@ impl From<TabBackgroundTheme> for theme::Container<'_> {
|
|||
icon_color: None,
|
||||
text_color: None,
|
||||
background: Some(Background::Color(
|
||||
theme.cosmic().primary.component.base.into(),
|
||||
theme.cosmic().primary(false).component.base.into(),
|
||||
)),
|
||||
border: Border {
|
||||
radius: 0.0.into(),
|
||||
|
|
|
|||
|
|
@ -45,7 +45,10 @@ use smithay::{
|
|||
IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, user_data::UserDataMap,
|
||||
},
|
||||
wayland::{
|
||||
compositor::{SurfaceData, TraversalAction, with_states, with_surface_tree_downward},
|
||||
compositor::{
|
||||
SubsurfaceCachedState, SurfaceData, TraversalAction, get_parent, with_states,
|
||||
with_surface_tree_downward,
|
||||
},
|
||||
seat::WaylandFocus,
|
||||
shell::xdg::{
|
||||
SurfaceCachedState, ToplevelCachedState, ToplevelSurface, XdgToplevelSurfaceData,
|
||||
|
|
@ -235,7 +238,8 @@ impl CosmicSurface {
|
|||
toplevel.with_pending_state(|state| state.size = Some(geo.size.as_logical()))
|
||||
}
|
||||
WindowSurface::X11(surface) => {
|
||||
let _ = surface.configure_with_sync(geo.as_logical(), None);
|
||||
let _ =
|
||||
surface.configure_with_sync(geo.as_logical() + surface.frame_extents(), None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -342,7 +346,7 @@ impl CosmicSurface {
|
|||
state.is_some_and(|state| state.states.contains(ToplevelState::Resizing))
|
||||
}))
|
||||
}
|
||||
WindowSurface::X11(surface) => surface.pending_geometry().map(|_| true),
|
||||
WindowSurface::X11(surface) => surface.pending_configure().map(|_| true),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -588,7 +592,7 @@ impl CosmicSurface {
|
|||
}
|
||||
})
|
||||
}
|
||||
WindowSurface::X11(surface) => surface.pending_geometry().is_none(),
|
||||
WindowSurface::X11(surface) => surface.pending_configure().is_none(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -652,6 +656,54 @@ impl CosmicSurface {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn surface_offset(&self, surface: &WlSurface) -> Option<Point<i32, Logical>> {
|
||||
match self.0.underlying_surface() {
|
||||
WindowSurface::Wayland(toplevel) => {
|
||||
Self::surface_tree_offset(toplevel.wl_surface(), surface)
|
||||
}
|
||||
WindowSurface::X11(surface_x11) => {
|
||||
if surface_x11.wl_surface().as_ref() == Some(surface) {
|
||||
Some(Point::default())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn surface_tree_offset(
|
||||
root: &WlSurface,
|
||||
surface: &WlSurface,
|
||||
) -> Option<Point<i32, Logical>> {
|
||||
let mut offset = Point::<i32, Logical>::default();
|
||||
let mut parent = surface.clone();
|
||||
loop {
|
||||
if parent == *root {
|
||||
return Some(offset);
|
||||
} else if let Some(s) = get_parent(&parent) {
|
||||
offset += with_states(&parent, |states| {
|
||||
states
|
||||
.cached_state
|
||||
.get::<SubsurfaceCachedState>()
|
||||
.current()
|
||||
.location
|
||||
});
|
||||
parent = s;
|
||||
} else {
|
||||
// `parent` is now root of subsurface tree; `surface` is not a subsurface child of `root`
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (popup, popup_offset) in PopupManager::popups_for_surface(root) {
|
||||
if let Some(offset) = Self::surface_tree_offset(popup.wl_surface(), surface) {
|
||||
return Some(popup_offset + offset);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn focus_under(
|
||||
&self,
|
||||
relative_pos: Point<f64, Logical>,
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ use smithay::{
|
|||
GestureSwipeUpdateEvent, MotionEvent, PointerTarget, RelativeMotionEvent,
|
||||
},
|
||||
touch::{
|
||||
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
|
||||
UpEvent,
|
||||
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
|
||||
TouchTarget, UpEvent,
|
||||
},
|
||||
},
|
||||
output::Output,
|
||||
|
|
@ -57,7 +57,7 @@ use smithay::{
|
|||
Buffer, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform,
|
||||
user_data::UserDataMap,
|
||||
},
|
||||
wayland::seat::WaylandFocus,
|
||||
wayland::{pointer_constraints::with_pointer_constraint, seat::WaylandFocus},
|
||||
};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
|
|
@ -274,12 +274,25 @@ impl CosmicWindow {
|
|||
&self,
|
||||
mut relative_pos: Point<f64, Logical>,
|
||||
surface_type: WindowSurfaceType,
|
||||
seat: Option<&Seat<State>>,
|
||||
) -> Option<(PointerFocusTarget, Point<f64, Logical>)> {
|
||||
let has_constraint = if let Some(seat) = seat
|
||||
&& let Some(pointer) = seat.get_pointer()
|
||||
&& let Some(surface) = self.wl_surface()
|
||||
&& with_pointer_constraint(&surface, &pointer, |constraint| {
|
||||
constraint.is_some_and(|c| c.is_active())
|
||||
}) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
self.0.with_program(|p| {
|
||||
let mut offset = Point::from((0., 0.));
|
||||
let mut window_ui = None;
|
||||
let has_ssd = p.has_ssd(false);
|
||||
if (has_ssd || p.has_tiled_state())
|
||||
|
||||
if (!has_constraint && (has_ssd || p.has_tiled_state()))
|
||||
&& surface_type.contains(WindowSurfaceType::TOPLEVEL)
|
||||
{
|
||||
let geo = p.window.geometry();
|
||||
|
|
@ -813,7 +826,7 @@ impl Program for CosmicWindowInternal {
|
|||
|
||||
fn background_color(&self, theme: &cosmic::Theme) -> Color {
|
||||
if self.window.is_maximized(false) {
|
||||
theme.cosmic().background.base.into()
|
||||
theme.cosmic().background(false).base.into()
|
||||
} else {
|
||||
Color::TRANSPARENT
|
||||
}
|
||||
|
|
@ -877,7 +890,8 @@ impl SpaceElement for CosmicWindow {
|
|||
})
|
||||
}
|
||||
fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
|
||||
self.focus_under(*point, WindowSurfaceType::ALL).is_some()
|
||||
self.focus_under(*point, WindowSurfaceType::ALL, None)
|
||||
.is_some()
|
||||
}
|
||||
fn set_activate(&self, activated: bool) {
|
||||
if self
|
||||
|
|
@ -1176,43 +1190,42 @@ impl PointerTarget<State> for CosmicWindow {
|
|||
}
|
||||
|
||||
impl TouchTarget<State> for CosmicWindow {
|
||||
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent, seq: Serial) {
|
||||
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent) {
|
||||
let mut event = event.clone();
|
||||
self.0.with_program(|p| {
|
||||
event.location -= p.window.geometry().loc.to_f64();
|
||||
});
|
||||
TouchTarget::down(&self.0, seat, data, &event, seq)
|
||||
TouchTarget::down(&self.0, seat, data, &event)
|
||||
}
|
||||
|
||||
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent, seq: Serial) {
|
||||
TouchTarget::up(&self.0, seat, data, event, seq)
|
||||
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent) {
|
||||
TouchTarget::up(&self.0, seat, data, event)
|
||||
}
|
||||
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent, seq: Serial) {
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent) {
|
||||
let mut event = event.clone();
|
||||
event.location -= self.0.with_program(|p| p.window.geometry().loc.to_f64());
|
||||
TouchTarget::motion(&self.0, seat, data, &event, seq)
|
||||
TouchTarget::motion(&self.0, seat, data, &event)
|
||||
}
|
||||
|
||||
fn frame(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
|
||||
TouchTarget::frame(&self.0, seat, data, seq)
|
||||
fn frame(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
|
||||
TouchTarget::frame(&self.0, seat, data, frame)
|
||||
}
|
||||
|
||||
fn cancel(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
|
||||
TouchTarget::cancel(&self.0, seat, data, seq)
|
||||
fn cancel(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
|
||||
TouchTarget::cancel(&self.0, seat, data, frame)
|
||||
}
|
||||
|
||||
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent, seq: Serial) {
|
||||
TouchTarget::shape(&self.0, seat, data, event, seq)
|
||||
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent) {
|
||||
TouchTarget::shape(&self.0, seat, data, event)
|
||||
}
|
||||
|
||||
fn orientation(
|
||||
&self,
|
||||
_seat: &Seat<State>,
|
||||
_data: &mut State,
|
||||
_event: &OrientationEvent,
|
||||
_seq: Serial,
|
||||
) {
|
||||
fn orientation(&self, seat: &Seat<State>, data: &mut State, event: &OrientationEvent) {
|
||||
TouchTarget::orientation(&self.0, seat, data, event)
|
||||
}
|
||||
|
||||
fn last_frame(&self, seat: &Seat<State>, data: &mut State) -> Option<FrameMarker> {
|
||||
TouchTarget::last_frame(&self.0, seat, data)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
shell::{CosmicSurface, MinimizedWindow, Shell, Trigger, element::CosmicMapped},
|
||||
state::Common,
|
||||
state::{Common, State},
|
||||
utils::prelude::*,
|
||||
wayland::handlers::{xdg_shell::PopupGrabData, xwayland_keyboard_grab::XWaylandGrabSeatData},
|
||||
};
|
||||
|
|
@ -12,6 +12,7 @@ use smithay::{
|
|||
reexports::wayland_server::{Resource, protocol::wl_surface::WlSurface},
|
||||
utils::{IsAlive, Point, SERIAL_COUNTER, Serial},
|
||||
wayland::{
|
||||
pointer_constraints::with_pointer_constraint,
|
||||
seat::WaylandFocus,
|
||||
selection::{data_device::set_data_device_focus, primary_selection::set_primary_focus},
|
||||
shell::wlr_layer::{KeyboardInteractivity, Layer},
|
||||
|
|
@ -287,22 +288,18 @@ impl Shell {
|
|||
}
|
||||
|
||||
let workspace = &mut set.workspaces[set.active];
|
||||
if let Some(fullscreen) = workspace.get_fullscreen() {
|
||||
if self.seats.iter().any(|seat| {
|
||||
for fs in workspace.get_fullscreen_surfaces() {
|
||||
let is_focused = self.seats.iter().any(|seat| {
|
||||
if let Some(KeyboardFocusTarget::Fullscreen(s)) =
|
||||
seat.get_keyboard().unwrap().current_focus()
|
||||
{
|
||||
&s == fullscreen
|
||||
s == fs.surface
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
fullscreen.set_activated(true);
|
||||
fullscreen.send_configure();
|
||||
} else {
|
||||
fullscreen.set_activated(false);
|
||||
fullscreen.send_configure();
|
||||
}
|
||||
});
|
||||
fs.surface.set_activated(is_focused);
|
||||
fs.surface.send_configure();
|
||||
}
|
||||
for focused in focused_windows.iter() {
|
||||
raise_with_children(&mut workspace.floating_layer, focused);
|
||||
|
|
@ -347,6 +344,20 @@ fn update_focus_state(
|
|||
) {
|
||||
// update keyboard focus
|
||||
if let Some(keyboard) = seat.get_keyboard() {
|
||||
// remove constraint when target changed
|
||||
let old_focus = keyboard.current_focus();
|
||||
if let Some(old_target) = old_focus
|
||||
&& target != Some(&old_target)
|
||||
&& let Some(surface) = old_target.wl_surface()
|
||||
&& let Some(pointer) = seat.get_pointer()
|
||||
{
|
||||
with_pointer_constraint(&surface, &pointer, |constraint| {
|
||||
if let Some(constraint) = constraint {
|
||||
constraint.deactivate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if should_update_cursor
|
||||
&& state.common.config.cosmic_conf.cursor_follows_focus
|
||||
&& target.is_some()
|
||||
|
|
@ -646,7 +657,9 @@ fn focus_target_is_valid(
|
|||
.has_node(&node),
|
||||
KeyboardFocusTarget::Fullscreen(window) => {
|
||||
let workspace = shell.active_space(output).unwrap();
|
||||
workspace.get_fullscreen().is_some_and(|w| w == &window)
|
||||
workspace
|
||||
.get_fullscreen_surfaces()
|
||||
.any(|f| f.surface == window)
|
||||
}
|
||||
KeyboardFocusTarget::Popup(_) => true,
|
||||
KeyboardFocusTarget::LockSurface(_) => false,
|
||||
|
|
@ -699,9 +712,9 @@ fn update_focus_target(
|
|||
.map(KeyboardFocusTarget::Element)
|
||||
.or_else(|| {
|
||||
workspace
|
||||
.get_fullscreen()
|
||||
.get_fullscreen(seat)
|
||||
.cloned()
|
||||
.map(KeyboardFocusTarget::Fullscreen)
|
||||
.map(|fs| KeyboardFocusTarget::Fullscreen(fs.surface))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,8 +112,8 @@ fn render_input_order_internal<R: 'static>(
|
|||
let output_size = output.geometry().size;
|
||||
|
||||
// this is more hacky than I would like..
|
||||
let fullscreen = workspace.fullscreen.as_ref().filter(|f| !f.is_animating());
|
||||
let seat = shell.seats.last_active();
|
||||
let fullscreen = workspace.get_fullscreen(seat);
|
||||
let is_active_workspace = seat.focused_output().is_some_and(|output| {
|
||||
shell
|
||||
.active_space(&output)
|
||||
|
|
@ -144,7 +144,7 @@ fn render_input_order_internal<R: 'static>(
|
|||
let Some(workspace) = shell.workspaces.space_for_handle(previous) else {
|
||||
return ControlFlow::Break(Err(OutputNoMode));
|
||||
};
|
||||
let has_fullscreen = workspace.fullscreen.is_some();
|
||||
let has_fullscreen = workspace.get_fullscreen(seat).is_some();
|
||||
|
||||
let (forward, percentage) = match start {
|
||||
WorkspaceDelta::Shortcut(st) => (
|
||||
|
|
@ -220,20 +220,18 @@ fn render_input_order_internal<R: 'static>(
|
|||
.rev()
|
||||
.filter(|or| {
|
||||
(*or)
|
||||
.geometry()
|
||||
.last_configure()
|
||||
.as_global()
|
||||
.intersection(output.geometry())
|
||||
.is_some()
|
||||
})
|
||||
.map(|or| (or, or.geometry().loc.as_global()))
|
||||
.map(|or| (or, or.last_configure().loc.as_global()))
|
||||
{
|
||||
callback(Stage::OverrideRedirect { surface, location })?;
|
||||
}
|
||||
|
||||
// sticky window popups
|
||||
if !has_focused_fullscreen {
|
||||
callback(Stage::StickyPopups(&set.sticky_layer))?;
|
||||
}
|
||||
callback(Stage::StickyPopups(&set.sticky_layer))?;
|
||||
}
|
||||
|
||||
if element_filter != ElementFilter::LayerShellOnly {
|
||||
|
|
@ -313,11 +311,11 @@ fn render_input_order_internal<R: 'static>(
|
|||
for (layer, location) in layer_surfaces(output, Layer::Top, element_filter) {
|
||||
callback(Stage::LayerSurface { layer, location })?;
|
||||
}
|
||||
}
|
||||
|
||||
// sticky windows
|
||||
if element_filter != ElementFilter::LayerShellOnly {
|
||||
callback(Stage::Sticky(&set.sticky_layer))?;
|
||||
}
|
||||
// sticky windows
|
||||
if element_filter != ElementFilter::LayerShellOnly {
|
||||
callback(Stage::Sticky(&set.sticky_layer))?;
|
||||
}
|
||||
|
||||
if element_filter != ElementFilter::LayerShellOnly {
|
||||
|
|
|
|||
|
|
@ -29,15 +29,18 @@ use smithay::{
|
|||
MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent,
|
||||
},
|
||||
touch::{
|
||||
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
|
||||
UpEvent,
|
||||
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
|
||||
TouchTarget, UpEvent,
|
||||
},
|
||||
},
|
||||
reexports::wayland_server::{
|
||||
Client, DisplayHandle, Resource, backend::ObjectId, protocol::wl_surface::WlSurface,
|
||||
},
|
||||
utils::{IsAlive, Logical, Point, Serial, Transform},
|
||||
wayland::{seat::WaylandFocus, selection::data_device::WlOfferData, session_lock::LockSurface},
|
||||
wayland::{
|
||||
compositor::with_states, seat::WaylandFocus, selection::data_device::WlOfferData,
|
||||
session_lock::LockSurface, shell::xdg::SurfaceCachedState,
|
||||
},
|
||||
xwayland::{
|
||||
X11Surface,
|
||||
xwm::{XwmId, XwmOfferData},
|
||||
|
|
@ -225,6 +228,63 @@ impl PointerFocusTarget {
|
|||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// Update image copy cursor position/hotspot for enter/motion event
|
||||
fn update_image_copy_cursor_position(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
event: &PointerMotionEvent,
|
||||
) {
|
||||
let Some(toplevel) = self.toplevel(&data.common.shell.read()) else {
|
||||
return;
|
||||
};
|
||||
let cursor_sessions = toplevel.cursor_sessions();
|
||||
if cursor_sessions.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let cursor_pos = if let Some(wl_surface) = self.wl_surface() {
|
||||
let surface_offset = toplevel
|
||||
.surface_offset(&wl_surface)
|
||||
.unwrap_or(Point::from((0, 0)))
|
||||
.to_f64();
|
||||
let geometry_loc = toplevel
|
||||
.wl_surface()
|
||||
.and_then(|s| {
|
||||
with_states(&s, |states| {
|
||||
states
|
||||
.cached_state
|
||||
.get::<SurfaceCachedState>()
|
||||
.current()
|
||||
.geometry
|
||||
.map(|g| g.loc.to_f64())
|
||||
})
|
||||
})
|
||||
.unwrap_or_default();
|
||||
Some(
|
||||
(event.location - geometry_loc + surface_offset)
|
||||
.to_buffer(1.0, Transform::Normal, &toplevel.geometry().size.to_f64())
|
||||
.to_i32_round(),
|
||||
)
|
||||
} else {
|
||||
// If cursor is in SSD, instead of a `wl_surface`, it is outside the captured bounds
|
||||
None
|
||||
};
|
||||
|
||||
let cursor_hotspot = if let Some((_, hotspot)) =
|
||||
seat.cursor_geometry((0.0, 0.0), Duration::from_millis(event.time as u64).into())
|
||||
{
|
||||
hotspot
|
||||
} else {
|
||||
Point::from((0, 0))
|
||||
};
|
||||
|
||||
for session in cursor_sessions {
|
||||
session.set_cursor_pos(cursor_pos);
|
||||
session.set_cursor_hotspot(cursor_hotspot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardFocusTarget {
|
||||
|
|
@ -281,6 +341,18 @@ impl KeyboardFocusTarget {
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_surface(&self, shell: &Shell, surface: &WlSurface) -> bool {
|
||||
if let Some(fe) = shell.focused_element(self) {
|
||||
fe.has_surface(surface, WindowSurfaceType::ALL)
|
||||
} else if let KeyboardFocusTarget::Fullscreen(s) = self {
|
||||
s.has_surface(surface, WindowSurfaceType::ALL)
|
||||
} else if let Some(root) = WaylandFocus::wl_surface(self) {
|
||||
CosmicSurface::surface_tree_offset(&root, surface).is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -325,47 +397,11 @@ impl IsAlive for KeyboardFocusTarget {
|
|||
|
||||
impl PointerTarget<State> for PointerFocusTarget {
|
||||
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &PointerMotionEvent) {
|
||||
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.update_image_copy_cursor_position(seat, data, event);
|
||||
self.inner_pointer_target().enter(seat, data, event);
|
||||
}
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &PointerMotionEvent) {
|
||||
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.update_image_copy_cursor_position(seat, data, event);
|
||||
self.inner_pointer_target().motion(seat, data, event);
|
||||
}
|
||||
fn relative_motion(&self, seat: &Seat<State>, data: &mut State, event: &RelativeMotionEvent) {
|
||||
|
|
@ -462,39 +498,36 @@ impl PointerTarget<State> for PointerFocusTarget {
|
|||
}
|
||||
|
||||
impl TouchTarget<State> for PointerFocusTarget {
|
||||
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent, seq: Serial) {
|
||||
self.inner_touch_target().down(seat, data, event, seq);
|
||||
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent) {
|
||||
self.inner_touch_target().down(seat, data, event);
|
||||
}
|
||||
|
||||
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent, seq: Serial) {
|
||||
self.inner_touch_target().up(seat, data, event, seq);
|
||||
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent) {
|
||||
self.inner_touch_target().up(seat, data, event);
|
||||
}
|
||||
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent, seq: Serial) {
|
||||
self.inner_touch_target().motion(seat, data, event, seq);
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent) {
|
||||
self.inner_touch_target().motion(seat, data, event);
|
||||
}
|
||||
|
||||
fn frame(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
|
||||
self.inner_touch_target().frame(seat, data, seq);
|
||||
fn frame(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
|
||||
self.inner_touch_target().frame(seat, data, frame);
|
||||
}
|
||||
|
||||
fn cancel(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
|
||||
self.inner_touch_target().cancel(seat, data, seq);
|
||||
fn cancel(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
|
||||
self.inner_touch_target().cancel(seat, data, frame);
|
||||
}
|
||||
|
||||
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent, seq: Serial) {
|
||||
self.inner_touch_target().shape(seat, data, event, seq);
|
||||
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent) {
|
||||
self.inner_touch_target().shape(seat, data, event);
|
||||
}
|
||||
|
||||
fn orientation(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
event: &OrientationEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
self.inner_touch_target()
|
||||
.orientation(seat, data, event, seq);
|
||||
fn orientation(&self, seat: &Seat<State>, data: &mut State, event: &OrientationEvent) {
|
||||
self.inner_touch_target().orientation(seat, data, event);
|
||||
}
|
||||
|
||||
fn last_frame(&self, seat: &Seat<State>, data: &mut State) -> Option<FrameMarker> {
|
||||
self.inner_touch_target().last_frame(seat, data)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -202,19 +202,12 @@ impl<G: TouchGrab<State>> TouchGrab<State> for DelayGrab<G> {
|
|||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
|
||||
event: &DownEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.down(data, focus, event, seq);
|
||||
handle.down(data, focus, event);
|
||||
}
|
||||
|
||||
fn up(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &UpEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.up(data, event, seq);
|
||||
fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) {
|
||||
handle.up(data, event);
|
||||
|
||||
if event.slot == TouchGrab::start_data(self).slot {
|
||||
handle.unset_grab(self, data);
|
||||
|
|
@ -227,9 +220,8 @@ impl<G: TouchGrab<State>> TouchGrab<State> for DelayGrab<G> {
|
|||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,
|
||||
event: &TouchMotionEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.motion(data, focus, event, seq);
|
||||
handle.motion(data, focus, event);
|
||||
|
||||
let distance = self.start_data.distance(event.location);
|
||||
if distance >= 1.
|
||||
|
|
@ -245,12 +237,12 @@ impl<G: TouchGrab<State>> TouchGrab<State> for DelayGrab<G> {
|
|||
}
|
||||
}
|
||||
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||
handle.frame(data, seq)
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
|
||||
handle.frame(data)
|
||||
}
|
||||
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||
handle.cancel(data, seq);
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
|
||||
handle.cancel(data);
|
||||
handle.unset_grab(self, data);
|
||||
}
|
||||
|
||||
|
|
@ -259,9 +251,8 @@ impl<G: TouchGrab<State>> TouchGrab<State> for DelayGrab<G> {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &ShapeEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.shape(data, event, seq)
|
||||
handle.shape(data, event)
|
||||
}
|
||||
|
||||
fn orientation(
|
||||
|
|
@ -269,9 +260,8 @@ impl<G: TouchGrab<State>> TouchGrab<State> for DelayGrab<G> {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &OrientationEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.orientation(data, event, seq);
|
||||
handle.orientation(data, event);
|
||||
}
|
||||
|
||||
fn start_data(&self) -> &TouchGrabStartData<State> {
|
||||
|
|
|
|||
|
|
@ -428,10 +428,11 @@ impl Program for ContextMenu {
|
|||
.width(mode)
|
||||
.class(if *disabled {
|
||||
theme::Text::Custom(|theme| {
|
||||
let mut color = theme.cosmic().background.component.on;
|
||||
let mut color = theme.cosmic().background(false).component.on;
|
||||
color.alpha *= 0.5;
|
||||
TextStyle {
|
||||
color: Some(color.into()),
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
|
|
@ -446,10 +447,11 @@ impl Program for ContextMenu {
|
|||
.align_x(Horizontal::Right)
|
||||
.width(Length::Shrink)
|
||||
.class(theme::Text::Custom(|theme| {
|
||||
let mut color = theme.cosmic().background.component.on;
|
||||
let mut color = theme.cosmic().background(false).component.on;
|
||||
color.alpha *= 0.75;
|
||||
TextStyle {
|
||||
color: Some(color.into()),
|
||||
..Default::default()
|
||||
}
|
||||
}))
|
||||
.into(),
|
||||
|
|
@ -474,7 +476,7 @@ impl Program for ContextMenu {
|
|||
.padding(1)
|
||||
.class(theme::Container::custom(|theme| {
|
||||
let cosmic = theme.cosmic();
|
||||
let component = &cosmic.background.component;
|
||||
let component = &cosmic.background(false).component;
|
||||
iced_widget::container::Style {
|
||||
snap: true,
|
||||
icon_color: Some(cosmic.accent.base.into()),
|
||||
|
|
@ -718,7 +720,6 @@ impl TouchGrab<State> for MenuGrab {
|
|||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &DownEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
{
|
||||
let mut guard = self.elements.lock().unwrap();
|
||||
|
|
@ -753,21 +754,15 @@ impl TouchGrab<State> for MenuGrab {
|
|||
time: event.time,
|
||||
};
|
||||
if element.touch_entered.is_none() {
|
||||
TouchTarget::down(&element.iced, &self.seat, data, &new_event, seq);
|
||||
TouchTarget::down(&element.iced, &self.seat, data, &new_event);
|
||||
element.touch_entered = Some(event.slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
handle.down(data, None, event, seq);
|
||||
handle.down(data, None, event);
|
||||
}
|
||||
|
||||
fn up(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &UpEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) {
|
||||
{
|
||||
let elements = self.elements.lock().unwrap();
|
||||
for element in elements.iter().filter(|elem| {
|
||||
|
|
@ -775,7 +770,7 @@ impl TouchGrab<State> for MenuGrab {
|
|||
.as_ref()
|
||||
.is_some_and(|slot| *slot == event.slot)
|
||||
}) {
|
||||
TouchTarget::up(&element.iced, &self.seat, data, event, seq);
|
||||
TouchTarget::up(&element.iced, &self.seat, data, event);
|
||||
}
|
||||
}
|
||||
handle.unset_grab(self, data);
|
||||
|
|
@ -787,7 +782,6 @@ impl TouchGrab<State> for MenuGrab {
|
|||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &TouchMotionEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
{
|
||||
let elements = self.elements.lock().unwrap();
|
||||
|
|
@ -796,24 +790,24 @@ impl TouchGrab<State> for MenuGrab {
|
|||
.as_ref()
|
||||
.is_some_and(|slot| *slot == event.slot)
|
||||
}) {
|
||||
TouchTarget::motion(&element.iced, &self.seat, data, event, seq);
|
||||
TouchTarget::motion(&element.iced, &self.seat, data, event);
|
||||
}
|
||||
}
|
||||
handle.motion(data, None, event, seq);
|
||||
handle.motion(data, None, event);
|
||||
}
|
||||
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||
handle.frame(data, seq);
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
|
||||
handle.frame(data);
|
||||
}
|
||||
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
|
||||
{
|
||||
let mut elements = self.elements.lock().unwrap();
|
||||
for element in elements.iter_mut() {
|
||||
let _ = element.touch_entered.take();
|
||||
}
|
||||
}
|
||||
handle.cancel(data, seq);
|
||||
handle.cancel(data);
|
||||
}
|
||||
|
||||
fn shape(
|
||||
|
|
@ -821,9 +815,8 @@ impl TouchGrab<State> for MenuGrab {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &smithay::input::touch::ShapeEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.shape(data, event, seq);
|
||||
handle.shape(data, event);
|
||||
}
|
||||
|
||||
fn orientation(
|
||||
|
|
@ -831,9 +824,8 @@ impl TouchGrab<State> for MenuGrab {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &smithay::input::touch::OrientationEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.orientation(data, event, seq);
|
||||
handle.orientation(data, event);
|
||||
}
|
||||
|
||||
fn start_data(&self) -> &TouchGrabStartData<State> {
|
||||
|
|
|
|||
|
|
@ -387,24 +387,17 @@ impl TouchGrab<State> for ResizeGrab {
|
|||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &DownEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
match self {
|
||||
ResizeGrab::Floating(grab) => TouchGrab::down(grab, data, handle, focus, event, seq),
|
||||
ResizeGrab::Tiling(grab) => TouchGrab::down(grab, data, handle, focus, event, seq),
|
||||
ResizeGrab::Floating(grab) => TouchGrab::down(grab, data, handle, focus, event),
|
||||
ResizeGrab::Tiling(grab) => TouchGrab::down(grab, data, handle, focus, event),
|
||||
}
|
||||
}
|
||||
|
||||
fn up(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &UpEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) {
|
||||
match self {
|
||||
ResizeGrab::Floating(grab) => TouchGrab::up(grab, data, handle, event, seq),
|
||||
ResizeGrab::Tiling(grab) => TouchGrab::up(grab, data, handle, event, seq),
|
||||
ResizeGrab::Floating(grab) => TouchGrab::up(grab, data, handle, event),
|
||||
ResizeGrab::Tiling(grab) => TouchGrab::up(grab, data, handle, event),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -414,25 +407,24 @@ impl TouchGrab<State> for ResizeGrab {
|
|||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &TouchMotionEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
match self {
|
||||
ResizeGrab::Floating(grab) => TouchGrab::motion(grab, data, handle, focus, event, seq),
|
||||
ResizeGrab::Tiling(grab) => TouchGrab::motion(grab, data, handle, focus, event, seq),
|
||||
ResizeGrab::Floating(grab) => TouchGrab::motion(grab, data, handle, focus, event),
|
||||
ResizeGrab::Tiling(grab) => TouchGrab::motion(grab, data, handle, focus, event),
|
||||
}
|
||||
}
|
||||
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
|
||||
match self {
|
||||
ResizeGrab::Floating(grab) => TouchGrab::frame(grab, data, handle, seq),
|
||||
ResizeGrab::Tiling(grab) => TouchGrab::frame(grab, data, handle, seq),
|
||||
ResizeGrab::Floating(grab) => TouchGrab::frame(grab, data, handle),
|
||||
ResizeGrab::Tiling(grab) => TouchGrab::frame(grab, data, handle),
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
|
||||
match self {
|
||||
ResizeGrab::Floating(grab) => TouchGrab::cancel(grab, data, handle, seq),
|
||||
ResizeGrab::Tiling(grab) => TouchGrab::cancel(grab, data, handle, seq),
|
||||
ResizeGrab::Floating(grab) => TouchGrab::cancel(grab, data, handle),
|
||||
ResizeGrab::Tiling(grab) => TouchGrab::cancel(grab, data, handle),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -441,11 +433,10 @@ impl TouchGrab<State> for ResizeGrab {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &ShapeEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
match self {
|
||||
ResizeGrab::Floating(grab) => TouchGrab::shape(grab, data, handle, event, seq),
|
||||
ResizeGrab::Tiling(grab) => TouchGrab::shape(grab, data, handle, event, seq),
|
||||
ResizeGrab::Floating(grab) => TouchGrab::shape(grab, data, handle, event),
|
||||
ResizeGrab::Tiling(grab) => TouchGrab::shape(grab, data, handle, event),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -454,11 +445,10 @@ impl TouchGrab<State> for ResizeGrab {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &OrientationEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
match self {
|
||||
ResizeGrab::Floating(grab) => TouchGrab::orientation(grab, data, handle, event, seq),
|
||||
ResizeGrab::Tiling(grab) => TouchGrab::orientation(grab, data, handle, event, seq),
|
||||
ResizeGrab::Floating(grab) => TouchGrab::orientation(grab, data, handle, event),
|
||||
ResizeGrab::Tiling(grab) => TouchGrab::orientation(grab, data, handle, event),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -729,24 +719,17 @@ impl TouchGrab<State> for MoveGrab {
|
|||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &DownEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
match self {
|
||||
MoveGrab::Move(grab) => TouchGrab::down(grab, data, handle, focus, event, seq),
|
||||
MoveGrab::Delayed(grab) => TouchGrab::down(grab, data, handle, focus, event, seq),
|
||||
MoveGrab::Move(grab) => TouchGrab::down(grab, data, handle, focus, event),
|
||||
MoveGrab::Delayed(grab) => TouchGrab::down(grab, data, handle, focus, event),
|
||||
}
|
||||
}
|
||||
|
||||
fn up(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &UpEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) {
|
||||
match self {
|
||||
MoveGrab::Move(grab) => TouchGrab::up(grab, data, handle, event, seq),
|
||||
MoveGrab::Delayed(grab) => TouchGrab::up(grab, data, handle, event, seq),
|
||||
MoveGrab::Move(grab) => TouchGrab::up(grab, data, handle, event),
|
||||
MoveGrab::Delayed(grab) => TouchGrab::up(grab, data, handle, event),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -756,25 +739,24 @@ impl TouchGrab<State> for MoveGrab {
|
|||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &TouchMotionEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
match self {
|
||||
MoveGrab::Move(grab) => TouchGrab::motion(grab, data, handle, focus, event, seq),
|
||||
MoveGrab::Delayed(grab) => TouchGrab::motion(grab, data, handle, focus, event, seq),
|
||||
MoveGrab::Move(grab) => TouchGrab::motion(grab, data, handle, focus, event),
|
||||
MoveGrab::Delayed(grab) => TouchGrab::motion(grab, data, handle, focus, event),
|
||||
}
|
||||
}
|
||||
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
|
||||
match self {
|
||||
MoveGrab::Move(grab) => TouchGrab::frame(grab, data, handle, seq),
|
||||
MoveGrab::Delayed(grab) => TouchGrab::frame(grab, data, handle, seq),
|
||||
MoveGrab::Move(grab) => TouchGrab::frame(grab, data, handle),
|
||||
MoveGrab::Delayed(grab) => TouchGrab::frame(grab, data, handle),
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
|
||||
match self {
|
||||
MoveGrab::Move(grab) => TouchGrab::cancel(grab, data, handle, seq),
|
||||
MoveGrab::Delayed(grab) => TouchGrab::cancel(grab, data, handle, seq),
|
||||
MoveGrab::Move(grab) => TouchGrab::cancel(grab, data, handle),
|
||||
MoveGrab::Delayed(grab) => TouchGrab::cancel(grab, data, handle),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -783,11 +765,10 @@ impl TouchGrab<State> for MoveGrab {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &ShapeEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
match self {
|
||||
MoveGrab::Move(grab) => TouchGrab::shape(grab, data, handle, event, seq),
|
||||
MoveGrab::Delayed(grab) => TouchGrab::shape(grab, data, handle, event, seq),
|
||||
MoveGrab::Move(grab) => TouchGrab::shape(grab, data, handle, event),
|
||||
MoveGrab::Delayed(grab) => TouchGrab::shape(grab, data, handle, event),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -796,11 +777,10 @@ impl TouchGrab<State> for MoveGrab {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &OrientationEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
match self {
|
||||
MoveGrab::Move(grab) => TouchGrab::orientation(grab, data, handle, event, seq),
|
||||
MoveGrab::Delayed(grab) => TouchGrab::orientation(grab, data, handle, event, seq),
|
||||
MoveGrab::Move(grab) => TouchGrab::orientation(grab, data, handle, event),
|
||||
MoveGrab::Delayed(grab) => TouchGrab::orientation(grab, data, handle, event),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ use smithay::{
|
|||
touch::{self, GrabStartData as TouchGrabStartData, TouchGrab, TouchInnerHandle},
|
||||
},
|
||||
output::Output,
|
||||
utils::{IsAlive, Logical, Point, Rectangle, SERIAL_COUNTER, Scale, Serial},
|
||||
utils::{IsAlive, Logical, Point, Rectangle, SERIAL_COUNTER, Scale},
|
||||
};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
|
|
@ -652,9 +652,8 @@ impl TouchGrab<State> for MoveGrab {
|
|||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &touch::DownEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.down(data, None, event, seq)
|
||||
handle.down(data, None, event)
|
||||
}
|
||||
|
||||
fn up(
|
||||
|
|
@ -662,13 +661,12 @@ impl TouchGrab<State> for MoveGrab {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &touch::UpEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
|
||||
handle.unset_grab(self, data);
|
||||
}
|
||||
|
||||
handle.up(data, event, seq);
|
||||
handle.up(data, event);
|
||||
}
|
||||
|
||||
fn motion(
|
||||
|
|
@ -677,20 +675,19 @@ impl TouchGrab<State> for MoveGrab {
|
|||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &touch::MotionEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
|
||||
self.update_location(data, event.location);
|
||||
}
|
||||
|
||||
handle.motion(data, None, event, seq);
|
||||
handle.motion(data, None, event);
|
||||
}
|
||||
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||
handle.frame(data, seq)
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
|
||||
handle.frame(data)
|
||||
}
|
||||
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _seq: Serial) {
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
|
||||
handle.unset_grab(self, data);
|
||||
}
|
||||
|
||||
|
|
@ -699,9 +696,8 @@ impl TouchGrab<State> for MoveGrab {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &touch::ShapeEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.shape(data, event, seq)
|
||||
handle.shape(data, event)
|
||||
}
|
||||
|
||||
fn orientation(
|
||||
|
|
@ -709,9 +705,8 @@ impl TouchGrab<State> for MoveGrab {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &touch::OrientationEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.orientation(data, event, seq)
|
||||
handle.orientation(data, event)
|
||||
}
|
||||
|
||||
fn start_data(&self) -> &TouchGrabStartData<State> {
|
||||
|
|
@ -953,6 +948,7 @@ impl Drop for MoveGrab {
|
|||
if let Some((target, offset)) = mapped.focus_under(
|
||||
current_location - position.as_logical().to_f64(),
|
||||
WindowSurfaceType::ALL,
|
||||
&seat,
|
||||
) {
|
||||
pointer.motion(
|
||||
state,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use smithay::{
|
|||
},
|
||||
},
|
||||
output::Output,
|
||||
utils::{IsAlive, Logical, Point, Rectangle, Serial, Size},
|
||||
utils::{IsAlive, Logical, Point, Rectangle, Size},
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
|
|
@ -330,23 +330,16 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
|
|||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &DownEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.down(data, None, event, seq)
|
||||
handle.down(data, None, event)
|
||||
}
|
||||
|
||||
fn up(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &UpEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) {
|
||||
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
|
||||
handle.unset_grab(self, data);
|
||||
}
|
||||
|
||||
handle.up(data, event, seq);
|
||||
handle.up(data, event);
|
||||
}
|
||||
|
||||
fn motion(
|
||||
|
|
@ -355,7 +348,6 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
|
|||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &TouchMotionEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot
|
||||
&& self.update_location(event.location.as_global())
|
||||
|
|
@ -363,14 +355,14 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
|
|||
handle.unset_grab(self, data);
|
||||
}
|
||||
|
||||
handle.motion(data, None, event, seq);
|
||||
handle.motion(data, None, event);
|
||||
}
|
||||
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||
handle.frame(data, seq)
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
|
||||
handle.frame(data)
|
||||
}
|
||||
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _seq: Serial) {
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
|
||||
handle.unset_grab(self, data);
|
||||
}
|
||||
|
||||
|
|
@ -379,9 +371,8 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &ShapeEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.shape(data, event, seq)
|
||||
handle.shape(data, event)
|
||||
}
|
||||
|
||||
fn orientation(
|
||||
|
|
@ -389,9 +380,8 @@ impl TouchGrab<State> for ResizeSurfaceGrab {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &OrientationEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.orientation(data, event, seq)
|
||||
handle.orientation(data, event)
|
||||
}
|
||||
|
||||
fn start_data(&self) -> &TouchGrabStartData<State> {
|
||||
|
|
@ -543,7 +533,7 @@ impl ResizeSurfaceGrab {
|
|||
);
|
||||
}
|
||||
WindowSurface::X11(surface) => {
|
||||
let mut geometry = surface.geometry();
|
||||
let mut geometry = surface.last_configure();
|
||||
geometry.loc += (location - new_location).as_logical();
|
||||
let _ = surface.configure(geometry);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -295,6 +295,7 @@ impl FloatingLayout {
|
|||
for mapped in self
|
||||
.space
|
||||
.elements()
|
||||
.filter(|w| w.alive())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
|
|
@ -309,7 +310,10 @@ impl FloatingLayout {
|
|||
None,
|
||||
);
|
||||
} else {
|
||||
let geometry = self.space.element_geometry(&mapped).unwrap().to_f64();
|
||||
let Some(geometry) = self.space.element_geometry(&mapped) else {
|
||||
continue;
|
||||
};
|
||||
let geometry = geometry.to_f64();
|
||||
let new_loc = (
|
||||
((geometry.loc.x - old_output_geometry.loc.x).max(0.)
|
||||
/ old_output_geometry.size.w
|
||||
|
|
@ -619,6 +623,10 @@ impl FloatingLayout {
|
|||
from: Rectangle<i32, Local>,
|
||||
position: Point<i32, Local>,
|
||||
) {
|
||||
if !mapped.alive() {
|
||||
return;
|
||||
}
|
||||
|
||||
let output = self.space.outputs().next().unwrap().clone();
|
||||
let layers = layer_map_for_output(&output);
|
||||
let geometry = layers.non_exclusive_zone().as_local();
|
||||
|
|
@ -731,7 +739,11 @@ impl FloatingLayout {
|
|||
self.space.element_geometry(elem).map(RectExt::as_local)
|
||||
}
|
||||
|
||||
pub fn popup_element_under(&self, location: Point<f64, Local>) -> Option<KeyboardFocusTarget> {
|
||||
pub fn popup_element_under(
|
||||
&self,
|
||||
location: Point<f64, Local>,
|
||||
seat: &Seat<State>,
|
||||
) -> Option<KeyboardFocusTarget> {
|
||||
self.space
|
||||
.elements()
|
||||
.rev()
|
||||
|
|
@ -752,6 +764,7 @@ impl FloatingLayout {
|
|||
if e.focus_under(
|
||||
point.as_logical(),
|
||||
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
|
||||
seat,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
|
|
@ -765,6 +778,7 @@ impl FloatingLayout {
|
|||
pub fn toplevel_element_under(
|
||||
&self,
|
||||
location: Point<f64, Local>,
|
||||
seat: &Seat<State>,
|
||||
) -> Option<KeyboardFocusTarget> {
|
||||
self.space
|
||||
.elements()
|
||||
|
|
@ -786,6 +800,7 @@ impl FloatingLayout {
|
|||
if e.focus_under(
|
||||
point.as_logical(),
|
||||
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
||||
seat,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
|
|
@ -799,6 +814,7 @@ impl FloatingLayout {
|
|||
pub fn popup_surface_under(
|
||||
&self,
|
||||
location: Point<f64, Local>,
|
||||
seat: &Seat<State>,
|
||||
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
|
||||
self.space
|
||||
.elements()
|
||||
|
|
@ -820,6 +836,7 @@ impl FloatingLayout {
|
|||
e.focus_under(
|
||||
point.as_logical(),
|
||||
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
|
||||
seat,
|
||||
)
|
||||
.map(|(surface, surface_offset)| {
|
||||
(surface, render_location + surface_offset.as_local())
|
||||
|
|
@ -830,6 +847,7 @@ impl FloatingLayout {
|
|||
pub fn toplevel_surface_under(
|
||||
&self,
|
||||
location: Point<f64, Local>,
|
||||
seat: &Seat<State>,
|
||||
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
|
||||
self.space
|
||||
.elements()
|
||||
|
|
@ -851,6 +869,7 @@ impl FloatingLayout {
|
|||
e.focus_under(
|
||||
point.as_logical(),
|
||||
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
||||
seat,
|
||||
)
|
||||
.map(|(surface, surface_offset)| {
|
||||
(surface, render_location + surface_offset.as_local())
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ pub fn is_dialog(window: &CosmicSurface) -> bool {
|
|||
}
|
||||
WindowSurface::X11(surface) => {
|
||||
if surface.is_override_redirect()
|
||||
|| surface.is_popup()
|
||||
|| surface.is_modal()
|
||||
|| !matches!(
|
||||
surface.window_type(),
|
||||
None | Some(WmWindowType::Normal) | Some(WmWindowType::Utility)
|
||||
|
|
|
|||
|
|
@ -22,12 +22,13 @@ use smithay::{
|
|||
PointerTarget, RelativeMotionEvent,
|
||||
},
|
||||
touch::{
|
||||
DownEvent, GrabStartData as TouchGrabStartData, MotionEvent as TouchMotionEvent,
|
||||
OrientationEvent, ShapeEvent, TouchGrab, TouchInnerHandle, TouchTarget, UpEvent,
|
||||
DownEvent, FrameMarker, GrabStartData as TouchGrabStartData,
|
||||
MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchGrab,
|
||||
TouchInnerHandle, TouchTarget, UpEvent,
|
||||
},
|
||||
},
|
||||
output::WeakOutput,
|
||||
utils::{IsAlive, Logical, Point, Serial},
|
||||
utils::{IsAlive, Logical, Point},
|
||||
};
|
||||
|
||||
use super::super::{Data, TilingLayout};
|
||||
|
|
@ -126,7 +127,7 @@ impl PointerTarget<State> for ResizeForkTarget {
|
|||
}
|
||||
|
||||
impl TouchTarget<State> for ResizeForkTarget {
|
||||
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent, _seq: Serial) {
|
||||
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent) {
|
||||
let seat = seat.clone();
|
||||
let node = self.node.clone();
|
||||
let output = self.output.clone();
|
||||
|
|
@ -157,25 +158,14 @@ impl TouchTarget<State> for ResizeForkTarget {
|
|||
});
|
||||
}
|
||||
|
||||
fn up(&self, _seat: &Seat<State>, _data: &mut State, _event: &UpEvent, _seq: Serial) {}
|
||||
fn motion(
|
||||
&self,
|
||||
_seat: &Seat<State>,
|
||||
_data: &mut State,
|
||||
_event: &TouchMotionEvent,
|
||||
_seq: Serial,
|
||||
) {
|
||||
}
|
||||
fn frame(&self, _seat: &Seat<State>, _data: &mut State, _seq: Serial) {}
|
||||
fn cancel(&self, _seat: &Seat<State>, _data: &mut State, _seq: Serial) {}
|
||||
fn shape(&self, _seat: &Seat<State>, _data: &mut State, _event: &ShapeEvent, _seq: Serial) {}
|
||||
fn orientation(
|
||||
&self,
|
||||
_seat: &Seat<State>,
|
||||
_data: &mut State,
|
||||
_event: &OrientationEvent,
|
||||
_seq: Serial,
|
||||
) {
|
||||
fn up(&self, _seat: &Seat<State>, _data: &mut State, _event: &UpEvent) {}
|
||||
fn motion(&self, _seat: &Seat<State>, _data: &mut State, _event: &TouchMotionEvent) {}
|
||||
fn frame(&self, _seat: &Seat<State>, _data: &mut State, _frame: FrameMarker) {}
|
||||
fn cancel(&self, _seat: &Seat<State>, _data: &mut State, _frame: FrameMarker) {}
|
||||
fn shape(&self, _seat: &Seat<State>, _data: &mut State, _event: &ShapeEvent) {}
|
||||
fn orientation(&self, _seat: &Seat<State>, _data: &mut State, _event: &OrientationEvent) {}
|
||||
fn last_frame(&self, _seat: &Seat<State>, _data: &mut State) -> Option<FrameMarker> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -508,23 +498,16 @@ impl TouchGrab<State> for ResizeForkGrab {
|
|||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &DownEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.down(data, None, event, seq)
|
||||
handle.down(data, None, event)
|
||||
}
|
||||
|
||||
fn up(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &UpEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) {
|
||||
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
|
||||
handle.unset_grab(self, data);
|
||||
}
|
||||
|
||||
handle.up(data, event, seq);
|
||||
handle.up(data, event);
|
||||
}
|
||||
|
||||
fn motion(
|
||||
|
|
@ -533,7 +516,6 @@ impl TouchGrab<State> for ResizeForkGrab {
|
|||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &TouchMotionEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot
|
||||
&& self.update_location(data, event.location, false)
|
||||
|
|
@ -541,14 +523,14 @@ impl TouchGrab<State> for ResizeForkGrab {
|
|||
handle.unset_grab(self, data);
|
||||
}
|
||||
|
||||
handle.motion(data, None, event, seq);
|
||||
handle.motion(data, None, event);
|
||||
}
|
||||
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {
|
||||
handle.frame(data, seq)
|
||||
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
|
||||
handle.frame(data)
|
||||
}
|
||||
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, _seq: Serial) {
|
||||
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
|
||||
handle.unset_grab(self, data);
|
||||
}
|
||||
|
||||
|
|
@ -557,9 +539,8 @@ impl TouchGrab<State> for ResizeForkGrab {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &ShapeEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.shape(data, event, seq)
|
||||
handle.shape(data, event)
|
||||
}
|
||||
|
||||
fn start_data(&self) -> &TouchGrabStartData<State> {
|
||||
|
|
@ -574,9 +555,8 @@ impl TouchGrab<State> for ResizeForkGrab {
|
|||
data: &mut State,
|
||||
handle: &mut TouchInnerHandle<'_, State>,
|
||||
event: &OrientationEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
handle.orientation(data, event, seq)
|
||||
handle.orientation(data, event)
|
||||
}
|
||||
|
||||
fn unset(&mut self, data: &mut State) {
|
||||
|
|
|
|||
|
|
@ -3145,6 +3145,7 @@ impl TilingLayout {
|
|||
pub fn popup_element_under(
|
||||
&self,
|
||||
location_f64: Point<f64, Local>,
|
||||
seat: &Seat<State>,
|
||||
) -> Option<KeyboardFocusTarget> {
|
||||
let location = location_f64.to_i32_round();
|
||||
|
||||
|
|
@ -3157,6 +3158,7 @@ impl TilingLayout {
|
|||
.focus_under(
|
||||
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
|
||||
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
|
||||
seat,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
|
|
@ -3170,6 +3172,7 @@ impl TilingLayout {
|
|||
pub fn toplevel_element_under(
|
||||
&self,
|
||||
location_f64: Point<f64, Local>,
|
||||
seat: &Seat<State>,
|
||||
) -> Option<KeyboardFocusTarget> {
|
||||
let location = location_f64.to_i32_round();
|
||||
|
||||
|
|
@ -3182,6 +3185,7 @@ impl TilingLayout {
|
|||
.focus_under(
|
||||
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
|
||||
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
||||
seat,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
|
|
@ -3196,6 +3200,7 @@ impl TilingLayout {
|
|||
&self,
|
||||
location_f64: Point<f64, Local>,
|
||||
overview: OverviewMode,
|
||||
seat: &Seat<State>,
|
||||
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
|
||||
let location = location_f64.to_i32_round();
|
||||
|
||||
|
|
@ -3210,6 +3215,7 @@ impl TilingLayout {
|
|||
if let Some((target, surface_offset)) = mapped.focus_under(
|
||||
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
|
||||
WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE,
|
||||
seat,
|
||||
) {
|
||||
return Some((
|
||||
target,
|
||||
|
|
@ -3227,6 +3233,7 @@ impl TilingLayout {
|
|||
&self,
|
||||
location_f64: Point<f64, Local>,
|
||||
overview: OverviewMode,
|
||||
seat: &Seat<State>,
|
||||
) -> Option<(PointerFocusTarget, Point<f64, Local>)> {
|
||||
let tree = &self.queue.trees.back().unwrap().0;
|
||||
let root = tree.root_node_id()?;
|
||||
|
|
@ -3243,6 +3250,7 @@ impl TilingLayout {
|
|||
if let Some((target, surface_offset)) = mapped.focus_under(
|
||||
(location_f64 - geo.loc.to_f64()).as_logical() + mapped.geometry().loc.to_f64(),
|
||||
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
||||
seat,
|
||||
) {
|
||||
return Some((
|
||||
target,
|
||||
|
|
@ -3293,6 +3301,7 @@ impl TilingLayout {
|
|||
.focus_under(
|
||||
test_point,
|
||||
WindowSurfaceType::TOPLEVEL | WindowSurfaceType::SUBSURFACE,
|
||||
seat,
|
||||
)
|
||||
.map(|(surface, surface_offset)| {
|
||||
(
|
||||
|
|
|
|||
213
src/shell/mod.rs
213
src/shell/mod.rs
|
|
@ -50,10 +50,13 @@ use smithay::{
|
|||
},
|
||||
utils::{IsAlive, Logical, Point, Rectangle, Serial, Size},
|
||||
wayland::{
|
||||
compositor::{SurfaceAttributes, with_states},
|
||||
compositor::{SurfaceAttributes, get_parent, with_states},
|
||||
seat::WaylandFocus,
|
||||
session_lock::LockSurface,
|
||||
shell::wlr_layer::{KeyboardInteractivity, Layer, LayerSurfaceCachedState},
|
||||
shell::{
|
||||
wlr_layer::{KeyboardInteractivity, Layer, LayerSurfaceCachedState},
|
||||
xdg::{XDG_POPUP_ROLE, XdgPopupSurfaceData},
|
||||
},
|
||||
xdg_activation::XdgActivationState,
|
||||
xwayland_keyboard_grab::XWaylandKeyboardGrab,
|
||||
},
|
||||
|
|
@ -436,6 +439,11 @@ fn create_workspace_from_pinned(
|
|||
| WorkspaceCapabilities::Pin
|
||||
| WorkspaceCapabilities::Move,
|
||||
);
|
||||
|
||||
if let Some(ref name) = pinned.name {
|
||||
state.set_workspace_name(&workspace_handle, name);
|
||||
}
|
||||
|
||||
Workspace::from_pinned(
|
||||
pinned,
|
||||
workspace_handle,
|
||||
|
|
@ -616,6 +624,7 @@ impl WorkspaceSet {
|
|||
state,
|
||||
self.workspaces.len() as u8 + 1,
|
||||
&workspace.handle,
|
||||
workspace.name.as_deref(),
|
||||
// this method is only used by code paths related to dynamic workspaces, so this should be fine
|
||||
);
|
||||
self.workspaces.push(workspace);
|
||||
|
|
@ -677,7 +686,12 @@ impl WorkspaceSet {
|
|||
|
||||
fn update_workspace_idxs(&self, state: &mut WorkspaceUpdateGuard<'_, State>) {
|
||||
for (i, workspace) in self.workspaces.iter().enumerate() {
|
||||
workspace_set_idx(state, i as u8 + 1, &workspace.handle);
|
||||
workspace_set_idx(
|
||||
state,
|
||||
i as u8 + 1,
|
||||
&workspace.handle,
|
||||
workspace.name.as_deref(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -728,6 +742,78 @@ impl WorkspaceSet {
|
|||
self.post_remove_workspace(workspace_state, &previous_active_handle);
|
||||
prefers
|
||||
}
|
||||
|
||||
pub fn surface_geometry_offset_from_toplevel(
|
||||
&self,
|
||||
surface: &WlSurface,
|
||||
) -> Option<(Rectangle<i32, Local>, Point<i32, Logical>)> {
|
||||
let mut root = surface.clone();
|
||||
|
||||
while let Some(parent) = get_parent(&root) {
|
||||
root = parent;
|
||||
}
|
||||
|
||||
while smithay::wayland::compositor::get_role(&root) == Some(XDG_POPUP_ROLE) {
|
||||
let parent = with_states(&root, |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<XdgPopupSurfaceData>()
|
||||
.and_then(|m| m.lock().unwrap().parent.as_ref().cloned())
|
||||
});
|
||||
if let Some(parent) = parent {
|
||||
root = parent;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.sticky_layer
|
||||
.mapped()
|
||||
.find(|w| {
|
||||
w.windows()
|
||||
.any(|(w, _)| w.wl_surface().as_deref() == Some(&root))
|
||||
})
|
||||
.and_then(|w| {
|
||||
self.sticky_layer
|
||||
.element_geometry(w)
|
||||
.zip(w.surface_offset(surface))
|
||||
})
|
||||
.or_else(|| {
|
||||
self.workspaces.iter().find_map(|workspace| {
|
||||
workspace
|
||||
.get_fullscreen_surfaces()
|
||||
.find_map(|fs| {
|
||||
(fs.surface.wl_surface().as_deref() == Some(&root))
|
||||
.then(|| {
|
||||
fs.surface.surface_offset(surface).map(|offset| {
|
||||
(workspace.fullscreen_geometry_for(fs), offset)
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
})
|
||||
.or_else(|| {
|
||||
workspace.mapped().find_map(|w| {
|
||||
w.windows()
|
||||
.any(|(w, _)| w.wl_surface().as_deref() == Some(&root))
|
||||
.then(|| {
|
||||
workspace.element_geometry(w).zip(w.surface_offset(surface))
|
||||
})
|
||||
.flatten()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
.or_else(|| {
|
||||
layer_map_for_output(&self.output).layers().find_map(|l| {
|
||||
(l.wl_surface() == &root)
|
||||
.then(|| {
|
||||
CosmicSurface::surface_tree_offset(l.wl_surface(), surface)
|
||||
.map(|offset| (l.geometry().as_local(), offset))
|
||||
})
|
||||
.flatten()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -1502,7 +1588,9 @@ impl Common {
|
|||
self.popups.cleanup();
|
||||
self.toplevel_info_state.refresh(&self.workspace_state);
|
||||
self.refresh_idle_inhibit();
|
||||
self.a11y_keyboard_monitor_state.refresh();
|
||||
if let Some(mut a11y_keyboard_monitor) = self.dbus_state.a11y_keyboard_monitor() {
|
||||
a11y_keyboard_monitor.refresh();
|
||||
}
|
||||
self.image_copy_capture_state.cleanup();
|
||||
}
|
||||
|
||||
|
|
@ -1562,12 +1650,13 @@ impl Common {
|
|||
if let Some(mapped) = shell.element_for_surface(surface) {
|
||||
mapped.on_commit(surface);
|
||||
}
|
||||
if let Some(surface) = shell
|
||||
if let Some(fs) = shell
|
||||
.workspaces
|
||||
.spaces()
|
||||
.find_map(|w| w.get_fullscreen().filter(|s| *s == surface))
|
||||
.flat_map(|w| w.get_fullscreen_surfaces())
|
||||
.find(|f| f.surface == *surface)
|
||||
{
|
||||
surface.on_commit()
|
||||
fs.surface.on_commit()
|
||||
};
|
||||
}
|
||||
self.popups.commit(surface);
|
||||
|
|
@ -1828,7 +1917,9 @@ impl Shell {
|
|||
.outputs()
|
||||
.find(|output| {
|
||||
let workspace = self.active_space(output).unwrap();
|
||||
workspace.get_fullscreen() == Some(&elem)
|
||||
workspace
|
||||
.get_fullscreen_surfaces()
|
||||
.any(|f| f.surface == elem)
|
||||
})
|
||||
.cloned(),
|
||||
KeyboardFocusTarget::Group(WindowGroup { node, .. }) => self
|
||||
|
|
@ -1961,8 +2052,8 @@ impl Shell {
|
|||
let workspace = self.active_space(o).unwrap();
|
||||
|
||||
workspace
|
||||
.get_fullscreen()
|
||||
.is_some_and(|s| s.has_surface(surface, WindowSurfaceType::ALL))
|
||||
.get_fullscreen_surfaces()
|
||||
.any(|f| f.surface.has_surface(surface, WindowSurfaceType::ALL))
|
||||
|| workspace
|
||||
.mapped()
|
||||
.any(|e| e.has_surface(surface, WindowSurfaceType::ALL))
|
||||
|
|
@ -2017,8 +2108,8 @@ impl Shell {
|
|||
.workspaces
|
||||
.spaces()
|
||||
.find(|w| {
|
||||
w.get_fullscreen()
|
||||
.is_some_and(|s| s.has_surface(surface, WindowSurfaceType::ALL))
|
||||
w.get_fullscreen_surfaces()
|
||||
.any(|f| f.surface.has_surface(surface, WindowSurfaceType::ALL))
|
||||
|| w.mapped()
|
||||
.any(|e| e.has_surface(surface, WindowSurfaceType::ALL))
|
||||
|| w.minimized_windows.iter().any(|m| {
|
||||
|
|
@ -2065,7 +2156,7 @@ impl Shell {
|
|||
.mapped()
|
||||
.any(|m| m.windows().any(|(s, _)| &s == surface))
|
||||
|| set.workspaces.iter().any(|w| {
|
||||
w.get_fullscreen().is_some_and(|s| s == surface)
|
||||
w.get_fullscreen_surfaces().any(|f| &f.surface == surface)
|
||||
|| w.minimized_windows
|
||||
.iter()
|
||||
.any(|m| m.windows().any(|s| &s == surface))
|
||||
|
|
@ -2754,12 +2845,7 @@ impl Shell {
|
|||
let floating_exception = layout::has_floating_exception(&self.tiling_exceptions, &window);
|
||||
|
||||
if should_be_fullscreen {
|
||||
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);
|
||||
}
|
||||
workspace.map_fullscreen(&window, &seat, None, None);
|
||||
if was_activated {
|
||||
workspace_state.add_workspace_state(&workspace_handle, WState::Urgent);
|
||||
}
|
||||
|
|
@ -3130,9 +3216,12 @@ impl Shell {
|
|||
let from_workspace = self.workspaces.space_for_handle_mut(from).unwrap(); // checked above
|
||||
|
||||
let is_minimized = window.is_minimized();
|
||||
let is_fullscreen = from_workspace.get_fullscreen().is_some_and(|f| f == window);
|
||||
let is_fullscreen = from_workspace
|
||||
.get_fullscreen_surfaces()
|
||||
.any(|f| &f.surface == window);
|
||||
let mut window_state = if is_fullscreen {
|
||||
let (_, previous_state, previous_geometry) = from_workspace.take_fullscreen().unwrap();
|
||||
let (_, previous_state, previous_geometry) =
|
||||
from_workspace.take_fullscreen(window).unwrap();
|
||||
WorkspaceRestoreData::Fullscreen(previous_state.zip(previous_geometry).map(
|
||||
|(previous_state, previous_geometry)| FullscreenRestoreData {
|
||||
previous_state,
|
||||
|
|
@ -3288,14 +3377,12 @@ impl Shell {
|
|||
);
|
||||
mapped.into()
|
||||
} else if let WorkspaceRestoreData::Fullscreen(previous) = window_state {
|
||||
if let Some((old_surface, previous_state, _)) = to_workspace.map_fullscreen(
|
||||
to_workspace.map_fullscreen(
|
||||
window,
|
||||
None,
|
||||
previous.clone().map(|p| p.previous_state),
|
||||
previous.map(|p| p.previous_geometry),
|
||||
) {
|
||||
self.remap_unfullscreened_window(old_surface, previous_state, evlh);
|
||||
}
|
||||
);
|
||||
window.clone().into()
|
||||
} else {
|
||||
unreachable!() // TODO: sticky
|
||||
|
|
@ -3513,8 +3600,12 @@ impl Shell {
|
|||
} else if let Some((workspace, output)) = self.workspace_for_surface(surface) {
|
||||
let workspace = self.workspaces.space_for_handle(&workspace).unwrap();
|
||||
|
||||
if let Some(window) = workspace.get_fullscreen().filter(|s| *s == surface) {
|
||||
let global_position = (workspace.fullscreen_geometry().unwrap().loc
|
||||
if let Some(fs) = workspace
|
||||
.get_fullscreen_surfaces()
|
||||
.find(|f| &f.surface == surface)
|
||||
{
|
||||
let window = &fs.surface;
|
||||
let global_position = (workspace.fullscreen_geometry_for(fs).loc
|
||||
+ location.as_local())
|
||||
.to_global(&output);
|
||||
|
||||
|
|
@ -3607,10 +3698,14 @@ impl Shell {
|
|||
let maybe_fullscreen_workspace = self
|
||||
.workspaces
|
||||
.spaces_mut()
|
||||
.find(|w| w.get_fullscreen().is_some_and(|s| s == surface));
|
||||
.find(|w| w.get_fullscreen_surfaces().any(|f| &f.surface == surface));
|
||||
if let Some(workspace) = maybe_fullscreen_workspace {
|
||||
element_geo = Some(workspace.fullscreen_geometry().unwrap());
|
||||
let (surface, state, _) = workspace.remove_fullscreen().unwrap();
|
||||
let fs = workspace
|
||||
.get_fullscreen_surfaces()
|
||||
.find(|f| &f.surface == surface)
|
||||
.unwrap();
|
||||
element_geo = Some(workspace.fullscreen_geometry_for(fs));
|
||||
let (surface, state, _) = workspace.remove_fullscreen_surface(surface).unwrap();
|
||||
self.remap_unfullscreened_window(surface, state, evlh);
|
||||
};
|
||||
|
||||
|
|
@ -3635,7 +3730,11 @@ impl Shell {
|
|||
if old_mapped.is_maximized(false) {
|
||||
new_mapped.set_maximized(false);
|
||||
}
|
||||
start_data.set_focus(new_mapped.focus_under((0., 0.).into(), WindowSurfaceType::ALL));
|
||||
start_data.set_focus(new_mapped.focus_under(
|
||||
(0., 0.).into(),
|
||||
WindowSurfaceType::ALL,
|
||||
seat,
|
||||
));
|
||||
new_mapped
|
||||
} else {
|
||||
old_mapped.clone()
|
||||
|
|
@ -3819,19 +3918,15 @@ impl Shell {
|
|||
/// Get the window geometry of a keyboard focus target
|
||||
pub fn focused_geometry(&self, target: &KeyboardFocusTarget) -> Option<Rectangle<i32, Global>> {
|
||||
match target {
|
||||
KeyboardFocusTarget::Fullscreen(surface) => {
|
||||
if let Some(workspace) = surface
|
||||
.wl_surface()
|
||||
.and_then(|s| self.workspace_for_surface(&s))
|
||||
.and_then(|(handle, _)| self.workspaces.space_for_handle(&handle))
|
||||
{
|
||||
KeyboardFocusTarget::Fullscreen(surface) => surface
|
||||
.wl_surface()
|
||||
.and_then(|s| self.workspace_for_surface(&s))
|
||||
.and_then(|(handle, _)| self.workspaces.space_for_handle(&handle))
|
||||
.map(|workspace| {
|
||||
workspace
|
||||
.fullscreen_geometry()
|
||||
.map(|f| f.to_global(workspace.output()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
.fullscreen_geometry_for_surface(surface)
|
||||
.to_global(workspace.output())
|
||||
}),
|
||||
_ => {
|
||||
if let Some(element) = self.focused_element(target) {
|
||||
self.element_geometry(&element)
|
||||
|
|
@ -4106,7 +4201,7 @@ impl Shell {
|
|||
|
||||
let element_offset = (new_loc - geometry.loc).as_logical();
|
||||
let focus = mapped
|
||||
.focus_under(element_offset.to_f64(), WindowSurfaceType::ALL)
|
||||
.focus_under(element_offset.to_f64(), WindowSurfaceType::ALL, seat)
|
||||
.map(|(target, surface_offset)| (target, (surface_offset + element_offset.to_f64())));
|
||||
start_data.set_location(new_loc.as_logical().to_f64());
|
||||
start_data.set_focus(focus.clone());
|
||||
|
|
@ -4183,9 +4278,8 @@ impl Shell {
|
|||
self.workspaces.sets.values_mut().find_map(|set| {
|
||||
set.workspaces.iter_mut().find_map(|workspace| {
|
||||
let window = workspace
|
||||
.get_fullscreen()
|
||||
.cloned()
|
||||
.into_iter()
|
||||
.get_fullscreen_surfaces()
|
||||
.map(|f| f.surface.clone())
|
||||
.chain(workspace.mapped().map(|m| m.active_window()))
|
||||
.find(|s| s == surface);
|
||||
window.map(|s| (workspace, s))
|
||||
|
|
@ -4685,15 +4779,16 @@ impl Shell {
|
|||
&mut self,
|
||||
surface: &S,
|
||||
output: Output,
|
||||
loop_handle: &LoopHandle<'static, State>,
|
||||
_loop_handle: &LoopHandle<'static, State>,
|
||||
) -> Option<KeyboardFocusTarget>
|
||||
where
|
||||
CosmicSurface: PartialEq<S>,
|
||||
{
|
||||
let mapped = self.element_for_surface(surface).cloned()?;
|
||||
let seat = self.seats.last_active().clone();
|
||||
let window;
|
||||
|
||||
let old_fullscreen = if let Some((old_output, set)) = self
|
||||
if let Some((old_output, set)) = self
|
||||
.workspaces
|
||||
.sets
|
||||
.iter_mut()
|
||||
|
|
@ -4740,7 +4835,7 @@ impl Shell {
|
|||
let workspace = self.active_space_mut(&output).unwrap();
|
||||
workspace.map_fullscreen(
|
||||
&window,
|
||||
None,
|
||||
&seat,
|
||||
Some(FullscreenRestoreState::Sticky {
|
||||
output: old_output,
|
||||
state: FloatingRestoreData {
|
||||
|
|
@ -4751,7 +4846,7 @@ impl Shell {
|
|||
},
|
||||
}),
|
||||
Some(from),
|
||||
)
|
||||
);
|
||||
} else if let Some(workspace) = self.space_for_mut(&mapped) {
|
||||
if mapped.is_minimized() {
|
||||
// TODO: Rewrite the `MinimizedWindow` to restore to fullscreen
|
||||
|
|
@ -4777,7 +4872,7 @@ impl Shell {
|
|||
|
||||
workspace.map_fullscreen(
|
||||
&window,
|
||||
None,
|
||||
&seat,
|
||||
match state {
|
||||
WorkspaceRestoreData::Floating(floating_state) => {
|
||||
floating_state.map(|state| FullscreenRestoreState::Floating {
|
||||
|
|
@ -4794,15 +4889,11 @@ impl Shell {
|
|||
WorkspaceRestoreData::Fullscreen(_) => unreachable!(),
|
||||
},
|
||||
Some(from),
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if let Some((old_fullscreen, restore, _)) = old_fullscreen {
|
||||
self.remap_unfullscreened_window(old_fullscreen, restore, loop_handle);
|
||||
}
|
||||
|
||||
Some(KeyboardFocusTarget::Fullscreen(window))
|
||||
}
|
||||
|
||||
|
|
@ -4817,11 +4908,12 @@ impl Shell {
|
|||
let maybe_workspace = self.workspaces.iter_mut().find_map(|(_, s)| {
|
||||
s.workspaces
|
||||
.iter_mut()
|
||||
.find(|w| w.get_fullscreen().is_some_and(|f| f == surface))
|
||||
.find(|w| w.get_fullscreen_surfaces().any(|f| &f.surface == surface))
|
||||
});
|
||||
|
||||
if let Some(workspace) = maybe_workspace {
|
||||
let (old_fullscreen, restore, _) = workspace.remove_fullscreen().unwrap();
|
||||
let (old_fullscreen, restore, _) =
|
||||
workspace.remove_fullscreen_surface(surface).unwrap();
|
||||
toplevel_leave_output(&old_fullscreen, &workspace.output);
|
||||
toplevel_leave_workspace(&old_fullscreen, &workspace.handle);
|
||||
|
||||
|
|
@ -4944,8 +5036,9 @@ fn workspace_set_idx(
|
|||
state: &mut WorkspaceUpdateGuard<'_, State>,
|
||||
idx: u8,
|
||||
handle: &WorkspaceHandle,
|
||||
name: Option<&str>,
|
||||
) {
|
||||
state.set_workspace_name(handle, format!("{}", idx));
|
||||
state.set_workspace_name(handle, name.unwrap_or(&format!("{}", idx)));
|
||||
state.set_workspace_coordinates(handle, &[idx as u32]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,11 @@ use smithay::{
|
|||
pointer::{CursorImageAttributes, CursorImageStatus},
|
||||
},
|
||||
output::Output,
|
||||
reexports::{input::Device as InputDevice, wayland_server::DisplayHandle},
|
||||
utils::{Buffer, IsAlive, Monotonic, Point, Rectangle, Serial, Time, Transform},
|
||||
reexports::{
|
||||
input::Device as InputDevice,
|
||||
wayland_server::{DisplayHandle, protocol::wl_surface::WlSurface},
|
||||
},
|
||||
utils::{Buffer, IsAlive, Logical, Monotonic, Point, Rectangle, Serial, Time, Transform},
|
||||
wayland::compositor::with_states,
|
||||
};
|
||||
use tracing::warn;
|
||||
|
|
@ -178,6 +181,9 @@ struct ActiveOutput(pub Mutex<Output>);
|
|||
/// The output which currently has keyboard focus
|
||||
struct FocusedOutput(pub Mutex<Option<Output>>);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PointerConstraintHint(pub Mutex<Option<(WlSurface, Point<f64, Logical>)>>);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LastModifierChange(pub Mutex<Option<Serial>>);
|
||||
|
||||
|
|
@ -201,6 +207,7 @@ pub fn create_seat(
|
|||
userdata.insert_if_missing_threadsafe(CursorState::default);
|
||||
userdata.insert_if_missing_threadsafe(|| ActiveOutput(Mutex::new(output.clone())));
|
||||
userdata.insert_if_missing_threadsafe(|| FocusedOutput(Mutex::new(None)));
|
||||
userdata.insert_if_missing_threadsafe(PointerConstraintHint::default);
|
||||
userdata.insert_if_missing_threadsafe(|| Mutex::new(CursorImageStatus::default_named()));
|
||||
|
||||
// A lot of clients bind keyboard and pointer unconditionally once on launch..
|
||||
|
|
@ -252,6 +259,8 @@ pub trait SeatExt {
|
|||
fn supressed_buttons(&self) -> &SupressedButtons;
|
||||
fn modifiers_shortcut_queue(&self) -> &ModifiersShortcutQueue;
|
||||
fn last_modifier_change(&self) -> Option<Serial>;
|
||||
fn pointer_constraint_hint(&self) -> Option<(WlSurface, Point<f64, Logical>)>;
|
||||
fn set_pointer_constraint_hint(&self, hint: Option<(WlSurface, Point<f64, Logical>)>);
|
||||
|
||||
fn cursor_geometry(
|
||||
&self,
|
||||
|
|
@ -337,6 +346,23 @@ impl SeatExt for Seat<State> {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn pointer_constraint_hint(&self) -> Option<(WlSurface, Point<f64, Logical>)> {
|
||||
let lock = self.user_data().get::<PointerConstraintHint>().unwrap();
|
||||
let mut hint = lock.0.lock().unwrap();
|
||||
// Check if alive
|
||||
if let Some((ref surface, _)) = *hint
|
||||
&& !surface.alive()
|
||||
{
|
||||
*hint = None;
|
||||
}
|
||||
hint.clone()
|
||||
}
|
||||
|
||||
fn set_pointer_constraint_hint(&self, hint: Option<(WlSurface, Point<f64, Logical>)>) {
|
||||
let lock = self.user_data().get::<PointerConstraintHint>().unwrap();
|
||||
*lock.0.lock().unwrap() = hint;
|
||||
}
|
||||
|
||||
fn cursor_geometry(
|
||||
&self,
|
||||
loc: impl Into<Point<f64, Buffer>>,
|
||||
|
|
|
|||
|
|
@ -108,9 +108,10 @@ pub struct Workspace {
|
|||
pub floating_layer: FloatingLayout,
|
||||
pub minimized_windows: Vec<MinimizedWindow>,
|
||||
pub tiling_enabled: bool,
|
||||
pub fullscreen: Option<FullscreenSurface>,
|
||||
pub fullscreen_surfaces: Vec<FullscreenSurface>,
|
||||
pub pinned: bool,
|
||||
pub id: Option<String>,
|
||||
pub name: Option<String>,
|
||||
|
||||
pub handle: WorkspaceHandle,
|
||||
pub focus_stack: FocusStacks,
|
||||
|
|
@ -383,9 +384,10 @@ impl Workspace {
|
|||
floating_layer,
|
||||
tiling_enabled,
|
||||
minimized_windows: Vec::new(),
|
||||
fullscreen: None,
|
||||
fullscreen_surfaces: Vec::new(),
|
||||
pinned: false,
|
||||
id: None,
|
||||
name: None,
|
||||
handle,
|
||||
focus_stack: FocusStacks::default(),
|
||||
image_copy: ImageCopySessions::default(),
|
||||
|
|
@ -416,9 +418,10 @@ impl Workspace {
|
|||
floating_layer,
|
||||
tiling_enabled: pinned.tiling_enabled,
|
||||
minimized_windows: Vec::new(),
|
||||
fullscreen: None,
|
||||
fullscreen_surfaces: Vec::new(),
|
||||
pinned: true,
|
||||
id: pinned.id.clone(),
|
||||
name: pinned.name.clone(),
|
||||
handle,
|
||||
focus_stack: FocusStacks::default(),
|
||||
image_copy: ImageCopySessions::default(),
|
||||
|
|
@ -436,9 +439,9 @@ impl Workspace {
|
|||
}
|
||||
|
||||
pub fn to_pinned(&self) -> Option<PinnedWorkspace> {
|
||||
debug_assert!(self.id.is_some());
|
||||
let output = self.explicit_output().clone();
|
||||
if self.pinned {
|
||||
debug_assert!(self.id.is_some());
|
||||
Some(PinnedWorkspace {
|
||||
output: cosmic_comp_config::workspace::OutputMatch {
|
||||
name: output.name,
|
||||
|
|
@ -446,6 +449,7 @@ impl Workspace {
|
|||
},
|
||||
tiling_enabled: self.tiling_enabled,
|
||||
id: self.id.clone(),
|
||||
name: self.name.clone(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
|
@ -454,7 +458,10 @@ impl Workspace {
|
|||
|
||||
#[profiling::function]
|
||||
pub fn refresh(&mut self) {
|
||||
self.fullscreen.take_if(|w| !w.alive());
|
||||
// seems it removes dead windows
|
||||
// self.fullscreen.take_if(|w| !w.alive());
|
||||
self.fullscreen_surfaces.retain(|w| w.alive());
|
||||
|
||||
self.floating_layer.refresh();
|
||||
self.tiling_layer.refresh();
|
||||
}
|
||||
|
|
@ -478,14 +485,15 @@ impl Workspace {
|
|||
self.is_empty() && !self.has_activation_token(xdg_activation_state) && !self.pinned
|
||||
}
|
||||
|
||||
/// cleans up any window that is not alive anymore
|
||||
pub fn refresh_focus_stack(&mut self) {
|
||||
for (seat, stack) in self.focus_stack.0.iter_mut() {
|
||||
let fullscreen = self
|
||||
.fullscreen
|
||||
.as_ref()
|
||||
.filter(|f| f.alive())
|
||||
.filter(|f| f.ended_at.is_none())
|
||||
.map(|f| &f.surface);
|
||||
let fullscreen_surfaces: Vec<&CosmicSurface> = self
|
||||
.fullscreen_surfaces
|
||||
.iter()
|
||||
.filter(|f| f.alive() && f.ended_at.is_none())
|
||||
.map(|f| &f.surface)
|
||||
.collect();
|
||||
|
||||
// Move grab is treated as focused, so don't change focus to a
|
||||
// window while grab exists.
|
||||
|
|
@ -506,7 +514,7 @@ impl Workspace {
|
|||
.chain(move_mapped.iter())
|
||||
};
|
||||
stack.retain(|w| match w {
|
||||
FocusTarget::Fullscreen(s) => fullscreen.is_some_and(|f| f == s),
|
||||
FocusTarget::Fullscreen(s) => fullscreen_surfaces.contains(&s),
|
||||
FocusTarget::Window(w) => mapped().any(|m| w == m),
|
||||
});
|
||||
}
|
||||
|
|
@ -515,15 +523,12 @@ impl Workspace {
|
|||
pub fn animations_going(&self) -> bool {
|
||||
self.tiling_layer.animations_going()
|
||||
|| self.floating_layer.animations_going()
|
||||
|| self
|
||||
.fullscreen
|
||||
.as_ref()
|
||||
.is_some_and(|f| f.start_at.is_some() || f.ended_at.is_some())
|
||||
|| self.fullscreen_surfaces.iter().any(|f| f.is_animating())
|
||||
|| self.dirty.swap(false, Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub fn update_animations(&mut self) -> HashMap<ClientId, Client> {
|
||||
if let Some(f) = self.fullscreen.as_mut() {
|
||||
for f in self.fullscreen_surfaces.iter_mut() {
|
||||
if let Some(start) = f.start_at.as_ref() {
|
||||
let duration_since = Instant::now().duration_since(*start);
|
||||
if duration_since > FULLSCREEN_ANIMATION_DURATION {
|
||||
|
|
@ -531,16 +536,18 @@ impl Workspace {
|
|||
self.dirty.store(true, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(end) = f.ended_at {
|
||||
let duration_since = Instant::now().duration_since(end);
|
||||
if duration_since >= FULLSCREEN_ANIMATION_DURATION {
|
||||
let _ = self.fullscreen.take();
|
||||
self.dirty.store(true, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.fullscreen_surfaces.retain(|f| {
|
||||
if let Some(end) = f.ended_at
|
||||
&& Instant::now().duration_since(end) >= FULLSCREEN_ANIMATION_DURATION
|
||||
{
|
||||
self.dirty.store(true, Ordering::SeqCst);
|
||||
return false;
|
||||
}
|
||||
true
|
||||
});
|
||||
|
||||
let clients = self.tiling_layer.update_animation_state();
|
||||
self.floating_layer.update_animation_state();
|
||||
clients
|
||||
|
|
@ -574,7 +581,11 @@ impl Workspace {
|
|||
toplevel_enter_output(&surface, output);
|
||||
}
|
||||
}
|
||||
if let Some(f) = self.fullscreen.as_ref().filter(|f| f.ended_at.is_none()) {
|
||||
for f in self
|
||||
.fullscreen_surfaces
|
||||
.iter()
|
||||
.filter(|f| f.ended_at.is_none())
|
||||
{
|
||||
toplevel_leave_output(&f.surface, &self.output);
|
||||
toplevel_enter_output(&f.surface, output);
|
||||
}
|
||||
|
|
@ -665,12 +676,13 @@ impl Workspace {
|
|||
where
|
||||
CosmicSurface: PartialEq<S>,
|
||||
{
|
||||
if self
|
||||
.fullscreen
|
||||
.as_ref()
|
||||
.is_some_and(|f| f.ended_at.is_none() && &f.surface == surface)
|
||||
if let Some(idx) = self
|
||||
.fullscreen_surfaces
|
||||
.iter()
|
||||
.position(|f| f.ended_at.is_none() && &f.surface == surface)
|
||||
{
|
||||
let (surface, previous_state, previous_geometry) = self.remove_fullscreen().unwrap();
|
||||
let (surface, previous_state, previous_geometry) =
|
||||
self.remove_fullscreen_at(idx).unwrap();
|
||||
return Some((
|
||||
surface,
|
||||
WorkspaceRestoreData::Fullscreen(previous_state.zip(previous_geometry).map(
|
||||
|
|
@ -723,24 +735,28 @@ impl Workspace {
|
|||
Some((mapped.active_window(), layer))
|
||||
}
|
||||
|
||||
pub fn fullscreen_geometry(&self) -> Option<Rectangle<i32, Local>> {
|
||||
self.fullscreen.as_ref().map(|fullscreen| {
|
||||
let bbox = fullscreen.surface.bbox().as_local();
|
||||
pub fn fullscreen_geometry_for_surface(
|
||||
&self,
|
||||
surface: &CosmicSurface,
|
||||
) -> Rectangle<i32, Local> {
|
||||
let bbox = surface.bbox().as_local();
|
||||
|
||||
let mut full_geo = Rectangle::from_size(self.output.geometry().size.as_local());
|
||||
if bbox != full_geo {
|
||||
if bbox.size.w < full_geo.size.w {
|
||||
full_geo.loc.x += (full_geo.size.w - bbox.size.w) / 2;
|
||||
full_geo.size.w = bbox.size.w;
|
||||
}
|
||||
if bbox.size.h < full_geo.size.h {
|
||||
full_geo.loc.y += (full_geo.size.h - bbox.size.h) / 2;
|
||||
full_geo.size.h = bbox.size.h;
|
||||
}
|
||||
let mut full_geo = Rectangle::from_size(self.output.geometry().size.as_local());
|
||||
if bbox != full_geo {
|
||||
if bbox.size.w < full_geo.size.w {
|
||||
full_geo.loc.x += (full_geo.size.w - bbox.size.w) / 2;
|
||||
full_geo.size.w = bbox.size.w;
|
||||
}
|
||||
if bbox.size.h < full_geo.size.h {
|
||||
full_geo.loc.y += (full_geo.size.h - bbox.size.h) / 2;
|
||||
full_geo.size.h = bbox.size.h;
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
|
|
@ -780,24 +796,23 @@ impl Workspace {
|
|||
let stack = self.focus_stack.get(seat);
|
||||
let last_focused = stack.last();
|
||||
|
||||
if let Some(fullscreen) = self.fullscreen.as_ref()
|
||||
&& last_focused.is_some_and(
|
||||
|t| matches!(t, FocusTarget::Fullscreen(f) if f == &fullscreen.surface),
|
||||
)
|
||||
&& !fullscreen.is_animating()
|
||||
{
|
||||
let geometry = self.fullscreen_geometry().unwrap();
|
||||
if let Some(fullscreen) = self.fullscreen_surfaces.iter().find(|f| {
|
||||
!f.is_animating()
|
||||
&& last_focused
|
||||
.is_some_and(|t| matches!(t, FocusTarget::Fullscreen(s) if s == &f.surface))
|
||||
}) {
|
||||
let geometry = self.fullscreen_geometry_for(fullscreen);
|
||||
return fullscreen_element_under(fullscreen, geometry);
|
||||
}
|
||||
|
||||
self.floating_layer
|
||||
.popup_element_under(location)
|
||||
.or_else(|| self.tiling_layer.popup_element_under(location))
|
||||
.popup_element_under(location, seat)
|
||||
.or_else(|| self.tiling_layer.popup_element_under(location, seat))
|
||||
.or_else(|| {
|
||||
if last_focused.is_none_or(|t| !matches!(t, FocusTarget::Fullscreen(_)))
|
||||
&& let Some(fullscreen) = self.fullscreen.as_ref()
|
||||
&& let Some(fullscreen) = self.get_fullscreen(seat)
|
||||
{
|
||||
let geometry = self.fullscreen_geometry().unwrap();
|
||||
let geometry = self.fullscreen_geometry_for(fullscreen);
|
||||
return fullscreen_element_under(fullscreen, geometry);
|
||||
}
|
||||
None
|
||||
|
|
@ -830,24 +845,23 @@ impl Workspace {
|
|||
let stack = self.focus_stack.get(seat);
|
||||
let last_focused = stack.last();
|
||||
|
||||
if let Some(fullscreen) = self.fullscreen.as_ref()
|
||||
&& last_focused.is_some_and(
|
||||
|t| matches!(t, FocusTarget::Fullscreen(f) if f == &fullscreen.surface),
|
||||
)
|
||||
&& !fullscreen.is_animating()
|
||||
{
|
||||
let geometry = self.fullscreen_geometry().unwrap();
|
||||
if let Some(fullscreen) = self.fullscreen_surfaces.iter().find(|fs| {
|
||||
!fs.is_animating()
|
||||
&& last_focused
|
||||
.is_some_and(|t| matches!(t, FocusTarget::Fullscreen(f) if f == &fs.surface))
|
||||
}) {
|
||||
let geometry = self.fullscreen_geometry_for(fullscreen);
|
||||
return fullscreen_element_under(fullscreen, geometry);
|
||||
}
|
||||
|
||||
self.floating_layer
|
||||
.toplevel_element_under(location)
|
||||
.or_else(|| self.tiling_layer.toplevel_element_under(location))
|
||||
.toplevel_element_under(location, seat)
|
||||
.or_else(|| self.tiling_layer.toplevel_element_under(location, seat))
|
||||
.or_else(|| {
|
||||
if last_focused.is_none_or(|t| !matches!(t, FocusTarget::Fullscreen(_)))
|
||||
&& let Some(fullscreen) = self.fullscreen.as_ref()
|
||||
&& let Some(fullscreen) = self.get_fullscreen(seat)
|
||||
{
|
||||
let geometry = self.fullscreen_geometry().unwrap();
|
||||
let geometry = self.fullscreen_geometry_for(fullscreen);
|
||||
return fullscreen_element_under(fullscreen, geometry);
|
||||
}
|
||||
None
|
||||
|
|
@ -867,7 +881,7 @@ impl Workspace {
|
|||
|
||||
let check_fullscreen = |fullscreen: &FullscreenSurface| {
|
||||
if !fullscreen.is_animating() {
|
||||
let geometry = self.fullscreen_geometry().unwrap();
|
||||
let geometry = self.fullscreen_geometry_for(fullscreen);
|
||||
return fullscreen
|
||||
.surface
|
||||
.0
|
||||
|
|
@ -891,18 +905,16 @@ impl Workspace {
|
|||
let stack = self.focus_stack.get(seat);
|
||||
let last_focused = stack.last();
|
||||
|
||||
self.fullscreen
|
||||
.as_ref()
|
||||
.filter(|f| last_focused.is_some_and(|t| t == &f.surface))
|
||||
self.fullscreen_surfaces
|
||||
.iter()
|
||||
.find(|f| last_focused.is_some_and(|t| t == &f.surface))
|
||||
.and_then(check_fullscreen)
|
||||
.or_else(|| self.floating_layer.popup_surface_under(location))
|
||||
.or_else(|| self.tiling_layer.popup_surface_under(location, overview))
|
||||
.or_else(|| self.floating_layer.popup_surface_under(location, seat))
|
||||
.or_else(|| {
|
||||
self.fullscreen
|
||||
.as_ref()
|
||||
.filter(|f| last_focused.is_none_or(|t| t != &f.surface))
|
||||
.and_then(check_fullscreen)
|
||||
self.tiling_layer
|
||||
.popup_surface_under(location, overview, seat)
|
||||
})
|
||||
.or_else(|| self.get_fullscreen(seat).and_then(check_fullscreen))
|
||||
.map(|(m, p)| (m, p.to_global(&self.output)))
|
||||
}
|
||||
|
||||
|
|
@ -919,7 +931,7 @@ impl Workspace {
|
|||
|
||||
let check_fullscreen = |fullscreen: &FullscreenSurface| {
|
||||
if !fullscreen.is_animating() {
|
||||
let geometry = self.fullscreen_geometry().unwrap();
|
||||
let geometry = self.fullscreen_geometry_for(fullscreen);
|
||||
return fullscreen
|
||||
.surface
|
||||
.focus_under(
|
||||
|
|
@ -937,18 +949,16 @@ impl Workspace {
|
|||
let stack = self.focus_stack.get(seat);
|
||||
let last_focused = stack.last();
|
||||
|
||||
self.fullscreen
|
||||
.as_ref()
|
||||
.filter(|f| last_focused.is_some_and(|t| t == &f.surface))
|
||||
self.fullscreen_surfaces
|
||||
.iter()
|
||||
.find(|f| last_focused.is_some_and(|t| t == &f.surface))
|
||||
.and_then(check_fullscreen)
|
||||
.or_else(|| self.floating_layer.toplevel_surface_under(location))
|
||||
.or_else(|| self.tiling_layer.toplevel_surface_under(location, overview))
|
||||
.or_else(|| self.floating_layer.toplevel_surface_under(location, seat))
|
||||
.or_else(|| {
|
||||
self.fullscreen
|
||||
.as_ref()
|
||||
.filter(|f| last_focused.is_none_or(|t| t != &f.surface))
|
||||
.and_then(check_fullscreen)
|
||||
self.tiling_layer
|
||||
.toplevel_surface_under(location, overview, seat)
|
||||
})
|
||||
.or_else(|| self.get_fullscreen(seat).and_then(check_fullscreen))
|
||||
.map(|(m, p)| (m, p.to_global(&self.output)))
|
||||
}
|
||||
|
||||
|
|
@ -1020,10 +1030,14 @@ impl Workspace {
|
|||
where
|
||||
CosmicSurface: PartialEq<S>,
|
||||
{
|
||||
if self.get_fullscreen().is_some_and(|s| s == surface) {
|
||||
let fullscreen_state = self.fullscreen.clone().unwrap();
|
||||
if let Some(idx) = self
|
||||
.fullscreen_surfaces
|
||||
.iter()
|
||||
.position(|f| f.ended_at.is_none() && &f.surface == surface)
|
||||
{
|
||||
let fullscreen_state = self.fullscreen_surfaces.get(idx)?.clone();
|
||||
{
|
||||
let f = self.fullscreen.as_mut().unwrap();
|
||||
let f = self.fullscreen_surfaces.get_mut(idx)?;
|
||||
f.previous_geometry = Some(to);
|
||||
f.ended_at = Some(
|
||||
Instant::now()
|
||||
|
|
@ -1127,16 +1141,20 @@ impl Workspace {
|
|||
)> {
|
||||
match window {
|
||||
MinimizedWindow::Fullscreen { previous, surface } => {
|
||||
let old_fullscreen = self.remove_fullscreen();
|
||||
surface.set_minimized(false);
|
||||
self.fullscreen = Some(FullscreenSurface {
|
||||
// focus it so it's the top fullscreen window
|
||||
self.focus_stack
|
||||
.get_mut(seat)
|
||||
.append(FocusTarget::Fullscreen(surface.clone()));
|
||||
self.fullscreen_surfaces.push(FullscreenSurface {
|
||||
surface,
|
||||
previous_state: previous.clone().map(|p| p.previous_state),
|
||||
previous_geometry: previous.map(|p| p.previous_geometry),
|
||||
start_at: None,
|
||||
ended_at: None,
|
||||
});
|
||||
old_fullscreen
|
||||
self.dirty.store(true, Ordering::SeqCst);
|
||||
None
|
||||
}
|
||||
MinimizedWindow::Floating { window, previous } => {
|
||||
let current_output_size = self.output.geometry().size.as_logical();
|
||||
|
|
@ -1224,13 +1242,7 @@ impl Workspace {
|
|||
seat: impl Into<Option<&'a Seat<State>>>,
|
||||
restore: Option<FullscreenRestoreState>,
|
||||
previous_geometry: Option<Rectangle<i32, Local>>,
|
||||
) -> Option<(
|
||||
CosmicSurface,
|
||||
Option<FullscreenRestoreState>,
|
||||
Option<Rectangle<i32, Local>>,
|
||||
)> {
|
||||
let res = self.remove_fullscreen();
|
||||
|
||||
) {
|
||||
window.set_fullscreen(true);
|
||||
window.set_geometry(self.output.geometry(), 0);
|
||||
window.send_configure();
|
||||
|
|
@ -1243,96 +1255,137 @@ impl Workspace {
|
|||
self.focus_stack.get_mut(seat).append(window.clone());
|
||||
}
|
||||
|
||||
self.fullscreen = Some(FullscreenSurface {
|
||||
self.dirty.store(true, Ordering::SeqCst);
|
||||
self.fullscreen_surfaces.push(FullscreenSurface {
|
||||
surface: window.clone(),
|
||||
previous_state: restore,
|
||||
previous_geometry,
|
||||
start_at: Some(Instant::now()),
|
||||
ended_at: None,
|
||||
});
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn take_fullscreen(
|
||||
pub fn take_fullscreen<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)?;
|
||||
let fs = self.fullscreen_surfaces.remove(idx);
|
||||
|
||||
for focus_stack in self.focus_stack.0.values_mut() {
|
||||
focus_stack.retain(|t| t != &fs.surface);
|
||||
}
|
||||
|
||||
Some((fs.surface, fs.previous_state, fs.previous_geometry))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn remove_fullscreen_at(
|
||||
&mut self,
|
||||
idx: usize,
|
||||
) -> Option<(
|
||||
CosmicSurface,
|
||||
Option<FullscreenRestoreState>,
|
||||
Option<Rectangle<i32, Local>>,
|
||||
)> {
|
||||
let surface = self.fullscreen.take_if(|s| s.ended_at.is_none())?;
|
||||
// if it doesn't exist we move on.
|
||||
let surface = self.fullscreen_surfaces.get_mut(idx)?;
|
||||
// if already being removed, do nothing
|
||||
if surface.ended_at.is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if surface.surface.alive() {
|
||||
surface.surface.output_leave(&self.output);
|
||||
surface.surface.set_fullscreen(false);
|
||||
if let Some(previous_geometry) = surface.previous_geometry.as_ref() {
|
||||
surface
|
||||
.surface
|
||||
.set_geometry(previous_geometry.to_global(&self.output), 0);
|
||||
}
|
||||
surface.surface.send_configure();
|
||||
}
|
||||
|
||||
for focus_stack in self.focus_stack.0.values_mut() {
|
||||
focus_stack.retain(|t| t != &surface.surface);
|
||||
}
|
||||
|
||||
surface.ended_at = Some(
|
||||
Instant::now()
|
||||
- (FULLSCREEN_ANIMATION_DURATION
|
||||
- surface
|
||||
.start_at
|
||||
.take()
|
||||
.map(|earlier| {
|
||||
Instant::now()
|
||||
.duration_since(earlier)
|
||||
.min(FULLSCREEN_ANIMATION_DURATION)
|
||||
})
|
||||
.unwrap_or(FULLSCREEN_ANIMATION_DURATION)),
|
||||
);
|
||||
|
||||
Some((
|
||||
surface.surface,
|
||||
surface.previous_state,
|
||||
surface.surface.clone(),
|
||||
surface.previous_state.clone(),
|
||||
surface.previous_geometry,
|
||||
))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn remove_fullscreen(
|
||||
pub fn remove_fullscreen_surface<S>(
|
||||
&mut self,
|
||||
surface: &S,
|
||||
) -> Option<(
|
||||
CosmicSurface,
|
||||
Option<FullscreenRestoreState>,
|
||||
Option<Rectangle<i32, Local>>,
|
||||
)> {
|
||||
if let Some(surface) = self.fullscreen.as_mut() {
|
||||
if surface.ended_at.is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if surface.surface.alive() {
|
||||
surface.surface.output_leave(&self.output);
|
||||
surface.surface.set_fullscreen(false);
|
||||
if let Some(previous_geometry) = surface.previous_geometry.as_ref() {
|
||||
surface
|
||||
.surface
|
||||
.set_geometry(previous_geometry.to_global(&self.output), 0);
|
||||
}
|
||||
surface.surface.send_configure();
|
||||
}
|
||||
|
||||
for focus_stack in self.focus_stack.0.values_mut() {
|
||||
focus_stack.retain(|t| t != &surface.surface);
|
||||
}
|
||||
|
||||
surface.ended_at = Some(
|
||||
Instant::now()
|
||||
- (FULLSCREEN_ANIMATION_DURATION
|
||||
- surface
|
||||
.start_at
|
||||
.take()
|
||||
.map(|earlier| {
|
||||
Instant::now()
|
||||
.duration_since(earlier)
|
||||
.min(FULLSCREEN_ANIMATION_DURATION)
|
||||
})
|
||||
.unwrap_or(FULLSCREEN_ANIMATION_DURATION)),
|
||||
);
|
||||
|
||||
Some((
|
||||
surface.surface.clone(),
|
||||
surface.previous_state.clone(),
|
||||
surface.previous_geometry,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
)>
|
||||
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) -> Option<&CosmicSurface> {
|
||||
self.fullscreen
|
||||
.as_ref()
|
||||
.filter(|f| f.alive())
|
||||
.filter(|f| f.ended_at.is_none())
|
||||
.map(|f| &f.surface)
|
||||
pub fn get_fullscreen(&self, seat: &Seat<State>) -> Option<&FullscreenSurface> {
|
||||
let stack = self.focus_stack.get(seat);
|
||||
stack
|
||||
.iter()
|
||||
.find_map(|t| {
|
||||
if let FocusTarget::Fullscreen(s) = t {
|
||||
self.fullscreen_surfaces
|
||||
.iter()
|
||||
.find(|f| f.alive() && f.ended_at.is_none() && &f.surface == s)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.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> {
|
||||
self.fullscreen_surfaces
|
||||
.iter()
|
||||
.filter(|f| f.alive() && f.ended_at.is_none())
|
||||
}
|
||||
|
||||
pub fn resize(
|
||||
|
|
@ -1459,14 +1512,18 @@ impl Workspace {
|
|||
self.floating_layer.mapped().count()
|
||||
+ self.tiling_layer.mapped().count()
|
||||
+ self.minimized_windows.len()
|
||||
+ if self.fullscreen.is_some() { 1 } else { 0 }
|
||||
+ self
|
||||
.fullscreen_surfaces
|
||||
.iter()
|
||||
.filter(|f| f.ended_at.is_none())
|
||||
.count()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.floating_layer.mapped().next().is_none()
|
||||
&& self.tiling_layer.mapped().next().is_none()
|
||||
&& self.minimized_windows.is_empty()
|
||||
&& self.fullscreen.is_none()
|
||||
&& self.fullscreen_surfaces.is_empty()
|
||||
}
|
||||
|
||||
pub fn is_floating<S>(&self, surface: &S) -> bool
|
||||
|
|
@ -1573,8 +1630,11 @@ impl Workspace {
|
|||
};
|
||||
let focused = self.focus_stack.get(last_active_seat).last().cloned();
|
||||
|
||||
let mut fullscreen_elements = if let Some(fullscreen) = self.fullscreen.as_ref() {
|
||||
let fullscreen_geo = self.fullscreen_geometry().unwrap();
|
||||
let render_fullscreen = |fullscreen: &FullscreenSurface,
|
||||
renderer: &mut R,
|
||||
output_scale: f64|
|
||||
-> Vec<WorkspaceRenderElement<R>> {
|
||||
let fullscreen_geo = self.fullscreen_geometry_for(fullscreen);
|
||||
let previous_geo = fullscreen
|
||||
.previous_geometry
|
||||
.as_ref()
|
||||
|
|
@ -1644,20 +1704,35 @@ impl Workspace {
|
|||
.into_iter()
|
||||
.map(animation_rescale)
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let top_fullscreen = self.get_fullscreen(last_active_seat);
|
||||
|
||||
let mut fullscreen_elements: Vec<WorkspaceRenderElement<R>> = Vec::new();
|
||||
if let Some(fs) = top_fullscreen {
|
||||
fullscreen_elements.extend(render_fullscreen(fs, renderer, output_scale));
|
||||
}
|
||||
// Also render any animating (entering/exiting) fullscreens
|
||||
for fs in self.fullscreen_surfaces.iter().filter(|f| f.is_animating()) {
|
||||
if top_fullscreen.is_none_or(|top| top.surface != fs.surface) {
|
||||
fullscreen_elements.extend(render_fullscreen(fs, renderer, output_scale));
|
||||
};
|
||||
}
|
||||
|
||||
if matches!(focused, Some(FocusTarget::Fullscreen(_))) {
|
||||
elements.append(&mut fullscreen_elements);
|
||||
}
|
||||
|
||||
let any_fullscreen_animating = self
|
||||
.fullscreen_surfaces
|
||||
.iter()
|
||||
.any(|f| f.start_at.is_some() || f.ended_at.is_some());
|
||||
if !matches!(focused, Some(FocusTarget::Fullscreen(_)))
|
||||
|| any_fullscreen_animating
|
||||
|| self
|
||||
.fullscreen
|
||||
.as_ref()
|
||||
.map(|f| f.start_at.is_some() || f.ended_at.is_some())
|
||||
.unwrap_or(true)
|
||||
.fullscreen_surfaces
|
||||
.iter()
|
||||
.all(|f| !f.alive() || f.ended_at.is_some())
|
||||
{
|
||||
// floating surfaces
|
||||
let alpha = match &overview.0 {
|
||||
|
|
@ -1682,13 +1757,17 @@ impl Workspace {
|
|||
self.floating_layer
|
||||
.render::<R>(
|
||||
renderer,
|
||||
focused.as_ref().and_then(|target| {
|
||||
if let FocusTarget::Window(mapped) = target {
|
||||
Some(mapped)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
render_focus
|
||||
.then(|| {
|
||||
focused.as_ref().and_then(|target| {
|
||||
if let FocusTarget::Window(mapped) = target {
|
||||
Some(mapped)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.flatten(),
|
||||
resize_indicator.clone(),
|
||||
indicator_thickness,
|
||||
alpha,
|
||||
|
|
@ -1774,8 +1853,12 @@ impl Workspace {
|
|||
layer_map.non_exclusive_zone().as_local()
|
||||
};
|
||||
|
||||
if let Some(fullscreen) = self.fullscreen.as_ref() {
|
||||
let fullscreen_geo = self.fullscreen_geometry().unwrap();
|
||||
// Render popups for the top (most recently focused) fullscreen
|
||||
let focus_stack = self.focus_stack.get(last_active_seat);
|
||||
let top_fullscreen = self.get_fullscreen(last_active_seat);
|
||||
|
||||
if let Some(fullscreen) = top_fullscreen {
|
||||
let fullscreen_geo = self.fullscreen_geometry_for(fullscreen);
|
||||
let previous_geo = fullscreen
|
||||
.previous_geometry
|
||||
.as_ref()
|
||||
|
|
@ -1832,13 +1915,16 @@ impl Workspace {
|
|||
);
|
||||
}
|
||||
|
||||
let focus_stack = self.focus_stack.get(last_active_seat);
|
||||
let any_fullscreen_animating = self
|
||||
.fullscreen_surfaces
|
||||
.iter()
|
||||
.any(|f| f.start_at.is_some() || f.ended_at.is_some());
|
||||
if !matches!(focus_stack.last(), Some(FocusTarget::Fullscreen(_)))
|
||||
|| any_fullscreen_animating
|
||||
|| self
|
||||
.fullscreen
|
||||
.as_ref()
|
||||
.map(|f| f.start_at.is_some() || f.ended_at.is_some())
|
||||
.unwrap_or(true)
|
||||
.fullscreen_surfaces
|
||||
.iter()
|
||||
.all(|f| !f.alive() || f.ended_at.is_some())
|
||||
{
|
||||
// floating surfaces
|
||||
let alpha = match &overview.0 {
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ use smithay::{
|
|||
MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent,
|
||||
},
|
||||
touch::{
|
||||
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
|
||||
UpEvent,
|
||||
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
|
||||
TouchTarget, UpEvent,
|
||||
},
|
||||
},
|
||||
output::Output,
|
||||
|
|
@ -259,10 +259,8 @@ impl ZoomState {
|
|||
original_position: Point<f64, Global>,
|
||||
movement: ZoomMovement,
|
||||
) {
|
||||
let cursor_position = cursor_position.to_i32_round();
|
||||
let original_position = original_position.to_i32_round();
|
||||
let output_geometry = output.geometry();
|
||||
let mut zoomed_output_geometry = output.zoomed_geometry().unwrap();
|
||||
let output_geometry = output.geometry().to_f64();
|
||||
let mut zoomed_output_geometry = output.zoomed_geometry().unwrap().to_f64();
|
||||
|
||||
let output_state = output.user_data().get::<Mutex<OutputZoomState>>().unwrap();
|
||||
let mut output_state_ref = output_state.lock().unwrap();
|
||||
|
|
@ -275,44 +273,40 @@ impl ZoomState {
|
|||
|
||||
let cursor_position = cursor_position.to_local(output);
|
||||
match movement {
|
||||
ZoomMovement::Continuously => output_state_ref.focal_point = cursor_position.to_f64(),
|
||||
ZoomMovement::Continuously => output_state_ref.focal_point = cursor_position,
|
||||
ZoomMovement::OnEdge => {
|
||||
if !zoomed_output_geometry
|
||||
.overlaps_or_touches(Rectangle::new(original_position, Size::from((16, 16))))
|
||||
.overlaps_or_touches(Rectangle::new(original_position, Size::from((16., 16.))))
|
||||
{
|
||||
zoomed_output_geometry.loc = cursor_position.to_global(output)
|
||||
- zoomed_output_geometry.size.downscale(2).to_point();
|
||||
- zoomed_output_geometry.size.downscale(2.).to_point();
|
||||
let mut focal_point = zoomed_output_geometry
|
||||
.loc
|
||||
.to_local(output)
|
||||
.upscale(
|
||||
output_geometry.size.w
|
||||
/ (output_geometry.size.w - zoomed_output_geometry.size.w),
|
||||
)
|
||||
.upscale(output_state_ref.level)
|
||||
.to_global(output);
|
||||
focal_point.x = focal_point.x.clamp(
|
||||
output_geometry.loc.x,
|
||||
output_geometry.loc.x + output_geometry.size.w - 1,
|
||||
output_geometry.loc.x + output_geometry.size.w - 1.,
|
||||
);
|
||||
focal_point.y = focal_point.y.clamp(
|
||||
output_geometry.loc.y,
|
||||
output_geometry.loc.y + output_geometry.size.h - 1,
|
||||
output_geometry.loc.y + output_geometry.size.h - 1.,
|
||||
);
|
||||
output_state_ref.previous_point =
|
||||
Some((output_state_ref.focal_point, Instant::now()));
|
||||
output_state_ref.focal_point = focal_point.to_local(output).to_f64();
|
||||
output_state_ref.focal_point = focal_point.to_local(output);
|
||||
} else if !zoomed_output_geometry.contains(cursor_position.to_global(output)) {
|
||||
let mut diff = output_state_ref.focal_point.to_global(output)
|
||||
+ (cursor_position.to_global(output) - original_position)
|
||||
.to_f64()
|
||||
.upscale(output_state_ref.level);
|
||||
diff.x = diff.x.clamp(
|
||||
output_geometry.loc.x as f64,
|
||||
((output_geometry.loc.x + output_geometry.size.w) as f64).next_down(),
|
||||
output_geometry.loc.x,
|
||||
(output_geometry.loc.x + output_geometry.size.w).next_down(),
|
||||
);
|
||||
diff.y = diff.y.clamp(
|
||||
output_geometry.loc.y as f64,
|
||||
((output_geometry.loc.y + output_geometry.size.h) as f64).next_down(),
|
||||
output_geometry.loc.y,
|
||||
(output_geometry.loc.y + output_geometry.size.h).next_down(),
|
||||
);
|
||||
diff -= output_state_ref.focal_point.to_global(output);
|
||||
|
||||
|
|
@ -321,28 +315,27 @@ impl ZoomState {
|
|||
}
|
||||
ZoomMovement::Centered => {
|
||||
zoomed_output_geometry.loc = cursor_position.to_global(output)
|
||||
- zoomed_output_geometry.size.downscale(2).to_point();
|
||||
- zoomed_output_geometry.size.downscale(2.).to_point();
|
||||
|
||||
let mut focal_point = zoomed_output_geometry
|
||||
.loc
|
||||
.to_local(output)
|
||||
.upscale(
|
||||
output_geometry
|
||||
.size
|
||||
.w
|
||||
.checked_div(output_geometry.size.w - zoomed_output_geometry.size.w)
|
||||
.unwrap_or(1),
|
||||
(output_geometry.size.w
|
||||
/ (output_geometry.size.w - zoomed_output_geometry.size.w)
|
||||
.max(f64::EPSILON))
|
||||
.max(1.),
|
||||
)
|
||||
.to_global(output);
|
||||
focal_point.x = focal_point.x.clamp(
|
||||
output_geometry.loc.x,
|
||||
output_geometry.loc.x + output_geometry.size.w - 1,
|
||||
output_geometry.loc.x + output_geometry.size.w - 1.,
|
||||
);
|
||||
focal_point.y = focal_point.y.clamp(
|
||||
output_geometry.loc.y,
|
||||
output_geometry.loc.y + output_geometry.size.h - 1,
|
||||
output_geometry.loc.y + output_geometry.size.h - 1.,
|
||||
);
|
||||
output_state_ref.focal_point = focal_point.to_local(output).to_f64();
|
||||
output_state_ref.focal_point = focal_point.to_local(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -512,7 +505,7 @@ impl Program for ZoomProgram {
|
|||
.padding(8)
|
||||
.class(theme::Container::custom(|theme| {
|
||||
let cosmic = theme.cosmic();
|
||||
let component = &cosmic.background.component;
|
||||
let component = &cosmic.background(false).component;
|
||||
iced_widget::container::Style {
|
||||
snap: true,
|
||||
icon_color: Some(component.on.into()),
|
||||
|
|
@ -1036,58 +1029,59 @@ impl PointerTarget<State> for ZoomFocusTarget {
|
|||
}
|
||||
|
||||
impl TouchTarget<State> for ZoomFocusTarget {
|
||||
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent, seq: Serial) {
|
||||
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent) {
|
||||
match self {
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::down(elem, seat, data, event, seq),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::down(elem, seat, data, event, seq),
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::down(elem, seat, data, event),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::down(elem, seat, data, event),
|
||||
}
|
||||
}
|
||||
|
||||
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent, seq: Serial) {
|
||||
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent) {
|
||||
match self {
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::up(elem, seat, data, event, seq),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::up(elem, seat, data, event, seq),
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::up(elem, seat, data, event),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::up(elem, seat, data, event),
|
||||
}
|
||||
}
|
||||
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent, seq: Serial) {
|
||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent) {
|
||||
match self {
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::motion(elem, seat, data, event, seq),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::motion(elem, seat, data, event, seq),
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::motion(elem, seat, data, event),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::motion(elem, seat, data, event),
|
||||
}
|
||||
}
|
||||
|
||||
fn frame(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
|
||||
fn frame(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
|
||||
match self {
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::frame(elem, seat, data, seq),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::frame(elem, seat, data, seq),
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::frame(elem, seat, data, frame),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::frame(elem, seat, data, frame),
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
|
||||
fn cancel(&self, seat: &Seat<State>, data: &mut State, frame: FrameMarker) {
|
||||
match self {
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::cancel(elem, seat, data, seq),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::cancel(elem, seat, data, seq),
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::cancel(elem, seat, data, frame),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::cancel(elem, seat, data, frame),
|
||||
}
|
||||
}
|
||||
|
||||
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent, seq: Serial) {
|
||||
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent) {
|
||||
match self {
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::shape(elem, seat, data, event, seq),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::shape(elem, seat, data, event, seq),
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::shape(elem, seat, data, event),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::shape(elem, seat, data, event),
|
||||
}
|
||||
}
|
||||
|
||||
fn orientation(
|
||||
&self,
|
||||
seat: &Seat<State>,
|
||||
data: &mut State,
|
||||
event: &OrientationEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
fn orientation(&self, seat: &Seat<State>, data: &mut State, event: &OrientationEvent) {
|
||||
match self {
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::orientation(elem, seat, data, event, seq),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::orientation(elem, seat, data, event, seq),
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::orientation(elem, seat, data, event),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::orientation(elem, seat, data, event),
|
||||
}
|
||||
}
|
||||
|
||||
fn last_frame(&self, seat: &Seat<State>, data: &mut State) -> Option<FrameMarker> {
|
||||
match self {
|
||||
ZoomFocusTarget::Main(elem) => TouchTarget::last_frame(elem, seat, data),
|
||||
ZoomFocusTarget::Menu(elem) => TouchTarget::last_frame(elem, seat, data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
54
src/state.rs
54
src/state.rs
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
x11::X11State,
|
||||
},
|
||||
config::{CompOutputConfig, Config, ScreenFilter},
|
||||
dbus::a11y_keyboard_monitor::A11yKeyboardMonitorState,
|
||||
dbus::DBusState,
|
||||
input::{PointerFocusState, gestures::GestureState},
|
||||
shell::{CosmicSurface, SeatExt, Shell, grabs::SeatMoveGrabState},
|
||||
utils::prelude::OutputExt,
|
||||
|
|
@ -32,7 +32,6 @@ use crate::{
|
|||
use anyhow::Context;
|
||||
use calloop::RegistrationToken;
|
||||
use cosmic_comp_config::output::comp::{OutputConfig, OutputState};
|
||||
use futures_executor::ThreadPool;
|
||||
use i18n_embed::{
|
||||
DesktopLanguageRequester,
|
||||
fluent::{FluentLanguageLoader, fluent_language_loader},
|
||||
|
|
@ -87,6 +86,7 @@ use smithay::{
|
|||
output::OutputManagerState,
|
||||
pointer_constraints::PointerConstraintsState,
|
||||
pointer_gestures::PointerGesturesState,
|
||||
pointer_warp::PointerWarpManager,
|
||||
presentation::PresentationState,
|
||||
seat::WaylandFocus,
|
||||
security_context::{SecurityContext, SecurityContextState},
|
||||
|
|
@ -117,7 +117,7 @@ use smithay::{
|
|||
};
|
||||
use tracing::warn;
|
||||
|
||||
#[cfg(feature = "systemd")]
|
||||
#[cfg(feature = "logind")]
|
||||
use std::os::fd::OwnedFd;
|
||||
|
||||
use std::{
|
||||
|
|
@ -234,7 +234,6 @@ pub struct Common {
|
|||
pub display_handle: DisplayHandle,
|
||||
pub event_loop_handle: LoopHandle<'static, State>,
|
||||
pub event_loop_signal: LoopSignal,
|
||||
pub async_executor: ThreadPool,
|
||||
|
||||
pub popups: PopupManager,
|
||||
pub shell: Arc<parking_lot::RwLock<Shell>>,
|
||||
|
|
@ -279,7 +278,7 @@ pub struct Common {
|
|||
pub xdg_decoration_state: XdgDecorationState,
|
||||
pub overlap_notify_state: OverlapNotifyState,
|
||||
pub a11y_state: A11yState,
|
||||
pub a11y_keyboard_monitor_state: A11yKeyboardMonitorState,
|
||||
pub dbus_state: DBusState,
|
||||
|
||||
// shell-related wayland state
|
||||
pub xdg_shell_state: XdgShellState,
|
||||
|
|
@ -294,7 +293,7 @@ pub struct Common {
|
|||
pub xwayland_shell_state: XWaylandShellState,
|
||||
pub pointer_focus_state: Option<PointerFocusState>,
|
||||
|
||||
#[cfg(feature = "systemd")]
|
||||
#[cfg(feature = "logind")]
|
||||
pub inhibit_lid_fd: Option<OwnedFd>,
|
||||
|
||||
pub with_xwayland: bool,
|
||||
|
|
@ -673,6 +672,7 @@ impl State {
|
|||
XWaylandKeyboardGrabState::new::<Self>(dh);
|
||||
let xwayland_shell_state = XWaylandShellState::new::<Self>(dh);
|
||||
PointerConstraintsState::new::<Self>(dh);
|
||||
PointerWarpManager::new::<Self>(dh);
|
||||
PointerGesturesState::new::<Self>(dh);
|
||||
TabletManagerState::new::<Self>(dh);
|
||||
SecurityContextState::new::<Self, _>(dh, client_has_no_security_context);
|
||||
|
|
@ -727,15 +727,9 @@ impl State {
|
|||
);
|
||||
let workspace_state = WorkspaceState::new(dh, client_not_sandboxed);
|
||||
|
||||
let async_executor = ThreadPool::builder().pool_size(1).create().unwrap();
|
||||
|
||||
if let Err(err) = crate::dbus::init(&handle, &async_executor) {
|
||||
tracing::warn!(?err, "Failed to initialize dbus handlers");
|
||||
}
|
||||
|
||||
let a11y_state = A11yState::new::<State, _>(dh, client_not_sandboxed);
|
||||
|
||||
let a11y_keyboard_monitor_state = A11yKeyboardMonitorState::new(&async_executor);
|
||||
let dbus_state = DBusState::init(&handle);
|
||||
|
||||
State {
|
||||
common: Common {
|
||||
|
|
@ -744,7 +738,6 @@ impl State {
|
|||
display_handle: dh.clone(),
|
||||
event_loop_handle: handle,
|
||||
event_loop_signal: signal,
|
||||
async_executor,
|
||||
|
||||
popups: PopupManager::default(),
|
||||
shell,
|
||||
|
|
@ -794,13 +787,13 @@ impl State {
|
|||
xdg_foreign_state,
|
||||
workspace_state,
|
||||
a11y_state,
|
||||
a11y_keyboard_monitor_state,
|
||||
xwayland_scale: None,
|
||||
xwayland_state: None,
|
||||
xwayland_shell_state,
|
||||
pointer_focus_state: None,
|
||||
dbus_state,
|
||||
|
||||
#[cfg(feature = "systemd")]
|
||||
#[cfg(feature = "logind")]
|
||||
inhibit_lid_fd: None,
|
||||
|
||||
with_xwayland,
|
||||
|
|
@ -825,7 +818,7 @@ impl State {
|
|||
}
|
||||
|
||||
fn update_inhibitor_locks(&mut self) {
|
||||
#[cfg(feature = "systemd")]
|
||||
#[cfg(feature = "logind")]
|
||||
{
|
||||
use smithay::backend::session::Session;
|
||||
use tracing::{debug, error, warn};
|
||||
|
|
@ -841,7 +834,7 @@ impl State {
|
|||
|
||||
if should_handle_lid {
|
||||
if self.common.inhibit_lid_fd.is_none() {
|
||||
match crate::dbus::logind::inhibit_lid() {
|
||||
match crate::dbus::logind::inhibit_lid(&self.common) {
|
||||
Ok(fd) => {
|
||||
debug!("Inhibiting lid switch");
|
||||
self.common.inhibit_lid_fd = Some(fd);
|
||||
|
|
@ -852,7 +845,8 @@ impl State {
|
|||
.iter()
|
||||
.find(|o| o.is_internal())
|
||||
.cloned();
|
||||
let closed = crate::dbus::logind::lid_closed().unwrap_or(false);
|
||||
let closed =
|
||||
crate::dbus::logind::lid_closed(&self.common).unwrap_or(false);
|
||||
|
||||
if closed {
|
||||
backend.disable_internal_output(
|
||||
|
|
@ -996,8 +990,8 @@ impl Common {
|
|||
|
||||
// normal windows
|
||||
for space in shell.workspaces.spaces() {
|
||||
if let Some(window) = space.get_fullscreen() {
|
||||
window.with_surfaces(processor);
|
||||
if let Some(fs) = space.get_fullscreen(shell.seats.last_active()) {
|
||||
fs.surface.with_surfaces(processor);
|
||||
}
|
||||
space.mapped().for_each(|mapped| {
|
||||
for (window, _) in mapped.windows() {
|
||||
|
|
@ -1162,15 +1156,16 @@ impl Common {
|
|||
});
|
||||
|
||||
if let Some(active) = shell.active_space(output) {
|
||||
if let Some(window) = active.get_fullscreen()
|
||||
&& let Some(feedback) = window
|
||||
if let Some(fs) = active.get_fullscreen(shell.seats.last_active())
|
||||
&& let Some(feedback) = fs
|
||||
.surface
|
||||
.wl_surface()
|
||||
.and_then(|wl_surface| {
|
||||
advertised_node_for_surface(&wl_surface, &self.display_handle)
|
||||
})
|
||||
.and_then(&mut dmabuf_feedback)
|
||||
{
|
||||
window.send_dmabuf_feedback(
|
||||
fs.surface.send_dmabuf_feedback(
|
||||
output,
|
||||
&feedback,
|
||||
render_element_states,
|
||||
|
|
@ -1356,8 +1351,9 @@ impl Common {
|
|||
});
|
||||
|
||||
if let Some(active) = shell.active_space(output) {
|
||||
if let Some(window) = active.get_fullscreen() {
|
||||
window.send_frame(output, time, throttle(window), should_send);
|
||||
if let Some(fs) = active.get_fullscreen(shell.seats.last_active()) {
|
||||
fs.surface
|
||||
.send_frame(output, time, throttle(&fs.surface), should_send);
|
||||
}
|
||||
active.mapped().for_each(|mapped| {
|
||||
for (window, _) in mapped.windows() {
|
||||
|
|
@ -1377,9 +1373,9 @@ impl Common {
|
|||
.spaces_for_output(output)
|
||||
.filter(|w| w.handle != active.handle)
|
||||
{
|
||||
if let Some(window) = space.get_fullscreen() {
|
||||
let throttle = min(throttle(space), throttle(window));
|
||||
window.send_frame(output, time, throttle, |_, _| None);
|
||||
if let Some(fs) = space.get_fullscreen(shell.seats.last_active()) {
|
||||
let throttle = min(throttle(space), throttle(&fs.surface));
|
||||
fs.surface.send_frame(output, time, throttle, |_, _| None);
|
||||
}
|
||||
space.mapped().for_each(|mapped| {
|
||||
for (window, _) in mapped.windows() {
|
||||
|
|
|
|||
|
|
@ -51,13 +51,12 @@ use smithay::{
|
|||
PointerTarget, RelativeMotionEvent,
|
||||
},
|
||||
touch::{
|
||||
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
|
||||
UpEvent,
|
||||
DownEvent, FrameMarker, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent,
|
||||
TouchTarget, UpEvent,
|
||||
},
|
||||
},
|
||||
output::Output,
|
||||
reexports::calloop::RegistrationToken,
|
||||
reexports::calloop::{self, LoopHandle, futures::Scheduler},
|
||||
reexports::calloop::{self, LoopHandle, RegistrationToken, futures::Scheduler},
|
||||
utils::{
|
||||
Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size,
|
||||
Transform,
|
||||
|
|
@ -180,6 +179,8 @@ pub(crate) struct IcedElementInternal<P: Program + Send + 'static> {
|
|||
last_seat: Arc<Mutex<Option<(Seat<crate::state::State>, Serial)>>>,
|
||||
cursor_pos: Option<Point<f64, Logical>>,
|
||||
touch_map: HashMap<Finger, IcedPoint>,
|
||||
last_touch_frame: Option<FrameMarker>,
|
||||
last_touch_serial: Option<Serial>,
|
||||
|
||||
// iced
|
||||
theme: Theme,
|
||||
|
|
@ -228,6 +229,8 @@ impl<P: Program + Send + Clone + 'static> Clone for IcedElementInternal<P> {
|
|||
last_seat: self.last_seat.clone(),
|
||||
cursor_pos: self.cursor_pos,
|
||||
touch_map: self.touch_map.clone(),
|
||||
last_touch_frame: None,
|
||||
last_touch_serial: None,
|
||||
theme: self.theme.clone(),
|
||||
renderer,
|
||||
state,
|
||||
|
|
@ -253,6 +256,8 @@ impl<P: Program + Send + 'static> fmt::Debug for IcedElementInternal<P> {
|
|||
.field("last_seat", &self.last_seat)
|
||||
.field("cursor_pos", &self.cursor_pos)
|
||||
.field("touch_map", &self.touch_map)
|
||||
.field("last_touch_frame", &self.last_touch_frame)
|
||||
.field("last_touch_serial", &self.last_touch_serial)
|
||||
.field("theme", &"...")
|
||||
.field("renderer", &"...")
|
||||
.field("state", &"...")
|
||||
|
|
@ -309,6 +314,8 @@ impl<P: Program + Send + 'static> IcedElement<P> {
|
|||
cursor_pos: None,
|
||||
last_seat,
|
||||
touch_map: HashMap::new(),
|
||||
last_touch_frame: None,
|
||||
last_touch_serial: None,
|
||||
theme,
|
||||
renderer,
|
||||
state,
|
||||
|
|
@ -633,7 +640,6 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
|
|||
seat: &Seat<crate::state::State>,
|
||||
_data: &mut crate::state::State,
|
||||
event: &DownEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
let mut internal = self.0.lock().unwrap();
|
||||
let id = Finger(i32::from(event.slot) as u64);
|
||||
|
|
@ -644,7 +650,8 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
|
|||
.queue_event(Event::Touch(TouchEvent::FingerPressed { id, position }));
|
||||
internal.touch_map.insert(id, position);
|
||||
internal.cursor_pos = Some(event_location);
|
||||
*internal.last_seat.lock().unwrap() = Some((seat.clone(), seq));
|
||||
internal.last_touch_serial = Some(event.serial);
|
||||
*internal.last_seat.lock().unwrap() = Some((seat.clone(), event.serial));
|
||||
internal.update(false);
|
||||
}
|
||||
|
||||
|
|
@ -653,12 +660,12 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
|
|||
seat: &Seat<crate::state::State>,
|
||||
_data: &mut crate::state::State,
|
||||
event: &UpEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
let mut internal = self.0.lock().unwrap();
|
||||
let id = Finger(i32::from(event.slot) as u64);
|
||||
if let Some(position) = internal.touch_map.remove(&id) {
|
||||
*internal.last_seat.lock().unwrap() = Some((seat.clone(), seq));
|
||||
*internal.last_seat.lock().unwrap() =
|
||||
Some((seat.clone(), internal.last_touch_serial.unwrap()));
|
||||
internal
|
||||
.state
|
||||
.queue_event(Event::Touch(TouchEvent::FingerLifted { id, position }));
|
||||
|
|
@ -671,13 +678,13 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
|
|||
seat: &Seat<crate::state::State>,
|
||||
_data: &mut crate::state::State,
|
||||
event: &TouchMotionEvent,
|
||||
seq: Serial,
|
||||
) {
|
||||
let mut internal = self.0.lock().unwrap();
|
||||
let id = Finger(i32::from(event.slot) as u64);
|
||||
let event_location = event.location.downscale(internal.additional_scale);
|
||||
let position = IcedPoint::new(event_location.x as f32, event_location.y as f32);
|
||||
*internal.last_seat.lock().unwrap() = Some((seat.clone(), seq));
|
||||
*internal.last_seat.lock().unwrap() =
|
||||
Some((seat.clone(), internal.last_touch_serial.unwrap()));
|
||||
internal
|
||||
.state
|
||||
.queue_event(Event::Touch(TouchEvent::FingerMoved { id, position }));
|
||||
|
|
@ -690,17 +697,19 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
|
|||
&self,
|
||||
_seat: &Seat<crate::state::State>,
|
||||
_data: &mut crate::state::State,
|
||||
_seq: Serial,
|
||||
frame: FrameMarker,
|
||||
) {
|
||||
self.0.lock().unwrap().last_touch_frame = Some(frame);
|
||||
}
|
||||
|
||||
fn cancel(
|
||||
&self,
|
||||
_seat: &Seat<crate::state::State>,
|
||||
_data: &mut crate::state::State,
|
||||
_seq: Serial,
|
||||
frame: FrameMarker,
|
||||
) {
|
||||
let mut internal = self.0.lock().unwrap();
|
||||
internal.last_touch_frame = Some(frame);
|
||||
for (id, position) in std::mem::take(&mut internal.touch_map) {
|
||||
internal
|
||||
.state
|
||||
|
|
@ -714,7 +723,6 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
|
|||
_seat: &Seat<crate::state::State>,
|
||||
_data: &mut crate::state::State,
|
||||
_event: &ShapeEvent,
|
||||
_seq: Serial,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -723,9 +731,16 @@ impl<P: Program + Send + 'static> TouchTarget<crate::state::State> for IcedEleme
|
|||
_seat: &Seat<crate::state::State>,
|
||||
_data: &mut crate::state::State,
|
||||
_event: &OrientationEvent,
|
||||
_seq: Serial,
|
||||
) {
|
||||
}
|
||||
|
||||
fn last_frame(
|
||||
&self,
|
||||
_seat: &Seat<crate::state::State>,
|
||||
_data: &mut crate::state::State,
|
||||
) -> Option<FrameMarker> {
|
||||
self.0.lock().unwrap().last_touch_frame
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Program + Send + 'static> KeyboardTarget<crate::state::State> for IcedElement<P> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use smithay::{desktop::layer_map_for_output, output::Output};
|
||||
use smithay::{
|
||||
backend::renderer::utils::with_renderer_surface_state, desktop::layer_map_for_output,
|
||||
output::Output,
|
||||
};
|
||||
|
||||
/// Layer shell namespace used by `cosmic-workspaces`
|
||||
// TODO: Avoid special case, or add protocol to expose required behavior
|
||||
|
|
@ -10,5 +13,14 @@ pub const WORKSPACE_OVERVIEW_NAMESPACE: &str = "cosmic-workspace-overview";
|
|||
pub fn workspace_overview_is_open(output: &Output) -> bool {
|
||||
layer_map_for_output(output)
|
||||
.layers()
|
||||
.any(|s| s.namespace() == WORKSPACE_OVERVIEW_NAMESPACE)
|
||||
.filter(|s| s.namespace() == WORKSPACE_OVERVIEW_NAMESPACE)
|
||||
// Only consider the overview open once it has committed a buffer. The
|
||||
// surface is inserted into the layer map on its initial (bufferless)
|
||||
// commit, so checking for the namespace alone hides all toplevels for a
|
||||
// frame before the overview has anything to draw, briefly flashing the
|
||||
// bare wallpaper.
|
||||
.any(|s| {
|
||||
with_renderer_surface_state(s.wl_surface(), |state| state.buffer().is_some())
|
||||
.unwrap_or(false)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,9 +51,11 @@ impl DmabufHandler for State {
|
|||
let is_fullscreen = shell
|
||||
.workspaces
|
||||
.space_for_handle(&handle)?
|
||||
.fullscreen
|
||||
.as_ref()
|
||||
.is_some_and(|f| f.surface.has_surface(surface, WindowSurfaceType::all()));
|
||||
.fullscreen_surfaces
|
||||
.iter()
|
||||
.any(|f| {
|
||||
f.ended_at.is_none() && f.surface.has_surface(surface, WindowSurfaceType::all())
|
||||
});
|
||||
|
||||
let node = kms
|
||||
.drm_devices
|
||||
|
|
|
|||
|
|
@ -753,11 +753,15 @@ pub fn render_cursor_to_buffer(
|
|||
seat: &Seat<State>,
|
||||
) {
|
||||
let buffer = frame.buffer();
|
||||
let cursor_size = seat
|
||||
let mut cursor_size = seat
|
||||
.cursor_geometry((0.0, 0.0), state.common.clock.now())
|
||||
.map(|(geo, _hotspot)| geo.size)
|
||||
.unwrap_or_else(|| Size::from((64, 64)));
|
||||
let buffer_size = buffer_dimensions(&buffer).unwrap();
|
||||
// Client shouldn't try to allocate 0x0 buffer
|
||||
if cursor_size == Size::new(0, 0) {
|
||||
cursor_size = Size::new(1, 1);
|
||||
}
|
||||
if buffer_size != cursor_size {
|
||||
let constraints = BufferConstraints {
|
||||
size: cursor_size,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ pub mod output_configuration;
|
|||
pub mod output_power;
|
||||
pub mod overlap_notify;
|
||||
pub mod pointer_constraints;
|
||||
pub mod pointer_warp;
|
||||
pub mod primary_selection;
|
||||
pub mod seat;
|
||||
pub mod security_context;
|
||||
|
|
|
|||
|
|
@ -1,35 +1,123 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::state::State;
|
||||
use crate::{shell::CosmicSurface, state::State, utils::prelude::*};
|
||||
use smithay::{
|
||||
input::pointer::PointerHandle,
|
||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||
utils::{Logical, Point},
|
||||
wayland::{
|
||||
pointer_constraints::{PointerConstraintsHandler, with_pointer_constraint},
|
||||
seat::WaylandFocus,
|
||||
},
|
||||
wayland::{pointer_constraints::PointerConstraintsHandler, seat::WaylandFocus},
|
||||
};
|
||||
|
||||
pub use smithay::wayland::pointer_constraints::{PointerConstraintRef, with_pointer_constraint};
|
||||
|
||||
impl PointerConstraintsHandler for State {
|
||||
fn new_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle<Self>) {
|
||||
// XXX region
|
||||
if pointer
|
||||
.current_focus()
|
||||
.is_some_and(|x| x.wl_surface().as_deref() == Some(surface))
|
||||
{
|
||||
let seat = self
|
||||
.common
|
||||
.shell
|
||||
.read()
|
||||
.seats
|
||||
.iter()
|
||||
.find(|s| s.get_pointer().as_ref() == Some(pointer))
|
||||
.cloned();
|
||||
|
||||
let (is_under, is_focused, surface_location) = if let Some(seat) = seat {
|
||||
seat.set_pointer_constraint_hint(None);
|
||||
let current_output = seat.active_output();
|
||||
let position = seat.get_pointer().unwrap().current_location().as_global();
|
||||
let shell = self.common.shell.read();
|
||||
let under = State::surface_under(position, ¤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| {
|
||||
constraint.unwrap().activate();
|
||||
if let Some(constraint) = constraint {
|
||||
if let Some(region) = constraint.region() {
|
||||
if let Some(surface_location) = surface_location
|
||||
&& let position = pointer.current_location()
|
||||
&& let point = (position - surface_location.as_logical()).to_i32_round()
|
||||
&& region.contains(point)
|
||||
{
|
||||
constraint.activate();
|
||||
}
|
||||
} else {
|
||||
constraint.activate();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle<Self>) {
|
||||
if with_pointer_constraint(surface, pointer, |constraint| constraint.is_none()) {
|
||||
let seat = self
|
||||
.common
|
||||
.shell
|
||||
.read()
|
||||
.seats
|
||||
.iter()
|
||||
.find(|s| s.get_pointer().as_ref() == Some(pointer))
|
||||
.cloned();
|
||||
|
||||
if let Some(seat) = seat
|
||||
&& let Some((hint_surface, hint_location)) = seat.pointer_constraint_hint()
|
||||
&& hint_surface == *surface
|
||||
{
|
||||
self.apply_cursor_hint(surface, pointer, hint_location);
|
||||
seat.set_pointer_constraint_hint(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cursor_position_hint(
|
||||
&mut self,
|
||||
_surface: &WlSurface,
|
||||
_pointer: &PointerHandle<Self>,
|
||||
_location: Point<f64, Logical>,
|
||||
surface: &WlSurface,
|
||||
pointer: &PointerHandle<Self>,
|
||||
location: Point<f64, Logical>,
|
||||
) {
|
||||
// TODO
|
||||
if with_pointer_constraint(surface, pointer, |constraint| {
|
||||
constraint.is_some_and(|c| c.is_active())
|
||||
}) {
|
||||
let seat = self
|
||||
.common
|
||||
.shell
|
||||
.read()
|
||||
.seats
|
||||
.iter()
|
||||
.find(|s| s.get_pointer().as_ref() == Some(pointer))
|
||||
.cloned();
|
||||
|
||||
if let Some(seat) = seat {
|
||||
seat.set_pointer_constraint_hint(Some((surface.clone(), location)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
44
src/wayland/handlers/pointer_warp.rs
Normal file
44
src/wayland/handlers/pointer_warp.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// 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)
|
||||
.enumerate()
|
||||
.find(|(_, w)| {
|
||||
w.get_fullscreen().is_some_and(|f| f == window)
|
||||
w.get_fullscreen_surfaces().any(|f| &f.surface == window)
|
||||
|| w.mapped()
|
||||
.flat_map(|m| m.windows().map(|(s, _)| s))
|
||||
.any(|w| &w == window)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use crate::{
|
|||
state::State,
|
||||
wayland::protocols::workspace::{State as WState, WorkspaceHandle},
|
||||
};
|
||||
use cosmic_comp_config::ActivationPolicy;
|
||||
use smithay::{
|
||||
input::Seat,
|
||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||
|
|
@ -20,6 +21,10 @@ pub enum ActivationContext {
|
|||
Workspace(WorkspaceHandle),
|
||||
}
|
||||
|
||||
// It may happen that we get activation-requests, while all outputs are disabled.
|
||||
// In these cases we won't be able to determine workspaces for windows and thus
|
||||
// need to handle the corresponding code paths defensively.
|
||||
|
||||
impl XdgActivationHandler for State {
|
||||
fn activation_state(&mut self) -> &mut XdgActivationState {
|
||||
&mut self.common.xdg_activation_state
|
||||
|
|
@ -51,7 +56,12 @@ impl XdgActivationHandler for State {
|
|||
});
|
||||
let output = seat.active_output();
|
||||
let mut shell = self.common.shell.write();
|
||||
let workspace = shell.active_space_mut(&output).unwrap();
|
||||
let Some(workspace) = shell.active_space_mut(&output) else {
|
||||
debug!(?token, "created urgent token for privileged client");
|
||||
data.user_data
|
||||
.insert_if_missing(move || ActivationContext::UrgentOnly);
|
||||
return true;
|
||||
};
|
||||
let handle = workspace.handle;
|
||||
data.user_data
|
||||
.insert_if_missing(move || ActivationContext::Workspace(handle));
|
||||
|
|
@ -85,7 +95,11 @@ impl XdgActivationHandler for State {
|
|||
if valid {
|
||||
let output = seat.active_output();
|
||||
let mut shell = self.common.shell.write();
|
||||
let workspace = shell.active_space_mut(&output).unwrap();
|
||||
let Some(workspace) = shell.active_space_mut(&output) else {
|
||||
data.user_data
|
||||
.insert_if_missing(|| ActivationContext::UrgentOnly);
|
||||
return true;
|
||||
};
|
||||
let handle = workspace.handle;
|
||||
data.user_data
|
||||
.insert_if_missing(move || ActivationContext::Workspace(handle));
|
||||
|
|
@ -115,10 +129,44 @@ impl XdgActivationHandler for State {
|
|||
}
|
||||
}
|
||||
ActivationContext::Workspace(_) => {
|
||||
self.activate_surface(
|
||||
&surface,
|
||||
Some((ActivationKey::Wayland(surface.clone()), *context)),
|
||||
);
|
||||
match self.common.config.cosmic_conf.activation_policy {
|
||||
ActivationPolicy::Focus => {
|
||||
self.activate_surface(
|
||||
&surface,
|
||||
Some((ActivationKey::Wayland(surface.clone()), *context)),
|
||||
);
|
||||
}
|
||||
ActivationPolicy::FocusIfActiveWorkspace => {
|
||||
let shell = self.common.shell.write();
|
||||
|
||||
let Some((target_workspace, _)) = shell.workspace_for_surface(&surface)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let seat = shell.seats.last_active().clone();
|
||||
let current_output = seat.active_output();
|
||||
let current_workspace = shell.active_space(¤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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -219,8 +267,8 @@ impl State {
|
|||
.workspaces
|
||||
.space_for_handle(&workspace)
|
||||
.unwrap()
|
||||
.get_fullscreen()
|
||||
.cloned()
|
||||
.get_fullscreen(&seat)
|
||||
.map(|f| f.surface.clone())
|
||||
.map(KeyboardFocusTarget::Fullscreen)
|
||||
else {
|
||||
return;
|
||||
|
|
@ -232,8 +280,8 @@ impl State {
|
|||
if let Some(surface) = shell
|
||||
.workspaces
|
||||
.space_for_handle(&workspace)
|
||||
.and_then(|w| w.get_fullscreen())
|
||||
.cloned()
|
||||
.and_then(|w| w.get_fullscreen(&seat))
|
||||
.map(|f| f.surface.clone())
|
||||
{
|
||||
shell.append_focus_stack(surface, &seat)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ impl Shell {
|
|||
unconstrain_xdg_popup(surface, window_loc, output.geometry());
|
||||
}
|
||||
} else if let Some(output) = self.workspaces.spaces().find_map(|w| {
|
||||
w.fullscreen.as_ref().and_then(|f| {
|
||||
w.fullscreen_surfaces.iter().find_map(|f| {
|
||||
(f.surface.wl_surface().as_deref() == Some(&parent)).then_some(w.output())
|
||||
})
|
||||
}) {
|
||||
|
|
|
|||
|
|
@ -495,35 +495,18 @@ where
|
|||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
let foreign_toplevel_handle = state.foreign_handle.as_ref();
|
||||
let mut changed = false;
|
||||
|
||||
if handle_state.title != window.title() {
|
||||
handle_state.title = window.title();
|
||||
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
|
||||
instance.title(handle_state.title.clone());
|
||||
}
|
||||
if let Some(handle) = foreign_toplevel_handle {
|
||||
handle.send_title(&handle_state.title);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
if handle_state.app_id != window.app_id() {
|
||||
handle_state.app_id = window.app_id();
|
||||
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
|
||||
instance.app_id(handle_state.app_id.clone());
|
||||
}
|
||||
if let Some(handle) = foreign_toplevel_handle {
|
||||
handle.send_app_id(&handle_state.app_id);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
let new_title = (handle_state.title != window.title()).then(|| window.title());
|
||||
let new_app_id = (handle_state.app_id != window.app_id()).then(|| window.app_id());
|
||||
|
||||
if handle_state.states.as_ref().is_none_or(|states| {
|
||||
let new_states = if handle_state.states.as_ref().is_none_or(|states| {
|
||||
(states.contains(&States::Maximized) != window.is_maximized())
|
||||
|| (states.contains(&States::Fullscreen) != window.is_fullscreen())
|
||||
|| (states.contains(&States::Activated) != window.is_activated())
|
||||
|| (states.contains(&States::Minimized) != window.is_minimized())
|
||||
|| (instance.version() >= zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE
|
||||
&& states.contains(&States::Sticky) != window.is_sticky())
|
||||
}) {
|
||||
let mut states = Vec::new();
|
||||
if window.is_maximized() {
|
||||
|
|
@ -543,8 +526,64 @@ where
|
|||
{
|
||||
states.push(States::Sticky);
|
||||
}
|
||||
handle_state.states = Some(states.clone());
|
||||
Some(states)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let geometry_changed = if !window.is_resizing() {
|
||||
let geometry = window.global_geometry();
|
||||
if handle_state.geometry != geometry {
|
||||
handle_state.geometry = geometry;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let outputs_changed = state.outputs != handle_state.outputs
|
||||
|| handle_state.wl_outputs.iter().any(|o| !o.is_alive());
|
||||
|
||||
let workspaces_changed = state.workspaces != handle_state.workspaces;
|
||||
|
||||
if new_title.is_none()
|
||||
&& new_app_id.is_none()
|
||||
&& new_states.is_none()
|
||||
&& !geometry_changed
|
||||
&& !outputs_changed
|
||||
&& !workspaces_changed
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
let foreign_toplevel_handle = state.foreign_handle.as_ref();
|
||||
|
||||
if let Some(title) = new_title {
|
||||
handle_state.title = title;
|
||||
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
|
||||
instance.title(handle_state.title.clone());
|
||||
}
|
||||
if let Some(handle) = foreign_toplevel_handle {
|
||||
handle.send_title(&handle_state.title);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if let Some(app_id) = new_app_id {
|
||||
handle_state.app_id = app_id;
|
||||
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
|
||||
instance.app_id(handle_state.app_id.clone());
|
||||
}
|
||||
if let Some(handle) = foreign_toplevel_handle {
|
||||
handle.send_app_id(&handle_state.app_id);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if let Some(states) = new_states {
|
||||
handle_state.states = Some(states.clone());
|
||||
let states = states
|
||||
.iter()
|
||||
.flat_map(|state| (*state as u32).to_ne_bytes())
|
||||
|
|
@ -553,17 +592,9 @@ where
|
|||
changed = true;
|
||||
}
|
||||
|
||||
let mut geometry_changed = false;
|
||||
if !window.is_resizing() {
|
||||
let geometry = window.global_geometry();
|
||||
if handle_state.geometry != geometry {
|
||||
handle_state.geometry = geometry;
|
||||
changed = true;
|
||||
geometry_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(client) = dh.get_client(instance.id()) {
|
||||
if (outputs_changed || geometry_changed)
|
||||
&& let Ok(client) = dh.get_client(instance.id())
|
||||
{
|
||||
handle_state.outputs = state.outputs.clone();
|
||||
|
||||
let handle_state = &mut *handle_state;
|
||||
|
|
@ -599,27 +630,29 @@ where
|
|||
});
|
||||
}
|
||||
|
||||
for new_workspace in state
|
||||
.workspaces
|
||||
.iter()
|
||||
.filter(|w| !handle_state.workspaces.contains(w))
|
||||
{
|
||||
for handle in workspace_state.raw_ext_workspace_handles(new_workspace, &instance.id()) {
|
||||
instance.ext_workspace_enter(handle);
|
||||
changed = true;
|
||||
if workspaces_changed {
|
||||
for new_workspace in state
|
||||
.workspaces
|
||||
.iter()
|
||||
.filter(|w| !handle_state.workspaces.contains(w))
|
||||
{
|
||||
for handle in workspace_state.raw_ext_workspace_handles(new_workspace, &instance.id()) {
|
||||
instance.ext_workspace_enter(handle);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for old_workspace in handle_state
|
||||
.workspaces
|
||||
.iter()
|
||||
.filter(|w| !state.workspaces.contains(w))
|
||||
{
|
||||
for handle in workspace_state.raw_ext_workspace_handles(old_workspace, &instance.id()) {
|
||||
instance.ext_workspace_leave(handle);
|
||||
changed = true;
|
||||
for old_workspace in handle_state
|
||||
.workspaces
|
||||
.iter()
|
||||
.filter(|w| !state.workspaces.contains(w))
|
||||
{
|
||||
for handle in workspace_state.raw_ext_workspace_handles(old_workspace, &instance.id()) {
|
||||
instance.ext_workspace_leave(handle);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
handle_state.workspaces = state.workspaces.clone();
|
||||
}
|
||||
handle_state.workspaces = state.workspaces.clone();
|
||||
|
||||
if changed {
|
||||
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
|
||||
|
|
|
|||
|
|
@ -237,11 +237,10 @@ where
|
|||
window_from_handle::<<D as ToplevelInfoHandler>::Window>(toplevel).unwrap();
|
||||
if let Some(toplevel_state) = window.user_data().get::<ToplevelState>() {
|
||||
let mut toplevel_state = toplevel_state.lock().unwrap();
|
||||
if width == 0 && height == 0 {
|
||||
toplevel_state
|
||||
.rectangles
|
||||
.retain(|(s, _)| s.id() != surface.id());
|
||||
} else {
|
||||
toplevel_state
|
||||
.rectangles
|
||||
.retain(|(s, _)| s.id() != surface.id());
|
||||
if width != 0 || height != 0 {
|
||||
toplevel_state.rectangles.push((
|
||||
surface.downgrade(),
|
||||
Rectangle::new((x, y).into(), (width, height).into()),
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ impl State {
|
|||
&self.common.display_handle,
|
||||
None,
|
||||
std::iter::empty::<(OsString, OsString)>(),
|
||||
std::iter::empty::<OsString>(),
|
||||
true,
|
||||
Stdio::null(),
|
||||
Stdio::null(),
|
||||
|
|
@ -575,16 +576,15 @@ impl Common {
|
|||
.filter(|(i, _)| *i != set.active),
|
||||
)
|
||||
.flat_map(|(_, workspace)| {
|
||||
let focus_last =
|
||||
workspace.focus_stack.get(seat).last().cloned();
|
||||
workspace
|
||||
.get_fullscreen()
|
||||
.get_fullscreen_surfaces()
|
||||
.filter(|f| {
|
||||
workspace
|
||||
.focus_stack
|
||||
.get(seat)
|
||||
.last()
|
||||
.is_some_and(|t| &t == f)
|
||||
focus_last.as_ref().is_some_and(|t| t == &f.surface)
|
||||
})
|
||||
.cloned()
|
||||
.map(|f| f.surface.clone())
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.chain(workspace.mapped().flat_map(|mapped| {
|
||||
let active = mapped.active_window();
|
||||
|
|
@ -603,15 +603,14 @@ impl Common {
|
|||
}))
|
||||
.chain(
|
||||
workspace
|
||||
.get_fullscreen()
|
||||
.get_fullscreen_surfaces()
|
||||
.filter(|f| {
|
||||
workspace
|
||||
.focus_stack
|
||||
.get(seat)
|
||||
.last()
|
||||
.is_none_or(|t| &t != f)
|
||||
focus_last
|
||||
.as_ref()
|
||||
.is_none_or(|t| t != &f.surface)
|
||||
})
|
||||
.cloned()
|
||||
.map(|f| f.surface.clone())
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter(),
|
||||
)
|
||||
.chain(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue