Merge pull request #194 from pop-os/ws-refactor_jammy
Overhaul of workspace code
This commit is contained in:
commit
7300e23ce8
41 changed files with 3626 additions and 3398 deletions
574
Cargo.lock
generated
574
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
12
Cargo.toml
12
Cargo.toml
|
|
@ -36,10 +36,10 @@ libsystemd = { version = "0.6", optional = true }
|
|||
wayland-backend = "0.3.2"
|
||||
wayland-scanner = "0.31.0"
|
||||
cosmic-comp-config = { path = "cosmic-comp-config" }
|
||||
cosmic-config = { git = "https://github.com/pop-os/libcosmic/", rev = "f91287d", features = ["calloop"] }
|
||||
cosmic-config = { git = "https://github.com/pop-os/libcosmic/", features = ["calloop", "macro"] }
|
||||
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", branch = "main", default-features = false, features = ["server"] }
|
||||
libcosmic = { git = "https://github.com/pop-os/libcosmic/", rev = "f91287d", default-features = false }
|
||||
iced_tiny_skia = { git = "https://github.com/pop-os/libcosmic/", rev = "f91287d" }
|
||||
libcosmic = { git = "https://github.com/pop-os/libcosmic/", default-features = false }
|
||||
iced_tiny_skia = { git = "https://github.com/pop-os/libcosmic/" }
|
||||
tiny-skia = "0.10"
|
||||
ordered-float = "4.0"
|
||||
glow = "0.12.0"
|
||||
|
|
@ -53,6 +53,7 @@ once_cell = "1.18.0"
|
|||
i18n-embed = { version = "0.14", features = ["fluent-system", "desktop-requester"] }
|
||||
i18n-embed-fl = "0.7"
|
||||
rust-embed = "8.0"
|
||||
libc = "0.2.149"
|
||||
|
||||
[dependencies.id_tree]
|
||||
git = "https://github.com/Drakulix/id-tree.git"
|
||||
|
|
@ -87,7 +88,4 @@ debug = true
|
|||
lto = "fat"
|
||||
|
||||
[patch."https://github.com/Smithay/smithay.git"]
|
||||
smithay = { git = "https://github.com/smithay//smithay", rev = "d60b1b83e" }
|
||||
|
||||
[patch.crates-io]
|
||||
calloop = { git = "https://github.com/Smithay/calloop", rev = "71b6e633b1" }
|
||||
smithay = { git = "https://github.com/pop-os/smithay", branch = "x11_fixes" }
|
||||
|
|
@ -16,6 +16,7 @@ use crate::{
|
|||
|
||||
use anyhow::{Context, Result};
|
||||
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::FailureReason;
|
||||
use libc::dev_t;
|
||||
use smithay::{
|
||||
backend::{
|
||||
allocator::{
|
||||
|
|
@ -25,7 +26,7 @@ use smithay::{
|
|||
Allocator, Format, Fourcc,
|
||||
},
|
||||
drm::{
|
||||
compositor::{BlitFrameResultError, DrmCompositor, FrameError},
|
||||
compositor::{BlitFrameResultError, DrmCompositor, FrameError, PrimaryPlaneElement},
|
||||
DrmDevice, DrmDeviceFd, DrmEvent, DrmEventTime, DrmNode, NodeType,
|
||||
},
|
||||
egl::{EGLContext, EGLDevice, EGLDisplay},
|
||||
|
|
@ -58,7 +59,7 @@ use smithay::{
|
|||
Device as _,
|
||||
},
|
||||
input::{self, Libinput},
|
||||
nix::{fcntl::OFlag, sys::stat::dev_t},
|
||||
rustix::fs::OFlags,
|
||||
wayland_protocols::wp::{
|
||||
linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1,
|
||||
presentation_time::server::wp_presentation_feedback,
|
||||
|
|
@ -82,7 +83,6 @@ use std::{
|
|||
collections::{HashMap, HashSet},
|
||||
ffi::CStr,
|
||||
fmt,
|
||||
os::unix::io::FromRawFd,
|
||||
path::PathBuf,
|
||||
time::Duration,
|
||||
};
|
||||
|
|
@ -418,23 +418,21 @@ impl State {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let fd = DrmDeviceFd::new(unsafe {
|
||||
DeviceFd::from_raw_fd(
|
||||
self.backend
|
||||
.kms()
|
||||
.session
|
||||
.open(
|
||||
&path,
|
||||
OFlag::O_RDWR | OFlag::O_CLOEXEC | OFlag::O_NOCTTY | OFlag::O_NONBLOCK,
|
||||
let fd = DrmDeviceFd::new(DeviceFd::from(
|
||||
self.backend
|
||||
.kms()
|
||||
.session
|
||||
.open(
|
||||
&path,
|
||||
OFlags::RDWR | OFlags::CLOEXEC | OFlags::NOCTTY | OFlags::NONBLOCK,
|
||||
)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Failed to optain file descriptor for drm device: {}",
|
||||
path.display()
|
||||
)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Failed to optain file descriptor for drm device: {}",
|
||||
path.display()
|
||||
)
|
||||
})?,
|
||||
)
|
||||
});
|
||||
})?,
|
||||
));
|
||||
let (drm, notifier) = DrmDevice::new(fd.clone(), false)
|
||||
.with_context(|| format!("Failed to initialize drm device for: {}", path.display()))?;
|
||||
let drm_node = DrmNode::from_dev_id(dev)?;
|
||||
|
|
@ -1014,8 +1012,14 @@ impl Device {
|
|||
interface,
|
||||
PhysicalProperties {
|
||||
size: (phys_w as i32, phys_h as i32).into(),
|
||||
// TODO: We need to read that from the connector properties
|
||||
subpixel: Subpixel::Unknown,
|
||||
subpixel: match conn_info.subpixel() {
|
||||
connector::SubPixel::HorizontalRgb => Subpixel::HorizontalRgb,
|
||||
connector::SubPixel::HorizontalBgr => Subpixel::HorizontalBgr,
|
||||
connector::SubPixel::VerticalRgb => Subpixel::VerticalRgb,
|
||||
connector::SubPixel::VerticalBgr => Subpixel::VerticalBgr,
|
||||
connector::SubPixel::None => Subpixel::None,
|
||||
_ => Subpixel::Unknown,
|
||||
},
|
||||
make: edid_info
|
||||
.as_ref()
|
||||
.map(|info| info.manufacturer.clone())
|
||||
|
|
@ -1093,8 +1097,8 @@ fn render_node_for_output(
|
|||
) -> DrmNode {
|
||||
let workspace = shell.active_space(output);
|
||||
let nodes = workspace
|
||||
.get_fullscreen(output)
|
||||
.map(|w| vec![w.surface()])
|
||||
.get_fullscreen()
|
||||
.map(|w| vec![w.clone()])
|
||||
.unwrap_or_else(|| workspace.windows().collect::<Vec<_>>())
|
||||
.into_iter()
|
||||
.flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s, dh)))
|
||||
|
|
@ -1235,8 +1239,11 @@ impl Surface {
|
|||
})?;
|
||||
self.fps.elements();
|
||||
|
||||
let res =
|
||||
compositor.render_frame::<_, _, GlesTexture>(&mut renderer, &elements, CLEAR_COLOR);
|
||||
let res = compositor.render_frame::<_, _, GlesTexture>(
|
||||
&mut renderer,
|
||||
&elements,
|
||||
CLEAR_COLOR, // TODO use a theme neutral color
|
||||
);
|
||||
self.fps.render();
|
||||
|
||||
match res {
|
||||
|
|
@ -1247,6 +1254,11 @@ impl Surface {
|
|||
None
|
||||
};
|
||||
|
||||
if frame_result.needs_sync() {
|
||||
if let PrimaryPlaneElement::Swapchain(elem) = &frame_result.primary_element {
|
||||
elem.sync.wait();
|
||||
}
|
||||
}
|
||||
match compositor.queue_frame(feedback) {
|
||||
Ok(()) | Err(FrameError::EmptyFrame) => {}
|
||||
Err(err) => {
|
||||
|
|
@ -1507,7 +1519,6 @@ impl KmsState {
|
|||
false
|
||||
};
|
||||
|
||||
shell.refresh_outputs();
|
||||
if recreated {
|
||||
let sessions = output.pending_buffers().collect::<Vec<_>>();
|
||||
if let Err(err) = self.schedule_render(
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use crate::{
|
|||
CosmicMapped, CosmicMappedRenderElement, OverviewMode, Trigger, WorkspaceRenderElement,
|
||||
},
|
||||
state::{Common, Fps},
|
||||
utils::prelude::{OutputExt, SeatExt},
|
||||
utils::prelude::*,
|
||||
wayland::{
|
||||
handlers::{
|
||||
data_device::get_dnd_icon,
|
||||
|
|
@ -79,10 +79,6 @@ pub type GlMultiFrame<'a, 'b, 'frame> =
|
|||
pub type GlMultiError = MultiError<GbmGlesBackend<GlowRenderer>, GbmGlesBackend<GlowRenderer>>;
|
||||
|
||||
pub static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0];
|
||||
pub static GROUP_COLOR: [f32; 3] = [0.788, 0.788, 0.788];
|
||||
pub static ACTIVE_GROUP_COLOR: [f32; 3] = [0.58, 0.922, 0.922];
|
||||
pub static FOCUS_INDICATOR_COLOR: [f32; 3] = [0.580, 0.921, 0.921];
|
||||
|
||||
pub static OUTLINE_SHADER: &str = include_str!("./shaders/rounded_outline.frag");
|
||||
pub static RECTANGLE_SHADER: &str = include_str!("./shaders/rounded_rectangle.frag");
|
||||
|
||||
|
|
@ -160,10 +156,11 @@ impl IndicatorShader {
|
|||
pub fn focus_element<R: AsGlowRenderer>(
|
||||
renderer: &R,
|
||||
key: impl Into<Key>,
|
||||
mut element_geo: Rectangle<i32, Logical>,
|
||||
mut element_geo: Rectangle<i32, Local>,
|
||||
thickness: u8,
|
||||
scale: f64,
|
||||
alpha: f32,
|
||||
active_window_hint: [f32; 3],
|
||||
) -> PixelShaderElement {
|
||||
let t = thickness as i32;
|
||||
element_geo.loc -= (t, t).into();
|
||||
|
|
@ -177,14 +174,14 @@ impl IndicatorShader {
|
|||
thickness * 2,
|
||||
alpha,
|
||||
scale,
|
||||
FOCUS_INDICATOR_COLOR,
|
||||
active_window_hint,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn element<R: AsGlowRenderer>(
|
||||
renderer: &R,
|
||||
key: impl Into<Key>,
|
||||
geo: Rectangle<i32, Logical>,
|
||||
geo: Rectangle<i32, Local>,
|
||||
thickness: u8,
|
||||
radius: u8,
|
||||
alpha: f32,
|
||||
|
|
@ -223,7 +220,7 @@ impl IndicatorShader {
|
|||
|
||||
let elem = PixelShaderElement::new(
|
||||
shader,
|
||||
geo,
|
||||
geo.as_logical(),
|
||||
None, //TODO
|
||||
alpha,
|
||||
vec![
|
||||
|
|
@ -240,8 +237,8 @@ impl IndicatorShader {
|
|||
}
|
||||
|
||||
let elem = &mut cache.get_mut(&key).unwrap().1;
|
||||
if elem.geometry(1.0.into()).to_logical(1) != geo {
|
||||
elem.resize(geo, None);
|
||||
if elem.geometry(1.0.into()).to_logical(1) != geo.as_logical() {
|
||||
elem.resize(geo.as_logical(), None);
|
||||
}
|
||||
elem.clone()
|
||||
}
|
||||
|
|
@ -271,7 +268,7 @@ impl BackdropShader {
|
|||
pub fn element<R: AsGlowRenderer>(
|
||||
renderer: &R,
|
||||
key: impl Into<Key>,
|
||||
geo: Rectangle<i32, Logical>,
|
||||
geo: Rectangle<i32, Local>,
|
||||
radius: f32,
|
||||
alpha: f32,
|
||||
color: [f32; 3],
|
||||
|
|
@ -304,7 +301,7 @@ impl BackdropShader {
|
|||
|
||||
let elem = PixelShaderElement::new(
|
||||
shader,
|
||||
geo,
|
||||
geo.as_logical(),
|
||||
None, // TODO
|
||||
alpha,
|
||||
vec![
|
||||
|
|
@ -320,8 +317,8 @@ impl BackdropShader {
|
|||
}
|
||||
|
||||
let elem = &mut cache.get_mut(&key).unwrap().1;
|
||||
if elem.geometry(1.0.into()).to_logical(1) != geo {
|
||||
elem.resize(geo, None);
|
||||
if elem.geometry(1.0.into()).to_logical(1) != geo.as_logical() {
|
||||
elem.resize(geo.as_logical(), None);
|
||||
}
|
||||
elem.clone()
|
||||
}
|
||||
|
|
@ -413,13 +410,14 @@ where
|
|||
);
|
||||
}
|
||||
|
||||
let theme = state.theme.cosmic();
|
||||
if let Some(grab_elements) = seat
|
||||
.user_data()
|
||||
.get::<SeatMoveGrabState>()
|
||||
.unwrap()
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map(|state| state.render::<E, R>(renderer, seat, output))
|
||||
.map(|state| state.render::<E, R>(renderer, seat, output, theme))
|
||||
{
|
||||
elements.extend(grab_elements);
|
||||
}
|
||||
|
|
@ -449,6 +447,7 @@ where
|
|||
#[cfg(feature = "debug")]
|
||||
puffin::profile_function!();
|
||||
|
||||
let theme = state.theme.cosmic();
|
||||
let mut elements = cursor_elements(renderer, state, output, cursor_mode);
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
|
|
@ -492,15 +491,11 @@ where
|
|||
let (resize_mode, resize_indicator) = state.shell.resize_mode();
|
||||
let resize_indicator = resize_indicator.map(|indicator| (resize_mode, indicator));
|
||||
let swap_tree = if let OverviewMode::Started(Trigger::KeyboardSwap(_, desc), _) = &overview.0 {
|
||||
if let Some(desc_output) = desc.output.upgrade() {
|
||||
if output != &desc_output || current.0 != desc.handle {
|
||||
state
|
||||
.shell
|
||||
.space_for_handle(&desc.handle)
|
||||
.and_then(|w| w.tiling_layer.tree_for_output(&desc_output))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if current.0 != desc.handle {
|
||||
state
|
||||
.shell
|
||||
.space_for_handle(&desc.handle)
|
||||
.map(|w| w.tiling_layer.tree())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -530,9 +525,9 @@ where
|
|||
|
||||
let has_fullscreen = workspace
|
||||
.fullscreen
|
||||
.get(output)
|
||||
.as_ref()
|
||||
.filter(|f| !f.is_animating())
|
||||
.map(|f| f.exclusive);
|
||||
.is_some();
|
||||
let (overlay_elements, overlay_popups) =
|
||||
split_layer_elements(renderer, output, Layer::Overlay, exclude_workspace_overview);
|
||||
|
||||
|
|
@ -540,7 +535,7 @@ where
|
|||
elements.extend(overlay_popups.into_iter().map(Into::into));
|
||||
elements.extend(overlay_elements.into_iter().map(Into::into));
|
||||
|
||||
let mut window_elements = if !has_fullscreen.unwrap_or(false) {
|
||||
let mut window_elements = if !has_fullscreen {
|
||||
let (top_elements, top_popups) =
|
||||
split_layer_elements(renderer, output, Layer::Top, exclude_workspace_overview);
|
||||
elements.extend(top_popups.into_iter().map(Into::into));
|
||||
|
|
@ -548,6 +543,7 @@ where
|
|||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let active_hint = theme.active_hint as u8;
|
||||
|
||||
let offset = match previous.as_ref() {
|
||||
Some((previous, previous_idx, start)) => {
|
||||
|
|
@ -557,7 +553,7 @@ where
|
|||
.shell
|
||||
.space_for_handle(&previous)
|
||||
.ok_or(OutputNoMode)?;
|
||||
let has_fullscreen = workspace.fullscreen.contains_key(output);
|
||||
let has_fullscreen = workspace.fullscreen.is_some();
|
||||
let is_active_space = workspace.outputs().any(|o| o == &active_output);
|
||||
|
||||
let percentage = {
|
||||
|
|
@ -581,15 +577,15 @@ where
|
|||
});
|
||||
|
||||
let (w_elements, p_elements) = workspace
|
||||
.render_output::<R>(
|
||||
.render::<R>(
|
||||
renderer,
|
||||
output,
|
||||
&state.shell.override_redirect_windows,
|
||||
state.xwayland_state.as_mut(),
|
||||
(!move_active && is_active_space).then_some(&last_active_seat),
|
||||
overview.clone(),
|
||||
resize_indicator.clone(),
|
||||
state.config.static_conf.active_hint,
|
||||
active_hint,
|
||||
theme,
|
||||
)
|
||||
.map_err(|_| OutputNoMode)?;
|
||||
elements.extend(p_elements.into_iter().map(|p_element| {
|
||||
|
|
@ -639,15 +635,15 @@ where
|
|||
let is_active_space = workspace.outputs().any(|o| o == &active_output);
|
||||
|
||||
let (w_elements, p_elements) = workspace
|
||||
.render_output::<R>(
|
||||
.render::<R>(
|
||||
renderer,
|
||||
output,
|
||||
&state.shell.override_redirect_windows,
|
||||
state.xwayland_state.as_mut(),
|
||||
(!move_active && is_active_space).then_some(&last_active_seat),
|
||||
overview,
|
||||
resize_indicator,
|
||||
state.config.static_conf.active_hint,
|
||||
active_hint,
|
||||
theme,
|
||||
)
|
||||
.map_err(|_| OutputNoMode)?;
|
||||
elements.extend(p_elements.into_iter().map(|p_element| {
|
||||
|
|
@ -665,7 +661,7 @@ where
|
|||
))
|
||||
}));
|
||||
|
||||
if has_fullscreen.is_none() {
|
||||
if !has_fullscreen {
|
||||
let (w_elements, p_elements) =
|
||||
background_layer_elements(renderer, output, exclude_workspace_overview);
|
||||
|
||||
|
|
@ -919,7 +915,12 @@ where
|
|||
}
|
||||
|
||||
renderer.bind(target).map_err(RenderError::Rendering)?;
|
||||
let res = damage_tracker.render_output(renderer, age, &elements, CLEAR_COLOR);
|
||||
let res = damage_tracker.render_output(
|
||||
renderer,
|
||||
age,
|
||||
&elements,
|
||||
CLEAR_COLOR, // TODO use a theme neutral color
|
||||
);
|
||||
|
||||
if let Some(fps) = fps.as_mut() {
|
||||
fps.render();
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ use smithay::{
|
|||
calloop::{ping, EventLoop},
|
||||
wayland_protocols::wp::presentation_time::server::wp_presentation_feedback,
|
||||
wayland_server::DisplayHandle,
|
||||
winit::platform::pump_events::PumpStatus,
|
||||
},
|
||||
utils::Transform,
|
||||
wayland::dmabuf::DmabufFeedbackBuilder,
|
||||
|
|
@ -125,12 +126,9 @@ impl WinitState {
|
|||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow_mut();
|
||||
if dbg!(config.mode.0) != dbg!((size.physical_size.w as i32, size.physical_size.h as i32)) {
|
||||
if dbg!(config.mode.0) != dbg!((size.w as i32, size.h as i32)) {
|
||||
if !test_only {
|
||||
config.mode = (
|
||||
(size.physical_size.w as i32, size.physical_size.h as i32),
|
||||
None,
|
||||
);
|
||||
config.mode = ((size.w as i32, size.h as i32), None);
|
||||
}
|
||||
Err(anyhow::anyhow!("Cannot set window size"))
|
||||
} else {
|
||||
|
|
@ -165,7 +163,7 @@ pub fn init_backend(
|
|||
model: name.clone(),
|
||||
};
|
||||
let mode = Mode {
|
||||
size: (size.physical_size.w as i32, size.physical_size.h as i32).into(),
|
||||
size: (size.w as i32, size.h as i32).into(),
|
||||
refresh: 60_000,
|
||||
};
|
||||
let output = Output::new(name, props);
|
||||
|
|
@ -179,10 +177,7 @@ pub fn init_backend(
|
|||
);
|
||||
output.user_data().insert_if_missing(|| {
|
||||
RefCell::new(OutputConfig {
|
||||
mode: (
|
||||
(size.physical_size.w as i32, size.physical_size.h as i32),
|
||||
None,
|
||||
),
|
||||
mode: ((size.w as i32, size.h as i32), None),
|
||||
transform: Transform::Flipped180.into(),
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -212,11 +207,11 @@ pub fn init_backend(
|
|||
match input
|
||||
.dispatch_new_events(|event| state.process_winit_event(event, &render_ping_handle))
|
||||
{
|
||||
Ok(_) => {
|
||||
PumpStatus::Continue => {
|
||||
event_ping_handle.ping();
|
||||
render_ping_handle.ping();
|
||||
}
|
||||
Err(winit::WinitError::WindowClosed) => {
|
||||
PumpStatus::Exit(_) => {
|
||||
let output = state.backend.winit().output.clone();
|
||||
let seats = state.common.seats().cloned().collect::<Vec<_>>();
|
||||
state.common.shell.remove_output(&output, seats.into_iter());
|
||||
|
|
@ -349,10 +344,9 @@ impl State {
|
|||
output.change_current_state(Some(mode), None, None, None);
|
||||
layer_map_for_output(output).arrange();
|
||||
self.common.output_configuration_state.update();
|
||||
self.common.shell.refresh_outputs();
|
||||
render_ping.ping();
|
||||
}
|
||||
WinitEvent::Refresh => render_ping.ping(),
|
||||
WinitEvent::Redraw => render_ping.ping(),
|
||||
WinitEvent::Input(event) => self.process_input_event(event, false),
|
||||
_ => {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -448,7 +448,6 @@ pub fn init_backend(
|
|||
output.set_preferred(mode);
|
||||
layer_map_for_output(output).arrange();
|
||||
state.common.output_configuration_state.update();
|
||||
state.common.shell.refresh_outputs();
|
||||
surface.dirty = true;
|
||||
if !surface.pending {
|
||||
surface.render.ping();
|
||||
|
|
|
|||
|
|
@ -50,10 +50,6 @@ pub struct StaticConfig {
|
|||
#[serde(default = "default_workspace_layout")]
|
||||
pub workspace_layout: WorkspaceLayout,
|
||||
pub tiling_enabled: bool,
|
||||
#[serde(default = "default_active_hint")]
|
||||
pub active_hint: u8,
|
||||
#[serde(default = "default_gaps")]
|
||||
pub gaps: (u8, u8),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Copy, PartialEq, Eq)]
|
||||
|
|
@ -100,14 +96,6 @@ fn default_enabled() -> bool {
|
|||
true
|
||||
}
|
||||
|
||||
fn default_active_hint() -> u8 {
|
||||
4
|
||||
}
|
||||
|
||||
fn default_gaps() -> (u8, u8) {
|
||||
(0, 4)
|
||||
}
|
||||
|
||||
fn default_workspace_layout() -> WorkspaceLayout {
|
||||
WorkspaceLayout::Vertical
|
||||
}
|
||||
|
|
@ -219,8 +207,6 @@ impl Config {
|
|||
workspace_amount: WorkspaceAmount::Dynamic,
|
||||
workspace_layout: WorkspaceLayout::Vertical,
|
||||
tiling_enabled: false,
|
||||
active_hint: default_active_hint(),
|
||||
gaps: default_gaps(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
304
src/input/mod.rs
304
src/input/mod.rs
|
|
@ -6,7 +6,10 @@ use crate::{
|
|||
shell::{
|
||||
focus::{target::PointerFocusTarget, FocusDirection},
|
||||
grabs::{ResizeEdge, SeatMoveGrabState},
|
||||
layout::tiling::{SwapWindowGrab, TilingLayout},
|
||||
layout::{
|
||||
floating::ResizeGrabMarker,
|
||||
tiling::{SwapWindowGrab, TilingLayout},
|
||||
},
|
||||
Direction, FocusResult, MoveResult, OverviewMode, ResizeDirection, ResizeMode, Trigger,
|
||||
Workspace,
|
||||
},
|
||||
|
|
@ -39,7 +42,7 @@ use smithay::{
|
|||
input::event::pointer::PointerAxisEvent as LibinputPointerAxisEvent,
|
||||
wayland_server::DisplayHandle,
|
||||
},
|
||||
utils::{Logical, Point, Rectangle, Serial, SERIAL_COUNTER},
|
||||
utils::{Point, Serial, SERIAL_COUNTER},
|
||||
wayland::{
|
||||
keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat,
|
||||
pointer_constraints::{with_pointer_constraint, PointerConstraint},
|
||||
|
|
@ -214,7 +217,6 @@ impl State {
|
|||
<B as InputBackend>::PointerAxisEvent: 'static,
|
||||
{
|
||||
use smithay::backend::input::Event;
|
||||
|
||||
match event {
|
||||
InputEvent::DeviceAdded { device } => {
|
||||
let seat = &mut self.common.last_active_seat();
|
||||
|
|
@ -350,7 +352,7 @@ impl State {
|
|||
if let Some(old_workspace) = old_w.get_mut(0) {
|
||||
if let Some(new_workspace) = other_w.iter_mut().find(|w| w.handle == new_workspace) {
|
||||
if new_workspace.tiling_layer.windows().next().is_none() {
|
||||
if let Some(focus) = TilingLayout::move_tree(&mut old_workspace.tiling_layer, &mut new_workspace.tiling_layer, ¤t_output, &new_workspace.handle, &seat, new_workspace.focus_stack.get(&seat).iter(), old_descriptor, &mut data.common.shell.toplevel_info_state) {
|
||||
if let Some(focus) = TilingLayout::move_tree(&mut old_workspace.tiling_layer, &mut new_workspace.tiling_layer, &new_workspace.handle, &seat, new_workspace.focus_stack.get(&seat).iter(), old_descriptor, &mut data.common.shell.toplevel_info_state) {
|
||||
let seat = seat.clone();
|
||||
data.common.event_loop_handle.insert_idle(move |state| {
|
||||
Common::set_focus(state, Some(&focus), &seat, None);
|
||||
|
|
@ -553,24 +555,18 @@ impl State {
|
|||
if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() {
|
||||
let current_output = seat.active_output();
|
||||
|
||||
let mut position = seat.get_pointer().unwrap().current_location();
|
||||
let mut position = seat.get_pointer().unwrap().current_location().as_global();
|
||||
|
||||
let relative_pos = self
|
||||
.common
|
||||
.shell
|
||||
.map_global_to_space(position, ¤t_output);
|
||||
let overview = self.common.shell.overview_mode();
|
||||
let output_geometry = current_output.geometry();
|
||||
let workspace = self.common.shell.workspaces.active_mut(¤t_output);
|
||||
let under = State::surface_under(
|
||||
position,
|
||||
relative_pos,
|
||||
¤t_output,
|
||||
output_geometry,
|
||||
&self.common.shell.override_redirect_windows,
|
||||
overview.0.clone(),
|
||||
workspace,
|
||||
);
|
||||
)
|
||||
.map(|(target, pos)| (target, pos.as_logical()));
|
||||
|
||||
let ptr = seat.get_pointer().unwrap();
|
||||
|
||||
|
|
@ -618,7 +614,7 @@ impl State {
|
|||
return;
|
||||
}
|
||||
|
||||
position += event.delta();
|
||||
position += event.delta().as_global();
|
||||
|
||||
let output = self
|
||||
.common
|
||||
|
|
@ -628,17 +624,30 @@ impl State {
|
|||
.cloned()
|
||||
.unwrap_or(current_output.clone());
|
||||
|
||||
let workspace = self.common.shell.workspaces.active_mut(&output);
|
||||
if ptr.is_grabbed()
|
||||
&& seat
|
||||
.user_data()
|
||||
.get::<ResizeGrabMarker>()
|
||||
.map(|marker| marker.get())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if output != current_output {
|
||||
ptr.frame(self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let output_geometry = output.geometry();
|
||||
|
||||
let workspace = self.common.shell.workspaces.active_mut(&output);
|
||||
let new_under = State::surface_under(
|
||||
position,
|
||||
relative_pos,
|
||||
&output,
|
||||
output_geometry,
|
||||
&self.common.shell.override_redirect_windows,
|
||||
overview.0,
|
||||
workspace,
|
||||
);
|
||||
)
|
||||
.map(|(target, pos)| (target, pos.as_logical()));
|
||||
|
||||
position.x = position.x.clamp(
|
||||
output_geometry.loc.x as f64,
|
||||
|
|
@ -660,13 +669,17 @@ impl State {
|
|||
}
|
||||
if let PointerFocusTarget::Element(element) = surface {
|
||||
//if !element.is_in_input_region(&(position.to_i32_round() - *surface_loc).to_f64()) {
|
||||
if !element.is_in_input_region(&(position - surface_loc.to_f64())) {
|
||||
if !element.is_in_input_region(
|
||||
&(position.as_logical() - surface_loc.to_f64()),
|
||||
) {
|
||||
ptr.frame(self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(region) = confine_region {
|
||||
if !region.contains(position.to_i32_round() - *surface_loc) {
|
||||
if !region
|
||||
.contains(position.as_logical().to_i32_round() - *surface_loc)
|
||||
{
|
||||
ptr.frame(self);
|
||||
return;
|
||||
}
|
||||
|
|
@ -679,7 +692,7 @@ impl State {
|
|||
self,
|
||||
under,
|
||||
&MotionEvent {
|
||||
location: position,
|
||||
location: position.as_logical(),
|
||||
serial,
|
||||
time: event.time_msec(),
|
||||
},
|
||||
|
|
@ -720,17 +733,16 @@ impl State {
|
|||
|
||||
for session in sessions_for_output(&self.common, &output) {
|
||||
if let Some((geometry, offset)) = seat.cursor_geometry(
|
||||
position.to_buffer(
|
||||
position.as_logical().to_buffer(
|
||||
output.current_scale().fractional_scale(),
|
||||
output.current_transform(),
|
||||
&output.geometry().size.to_f64(),
|
||||
&output_geometry.size.to_f64().as_logical(),
|
||||
),
|
||||
self.common.clock.now(),
|
||||
) {
|
||||
session.cursor_info(&seat, InputType::Pointer, geometry, offset);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
if self.common.seats().position(|x| x == &seat).unwrap() == 0 {
|
||||
let location = if let Some(output) = self.common.shell.outputs.first() {
|
||||
|
|
@ -752,28 +764,27 @@ impl State {
|
|||
let position = geometry.loc.to_f64()
|
||||
+ smithay::backend::input::AbsolutePositionEvent::position_transformed(
|
||||
&event,
|
||||
geometry.size,
|
||||
);
|
||||
let relative_pos = self.common.shell.map_global_to_space(position, &output);
|
||||
geometry.size.as_logical(),
|
||||
)
|
||||
.as_global();
|
||||
let overview = self.common.shell.overview_mode();
|
||||
let workspace = self.common.shell.workspaces.active_mut(&output);
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let under = State::surface_under(
|
||||
position,
|
||||
relative_pos,
|
||||
&output,
|
||||
geometry,
|
||||
&self.common.shell.override_redirect_windows,
|
||||
overview.0,
|
||||
workspace,
|
||||
);
|
||||
)
|
||||
.map(|(target, pos)| (target, pos.as_logical()));
|
||||
|
||||
for session in sessions_for_output(&self.common, &output) {
|
||||
if let Some((geometry, offset)) = seat.cursor_geometry(
|
||||
position.to_buffer(
|
||||
position.as_logical().to_buffer(
|
||||
output.current_scale().fractional_scale(),
|
||||
output.current_transform(),
|
||||
&output.geometry().size.to_f64(),
|
||||
&geometry.size.to_f64().as_logical(),
|
||||
),
|
||||
self.common.clock.now(),
|
||||
) {
|
||||
|
|
@ -785,7 +796,7 @@ impl State {
|
|||
self,
|
||||
under,
|
||||
&MotionEvent {
|
||||
location: position,
|
||||
location: position.as_logical(),
|
||||
serial,
|
||||
time: event.time_msec(),
|
||||
},
|
||||
|
|
@ -835,22 +846,22 @@ impl State {
|
|||
&& !seat.get_keyboard().map(|k| k.is_grabbed()).unwrap_or(false)
|
||||
{
|
||||
let output = seat.active_output();
|
||||
let pos = seat.get_pointer().unwrap().current_location();
|
||||
let relative_pos = self.common.shell.map_global_to_space(pos, &output);
|
||||
let pos = seat.get_pointer().unwrap().current_location().as_global();
|
||||
let relative_pos = pos.to_local(&output);
|
||||
let overview = self.common.shell.overview_mode();
|
||||
let workspace = self.common.shell.active_space_mut(&output);
|
||||
let mut under = None;
|
||||
|
||||
if let Some(window) = workspace.get_fullscreen(&output) {
|
||||
if let Some(window) = workspace.get_fullscreen() {
|
||||
let layers = layer_map_for_output(&output);
|
||||
if let Some(layer) =
|
||||
layers.layer_under(WlrLayer::Overlay, relative_pos)
|
||||
layers.layer_under(WlrLayer::Overlay, relative_pos.as_logical())
|
||||
{
|
||||
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
||||
if layer.can_receive_keyboard_focus()
|
||||
&& layer
|
||||
.surface_under(
|
||||
relative_pos - layer_loc.to_f64(),
|
||||
relative_pos.as_logical() - layer_loc.to_f64(),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.is_some()
|
||||
|
|
@ -864,14 +875,19 @@ impl State {
|
|||
let done = {
|
||||
let layers = layer_map_for_output(&output);
|
||||
if let Some(layer) = layers
|
||||
.layer_under(WlrLayer::Overlay, relative_pos)
|
||||
.or_else(|| layers.layer_under(WlrLayer::Top, relative_pos))
|
||||
.layer_under(WlrLayer::Overlay, relative_pos.as_logical())
|
||||
.or_else(|| {
|
||||
layers.layer_under(
|
||||
WlrLayer::Top,
|
||||
relative_pos.as_logical(),
|
||||
)
|
||||
})
|
||||
{
|
||||
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
||||
if layer.can_receive_keyboard_focus()
|
||||
&& layer
|
||||
.surface_under(
|
||||
relative_pos - layer_loc.to_f64(),
|
||||
relative_pos.as_logical() - layer_loc.to_f64(),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.is_some()
|
||||
|
|
@ -884,35 +900,38 @@ impl State {
|
|||
}
|
||||
};
|
||||
if !done {
|
||||
if let Some(surface) = workspace.get_maximized(&output) {
|
||||
under = Some(surface.clone().into());
|
||||
if let Some((target, _)) =
|
||||
workspace.element_under(pos, overview.0)
|
||||
{
|
||||
under = Some(target);
|
||||
} else {
|
||||
if let Some((target, _)) =
|
||||
workspace.element_under(relative_pos, overview.0)
|
||||
let layers = layer_map_for_output(&output);
|
||||
if let Some(layer) = layers
|
||||
.layer_under(
|
||||
WlrLayer::Bottom,
|
||||
relative_pos.as_logical(),
|
||||
)
|
||||
.or_else(|| {
|
||||
layers.layer_under(
|
||||
WlrLayer::Background,
|
||||
relative_pos.as_logical(),
|
||||
)
|
||||
})
|
||||
{
|
||||
under = Some(target);
|
||||
} else {
|
||||
let layers = layer_map_for_output(&output);
|
||||
if let Some(layer) = layers
|
||||
.layer_under(WlrLayer::Bottom, pos)
|
||||
.or_else(|| {
|
||||
layers.layer_under(WlrLayer::Background, pos)
|
||||
})
|
||||
let layer_loc =
|
||||
layers.layer_geometry(layer).unwrap().loc;
|
||||
if layer.can_receive_keyboard_focus()
|
||||
&& layer
|
||||
.surface_under(
|
||||
relative_pos.as_logical()
|
||||
- layer_loc.to_f64(),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
let layer_loc =
|
||||
layers.layer_geometry(layer).unwrap().loc;
|
||||
if layer.can_receive_keyboard_focus()
|
||||
&& layer
|
||||
.surface_under(
|
||||
relative_pos - layer_loc.to_f64(),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
under = Some(layer.clone().into());
|
||||
}
|
||||
};
|
||||
}
|
||||
under = Some(layer.clone().into());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1330,16 +1349,15 @@ impl State {
|
|||
}
|
||||
Action::NextOutput => {
|
||||
let current_output = seat.active_output();
|
||||
if let Some(next_output) = self
|
||||
let next_output = self
|
||||
.common
|
||||
.shell
|
||||
.outputs
|
||||
.iter()
|
||||
.outputs()
|
||||
.skip_while(|o| *o != ¤t_output)
|
||||
.skip(1)
|
||||
.next()
|
||||
.cloned()
|
||||
{
|
||||
.cloned();
|
||||
if let Some(next_output) = next_output {
|
||||
let idx = self.common.shell.workspaces.active_num(&next_output).1;
|
||||
match self.common.shell.activate(&next_output, idx) {
|
||||
Ok(Some(new_pos)) => {
|
||||
|
|
@ -1349,7 +1367,7 @@ impl State {
|
|||
self,
|
||||
None,
|
||||
&MotionEvent {
|
||||
location: new_pos.to_f64(),
|
||||
location: new_pos.to_f64().as_logical(),
|
||||
serial,
|
||||
time,
|
||||
},
|
||||
|
|
@ -1366,17 +1384,16 @@ impl State {
|
|||
}
|
||||
Action::PreviousOutput => {
|
||||
let current_output = seat.active_output();
|
||||
if let Some(prev_output) = self
|
||||
let prev_output = self
|
||||
.common
|
||||
.shell
|
||||
.outputs
|
||||
.iter()
|
||||
.outputs()
|
||||
.rev()
|
||||
.skip_while(|o| *o != ¤t_output)
|
||||
.skip(1)
|
||||
.next()
|
||||
.cloned()
|
||||
{
|
||||
.cloned();
|
||||
if let Some(prev_output) = prev_output {
|
||||
let idx = self.common.shell.workspaces.active_num(&prev_output).1;
|
||||
match self.common.shell.activate(&prev_output, idx) {
|
||||
Ok(Some(new_pos)) => {
|
||||
|
|
@ -1386,7 +1403,7 @@ impl State {
|
|||
self,
|
||||
None,
|
||||
&MotionEvent {
|
||||
location: new_pos.to_f64(),
|
||||
location: new_pos.to_f64().as_logical(),
|
||||
serial,
|
||||
time,
|
||||
},
|
||||
|
|
@ -1403,16 +1420,15 @@ impl State {
|
|||
}
|
||||
x @ Action::MoveToNextOutput | x @ Action::SendToNextOutput => {
|
||||
let current_output = seat.active_output();
|
||||
if let Some(next_output) = self
|
||||
let next_output = self
|
||||
.common
|
||||
.shell
|
||||
.outputs
|
||||
.iter()
|
||||
.outputs()
|
||||
.skip_while(|o| *o != ¤t_output)
|
||||
.skip(1)
|
||||
.next()
|
||||
.cloned()
|
||||
{
|
||||
.cloned();
|
||||
if let Some(next_output) = next_output {
|
||||
if let Ok(Some(new_pos)) = Shell::move_current_window(
|
||||
self,
|
||||
seat,
|
||||
|
|
@ -1426,7 +1442,7 @@ impl State {
|
|||
self,
|
||||
None,
|
||||
&MotionEvent {
|
||||
location: new_pos.to_f64(),
|
||||
location: new_pos.to_f64().as_logical(),
|
||||
serial,
|
||||
time,
|
||||
},
|
||||
|
|
@ -1438,17 +1454,16 @@ impl State {
|
|||
}
|
||||
x @ Action::MoveToPreviousOutput | x @ Action::SendToPreviousOutput => {
|
||||
let current_output = seat.active_output();
|
||||
if let Some(prev_output) = self
|
||||
let prev_output = self
|
||||
.common
|
||||
.shell
|
||||
.outputs
|
||||
.iter()
|
||||
.outputs()
|
||||
.rev()
|
||||
.skip_while(|o| *o != ¤t_output)
|
||||
.skip(1)
|
||||
.next()
|
||||
.cloned()
|
||||
{
|
||||
.cloned();
|
||||
if let Some(prev_output) = prev_output {
|
||||
if let Ok(Some(new_pos)) = Shell::move_current_window(
|
||||
self,
|
||||
seat,
|
||||
|
|
@ -1462,7 +1477,7 @@ impl State {
|
|||
self,
|
||||
None,
|
||||
&MotionEvent {
|
||||
location: new_pos.to_f64(),
|
||||
location: new_pos.to_f64().as_logical(),
|
||||
serial,
|
||||
time,
|
||||
},
|
||||
|
|
@ -1540,9 +1555,6 @@ impl State {
|
|||
Action::Move(direction) => {
|
||||
let current_output = seat.active_output();
|
||||
let workspace = self.common.shell.active_space_mut(¤t_output);
|
||||
if workspace.get_fullscreen(¤t_output).is_some() {
|
||||
return; // TODO, is this what we want? How do we indicate the switch?
|
||||
}
|
||||
|
||||
match workspace.move_current_element(direction, seat) {
|
||||
MoveResult::MoveFurther(_move_further) => {
|
||||
|
|
@ -1603,7 +1615,7 @@ impl State {
|
|||
Action::SwapWindow => {
|
||||
let current_output = seat.active_output();
|
||||
let workspace = self.common.shell.active_space_mut(¤t_output);
|
||||
if workspace.get_fullscreen(¤t_output).is_some() {
|
||||
if workspace.get_fullscreen().is_some() {
|
||||
return; // TODO, is this what we want? Maybe disengage fullscreen instead?
|
||||
}
|
||||
|
||||
|
|
@ -1625,11 +1637,7 @@ impl State {
|
|||
let focus_stack = workspace.focus_stack.get(seat);
|
||||
let focused_window = focus_stack.last();
|
||||
if let Some(window) = focused_window.map(|f| f.active_window()) {
|
||||
workspace.maximize_toggle(
|
||||
&window,
|
||||
¤t_output,
|
||||
self.common.event_loop_handle.clone(),
|
||||
);
|
||||
workspace.maximize_toggle(&window);
|
||||
}
|
||||
}
|
||||
Action::Resizing(direction) => self.common.shell.set_resize_mode(
|
||||
|
|
@ -1705,77 +1713,83 @@ impl State {
|
|||
}
|
||||
|
||||
pub fn surface_under(
|
||||
global_pos: Point<f64, Logical>,
|
||||
relative_pos: Point<f64, Logical>,
|
||||
global_pos: Point<f64, Global>,
|
||||
output: &Output,
|
||||
output_geo: Rectangle<i32, Logical>,
|
||||
override_redirect_windows: &[X11Surface],
|
||||
overview: OverviewMode,
|
||||
workspace: &mut Workspace,
|
||||
) -> Option<(PointerFocusTarget, Point<i32, Logical>)> {
|
||||
if let Some(window) = workspace.get_fullscreen(output) {
|
||||
) -> Option<(PointerFocusTarget, Point<i32, Global>)> {
|
||||
let relative_pos = global_pos.to_local(output);
|
||||
let output_geo = output.geometry();
|
||||
|
||||
if let Some(window) = workspace.get_fullscreen() {
|
||||
let layers = layer_map_for_output(output);
|
||||
if let Some(layer) = layers.layer_under(WlrLayer::Overlay, relative_pos) {
|
||||
if let Some(layer) = layers.layer_under(WlrLayer::Overlay, relative_pos.as_logical()) {
|
||||
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
||||
if layer
|
||||
.surface_under(relative_pos - layer_loc.to_f64(), WindowSurfaceType::ALL)
|
||||
.surface_under(
|
||||
relative_pos.as_logical() - layer_loc.to_f64(),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
return Some((layer.clone().into(), output_geo.loc + layer_loc));
|
||||
return Some((layer.clone().into(), output_geo.loc + layer_loc.as_global()));
|
||||
}
|
||||
}
|
||||
if let Some(or) = override_redirect_windows
|
||||
.iter()
|
||||
.find(|or| or.is_in_input_region(&(global_pos - or.geometry().loc.to_f64())))
|
||||
{
|
||||
return Some((or.clone().into(), or.geometry().loc));
|
||||
if let Some(or) = override_redirect_windows.iter().find(|or| {
|
||||
or.is_in_input_region(&(global_pos.as_logical() - or.geometry().loc.to_f64()))
|
||||
}) {
|
||||
return Some((or.clone().into(), or.geometry().loc.as_global()));
|
||||
}
|
||||
Some((window.clone().into(), output_geo.loc))
|
||||
} else {
|
||||
{
|
||||
let layers = layer_map_for_output(output);
|
||||
if let Some(layer) = layers
|
||||
.layer_under(WlrLayer::Overlay, relative_pos)
|
||||
.or_else(|| layers.layer_under(WlrLayer::Top, relative_pos))
|
||||
.layer_under(WlrLayer::Overlay, relative_pos.as_logical())
|
||||
.or_else(|| layers.layer_under(WlrLayer::Top, relative_pos.as_logical()))
|
||||
{
|
||||
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
||||
if layer
|
||||
.surface_under(relative_pos - layer_loc.to_f64(), WindowSurfaceType::ALL)
|
||||
.surface_under(
|
||||
relative_pos.as_logical() - layer_loc.to_f64(),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
return Some((layer.clone().into(), output_geo.loc + layer_loc));
|
||||
return Some((
|
||||
layer.clone().into(),
|
||||
output_geo.loc + layer_loc.as_global(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(or) = override_redirect_windows
|
||||
.iter()
|
||||
.find(|or| or.is_in_input_region(&(global_pos - or.geometry().loc.to_f64())))
|
||||
{
|
||||
return Some((or.clone().into(), or.geometry().loc));
|
||||
if let Some(or) = override_redirect_windows.iter().find(|or| {
|
||||
or.is_in_input_region(&(global_pos.as_logical() - or.geometry().loc.to_f64()))
|
||||
}) {
|
||||
return Some((or.clone().into(), or.geometry().loc.as_global()));
|
||||
}
|
||||
if let Some(surface) = workspace.get_maximized(output) {
|
||||
let offset = layer_map_for_output(output).non_exclusive_zone().loc;
|
||||
return Some((surface.clone().into(), output_geo.loc + offset));
|
||||
} else {
|
||||
if let Some((target, loc)) = workspace.element_under(relative_pos, overview) {
|
||||
return Some((target, loc + (global_pos - relative_pos).to_i32_round()));
|
||||
}
|
||||
if let Some((target, loc)) = workspace.element_under(global_pos, overview) {
|
||||
return Some((target, loc));
|
||||
}
|
||||
{
|
||||
let layers = layer_map_for_output(output);
|
||||
if let Some(layer) = layers
|
||||
.layer_under(WlrLayer::Bottom, relative_pos.as_logical())
|
||||
.or_else(|| layers.layer_under(WlrLayer::Background, relative_pos.as_logical()))
|
||||
{
|
||||
let layers = layer_map_for_output(output);
|
||||
if let Some(layer) = layers
|
||||
.layer_under(WlrLayer::Bottom, relative_pos)
|
||||
.or_else(|| layers.layer_under(WlrLayer::Background, relative_pos))
|
||||
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
||||
if layer
|
||||
.surface_under(
|
||||
relative_pos.as_logical() - layer_loc.to_f64(),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
||||
if layer
|
||||
.surface_under(
|
||||
relative_pos - layer_loc.to_f64(),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
return Some((layer.clone().into(), output_geo.loc + layer_loc));
|
||||
}
|
||||
return Some((
|
||||
layer.clone().into(),
|
||||
output_geo.loc + layer_loc.as_global(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1786,7 +1800,7 @@ impl State {
|
|||
|
||||
fn sessions_for_output(state: &Common, output: &Output) -> impl Iterator<Item = Session> {
|
||||
let workspace = state.shell.active_space(&output);
|
||||
let maybe_fullscreen = workspace.get_fullscreen(&output);
|
||||
let maybe_fullscreen = workspace.get_fullscreen();
|
||||
workspace
|
||||
.screencopy_sessions
|
||||
.iter()
|
||||
|
|
@ -1795,7 +1809,7 @@ fn sessions_for_output(state: &Common, output: &Output) -> impl Iterator<Item =
|
|||
maybe_fullscreen
|
||||
.as_ref()
|
||||
.and_then(|w| {
|
||||
if let Some(sessions) = w.surface().user_data().get::<ScreencopySessions>() {
|
||||
if let Some(sessions) = w.user_data().get::<ScreencopySessions>() {
|
||||
Some(
|
||||
sessions
|
||||
.0
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ pub mod shell;
|
|||
pub mod state;
|
||||
#[cfg(feature = "systemd")]
|
||||
pub mod systemd;
|
||||
pub mod theme;
|
||||
pub mod utils;
|
||||
pub mod wayland;
|
||||
pub mod xwayland;
|
||||
|
|
@ -55,6 +56,10 @@ fn main() -> Result<()> {
|
|||
// potentially tell the session we are setup now
|
||||
session::setup_socket(event_loop.handle(), &state)?;
|
||||
|
||||
if let Err(err) = theme::watch_theme(event_loop.handle()) {
|
||||
warn!(?err, "Failed to watch theme");
|
||||
}
|
||||
|
||||
// run the event loop
|
||||
event_loop.run(None, &mut state, |state| {
|
||||
// shall we shut down?
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use smithay::reexports::{
|
||||
calloop::{generic::Generic, Interest, LoopHandle, Mode, PostAction},
|
||||
nix::{fcntl, unistd},
|
||||
rustix,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
|
|
@ -50,20 +50,24 @@ impl From<UnixStream> for StreamWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe fn set_cloexec(fd: RawFd) -> rustix::io::Result<()> {
|
||||
if fd == -1 {
|
||||
return Err(rustix::io::Errno::BADF);
|
||||
}
|
||||
let fd = BorrowedFd::borrow_raw(fd);
|
||||
let flags = rustix::io::fcntl_getfd(fd)?;
|
||||
rustix::io::fcntl_setfd(fd, flags | rustix::io::FdFlags::CLOEXEC)
|
||||
}
|
||||
|
||||
pub fn setup_socket(handle: LoopHandle<State>, state: &State) -> Result<()> {
|
||||
if let Ok(fd_num) = std::env::var("COSMIC_SESSION_SOCK") {
|
||||
if let Ok(fd) = fd_num.parse::<RawFd>() {
|
||||
// set CLOEXEC
|
||||
let flags = fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFD);
|
||||
let result = flags
|
||||
.map(|f| fcntl::FdFlag::from_bits(f).unwrap() | fcntl::FdFlag::FD_CLOEXEC)
|
||||
.and_then(|f| fcntl::fcntl(fd, fcntl::FcntlArg::F_SETFD(f)));
|
||||
let mut session_socket = match result {
|
||||
let mut session_socket = match unsafe { set_cloexec(fd) } {
|
||||
// CLOEXEC worked and we can startup with session IPC
|
||||
Ok(_) => unsafe { UnixStream::from_raw_fd(fd) },
|
||||
// CLOEXEC didn't work, something is wrong with the fd, just close it
|
||||
Err(err) => {
|
||||
let _ = unistd::close(fd);
|
||||
unsafe { rustix::io::close(fd) };
|
||||
return Err(err).with_context(|| "Failed to setup session socket");
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
GlMultiError, GlMultiFrame, GlMultiRenderer,
|
||||
},
|
||||
state::State,
|
||||
utils::prelude::SeatExt,
|
||||
utils::prelude::*,
|
||||
};
|
||||
use calloop::LoopHandle;
|
||||
use id_tree::NodeId;
|
||||
|
|
@ -74,7 +74,7 @@ use tracing::debug;
|
|||
use super::{
|
||||
focus::FocusDirection,
|
||||
layout::{floating::ResizeState, tiling::NodeDesc},
|
||||
Direction,
|
||||
Direction, ManagedLayer,
|
||||
};
|
||||
|
||||
space_elements! {
|
||||
|
|
@ -84,18 +84,25 @@ space_elements! {
|
|||
Stack=CosmicStack,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MaximizedState {
|
||||
pub original_geometry: Rectangle<i32, Local>,
|
||||
pub original_layer: ManagedLayer,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CosmicMapped {
|
||||
element: CosmicMappedInternal,
|
||||
|
||||
// associated data
|
||||
last_cursor_position: Arc<Mutex<HashMap<usize, Point<f64, Logical>>>>,
|
||||
pub maximized_state: Arc<Mutex<Option<MaximizedState>>>,
|
||||
|
||||
//tiling
|
||||
pub tiling_node_id: Arc<Mutex<Option<NodeId>>>,
|
||||
//floating
|
||||
pub(super) last_geometry: Arc<Mutex<Option<Rectangle<i32, Logical>>>>,
|
||||
pub(super) resize_state: Arc<Mutex<Option<ResizeState>>>,
|
||||
pub last_geometry: Arc<Mutex<Option<Rectangle<i32, Local>>>>,
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
debug: Arc<Mutex<Option<smithay_egui::EguiState>>>,
|
||||
|
|
@ -106,8 +113,10 @@ impl fmt::Debug for CosmicMapped {
|
|||
f.debug_struct("CosmicMapped")
|
||||
.field("element", &self.element)
|
||||
.field("last_cursor_position", &self.last_cursor_position)
|
||||
.field("maximized_state", &self.maximized_state)
|
||||
.field("tiling_node_id", &self.tiling_node_id)
|
||||
.field("resize_state", &self.resize_state)
|
||||
.field("last_geometry", &self.last_geometry)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
|
@ -361,7 +370,15 @@ impl CosmicMapped {
|
|||
window.is_activated(pending)
|
||||
}
|
||||
|
||||
pub fn set_geometry(&self, geo: Rectangle<i32, Logical>) {
|
||||
pub fn pending_size(&self) -> Option<Size<i32, Logical>> {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => s.pending_size(),
|
||||
CosmicMappedInternal::Window(w) => w.pending_size(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_geometry(&self, geo: Rectangle<i32, Global>) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => s.set_geometry(geo),
|
||||
CosmicMappedInternal::Window(w) => w.set_geometry(geo),
|
||||
|
|
@ -491,7 +508,8 @@ impl CosmicMapped {
|
|||
|
||||
pub fn convert_to_stack<'a>(
|
||||
&mut self,
|
||||
outputs: impl Iterator<Item = (&'a Output, Rectangle<i32, Logical>)>,
|
||||
(output, overlap): (&'a Output, Rectangle<i32, Logical>),
|
||||
theme: cosmic::Theme,
|
||||
) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Window(window) => {
|
||||
|
|
@ -499,13 +517,11 @@ impl CosmicMapped {
|
|||
let activated = surface.is_activated(true);
|
||||
let handle = window.loop_handle();
|
||||
|
||||
let stack = CosmicStack::new(std::iter::once(surface), handle);
|
||||
let stack = CosmicStack::new(std::iter::once(surface), handle, theme);
|
||||
if let Some(geo) = self.last_geometry.lock().unwrap().clone() {
|
||||
stack.set_geometry(geo);
|
||||
}
|
||||
for (output, overlap) in outputs {
|
||||
stack.output_enter(output, overlap);
|
||||
stack.set_geometry(geo.to_global(&output));
|
||||
}
|
||||
stack.output_enter(output, overlap);
|
||||
stack.set_activate(activated);
|
||||
stack.active().send_configure();
|
||||
stack.refresh();
|
||||
|
|
@ -519,19 +535,18 @@ impl CosmicMapped {
|
|||
pub fn convert_to_surface<'a>(
|
||||
&mut self,
|
||||
surface: CosmicSurface,
|
||||
outputs: impl Iterator<Item = (&'a Output, Rectangle<i32, Logical>)>,
|
||||
(output, overlap): (&'a Output, Rectangle<i32, Logical>),
|
||||
theme: cosmic::Theme,
|
||||
) {
|
||||
let handle = self.loop_handle();
|
||||
surface.try_force_undecorated(false);
|
||||
surface.set_tiled(false);
|
||||
let window = CosmicWindow::new(surface, handle);
|
||||
let window = CosmicWindow::new(surface, handle, theme);
|
||||
|
||||
if let Some(geo) = self.last_geometry.lock().unwrap().clone() {
|
||||
window.set_geometry(geo);
|
||||
}
|
||||
for (output, overlap) in outputs {
|
||||
window.output_enter(output, overlap);
|
||||
window.set_geometry(geo.to_global(&output));
|
||||
}
|
||||
window.output_enter(output, overlap);
|
||||
window.set_activate(self.is_activated(true));
|
||||
window.surface().send_configure();
|
||||
window.refresh();
|
||||
|
|
@ -759,6 +774,22 @@ impl CosmicMapped {
|
|||
popup_elements.into_iter().map(C::from).collect(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn update_theme(&self, theme: cosmic::Theme) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Window(w) => w.set_theme(theme),
|
||||
CosmicMappedInternal::Stack(s) => s.set_theme(theme),
|
||||
CosmicMappedInternal::_GenericCatcher(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn force_redraw(&self) {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Window(w) => w.force_redraw(),
|
||||
CosmicMappedInternal::Stack(s) => s.force_redraw(),
|
||||
CosmicMappedInternal::_GenericCatcher(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IsAlive for CosmicMapped {
|
||||
|
|
@ -1067,9 +1098,10 @@ impl From<CosmicWindow> for CosmicMapped {
|
|||
CosmicMapped {
|
||||
element: CosmicMappedInternal::Window(w),
|
||||
last_cursor_position: Arc::new(Mutex::new(HashMap::new())),
|
||||
maximized_state: Arc::new(Mutex::new(None)),
|
||||
tiling_node_id: Arc::new(Mutex::new(None)),
|
||||
last_geometry: Arc::new(Mutex::new(None)),
|
||||
resize_state: Arc::new(Mutex::new(None)),
|
||||
last_geometry: Arc::new(Mutex::new(None)),
|
||||
#[cfg(feature = "debug")]
|
||||
debug: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
|
|
@ -1081,9 +1113,10 @@ impl From<CosmicStack> for CosmicMapped {
|
|||
CosmicMapped {
|
||||
element: CosmicMappedInternal::Stack(s),
|
||||
last_cursor_position: Arc::new(Mutex::new(HashMap::new())),
|
||||
maximized_state: Arc::new(Mutex::new(None)),
|
||||
tiling_node_id: Arc::new(Mutex::new(None)),
|
||||
last_geometry: Arc::new(Mutex::new(None)),
|
||||
resize_state: Arc::new(Mutex::new(None)),
|
||||
last_geometry: Arc::new(Mutex::new(None)),
|
||||
#[cfg(feature = "debug")]
|
||||
debug: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ pub fn resize_indicator(
|
|||
direction: ResizeDirection,
|
||||
config: &Config,
|
||||
evlh: LoopHandle<'static, crate::state::State>,
|
||||
theme: cosmic::Theme,
|
||||
) -> ResizeIndicator {
|
||||
ResizeIndicator::new(
|
||||
ResizeIndicatorInternal {
|
||||
|
|
@ -49,6 +50,7 @@ pub fn resize_indicator(
|
|||
},
|
||||
Size::from((1, 1)),
|
||||
evlh,
|
||||
theme,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
},
|
||||
state::State,
|
||||
utils::iced::{IcedElement, Program},
|
||||
utils::prelude::SeatExt,
|
||||
utils::prelude::*,
|
||||
wayland::handlers::screencopy::ScreencopySessions,
|
||||
};
|
||||
use calloop::LoopHandle;
|
||||
|
|
@ -91,7 +91,7 @@ pub struct CosmicStackInternal {
|
|||
override_alive: Arc<AtomicBool>,
|
||||
last_seat: Arc<Mutex<Option<(Seat<State>, Serial)>>>,
|
||||
last_location: Arc<Mutex<Option<(Point<f64, Logical>, Serial, u32)>>>,
|
||||
geometry: Arc<Mutex<Option<Rectangle<i32, Logical>>>>,
|
||||
geometry: Arc<Mutex<Option<Rectangle<i32, Global>>>>,
|
||||
mask: Arc<Mutex<Option<tiny_skia::Mask>>>,
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +129,7 @@ impl CosmicStack {
|
|||
pub fn new<I: Into<CosmicSurface>>(
|
||||
windows: impl Iterator<Item = I>,
|
||||
handle: LoopHandle<'static, crate::state::State>,
|
||||
theme: cosmic::Theme,
|
||||
) -> CosmicStack {
|
||||
let windows = windows.map(Into::into).collect::<Vec<_>>();
|
||||
assert!(!windows.is_empty());
|
||||
|
|
@ -160,6 +161,7 @@ impl CosmicStack {
|
|||
},
|
||||
(width, TAB_HEIGHT),
|
||||
handle,
|
||||
theme,
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -406,7 +408,17 @@ impl CosmicStack {
|
|||
Point::from((0, TAB_HEIGHT))
|
||||
}
|
||||
|
||||
pub fn set_geometry(&self, geo: Rectangle<i32, Logical>) {
|
||||
pub fn pending_size(&self) -> Option<Size<i32, Logical>> {
|
||||
self.0.with_program(|p| {
|
||||
p.geometry
|
||||
.lock()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.map(|geo| geo.size.as_logical())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_geometry(&self, geo: Rectangle<i32, Global>) {
|
||||
self.0.with_program(|p| {
|
||||
let loc = (geo.loc.x, geo.loc.y + TAB_HEIGHT);
|
||||
let size = (geo.size.w, geo.size.h - TAB_HEIGHT);
|
||||
|
|
@ -544,6 +556,14 @@ impl CosmicStack {
|
|||
popup_elements.into_iter().map(C::from).collect(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn set_theme(&self, theme: cosmic::Theme) {
|
||||
self.0.set_theme(theme);
|
||||
}
|
||||
|
||||
pub(crate) fn force_redraw(&self) {
|
||||
self.0.force_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -1047,20 +1067,21 @@ impl PointerTarget<State> for CosmicStack {
|
|||
if let Some(workspace) = data.common.shell.space_for(stack_mapped) {
|
||||
// TODO: Unify this somehow with Shell::move_request/Workspace::move_request
|
||||
let button = 0x110; // BTN_LEFT
|
||||
let pos = event.location;
|
||||
let pos = event.location.as_global();
|
||||
let start_data = PointerGrabStartData {
|
||||
focus: None,
|
||||
button,
|
||||
location: pos,
|
||||
location: pos.as_logical(),
|
||||
};
|
||||
let mapped = CosmicMapped::from(CosmicWindow::new(
|
||||
surface,
|
||||
self.0.loop_handle(),
|
||||
data.common.theme.clone(),
|
||||
));
|
||||
let elem_geo =
|
||||
workspace.element_geometry(stack_mapped).unwrap();
|
||||
let indicator_thickness =
|
||||
data.common.config.static_conf.active_hint;
|
||||
data.common.theme.cosmic().active_hint as u8;
|
||||
let was_tiled = workspace.is_tiled(stack_mapped);
|
||||
|
||||
self.remove_idx(dragged_out);
|
||||
|
|
|
|||
|
|
@ -248,6 +248,14 @@ where
|
|||
}
|
||||
|
||||
impl State {
|
||||
fn next_tab_animation(&self) -> Option<&TabAnimationState> {
|
||||
let now = Instant::now();
|
||||
|
||||
self.tab_animations
|
||||
.iter()
|
||||
.find(|anim| now.duration_since(anim.start_time) <= TAB_ANIMATION_DURATION)
|
||||
}
|
||||
|
||||
pub fn offset(&self, bounds: Rectangle, content_bounds: Size) -> Vector {
|
||||
if let Some(animation) = self.scroll_animation {
|
||||
let percentage = {
|
||||
|
|
@ -275,19 +283,41 @@ impl State {
|
|||
}
|
||||
|
||||
pub fn cleanup_old_animations(&mut self) {
|
||||
let start_time = Instant::now();
|
||||
|
||||
if let Some(animation) = self.scroll_animation.as_ref() {
|
||||
if Instant::now().duration_since(animation.start_time) > SCROLL_ANIMATION_DURATION {
|
||||
if start_time.duration_since(animation.start_time) > SCROLL_ANIMATION_DURATION {
|
||||
self.scroll_animation.take();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(animation) = self.tab_animations.front() {
|
||||
if Instant::now().duration_since(animation.start_time) > TAB_ANIMATION_DURATION {
|
||||
self.tab_animations.pop_front();
|
||||
if let Some(next_animation) = self.tab_animations.front_mut() {
|
||||
next_animation.start_time = Instant::now();
|
||||
Self::discard_expired_tab_animations(&mut self.tab_animations, start_time);
|
||||
}
|
||||
|
||||
/// Remove expired tab animations from the queue.
|
||||
fn discard_expired_tab_animations(
|
||||
tab_animations: &mut VecDeque<TabAnimationState>,
|
||||
start_time: Instant,
|
||||
) {
|
||||
if let Some(mut animation) = tab_animations.pop_front() {
|
||||
let mut set_next_start = false;
|
||||
|
||||
while start_time.duration_since(animation.start_time) > TAB_ANIMATION_DURATION {
|
||||
set_next_start = true;
|
||||
|
||||
if let Some(next) = tab_animations.pop_front() {
|
||||
animation = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if set_next_start {
|
||||
animation.start_time = start_time;
|
||||
}
|
||||
|
||||
tab_animations.push_front(animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -532,7 +562,9 @@ where
|
|||
|
||||
renderer.with_layer(bounds, |renderer| {
|
||||
renderer.with_translation(Vector::new(-offset.x, -offset.y), |renderer| {
|
||||
let percentage = if let Some(animation) = state.tab_animations.front() {
|
||||
let tab_animation = state.next_tab_animation();
|
||||
|
||||
let percentage = if let Some(animation) = tab_animation {
|
||||
let percentage = Instant::now()
|
||||
.duration_since(animation.start_time)
|
||||
.as_millis() as f32
|
||||
|
|
@ -547,7 +579,7 @@ where
|
|||
.zip(tree.children.iter().skip(2))
|
||||
.zip(layout.children().skip(2))
|
||||
{
|
||||
let bounds = if let Some(animation) = state.tab_animations.front() {
|
||||
let bounds = if let Some(animation) = tab_animation {
|
||||
let id = tab.as_widget().id().unwrap();
|
||||
let previous =
|
||||
animation
|
||||
|
|
@ -755,11 +787,15 @@ where
|
|||
|
||||
if unknown_keys || changes.is_some() {
|
||||
if !scrolling || !matches!(changes, Some(Difference::Focus)) {
|
||||
let start_time = Instant::now();
|
||||
|
||||
State::discard_expired_tab_animations(&mut state.tab_animations, start_time);
|
||||
|
||||
// new tab_animation
|
||||
state.tab_animations.push_back(TabAnimationState {
|
||||
previous_bounds: last_state.clone(),
|
||||
next_bounds: current_state.clone(),
|
||||
start_time: Instant::now(),
|
||||
start_time,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ pub type StackHover = IcedElement<StackHoverInternal>;
|
|||
pub fn stack_hover(
|
||||
evlh: LoopHandle<'static, crate::state::State>,
|
||||
size: Size<i32, Logical>,
|
||||
theme: cosmic::Theme,
|
||||
) -> StackHover {
|
||||
StackHover::new(StackHoverInternal, size, evlh)
|
||||
StackHover::new(StackHoverInternal, size, evlh, theme)
|
||||
}
|
||||
|
||||
pub struct StackHoverInternal;
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ use smithay::{
|
|||
|
||||
use crate::{
|
||||
state::{State, SurfaceDmabufFeedback},
|
||||
utils::prelude::*,
|
||||
wayland::handlers::decoration::PreferredDecorationMode,
|
||||
};
|
||||
|
||||
|
|
@ -122,13 +123,23 @@ impl CosmicSurface {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_geometry(&self, geo: Rectangle<i32, Logical>) {
|
||||
pub fn pending_size(&self) -> Option<Size<i32, Logical>> {
|
||||
match self {
|
||||
CosmicSurface::Wayland(window) => {
|
||||
window.toplevel().with_pending_state(|state| state.size)
|
||||
}
|
||||
CosmicSurface::X11(surface) => Some(surface.geometry().size),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_geometry(&self, geo: Rectangle<i32, Global>) {
|
||||
match self {
|
||||
CosmicSurface::Wayland(window) => window
|
||||
.toplevel()
|
||||
.with_pending_state(|state| state.size = Some(geo.size)),
|
||||
.with_pending_state(|state| state.size = Some(geo.size.as_logical())),
|
||||
CosmicSurface::X11(surface) => {
|
||||
let _ = surface.configure(geo);
|
||||
let _ = surface.configure(geo.as_logical());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,11 @@ use smithay::utils::Size;
|
|||
|
||||
pub type SwapIndicator = IcedElement<SwapIndicatorInternal>;
|
||||
|
||||
pub fn swap_indicator(evlh: LoopHandle<'static, crate::state::State>) -> SwapIndicator {
|
||||
SwapIndicator::new(SwapIndicatorInternal, Size::from((1, 1)), evlh)
|
||||
pub fn swap_indicator(
|
||||
evlh: LoopHandle<'static, crate::state::State>,
|
||||
theme: cosmic::Theme,
|
||||
) -> SwapIndicator {
|
||||
SwapIndicator::new(SwapIndicatorInternal, Size::from((1, 1)), evlh, theme)
|
||||
}
|
||||
|
||||
pub struct SwapIndicatorInternal;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
state::State,
|
||||
utils::{
|
||||
iced::{IcedElement, Program},
|
||||
prelude::SeatExt,
|
||||
prelude::*,
|
||||
},
|
||||
wayland::handlers::screencopy::ScreencopySessions,
|
||||
};
|
||||
|
|
@ -114,6 +114,7 @@ impl CosmicWindow {
|
|||
pub fn new(
|
||||
window: impl Into<CosmicSurface>,
|
||||
handle: LoopHandle<'static, crate::state::State>,
|
||||
theme: cosmic::Theme,
|
||||
) -> CosmicWindow {
|
||||
let window = window.into();
|
||||
let width = window.geometry().size.w;
|
||||
|
|
@ -129,10 +130,21 @@ impl CosmicWindow {
|
|||
},
|
||||
(width, SSD_HEIGHT),
|
||||
handle,
|
||||
theme,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn set_geometry(&self, geo: Rectangle<i32, Logical>) {
|
||||
pub fn pending_size(&self) -> Option<Size<i32, Logical>> {
|
||||
self.0.with_program(|p| {
|
||||
let mut size = p.window.pending_size()?;
|
||||
if p.has_ssd(true) {
|
||||
size.h += SSD_HEIGHT;
|
||||
}
|
||||
Some(size)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_geometry(&self, geo: Rectangle<i32, Global>) {
|
||||
self.0.with_program(|p| {
|
||||
let loc = (
|
||||
geo.loc.x,
|
||||
|
|
@ -213,6 +225,14 @@ impl CosmicWindow {
|
|||
popup_elements.into_iter().map(C::from).collect(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn set_theme(&self, theme: cosmic::Theme) {
|
||||
self.0.set_theme(theme);
|
||||
}
|
||||
|
||||
pub(crate) fn force_redraw(&self) {
|
||||
self.0.force_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -241,27 +261,20 @@ impl Program for CosmicWindowInternal {
|
|||
}
|
||||
}
|
||||
Message::Maximize => {
|
||||
if let Some((seat, _serial)) = self.last_seat.lock().unwrap().clone() {
|
||||
if let Some(surface) = self.window.wl_surface() {
|
||||
loop_handle.insert_idle(move |state| {
|
||||
if let Some(mapped) =
|
||||
state.common.shell.element_for_wl_surface(&surface).cloned()
|
||||
{
|
||||
if let Some(workspace) = state.common.shell.space_for_mut(&mapped) {
|
||||
let output = seat.active_output();
|
||||
let (window, _) = mapped
|
||||
.windows()
|
||||
.find(|(w, _)| w.wl_surface().as_ref() == Some(&surface))
|
||||
.unwrap();
|
||||
workspace.maximize_toggle(
|
||||
&window,
|
||||
&output,
|
||||
state.common.event_loop_handle.clone(),
|
||||
)
|
||||
}
|
||||
if let Some(surface) = self.window.wl_surface() {
|
||||
loop_handle.insert_idle(move |state| {
|
||||
if let Some(mapped) =
|
||||
state.common.shell.element_for_wl_surface(&surface).cloned()
|
||||
{
|
||||
if let Some(workspace) = state.common.shell.space_for_mut(&mapped) {
|
||||
let (window, _) = mapped
|
||||
.windows()
|
||||
.find(|(w, _)| w.wl_surface().as_ref() == Some(&surface))
|
||||
.unwrap();
|
||||
workspace.maximize_toggle(&window)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Message::Close => self.window.close(),
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ use crate::{
|
|||
state::Common,
|
||||
utils::prelude::*,
|
||||
wayland::handlers::xdg_shell::PopupGrabData,
|
||||
xwayland::XWaylandState,
|
||||
};
|
||||
use indexmap::IndexSet;
|
||||
use smithay::{
|
||||
|
|
@ -100,11 +99,9 @@ impl Shell {
|
|||
// update FocusStack and notify layouts about new focus (if any window)
|
||||
let element = match target {
|
||||
Some(KeyboardFocusTarget::Element(mapped)) => Some(mapped.clone()),
|
||||
Some(KeyboardFocusTarget::Fullscreen(window)) => state
|
||||
.common
|
||||
.shell
|
||||
.element_for_surface(&window.surface())
|
||||
.cloned(),
|
||||
Some(KeyboardFocusTarget::Fullscreen(window)) => {
|
||||
state.common.shell.element_for_surface(window).cloned()
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
|
@ -139,11 +136,7 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
fn update_active<'a, 'b>(
|
||||
&mut self,
|
||||
seats: impl Iterator<Item = &'a Seat<State>>,
|
||||
mut xwm: Option<&'b mut XWaylandState>,
|
||||
) {
|
||||
fn update_active<'a, 'b>(&mut self, seats: impl Iterator<Item = &'a Seat<State>>) {
|
||||
// update activate status
|
||||
let focused_windows = seats
|
||||
.flat_map(|seat| {
|
||||
|
|
@ -154,7 +147,7 @@ impl Shell {
|
|||
return None;
|
||||
}
|
||||
|
||||
Some(self.outputs.iter().flat_map(|o| {
|
||||
Some(self.outputs().flat_map(|o| {
|
||||
let space = self.active_space(o);
|
||||
let stack = space.focus_stack.get(seat);
|
||||
stack.last().cloned()
|
||||
|
|
@ -163,14 +156,10 @@ impl Shell {
|
|||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for output in self.outputs.iter() {
|
||||
let workspace = self.workspaces.active_mut(output);
|
||||
for output in self.outputs().cloned().collect::<Vec<_>>().into_iter() {
|
||||
// TODO: Add self.workspaces.active_workspaces()
|
||||
let workspace = self.workspaces.active_mut(&output);
|
||||
for focused in focused_windows.iter() {
|
||||
if let CosmicSurface::X11(window) = focused.active_window() {
|
||||
if let Some(xwm) = xwm.as_mut().and_then(|state| state.xwm.as_mut()) {
|
||||
let _ = xwm.raise_window(&window);
|
||||
}
|
||||
}
|
||||
raise_with_children(&mut workspace.floating_layer, focused);
|
||||
}
|
||||
for window in workspace.mapped() {
|
||||
|
|
@ -213,18 +202,15 @@ impl Common {
|
|||
) {
|
||||
Shell::set_focus(state, target, active_seat, serial);
|
||||
let seats = state.common.seats().cloned().collect::<Vec<_>>();
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.update_active(seats.iter(), state.common.xwayland_state.as_mut());
|
||||
state.common.shell.update_active(seats.iter());
|
||||
}
|
||||
|
||||
pub fn refresh_focus(state: &mut State) {
|
||||
let seats = state.common.seats().cloned().collect::<Vec<_>>();
|
||||
for seat in seats {
|
||||
let output = seat.active_output();
|
||||
if !state.common.shell.outputs.contains(&output) {
|
||||
seat.set_active_output(&state.common.shell.outputs[0]);
|
||||
if !state.common.shell.outputs().any(|o| o == &output) {
|
||||
seat.set_active_output(&state.common.shell.outputs().next().unwrap());
|
||||
continue;
|
||||
}
|
||||
let last_known_focus = ActiveFocus::get(&seat);
|
||||
|
|
@ -236,7 +222,7 @@ impl Common {
|
|||
let workspace = state.common.shell.active_space(&output);
|
||||
let focus_stack = workspace.focus_stack.get(&seat);
|
||||
if focus_stack.last().map(|m| m == &mapped).unwrap_or(false)
|
||||
&& workspace.get_fullscreen(&output).is_none()
|
||||
&& workspace.get_fullscreen().is_none()
|
||||
{
|
||||
continue; // Focus is valid
|
||||
} else {
|
||||
|
|
@ -248,11 +234,16 @@ impl Common {
|
|||
continue; // Focus is valid
|
||||
}
|
||||
}
|
||||
KeyboardFocusTarget::Group(WindowGroup {
|
||||
output: weak_output,
|
||||
..
|
||||
}) => {
|
||||
if weak_output == output {
|
||||
KeyboardFocusTarget::Group(WindowGroup { node, .. }) => {
|
||||
if state
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
.active(&output)
|
||||
.1
|
||||
.tiling_layer
|
||||
.has_node(&node)
|
||||
{
|
||||
continue; // Focus is valid,
|
||||
}
|
||||
}
|
||||
|
|
@ -262,9 +253,9 @@ impl Common {
|
|||
|
||||
if focus_stack
|
||||
.last()
|
||||
.map(|m| m.has_active_window(&window.surface()))
|
||||
.map(|m| m.has_active_window(&window))
|
||||
.unwrap_or(false)
|
||||
&& workspace.get_fullscreen(&output).is_some()
|
||||
&& workspace.get_fullscreen().is_some()
|
||||
{
|
||||
continue; // Focus is valid
|
||||
} else {
|
||||
|
|
@ -276,6 +267,31 @@ impl Common {
|
|||
}
|
||||
};
|
||||
} else {
|
||||
if let KeyboardFocusTarget::Popup(_) = target {
|
||||
if let Some(popup_grab) = seat
|
||||
.user_data()
|
||||
.get::<PopupGrabData>()
|
||||
.and_then(|x| x.take())
|
||||
{
|
||||
if !popup_grab.has_ended() {
|
||||
if let Some(new) = popup_grab.current_grab() {
|
||||
trace!("restore focus to previous popup grab");
|
||||
if let Some(keyboard) = seat.get_keyboard() {
|
||||
keyboard.set_focus(
|
||||
state,
|
||||
Some(new.clone()),
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
);
|
||||
}
|
||||
ActiveFocus::set(&seat, Some(new));
|
||||
seat.user_data()
|
||||
.get_or_insert::<PopupGrabData, _>(PopupGrabData::default)
|
||||
.set(Some(popup_grab));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
trace!("Surface dead, focus fixup");
|
||||
}
|
||||
} else {
|
||||
|
|
@ -307,7 +323,7 @@ impl Common {
|
|||
.common
|
||||
.shell
|
||||
.active_space(&output)
|
||||
.get_fullscreen(&output)
|
||||
.get_fullscreen()
|
||||
.cloned()
|
||||
.map(KeyboardFocusTarget::Fullscreen)
|
||||
.or_else(|| {
|
||||
|
|
@ -330,9 +346,6 @@ impl Common {
|
|||
}
|
||||
|
||||
let seats = state.common.seats().cloned().collect::<Vec<_>>();
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.update_active(seats.iter(), state.common.xwayland_state.as_mut())
|
||||
state.common.shell.update_active(seats.iter())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
use std::sync::Weak;
|
||||
|
||||
use crate::{
|
||||
shell::{
|
||||
element::{CosmicMapped, CosmicWindow},
|
||||
layout::tiling::ResizeForkTarget,
|
||||
},
|
||||
shell::{element::CosmicMapped, layout::tiling::ResizeForkTarget, CosmicSurface},
|
||||
utils::prelude::*,
|
||||
wayland::handlers::xdg_shell::popup::get_popup_toplevel,
|
||||
};
|
||||
|
|
@ -22,7 +19,6 @@ use smithay::{
|
|||
},
|
||||
Seat,
|
||||
},
|
||||
output::WeakOutput,
|
||||
reexports::wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface, Resource},
|
||||
utils::{IsAlive, Serial},
|
||||
wayland::seat::WaylandFocus,
|
||||
|
|
@ -32,7 +28,7 @@ use smithay::{
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum PointerFocusTarget {
|
||||
Element(CosmicMapped),
|
||||
Fullscreen(CosmicWindow),
|
||||
Fullscreen(CosmicSurface),
|
||||
LayerSurface(LayerSurface),
|
||||
Popup(PopupKind),
|
||||
OverrideRedirect(X11Surface),
|
||||
|
|
@ -42,7 +38,7 @@ pub enum PointerFocusTarget {
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum KeyboardFocusTarget {
|
||||
Element(CosmicMapped),
|
||||
Fullscreen(CosmicWindow),
|
||||
Fullscreen(CosmicSurface),
|
||||
Group(WindowGroup),
|
||||
LayerSurface(LayerSurface),
|
||||
Popup(PopupKind),
|
||||
|
|
@ -87,16 +83,13 @@ impl KeyboardFocusTarget {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct WindowGroup {
|
||||
pub node: NodeId,
|
||||
pub output: WeakOutput,
|
||||
pub alive: Weak<()>,
|
||||
pub focus_stack: Vec<NodeId>,
|
||||
}
|
||||
|
||||
impl PartialEq for WindowGroup {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.node == other.node
|
||||
&& self.output == other.output
|
||||
&& Weak::ptr_eq(&self.alive, &other.alive)
|
||||
self.node == other.node && Weak::ptr_eq(&self.alive, &other.alive)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -564,8 +557,8 @@ impl From<CosmicMapped> for PointerFocusTarget {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<CosmicWindow> for PointerFocusTarget {
|
||||
fn from(s: CosmicWindow) -> Self {
|
||||
impl From<CosmicSurface> for PointerFocusTarget {
|
||||
fn from(s: CosmicSurface) -> Self {
|
||||
PointerFocusTarget::Fullscreen(s)
|
||||
}
|
||||
}
|
||||
|
|
@ -600,8 +593,8 @@ impl From<CosmicMapped> for KeyboardFocusTarget {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<CosmicWindow> for KeyboardFocusTarget {
|
||||
fn from(s: CosmicWindow) -> Self {
|
||||
impl From<CosmicSurface> for KeyboardFocusTarget {
|
||||
fn from(s: CosmicSurface) -> Self {
|
||||
KeyboardFocusTarget::Fullscreen(s)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use crate::{
|
|||
utils::prelude::*,
|
||||
};
|
||||
|
||||
use cosmic::theme::CosmicTheme;
|
||||
use smithay::{
|
||||
backend::renderer::{
|
||||
element::{utils::RescaleRenderElement, AsRenderElements, RenderElement},
|
||||
|
|
@ -58,7 +59,13 @@ pub struct MoveGrabState {
|
|||
}
|
||||
|
||||
impl MoveGrabState {
|
||||
pub fn render<I, R>(&self, renderer: &mut R, seat: &Seat<State>, output: &Output) -> Vec<I>
|
||||
pub fn render<I, R>(
|
||||
&self,
|
||||
renderer: &mut R,
|
||||
seat: &Seat<State>,
|
||||
output: &Output,
|
||||
theme: &CosmicTheme,
|
||||
) -> Vec<I>
|
||||
where
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
|
|
@ -77,21 +84,33 @@ impl MoveGrabState {
|
|||
} else {
|
||||
1.0
|
||||
};
|
||||
let alpha = if &seat.active_output() == output {
|
||||
1.0
|
||||
} else {
|
||||
0.4
|
||||
};
|
||||
|
||||
let cursor_at = seat.get_pointer().unwrap().current_location();
|
||||
|
||||
let mut window_geo = self.window.geometry();
|
||||
window_geo.loc += cursor_at.to_i32_round() + self.window_offset;
|
||||
if !output.geometry().intersection(window_geo).is_some() {
|
||||
if !output
|
||||
.geometry()
|
||||
.as_logical()
|
||||
.intersection(window_geo)
|
||||
.is_some()
|
||||
{
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let output_scale: Scale<f64> = output.current_scale().fractional_scale().into();
|
||||
let scaling_offset =
|
||||
self.window_offset - self.window_offset.to_f64().upscale(scale).to_i32_round();
|
||||
let render_location =
|
||||
cursor_at.to_i32_round() - output.geometry().loc + self.window_offset - scaling_offset;
|
||||
let render_location = cursor_at.to_i32_round() - output.geometry().loc.as_logical()
|
||||
+ self.window_offset
|
||||
- scaling_offset;
|
||||
|
||||
let active_window_hint = crate::theme::active_window_hint(theme);
|
||||
let focus_element = if self.indicator_thickness > 0 {
|
||||
Some(
|
||||
CosmicMappedRenderElement::from(IndicatorShader::focus_element(
|
||||
|
|
@ -105,10 +124,16 @@ impl MoveGrabState {
|
|||
.to_f64()
|
||||
.upscale(scale)
|
||||
.to_i32_round(),
|
||||
),
|
||||
)
|
||||
.as_local(),
|
||||
self.indicator_thickness,
|
||||
output_scale.x,
|
||||
1.0,
|
||||
alpha,
|
||||
[
|
||||
active_window_hint.red,
|
||||
active_window_hint.green,
|
||||
active_window_hint.blue,
|
||||
],
|
||||
))
|
||||
.into(),
|
||||
)
|
||||
|
|
@ -123,7 +148,7 @@ impl MoveGrabState {
|
|||
(render_location - self.window.geometry().loc)
|
||||
.to_physical_precise_round(output_scale),
|
||||
output_scale,
|
||||
1.0,
|
||||
alpha,
|
||||
);
|
||||
|
||||
self.stacking_indicator
|
||||
|
|
@ -208,6 +233,7 @@ impl PointerGrab<State> for MoveGrab {
|
|||
.find(|output| {
|
||||
output
|
||||
.geometry()
|
||||
.as_logical()
|
||||
.contains(handle.current_location().to_i32_round())
|
||||
})
|
||||
.cloned()
|
||||
|
|
@ -221,7 +247,7 @@ impl PointerGrab<State> for MoveGrab {
|
|||
.workspaces
|
||||
.active_mut(&self.cursor_output)
|
||||
.tiling_layer
|
||||
.cleanup_drag(&self.cursor_output);
|
||||
.cleanup_drag();
|
||||
self.cursor_output = current_output.clone();
|
||||
}
|
||||
|
||||
|
|
@ -233,8 +259,8 @@ impl PointerGrab<State> for MoveGrab {
|
|||
if let Some(grab_state) = borrow.as_mut().and_then(|s| s.as_mut()) {
|
||||
let mut window_geo = self.window.geometry();
|
||||
window_geo.loc += event.location.to_i32_round() + grab_state.window_offset;
|
||||
for output in &state.common.shell.outputs {
|
||||
if let Some(overlap) = output.geometry().intersection(window_geo) {
|
||||
for output in state.common.shell.outputs() {
|
||||
if let Some(overlap) = output.geometry().as_logical().intersection(window_geo) {
|
||||
if self.window_outputs.insert(output.clone()) {
|
||||
self.window.output_enter(output, overlap);
|
||||
if let Some(indicator) =
|
||||
|
|
@ -261,11 +287,21 @@ impl PointerGrab<State> for MoveGrab {
|
|||
|
||||
if indicator_location.is_some() != grab_state.stacking_indicator.is_some() {
|
||||
grab_state.stacking_indicator = indicator_location.map(|geo| {
|
||||
let element = stack_hover(state.common.event_loop_handle.clone(), geo.size);
|
||||
let element = stack_hover(
|
||||
state.common.event_loop_handle.clone(),
|
||||
geo.size.as_logical(),
|
||||
state.common.theme.clone(),
|
||||
);
|
||||
for output in &self.window_outputs {
|
||||
element.output_enter(output, output.geometry());
|
||||
element.output_enter(
|
||||
output,
|
||||
Rectangle::from_loc_and_size(
|
||||
(0, 0),
|
||||
output.geometry().size.as_logical(),
|
||||
),
|
||||
);
|
||||
}
|
||||
(element, geo.loc)
|
||||
(element, geo.loc.as_logical())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -397,8 +433,8 @@ impl MoveGrab {
|
|||
start_data: PointerGrabStartData<State>,
|
||||
window: CosmicMapped,
|
||||
seat: &Seat<State>,
|
||||
initial_cursor_location: Point<f64, Logical>,
|
||||
initial_window_location: Point<i32, Logical>,
|
||||
initial_cursor_location: Point<f64, Global>,
|
||||
initial_window_location: Point<i32, Global>,
|
||||
indicator_thickness: u8,
|
||||
was_tiled: bool,
|
||||
) -> MoveGrab {
|
||||
|
|
@ -409,7 +445,8 @@ impl MoveGrab {
|
|||
|
||||
let grab_state = MoveGrabState {
|
||||
window: window.clone(),
|
||||
window_offset: initial_window_location - initial_cursor_location.to_i32_round(),
|
||||
window_offset: (initial_window_location - initial_cursor_location.to_i32_round())
|
||||
.as_logical(),
|
||||
indicator_thickness,
|
||||
start: Instant::now(),
|
||||
stacking_indicator: None,
|
||||
|
|
@ -451,16 +488,16 @@ impl MoveGrab {
|
|||
// No more buttons are pressed, release the grab.
|
||||
let output = self.seat.active_output();
|
||||
|
||||
let position = if let Some(grab_state) = self
|
||||
let position: Option<(CosmicMapped, Point<i32, Global>)> = if let Some(grab_state) = self
|
||||
.seat
|
||||
.user_data()
|
||||
.get::<SeatMoveGrabState>()
|
||||
.and_then(|s| s.borrow_mut().take())
|
||||
{
|
||||
if grab_state.window.alive() {
|
||||
let window_location = handle.current_location().to_i32_round()
|
||||
- output.geometry().loc
|
||||
+ grab_state.window_offset;
|
||||
let window_location = (handle.current_location().to_i32_round()
|
||||
+ grab_state.window_offset)
|
||||
.as_global();
|
||||
|
||||
let workspace_handle = state.common.shell.active_space(&output).handle;
|
||||
for old_output in self.window_outputs.iter().filter(|o| *o != &output) {
|
||||
|
|
@ -480,41 +517,26 @@ impl MoveGrab {
|
|||
}
|
||||
|
||||
if self.tiling {
|
||||
Some(
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.active_space_mut(&output)
|
||||
.tiling_layer
|
||||
.drop_window(grab_state.window, &output, handle.current_location()),
|
||||
)
|
||||
} else {
|
||||
let offset = state
|
||||
.common
|
||||
.shell
|
||||
.active_space(&output)
|
||||
.floating_layer
|
||||
.space
|
||||
.output_geometry(&output)
|
||||
.unwrap()
|
||||
.loc;
|
||||
grab_state.window.set_geometry(Rectangle::from_loc_and_size(
|
||||
window_location + offset,
|
||||
grab_state.window.geometry().size,
|
||||
));
|
||||
state
|
||||
let (window, location) = state
|
||||
.common
|
||||
.shell
|
||||
.active_space_mut(&output)
|
||||
.floating_layer
|
||||
.map_internal(grab_state.window, &output, Some(window_location + offset));
|
||||
.tiling_layer
|
||||
.drop_window(grab_state.window);
|
||||
Some((window, location.to_global(&output)))
|
||||
} else {
|
||||
grab_state.window.set_geometry(Rectangle::from_loc_and_size(
|
||||
window_location,
|
||||
grab_state.window.geometry().size.as_global(),
|
||||
));
|
||||
let workspace = state.common.shell.active_space_mut(&output);
|
||||
workspace.floating_layer.map_internal(
|
||||
grab_state.window,
|
||||
Some(window_location.to_local(&workspace.output)),
|
||||
None,
|
||||
);
|
||||
|
||||
let pointer_pos = handle.current_location();
|
||||
let relative_pos = state.common.shell.map_global_to_space(pointer_pos, &output);
|
||||
Some((
|
||||
self.window.clone(),
|
||||
window_location + offset + (pointer_pos - relative_pos).to_i32_round(),
|
||||
))
|
||||
Some((self.window.clone(), window_location))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
|
|
@ -523,7 +545,7 @@ impl MoveGrab {
|
|||
None
|
||||
};
|
||||
|
||||
handle.unset_grab(state, serial, time);
|
||||
handle.unset_grab(state, serial, time, true);
|
||||
|
||||
{
|
||||
let cursor_state = self.seat.user_data().get::<CursorState>().unwrap();
|
||||
|
|
@ -535,7 +557,7 @@ impl MoveGrab {
|
|||
state,
|
||||
Some((
|
||||
PointerFocusTarget::from(mapped.clone()),
|
||||
position - self.window.geometry().loc,
|
||||
position.as_logical() - self.window.geometry().loc,
|
||||
)),
|
||||
&MotionEvent {
|
||||
location: handle.current_location(),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::{
|
||||
shell::{
|
||||
element::CosmicMapped, focus::target::PointerFocusTarget, grabs::ResizeEdge, CosmicSurface,
|
||||
|
|
@ -8,11 +10,15 @@ use crate::{
|
|||
};
|
||||
use smithay::{
|
||||
desktop::space::SpaceElement,
|
||||
input::pointer::{
|
||||
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent,
|
||||
GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent,
|
||||
GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData,
|
||||
MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent,
|
||||
input::{
|
||||
pointer::{
|
||||
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent,
|
||||
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,
|
||||
GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent,
|
||||
GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle,
|
||||
RelativeMotionEvent,
|
||||
},
|
||||
Seat,
|
||||
},
|
||||
utils::{IsAlive, Logical, Point, Rectangle, Size},
|
||||
};
|
||||
|
|
@ -39,6 +45,7 @@ pub enum ResizeState {
|
|||
|
||||
pub struct ResizeSurfaceGrab {
|
||||
start_data: PointerGrabStartData<State>,
|
||||
seat: Seat<State>,
|
||||
window: CosmicMapped,
|
||||
edges: ResizeEdge,
|
||||
initial_window_size: Size<i32, Logical>,
|
||||
|
|
@ -58,7 +65,13 @@ impl PointerGrab<State> for ResizeSurfaceGrab {
|
|||
|
||||
// It is impossible to get `min_size` and `max_size` of dead toplevel, so we return early.
|
||||
if !self.window.alive() {
|
||||
handle.unset_grab(data, event.serial, event.time);
|
||||
self.seat
|
||||
.user_data()
|
||||
.get::<ResizeGrabMarker>()
|
||||
.unwrap()
|
||||
.0
|
||||
.store(false, Ordering::SeqCst);
|
||||
handle.unset_grab(data, event.serial, event.time, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -101,10 +114,10 @@ impl PointerGrab<State> for ResizeSurfaceGrab {
|
|||
self.window.set_resizing(true);
|
||||
self.window.set_geometry(Rectangle::from_loc_and_size(
|
||||
match self.window.active_window() {
|
||||
CosmicSurface::X11(s) => s.geometry().loc,
|
||||
CosmicSurface::X11(s) => s.geometry().loc.as_global(),
|
||||
_ => (0, 0).into(),
|
||||
},
|
||||
self.last_window_size,
|
||||
self.last_window_size.as_global(),
|
||||
));
|
||||
self.window.configure();
|
||||
}
|
||||
|
|
@ -129,7 +142,13 @@ impl PointerGrab<State> for ResizeSurfaceGrab {
|
|||
handle.button(data, event);
|
||||
if handle.current_pressed().is_empty() {
|
||||
// No more buttons are pressed, release the grab.
|
||||
handle.unset_grab(data, event.serial, event.time);
|
||||
self.seat
|
||||
.user_data()
|
||||
.get::<ResizeGrabMarker>()
|
||||
.unwrap()
|
||||
.0
|
||||
.store(false, Ordering::SeqCst);
|
||||
handle.unset_grab(data, event.serial, event.time, true);
|
||||
|
||||
// If toplevel is dead, we can't resize it, so we return early.
|
||||
if !self.window.alive() {
|
||||
|
|
@ -139,10 +158,10 @@ impl PointerGrab<State> for ResizeSurfaceGrab {
|
|||
self.window.set_resizing(false);
|
||||
self.window.set_geometry(Rectangle::from_loc_and_size(
|
||||
match self.window.active_window() {
|
||||
CosmicSurface::X11(s) => s.geometry().loc,
|
||||
CosmicSurface::X11(s) => s.geometry().loc.as_global(),
|
||||
_ => (0, 0).into(),
|
||||
},
|
||||
self.last_window_size,
|
||||
self.last_window_size.as_global(),
|
||||
));
|
||||
self.window.configure();
|
||||
|
||||
|
|
@ -245,6 +264,14 @@ impl PointerGrab<State> for ResizeSurfaceGrab {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ResizeGrabMarker(AtomicBool);
|
||||
|
||||
impl ResizeGrabMarker {
|
||||
pub fn get(&self) -> bool {
|
||||
self.0.load(Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
impl ResizeSurfaceGrab {
|
||||
pub fn new(
|
||||
start_data: PointerGrabStartData<State>,
|
||||
|
|
@ -252,6 +279,7 @@ impl ResizeSurfaceGrab {
|
|||
edges: ResizeEdge,
|
||||
initial_window_location: Point<i32, Logical>,
|
||||
initial_window_size: Size<i32, Logical>,
|
||||
seat: &Seat<State>,
|
||||
) -> ResizeSurfaceGrab {
|
||||
let resize_state = ResizeState::Resizing(ResizeData {
|
||||
edges,
|
||||
|
|
@ -260,9 +288,14 @@ impl ResizeSurfaceGrab {
|
|||
});
|
||||
|
||||
*mapped.resize_state.lock().unwrap() = Some(resize_state);
|
||||
seat.user_data()
|
||||
.get_or_insert::<ResizeGrabMarker, _>(|| ResizeGrabMarker(AtomicBool::new(true)))
|
||||
.0
|
||||
.store(true, Ordering::SeqCst);
|
||||
|
||||
ResizeSurfaceGrab {
|
||||
start_data,
|
||||
seat: seat.clone(),
|
||||
window: mapped,
|
||||
edges,
|
||||
initial_window_size,
|
||||
|
|
@ -271,7 +304,13 @@ impl ResizeSurfaceGrab {
|
|||
}
|
||||
|
||||
pub fn apply_resize_to_location(window: CosmicMapped, space: &mut Workspace) {
|
||||
if let Some(location) = space.floating_layer.space.element_location(&window) {
|
||||
if let Some(location) = space
|
||||
.floating_layer
|
||||
.space
|
||||
.element_location(&window)
|
||||
.map(PointExt::as_local)
|
||||
.map(|p| p.to_global(space.output()))
|
||||
{
|
||||
let mut new_location = None;
|
||||
|
||||
let mut resize_state = window.resize_state.lock().unwrap();
|
||||
|
|
@ -316,13 +355,13 @@ impl ResizeSurfaceGrab {
|
|||
CosmicSurface::Wayland(window) => {
|
||||
update_reactive_popups(
|
||||
&window,
|
||||
new_location + offset,
|
||||
new_location + offset.as_global(),
|
||||
space.floating_layer.space.outputs(),
|
||||
);
|
||||
}
|
||||
CosmicSurface::X11(surface) => {
|
||||
let mut geometry = surface.geometry();
|
||||
geometry.loc += location - new_location;
|
||||
geometry.loc += (location - new_location).as_logical();
|
||||
let _ = surface.configure(geometry);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
|
@ -331,7 +370,7 @@ impl ResizeSurfaceGrab {
|
|||
space
|
||||
.floating_layer
|
||||
.space
|
||||
.map_element(window, new_location, false);
|
||||
.map_element(window, new_location.as_logical(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use smithay::{
|
|||
utils::{Logical, Point, Rectangle, Size},
|
||||
wayland::seat::WaylandFocus,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
backend::render::{element::AsGlowRenderer, IndicatorShader, Key, Usage},
|
||||
|
|
@ -28,9 +27,7 @@ use crate::{
|
|||
},
|
||||
state::State,
|
||||
utils::prelude::*,
|
||||
wayland::{
|
||||
handlers::xdg_shell::popup::get_popup_toplevel, protocols::toplevel_info::ToplevelInfoState,
|
||||
},
|
||||
wayland::handlers::xdg_shell::popup::get_popup_toplevel,
|
||||
};
|
||||
|
||||
mod grabs;
|
||||
|
|
@ -42,67 +39,68 @@ pub struct FloatingLayout {
|
|||
}
|
||||
|
||||
impl FloatingLayout {
|
||||
pub fn new() -> FloatingLayout {
|
||||
Default::default()
|
||||
pub fn new(output: &Output) -> FloatingLayout {
|
||||
let mut layout = Self::default();
|
||||
layout.space.map_output(output, (0, 0));
|
||||
layout
|
||||
}
|
||||
|
||||
pub fn map_output(&mut self, output: &Output, location: Point<i32, Logical>) {
|
||||
self.space.map_output(output, location)
|
||||
}
|
||||
pub fn set_output(&mut self, output: &Output) {
|
||||
let old_output = self.space.outputs().next().unwrap().clone();
|
||||
self.space.unmap_output(&old_output);
|
||||
self.space.map_output(output, (0, 0));
|
||||
|
||||
/*
|
||||
TODO: rescale all positions? (evem rescale windows?)
|
||||
*/
|
||||
|
||||
pub fn unmap_output(
|
||||
&mut self,
|
||||
output: &Output,
|
||||
toplevel_info: &mut ToplevelInfoState<State, CosmicSurface>,
|
||||
) {
|
||||
let windows = self
|
||||
.space
|
||||
.elements_for_output(output)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
for window in &windows {
|
||||
for (toplevel, _) in window.windows() {
|
||||
toplevel_info.toplevel_leave_output(&toplevel, output);
|
||||
}
|
||||
}
|
||||
self.space.unmap_output(output);
|
||||
self.refresh();
|
||||
for window in &windows {
|
||||
for output in self.space.outputs_for_element(&window) {
|
||||
for (toplevel, _) in window.windows() {
|
||||
toplevel_info.toplevel_enter_output(&toplevel, &output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map(
|
||||
&mut self,
|
||||
mapped: impl Into<CosmicMapped>,
|
||||
seat: &Seat<State>,
|
||||
position: impl Into<Option<Point<i32, Logical>>>,
|
||||
position: impl Into<Option<Point<i32, Local>>>,
|
||||
) {
|
||||
let mapped = mapped.into();
|
||||
let output = seat.active_output();
|
||||
let position = position.into();
|
||||
|
||||
self.map_internal(mapped, &output, position)
|
||||
self.map_internal(mapped, position, None)
|
||||
}
|
||||
|
||||
pub fn map_maximized(&mut self, mapped: CosmicMapped) {
|
||||
let output = self.space.outputs().next().unwrap().clone();
|
||||
let layers = layer_map_for_output(&output);
|
||||
let geometry = layers.non_exclusive_zone().as_local();
|
||||
|
||||
mapped.set_bounds(geometry.size.as_logical());
|
||||
mapped.set_tiled(true);
|
||||
mapped.set_maximized(true);
|
||||
mapped.set_geometry(geometry.to_global(&output));
|
||||
mapped.configure();
|
||||
|
||||
self.space
|
||||
.map_element(mapped, geometry.loc.as_logical(), true);
|
||||
}
|
||||
|
||||
pub(in crate::shell) fn map_internal(
|
||||
&mut self,
|
||||
mapped: CosmicMapped,
|
||||
output: &Output,
|
||||
position: Option<Point<i32, Logical>>,
|
||||
position: Option<Point<i32, Local>>,
|
||||
size: Option<Size<i32, Logical>>,
|
||||
) {
|
||||
let mut win_geo = mapped.geometry();
|
||||
let mut win_geo = mapped.geometry().as_local();
|
||||
|
||||
let output = self.space.outputs().next().unwrap().clone();
|
||||
let layers = layer_map_for_output(&output);
|
||||
let geometry = layers.non_exclusive_zone();
|
||||
mapped.set_bounds(geometry.size);
|
||||
let last_geometry = mapped.last_geometry.lock().unwrap().clone();
|
||||
|
||||
if let Some(size) = last_geometry.map(|g| g.size) {
|
||||
if let Some(size) = size
|
||||
.map(SizeExt::as_local)
|
||||
.or(last_geometry.map(|g| g.size))
|
||||
{
|
||||
win_geo.size = size;
|
||||
} else {
|
||||
let (min_size, max_size) = (
|
||||
|
|
@ -150,30 +148,24 @@ impl FloatingLayout {
|
|||
});
|
||||
|
||||
mapped.set_tiled(false);
|
||||
let offset = output.geometry().loc
|
||||
- self
|
||||
.space
|
||||
.output_geometry(output)
|
||||
.map(|g| g.loc)
|
||||
.unwrap_or_default();
|
||||
mapped.set_geometry(Rectangle::from_loc_and_size(
|
||||
position + offset,
|
||||
win_geo.size,
|
||||
));
|
||||
mapped
|
||||
.set_geometry(Rectangle::from_loc_and_size(position, win_geo.size).to_global(&output));
|
||||
mapped.configure();
|
||||
self.space.map_element(mapped, position, false);
|
||||
self.space.map_element(mapped, position.as_logical(), false);
|
||||
}
|
||||
|
||||
pub fn unmap(&mut self, window: &CosmicMapped) -> bool {
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
let is_maximized = window.is_maximized(true);
|
||||
|
||||
if !is_maximized {
|
||||
if !window.is_maximized(true) || !window.is_fullscreen(true) {
|
||||
if let Some(location) = self.space.element_location(window) {
|
||||
*window.last_geometry.lock().unwrap() = Some(Rectangle::from_loc_and_size(
|
||||
location,
|
||||
window.geometry().size,
|
||||
));
|
||||
*window.last_geometry.lock().unwrap() = Some(
|
||||
Rectangle::from_loc_and_size(
|
||||
location,
|
||||
window
|
||||
.pending_size()
|
||||
.unwrap_or_else(|| window.geometry().size),
|
||||
)
|
||||
.as_local(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -182,56 +174,8 @@ impl FloatingLayout {
|
|||
was_unmaped
|
||||
}
|
||||
|
||||
pub fn element_geometry(&self, elem: &CosmicMapped) -> Option<Rectangle<i32, Logical>> {
|
||||
self.space.element_geometry(elem)
|
||||
}
|
||||
|
||||
pub fn maximize_request(&mut self, window: &CosmicSurface) {
|
||||
if let Some(mapped) = self
|
||||
.space
|
||||
.elements()
|
||||
.find(|m| m.windows().any(|(w, _)| &w == window))
|
||||
{
|
||||
if let Some(location) = self.space.element_location(mapped) {
|
||||
*mapped.last_geometry.lock().unwrap() = Some(Rectangle::from_loc_and_size(
|
||||
location,
|
||||
mapped.geometry().size,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option<Size<i32, Logical>> {
|
||||
let maybe_mapped = self
|
||||
.space
|
||||
.elements()
|
||||
.find(|m| m.windows().any(|(w, _)| &w == window))
|
||||
.cloned();
|
||||
|
||||
if let Some(mapped) = maybe_mapped {
|
||||
let last_geometry = mapped.last_geometry.lock().unwrap().clone();
|
||||
let last_size = last_geometry.map(|g| g.size).expect("No previous size?");
|
||||
let last_location = last_geometry.map(|g| g.loc).expect("No previous location?");
|
||||
let output = self
|
||||
.space
|
||||
.output_under(last_location.to_f64())
|
||||
.next()
|
||||
.unwrap_or(self.space.outputs().next().unwrap());
|
||||
let offset = output.geometry().loc
|
||||
- self
|
||||
.space
|
||||
.output_geometry(output)
|
||||
.map(|g| g.loc)
|
||||
.unwrap_or_default();
|
||||
mapped.set_geometry(Rectangle::from_loc_and_size(
|
||||
last_location + offset,
|
||||
last_size,
|
||||
));
|
||||
self.space.map_element(mapped, last_location, true);
|
||||
Some(last_size)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn element_geometry(&self, elem: &CosmicMapped) -> Option<Rectangle<i32, Local>> {
|
||||
self.space.element_geometry(elem).map(RectExt::as_local)
|
||||
}
|
||||
|
||||
pub fn resize_request(
|
||||
|
|
@ -251,6 +195,7 @@ impl FloatingLayout {
|
|||
edges,
|
||||
location,
|
||||
size,
|
||||
seat,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
|
|
@ -274,6 +219,9 @@ impl FloatingLayout {
|
|||
else {
|
||||
return false;
|
||||
};
|
||||
if mapped.is_maximized(true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let Some(original_geo) = self.space.element_geometry(mapped) else {
|
||||
return false; // we don't have that window
|
||||
|
|
@ -339,13 +287,10 @@ impl FloatingLayout {
|
|||
}));
|
||||
|
||||
mapped.set_resizing(true);
|
||||
mapped.set_geometry(Rectangle::from_loc_and_size(
|
||||
match mapped.active_window() {
|
||||
CosmicSurface::X11(s) => s.geometry().loc,
|
||||
_ => (0, 0).into(),
|
||||
},
|
||||
geo.size,
|
||||
));
|
||||
mapped.set_geometry(
|
||||
geo.as_local()
|
||||
.to_global(self.space.outputs().next().unwrap()),
|
||||
);
|
||||
mapped.configure();
|
||||
|
||||
true
|
||||
|
|
@ -427,6 +372,7 @@ impl FloatingLayout {
|
|||
&mut self,
|
||||
direction: Direction,
|
||||
seat: &Seat<State>,
|
||||
theme: cosmic::Theme,
|
||||
) -> MoveResult {
|
||||
let Some(target) = seat.get_keyboard().unwrap().current_focus() else {
|
||||
return MoveResult::None
|
||||
|
|
@ -451,7 +397,7 @@ impl FloatingLayout {
|
|||
match focused.handle_move(direction) {
|
||||
StackMoveResult::Handled => return MoveResult::Done,
|
||||
StackMoveResult::MoveOut(surface, loop_handle) => {
|
||||
let mapped: CosmicMapped = CosmicWindow::new(surface, loop_handle).into();
|
||||
let mapped: CosmicMapped = CosmicWindow::new(surface, loop_handle, theme).into();
|
||||
let output = seat.active_output();
|
||||
let pos = self.space.element_geometry(focused).unwrap().loc
|
||||
+ match direction {
|
||||
|
|
@ -471,7 +417,7 @@ impl FloatingLayout {
|
|||
})
|
||||
.then_some(pos);
|
||||
|
||||
self.map_internal(mapped.clone(), &output, position);
|
||||
self.map_internal(mapped.clone(), position.map(PointExt::as_local), None);
|
||||
return MoveResult::ShiftFocus(KeyboardFocusTarget::Element(mapped));
|
||||
}
|
||||
StackMoveResult::Default => {}
|
||||
|
|
@ -503,87 +449,31 @@ impl FloatingLayout {
|
|||
{
|
||||
// TODO what about windows leaving to the top with no headerbar to drag? can that happen? (Probably if the user is moving outputs down)
|
||||
*element.last_geometry.lock().unwrap() = None;
|
||||
let output = self.space.outputs().next().unwrap().clone();
|
||||
self.map_internal(element, &output, None);
|
||||
self.map_internal(element, None, None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn most_overlapped_output_for_element(&self, elem: &CosmicMapped) -> Option<Output> {
|
||||
let elem_geo = self.space.element_geometry(elem)?;
|
||||
|
||||
if self.space.outputs().nth(1).is_none() {
|
||||
return self.space.outputs().next().cloned();
|
||||
}
|
||||
|
||||
Some(
|
||||
self.space
|
||||
.outputs_for_element(elem)
|
||||
.into_iter()
|
||||
.max_by_key(|o| {
|
||||
let output_geo = self.space.output_geometry(o).unwrap();
|
||||
if let Some(intersection) = output_geo.intersection(elem_geo) {
|
||||
intersection.size.w * intersection.size.h
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
.unwrap_or(self.space.outputs().next().unwrap().clone()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn merge(&mut self, other: FloatingLayout) {
|
||||
let mut output_pos_map = HashMap::new();
|
||||
for output in self.space.outputs() {
|
||||
output_pos_map.insert(
|
||||
output.clone(),
|
||||
self.space.output_geometry(output).unwrap().loc
|
||||
- other
|
||||
.space
|
||||
.output_geometry(output)
|
||||
.map(|geo| geo.loc)
|
||||
.unwrap_or_else(|| (0, 0).into()),
|
||||
);
|
||||
}
|
||||
for element in other.space.elements() {
|
||||
let mut elem_geo = other.space.element_geometry(element).unwrap();
|
||||
let output = other
|
||||
let elem_loc = other
|
||||
.space
|
||||
.outputs_for_element(element)
|
||||
.into_iter()
|
||||
.filter(|o| self.space.outputs().any(|o2| o == o2))
|
||||
.max_by_key(|o| {
|
||||
let output_geo = other.space.output_geometry(o).unwrap();
|
||||
let intersection = output_geo.intersection(elem_geo).unwrap();
|
||||
intersection.size.w * intersection.size.h
|
||||
})
|
||||
.unwrap_or(self.space.outputs().next().unwrap().clone());
|
||||
elem_geo.loc += output_pos_map
|
||||
.get(&output)
|
||||
.copied()
|
||||
.unwrap_or_else(|| (0, 0).into());
|
||||
let offset = output.geometry().loc
|
||||
- self
|
||||
.space
|
||||
.output_geometry(&output)
|
||||
.map(|g| g.loc)
|
||||
.unwrap_or_default();
|
||||
element.set_geometry(Rectangle::from_loc_and_size(
|
||||
elem_geo.loc + offset,
|
||||
elem_geo.size,
|
||||
));
|
||||
self.space.map_element(element.clone(), elem_geo.loc, false);
|
||||
.element_geometry(element)
|
||||
.unwrap()
|
||||
.loc
|
||||
.as_local();
|
||||
self.map_internal(element.clone(), Some(elem_loc), None);
|
||||
}
|
||||
self.refresh(); //fixup any out of bounds elements
|
||||
}
|
||||
|
||||
pub fn render_output<R>(
|
||||
pub fn render<R>(
|
||||
&self,
|
||||
renderer: &mut R,
|
||||
output: &Output,
|
||||
focused: Option<&CosmicMapped>,
|
||||
mut resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
|
||||
indicator_thickness: u8,
|
||||
alpha: f32,
|
||||
theme: &cosmic::theme::CosmicTheme,
|
||||
) -> (
|
||||
Vec<CosmicMappedRenderElement<R>>,
|
||||
Vec<CosmicMappedRenderElement<R>>,
|
||||
|
|
@ -598,68 +488,72 @@ impl FloatingLayout {
|
|||
#[cfg(feature = "debug")]
|
||||
puffin::profile_function!();
|
||||
|
||||
let output = self.space.outputs().next().unwrap();
|
||||
let output_scale = output.current_scale().fractional_scale();
|
||||
let output_geo = self.space.output_geometry(output).unwrap();
|
||||
|
||||
let mut window_elements = Vec::new();
|
||||
let mut popup_elements = Vec::new();
|
||||
|
||||
self.space
|
||||
.elements_for_output(output)
|
||||
.rev()
|
||||
.for_each(|elem| {
|
||||
let render_location = self.space.element_location(elem).unwrap()
|
||||
- output_geo.loc
|
||||
- elem.geometry().loc;
|
||||
let (w_elements, p_elements) = elem.split_render_elements(
|
||||
renderer,
|
||||
render_location.to_physical_precise_round(output_scale),
|
||||
output_scale.into(),
|
||||
alpha,
|
||||
);
|
||||
self.space.elements().rev().for_each(|elem| {
|
||||
let render_location = self.space.element_location(elem).unwrap() - elem.geometry().loc;
|
||||
let (w_elements, p_elements) = elem.split_render_elements(
|
||||
renderer,
|
||||
render_location.to_physical_precise_round(output_scale),
|
||||
output_scale.into(),
|
||||
alpha,
|
||||
);
|
||||
|
||||
if focused == Some(elem) {
|
||||
let mut indicator_geometry = Rectangle::from_loc_and_size(
|
||||
self.space.element_location(elem).unwrap() - output_geo.loc,
|
||||
elem.geometry().size,
|
||||
if focused == Some(elem) && !elem.is_maximized(false) {
|
||||
let mut indicator_geometry = Rectangle::from_loc_and_size(
|
||||
self.space.element_location(elem).unwrap(),
|
||||
elem.geometry().size,
|
||||
)
|
||||
.as_local();
|
||||
|
||||
if let Some((mode, resize)) = resize_indicator.as_mut() {
|
||||
indicator_geometry.loc -= (18, 18).into();
|
||||
indicator_geometry.size += (36, 36).into();
|
||||
resize.resize(indicator_geometry.size.as_logical());
|
||||
resize.output_enter(output, Rectangle::default() /* unused */);
|
||||
window_elements.extend(
|
||||
resize
|
||||
.render_elements::<CosmicWindowRenderElement<R>>(
|
||||
renderer,
|
||||
indicator_geometry
|
||||
.loc
|
||||
.as_logical()
|
||||
.to_physical_precise_round(output_scale),
|
||||
output_scale.into(),
|
||||
alpha * mode.alpha().unwrap_or(1.0),
|
||||
)
|
||||
.into_iter()
|
||||
.map(CosmicMappedRenderElement::Window),
|
||||
);
|
||||
|
||||
if let Some((mode, resize)) = resize_indicator.as_mut() {
|
||||
indicator_geometry.loc -= (18, 18).into();
|
||||
indicator_geometry.size += (36, 36).into();
|
||||
resize.resize(indicator_geometry.size);
|
||||
resize.output_enter(output, output_geo);
|
||||
window_elements.extend(
|
||||
resize
|
||||
.render_elements::<CosmicWindowRenderElement<R>>(
|
||||
renderer,
|
||||
indicator_geometry
|
||||
.loc
|
||||
.to_physical_precise_round(output_scale),
|
||||
output_scale.into(),
|
||||
alpha * mode.alpha().unwrap_or(1.0),
|
||||
)
|
||||
.into_iter()
|
||||
.map(CosmicMappedRenderElement::Window),
|
||||
);
|
||||
}
|
||||
|
||||
if indicator_thickness > 0 {
|
||||
let element = IndicatorShader::focus_element(
|
||||
renderer,
|
||||
Key::Window(Usage::FocusIndicator, elem.clone()),
|
||||
indicator_geometry,
|
||||
indicator_thickness,
|
||||
output_scale,
|
||||
alpha,
|
||||
);
|
||||
window_elements.push(element.into());
|
||||
}
|
||||
}
|
||||
|
||||
window_elements.extend(w_elements);
|
||||
popup_elements.extend(p_elements);
|
||||
});
|
||||
let active_window_hint = crate::theme::active_window_hint(theme);
|
||||
|
||||
if indicator_thickness > 0 {
|
||||
let element = IndicatorShader::focus_element(
|
||||
renderer,
|
||||
Key::Window(Usage::FocusIndicator, elem.clone()),
|
||||
indicator_geometry,
|
||||
indicator_thickness,
|
||||
output_scale,
|
||||
alpha,
|
||||
[
|
||||
active_window_hint.red,
|
||||
active_window_hint.green,
|
||||
active_window_hint.blue,
|
||||
],
|
||||
);
|
||||
window_elements.push(element.into());
|
||||
}
|
||||
}
|
||||
|
||||
window_elements.extend(w_elements);
|
||||
popup_elements.extend(p_elements);
|
||||
});
|
||||
|
||||
(window_elements, popup_elements)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
shell::{focus::target::PointerFocusTarget, layout::Orientation},
|
||||
utils::prelude::*,
|
||||
};
|
||||
use id_tree::NodeId;
|
||||
use id_tree::{NodeId, Tree};
|
||||
use smithay::{
|
||||
backend::input::ButtonState,
|
||||
input::{
|
||||
|
|
@ -80,6 +80,8 @@ impl PointerTarget<State> for ResizeForkTarget {
|
|||
button,
|
||||
location,
|
||||
},
|
||||
old_tree: None,
|
||||
accumulated_delta: 0.0,
|
||||
last_loc: location,
|
||||
node,
|
||||
output,
|
||||
|
|
@ -116,6 +118,8 @@ impl PointerTarget<State> for ResizeForkTarget {
|
|||
pub struct ResizeForkGrab {
|
||||
start_data: PointerGrabStartData<State>,
|
||||
last_loc: Point<f64, Logical>,
|
||||
old_tree: Option<Tree<Data>>,
|
||||
accumulated_delta: f64,
|
||||
node: NodeId,
|
||||
output: WeakOutput,
|
||||
left_up_idx: usize,
|
||||
|
|
@ -137,67 +141,104 @@ impl PointerGrab<State> for ResizeForkGrab {
|
|||
|
||||
if let Some(output) = self.output.upgrade() {
|
||||
let tiling_layer = &mut data.common.shell.active_space_mut(&output).tiling_layer;
|
||||
if let Some(queue) = tiling_layer.queues.get_mut(&output) {
|
||||
let tree = &mut queue.trees.back_mut().unwrap().0;
|
||||
if tree.get(&self.node).is_ok() {
|
||||
let delta = match self.orientation {
|
||||
Orientation::Vertical => delta.x,
|
||||
Orientation::Horizontal => delta.y,
|
||||
}
|
||||
.round() as i32;
|
||||
let gaps = tiling_layer.gaps();
|
||||
|
||||
// check that we are still alive
|
||||
let mut iter = tree
|
||||
.children_ids(&self.node)
|
||||
.unwrap()
|
||||
.skip(self.left_up_idx);
|
||||
let first_elem = iter.next();
|
||||
let second_elem = iter.next();
|
||||
if first_elem.is_none() || second_elem.is_none() {
|
||||
return handle.unset_grab(data, event.serial, event.time);
|
||||
};
|
||||
let tree = &mut tiling_layer.queue.trees.back_mut().unwrap().0;
|
||||
match &mut self.old_tree {
|
||||
Some(old_tree) => {
|
||||
// it would be so nice to just `zip` here, but `zip` just returns `None` once either returns `None`.
|
||||
let mut iter_a = old_tree
|
||||
.root_node_id()
|
||||
.into_iter()
|
||||
.flat_map(|root_id| old_tree.traverse_pre_order_ids(root_id).unwrap());
|
||||
let mut iter_b = tree
|
||||
.root_node_id()
|
||||
.into_iter()
|
||||
.flat_map(|root_id| tree.traverse_pre_order_ids(root_id).unwrap());
|
||||
|
||||
match tree.get_mut(&self.node).unwrap().data_mut() {
|
||||
Data::Group {
|
||||
sizes, orientation, ..
|
||||
} => {
|
||||
if sizes[self.left_up_idx] + sizes[self.left_up_idx + 1]
|
||||
< match orientation {
|
||||
Orientation::Vertical => 720,
|
||||
Orientation::Horizontal => 480,
|
||||
}
|
||||
{
|
||||
return;
|
||||
};
|
||||
|
||||
let old_size = sizes[self.left_up_idx];
|
||||
sizes[self.left_up_idx] = (old_size + delta).max(
|
||||
if self.orientation == Orientation::Vertical {
|
||||
360
|
||||
} else {
|
||||
240
|
||||
},
|
||||
);
|
||||
let diff = old_size - sizes[self.left_up_idx];
|
||||
let next_size = sizes[self.left_up_idx + 1] + diff;
|
||||
sizes[self.left_up_idx + 1] =
|
||||
next_size.max(if self.orientation == Orientation::Vertical {
|
||||
360
|
||||
} else {
|
||||
240
|
||||
});
|
||||
let next_diff = next_size - sizes[self.left_up_idx + 1];
|
||||
sizes[self.left_up_idx] += next_diff;
|
||||
// so lets do it manually
|
||||
let mut equal = true;
|
||||
let mut a = iter_a.next();
|
||||
let mut b = iter_b.next();
|
||||
while a.is_some() || b.is_some() {
|
||||
equal = a == b;
|
||||
if !equal {
|
||||
break;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
a = iter_a.next();
|
||||
b = iter_b.next();
|
||||
}
|
||||
|
||||
self.last_loc = event.location;
|
||||
let blocker = TilingLayout::update_positions(&output, tree, tiling_layer.gaps);
|
||||
tiling_layer.pending_blockers.extend(blocker);
|
||||
} else {
|
||||
handle.unset_grab(data, event.serial, event.time);
|
||||
if !equal {
|
||||
*old_tree = tree.copy_clone();
|
||||
self.accumulated_delta = 0.0;
|
||||
} else {
|
||||
*tree = old_tree.copy_clone();
|
||||
}
|
||||
}
|
||||
x @ None => {
|
||||
*x = Some(tree.copy_clone());
|
||||
}
|
||||
};
|
||||
if tree.get(&self.node).is_ok() {
|
||||
let delta = match self.orientation {
|
||||
Orientation::Vertical => delta.x,
|
||||
Orientation::Horizontal => delta.y,
|
||||
}
|
||||
.round();
|
||||
self.accumulated_delta += delta;
|
||||
|
||||
// check that we are still alive
|
||||
let mut iter = tree
|
||||
.children_ids(&self.node)
|
||||
.unwrap()
|
||||
.skip(self.left_up_idx);
|
||||
let first_elem = iter.next();
|
||||
let second_elem = iter.next();
|
||||
if first_elem.is_none() || second_elem.is_none() {
|
||||
return handle.unset_grab(data, event.serial, event.time, true);
|
||||
};
|
||||
|
||||
match tree.get_mut(&self.node).unwrap().data_mut() {
|
||||
Data::Group {
|
||||
sizes, orientation, ..
|
||||
} => {
|
||||
if sizes[self.left_up_idx] + sizes[self.left_up_idx + 1]
|
||||
< match orientation {
|
||||
Orientation::Vertical => 720,
|
||||
Orientation::Horizontal => 480,
|
||||
}
|
||||
{
|
||||
return;
|
||||
};
|
||||
|
||||
let old_size = sizes[self.left_up_idx];
|
||||
sizes[self.left_up_idx] = (old_size
|
||||
+ self.accumulated_delta.round() as i32)
|
||||
.max(if self.orientation == Orientation::Vertical {
|
||||
360
|
||||
} else {
|
||||
240
|
||||
});
|
||||
let diff = old_size - sizes[self.left_up_idx];
|
||||
let next_size = sizes[self.left_up_idx + 1] + diff;
|
||||
sizes[self.left_up_idx + 1] =
|
||||
next_size.max(if self.orientation == Orientation::Vertical {
|
||||
360
|
||||
} else {
|
||||
240
|
||||
});
|
||||
let next_diff = next_size - sizes[self.left_up_idx + 1];
|
||||
sizes[self.left_up_idx] += next_diff;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
self.last_loc = event.location;
|
||||
let blocker = TilingLayout::update_positions(&output, tree, gaps);
|
||||
tiling_layer.pending_blockers.extend(blocker);
|
||||
} else {
|
||||
handle.unset_grab(data, event.serial, event.time, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -222,7 +263,7 @@ impl PointerGrab<State> for ResizeForkGrab {
|
|||
handle.button(data, event);
|
||||
if handle.current_pressed().is_empty() {
|
||||
// No more buttons are pressed, release the grab.
|
||||
handle.unset_grab(data, event.serial, event.time);
|
||||
handle.unset_grab(data, event.serial, event.time, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,8 +39,7 @@ impl KeyboardGrab<State> for SwapWindowGrab {
|
|||
serial: Serial,
|
||||
time: u32,
|
||||
) {
|
||||
if self.desc.output.upgrade().is_none()
|
||||
|| !matches!(&data.common.shell.overview_mode, OverviewMode::Started(Trigger::KeyboardSwap(_, d), _) if d == &self.desc)
|
||||
if !matches!(&data.common.shell.overview_mode, OverviewMode::Started(Trigger::KeyboardSwap(_, d), _) if d == &self.desc)
|
||||
{
|
||||
handle.unset_grab(data, serial, false);
|
||||
return;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
1376
src/shell/mod.rs
1376
src/shell/mod.rs
File diff suppressed because it is too large
Load diff
|
|
@ -21,7 +21,7 @@ use crate::{
|
|||
xwayland::XWaylandState,
|
||||
};
|
||||
|
||||
use calloop::LoopHandle;
|
||||
use cosmic::theme::CosmicTheme;
|
||||
use id_tree::Tree;
|
||||
use indexmap::IndexSet;
|
||||
use keyframe::{ease, functions::EaseInOutCubic};
|
||||
|
|
@ -47,7 +47,7 @@ use smithay::{
|
|||
xwayland::X11Surface,
|
||||
};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::{HashMap, VecDeque},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
|
|
@ -61,7 +61,7 @@ use super::{
|
|||
element::{
|
||||
resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement,
|
||||
swap_indicator::SwapIndicator, window::CosmicWindowRenderElement, CosmicMapped,
|
||||
CosmicWindow,
|
||||
MaximizedState,
|
||||
},
|
||||
focus::{
|
||||
target::{KeyboardFocusTarget, PointerFocusTarget, WindowGroup},
|
||||
|
|
@ -76,28 +76,37 @@ const FULLSCREEN_ANIMATION_DURATION: Duration = Duration::from_millis(200);
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Workspace {
|
||||
pub output: Output,
|
||||
pub tiling_layer: TilingLayout,
|
||||
pub floating_layer: FloatingLayout,
|
||||
pub tiling_enabled: bool,
|
||||
pub fullscreen: HashMap<Output, FullscreenSurface>,
|
||||
pub fullscreen: Option<FullscreenSurface>,
|
||||
|
||||
pub handle: WorkspaceHandle,
|
||||
pub focus_stack: FocusStacks,
|
||||
pub pending_buffers: Vec<(ScreencopySession, BufferParams)>,
|
||||
pub screencopy_sessions: Vec<DropableSession>,
|
||||
pub output_stack: VecDeque<String>,
|
||||
pub(super) backdrop_id: Id,
|
||||
pub dirty: AtomicBool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FullscreenSurface {
|
||||
pub window: CosmicWindow,
|
||||
pub exclusive: bool,
|
||||
original_size: Size<i32, Logical>,
|
||||
pub surface: CosmicSurface,
|
||||
pub previously: Option<(ManagedLayer, WorkspaceHandle)>,
|
||||
original_geometry: Rectangle<i32, Global>,
|
||||
start_at: Option<Instant>,
|
||||
ended_at: Option<Instant>,
|
||||
animation_signal: Option<Arc<AtomicBool>>,
|
||||
}
|
||||
|
||||
impl PartialEq for FullscreenSurface {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.surface == other.surface
|
||||
}
|
||||
}
|
||||
|
||||
struct FullscreenBlocker {
|
||||
signal: Arc<AtomicBool>,
|
||||
}
|
||||
|
|
@ -120,17 +129,17 @@ impl FullscreenSurface {
|
|||
|
||||
impl IsAlive for FullscreenSurface {
|
||||
fn alive(&self) -> bool {
|
||||
self.window.alive()
|
||||
self.surface.alive()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FocusStacks(HashMap<Seat<State>, IndexSet<CosmicMapped>>);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ManagedState {
|
||||
pub layer: ManagedLayer,
|
||||
pub was_fullscreen: Option<bool>,
|
||||
pub was_fullscreen: Option<FullscreenSurface>,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ManagedLayer {
|
||||
|
|
@ -198,16 +207,26 @@ impl MoveResult {
|
|||
}
|
||||
|
||||
impl Workspace {
|
||||
pub fn new(handle: WorkspaceHandle, tiling_enabled: bool, gaps: (u8, u8)) -> Workspace {
|
||||
pub fn new(
|
||||
handle: WorkspaceHandle,
|
||||
output: Output,
|
||||
tiling_enabled: bool,
|
||||
theme: cosmic::Theme,
|
||||
) -> Workspace {
|
||||
let tiling_layer = TilingLayout::new(theme, &output);
|
||||
let floating_layer = FloatingLayout::new(&output);
|
||||
|
||||
Workspace {
|
||||
tiling_layer: TilingLayout::new(gaps),
|
||||
floating_layer: FloatingLayout::new(),
|
||||
output,
|
||||
tiling_layer,
|
||||
floating_layer,
|
||||
tiling_enabled,
|
||||
fullscreen: HashMap::new(),
|
||||
fullscreen: None,
|
||||
handle,
|
||||
focus_stack: FocusStacks::default(),
|
||||
pending_buffers: Vec::new(),
|
||||
screencopy_sessions: Vec::new(),
|
||||
output_stack: VecDeque::new(),
|
||||
backdrop_id: Id::new(),
|
||||
dirty: AtomicBool::new(false),
|
||||
}
|
||||
|
|
@ -217,7 +236,11 @@ impl Workspace {
|
|||
#[cfg(feature = "debug")]
|
||||
puffin::profile_function!();
|
||||
|
||||
self.fullscreen.retain(|_, w| w.alive());
|
||||
// TODO: `Option::take_if` once stabilitized
|
||||
if self.fullscreen.as_ref().is_some_and(|w| !w.alive()) {
|
||||
let _ = self.fullscreen.take();
|
||||
};
|
||||
|
||||
self.floating_layer.refresh();
|
||||
self.tiling_layer.refresh();
|
||||
}
|
||||
|
|
@ -233,15 +256,15 @@ impl Workspace {
|
|||
self.tiling_layer.animations_going()
|
||||
|| self
|
||||
.fullscreen
|
||||
.values()
|
||||
.any(|f| f.start_at.is_some() || f.ended_at.is_some())
|
||||
.as_ref()
|
||||
.is_some_and(|f| f.start_at.is_some() || f.ended_at.is_some())
|
||||
|| self.dirty.swap(false, Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub fn update_animations(&mut self) -> HashMap<ClientId, Client> {
|
||||
let mut clients = HashMap::new();
|
||||
|
||||
for f in self.fullscreen.values_mut() {
|
||||
if let Some(f) = self.fullscreen.as_mut() {
|
||||
if let Some(start) = f.start_at.as_ref() {
|
||||
let duration_since = Instant::now().duration_since(*start);
|
||||
if duration_since > FULLSCREEN_ANIMATION_DURATION {
|
||||
|
|
@ -252,36 +275,32 @@ impl Workspace {
|
|||
if let Some(signal) = f.animation_signal.take() {
|
||||
signal.store(true, Ordering::SeqCst);
|
||||
if let Some(client) =
|
||||
f.window.wl_surface().as_ref().and_then(Resource::client)
|
||||
f.surface.wl_surface().as_ref().and_then(Resource::client)
|
||||
{
|
||||
clients.insert(client.id(), client);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let len = self.fullscreen.len();
|
||||
self.fullscreen.retain(|_, f| match f.ended_at {
|
||||
None => true,
|
||||
Some(instant) => {
|
||||
let duration_since = Instant::now().duration_since(instant);
|
||||
if let Some(end) = f.ended_at {
|
||||
let duration_since = Instant::now().duration_since(end);
|
||||
if duration_since * 2 > FULLSCREEN_ANIMATION_DURATION {
|
||||
if let Some(signal) = f.animation_signal.take() {
|
||||
signal.store(true, Ordering::SeqCst);
|
||||
if let Some(client) =
|
||||
f.window.wl_surface().as_ref().and_then(Resource::client)
|
||||
f.surface.wl_surface().as_ref().and_then(Resource::client)
|
||||
{
|
||||
clients.insert(client.id(), client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
duration_since < FULLSCREEN_ANIMATION_DURATION
|
||||
if duration_since >= FULLSCREEN_ANIMATION_DURATION {
|
||||
let _ = self.fullscreen.take();
|
||||
self.dirty.store(true, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
});
|
||||
if len != self.fullscreen.len() {
|
||||
self.dirty.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
clients.extend(self.tiling_layer.update_animation_state());
|
||||
|
|
@ -299,35 +318,44 @@ impl Workspace {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn map_output(&mut self, output: &Output, position: Point<i32, Logical>) {
|
||||
self.tiling_layer.map_output(output, position);
|
||||
self.floating_layer.map_output(output, position);
|
||||
pub fn output(&self) -> &Output {
|
||||
&self.output
|
||||
}
|
||||
|
||||
pub fn unmap_output(
|
||||
pub fn set_output(
|
||||
&mut self,
|
||||
output: &Output,
|
||||
toplevel_info: &mut ToplevelInfoState<State, CosmicSurface>,
|
||||
) {
|
||||
if let Some(dead_output_fullscreen) = self.fullscreen.remove(output) {
|
||||
self.unfullscreen_request(&dead_output_fullscreen.window.surface());
|
||||
self.tiling_layer.set_output(output);
|
||||
self.floating_layer.set_output(output);
|
||||
for mapped in self.mapped() {
|
||||
for (surface, _) in mapped.windows() {
|
||||
toplevel_info.toplevel_leave_output(&surface, &self.output);
|
||||
toplevel_info.toplevel_enter_output(&surface, output);
|
||||
}
|
||||
}
|
||||
self.tiling_layer.unmap_output(output, toplevel_info);
|
||||
self.floating_layer.unmap_output(output, toplevel_info);
|
||||
self.refresh();
|
||||
self.output = output.clone();
|
||||
}
|
||||
|
||||
pub fn unmap(&mut self, mapped: &CosmicMapped) -> Option<ManagedState> {
|
||||
let was_floating = self.floating_layer.unmap(&mapped);
|
||||
let was_tiling = self.tiling_layer.unmap(&mapped).is_some();
|
||||
if was_floating || was_tiling {
|
||||
assert!(was_floating != was_tiling);
|
||||
let was_fullscreen = self
|
||||
.fullscreen
|
||||
.as_ref()
|
||||
.filter(|f| f.ended_at.is_none())
|
||||
.map(|f| mapped.windows().any(|(w, _)| w == f.surface))
|
||||
.unwrap_or(false)
|
||||
.then(|| self.fullscreen.take().unwrap());
|
||||
|
||||
if mapped.maximized_state.lock().unwrap().is_some() {
|
||||
// If surface is maximized then unmaximize it, so it is assigned to only one layer
|
||||
let _ = self.unmaximize_request(&mapped.active_window());
|
||||
}
|
||||
|
||||
let was_maximized = mapped.is_maximized(true);
|
||||
let was_fullscreen = mapped.is_fullscreen(true);
|
||||
if was_maximized || was_fullscreen {
|
||||
self.unmaximize_request(&mapped.active_window());
|
||||
let was_floating = self.floating_layer.unmap(&mapped);
|
||||
let was_tiling = self.tiling_layer.unmap(&mapped);
|
||||
if was_floating || was_tiling {
|
||||
assert!(was_floating != was_tiling);
|
||||
}
|
||||
|
||||
self.focus_stack
|
||||
|
|
@ -337,12 +365,12 @@ impl Workspace {
|
|||
if was_floating {
|
||||
Some(ManagedState {
|
||||
layer: ManagedLayer::Floating,
|
||||
was_fullscreen: (was_fullscreen || was_maximized).then_some(was_fullscreen),
|
||||
was_fullscreen,
|
||||
})
|
||||
} else if was_tiling {
|
||||
Some(ManagedState {
|
||||
layer: ManagedLayer::Tiling,
|
||||
was_fullscreen: (was_fullscreen || was_maximized).then_some(was_fullscreen),
|
||||
was_fullscreen,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
|
@ -366,140 +394,103 @@ impl Workspace {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn outputs_for_element(&self, elem: &CosmicMapped) -> impl Iterator<Item = Output> {
|
||||
self.floating_layer
|
||||
.space
|
||||
.outputs_for_element(elem)
|
||||
.into_iter()
|
||||
.chain(self.tiling_layer.output_for_element(elem).cloned())
|
||||
}
|
||||
|
||||
pub fn output_under(&self, point: Point<i32, Logical>) -> Option<&Output> {
|
||||
let space = &self.floating_layer.space;
|
||||
space.outputs().find(|o| {
|
||||
let internal_output_geo = space.output_geometry(o).unwrap();
|
||||
let external_output_geo = o.geometry();
|
||||
internal_output_geo.contains(point - external_output_geo.loc + internal_output_geo.loc)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn element_under(
|
||||
&mut self,
|
||||
location: Point<f64, Logical>,
|
||||
location: Point<f64, Global>,
|
||||
overview: OverviewMode,
|
||||
) -> Option<(PointerFocusTarget, Point<i32, Logical>)> {
|
||||
) -> Option<(PointerFocusTarget, Point<i32, Global>)> {
|
||||
let location = location.to_local(&self.output);
|
||||
self.floating_layer
|
||||
.space
|
||||
.element_under(location)
|
||||
.map(|(mapped, p)| (mapped.clone().into(), p))
|
||||
.element_under(location.as_logical())
|
||||
.map(|(mapped, p)| (mapped.clone().into(), p.as_local()))
|
||||
.or_else(|| self.tiling_layer.element_under(location, overview))
|
||||
.map(|(m, p)| (m, p.to_global(&self.output)))
|
||||
}
|
||||
|
||||
pub fn element_geometry(&self, elem: &CosmicMapped) -> Option<Rectangle<i32, Logical>> {
|
||||
let space = &self.floating_layer.space;
|
||||
let outputs = space.outputs().collect::<Vec<_>>();
|
||||
let offset = if outputs.len() == 1
|
||||
&& space.output_geometry(&outputs[0]).unwrap().loc == Point::from((0, 0))
|
||||
{
|
||||
outputs[0].geometry().loc
|
||||
} else {
|
||||
(0, 0).into()
|
||||
};
|
||||
|
||||
pub fn element_geometry(&self, elem: &CosmicMapped) -> Option<Rectangle<i32, Local>> {
|
||||
self.floating_layer
|
||||
.space
|
||||
.element_geometry(elem)
|
||||
.or_else(|| self.tiling_layer.element_geometry(elem))
|
||||
.map(|mut geo| {
|
||||
geo.loc += offset;
|
||||
geo
|
||||
})
|
||||
}
|
||||
|
||||
pub fn recalculate(&mut self, output: &Output) {
|
||||
if let Some(f) = self.fullscreen.get(output) {
|
||||
if !f.exclusive {
|
||||
f.window
|
||||
.set_geometry(layer_map_for_output(output).non_exclusive_zone());
|
||||
}
|
||||
}
|
||||
self.tiling_layer.recalculate(output);
|
||||
pub fn recalculate(&mut self) {
|
||||
self.tiling_layer.recalculate();
|
||||
self.floating_layer.refresh();
|
||||
}
|
||||
|
||||
pub fn maximize_request(
|
||||
&mut self,
|
||||
window: &CosmicSurface,
|
||||
output: &Output,
|
||||
evlh: LoopHandle<'static, crate::state::State>,
|
||||
) {
|
||||
if self.fullscreen.contains_key(output) {
|
||||
pub fn maximize_request(&mut self, window: &CosmicSurface) {
|
||||
if self.fullscreen.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.floating_layer.maximize_request(window);
|
||||
|
||||
window.set_fullscreen(false);
|
||||
window.set_maximized(true);
|
||||
self.set_fullscreen(window, output, false, evlh)
|
||||
if let Some(elem) = self.element_for_surface(window).cloned() {
|
||||
let mut state = elem.maximized_state.lock().unwrap();
|
||||
if state.is_none() {
|
||||
*state = Some(MaximizedState {
|
||||
original_geometry: self.element_geometry(&elem).unwrap(),
|
||||
original_layer: if self.is_floating(&elem) {
|
||||
ManagedLayer::Floating
|
||||
} else {
|
||||
ManagedLayer::Tiling
|
||||
},
|
||||
});
|
||||
std::mem::drop(state);
|
||||
self.floating_layer.map_maximized(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option<Size<i32, Logical>> {
|
||||
if self
|
||||
.fullscreen
|
||||
.values()
|
||||
.any(|f| &f.window.surface() == window)
|
||||
{
|
||||
self.unfullscreen_request(window)
|
||||
} else {
|
||||
None
|
||||
if let Some(elem) = self.element_for_surface(window).cloned() {
|
||||
let mut state = elem.maximized_state.lock().unwrap();
|
||||
if let Some(state) = state.take() {
|
||||
match state.original_layer {
|
||||
ManagedLayer::Tiling => {
|
||||
// should still be mapped in tiling
|
||||
self.floating_layer.unmap(&elem);
|
||||
elem.output_enter(&self.output, elem.bbox());
|
||||
elem.set_maximized(false);
|
||||
elem.set_geometry(state.original_geometry.to_global(&self.output));
|
||||
elem.configure();
|
||||
self.tiling_layer.recalculate();
|
||||
return self
|
||||
.tiling_layer
|
||||
.element_geometry(&elem)
|
||||
.map(|geo| geo.size.as_logical());
|
||||
}
|
||||
ManagedLayer::Floating => {
|
||||
elem.set_maximized(false);
|
||||
self.floating_layer.map_internal(
|
||||
elem.clone(),
|
||||
Some(state.original_geometry.loc),
|
||||
Some(state.original_geometry.size.as_logical()),
|
||||
);
|
||||
return Some(state.original_geometry.size.as_logical());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn fullscreen_request(
|
||||
&mut self,
|
||||
window: &CosmicSurface,
|
||||
output: &Output,
|
||||
evlh: LoopHandle<'static, crate::state::State>,
|
||||
previously: Option<(ManagedLayer, WorkspaceHandle)>,
|
||||
) {
|
||||
if self
|
||||
.fullscreen
|
||||
.get(output)
|
||||
.map(|f| &f.window.surface() != window)
|
||||
.unwrap_or(false)
|
||||
.as_ref()
|
||||
.filter(|f| f.ended_at.is_none())
|
||||
.is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if !window.is_maximized(true) {
|
||||
self.floating_layer.maximize_request(window);
|
||||
}
|
||||
|
||||
window.set_maximized(false);
|
||||
window.set_fullscreen(true);
|
||||
self.set_fullscreen(window, output, true, evlh)
|
||||
}
|
||||
|
||||
fn set_fullscreen<'a>(
|
||||
&mut self,
|
||||
window: &'a CosmicSurface,
|
||||
output: &Output,
|
||||
exclusive: bool,
|
||||
evlh: LoopHandle<'static, crate::state::State>,
|
||||
) {
|
||||
if let Some(mapped) = self
|
||||
.mapped()
|
||||
.find(|m| m.windows().any(|(w, _)| &w == window))
|
||||
{
|
||||
mapped.set_active(window);
|
||||
}
|
||||
|
||||
let window = CosmicWindow::new(window.clone(), evlh);
|
||||
let geo = if exclusive {
|
||||
output.geometry()
|
||||
} else {
|
||||
layer_map_for_output(output).non_exclusive_zone()
|
||||
};
|
||||
let original_size = window.geometry().size;
|
||||
let geo = self.output.geometry();
|
||||
let original_geometry = window.geometry().as_global();
|
||||
let signal = if let Some(surface) = window.wl_surface() {
|
||||
let signal = Arc::new(AtomicBool::new(false));
|
||||
add_blocker(
|
||||
|
|
@ -513,35 +504,35 @@ impl Workspace {
|
|||
None
|
||||
};
|
||||
window.set_geometry(geo);
|
||||
window.output_enter(output, Rectangle::from_loc_and_size((0, 0), geo.size));
|
||||
window.surface().send_configure();
|
||||
window.send_configure();
|
||||
|
||||
self.fullscreen.insert(
|
||||
output.clone(),
|
||||
FullscreenSurface {
|
||||
window: window.clone(),
|
||||
exclusive,
|
||||
original_size,
|
||||
start_at: Some(Instant::now()),
|
||||
ended_at: None,
|
||||
animation_signal: signal,
|
||||
},
|
||||
);
|
||||
self.fullscreen = Some(FullscreenSurface {
|
||||
surface: window.clone(),
|
||||
previously,
|
||||
original_geometry,
|
||||
start_at: Some(Instant::now()),
|
||||
ended_at: None,
|
||||
animation_signal: signal,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn unfullscreen_request(&mut self, window: &CosmicSurface) -> Option<Size<i32, Logical>> {
|
||||
if let Some((output, f)) = self
|
||||
#[must_use]
|
||||
pub fn unfullscreen_request(
|
||||
&mut self,
|
||||
window: &CosmicSurface,
|
||||
) -> Option<(ManagedLayer, WorkspaceHandle)> {
|
||||
if let Some(f) = self
|
||||
.fullscreen
|
||||
.iter_mut()
|
||||
.find(|(_, f)| &f.window.surface() == window)
|
||||
.as_mut()
|
||||
.filter(|f| &f.surface == window && f.ended_at.is_none())
|
||||
{
|
||||
f.window.output_leave(output);
|
||||
window.set_maximized(false);
|
||||
window.set_fullscreen(false);
|
||||
let result = self.floating_layer.unmaximize_request(window);
|
||||
window.set_geometry(f.original_geometry);
|
||||
|
||||
self.floating_layer.refresh();
|
||||
self.tiling_layer.recalculate(output);
|
||||
self.tiling_layer.recalculate();
|
||||
self.tiling_layer.refresh();
|
||||
|
||||
let signal = if let Some(surface) = window.wl_surface() {
|
||||
let signal = Arc::new(AtomicBool::new(false));
|
||||
add_blocker(
|
||||
|
|
@ -574,90 +565,36 @@ impl Workspace {
|
|||
}
|
||||
}
|
||||
|
||||
result
|
||||
f.previously
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_fullscreen(&mut self, output: &Output) {
|
||||
if let Some(FullscreenSurface {
|
||||
window,
|
||||
ended_at,
|
||||
start_at,
|
||||
animation_signal,
|
||||
..
|
||||
}) = self.fullscreen.get_mut(output)
|
||||
{
|
||||
window.output_leave(output);
|
||||
let surface = window.surface();
|
||||
surface.set_maximized(false);
|
||||
surface.set_fullscreen(false);
|
||||
self.floating_layer.unmaximize_request(&surface);
|
||||
self.floating_layer.refresh();
|
||||
self.tiling_layer.recalculate(output);
|
||||
self.tiling_layer.refresh();
|
||||
let signal = if let Some(surface) = surface.wl_surface() {
|
||||
let signal = Arc::new(AtomicBool::new(false));
|
||||
add_blocker(
|
||||
&surface,
|
||||
FullscreenBlocker {
|
||||
signal: signal.clone(),
|
||||
},
|
||||
);
|
||||
Some(signal)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
surface.send_configure();
|
||||
|
||||
*ended_at = Some(
|
||||
Instant::now()
|
||||
- (FULLSCREEN_ANIMATION_DURATION
|
||||
- start_at
|
||||
.take()
|
||||
.map(|earlier| {
|
||||
Instant::now()
|
||||
.duration_since(earlier)
|
||||
.min(FULLSCREEN_ANIMATION_DURATION)
|
||||
})
|
||||
.unwrap_or(FULLSCREEN_ANIMATION_DURATION)),
|
||||
);
|
||||
if let Some(new_signal) = signal {
|
||||
if let Some(old_signal) = animation_signal.replace(new_signal) {
|
||||
old_signal.store(true, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
#[must_use]
|
||||
pub fn remove_fullscreen(&mut self) -> Option<(CosmicMapped, ManagedLayer, WorkspaceHandle)> {
|
||||
if let Some(surface) = self.fullscreen.as_ref().map(|f| f.surface.clone()) {
|
||||
self.unfullscreen_request(&surface)
|
||||
.map(|(l, h)| (self.element_for_surface(&surface).unwrap().clone(), l, h))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maximize_toggle(
|
||||
&mut self,
|
||||
window: &CosmicSurface,
|
||||
output: &Output,
|
||||
evlh: LoopHandle<'static, crate::state::State>,
|
||||
) {
|
||||
if self.fullscreen.contains_key(output) {
|
||||
pub fn maximize_toggle(&mut self, window: &CosmicSurface) {
|
||||
if window.is_maximized(true) {
|
||||
self.unmaximize_request(window);
|
||||
} else {
|
||||
self.maximize_request(window, output, evlh);
|
||||
self.maximize_request(window);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_fullscreen(&self, output: &Output) -> Option<&CosmicWindow> {
|
||||
pub fn get_fullscreen(&self) -> Option<&CosmicSurface> {
|
||||
self.fullscreen
|
||||
.get(output)
|
||||
.filter(|f| f.alive() && f.exclusive)
|
||||
.as_ref()
|
||||
.filter(|f| f.alive())
|
||||
.filter(|f| f.ended_at.is_none() && f.start_at.is_none())
|
||||
.map(|f| &f.window)
|
||||
}
|
||||
|
||||
pub fn get_maximized(&self, output: &Output) -> Option<&CosmicWindow> {
|
||||
self.fullscreen
|
||||
.get(output)
|
||||
.filter(|f| f.alive() && !f.exclusive)
|
||||
.filter(|f| f.ended_at.is_none() && f.start_at.is_none())
|
||||
.map(|f| &f.window)
|
||||
.map(|f| &f.surface)
|
||||
}
|
||||
|
||||
pub fn resize_request(
|
||||
|
|
@ -690,8 +627,8 @@ impl Workspace {
|
|||
if let Some(toplevel) = focused.toplevel() {
|
||||
if self
|
||||
.fullscreen
|
||||
.values()
|
||||
.any(|f| f.window.surface().wl_surface().as_ref() == Some(&toplevel))
|
||||
.as_ref()
|
||||
.is_some_and(|f| f.surface.wl_surface().as_ref() == Some(&toplevel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -713,15 +650,27 @@ impl Workspace {
|
|||
indicator_thickness: u8,
|
||||
) -> Option<MoveGrab> {
|
||||
let pointer = seat.get_pointer().unwrap();
|
||||
let pos = pointer.current_location();
|
||||
let pos = pointer.current_location().as_global();
|
||||
|
||||
if self
|
||||
.fullscreen
|
||||
.as_ref()
|
||||
.is_some_and(|f| &f.surface == window)
|
||||
{
|
||||
let _ = self.remove_fullscreen(); // We are moving this window, we don't need to send it back to it's original workspace
|
||||
}
|
||||
|
||||
let mapped = self.element_for_surface(&window)?.clone();
|
||||
let mut initial_window_location = self.element_geometry(&mapped).unwrap().loc;
|
||||
let mut initial_window_location = self
|
||||
.element_geometry(&mapped)
|
||||
.unwrap()
|
||||
.loc
|
||||
.to_global(&self.output);
|
||||
|
||||
if mapped.is_fullscreen(true) || mapped.is_maximized(true) {
|
||||
if mapped.maximized_state.lock().unwrap().is_some() {
|
||||
// If surface is maximized then unmaximize it
|
||||
let new_size = self.unmaximize_request(window);
|
||||
let ratio = pos.x / output.geometry().size.w as f64;
|
||||
let ratio = pos.to_local(&self.output).x / output.geometry().size.w as f64;
|
||||
|
||||
initial_window_location = new_size
|
||||
.map(|size| (pos.x - (size.w as f64 * ratio), pos.y).into())
|
||||
|
|
@ -754,7 +703,7 @@ impl Workspace {
|
|||
.into_iter()
|
||||
{
|
||||
self.tiling_layer.unmap(&window);
|
||||
self.floating_layer.map(window, seat, None);
|
||||
self.floating_layer.map(window, None);
|
||||
}
|
||||
self.tiling_enabled = false;
|
||||
} else {
|
||||
|
|
@ -768,7 +717,7 @@ impl Workspace {
|
|||
{
|
||||
self.floating_layer.unmap(&window);
|
||||
self.tiling_layer
|
||||
.map(window, seat, focus_stack.iter(), None)
|
||||
.map(window, Some(focus_stack.iter()), None)
|
||||
}
|
||||
self.tiling_enabled = true;
|
||||
}
|
||||
|
|
@ -779,12 +728,12 @@ impl Workspace {
|
|||
if let Some(window) = self.focus_stack.get(seat).iter().next().cloned() {
|
||||
if self.tiling_layer.mapped().any(|(_, m, _)| m == &window) {
|
||||
self.tiling_layer.unmap(&window);
|
||||
self.floating_layer.map(window, seat, None);
|
||||
self.floating_layer.map(window, None);
|
||||
} else if self.floating_layer.mapped().any(|w| w == &window) {
|
||||
let focus_stack = self.focus_stack.get(seat);
|
||||
self.floating_layer.unmap(&window);
|
||||
self.tiling_layer
|
||||
.map(window, seat, focus_stack.iter(), None)
|
||||
.map(window, Some(focus_stack.iter()), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -808,37 +757,23 @@ impl Workspace {
|
|||
|
||||
pub fn is_fullscreen(&self, mapped: &CosmicMapped) -> bool {
|
||||
self.fullscreen
|
||||
.values()
|
||||
.any(|f| f.exclusive && f.window.surface() == mapped.active_window())
|
||||
}
|
||||
|
||||
pub fn is_maximized(&self, mapped: &CosmicMapped) -> bool {
|
||||
self.fullscreen
|
||||
.values()
|
||||
.any(|f| !f.exclusive && f.window.surface() == mapped.active_window())
|
||||
.as_ref()
|
||||
.is_some_and(|f| f.surface == mapped.active_window())
|
||||
}
|
||||
|
||||
pub fn is_floating(&self, mapped: &CosmicMapped) -> bool {
|
||||
!self
|
||||
.fullscreen
|
||||
.values()
|
||||
.any(|f| f.window.surface() == mapped.active_window())
|
||||
&& self.floating_layer.mapped().any(|m| m == mapped)
|
||||
!self.is_fullscreen(mapped) && self.floating_layer.mapped().any(|m| m == mapped)
|
||||
}
|
||||
|
||||
pub fn is_tiled(&self, mapped: &CosmicMapped) -> bool {
|
||||
!self
|
||||
.fullscreen
|
||||
.values()
|
||||
.any(|f| f.window.surface() == mapped.active_window())
|
||||
&& self.tiling_layer.mapped().any(|(_, m, _)| m == mapped)
|
||||
!self.is_fullscreen(mapped) && self.tiling_layer.mapped().any(|(_, m, _)| m == mapped)
|
||||
}
|
||||
|
||||
pub fn node_desc(&self, focus: KeyboardFocusTarget) -> Option<NodeDesc> {
|
||||
match focus {
|
||||
KeyboardFocusTarget::Element(mapped) => {
|
||||
self.tiling_layer.mapped().find_map(|(output, m, _)| {
|
||||
(m == &mapped).then_some(output.clone()).and_then(|output| {
|
||||
self.tiling_layer.mapped().find_map(|(_, m, _)| {
|
||||
if m == &mapped {
|
||||
mapped
|
||||
.tiling_node_id
|
||||
.lock()
|
||||
|
|
@ -846,7 +781,6 @@ impl Workspace {
|
|||
.clone()
|
||||
.map(|node_id| NodeDesc {
|
||||
handle: self.handle.clone(),
|
||||
output: output.downgrade(),
|
||||
node: node_id,
|
||||
stack_window: if mapped
|
||||
.stack_ref()
|
||||
|
|
@ -858,12 +792,13 @@ impl Workspace {
|
|||
None
|
||||
},
|
||||
})
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
KeyboardFocusTarget::Group(WindowGroup { output, node, .. }) => Some(NodeDesc {
|
||||
KeyboardFocusTarget::Group(WindowGroup { node, .. }) => Some(NodeDesc {
|
||||
handle: self.handle.clone(),
|
||||
output,
|
||||
node,
|
||||
stack_window: None,
|
||||
}),
|
||||
|
|
@ -877,7 +812,7 @@ impl Workspace {
|
|||
seat: &Seat<State>,
|
||||
swap_desc: Option<NodeDesc>,
|
||||
) -> FocusResult {
|
||||
if self.fullscreen.contains_key(&seat.active_output()) {
|
||||
if self.fullscreen.is_some() {
|
||||
return FocusResult::None;
|
||||
}
|
||||
|
||||
|
|
@ -895,25 +830,25 @@ impl Workspace {
|
|||
direction: Direction,
|
||||
seat: &Seat<State>,
|
||||
) -> MoveResult {
|
||||
if let Some(f) = self.fullscreen.get(&seat.active_output()) {
|
||||
MoveResult::MoveFurther(KeyboardFocusTarget::Fullscreen(f.window.clone()))
|
||||
if let Some(f) = self.fullscreen.as_ref() {
|
||||
MoveResult::MoveFurther(KeyboardFocusTarget::Fullscreen(f.surface.clone()))
|
||||
} else {
|
||||
self.floating_layer
|
||||
.move_current_element(direction, seat)
|
||||
.move_current_element(direction, seat, self.tiling_layer.theme.clone())
|
||||
.or_else(|| self.tiling_layer.move_current_node(direction, seat))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_output<'a, R>(
|
||||
pub fn render<'a, R>(
|
||||
&self,
|
||||
renderer: &mut R,
|
||||
output: &Output,
|
||||
override_redirect_windows: &[X11Surface],
|
||||
xwm_state: Option<&'a mut XWaylandState>,
|
||||
draw_focus_indicator: Option<&Seat<State>>,
|
||||
overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree<Data>>)>),
|
||||
resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
|
||||
indicator_thickness: u8,
|
||||
theme: &CosmicTheme,
|
||||
) -> Result<
|
||||
(
|
||||
Vec<WorkspaceRenderElement<R>>,
|
||||
|
|
@ -935,20 +870,28 @@ impl Workspace {
|
|||
let mut window_elements = Vec::new();
|
||||
let mut popup_elements = Vec::new();
|
||||
|
||||
let output_scale = output.current_scale().fractional_scale();
|
||||
let layer_map = layer_map_for_output(output);
|
||||
let zone = layer_map.non_exclusive_zone();
|
||||
let output_scale = self.output.current_scale().fractional_scale();
|
||||
let zone = {
|
||||
let layer_map = layer_map_for_output(&self.output);
|
||||
layer_map.non_exclusive_zone().as_local()
|
||||
};
|
||||
|
||||
// OR windows above all
|
||||
popup_elements.extend(
|
||||
override_redirect_windows
|
||||
.iter()
|
||||
.filter(|or| (*or).geometry().intersection(output.geometry()).is_some())
|
||||
.filter(|or| {
|
||||
(*or)
|
||||
.geometry()
|
||||
.as_global()
|
||||
.intersection(self.output.geometry())
|
||||
.is_some()
|
||||
})
|
||||
.flat_map(|or| {
|
||||
AsRenderElements::<R>::render_elements::<WorkspaceRenderElement<R>>(
|
||||
or,
|
||||
renderer,
|
||||
(or.geometry().loc - output.geometry().loc)
|
||||
(or.geometry().loc - self.output.geometry().loc.as_logical())
|
||||
.to_physical_precise_round(output_scale),
|
||||
Scale::from(output_scale),
|
||||
1.0,
|
||||
|
|
@ -956,32 +899,27 @@ impl Workspace {
|
|||
}),
|
||||
);
|
||||
|
||||
if let Some(fullscreen) = self.fullscreen.get(output) {
|
||||
if let Some(fullscreen) = self.fullscreen.as_ref() {
|
||||
// fullscreen window
|
||||
let bbox = fullscreen.window.bbox();
|
||||
let bbox = fullscreen.surface.bbox().as_local();
|
||||
let element_geo = Rectangle::from_loc_and_size(
|
||||
self.element_for_surface(&fullscreen.window.surface())
|
||||
self.element_for_surface(&fullscreen.surface)
|
||||
.and_then(|elem| {
|
||||
self.floating_layer
|
||||
.space
|
||||
.element_geometry(elem)
|
||||
.or_else(|| self.tiling_layer.element_geometry(elem))
|
||||
.map(|mut geo| {
|
||||
geo.loc -= elem.geometry().loc;
|
||||
geo.loc -= elem.geometry().loc.as_local();
|
||||
geo
|
||||
})
|
||||
})
|
||||
.unwrap_or(bbox)
|
||||
.loc,
|
||||
fullscreen.original_size,
|
||||
fullscreen.original_geometry.size.as_local(),
|
||||
);
|
||||
|
||||
let mut full_geo = if fullscreen.exclusive {
|
||||
Rectangle::from_loc_and_size((0, 0), output.geometry().size)
|
||||
} else {
|
||||
layer_map.non_exclusive_zone()
|
||||
};
|
||||
|
||||
let mut full_geo =
|
||||
Rectangle::from_loc_and_size((0, 0), self.output.geometry().size.as_local());
|
||||
if fullscreen.start_at.is_none() {
|
||||
if bbox != full_geo {
|
||||
if bbox.size.w < full_geo.size.w {
|
||||
|
|
@ -1027,14 +965,17 @@ impl Workspace {
|
|||
(None, None) => (full_geo, 1.0),
|
||||
};
|
||||
|
||||
let render_loc = target_geo.loc.to_physical_precise_round(output_scale);
|
||||
let render_loc = target_geo
|
||||
.loc
|
||||
.as_logical()
|
||||
.to_physical_precise_round(output_scale);
|
||||
let scale = Scale {
|
||||
x: target_geo.size.w as f64 / bbox.size.w as f64,
|
||||
y: target_geo.size.h as f64 / bbox.size.h as f64,
|
||||
};
|
||||
|
||||
let (w_elements, p_elements) = fullscreen
|
||||
.window
|
||||
.surface
|
||||
.split_render_elements::<R, CosmicWindowRenderElement<R>>(
|
||||
renderer,
|
||||
render_loc,
|
||||
|
|
@ -1052,12 +993,12 @@ impl Workspace {
|
|||
|
||||
if self
|
||||
.fullscreen
|
||||
.get(output)
|
||||
.as_ref()
|
||||
.map(|f| f.start_at.is_some() || f.ended_at.is_some())
|
||||
.unwrap_or(true)
|
||||
{
|
||||
let focused = draw_focus_indicator
|
||||
.filter(|_| !self.fullscreen.contains_key(output))
|
||||
.filter(|_| !self.fullscreen.is_some())
|
||||
.and_then(|seat| self.focus_stack.get(seat).last().cloned());
|
||||
|
||||
// floating surfaces
|
||||
|
|
@ -1078,13 +1019,13 @@ impl Workspace {
|
|||
OverviewMode::None => 1.0,
|
||||
};
|
||||
|
||||
let (w_elements, p_elements) = self.floating_layer.render_output::<R>(
|
||||
let (w_elements, p_elements) = self.floating_layer.render::<R>(
|
||||
renderer,
|
||||
output,
|
||||
focused.as_ref(),
|
||||
resize_indicator.clone(),
|
||||
indicator_thickness,
|
||||
alpha,
|
||||
theme,
|
||||
);
|
||||
popup_elements.extend(p_elements.into_iter().map(WorkspaceRenderElement::from));
|
||||
window_elements.extend(w_elements.into_iter().map(WorkspaceRenderElement::from));
|
||||
|
|
@ -1102,14 +1043,14 @@ impl Workspace {
|
|||
};
|
||||
|
||||
//tiling surfaces
|
||||
let (w_elements, p_elements) = self.tiling_layer.render_output::<R>(
|
||||
let (w_elements, p_elements) = self.tiling_layer.render::<R>(
|
||||
renderer,
|
||||
output,
|
||||
draw_focus_indicator,
|
||||
layer_map.non_exclusive_zone(),
|
||||
zone,
|
||||
overview,
|
||||
resize_indicator,
|
||||
indicator_thickness,
|
||||
theme,
|
||||
)?;
|
||||
popup_elements.extend(p_elements.into_iter().map(WorkspaceRenderElement::from));
|
||||
window_elements.extend(w_elements.into_iter().map(WorkspaceRenderElement::from));
|
||||
|
|
@ -1131,7 +1072,7 @@ impl Workspace {
|
|||
|
||||
if let Some(xwm) = xwm_state.and_then(|state| state.xwm.as_mut()) {
|
||||
if let Err(err) =
|
||||
xwm.update_stacking_order_upwards(window_elements.iter().map(|e| e.id()))
|
||||
xwm.update_stacking_order_upwards(window_elements.iter().rev().map(|e| e.id()))
|
||||
{
|
||||
warn!(
|
||||
wm_id = ?xwm.id(),
|
||||
|
|
|
|||
82
src/state.rs
82
src/state.rs
|
|
@ -137,6 +137,8 @@ pub struct Common {
|
|||
pub clock: Clock<Monotonic>,
|
||||
pub should_stop: bool,
|
||||
|
||||
pub theme: cosmic::Theme,
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
pub egui: Egui,
|
||||
|
||||
|
|
@ -283,7 +285,7 @@ impl State {
|
|||
.with_context(|| "Failed to load languages")
|
||||
.unwrap();
|
||||
|
||||
let clock = Clock::new().expect("Failed to initialize clock");
|
||||
let clock = Clock::new();
|
||||
let config = Config::load(&handle);
|
||||
let compositor_state = CompositorState::new::<Self>(dh);
|
||||
let data_device_state = DataDeviceState::new::<Self>(dh);
|
||||
|
|
@ -330,6 +332,8 @@ impl State {
|
|||
clock,
|
||||
should_stop: false,
|
||||
|
||||
theme: cosmic::theme::system_preference(),
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
egui: Egui {
|
||||
active: false,
|
||||
|
|
@ -511,50 +515,34 @@ impl Common {
|
|||
|
||||
let active = self.shell.active_space(output);
|
||||
active.mapped().for_each(|mapped| {
|
||||
let outputs_for_element: Vec<_> = active.outputs_for_element(mapped).collect();
|
||||
if outputs_for_element.contains(&output) {
|
||||
let window = mapped.active_window();
|
||||
window.with_surfaces(|surface, states| {
|
||||
let primary_scanout_output = update_surface_primary_scanout_output(
|
||||
surface,
|
||||
output,
|
||||
states,
|
||||
render_element_states,
|
||||
|current_output, current_state, next_output, next_state| {
|
||||
if outputs_for_element.contains(current_output) {
|
||||
default_primary_scanout_output_compare(
|
||||
current_output,
|
||||
current_state,
|
||||
next_output,
|
||||
next_state,
|
||||
)
|
||||
} else {
|
||||
next_output
|
||||
}
|
||||
},
|
||||
);
|
||||
if let Some(output) = primary_scanout_output {
|
||||
with_fractional_scale(states, |fraction_scale| {
|
||||
fraction_scale
|
||||
.set_preferred_scale(output.current_scale().fractional_scale());
|
||||
});
|
||||
}
|
||||
});
|
||||
window.send_frame(output, time, throttle, surface_primary_scanout_output);
|
||||
if let Some(feedback) = window
|
||||
.wl_surface()
|
||||
.and_then(|wl_surface| {
|
||||
source_node_for_surface(&wl_surface, &self.display_handle)
|
||||
})
|
||||
.and_then(|source| dmabuf_feedback(source))
|
||||
{
|
||||
window.send_dmabuf_feedback(
|
||||
output,
|
||||
&feedback,
|
||||
render_element_states,
|
||||
surface_primary_scanout_output,
|
||||
);
|
||||
let window = mapped.active_window();
|
||||
window.with_surfaces(|surface, states| {
|
||||
let primary_scanout_output = update_surface_primary_scanout_output(
|
||||
surface,
|
||||
output,
|
||||
states,
|
||||
render_element_states,
|
||||
|_current_output, _current_state, next_output, _next_state| next_output,
|
||||
);
|
||||
if let Some(output) = primary_scanout_output {
|
||||
with_fractional_scale(states, |fraction_scale| {
|
||||
fraction_scale
|
||||
.set_preferred_scale(output.current_scale().fractional_scale());
|
||||
});
|
||||
}
|
||||
});
|
||||
window.send_frame(output, time, throttle, surface_primary_scanout_output);
|
||||
if let Some(feedback) = window
|
||||
.wl_surface()
|
||||
.and_then(|wl_surface| source_node_for_surface(&wl_surface, &self.display_handle))
|
||||
.and_then(|source| dmabuf_feedback(source))
|
||||
{
|
||||
window.send_dmabuf_feedback(
|
||||
output,
|
||||
&feedback,
|
||||
render_element_states,
|
||||
surface_primary_scanout_output,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -565,10 +553,8 @@ impl Common {
|
|||
.filter(|w| w.handle != active.handle)
|
||||
{
|
||||
space.mapped().for_each(|mapped| {
|
||||
if space.outputs_for_element(mapped).any(|o| &o == output) {
|
||||
let window = mapped.active_window();
|
||||
window.send_frame(output, time, throttle, |_, _| None);
|
||||
}
|
||||
let window = mapped.active_window();
|
||||
window.send_frame(space.output(), time, throttle, |_, _| None);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
67
src/theme.rs
Normal file
67
src/theme.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// insert into the event loop, a watcher for the theme & theme mode for changes
|
||||
|
||||
// update a Arc<Mutex<Theme>> in the state on change of the theme and mark all interfaces for a redraw.
|
||||
|
||||
use calloop::LoopHandle;
|
||||
use cosmic::cosmic_theme::{
|
||||
palette::{self, Srgba},
|
||||
Theme, ThemeMode,
|
||||
};
|
||||
|
||||
use crate::state::State;
|
||||
|
||||
pub(crate) fn group_color(theme: &Theme<Srgba>) -> [f32; 3] {
|
||||
let neutral_8 = theme.palette.neutral_8;
|
||||
[neutral_8.red, neutral_8.green, neutral_8.blue]
|
||||
}
|
||||
|
||||
pub(crate) fn active_window_hint(theme: &Theme<Srgba>) -> palette::Srgba {
|
||||
if let Some(hint) = theme.window_hint {
|
||||
palette::Srgba::from(hint)
|
||||
} else {
|
||||
theme.accent_color()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn watch_theme(handle: LoopHandle<'_, State>) -> Result<(), cosmic_config::Error> {
|
||||
let (ping_tx, ping_rx) = calloop::ping::make_ping().unwrap();
|
||||
let config_mode_helper = ThemeMode::config()?;
|
||||
let config_dark_helper = Theme::<palette::Srgba>::dark_config()?;
|
||||
let config_light_helper = Theme::<palette::Srgba>::light_config()?;
|
||||
|
||||
if let Err(e) = handle.insert_source(ping_rx, move |_, _, state| {
|
||||
let new_theme = cosmic::theme::system_preference();
|
||||
let theme = &mut state.common.theme;
|
||||
|
||||
if theme.theme_type != new_theme.theme_type {
|
||||
*theme = new_theme;
|
||||
state.common.shell.set_theme(theme.clone());
|
||||
state.common.shell.workspaces.spaces().for_each(|s| {
|
||||
s.mapped().for_each(|m| {
|
||||
m.update_theme(theme.clone());
|
||||
m.force_redraw();
|
||||
})
|
||||
});
|
||||
}
|
||||
}) {
|
||||
tracing::error!("{e}");
|
||||
};
|
||||
|
||||
let ping_tx_clone = ping_tx.clone();
|
||||
let theme_watcher_mode = config_mode_helper.watch(move |_, _keys| {
|
||||
ping_tx_clone.ping();
|
||||
})?;
|
||||
let ping_tx_clone = ping_tx.clone();
|
||||
let theme_watcher_light = config_light_helper.watch(move |_, _keys| {
|
||||
ping_tx_clone.ping();
|
||||
})?;
|
||||
let theme_watcher_dark = config_dark_helper.watch(move |_, _keys| {
|
||||
ping_tx.ping();
|
||||
})?;
|
||||
|
||||
std::mem::forget(theme_watcher_dark);
|
||||
std::mem::forget(theme_watcher_light);
|
||||
std::mem::forget(theme_watcher_mode);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
149
src/utils/geometry.rs
Normal file
149
src/utils/geometry.rs
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
use smithay::{
|
||||
output::Output,
|
||||
utils::{Coordinate, Logical, Point, Rectangle, Size},
|
||||
};
|
||||
|
||||
use super::prelude::OutputExt;
|
||||
|
||||
/// Marker type for coordinates in global space
|
||||
#[derive(Debug)]
|
||||
pub struct Global;
|
||||
|
||||
/// Marker type for coordinates in workspace local space
|
||||
#[derive(Debug)]
|
||||
pub struct Local;
|
||||
|
||||
pub trait PointExt<C: Coordinate> {
|
||||
fn as_global(self) -> Point<C, Global>;
|
||||
fn as_local(self) -> Point<C, Local>;
|
||||
}
|
||||
|
||||
pub trait PointGlobalExt<C: Coordinate> {
|
||||
fn to_local(self, output: &Output) -> Point<C, Local>;
|
||||
fn as_logical(self) -> Point<C, Logical>;
|
||||
}
|
||||
|
||||
pub trait PointLocalExt<C: Coordinate> {
|
||||
fn to_global(self, output: &Output) -> Point<C, Global>;
|
||||
fn as_logical(self) -> Point<C, Logical>;
|
||||
}
|
||||
|
||||
pub trait SizeExt<C: Coordinate> {
|
||||
fn as_logical(self) -> Size<C, Logical>;
|
||||
fn as_local(self) -> Size<C, Local>;
|
||||
fn as_global(self) -> Size<C, Global>;
|
||||
}
|
||||
|
||||
pub trait RectExt<C: Coordinate> {
|
||||
fn as_global(self) -> Rectangle<C, Global>;
|
||||
fn as_local(self) -> Rectangle<C, Local>;
|
||||
}
|
||||
|
||||
pub trait RectGlobalExt<C: Coordinate> {
|
||||
fn to_local(self, output: &Output) -> Rectangle<C, Local>;
|
||||
fn as_logical(self) -> Rectangle<C, Logical>;
|
||||
}
|
||||
|
||||
pub trait RectLocalExt<C: Coordinate> {
|
||||
fn to_global(self, output: &Output) -> Rectangle<C, Global>;
|
||||
fn as_logical(self) -> Rectangle<C, Logical>;
|
||||
}
|
||||
|
||||
impl<C: Coordinate> PointExt<C> for Point<C, Logical> {
|
||||
fn as_global(self) -> Point<C, Global> {
|
||||
(self.x, self.y).into()
|
||||
}
|
||||
|
||||
fn as_local(self) -> Point<C, Local> {
|
||||
(self.x, self.y).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Coordinate> PointGlobalExt<C> for Point<C, Global> {
|
||||
fn to_local(self, output: &Output) -> Point<C, Local> {
|
||||
let point = (self.to_f64() - output.geometry().loc.to_f64()).as_logical();
|
||||
(C::from_f64(point.x), C::from_f64(point.y)).into()
|
||||
}
|
||||
|
||||
fn as_logical(self) -> Point<C, Logical> {
|
||||
(self.x, self.y).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Coordinate> PointLocalExt<C> for Point<C, Local> {
|
||||
fn to_global(self, output: &Output) -> Point<C, Global> {
|
||||
let point =
|
||||
(self.to_f64().as_logical() + output.geometry().loc.to_f64().as_logical()).as_global();
|
||||
(C::from_f64(point.x), C::from_f64(point.y)).into()
|
||||
}
|
||||
|
||||
fn as_logical(self) -> Point<C, Logical> {
|
||||
(self.x, self.y).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Coordinate> SizeExt<C> for Size<C, Global> {
|
||||
fn as_logical(self) -> Size<C, Logical> {
|
||||
(self.w, self.h).into()
|
||||
}
|
||||
fn as_global(self) -> Size<C, Global> {
|
||||
self
|
||||
}
|
||||
fn as_local(self) -> Size<C, Local> {
|
||||
(self.w, self.h).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Coordinate> SizeExt<C> for Size<C, Local> {
|
||||
fn as_logical(self) -> Size<C, Logical> {
|
||||
(self.w, self.h).into()
|
||||
}
|
||||
fn as_global(self) -> Size<C, Global> {
|
||||
(self.w, self.h).into()
|
||||
}
|
||||
fn as_local(self) -> Size<C, Local> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Coordinate> SizeExt<C> for Size<C, Logical> {
|
||||
fn as_logical(self) -> Size<C, Logical> {
|
||||
self
|
||||
}
|
||||
fn as_global(self) -> Size<C, Global> {
|
||||
(self.w, self.h).into()
|
||||
}
|
||||
fn as_local(self) -> Size<C, Local> {
|
||||
(self.w, self.h).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Coordinate> RectExt<C> for Rectangle<C, Logical> {
|
||||
fn as_global(self) -> Rectangle<C, Global> {
|
||||
Rectangle::from_loc_and_size(self.loc.as_global(), (self.size.w, self.size.h))
|
||||
}
|
||||
|
||||
fn as_local(self) -> Rectangle<C, Local> {
|
||||
Rectangle::from_loc_and_size(self.loc.as_local(), (self.size.w, self.size.h))
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Coordinate> RectGlobalExt<C> for Rectangle<C, Global> {
|
||||
fn to_local(self, output: &Output) -> Rectangle<C, Local> {
|
||||
Rectangle::from_loc_and_size(self.loc.to_local(output), (self.size.w, self.size.h))
|
||||
}
|
||||
|
||||
fn as_logical(self) -> Rectangle<C, Logical> {
|
||||
Rectangle::from_loc_and_size(self.loc.as_logical(), self.size.as_logical())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Coordinate> RectLocalExt<C> for Rectangle<C, Local> {
|
||||
fn to_global(self, output: &Output) -> Rectangle<C, Global> {
|
||||
Rectangle::from_loc_and_size(self.loc.to_global(output), (self.size.w, self.size.h))
|
||||
}
|
||||
|
||||
fn as_logical(self) -> Rectangle<C, Logical> {
|
||||
Rectangle::from_loc_and_size(self.loc.as_logical(), self.size.as_logical())
|
||||
}
|
||||
}
|
||||
|
|
@ -229,6 +229,7 @@ impl<P: Program + Send + 'static> IcedElement<P> {
|
|||
program: P,
|
||||
size: impl Into<Size<i32, Logical>>,
|
||||
handle: LoopHandle<'static, crate::state::State>,
|
||||
theme: cosmic::Theme,
|
||||
) -> IcedElement<P> {
|
||||
let size = size.into();
|
||||
let mut renderer =
|
||||
|
|
@ -257,7 +258,7 @@ impl<P: Program + Send + 'static> IcedElement<P> {
|
|||
pending_update: None,
|
||||
size,
|
||||
cursor_pos: None,
|
||||
theme: Theme::dark(), // TODO
|
||||
theme,
|
||||
renderer,
|
||||
state,
|
||||
debug,
|
||||
|
|
@ -308,6 +309,11 @@ impl<P: Program + Send + 'static> IcedElement<P> {
|
|||
self.0.lock().unwrap().update(true);
|
||||
}
|
||||
|
||||
pub fn set_theme(&self, theme: cosmic::Theme) {
|
||||
let mut guard = self.0.lock().unwrap();
|
||||
guard.theme = theme.clone();
|
||||
}
|
||||
|
||||
pub fn force_redraw(&self) {
|
||||
let mut internal = self.0.lock().unwrap();
|
||||
for (_buffer, ref mut old_primitives) in internal.buffers.values_mut() {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
mod ids;
|
||||
pub(crate) use self::ids::id_gen;
|
||||
pub mod geometry;
|
||||
pub mod iced;
|
||||
pub mod prelude;
|
||||
pub mod tween;
|
||||
|
|
|
|||
|
|
@ -11,20 +11,21 @@ use smithay::{
|
|||
Seat,
|
||||
},
|
||||
output::Output,
|
||||
utils::{Buffer, IsAlive, Logical, Monotonic, Point, Rectangle, Time, Transform},
|
||||
utils::{Buffer, IsAlive, Monotonic, Point, Rectangle, Time, Transform},
|
||||
wayland::compositor::with_states,
|
||||
};
|
||||
|
||||
pub use super::geometry::*;
|
||||
pub use crate::shell::{Shell, Workspace};
|
||||
pub use crate::state::{Common, State};
|
||||
pub use crate::wayland::handlers::xdg_shell::popup::update_reactive_popups;
|
||||
|
||||
pub trait OutputExt {
|
||||
fn geometry(&self) -> Rectangle<i32, Logical>;
|
||||
fn geometry(&self) -> Rectangle<i32, Global>;
|
||||
}
|
||||
|
||||
impl OutputExt for Output {
|
||||
fn geometry(&self) -> Rectangle<i32, Logical> {
|
||||
fn geometry(&self) -> Rectangle<i32, Global> {
|
||||
Rectangle::from_loc_and_size(self.current_location(), {
|
||||
Transform::from(self.current_transform())
|
||||
.transform_size(
|
||||
|
|
@ -36,6 +37,7 @@ impl OutputExt for Output {
|
|||
.to_logical(self.current_scale().fractional_scale())
|
||||
.to_i32_round()
|
||||
})
|
||||
.as_global()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -171,12 +171,12 @@ impl CompositorHandler for State {
|
|||
on_commit_buffer_handler::<Self>(surface);
|
||||
|
||||
// then handle initial configure events and map windows if necessary
|
||||
if let Some((window, seat)) = self
|
||||
if let Some((window, _, _)) = self
|
||||
.common
|
||||
.shell
|
||||
.pending_windows
|
||||
.iter()
|
||||
.find(|(window, _)| window.wl_surface().as_ref() == Some(surface))
|
||||
.find(|(window, _, _)| window.wl_surface().as_ref() == Some(surface))
|
||||
.cloned()
|
||||
{
|
||||
match window {
|
||||
|
|
@ -185,9 +185,8 @@ impl CompositorHandler for State {
|
|||
if self.toplevel_ensure_initial_configure(&toplevel)
|
||||
&& with_renderer_surface_state(&surface, |state| state.buffer().is_some())
|
||||
{
|
||||
let output = seat.active_output();
|
||||
window.on_commit();
|
||||
Shell::map_window(self, &window, &output);
|
||||
Shell::map_window(self, &window);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
|
@ -254,7 +253,7 @@ impl CompositorHandler for State {
|
|||
let changed = layer_map_for_output(&output).arrange();
|
||||
if changed {
|
||||
for workspace in self.common.shell.workspaces.spaces_mut() {
|
||||
workspace.recalculate(&output);
|
||||
workspace.recalculate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@ impl WlrLayerShellHandler for State {
|
|||
}
|
||||
|
||||
fn new_popup(&mut self, _parent: WlrLayerSurface, popup: PopupSurface) {
|
||||
let positioner = popup.with_pending_state(|state| state.positioner);
|
||||
self.common.shell.unconstrain_popup(&popup, &positioner);
|
||||
self.common.shell.unconstrain_popup(&popup);
|
||||
|
||||
if popup.send_configure().is_ok() {
|
||||
self.common
|
||||
|
|
@ -76,7 +75,7 @@ impl WlrLayerShellHandler for State {
|
|||
}
|
||||
|
||||
for workspace in self.common.shell.workspaces.spaces_mut() {
|
||||
workspace.recalculate(&output);
|
||||
workspace.recalculate();
|
||||
}
|
||||
|
||||
// collect screencopy sessions needing an update
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ use crate::{
|
|||
},
|
||||
shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement},
|
||||
state::{BackendData, ClientState, Common, State},
|
||||
utils::prelude::OutputExt,
|
||||
utils::prelude::{OutputExt, PointExt},
|
||||
wayland::protocols::{
|
||||
screencopy::{
|
||||
delegate_screencopy, BufferInfo, BufferParams, CursorMode as ScreencopyCursorMode,
|
||||
|
|
@ -106,7 +106,7 @@ impl ScreencopyHandler for State {
|
|||
if let Some(pointer) = seat.get_pointer() {
|
||||
if output
|
||||
.geometry()
|
||||
.contains(pointer.current_location().to_i32_round())
|
||||
.contains(pointer.current_location().to_i32_round().as_global())
|
||||
{
|
||||
session.cursor_enter(seat, InputType::Pointer);
|
||||
}
|
||||
|
|
@ -1015,7 +1015,12 @@ pub fn render_window_to_buffer(
|
|||
renderer.bind(render_buffer).map_err(DTError::Rendering)?;
|
||||
}
|
||||
|
||||
dt.render_output(renderer, age, &elements, CLEAR_COLOR)
|
||||
dt.render_output(
|
||||
renderer,
|
||||
age,
|
||||
&elements,
|
||||
CLEAR_COLOR, // TODO use a theme neutral color
|
||||
)
|
||||
}
|
||||
|
||||
let node = node_from_params(¶ms, &mut state.backend, None);
|
||||
|
|
@ -1296,7 +1301,7 @@ pub fn schedule_offscreen_workspace_session(
|
|||
if !session.alive() {
|
||||
return;
|
||||
}
|
||||
if !state.common.shell.outputs.contains(&output) {
|
||||
if !state.common.shell.outputs().any(|o| o == &output) {
|
||||
return;
|
||||
}
|
||||
match render_workspace_to_buffer(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{
|
||||
shell::WorkspaceMode,
|
||||
state::ClientState,
|
||||
utils::prelude::*,
|
||||
wayland::protocols::workspace::{
|
||||
|
|
@ -30,19 +29,12 @@ impl WorkspaceHandler for State {
|
|||
for request in requests.into_iter() {
|
||||
match request {
|
||||
Request::Activate(handle) => {
|
||||
let maybe = match &self.common.shell.workspaces {
|
||||
WorkspaceMode::Global(set) => set
|
||||
.workspaces
|
||||
let maybe = self.common.shell.workspaces.iter().find_map(|(o, set)| {
|
||||
set.workspaces
|
||||
.iter()
|
||||
.position(|w| w.handle == handle)
|
||||
.map(|i| (self.common.last_active_seat().active_output(), i)),
|
||||
WorkspaceMode::OutputBound(sets, _) => sets.iter().find_map(|(o, set)| {
|
||||
set.workspaces
|
||||
.iter()
|
||||
.position(|w| w.handle == handle)
|
||||
.map(|i| (o.clone(), i))
|
||||
}),
|
||||
};
|
||||
.map(|i| (o.clone(), i))
|
||||
});
|
||||
|
||||
if let Some((output, idx)) = maybe {
|
||||
let _ = self.common.shell.activate(&output, idx); // TODO: move cursor?
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{shell::CosmicSurface, utils::prelude::*, wayland::protocols::screencopy::SessionType};
|
||||
use crate::{
|
||||
shell::{element::CosmicWindow, CosmicMapped, CosmicSurface, ManagedLayer},
|
||||
utils::prelude::*,
|
||||
wayland::protocols::screencopy::SessionType,
|
||||
};
|
||||
use smithay::{
|
||||
delegate_xdg_shell,
|
||||
desktop::{
|
||||
|
|
@ -38,7 +42,7 @@ impl XdgShellHandler for State {
|
|||
fn new_toplevel(&mut self, surface: ToplevelSurface) {
|
||||
let seat = self.common.last_active_seat().clone();
|
||||
let window = CosmicSurface::Wayland(Window::new(surface));
|
||||
self.common.shell.pending_windows.push((window, seat));
|
||||
self.common.shell.pending_windows.push((window, seat, None));
|
||||
// We will position the window after the first commit, when we know its size hints
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +54,7 @@ impl XdgShellHandler for State {
|
|||
|
||||
if surface.get_parent_surface().is_some() {
|
||||
// let other shells deal with their popups
|
||||
self.common.shell.unconstrain_popup(&surface, &positioner);
|
||||
self.common.shell.unconstrain_popup(&surface);
|
||||
|
||||
if surface.send_configure().is_ok() {
|
||||
self.common
|
||||
|
|
@ -123,7 +127,7 @@ impl XdgShellHandler for State {
|
|||
state.positioner = positioner;
|
||||
});
|
||||
|
||||
self.common.shell.unconstrain_popup(&surface, &positioner);
|
||||
self.common.shell.unconstrain_popup(&surface);
|
||||
surface.send_repositioned(token);
|
||||
if let Err(err) = surface.send_configure() {
|
||||
warn!(
|
||||
|
|
@ -150,9 +154,6 @@ impl XdgShellHandler for State {
|
|||
}
|
||||
|
||||
fn maximize_request(&mut self, surface: ToplevelSurface) {
|
||||
let seat = self.common.last_active_seat();
|
||||
let output = seat.active_output();
|
||||
|
||||
if let Some(mapped) = self
|
||||
.common
|
||||
.shell
|
||||
|
|
@ -164,7 +165,7 @@ impl XdgShellHandler for State {
|
|||
.windows()
|
||||
.find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface()))
|
||||
.unwrap();
|
||||
workspace.maximize_request(&window, &output, self.common.event_loop_handle.clone())
|
||||
workspace.maximize_request(&window)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -187,13 +188,14 @@ impl XdgShellHandler for State {
|
|||
}
|
||||
|
||||
fn fullscreen_request(&mut self, surface: ToplevelSurface, output: Option<WlOutput>) {
|
||||
let active_output = {
|
||||
let seat = self.common.last_active_seat();
|
||||
seat.active_output()
|
||||
};
|
||||
let output = output
|
||||
.as_ref()
|
||||
.and_then(Output::from_resource)
|
||||
.unwrap_or_else(|| {
|
||||
let seat = self.common.last_active_seat();
|
||||
seat.active_output()
|
||||
});
|
||||
.unwrap_or_else(|| active_output.clone());
|
||||
|
||||
if let Some(mapped) = self
|
||||
.common
|
||||
|
|
@ -202,15 +204,70 @@ impl XdgShellHandler for State {
|
|||
.cloned()
|
||||
{
|
||||
if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
|
||||
let (window, _) = mapped
|
||||
.windows()
|
||||
.find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface()))
|
||||
.unwrap();
|
||||
workspace.fullscreen_request(
|
||||
&window,
|
||||
&output,
|
||||
self.common.event_loop_handle.clone(),
|
||||
)
|
||||
if workspace.output != output {
|
||||
let (mapped, layer) = if mapped
|
||||
.stack_ref()
|
||||
.map(|stack| stack.len() > 1)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let stack = mapped.stack_ref().unwrap();
|
||||
let surface = stack
|
||||
.surfaces()
|
||||
.find(|s| s.wl_surface().as_ref() == Some(surface.wl_surface()))
|
||||
.unwrap();
|
||||
stack.remove_window(&surface);
|
||||
(
|
||||
CosmicMapped::from(CosmicWindow::new(
|
||||
surface,
|
||||
self.common.event_loop_handle.clone(),
|
||||
self.common.theme.clone(),
|
||||
)),
|
||||
if workspace.is_tiled(&mapped) {
|
||||
ManagedLayer::Tiling
|
||||
} else {
|
||||
ManagedLayer::Floating
|
||||
},
|
||||
)
|
||||
} else {
|
||||
let layer = workspace.unmap(&mapped).unwrap().layer;
|
||||
(mapped, layer)
|
||||
};
|
||||
let handle = workspace.handle.clone();
|
||||
std::mem::drop(workspace);
|
||||
|
||||
let workspace_handle = self.common.shell.active_space(&output).handle.clone();
|
||||
for (window, _) in mapped.windows() {
|
||||
self.common
|
||||
.shell
|
||||
.toplevel_info_state
|
||||
.toplevel_enter_output(&window, &output);
|
||||
self.common
|
||||
.shell
|
||||
.toplevel_info_state
|
||||
.toplevel_enter_workspace(&window, &workspace_handle);
|
||||
}
|
||||
|
||||
let workspace = self.common.shell.active_space_mut(&output);
|
||||
workspace.floating_layer.map(mapped.clone(), None);
|
||||
workspace.fullscreen_request(&mapped.active_window(), Some((layer, handle)));
|
||||
} else {
|
||||
let (window, _) = mapped
|
||||
.windows()
|
||||
.find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface()))
|
||||
.unwrap();
|
||||
workspace.fullscreen_request(&window, None)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(o) = self
|
||||
.common
|
||||
.shell
|
||||
.pending_windows
|
||||
.iter_mut()
|
||||
.find(|(s, _, _)| s.wl_surface().as_ref() == Some(surface.wl_surface()))
|
||||
.map(|(_, _, o)| o)
|
||||
{
|
||||
*o = Some(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -227,7 +284,23 @@ impl XdgShellHandler for State {
|
|||
.windows()
|
||||
.find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface()))
|
||||
.unwrap();
|
||||
workspace.unfullscreen_request(&window);
|
||||
if let Some((layer, previous_workspace)) = workspace.unfullscreen_request(&window) {
|
||||
let old_handle = workspace.handle.clone();
|
||||
let new_workspace_handle = self
|
||||
.common
|
||||
.shell
|
||||
.space_for_handle(&previous_workspace)
|
||||
.is_some()
|
||||
.then_some(previous_workspace)
|
||||
.unwrap_or(old_handle); // if the workspace doesn't exist anymore, we can still remap on the right layer
|
||||
|
||||
self.common.shell.remap_unfullscreened_window(
|
||||
mapped,
|
||||
&old_handle,
|
||||
&new_workspace_handle,
|
||||
layer,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,27 +27,28 @@ use std::sync::Mutex;
|
|||
use tracing::{trace, warn};
|
||||
|
||||
impl Shell {
|
||||
pub fn unconstrain_popup(&self, surface: &PopupSurface, positioner: &PositionerState) {
|
||||
pub fn unconstrain_popup(&self, surface: &PopupSurface) {
|
||||
if let Some(parent) = get_popup_toplevel(&surface) {
|
||||
if let Some(elem) = self.element_for_wl_surface(&parent) {
|
||||
let workspace = self.space_for(elem).unwrap();
|
||||
let mut element_geo = workspace.element_geometry(elem).unwrap();
|
||||
let mut element_geo = workspace
|
||||
.element_geometry(elem)
|
||||
.unwrap()
|
||||
.to_global(workspace.output());
|
||||
let (window, offset) = elem
|
||||
.windows()
|
||||
.find(|(w, _)| w.wl_surface().as_ref() == Some(&parent))
|
||||
.unwrap();
|
||||
let window_geo_offset = window.geometry().loc;
|
||||
let window_loc = element_geo.loc + offset + window_geo_offset;
|
||||
let anchor_point = get_anchor_point(&positioner) + window_loc;
|
||||
let window_loc: Point<i32, Global> =
|
||||
element_geo.loc + offset.as_global() + window_geo_offset.as_global();
|
||||
if workspace.is_tiled(elem) {
|
||||
element_geo.loc = (0, 0).into(); //-= window_loc;
|
||||
if !unconstrain_xdg_popup_tile(surface, element_geo) {
|
||||
if let Some(output) = workspace.output_under(anchor_point) {
|
||||
unconstrain_xdg_popup(surface, window_loc, output.geometry());
|
||||
}
|
||||
element_geo.loc = (0, 0).into();
|
||||
if !unconstrain_xdg_popup_tile(surface, element_geo.as_logical()) {
|
||||
unconstrain_xdg_popup(surface, window_loc, workspace.output().geometry());
|
||||
}
|
||||
} else if let Some(output) = workspace.output_under(anchor_point) {
|
||||
unconstrain_xdg_popup(surface, window_loc, output.geometry());
|
||||
} else {
|
||||
unconstrain_xdg_popup(surface, window_loc, workspace.output().geometry());
|
||||
}
|
||||
} else if let Some((output, layer_surface)) = self.outputs().find_map(|o| {
|
||||
let map = layer_map_for_output(o);
|
||||
|
|
@ -62,7 +63,7 @@ impl Shell {
|
|||
|
||||
pub fn update_reactive_popups<'a>(
|
||||
window: &Window,
|
||||
loc: Point<i32, Logical>,
|
||||
loc: Point<i32, Global>,
|
||||
outputs: impl Iterator<Item = &'a Output>,
|
||||
) {
|
||||
let output_geo = outputs.map(|o| o.geometry()).collect::<Vec<_>>();
|
||||
|
|
@ -79,7 +80,7 @@ pub fn update_reactive_popups<'a>(
|
|||
attributes.current.positioner.clone()
|
||||
});
|
||||
if positioner.reactive {
|
||||
let anchor_point = get_anchor_point(&positioner) + loc;
|
||||
let anchor_point = loc + get_anchor_point(&positioner).as_global();
|
||||
if let Some(rect) = output_geo
|
||||
.iter()
|
||||
.find(|geo| geo.contains(anchor_point))
|
||||
|
|
@ -101,7 +102,9 @@ pub fn update_reactive_popups<'a>(
|
|||
}
|
||||
|
||||
fn unconstrain_xdg_popup_tile(surface: &PopupSurface, rect: Rectangle<i32, Logical>) -> bool {
|
||||
let geometry = surface.with_pending_state(|state| state.positioner.get_geometry());
|
||||
let toplevel_offset = get_popup_toplevel_coords(surface);
|
||||
let mut geometry = surface.with_pending_state(|state| state.positioner.get_geometry());
|
||||
geometry.loc += toplevel_offset;
|
||||
let offset = check_constrained(geometry, rect);
|
||||
|
||||
if offset.x != 0 || offset.y != 0 {
|
||||
|
|
@ -116,12 +119,14 @@ fn unconstrain_xdg_popup_tile(surface: &PopupSurface, rect: Rectangle<i32, Logic
|
|||
|
||||
fn unconstrain_xdg_popup(
|
||||
surface: &PopupSurface,
|
||||
window_loc: Point<i32, Logical>,
|
||||
rect: Rectangle<i32, Logical>,
|
||||
window_loc: Point<i32, Global>,
|
||||
mut rect: Rectangle<i32, Global>,
|
||||
) {
|
||||
let mut relative = rect;
|
||||
relative.loc -= window_loc;
|
||||
let geometry = surface.with_pending_state(|state| state.positioner.get_geometry());
|
||||
rect.loc -= window_loc;
|
||||
let relative = rect.as_logical();
|
||||
let toplevel_offset = get_popup_toplevel_coords(surface);
|
||||
let mut geometry = surface.with_pending_state(|state| state.positioner.get_geometry());
|
||||
geometry.loc += toplevel_offset;
|
||||
let offset = check_constrained(geometry, relative);
|
||||
|
||||
if offset.x != 0 || offset.y != 0 {
|
||||
|
|
@ -139,9 +144,11 @@ fn unconstrain_layer_popup(surface: &PopupSurface, output: &Output, layer_surfac
|
|||
let layer_geo = map.layer_geometry(layer_surface).unwrap();
|
||||
|
||||
// the output_rect represented relative to the parents coordinate system
|
||||
let mut relative = Rectangle::from_loc_and_size((0, 0), output.geometry().size);
|
||||
let mut relative = Rectangle::from_loc_and_size((0, 0), output.geometry().size).as_logical();
|
||||
relative.loc -= layer_geo.loc;
|
||||
let geometry = surface.with_pending_state(|state| state.positioner.get_geometry());
|
||||
let toplevel_offset = get_popup_toplevel_coords(surface);
|
||||
let mut geometry = surface.with_pending_state(|state| state.positioner.get_geometry());
|
||||
geometry.loc += toplevel_offset;
|
||||
let offset = check_constrained(geometry, relative);
|
||||
|
||||
if offset.x != 0 || offset.y != 0 {
|
||||
|
|
@ -155,8 +162,11 @@ fn unconstrain_layer_popup(surface: &PopupSurface, output: &Output, layer_surfac
|
|||
}
|
||||
|
||||
fn unconstrain_flip(popup: &PopupSurface, toplevel_box: Rectangle<i32, Logical>) -> bool {
|
||||
let toplevel_offset = get_popup_toplevel_coords(popup);
|
||||
let positioner = popup.with_pending_state(|state| state.positioner.clone());
|
||||
let offset = check_constrained(positioner.get_geometry(), toplevel_box);
|
||||
let mut geometry = positioner.get_geometry();
|
||||
geometry.loc += toplevel_offset;
|
||||
let offset = check_constrained(geometry, toplevel_box);
|
||||
if offset.x == 0 && offset.y == 0 {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -176,7 +186,9 @@ fn unconstrain_flip(popup: &PopupSurface, toplevel_box: Rectangle<i32, Logical>)
|
|||
let old_positioner = positioner.clone();
|
||||
positioner.anchor_edges = invert_anchor_x(positioner.anchor_edges);
|
||||
positioner.gravity = invert_gravity_x(positioner.gravity);
|
||||
let new_offset = check_constrained(positioner.get_geometry(), toplevel_box);
|
||||
geometry = positioner.get_geometry();
|
||||
geometry.loc += toplevel_offset;
|
||||
let new_offset = check_constrained(geometry, toplevel_box);
|
||||
if !(new_offset.x.abs() < offset.x.abs()) {
|
||||
positioner = old_positioner;
|
||||
}
|
||||
|
|
@ -185,13 +197,17 @@ fn unconstrain_flip(popup: &PopupSurface, toplevel_box: Rectangle<i32, Logical>)
|
|||
let old_positioner = positioner.clone();
|
||||
positioner.anchor_edges = invert_anchor_y(positioner.anchor_edges);
|
||||
positioner.gravity = invert_gravity_y(positioner.gravity);
|
||||
let new_offset = check_constrained(positioner.get_geometry(), toplevel_box);
|
||||
geometry = positioner.get_geometry();
|
||||
geometry.loc += toplevel_offset;
|
||||
let new_offset = check_constrained(geometry, toplevel_box);
|
||||
if !(new_offset.y.abs() < offset.y.abs()) {
|
||||
positioner = old_positioner;
|
||||
}
|
||||
}
|
||||
|
||||
let new_offset = check_constrained(positioner.get_geometry(), toplevel_box);
|
||||
geometry = positioner.get_geometry();
|
||||
geometry.loc += toplevel_offset;
|
||||
let new_offset = check_constrained(geometry, toplevel_box);
|
||||
if new_offset.x.abs() < offset.x.abs() || new_offset.y.abs() < offset.y.abs() {
|
||||
popup.with_pending_state(|state| {
|
||||
state.geometry = positioner.get_geometry();
|
||||
|
|
@ -203,8 +219,11 @@ fn unconstrain_flip(popup: &PopupSurface, toplevel_box: Rectangle<i32, Logical>)
|
|||
}
|
||||
|
||||
fn unconstrain_slide(popup: &PopupSurface, toplevel_box: Rectangle<i32, Logical>) -> bool {
|
||||
let toplevel_offset = get_popup_toplevel_coords(popup);
|
||||
let positioner = popup.with_pending_state(|state| state.positioner.clone());
|
||||
let offset = check_constrained(positioner.get_geometry(), toplevel_box);
|
||||
let mut geometry = positioner.get_geometry();
|
||||
geometry.loc += toplevel_offset;
|
||||
let offset = check_constrained(geometry, toplevel_box);
|
||||
if offset.x == 0 && offset.y == 0 {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -234,7 +253,9 @@ fn unconstrain_slide(popup: &PopupSurface, toplevel_box: Rectangle<i32, Logical>
|
|||
geometry.loc.y += toplevel_box.loc.y - toplevel.y;
|
||||
}
|
||||
|
||||
let new_offset = check_constrained(geometry, toplevel_box);
|
||||
let mut check_geometry = geometry.clone();
|
||||
check_geometry.loc += toplevel;
|
||||
let new_offset = check_constrained(check_geometry, toplevel_box);
|
||||
if new_offset.x.abs() < offset.x.abs() || new_offset.y.abs() < offset.y.abs() {
|
||||
popup.with_pending_state(|state| {
|
||||
state.geometry = geometry;
|
||||
|
|
@ -245,8 +266,11 @@ fn unconstrain_slide(popup: &PopupSurface, toplevel_box: Rectangle<i32, Logical>
|
|||
}
|
||||
|
||||
fn unconstrain_resize(popup: &PopupSurface, toplevel_box: Rectangle<i32, Logical>) -> bool {
|
||||
let toplevel_offset = get_popup_toplevel_coords(popup);
|
||||
let positioner = popup.with_pending_state(|state| state.positioner.clone());
|
||||
let offset = check_constrained(positioner.get_geometry(), toplevel_box);
|
||||
let mut geometry = positioner.get_geometry();
|
||||
geometry.loc += toplevel_offset;
|
||||
let offset = check_constrained(geometry, toplevel_box);
|
||||
if offset.x == 0 && offset.y == 0 {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -268,6 +292,8 @@ fn unconstrain_resize(popup: &PopupSurface, toplevel_box: Rectangle<i32, Logical
|
|||
geometry.size.h -= offset.y;
|
||||
}
|
||||
|
||||
let mut check_geometry = geometry.clone();
|
||||
check_geometry.loc += toplevel_offset;
|
||||
let offset = check_constrained(geometry, toplevel_box);
|
||||
if offset.x == 0 && offset.y == 0 {
|
||||
// no longer constrained
|
||||
|
|
|
|||
|
|
@ -151,16 +151,19 @@ impl XwmHandler for State {
|
|||
}
|
||||
|
||||
let seat = self.common.last_active_seat().clone();
|
||||
self.common.shell.pending_windows.push((surface, seat));
|
||||
self.common
|
||||
.shell
|
||||
.pending_windows
|
||||
.push((surface, seat, None));
|
||||
}
|
||||
|
||||
fn map_window_notify(&mut self, _xwm: XwmId, surface: X11Surface) {
|
||||
if let Some((window, seat)) = self
|
||||
if let Some((window, _, _)) = self
|
||||
.common
|
||||
.shell
|
||||
.pending_windows
|
||||
.iter()
|
||||
.find(|(window, _)| {
|
||||
.find(|(window, _, _)| {
|
||||
if let CosmicSurface::X11(window) = window {
|
||||
window == &surface
|
||||
} else {
|
||||
|
|
@ -169,8 +172,7 @@ impl XwmHandler for State {
|
|||
})
|
||||
.cloned()
|
||||
{
|
||||
let output = seat.active_output();
|
||||
Shell::map_window(self, &window, &output);
|
||||
Shell::map_window(self, &window);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -277,13 +279,16 @@ impl XwmHandler for State {
|
|||
{
|
||||
let space = self.common.shell.space_for(mapped).unwrap();
|
||||
if space.is_floating(mapped) {
|
||||
mapped.set_geometry(Rectangle::from_loc_and_size(
|
||||
current_geo.loc,
|
||||
(
|
||||
w.map(|w| w as i32).unwrap_or(current_geo.size.w),
|
||||
h.map(|h| h as i32).unwrap_or(current_geo.size.h),
|
||||
),
|
||||
))
|
||||
mapped.set_geometry(
|
||||
Rectangle::from_loc_and_size(
|
||||
current_geo.loc,
|
||||
(
|
||||
w.map(|w| w as i32).unwrap_or(current_geo.size.w),
|
||||
h.map(|h| h as i32).unwrap_or(current_geo.size.h),
|
||||
),
|
||||
)
|
||||
.as_global(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if let Some(x) = x {
|
||||
|
|
@ -325,13 +330,13 @@ impl XwmHandler for State {
|
|||
}
|
||||
}
|
||||
|
||||
let geo = window.geometry();
|
||||
let geo = window.geometry().as_global();
|
||||
for (output, overlap) in self.common.shell.outputs().cloned().map(|o| {
|
||||
let intersection = o.geometry().intersection(geo);
|
||||
(o, intersection)
|
||||
}) {
|
||||
if let Some(overlap) = overlap {
|
||||
window.output_enter(&output, overlap);
|
||||
window.output_enter(&output, overlap.as_logical());
|
||||
} else {
|
||||
window.output_leave(&output);
|
||||
}
|
||||
|
|
@ -360,14 +365,11 @@ impl XwmHandler for State {
|
|||
}
|
||||
|
||||
fn maximize_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||
let seat = self.common.last_active_seat();
|
||||
let output = seat.active_output();
|
||||
let surface = CosmicSurface::X11(window);
|
||||
|
||||
if let Some(mapped) = self.common.shell.element_for_surface(&surface).cloned() {
|
||||
if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
|
||||
let (window, _) = mapped.windows().find(|(w, _)| w == &surface).unwrap();
|
||||
workspace.maximize_request(&window, &output, self.common.event_loop_handle.clone())
|
||||
workspace.maximize_request(&window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -383,18 +385,10 @@ impl XwmHandler for State {
|
|||
}
|
||||
|
||||
fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||
let seat = self.common.last_active_seat();
|
||||
let output = seat.active_output();
|
||||
let surface = CosmicSurface::X11(window);
|
||||
|
||||
if let Some(mapped) = self.common.shell.element_for_surface(&surface).cloned() {
|
||||
if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
|
||||
let (window, _) = mapped.windows().find(|(w, _)| w == &surface).unwrap();
|
||||
workspace.fullscreen_request(
|
||||
&window,
|
||||
&output,
|
||||
self.common.event_loop_handle.clone(),
|
||||
)
|
||||
workspace.fullscreen_request(&surface, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -404,7 +398,8 @@ impl XwmHandler for State {
|
|||
if let Some(mapped) = self.common.shell.element_for_surface(&surface).cloned() {
|
||||
if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
|
||||
let (window, _) = mapped.windows().find(|(w, _)| w == &surface).unwrap();
|
||||
workspace.unfullscreen_request(&window);
|
||||
let previous = workspace.unfullscreen_request(&window);
|
||||
assert!(previous.is_none());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue