mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-30 20:34:49 +01:00
Get xterm to show up
This commit is contained in:
parent
52094c9504
commit
f92153eb04
15 changed files with 1055 additions and 826 deletions
|
@ -21,9 +21,10 @@ calloop = { version = "0.10.1", features = ["executor", "futures-io"] }
|
|||
futures-lite = { version = "1.13.0" }
|
||||
async-process = { version = "1.7.0" }
|
||||
itertools = { version = "0.11.0" }
|
||||
x11rb = { version = "0.11.1", default-features = false, features = ["composite"], optional = true }
|
||||
|
||||
[features]
|
||||
default = ["egl", "winit", "udev"]
|
||||
default = ["egl", "winit", "udev", "xwayland"]
|
||||
egl = ["smithay/use_system_lib", "smithay/backend_egl"]
|
||||
udev = [
|
||||
"smithay-drm-extras",
|
||||
|
@ -40,3 +41,4 @@ udev = [
|
|||
"xcursor",
|
||||
]
|
||||
winit = ["smithay/backend_winit", "smithay/backend_drm"]
|
||||
xwayland = ["smithay/backend_x11", "x11rb", "smithay/x11rb_event_source", "xcursor"]
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
error::Error,
|
||||
ffi::OsString,
|
||||
os::fd::FromRawFd,
|
||||
path::Path,
|
||||
sync::Mutex,
|
||||
|
@ -52,7 +53,7 @@ use smithay::{
|
|||
desktop::{
|
||||
space::{self, SurfaceTree},
|
||||
utils::{self, surface_primary_scanout_output, OutputPresentationFeedback},
|
||||
Space, Window,
|
||||
Space,
|
||||
},
|
||||
input::pointer::{CursorImageAttributes, CursorImageStatus},
|
||||
output::{Output, PhysicalProperties, Subpixel},
|
||||
|
@ -100,6 +101,7 @@ use crate::{
|
|||
api::msg::{Args, OutgoingMsg},
|
||||
render::{pointer::PointerElement, CustomRenderElements, OutputRenderElements},
|
||||
state::{take_presentation_feedback, CalloopData, State, SurfaceDmabufFeedback},
|
||||
window::WindowElement,
|
||||
};
|
||||
|
||||
use super::Backend;
|
||||
|
@ -449,6 +451,16 @@ pub fn run_udev() -> Result<(), Box<dyn Error>> {
|
|||
});
|
||||
});
|
||||
|
||||
if let Err(err) = state.xwayland.start(
|
||||
state.loop_handle.clone(),
|
||||
None,
|
||||
std::iter::empty::<(OsString, OsString)>(),
|
||||
true,
|
||||
|_| {},
|
||||
) {
|
||||
tracing::error!("Failed to start XWayland: {err}");
|
||||
}
|
||||
|
||||
event_loop.run(
|
||||
Some(Duration::from_millis(6)),
|
||||
&mut CalloopData { state, display },
|
||||
|
@ -1457,7 +1469,7 @@ impl State<UdevData> {
|
|||
fn render_surface<'a>(
|
||||
surface: &'a mut SurfaceData,
|
||||
renderer: &mut UdevRenderer<'a, '_>,
|
||||
space: &Space<Window>,
|
||||
space: &Space<WindowElement>,
|
||||
output: &Output,
|
||||
input_method: &InputMethodHandle,
|
||||
pointer_location: Point<f64, Logical>,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
use std::{error::Error, sync::Mutex, time::Duration};
|
||||
use std::{error::Error, ffi::OsString, sync::Mutex, time::Duration};
|
||||
|
||||
use smithay::{
|
||||
backend::{
|
||||
|
@ -192,6 +192,16 @@ pub fn run_winit() -> Result<(), Box<dyn Error>> {
|
|||
|
||||
state.space.map_output(&output, (0, 0));
|
||||
|
||||
if let Err(err) = state.xwayland.start(
|
||||
state.loop_handle.clone(),
|
||||
None,
|
||||
std::iter::empty::<(OsString, OsString)>(),
|
||||
true,
|
||||
|_| {},
|
||||
) {
|
||||
tracing::error!("Failed to start XWayland: {err}");
|
||||
}
|
||||
|
||||
let mut pointer_element = PointerElement::<GlesTexture>::new();
|
||||
|
||||
// TODO: pointer
|
||||
|
|
10
src/focus.rs
10
src/focus.rs
|
@ -1,10 +1,12 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
use smithay::{desktop::Window, output::Output, utils::IsAlive};
|
||||
use smithay::{output::Output, utils::IsAlive};
|
||||
|
||||
use crate::window::WindowElement;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FocusState {
|
||||
focus_stack: Vec<Window>,
|
||||
focus_stack: Vec<WindowElement>,
|
||||
pub focused_output: Option<Output>,
|
||||
}
|
||||
|
||||
|
@ -15,7 +17,7 @@ impl FocusState {
|
|||
|
||||
// TODO: how does this work with unmapped windows?
|
||||
/// Get the currently focused window. If there is none, the previous focus is returned.
|
||||
pub fn current_focus(&mut self) -> Option<Window> {
|
||||
pub fn current_focus(&mut self) -> Option<WindowElement> {
|
||||
while let Some(window) = self.focus_stack.last() {
|
||||
if window.alive() {
|
||||
return Some(window.clone());
|
||||
|
@ -26,7 +28,7 @@ impl FocusState {
|
|||
}
|
||||
|
||||
/// Set the currently focused window.
|
||||
pub fn set_focus(&mut self, window: Window) {
|
||||
pub fn set_focus(&mut self, window: WindowElement) {
|
||||
self.focus_stack.retain(|win| win != &window);
|
||||
self.focus_stack.push(window);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
use smithay::{
|
||||
desktop::Window,
|
||||
desktop::space::SpaceElement,
|
||||
// NOTE: maybe alias this to PointerGrabStartData because there's another GrabStartData in
|
||||
// | input::keyboard
|
||||
input::{
|
||||
|
@ -18,12 +18,12 @@ use smithay::{
|
|||
use crate::{
|
||||
backend::Backend,
|
||||
state::{State, WithState},
|
||||
window::window_state::WindowResizeState,
|
||||
window::{window_state::WindowResizeState, WindowElement},
|
||||
};
|
||||
|
||||
pub struct MoveSurfaceGrab<S: SeatHandler> {
|
||||
pub start_data: GrabStartData<S>,
|
||||
pub window: Window,
|
||||
pub window: WindowElement,
|
||||
pub initial_window_loc: Point<i32, Logical>,
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,13 @@ impl<B: Backend> PointerGrab<State<B>> for MoveSurfaceGrab<State<B>> {
|
|||
}
|
||||
|
||||
data.space.raise_element(&self.window, false);
|
||||
if let WindowElement::X11(surface) = &self.window {
|
||||
data.xwm
|
||||
.as_mut()
|
||||
.expect("no xwm")
|
||||
.raise_window(surface)
|
||||
.expect("failed to raise x11 win");
|
||||
}
|
||||
|
||||
// tracing::info!("window geo is: {:?}", self.window.geometry());
|
||||
// tracing::info!("loc is: {:?}", data.space.element_location(&self.window));
|
||||
|
|
|
@ -1,27 +1,47 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
use smithay::{
|
||||
desktop::Window,
|
||||
desktop::space::SpaceElement,
|
||||
input::{
|
||||
pointer::{AxisFrame, ButtonEvent, GrabStartData, PointerGrab, PointerInnerHandle},
|
||||
SeatHandler,
|
||||
},
|
||||
reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge},
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel::{self},
|
||||
wayland_server::protocol::wl_surface::WlSurface,
|
||||
},
|
||||
utils::{IsAlive, Logical, Point, Rectangle, Size},
|
||||
wayland::{compositor, seat::WaylandFocus, shell::xdg::SurfaceCachedState},
|
||||
wayland::{compositor, shell::xdg::SurfaceCachedState},
|
||||
xwayland,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
state::{State, WithState},
|
||||
window::WindowElement,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ResizeEdge(pub xdg_toplevel::ResizeEdge);
|
||||
|
||||
impl From<xwayland::xwm::ResizeEdge> for ResizeEdge {
|
||||
fn from(value: xwayland::xwm::ResizeEdge) -> Self {
|
||||
match value {
|
||||
xwayland::xwm::ResizeEdge::Bottom => Self(xdg_toplevel::ResizeEdge::Bottom),
|
||||
xwayland::xwm::ResizeEdge::BottomLeft => Self(xdg_toplevel::ResizeEdge::BottomLeft),
|
||||
xwayland::xwm::ResizeEdge::BottomRight => Self(xdg_toplevel::ResizeEdge::BottomRight),
|
||||
xwayland::xwm::ResizeEdge::Left => Self(xdg_toplevel::ResizeEdge::Left),
|
||||
xwayland::xwm::ResizeEdge::Right => Self(xdg_toplevel::ResizeEdge::Right),
|
||||
xwayland::xwm::ResizeEdge::Top => Self(xdg_toplevel::ResizeEdge::Top),
|
||||
xwayland::xwm::ResizeEdge::TopLeft => Self(xdg_toplevel::ResizeEdge::TopLeft),
|
||||
xwayland::xwm::ResizeEdge::TopRight => Self(xdg_toplevel::ResizeEdge::TopRight),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResizeSurfaceGrab<S: SeatHandler> {
|
||||
start_data: GrabStartData<S>,
|
||||
window: Window,
|
||||
window: WindowElement,
|
||||
|
||||
edges: ResizeEdge,
|
||||
|
||||
|
@ -34,26 +54,26 @@ pub struct ResizeSurfaceGrab<S: SeatHandler> {
|
|||
impl<S: SeatHandler> ResizeSurfaceGrab<S> {
|
||||
pub fn start(
|
||||
start_data: GrabStartData<S>,
|
||||
window: Window,
|
||||
window: WindowElement,
|
||||
edges: ResizeEdge,
|
||||
initial_window_rect: Rectangle<i32, Logical>,
|
||||
button_used: u32,
|
||||
) -> Self {
|
||||
window.toplevel().wl_surface().with_state(|state| {
|
||||
) -> Option<Self> {
|
||||
window.wl_surface()?.with_state(|state| {
|
||||
state.resize_state = ResizeSurfaceState::Resizing {
|
||||
edges,
|
||||
initial_window_rect,
|
||||
};
|
||||
});
|
||||
|
||||
Self {
|
||||
Some(Self {
|
||||
start_data,
|
||||
window,
|
||||
edges,
|
||||
initial_window_rect,
|
||||
last_window_size: initial_window_rect.size,
|
||||
button_used,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,16 +97,28 @@ impl<B: Backend> PointerGrab<State<B>> for ResizeSurfaceGrab<State<B>> {
|
|||
let mut new_window_width = self.initial_window_rect.size.w;
|
||||
let mut new_window_height = self.initial_window_rect.size.h;
|
||||
|
||||
if let ResizeEdge::Left | ResizeEdge::TopLeft | ResizeEdge::BottomLeft = self.edges {
|
||||
if let xdg_toplevel::ResizeEdge::Left
|
||||
| xdg_toplevel::ResizeEdge::TopLeft
|
||||
| xdg_toplevel::ResizeEdge::BottomLeft = self.edges.0
|
||||
{
|
||||
new_window_width = self.initial_window_rect.size.w - delta.x;
|
||||
}
|
||||
if let ResizeEdge::Right | ResizeEdge::TopRight | ResizeEdge::BottomRight = self.edges {
|
||||
if let xdg_toplevel::ResizeEdge::Right
|
||||
| xdg_toplevel::ResizeEdge::TopRight
|
||||
| xdg_toplevel::ResizeEdge::BottomRight = self.edges.0
|
||||
{
|
||||
new_window_width = self.initial_window_rect.size.w + delta.x;
|
||||
}
|
||||
if let ResizeEdge::Top | ResizeEdge::TopRight | ResizeEdge::TopLeft = self.edges {
|
||||
if let xdg_toplevel::ResizeEdge::Top
|
||||
| xdg_toplevel::ResizeEdge::TopRight
|
||||
| xdg_toplevel::ResizeEdge::TopLeft = self.edges.0
|
||||
{
|
||||
new_window_height = self.initial_window_rect.size.h - delta.y;
|
||||
}
|
||||
if let ResizeEdge::Bottom | ResizeEdge::BottomRight | ResizeEdge::BottomLeft = self.edges {
|
||||
if let xdg_toplevel::ResizeEdge::Bottom
|
||||
| xdg_toplevel::ResizeEdge::BottomRight
|
||||
| xdg_toplevel::ResizeEdge::BottomLeft = self.edges.0
|
||||
{
|
||||
new_window_height = self.initial_window_rect.size.h + delta.y;
|
||||
}
|
||||
|
||||
|
@ -129,14 +161,27 @@ impl<B: Backend> PointerGrab<State<B>> for ResizeSurfaceGrab<State<B>> {
|
|||
new_window_height.clamp(min_height, max_height),
|
||||
));
|
||||
|
||||
let toplevel_surface = self.window.toplevel();
|
||||
match &self.window {
|
||||
WindowElement::Wayland(window) => {
|
||||
let toplevel_surface = window.toplevel();
|
||||
|
||||
toplevel_surface.with_pending_state(|state| {
|
||||
state.states.set(xdg_toplevel::State::Resizing);
|
||||
state.size = Some(self.last_window_size);
|
||||
});
|
||||
toplevel_surface.with_pending_state(|state| {
|
||||
state.states.set(xdg_toplevel::State::Resizing);
|
||||
state.size = Some(self.last_window_size);
|
||||
});
|
||||
|
||||
toplevel_surface.send_pending_configure();
|
||||
toplevel_surface.send_pending_configure();
|
||||
}
|
||||
WindowElement::X11(surface) => {
|
||||
let loc = data
|
||||
.space
|
||||
.element_location(&self.window)
|
||||
.expect("failed to get x11 win loc");
|
||||
surface
|
||||
.configure(Rectangle::from_loc_and_size(loc, self.last_window_size))
|
||||
.expect("failed to configure x11 win");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn relative_motion(
|
||||
|
@ -160,20 +205,38 @@ impl<B: Backend> PointerGrab<State<B>> for ResizeSurfaceGrab<State<B>> {
|
|||
if !handle.current_pressed().contains(&self.button_used) {
|
||||
handle.unset_grab(data, event.serial, event.time);
|
||||
|
||||
let toplevel_surface = self.window.toplevel();
|
||||
toplevel_surface.with_pending_state(|state| {
|
||||
state.states.unset(xdg_toplevel::State::Resizing);
|
||||
state.size = Some(self.last_window_size);
|
||||
});
|
||||
if !self.window.alive() {
|
||||
return;
|
||||
}
|
||||
|
||||
toplevel_surface.send_pending_configure();
|
||||
match &self.window {
|
||||
WindowElement::Wayland(window) => {
|
||||
let toplevel_surface = window.toplevel();
|
||||
toplevel_surface.with_pending_state(|state| {
|
||||
state.states.unset(xdg_toplevel::State::Resizing);
|
||||
state.size = Some(self.last_window_size);
|
||||
});
|
||||
|
||||
toplevel_surface.wl_surface().with_state(|state| {
|
||||
state.resize_state = ResizeSurfaceState::WaitingForLastCommit {
|
||||
edges: self.edges,
|
||||
initial_window_rect: self.initial_window_rect,
|
||||
};
|
||||
});
|
||||
toplevel_surface.send_pending_configure();
|
||||
|
||||
toplevel_surface.wl_surface().with_state(|state| {
|
||||
// TODO: validate resize state
|
||||
state.resize_state = ResizeSurfaceState::WaitingForLastCommit {
|
||||
edges: self.edges,
|
||||
initial_window_rect: self.initial_window_rect,
|
||||
};
|
||||
});
|
||||
}
|
||||
WindowElement::X11(surface) => {
|
||||
let Some(surface) = surface.wl_surface() else { return };
|
||||
surface.with_state(|state| {
|
||||
state.resize_state = ResizeSurfaceState::WaitingForLastCommit {
|
||||
edges: self.edges,
|
||||
initial_window_rect: self.initial_window_rect,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,12 +299,18 @@ pub fn handle_commit<B: Backend>(state: &mut State<B>, surface: &WlSurface) -> O
|
|||
.map(|(edges, initial_window_rect)| {
|
||||
let mut new_x: Option<i32> = None;
|
||||
let mut new_y: Option<i32> = None;
|
||||
if let ResizeEdge::Left | ResizeEdge::TopLeft | ResizeEdge::BottomLeft = edges {
|
||||
if let xdg_toplevel::ResizeEdge::Left
|
||||
| xdg_toplevel::ResizeEdge::TopLeft
|
||||
| xdg_toplevel::ResizeEdge::BottomLeft = edges.0
|
||||
{
|
||||
new_x = Some(
|
||||
initial_window_rect.loc.x + (initial_window_rect.size.w - geometry.size.w),
|
||||
);
|
||||
}
|
||||
if let ResizeEdge::Top | ResizeEdge::TopLeft | ResizeEdge::TopRight = edges {
|
||||
if let xdg_toplevel::ResizeEdge::Top
|
||||
| xdg_toplevel::ResizeEdge::TopLeft
|
||||
| xdg_toplevel::ResizeEdge::TopRight = edges.0
|
||||
{
|
||||
new_y = Some(
|
||||
initial_window_rect.loc.y + (initial_window_rect.size.h - geometry.size.h),
|
||||
);
|
||||
|
|
|
@ -43,12 +43,13 @@ use smithay::{
|
|||
},
|
||||
shm::{ShmHandler, ShmState},
|
||||
},
|
||||
xwayland::{X11Wm, XWaylandClientData},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
state::{ClientState, State, WithState},
|
||||
window::{window_state::WindowResizeState, WindowBlocker, BLOCKER_COUNTER},
|
||||
state::{CalloopData, ClientState, State, WithState},
|
||||
window::{window_state::WindowResizeState, WindowBlocker, WindowElement, BLOCKER_COUNTER},
|
||||
};
|
||||
|
||||
impl<B: Backend> BufferHandler for State<B> {
|
||||
|
@ -96,6 +97,8 @@ impl<B: Backend> CompositorHandler for State<B> {
|
|||
fn commit(&mut self, surface: &WlSurface) {
|
||||
// tracing::debug!("commit");
|
||||
|
||||
X11Wm::commit_hook::<CalloopData<B>>(surface);
|
||||
|
||||
utils::on_commit_buffer_handler::<Self>(surface);
|
||||
|
||||
if !compositor::is_sync_subsurface(surface) {
|
||||
|
@ -103,7 +106,7 @@ impl<B: Backend> CompositorHandler for State<B> {
|
|||
while let Some(parent) = compositor::get_parent(&root) {
|
||||
root = parent;
|
||||
}
|
||||
if let Some(window) = self.window_for_surface(surface) {
|
||||
if let Some(WindowElement::Wayland(window)) = self.window_for_surface(surface) {
|
||||
window.on_commit();
|
||||
}
|
||||
};
|
||||
|
@ -131,30 +134,34 @@ impl<B: Backend> CompositorHandler for State<B> {
|
|||
}
|
||||
|
||||
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
|
||||
&client
|
||||
.get_data::<ClientState>()
|
||||
.expect("ClientState wasn't in client's data map")
|
||||
.compositor_state
|
||||
if let Some(state) = client.get_data::<XWaylandClientData>() {
|
||||
return &state.compositor_state;
|
||||
}
|
||||
if let Some(state) = client.get_data::<ClientState>() {
|
||||
return &state.compositor_state;
|
||||
}
|
||||
panic!("Unknown client data type");
|
||||
}
|
||||
}
|
||||
delegate_compositor!(@<B: Backend> State<B>);
|
||||
|
||||
fn ensure_initial_configure<B: Backend>(surface: &WlSurface, state: &mut State<B>) {
|
||||
if let Some(window) = state.window_for_surface(surface) {
|
||||
let initial_configure_sent = compositor::with_states(surface, |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
|
||||
.lock()
|
||||
.expect("Failed to lock Mutex<XdgToplevelSurfaceData>")
|
||||
.initial_configure_sent
|
||||
});
|
||||
// println!("initial_configure_sent is {}", initial_configure_sent);
|
||||
if let WindowElement::Wayland(window) = &window {
|
||||
let initial_configure_sent = compositor::with_states(surface, |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
|
||||
.lock()
|
||||
.expect("Failed to lock Mutex<XdgToplevelSurfaceData>")
|
||||
.initial_configure_sent
|
||||
});
|
||||
|
||||
if !initial_configure_sent {
|
||||
tracing::debug!("Initial configure");
|
||||
window.toplevel().send_configure();
|
||||
if !initial_configure_sent {
|
||||
tracing::debug!("Initial configure");
|
||||
window.toplevel().send_configure();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -227,14 +234,18 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
}
|
||||
|
||||
fn new_toplevel(&mut self, surface: ToplevelSurface) {
|
||||
let window = Window::new(surface);
|
||||
let window = WindowElement::Wayland(Window::new(surface));
|
||||
|
||||
{
|
||||
let WindowElement::Wayland(window) = &window else { unreachable!() };
|
||||
window.toplevel().with_pending_state(|tl_state| {
|
||||
tl_state.states.set(xdg_toplevel::State::TiledTop);
|
||||
tl_state.states.set(xdg_toplevel::State::TiledBottom);
|
||||
tl_state.states.set(xdg_toplevel::State::TiledLeft);
|
||||
tl_state.states.set(xdg_toplevel::State::TiledRight);
|
||||
});
|
||||
}
|
||||
|
||||
window.toplevel().with_pending_state(|tl_state| {
|
||||
tl_state.states.set(xdg_toplevel::State::TiledTop);
|
||||
tl_state.states.set(xdg_toplevel::State::TiledBottom);
|
||||
tl_state.states.set(xdg_toplevel::State::TiledLeft);
|
||||
tl_state.states.set(xdg_toplevel::State::TiledRight);
|
||||
});
|
||||
window.with_state(|state| {
|
||||
state.tags = match (
|
||||
&self.focus_state.focused_output,
|
||||
|
@ -296,7 +307,9 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst)
|
||||
);
|
||||
for win in windows_on_output.iter() {
|
||||
compositor::add_blocker(win.toplevel().wl_surface(), WindowBlocker);
|
||||
if let Some(surf) = win.wl_surface() {
|
||||
compositor::add_blocker(&surf, WindowBlocker);
|
||||
}
|
||||
}
|
||||
let clone = window.clone();
|
||||
self.loop_handle.insert_idle(|data| {
|
||||
|
@ -308,7 +321,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
);
|
||||
for client in windows_on_output
|
||||
.iter()
|
||||
.filter_map(|win| win.toplevel().wl_surface().client())
|
||||
.filter_map(|win| win.wl_surface()?.client())
|
||||
{
|
||||
data.state
|
||||
.client_compositor_state(&client)
|
||||
|
@ -324,7 +337,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
.expect("Seat had no keyboard") // FIXME: actually handle error
|
||||
.set_focus(
|
||||
&mut data.state,
|
||||
Some(window.toplevel().wl_surface().clone()),
|
||||
window.wl_surface(),
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
);
|
||||
});
|
||||
|
@ -332,7 +345,11 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
|
||||
fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {
|
||||
tracing::debug!("toplevel destroyed");
|
||||
self.windows.retain(|window| window.toplevel() != &surface);
|
||||
self.windows.retain(|window| {
|
||||
window
|
||||
.wl_surface()
|
||||
.is_some_and(|surf| &surf != surface.wl_surface())
|
||||
});
|
||||
if let Some(focused_output) = self.focus_state.focused_output.as_ref() {
|
||||
focused_output.with_state(|state| {
|
||||
let first_tag = state.focused_tags().next();
|
||||
|
@ -353,7 +370,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
let focus = self
|
||||
.focus_state
|
||||
.current_focus()
|
||||
.map(|win| win.toplevel().wl_surface().clone());
|
||||
.and_then(|win| win.wl_surface());
|
||||
self.seat
|
||||
.get_keyboard()
|
||||
.expect("Seat had no keyboard")
|
||||
|
@ -385,7 +402,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
const BUTTON_LEFT: u32 = 0x110;
|
||||
crate::xdg::request::resize_request(
|
||||
self,
|
||||
&surface,
|
||||
surface.wl_surface(),
|
||||
&Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"),
|
||||
serial,
|
||||
edges,
|
||||
|
@ -413,12 +430,11 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
.ok()
|
||||
.and_then(|root| self.window_for_surface(&root))
|
||||
{
|
||||
if let Ok(mut grab) = self.popup_manager.grab_popup(
|
||||
root.toplevel().wl_surface().clone(),
|
||||
popup_kind,
|
||||
&seat,
|
||||
serial,
|
||||
) {
|
||||
let Some(wl_surface) = root.wl_surface() else { return };
|
||||
if let Ok(mut grab) = self
|
||||
.popup_manager
|
||||
.grab_popup(wl_surface, popup_kind, &seat, serial)
|
||||
{
|
||||
if let Some(keyboard) = seat.get_keyboard() {
|
||||
if keyboard.is_grabbed()
|
||||
&& !(keyboard.has_grab(serial)
|
||||
|
|
|
@ -4,66 +4,181 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use smithay::xwayland::XwmHandler;
|
||||
use smithay::{
|
||||
desktop::space::SpaceElement,
|
||||
input::pointer::Focus,
|
||||
reexports::wayland_server::Resource,
|
||||
utils::{Rectangle, SERIAL_COUNTER},
|
||||
wayland::compositor::{self, CompositorHandler},
|
||||
xwayland::{xwm::XwmId, X11Wm, XwmHandler},
|
||||
};
|
||||
|
||||
use crate::{backend::Backend, state::CalloopData};
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
grab::resize_grab::{ResizeSurfaceGrab, ResizeSurfaceState},
|
||||
state::{CalloopData, WithState},
|
||||
window::{WindowBlocker, WindowElement, BLOCKER_COUNTER},
|
||||
};
|
||||
|
||||
impl<B: Backend> XwmHandler for CalloopData<B> {
|
||||
fn xwm_state(&mut self, xwm: smithay::xwayland::xwm::XwmId) -> &mut smithay::xwayland::X11Wm {
|
||||
todo!()
|
||||
fn xwm_state(&mut self, xwm: XwmId) -> &mut X11Wm {
|
||||
self.state.xwm.as_mut().expect("xwm not in state")
|
||||
}
|
||||
|
||||
fn new_window(
|
||||
&mut self,
|
||||
xwm: smithay::xwayland::xwm::XwmId,
|
||||
window: smithay::xwayland::X11Surface,
|
||||
) {
|
||||
todo!()
|
||||
}
|
||||
fn new_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {}
|
||||
|
||||
fn new_override_redirect_window(
|
||||
&mut self,
|
||||
xwm: smithay::xwayland::xwm::XwmId,
|
||||
window: smithay::xwayland::X11Surface,
|
||||
) {
|
||||
todo!()
|
||||
}
|
||||
fn new_override_redirect_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {}
|
||||
|
||||
fn map_window_request(
|
||||
&mut self,
|
||||
xwm: smithay::xwayland::xwm::XwmId,
|
||||
window: smithay::xwayland::X11Surface,
|
||||
) {
|
||||
todo!()
|
||||
fn map_window_request(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {
|
||||
tracing::debug!("new x11 window from map_window_request");
|
||||
window.set_mapped(true).expect("failed to map x11 window");
|
||||
let window = WindowElement::X11(window);
|
||||
// TODO: place the window in the space
|
||||
self.state.space.map_element(window.clone(), (0, 0), true);
|
||||
let bbox = self
|
||||
.state
|
||||
.space
|
||||
.element_bbox(&window)
|
||||
.expect("failed to get x11 bbox");
|
||||
let WindowElement::X11(surface) = &window else { unreachable!() };
|
||||
surface
|
||||
.configure(Some(bbox))
|
||||
.expect("failed to configure x11 window");
|
||||
// TODO: ssd
|
||||
|
||||
// TODO: this is a duplicate of the code in new_toplevel,
|
||||
// | move into its own function
|
||||
{
|
||||
window.with_state(|state| {
|
||||
state.tags = match (
|
||||
&self.state.focus_state.focused_output,
|
||||
self.state.space.outputs().next(),
|
||||
) {
|
||||
(Some(output), _) | (None, Some(output)) => output.with_state(|state| {
|
||||
let output_tags = state.focused_tags().cloned().collect::<Vec<_>>();
|
||||
if !output_tags.is_empty() {
|
||||
output_tags
|
||||
} else if let Some(first_tag) = state.tags.first() {
|
||||
vec![first_tag.clone()]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}),
|
||||
(None, None) => vec![],
|
||||
};
|
||||
|
||||
tracing::debug!("new window, tags are {:?}", state.tags);
|
||||
});
|
||||
|
||||
let windows_on_output = self
|
||||
.state
|
||||
.windows
|
||||
.iter()
|
||||
.filter(|win| {
|
||||
win.with_state(|state| {
|
||||
self.state
|
||||
.focus_state
|
||||
.focused_output
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.with_state(|op_state| {
|
||||
op_state
|
||||
.tags
|
||||
.iter()
|
||||
.any(|tag| state.tags.iter().any(|tg| tg == tag))
|
||||
})
|
||||
})
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
self.state.windows.push(window.clone());
|
||||
// self.space.map_element(window.clone(), (0, 0), true);
|
||||
if let Some(focused_output) = self.state.focus_state.focused_output.clone() {
|
||||
focused_output.with_state(|state| {
|
||||
let first_tag = state.focused_tags().next();
|
||||
if let Some(first_tag) = first_tag {
|
||||
first_tag.layout().layout(
|
||||
self.state.windows.clone(),
|
||||
state.focused_tags().cloned().collect(),
|
||||
&self.state.space,
|
||||
&focused_output,
|
||||
);
|
||||
}
|
||||
});
|
||||
BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst);
|
||||
tracing::debug!(
|
||||
"blocker {}",
|
||||
BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst)
|
||||
);
|
||||
for win in windows_on_output.iter() {
|
||||
if let Some(surf) = win.wl_surface() {
|
||||
compositor::add_blocker(&surf, WindowBlocker);
|
||||
}
|
||||
}
|
||||
let clone = window.clone();
|
||||
self.state.loop_handle.insert_idle(|data| {
|
||||
crate::state::schedule_on_commit(data, vec![clone], move |data| {
|
||||
BLOCKER_COUNTER.store(0, std::sync::atomic::Ordering::SeqCst);
|
||||
tracing::debug!(
|
||||
"blocker {}",
|
||||
BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst)
|
||||
);
|
||||
for client in windows_on_output
|
||||
.iter()
|
||||
.filter_map(|win| win.wl_surface()?.client())
|
||||
{
|
||||
data.state
|
||||
.client_compositor_state(&client)
|
||||
.blocker_cleared(&mut data.state, &data.display.handle())
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
self.state.loop_handle.insert_idle(move |data| {
|
||||
data.state
|
||||
.seat
|
||||
.get_keyboard()
|
||||
.expect("Seat had no keyboard") // FIXME: actually handle error
|
||||
.set_focus(
|
||||
&mut data.state,
|
||||
window.wl_surface(),
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn mapped_override_redirect_window(
|
||||
&mut self,
|
||||
xwm: smithay::xwayland::xwm::XwmId,
|
||||
xwm: XwmId,
|
||||
window: smithay::xwayland::X11Surface,
|
||||
) {
|
||||
todo!()
|
||||
let loc = window.geometry().loc;
|
||||
let window = WindowElement::X11(window);
|
||||
self.state.space.map_element(window, loc, true);
|
||||
}
|
||||
|
||||
fn unmapped_window(
|
||||
&mut self,
|
||||
xwm: smithay::xwayland::xwm::XwmId,
|
||||
window: smithay::xwayland::X11Surface,
|
||||
) {
|
||||
todo!()
|
||||
fn unmapped_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {
|
||||
let win = self
|
||||
.state
|
||||
.space
|
||||
.elements()
|
||||
.find(|elem| matches!(elem, WindowElement::X11(surface) if surface == &window))
|
||||
.cloned();
|
||||
if let Some(win) = win {
|
||||
self.state.space.unmap_elem(&win);
|
||||
}
|
||||
if !window.is_override_redirect() {
|
||||
window.set_mapped(false).expect("failed to unmap x11 win");
|
||||
}
|
||||
}
|
||||
|
||||
fn destroyed_window(
|
||||
&mut self,
|
||||
xwm: smithay::xwayland::xwm::XwmId,
|
||||
window: smithay::xwayland::X11Surface,
|
||||
) {
|
||||
todo!()
|
||||
}
|
||||
fn destroyed_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {}
|
||||
|
||||
fn configure_request(
|
||||
&mut self,
|
||||
xwm: smithay::xwayland::xwm::XwmId,
|
||||
xwm: XwmId,
|
||||
window: smithay::xwayland::X11Surface,
|
||||
x: Option<i32>,
|
||||
y: Option<i32>,
|
||||
|
@ -71,35 +186,112 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
|
|||
h: Option<u32>,
|
||||
reorder: Option<smithay::xwayland::xwm::Reorder>,
|
||||
) {
|
||||
todo!()
|
||||
let mut geo = window.geometry();
|
||||
if let Some(w) = w {
|
||||
geo.size.w = w as i32;
|
||||
}
|
||||
if let Some(h) = h {
|
||||
geo.size.h = h as i32;
|
||||
}
|
||||
if let Err(err) = window.configure(geo) {
|
||||
tracing::error!("Failed to configure x11 win: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_notify(
|
||||
&mut self,
|
||||
xwm: smithay::xwayland::xwm::XwmId,
|
||||
xwm: XwmId,
|
||||
window: smithay::xwayland::X11Surface,
|
||||
geometry: smithay::utils::Rectangle<i32, smithay::utils::Logical>,
|
||||
above: Option<smithay::reexports::x11rb::protocol::xproto::Window>,
|
||||
) {
|
||||
todo!()
|
||||
let Some(win) = self
|
||||
.state
|
||||
.space
|
||||
.elements()
|
||||
.find(|elem| matches!(elem, WindowElement::X11(surface) if surface == &window))
|
||||
.cloned()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
self.state.space.map_element(win, geometry.loc, false);
|
||||
// TODO: anvil has a TODO here
|
||||
}
|
||||
|
||||
// TODO: maximize request
|
||||
|
||||
// TODO: unmaximize request
|
||||
|
||||
// TODO: fullscreen request
|
||||
|
||||
// TODO: unfullscreen request
|
||||
|
||||
fn resize_request(
|
||||
&mut self,
|
||||
xwm: smithay::xwayland::xwm::XwmId,
|
||||
xwm: XwmId,
|
||||
window: smithay::xwayland::X11Surface,
|
||||
button: u32,
|
||||
resize_edge: smithay::xwayland::xwm::ResizeEdge,
|
||||
) {
|
||||
let seat = &self.state.seat;
|
||||
let pointer = seat.get_pointer().expect("failed to get pointer");
|
||||
let start_data = pointer.grab_start_data().expect("no grab start data");
|
||||
|
||||
let Some(win) = self
|
||||
.state
|
||||
.space
|
||||
.elements()
|
||||
.find(|elem| matches!(elem, WindowElement::X11(surface) if surface == &window))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let initial_window_location = self
|
||||
.state
|
||||
.space
|
||||
.element_location(win)
|
||||
.expect("failed to get x11 loc");
|
||||
let initial_window_size = win.geometry().size;
|
||||
|
||||
if let Some(wl_surface) = win.wl_surface() {
|
||||
wl_surface.with_state(|state| {
|
||||
state.resize_state = ResizeSurfaceState::Resizing {
|
||||
edges: resize_edge.into(),
|
||||
initial_window_rect: Rectangle::from_loc_and_size(
|
||||
initial_window_location,
|
||||
initial_window_size,
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
let grab = ResizeSurfaceGrab::start(
|
||||
start_data,
|
||||
win.clone(),
|
||||
resize_edge.into(),
|
||||
Rectangle::from_loc_and_size(initial_window_location, initial_window_size),
|
||||
0x110, // BUTTON_LEFT
|
||||
);
|
||||
|
||||
if let Some(grab) = grab {
|
||||
pointer.set_grab(
|
||||
&mut self.state,
|
||||
grab,
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
Focus::Clear,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn move_request(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface, button: u32) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn move_request(
|
||||
&mut self,
|
||||
xwm: smithay::xwayland::xwm::XwmId,
|
||||
window: smithay::xwayland::X11Surface,
|
||||
button: u32,
|
||||
) {
|
||||
todo!()
|
||||
}
|
||||
// TODO: allow_selection_access
|
||||
|
||||
// TODO: send_selection
|
||||
|
||||
// TODO: new_selection
|
||||
|
||||
// TODO: cleared_selection
|
||||
}
|
||||
|
|
69
src/input.rs
69
src/input.rs
|
@ -2,20 +2,22 @@
|
|||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::api::msg::{CallbackId, Modifier, ModifierMask, OutgoingMsg};
|
||||
use crate::{
|
||||
api::msg::{CallbackId, Modifier, ModifierMask, OutgoingMsg},
|
||||
window::WindowElement,
|
||||
};
|
||||
use smithay::{
|
||||
backend::input::{
|
||||
AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputBackend, InputEvent,
|
||||
KeyState, KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent, PointerMotionEvent,
|
||||
},
|
||||
desktop::{Window, WindowSurfaceType},
|
||||
desktop::{space::SpaceElement, WindowSurfaceType},
|
||||
input::{
|
||||
keyboard::{keysyms, FilterResult},
|
||||
pointer::{AxisFrame, ButtonEvent, MotionEvent},
|
||||
},
|
||||
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
|
||||
utils::{Logical, Point, SERIAL_COUNTER},
|
||||
wayland::seat::WaylandFocus,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -38,7 +40,7 @@ impl InputState {
|
|||
}
|
||||
|
||||
impl<B: Backend> State<B> {
|
||||
pub fn surface_under<P>(&self, point: P) -> Option<(Window, Point<i32, Logical>)>
|
||||
pub fn surface_under<P>(&self, point: P) -> Option<(WindowElement, Point<i32, Logical>)>
|
||||
where
|
||||
P: Into<Point<f64, Logical>>,
|
||||
{
|
||||
|
@ -74,12 +76,14 @@ impl<B: Backend> State<B> {
|
|||
const BUTTON_RIGHT: u32 = 0x111;
|
||||
if self.move_mode {
|
||||
if event.button_code() == BUTTON_LEFT {
|
||||
crate::xdg::request::move_request_force(
|
||||
self,
|
||||
window.toplevel(),
|
||||
&self.seat.clone(),
|
||||
serial,
|
||||
);
|
||||
if let Some(wl_surf) = window.wl_surface() {
|
||||
crate::xdg::request::move_request_force(
|
||||
self,
|
||||
&wl_surf,
|
||||
&self.seat.clone(),
|
||||
serial,
|
||||
);
|
||||
}
|
||||
return; // TODO: kinda ugly return here
|
||||
} else if event.button_code() == BUTTON_RIGHT {
|
||||
let window_geometry = window.geometry();
|
||||
|
@ -120,29 +124,48 @@ impl<B: Backend> State<B> {
|
|||
_ => ResizeEdge::None,
|
||||
};
|
||||
|
||||
crate::xdg::request::resize_request_force(
|
||||
self,
|
||||
window.toplevel(),
|
||||
&self.seat.clone(),
|
||||
serial,
|
||||
edges,
|
||||
BUTTON_RIGHT,
|
||||
);
|
||||
if let Some(wl_surf) = window.wl_surface() {
|
||||
crate::xdg::request::resize_request_force(
|
||||
self,
|
||||
&wl_surf,
|
||||
&self.seat.clone(),
|
||||
serial,
|
||||
edges,
|
||||
BUTTON_RIGHT,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Move window to top of stack.
|
||||
self.space.raise_element(&window, true);
|
||||
if let WindowElement::X11(surface) = &window {
|
||||
self.xwm
|
||||
.as_mut()
|
||||
.expect("no xwm")
|
||||
.raise_window(surface)
|
||||
.expect("failed to raise x11 win");
|
||||
}
|
||||
|
||||
keyboard.set_focus(self, Some(window.toplevel().wl_surface().clone()), serial);
|
||||
keyboard.set_focus(self, window.wl_surface(), serial);
|
||||
|
||||
self.space.elements().for_each(|window| {
|
||||
window.toplevel().send_configure();
|
||||
if let WindowElement::Wayland(window) = window {
|
||||
window.toplevel().send_configure();
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
self.space.elements().for_each(|window| {
|
||||
window.set_activated(false);
|
||||
window.toplevel().send_configure();
|
||||
self.space.elements().for_each(|window| match window {
|
||||
WindowElement::Wayland(window) => {
|
||||
window.set_activated(false);
|
||||
window.toplevel().send_configure();
|
||||
}
|
||||
WindowElement::X11(surface) => {
|
||||
surface
|
||||
.set_activated(false)
|
||||
.expect("failed to deactivate x11 win");
|
||||
// INFO: do i need to configure this?
|
||||
}
|
||||
});
|
||||
keyboard.set_focus(self, None, serial);
|
||||
}
|
||||
|
|
861
src/layout.rs
861
src/layout.rs
|
@ -2,7 +2,7 @@
|
|||
|
||||
use itertools::{Either, Itertools};
|
||||
use smithay::{
|
||||
desktop::{Space, Window},
|
||||
desktop::Space,
|
||||
output::Output,
|
||||
utils::{Logical, Size},
|
||||
};
|
||||
|
@ -11,7 +11,7 @@ use crate::{
|
|||
backend::Backend,
|
||||
state::{State, WithState},
|
||||
tag::Tag,
|
||||
window::window_state::WindowResizeState,
|
||||
window::WindowElement,
|
||||
};
|
||||
|
||||
// TODO: couple this with the layouts
|
||||
|
@ -29,9 +29,9 @@ pub enum Layout {
|
|||
impl Layout {
|
||||
pub fn layout(
|
||||
&self,
|
||||
windows: Vec<Window>,
|
||||
windows: Vec<WindowElement>,
|
||||
tags: Vec<Tag>,
|
||||
space: &Space<Window>,
|
||||
space: &Space<WindowElement>,
|
||||
output: &Output,
|
||||
) {
|
||||
let windows = filter_windows(&windows, tags);
|
||||
|
@ -44,525 +44,352 @@ impl Layout {
|
|||
let output_loc = output.current_location();
|
||||
|
||||
match self {
|
||||
Layout::MasterStack => {
|
||||
let master = windows.first();
|
||||
let stack = windows.iter().skip(1);
|
||||
|
||||
let Some(master) = master else { return };
|
||||
|
||||
let stack_count = stack.clone().count();
|
||||
|
||||
if stack_count == 0 {
|
||||
// one window
|
||||
master.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(output_geo.size);
|
||||
});
|
||||
|
||||
master.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
master.toplevel().send_configure(),
|
||||
(output_loc.x, output_loc.y).into(),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
let new_master_size: Size<i32, Logical> =
|
||||
(output_geo.size.w / 2, output_geo.size.h).into();
|
||||
master.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(new_master_size);
|
||||
});
|
||||
master.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
master.toplevel().send_configure(),
|
||||
(output_loc.x, output_loc.y).into(),
|
||||
);
|
||||
});
|
||||
|
||||
let stack_count = stack_count;
|
||||
|
||||
let height = output_geo.size.h as f32 / stack_count as f32;
|
||||
let mut y_s = vec![];
|
||||
for i in 0..stack_count {
|
||||
y_s.push((i as f32 * height).round() as i32);
|
||||
}
|
||||
let heights = y_s
|
||||
.windows(2)
|
||||
.map(|pair| pair[1] - pair[0])
|
||||
.chain(vec![output_geo.size.h - y_s.last().expect("vec was empty")])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (i, win) in stack.enumerate() {
|
||||
win.toplevel().with_pending_state(|state| {
|
||||
// INFO: Some windows crash the compositor if they become too short in height,
|
||||
// | so they're limited to a minimum of 40 pixels as a workaround.
|
||||
state.size =
|
||||
Some((output_geo.size.w / 2, i32::max(heights[i], 40)).into());
|
||||
});
|
||||
|
||||
win.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win.toplevel().send_configure(),
|
||||
(output_geo.size.w / 2 + output_loc.x, y_s[i] + output_loc.y)
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Layout::Dwindle => {
|
||||
let mut iter = windows.windows(2).peekable();
|
||||
|
||||
if iter.peek().is_none() {
|
||||
if let Some(window) = windows.first() {
|
||||
window.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(output_geo.size);
|
||||
});
|
||||
|
||||
window.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
window.toplevel().send_configure(),
|
||||
(output_loc.x, output_loc.y).into(),
|
||||
);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (i, wins) in iter.enumerate() {
|
||||
let win1 = &wins[0];
|
||||
let win2 = &wins[1];
|
||||
|
||||
enum Slice {
|
||||
Right,
|
||||
Below,
|
||||
}
|
||||
|
||||
let slice = if i % 2 == 0 {
|
||||
Slice::Right
|
||||
} else {
|
||||
Slice::Below
|
||||
};
|
||||
|
||||
if i == 0 {
|
||||
win1.toplevel()
|
||||
.with_pending_state(|state| state.size = Some(output_geo.size));
|
||||
win1.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win1.toplevel().send_configure(),
|
||||
output_loc,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
let win1_size = win1.toplevel().with_pending_state(|state| {
|
||||
state.size.expect("size should have been set")
|
||||
});
|
||||
let win1_loc = win1.with_state(|state| {
|
||||
let WindowResizeState::Requested(_, loc) =
|
||||
state.resize_state else { unreachable!() };
|
||||
loc
|
||||
});
|
||||
|
||||
match slice {
|
||||
Slice::Right => {
|
||||
let width_partition = win1_size.w / 2;
|
||||
win1.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(
|
||||
(win1_size.w - width_partition, i32::max(win1_size.h, 40))
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
win1.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win1.toplevel().send_configure(),
|
||||
win1_loc,
|
||||
);
|
||||
});
|
||||
win2.toplevel().with_pending_state(|state| {
|
||||
state.size =
|
||||
Some((width_partition, i32::max(win1_size.h, 40)).into());
|
||||
});
|
||||
win2.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win2.toplevel().send_configure(),
|
||||
(win1_loc.x + (win1_size.w - width_partition), win1_loc.y)
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
}
|
||||
Slice::Below => {
|
||||
let height_partition = win1_size.h / 2;
|
||||
win1.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(
|
||||
(win1_size.w, i32::max(win1_size.h - height_partition, 40))
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
win1.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win1.toplevel().send_configure(),
|
||||
win1_loc,
|
||||
);
|
||||
});
|
||||
win2.toplevel().with_pending_state(|state| {
|
||||
state.size =
|
||||
Some((win1_size.w, i32::max(height_partition, 40)).into());
|
||||
});
|
||||
win2.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win2.toplevel().send_configure(),
|
||||
(win1_loc.x, win1_loc.y + (win1_size.h - height_partition))
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Layout::Spiral => {
|
||||
let mut iter = windows.windows(2).peekable();
|
||||
|
||||
if iter.peek().is_none() {
|
||||
if let Some(window) = windows.first() {
|
||||
window.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(output_geo.size);
|
||||
});
|
||||
|
||||
window.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
window.toplevel().send_configure(),
|
||||
(output_loc.x, output_loc.y).into(),
|
||||
);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (i, wins) in iter.enumerate() {
|
||||
let win1 = &wins[0];
|
||||
let win2 = &wins[1];
|
||||
|
||||
enum Slice {
|
||||
Above,
|
||||
Below,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
let slice = match i % 4 {
|
||||
0 => Slice::Right,
|
||||
1 => Slice::Below,
|
||||
2 => Slice::Left,
|
||||
3 => Slice::Above,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if i == 0 {
|
||||
win1.toplevel()
|
||||
.with_pending_state(|state| state.size = Some(output_geo.size));
|
||||
win1.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win1.toplevel().send_configure(),
|
||||
output_loc,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
let win1_size = win1.toplevel().with_pending_state(|state| {
|
||||
state.size.expect("size should have been set")
|
||||
});
|
||||
let win1_loc = win1.with_state(|state| {
|
||||
let WindowResizeState::Requested(_, loc) =
|
||||
state.resize_state else { unreachable!() };
|
||||
loc
|
||||
});
|
||||
|
||||
match slice {
|
||||
Slice::Above => {
|
||||
let height_partition = win1_size.h / 2;
|
||||
win1.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(
|
||||
(win1_size.w, i32::max(win1_size.h - height_partition, 40))
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
win1.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win1.toplevel().send_configure(),
|
||||
(win1_loc.x, win1_loc.y + height_partition).into(),
|
||||
);
|
||||
});
|
||||
win2.toplevel().with_pending_state(|state| {
|
||||
state.size =
|
||||
Some((win1_size.w, i32::max(height_partition, 40)).into());
|
||||
});
|
||||
win2.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win2.toplevel().send_configure(),
|
||||
win1_loc,
|
||||
);
|
||||
});
|
||||
}
|
||||
Slice::Below => {
|
||||
let height_partition = win1_size.h / 2;
|
||||
win1.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(
|
||||
(win1_size.w, win1_size.h - i32::max(height_partition, 40))
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
win1.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win1.toplevel().send_configure(),
|
||||
win1_loc,
|
||||
);
|
||||
});
|
||||
win2.toplevel().with_pending_state(|state| {
|
||||
state.size =
|
||||
Some((win1_size.w, i32::max(height_partition, 40)).into());
|
||||
});
|
||||
win2.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win2.toplevel().send_configure(),
|
||||
(win1_loc.x, win1_loc.y + (win1_size.h - height_partition))
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
}
|
||||
Slice::Left => {
|
||||
let width_partition = win1_size.w / 2;
|
||||
win1.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(
|
||||
(win1_size.w - width_partition, i32::max(win1_size.h, 40))
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
win1.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win1.toplevel().send_configure(),
|
||||
(win1_loc.x + width_partition, win1_loc.y).into(),
|
||||
);
|
||||
});
|
||||
win2.toplevel().with_pending_state(|state| {
|
||||
state.size =
|
||||
Some((width_partition, i32::max(win1_size.h, 40)).into());
|
||||
});
|
||||
win2.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win2.toplevel().send_configure(),
|
||||
win1_loc,
|
||||
);
|
||||
});
|
||||
}
|
||||
Slice::Right => {
|
||||
let width_partition = win1_size.w / 2;
|
||||
win1.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(
|
||||
(win1_size.w - width_partition, i32::max(win1_size.h, 40))
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
win1.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win1.toplevel().send_configure(),
|
||||
win1_loc,
|
||||
);
|
||||
});
|
||||
win2.toplevel().with_pending_state(|state| {
|
||||
state.size =
|
||||
Some((width_partition, i32::max(win1_size.h, 40)).into());
|
||||
});
|
||||
win2.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win2.toplevel().send_configure(),
|
||||
(win1_loc.x + (win1_size.w - width_partition), win1_loc.y)
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Layout::MasterStack => master_stack(windows, space, output),
|
||||
Layout::Dwindle => dwindle(windows, space, output),
|
||||
Layout::Spiral => spiral(windows, space, output),
|
||||
layout @ (Layout::CornerTopLeft
|
||||
| Layout::CornerTopRight
|
||||
| Layout::CornerBottomLeft
|
||||
| Layout::CornerBottomRight) => match windows.len() {
|
||||
0 => (),
|
||||
1 => {
|
||||
windows[0].toplevel().with_pending_state(|state| {
|
||||
state.size = Some(output_geo.size);
|
||||
});
|
||||
|
||||
windows[0].with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
windows[0].toplevel().send_configure(),
|
||||
(output_loc.x, output_loc.y).into(),
|
||||
);
|
||||
});
|
||||
}
|
||||
2 => {
|
||||
windows[0].toplevel().with_pending_state(|state| {
|
||||
state.size = Some((output_geo.size.w / 2, output_geo.size.h).into());
|
||||
});
|
||||
windows[0].with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
windows[0].toplevel().send_configure(),
|
||||
(output_loc.x, output_loc.y).into(),
|
||||
);
|
||||
});
|
||||
windows[1].toplevel().with_pending_state(|state| {
|
||||
state.size = Some((output_geo.size.w / 2, output_geo.size.h).into());
|
||||
});
|
||||
windows[1].with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
windows[1].toplevel().send_configure(),
|
||||
(output_loc.x + output_geo.size.w / 2, output_loc.y).into(),
|
||||
);
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
let mut windows = windows.into_iter();
|
||||
let Some(corner) = windows.next() else { unreachable!() };
|
||||
let (horiz_stack, vert_stack): (Vec<Window>, Vec<Window>) =
|
||||
windows.enumerate().partition_map(|(i, win)| {
|
||||
if i % 2 == 0 {
|
||||
Either::Left(win)
|
||||
} else {
|
||||
Either::Right(win)
|
||||
}
|
||||
});
|
||||
|
||||
let div_factor = 2;
|
||||
|
||||
corner.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(
|
||||
(
|
||||
output_geo.size.w / div_factor,
|
||||
output_geo.size.h / div_factor,
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
corner.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
corner.toplevel().send_configure(),
|
||||
match layout {
|
||||
Layout::CornerTopLeft => (output_loc.x, output_loc.y),
|
||||
Layout::CornerTopRight => (
|
||||
output_loc.x + output_geo.size.w
|
||||
- output_geo.size.w / div_factor,
|
||||
output_loc.y,
|
||||
),
|
||||
Layout::CornerBottomLeft => (
|
||||
output_loc.x,
|
||||
output_loc.y + output_geo.size.h
|
||||
- output_geo.size.h / div_factor,
|
||||
),
|
||||
Layout::CornerBottomRight => (
|
||||
output_loc.x + output_geo.size.w
|
||||
- output_geo.size.w / div_factor,
|
||||
output_loc.y + output_geo.size.h
|
||||
- output_geo.size.h / div_factor,
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
|
||||
let vert_stack_count = vert_stack.len();
|
||||
|
||||
let height = output_geo.size.h as f32 / vert_stack_count as f32;
|
||||
let mut y_s = vec![];
|
||||
for i in 0..vert_stack_count {
|
||||
y_s.push((i as f32 * height).round() as i32);
|
||||
}
|
||||
let heights = y_s
|
||||
.windows(2)
|
||||
.map(|pair| pair[1] - pair[0])
|
||||
.chain(vec![output_geo.size.h - y_s.last().expect("vec was empty")])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (i, win) in vert_stack.iter().enumerate() {
|
||||
win.toplevel().with_pending_state(|state| {
|
||||
// INFO: Some windows crash the compositor if they become too short in height,
|
||||
// | so they're limited to a minimum of 40 pixels as a workaround.
|
||||
state.size =
|
||||
Some((output_geo.size.w / 2, i32::max(heights[i], 40)).into());
|
||||
});
|
||||
|
||||
win.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win.toplevel().send_configure(),
|
||||
(
|
||||
match layout {
|
||||
Layout::CornerTopLeft | Layout::CornerBottomLeft => {
|
||||
output_geo.size.w / 2 + output_loc.x
|
||||
}
|
||||
Layout::CornerTopRight | Layout::CornerBottomRight => {
|
||||
output_loc.x
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
y_s[i] + output_loc.y,
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
let horiz_stack_count = horiz_stack.len();
|
||||
|
||||
let width = output_geo.size.w as f32 / 2.0 / horiz_stack_count as f32;
|
||||
let mut x_s = vec![];
|
||||
for i in 0..horiz_stack_count {
|
||||
x_s.push((i as f32 * width).round() as i32);
|
||||
}
|
||||
let widths = x_s
|
||||
.windows(2)
|
||||
.map(|pair| pair[1] - pair[0])
|
||||
.chain(vec![
|
||||
output_geo.size.w / 2 - x_s.last().expect("vec was empty"),
|
||||
])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (i, win) in horiz_stack.iter().enumerate() {
|
||||
win.toplevel().with_pending_state(|state| {
|
||||
// INFO: Some windows crash the compositor if they become too short in height,
|
||||
// | so they're limited to a minimum of 40 pixels as a workaround.
|
||||
state.size =
|
||||
Some((i32::max(widths[i], 1), output_geo.size.h / 2).into());
|
||||
});
|
||||
|
||||
win.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Requested(
|
||||
win.toplevel().send_configure(),
|
||||
match layout {
|
||||
Layout::CornerTopLeft => (
|
||||
x_s[i] + output_loc.x,
|
||||
output_loc.y + output_geo.size.h / 2,
|
||||
),
|
||||
Layout::CornerTopRight => (
|
||||
x_s[i] + output_loc.x + output_geo.size.w / 2,
|
||||
output_loc.y + output_geo.size.h / 2,
|
||||
),
|
||||
Layout::CornerBottomLeft => {
|
||||
(x_s[i] + output_loc.x, output_loc.y)
|
||||
}
|
||||
Layout::CornerBottomRight => (
|
||||
x_s[i] + output_loc.x + output_geo.size.w / 2,
|
||||
output_loc.y,
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
| Layout::CornerBottomRight) => corner(layout, windows, space, output),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_windows(windows: &[Window], tags: Vec<Tag>) -> Vec<Window> {
|
||||
fn master_stack(windows: Vec<WindowElement>, space: &Space<WindowElement>, output: &Output) {
|
||||
let Some(output_geo) = space.output_geometry(output) else {
|
||||
tracing::error!("could not get output geometry");
|
||||
return;
|
||||
};
|
||||
|
||||
let output_loc = output.current_location();
|
||||
|
||||
let master = windows.first();
|
||||
let stack = windows.iter().skip(1);
|
||||
|
||||
let Some(master) = master else { return };
|
||||
|
||||
let stack_count = stack.clone().count();
|
||||
|
||||
if stack_count == 0 {
|
||||
// one window
|
||||
master.request_size_change(output_loc, output_geo.size);
|
||||
} else {
|
||||
let loc = (output_loc.x, output_loc.y).into();
|
||||
let new_master_size: Size<i32, Logical> = (output_geo.size.w / 2, output_geo.size.h).into();
|
||||
master.request_size_change(loc, new_master_size);
|
||||
|
||||
let stack_count = stack_count;
|
||||
|
||||
let height = output_geo.size.h as f32 / stack_count as f32;
|
||||
let mut y_s = vec![];
|
||||
for i in 0..stack_count {
|
||||
y_s.push((i as f32 * height).round() as i32);
|
||||
}
|
||||
let heights = y_s
|
||||
.windows(2)
|
||||
.map(|pair| pair[1] - pair[0])
|
||||
.chain(vec![output_geo.size.h - y_s.last().expect("vec was empty")])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (i, win) in stack.enumerate() {
|
||||
win.request_size_change(
|
||||
(output_geo.size.w / 2 + output_loc.x, y_s[i] + output_loc.y).into(),
|
||||
(output_geo.size.w / 2, i32::max(heights[i], 40)).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dwindle(windows: Vec<WindowElement>, space: &Space<WindowElement>, output: &Output) {
|
||||
let Some(output_geo) = space.output_geometry(output) else {
|
||||
tracing::error!("could not get output geometry");
|
||||
return;
|
||||
};
|
||||
|
||||
let output_loc = output.current_location();
|
||||
|
||||
let mut iter = windows.windows(2).peekable();
|
||||
|
||||
if iter.peek().is_none() {
|
||||
if let Some(window) = windows.first() {
|
||||
window.request_size_change(output_loc, output_geo.size);
|
||||
}
|
||||
} else {
|
||||
let mut win1_size = output_geo.size;
|
||||
let mut win1_loc = output_loc;
|
||||
for (i, wins) in iter.enumerate() {
|
||||
let win1 = &wins[0];
|
||||
let win2 = &wins[1];
|
||||
|
||||
enum Slice {
|
||||
Right,
|
||||
Below,
|
||||
}
|
||||
|
||||
let slice = if i % 2 == 0 {
|
||||
Slice::Right
|
||||
} else {
|
||||
Slice::Below
|
||||
};
|
||||
|
||||
match slice {
|
||||
Slice::Right => {
|
||||
let width_partition = win1_size.w / 2;
|
||||
|
||||
win1.request_size_change(
|
||||
win1_loc,
|
||||
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(),
|
||||
);
|
||||
|
||||
win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into();
|
||||
win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
|
||||
|
||||
win2.request_size_change(win1_loc, win1_size);
|
||||
}
|
||||
Slice::Below => {
|
||||
let height_partition = win1_size.h / 2;
|
||||
|
||||
win1.request_size_change(
|
||||
win1_loc,
|
||||
(win1_size.w, i32::max(win1_size.h - height_partition, 40)).into(),
|
||||
);
|
||||
|
||||
win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into();
|
||||
win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
|
||||
|
||||
win2.request_size_change(win1_loc, win1_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn spiral(windows: Vec<WindowElement>, space: &Space<WindowElement>, output: &Output) {
|
||||
let Some(output_geo) = space.output_geometry(output) else {
|
||||
tracing::error!("could not get output geometry");
|
||||
return;
|
||||
};
|
||||
|
||||
let output_loc = output.current_location();
|
||||
|
||||
let mut iter = windows.windows(2).peekable();
|
||||
|
||||
if iter.peek().is_none() {
|
||||
if let Some(window) = windows.first() {
|
||||
window.request_size_change(output_loc, output_geo.size);
|
||||
}
|
||||
} else {
|
||||
let mut win1_loc = output_loc;
|
||||
let mut win1_size = output_geo.size;
|
||||
|
||||
for (i, wins) in iter.enumerate() {
|
||||
let win1 = &wins[0];
|
||||
let win2 = &wins[1];
|
||||
|
||||
enum Slice {
|
||||
Above,
|
||||
Below,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
let slice = match i % 4 {
|
||||
0 => Slice::Right,
|
||||
1 => Slice::Below,
|
||||
2 => Slice::Left,
|
||||
3 => Slice::Above,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
match slice {
|
||||
Slice::Above => {
|
||||
let height_partition = win1_size.h / 2;
|
||||
|
||||
win1.request_size_change(
|
||||
(win1_loc.x, win1_loc.y + height_partition).into(),
|
||||
(win1_size.w, i32::max(win1_size.h - height_partition, 40)).into(),
|
||||
);
|
||||
|
||||
win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
|
||||
win2.request_size_change(win1_loc, win1_size);
|
||||
}
|
||||
Slice::Below => {
|
||||
let height_partition = win1_size.h / 2;
|
||||
|
||||
win1.request_size_change(
|
||||
win1_loc,
|
||||
(win1_size.w, win1_size.h - i32::max(height_partition, 40)).into(),
|
||||
);
|
||||
|
||||
win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into();
|
||||
win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
|
||||
win2.request_size_change(win1_loc, win1_size);
|
||||
}
|
||||
Slice::Left => {
|
||||
let width_partition = win1_size.w / 2;
|
||||
|
||||
win1.request_size_change(
|
||||
(win1_loc.x + width_partition, win1_loc.y).into(),
|
||||
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(),
|
||||
);
|
||||
|
||||
win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
|
||||
win2.request_size_change(win1_loc, win1_size);
|
||||
}
|
||||
Slice::Right => {
|
||||
let width_partition = win1_size.w / 2;
|
||||
|
||||
win1.request_size_change(
|
||||
win1_loc,
|
||||
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(),
|
||||
);
|
||||
|
||||
win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into();
|
||||
win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
|
||||
win2.request_size_change(win1_loc, win1_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn corner(
|
||||
layout: &Layout,
|
||||
windows: Vec<WindowElement>,
|
||||
space: &Space<WindowElement>,
|
||||
output: &Output,
|
||||
) {
|
||||
let Some(output_geo) = space.output_geometry(output) else {
|
||||
tracing::error!("could not get output geometry");
|
||||
return;
|
||||
};
|
||||
|
||||
let output_loc = output.current_location();
|
||||
match windows.len() {
|
||||
0 => (),
|
||||
1 => {
|
||||
windows[0].request_size_change(output_loc, output_geo.size);
|
||||
}
|
||||
2 => {
|
||||
windows[0].request_size_change(
|
||||
output_loc,
|
||||
(output_geo.size.w / 2, output_geo.size.h).into(),
|
||||
);
|
||||
|
||||
windows[1].request_size_change(
|
||||
(output_loc.x + output_geo.size.w / 2, output_loc.y).into(),
|
||||
(output_geo.size.w / 2, output_geo.size.h).into(),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
let mut windows = windows.into_iter();
|
||||
let Some(corner) = windows.next() else { unreachable!() };
|
||||
let (horiz_stack, vert_stack): (Vec<WindowElement>, Vec<WindowElement>) =
|
||||
windows.enumerate().partition_map(|(i, win)| {
|
||||
if i % 2 == 0 {
|
||||
Either::Left(win)
|
||||
} else {
|
||||
Either::Right(win)
|
||||
}
|
||||
});
|
||||
|
||||
let div_factor = 2;
|
||||
|
||||
corner.request_size_change(
|
||||
match layout {
|
||||
Layout::CornerTopLeft => (output_loc.x, output_loc.y),
|
||||
Layout::CornerTopRight => (
|
||||
output_loc.x + output_geo.size.w - output_geo.size.w / div_factor,
|
||||
output_loc.y,
|
||||
),
|
||||
Layout::CornerBottomLeft => (
|
||||
output_loc.x,
|
||||
output_loc.y + output_geo.size.h - output_geo.size.h / div_factor,
|
||||
),
|
||||
Layout::CornerBottomRight => (
|
||||
output_loc.x + output_geo.size.w - output_geo.size.w / div_factor,
|
||||
output_loc.y + output_geo.size.h - output_geo.size.h / div_factor,
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.into(),
|
||||
(
|
||||
output_geo.size.w / div_factor,
|
||||
output_geo.size.h / div_factor,
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
||||
let vert_stack_count = vert_stack.len();
|
||||
|
||||
let height = output_geo.size.h as f32 / vert_stack_count as f32;
|
||||
let mut y_s = vec![];
|
||||
for i in 0..vert_stack_count {
|
||||
y_s.push((i as f32 * height).round() as i32);
|
||||
}
|
||||
let heights = y_s
|
||||
.windows(2)
|
||||
.map(|pair| pair[1] - pair[0])
|
||||
.chain(vec![output_geo.size.h - y_s.last().expect("vec was empty")])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (i, win) in vert_stack.iter().enumerate() {
|
||||
win.request_size_change(
|
||||
(
|
||||
match layout {
|
||||
Layout::CornerTopLeft | Layout::CornerBottomLeft => {
|
||||
output_geo.size.w / 2 + output_loc.x
|
||||
}
|
||||
Layout::CornerTopRight | Layout::CornerBottomRight => output_loc.x,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
y_s[i] + output_loc.y,
|
||||
)
|
||||
.into(),
|
||||
(output_geo.size.w / 2, i32::max(heights[i], 40)).into(),
|
||||
);
|
||||
}
|
||||
|
||||
let horiz_stack_count = horiz_stack.len();
|
||||
|
||||
let width = output_geo.size.w as f32 / 2.0 / horiz_stack_count as f32;
|
||||
let mut x_s = vec![];
|
||||
for i in 0..horiz_stack_count {
|
||||
x_s.push((i as f32 * width).round() as i32);
|
||||
}
|
||||
let widths = x_s
|
||||
.windows(2)
|
||||
.map(|pair| pair[1] - pair[0])
|
||||
.chain(vec![
|
||||
output_geo.size.w / 2 - x_s.last().expect("vec was empty"),
|
||||
])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (i, win) in horiz_stack.iter().enumerate() {
|
||||
win.request_size_change(
|
||||
match layout {
|
||||
Layout::CornerTopLeft => {
|
||||
(x_s[i] + output_loc.x, output_loc.y + output_geo.size.h / 2)
|
||||
}
|
||||
Layout::CornerTopRight => (
|
||||
x_s[i] + output_loc.x + output_geo.size.w / 2,
|
||||
output_loc.y + output_geo.size.h / 2,
|
||||
),
|
||||
Layout::CornerBottomLeft => (x_s[i] + output_loc.x, output_loc.y),
|
||||
Layout::CornerBottomRight => {
|
||||
(x_s[i] + output_loc.x + output_geo.size.w / 2, output_loc.y)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.into(),
|
||||
(i32::max(widths[i], 1), output_geo.size.h / 2).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_windows(windows: &[WindowElement], tags: Vec<Tag>) -> Vec<WindowElement> {
|
||||
windows
|
||||
.iter()
|
||||
.filter(|window| {
|
||||
|
@ -582,7 +409,7 @@ fn filter_windows(windows: &[Window], tags: Vec<Tag>) -> Vec<Window> {
|
|||
}
|
||||
|
||||
impl<B: Backend> State<B> {
|
||||
pub fn swap_window_positions(&mut self, win1: &Window, win2: &Window) {
|
||||
pub fn swap_window_positions(&mut self, win1: &WindowElement, win2: &WindowElement) {
|
||||
let mut elems = self
|
||||
.windows
|
||||
.iter_mut()
|
||||
|
|
|
@ -2,13 +2,16 @@
|
|||
|
||||
use smithay::{
|
||||
backend::renderer::{
|
||||
element::{surface::WaylandSurfaceRenderElement, Wrap},
|
||||
ImportAll, ImportMem,
|
||||
element::{surface::WaylandSurfaceRenderElement, AsRenderElements, Wrap},
|
||||
ImportAll, ImportMem, Renderer, Texture,
|
||||
},
|
||||
desktop::space::SpaceRenderElements,
|
||||
desktop::space::{SpaceElement, SpaceRenderElements},
|
||||
render_elements,
|
||||
utils::{Physical, Point, Scale},
|
||||
};
|
||||
|
||||
use crate::window::WindowElement;
|
||||
|
||||
use self::pointer::PointerRenderElement;
|
||||
|
||||
pub mod pointer;
|
||||
|
@ -26,3 +29,34 @@ render_elements! {
|
|||
Custom=CustomRenderElements<R>,
|
||||
// TODO: preview
|
||||
}
|
||||
|
||||
impl<R> AsRenderElements<R> for WindowElement
|
||||
where
|
||||
R: Renderer + ImportAll + ImportMem,
|
||||
<R as Renderer>::TextureId: Texture + 'static,
|
||||
{
|
||||
type RenderElement = WaylandSurfaceRenderElement<R>;
|
||||
|
||||
fn render_elements<C: From<Self::RenderElement>>(
|
||||
&self,
|
||||
renderer: &mut R,
|
||||
location: Point<i32, Physical>,
|
||||
scale: Scale<f64>,
|
||||
alpha: f32,
|
||||
) -> Vec<C> {
|
||||
let window_bbox = self.bbox();
|
||||
match self {
|
||||
WindowElement::Wayland(window) => {
|
||||
AsRenderElements::<R>::render_elements::<WaylandSurfaceRenderElement<R>>(
|
||||
window, renderer, location, scale, alpha,
|
||||
)
|
||||
}
|
||||
WindowElement::X11(surface) => AsRenderElements::<R>::render_elements::<
|
||||
WaylandSurfaceRenderElement<R>,
|
||||
>(surface, renderer, location, scale, alpha),
|
||||
}
|
||||
.into_iter()
|
||||
.map(C::from)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
133
src/state.rs
133
src/state.rs
|
@ -20,18 +20,19 @@ use crate::{
|
|||
focus::FocusState,
|
||||
grab::resize_grab::ResizeSurfaceState,
|
||||
tag::Tag,
|
||||
window::window_state::WindowResizeState,
|
||||
window::{window_state::WindowResizeState, WindowElement},
|
||||
};
|
||||
use calloop::futures::Scheduler;
|
||||
use futures_lite::AsyncBufReadExt;
|
||||
use smithay::{
|
||||
backend::renderer::element::RenderElementStates,
|
||||
desktop::{
|
||||
space::SpaceElement,
|
||||
utils::{
|
||||
surface_presentation_feedback_flags_from_states, surface_primary_scanout_output,
|
||||
OutputPresentationFeedback,
|
||||
},
|
||||
PopupManager, Space, Window,
|
||||
PopupManager, Space,
|
||||
},
|
||||
input::{keyboard::XkbConfig, pointer::CursorImageStatus, Seat, SeatState},
|
||||
output::Output,
|
||||
|
@ -71,7 +72,7 @@ pub struct State<B: Backend> {
|
|||
pub loop_handle: LoopHandle<'static, CalloopData<B>>,
|
||||
pub clock: Clock<Monotonic>,
|
||||
|
||||
pub space: Space<Window>,
|
||||
pub space: Space<WindowElement>,
|
||||
pub move_mode: bool,
|
||||
pub socket_name: String,
|
||||
|
||||
|
@ -93,7 +94,7 @@ pub struct State<B: Backend> {
|
|||
|
||||
pub cursor_status: CursorImageStatus,
|
||||
pub pointer_location: Point<f64, Logical>,
|
||||
pub windows: Vec<Window>,
|
||||
pub windows: Vec<WindowElement>,
|
||||
|
||||
pub async_scheduler: Scheduler<()>,
|
||||
|
||||
|
@ -123,7 +124,12 @@ impl<B: Backend> State<B> {
|
|||
Msg::SetMousebind { button: _ } => todo!(),
|
||||
Msg::CloseWindow { window_id } => {
|
||||
if let Some(window) = window_id.window(self) {
|
||||
window.toplevel().send_close();
|
||||
match window {
|
||||
WindowElement::Wayland(window) => window.toplevel().send_close(),
|
||||
WindowElement::X11(surface) => {
|
||||
surface.close().expect("failed to close x11 win");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Msg::ToggleFloating { window_id } => {
|
||||
|
@ -147,19 +153,13 @@ impl<B: Backend> State<B> {
|
|||
let Some(window) = window_id.window(self) else { return };
|
||||
|
||||
// TODO: tiled vs floating
|
||||
// FIXME: this will map unmapped windows at 0,0
|
||||
let window_loc = self
|
||||
.space
|
||||
.element_location(&window)
|
||||
.unwrap_or((0, 0).into());
|
||||
let window_size = window.geometry().size;
|
||||
window.toplevel().with_pending_state(|state| {
|
||||
// INFO: calling window.geometry() in with_pending_state
|
||||
// | will hang the compositor
|
||||
state.size = Some(
|
||||
(
|
||||
width.unwrap_or(window_size.w),
|
||||
height.unwrap_or(window_size.h),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
window.toplevel().send_pending_configure();
|
||||
window.request_size_change(window_loc, window_size);
|
||||
}
|
||||
Msg::MoveWindowToTag { window_id, tag_id } => {
|
||||
let Some(window) = window_id.window(self) else { return };
|
||||
|
@ -305,17 +305,20 @@ impl<B: Backend> State<B> {
|
|||
.as_ref()
|
||||
.and_then(|win| self.space.element_location(win))
|
||||
.map(|loc| (loc.x, loc.y));
|
||||
let (class, title) = window.as_ref().map_or((None, None), |win| {
|
||||
compositor::with_states(win.toplevel().wl_surface(), |states| {
|
||||
let lock = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
|
||||
.lock()
|
||||
.expect("failed to acquire lock");
|
||||
(lock.app_id.clone(), lock.title.clone())
|
||||
})
|
||||
});
|
||||
let (class, title) = window.as_ref().and_then(|win| win.wl_surface()).map_or(
|
||||
(None, None),
|
||||
|wl_surf| {
|
||||
compositor::with_states(&wl_surf, |states| {
|
||||
let lock = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
|
||||
.lock()
|
||||
.expect("failed to acquire lock");
|
||||
(lock.app_id.clone(), lock.title.clone())
|
||||
})
|
||||
},
|
||||
);
|
||||
let floating = window
|
||||
.as_ref()
|
||||
.map(|win| win.with_state(|state| state.floating.is_floating()));
|
||||
|
@ -459,7 +462,13 @@ impl<B: Backend> State<B> {
|
|||
|
||||
let program = OsString::from(program);
|
||||
let Ok(mut child) = async_process::Command::new(&program)
|
||||
.env("WAYLAND_DISPLAY", self.socket_name.clone())
|
||||
.envs(
|
||||
[("WAYLAND_DISPLAY", self.socket_name.clone())]
|
||||
.into_iter()
|
||||
.chain(
|
||||
self.xdisplay.map(|xdisp| ("DISPLAY", format!(":{xdisp}")))
|
||||
)
|
||||
)
|
||||
.stdin(if callback_id.is_some() {
|
||||
Stdio::piped()
|
||||
} else {
|
||||
|
@ -670,7 +679,7 @@ impl<B: Backend> State<B> {
|
|||
/// idle.
|
||||
pub fn schedule_on_commit<F, B: Backend>(
|
||||
data: &mut CalloopData<B>,
|
||||
windows: Vec<Window>,
|
||||
windows: Vec<WindowElement>,
|
||||
on_commit: F,
|
||||
) where
|
||||
F: FnOnce(&mut CalloopData<B>) + 'static,
|
||||
|
@ -804,44 +813,48 @@ impl<B: Backend> State<B> {
|
|||
.expect("failed to insert rx_channel into loop");
|
||||
});
|
||||
|
||||
tracing::debug!("before xwayland");
|
||||
let xwayland = {
|
||||
let (xwayland, channel) = XWayland::new(&display_handle);
|
||||
let clone = display_handle.clone();
|
||||
let res =
|
||||
loop_handle.insert_source(channel, move |event, _metadata, data| match event {
|
||||
XWaylandEvent::Ready {
|
||||
tracing::debug!("inserting into loop");
|
||||
let res = loop_handle.insert_source(channel, move |event, _, data| match event {
|
||||
XWaylandEvent::Ready {
|
||||
connection,
|
||||
client,
|
||||
client_fd: _,
|
||||
display,
|
||||
} => {
|
||||
tracing::debug!("XWaylandEvent ready");
|
||||
let mut wm = X11Wm::start_wm(
|
||||
data.state.loop_handle.clone(),
|
||||
clone.clone(),
|
||||
connection,
|
||||
client,
|
||||
client_fd: _,
|
||||
display,
|
||||
} => {
|
||||
let mut wm = X11Wm::start_wm(
|
||||
data.state.loop_handle.clone(),
|
||||
clone.clone(),
|
||||
connection,
|
||||
client,
|
||||
)
|
||||
.expect("failed to attach x11wm");
|
||||
let cursor = Cursor::load();
|
||||
let image = cursor.get_image(1, Duration::ZERO);
|
||||
wm.set_cursor(
|
||||
&image.pixels_rgba,
|
||||
Size::from((image.width as u16, image.height as u16)),
|
||||
Point::from((image.xhot as u16, image.yhot as u16)),
|
||||
)
|
||||
.expect("failed to set xwayland default cursor");
|
||||
data.state.xwm = Some(wm);
|
||||
data.state.xdisplay = Some(display);
|
||||
}
|
||||
XWaylandEvent::Exited => {
|
||||
data.state.xwm.take();
|
||||
}
|
||||
});
|
||||
)
|
||||
.expect("failed to attach x11wm");
|
||||
let cursor = Cursor::load();
|
||||
let image = cursor.get_image(1, Duration::ZERO);
|
||||
wm.set_cursor(
|
||||
&image.pixels_rgba,
|
||||
Size::from((image.width as u16, image.height as u16)),
|
||||
Point::from((image.xhot as u16, image.yhot as u16)),
|
||||
)
|
||||
.expect("failed to set xwayland default cursor");
|
||||
tracing::debug!("setting xwm and xdisplay");
|
||||
data.state.xwm = Some(wm);
|
||||
data.state.xdisplay = Some(display);
|
||||
}
|
||||
XWaylandEvent::Exited => {
|
||||
data.state.xwm.take();
|
||||
}
|
||||
});
|
||||
if let Err(err) = res {
|
||||
tracing::error!("Failed to insert XWayland source into loop: {err}");
|
||||
}
|
||||
xwayland
|
||||
};
|
||||
tracing::debug!("after xwayland");
|
||||
|
||||
Ok(Self {
|
||||
backend_data,
|
||||
|
@ -853,7 +866,7 @@ impl<B: Backend> State<B> {
|
|||
seat_state,
|
||||
pointer_location: (0.0, 0.0).into(),
|
||||
shm_state: ShmState::new::<Self>(&display_handle, vec![]),
|
||||
space: Space::<Window>::default(),
|
||||
space: Space::<WindowElement>::default(),
|
||||
cursor_status: CursorImageStatus::Default,
|
||||
output_manager_state: OutputManagerState::new_with_xdg_output::<Self>(&display_handle),
|
||||
xdg_shell_state: XdgShellState::new::<Self>(&display_handle),
|
||||
|
@ -908,7 +921,7 @@ pub struct SurfaceDmabufFeedback<'a> {
|
|||
// TODO: docs
|
||||
pub fn take_presentation_feedback(
|
||||
output: &Output,
|
||||
space: &Space<Window>,
|
||||
space: &Space<WindowElement>,
|
||||
render_element_states: &RenderElementStates,
|
||||
) -> OutputPresentationFeedback {
|
||||
let mut output_presentation_feedback = OutputPresentationFeedback::new(output);
|
||||
|
|
|
@ -8,8 +8,8 @@ use smithay::{
|
|||
space::SpaceElement,
|
||||
utils::{
|
||||
send_dmabuf_feedback_surface_tree, send_frames_surface_tree,
|
||||
take_presentation_feedback_surface_tree, with_surfaces_surface_tree,
|
||||
OutputPresentationFeedback,
|
||||
take_presentation_feedback_surface_tree, under_from_surface_tree,
|
||||
with_surfaces_surface_tree, OutputPresentationFeedback,
|
||||
},
|
||||
Window, WindowSurfaceType,
|
||||
},
|
||||
|
@ -26,7 +26,7 @@ use smithay::{
|
|||
},
|
||||
wayland_server::protocol::wl_surface::WlSurface,
|
||||
},
|
||||
utils::{user_data::UserDataMap, IsAlive, Logical, Point, Rectangle, Serial},
|
||||
utils::{user_data::UserDataMap, IsAlive, Logical, Point, Rectangle, Serial, Size},
|
||||
wayland::{
|
||||
compositor::{Blocker, BlockerState, SurfaceData},
|
||||
dmabuf::DmabufFeedback,
|
||||
|
@ -40,7 +40,7 @@ use crate::{
|
|||
state::{State, WithState},
|
||||
};
|
||||
|
||||
use self::window_state::{Float, WindowState};
|
||||
use self::window_state::{Float, WindowResizeState, WindowState};
|
||||
|
||||
pub mod window_state;
|
||||
|
||||
|
@ -53,10 +53,15 @@ pub enum WindowElement {
|
|||
impl WindowElement {
|
||||
pub fn surface_under(
|
||||
&self,
|
||||
location: Point<i32, Logical>,
|
||||
location: Point<f64, Logical>,
|
||||
window_type: WindowSurfaceType,
|
||||
) -> Option<(WlSurface, Point<i32, Logical>)> {
|
||||
todo!()
|
||||
match self {
|
||||
WindowElement::Wayland(window) => window.surface_under(location, window_type),
|
||||
WindowElement::X11(surface) => surface.wl_surface().and_then(|wl_surf| {
|
||||
under_from_surface_tree(&wl_surf, location, (0, 0), window_type)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_surfaces<F>(&self, processor: F)
|
||||
|
@ -174,6 +179,29 @@ impl WindowElement {
|
|||
WindowElement::X11(surface) => surface.user_data(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Request a size and loc change.
|
||||
pub fn request_size_change(&self, new_loc: Point<i32, Logical>, new_size: Size<i32, Logical>) {
|
||||
match self {
|
||||
WindowElement::Wayland(window) => {
|
||||
window.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(new_size);
|
||||
});
|
||||
self.with_state(|state| {
|
||||
state.resize_state =
|
||||
WindowResizeState::Requested(window.toplevel().send_configure(), new_loc)
|
||||
});
|
||||
}
|
||||
WindowElement::X11(surface) => {
|
||||
surface
|
||||
.configure(Rectangle::from_loc_and_size(new_loc, new_size))
|
||||
.expect("failed to configure x11 win");
|
||||
self.with_state(|state| {
|
||||
state.resize_state = WindowResizeState::Acknowledged(new_loc);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IsAlive for WindowElement {
|
||||
|
@ -393,7 +421,7 @@ impl WithState for WindowElement {
|
|||
|
||||
impl<B: Backend> State<B> {
|
||||
/// Returns the [Window] associated with a given [WlSurface].
|
||||
pub fn window_for_surface(&self, surface: &WlSurface) -> Option<Window> {
|
||||
pub fn window_for_surface(&self, surface: &WlSurface) -> Option<WindowElement> {
|
||||
self.space
|
||||
.elements()
|
||||
.find(|window| window.wl_surface().map(|s| s == *surface).unwrap_or(false))
|
||||
|
@ -401,35 +429,31 @@ impl<B: Backend> State<B> {
|
|||
.or_else(|| {
|
||||
self.windows
|
||||
.iter()
|
||||
.find(|&win| win.toplevel().wl_surface() == surface)
|
||||
.find(|&win| win.wl_surface().is_some_and(|surf| &surf == surface))
|
||||
.cloned()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggle a window's floating status.
|
||||
pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &Window) {
|
||||
pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &WindowElement) {
|
||||
let mut resize: Option<_> = None;
|
||||
window.with_state(|window_state| {
|
||||
match window_state.floating {
|
||||
Float::Tiled(prev_loc_and_size) => {
|
||||
if let Some((prev_loc, prev_size)) = prev_loc_and_size {
|
||||
window.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(prev_size);
|
||||
});
|
||||
|
||||
window.toplevel().send_pending_configure();
|
||||
|
||||
state.space.map_element(window.clone(), prev_loc, false);
|
||||
// TODO: should it activate?
|
||||
resize = Some((prev_loc, prev_size));
|
||||
}
|
||||
|
||||
window_state.floating = Float::Floating;
|
||||
window.toplevel().with_pending_state(|tl_state| {
|
||||
tl_state.states.unset(xdg_toplevel::State::TiledTop);
|
||||
tl_state.states.unset(xdg_toplevel::State::TiledBottom);
|
||||
tl_state.states.unset(xdg_toplevel::State::TiledLeft);
|
||||
tl_state.states.unset(xdg_toplevel::State::TiledRight);
|
||||
});
|
||||
if let WindowElement::Wayland(window) = window {
|
||||
window.toplevel().with_pending_state(|tl_state| {
|
||||
tl_state.states.unset(xdg_toplevel::State::TiledTop);
|
||||
tl_state.states.unset(xdg_toplevel::State::TiledBottom);
|
||||
tl_state.states.unset(xdg_toplevel::State::TiledLeft);
|
||||
tl_state.states.unset(xdg_toplevel::State::TiledRight);
|
||||
});
|
||||
} // TODO: tiled states for x11
|
||||
}
|
||||
Float::Floating => {
|
||||
window_state.floating = Float::Tiled(Some((
|
||||
|
@ -438,16 +462,23 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &Window) {
|
|||
state.space.element_location(window).unwrap(),
|
||||
window.geometry().size,
|
||||
)));
|
||||
window.toplevel().with_pending_state(|tl_state| {
|
||||
tl_state.states.set(xdg_toplevel::State::TiledTop);
|
||||
tl_state.states.set(xdg_toplevel::State::TiledBottom);
|
||||
tl_state.states.set(xdg_toplevel::State::TiledLeft);
|
||||
tl_state.states.set(xdg_toplevel::State::TiledRight);
|
||||
});
|
||||
|
||||
if let WindowElement::Wayland(window) = window {
|
||||
window.toplevel().with_pending_state(|tl_state| {
|
||||
tl_state.states.set(xdg_toplevel::State::TiledTop);
|
||||
tl_state.states.set(xdg_toplevel::State::TiledBottom);
|
||||
tl_state.states.set(xdg_toplevel::State::TiledLeft);
|
||||
tl_state.states.set(xdg_toplevel::State::TiledRight);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if let Some((prev_loc, prev_size)) = resize {
|
||||
window.request_size_change(prev_loc, prev_size);
|
||||
}
|
||||
|
||||
let output = state.focus_state.focused_output.clone().unwrap();
|
||||
state.re_layout(&output);
|
||||
|
||||
|
@ -476,6 +507,14 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &Window) {
|
|||
state.loop_handle.insert_idle(move |data| {
|
||||
crate::state::schedule_on_commit(data, render, move |dt| {
|
||||
dt.state.space.raise_element(&clone, true);
|
||||
if let WindowElement::X11(surface) = clone {
|
||||
dt.state
|
||||
.xwm
|
||||
.as_mut()
|
||||
.expect("no xwm")
|
||||
.raise_window(&surface)
|
||||
.expect("failed to raise x11 win");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
fmt,
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
};
|
||||
|
||||
use smithay::{
|
||||
desktop::Window,
|
||||
utils::{Logical, Point, Serial, Size},
|
||||
};
|
||||
use smithay::utils::{Logical, Point, Serial, Size};
|
||||
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
|
@ -17,6 +13,8 @@ use crate::{
|
|||
tag::Tag,
|
||||
};
|
||||
|
||||
use super::WindowElement;
|
||||
|
||||
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct WindowId(u32);
|
||||
|
||||
|
@ -28,7 +26,7 @@ impl WindowId {
|
|||
}
|
||||
|
||||
/// Get the window that has this WindowId.
|
||||
pub fn window<B: Backend>(&self, state: &State<B>) -> Option<Window> {
|
||||
pub fn window<B: Backend>(&self, state: &State<B>) -> Option<WindowElement> {
|
||||
state
|
||||
.windows
|
||||
.iter()
|
||||
|
@ -128,25 +126,6 @@ impl WindowState {
|
|||
}
|
||||
}
|
||||
|
||||
impl WithState for Window {
|
||||
type State = WindowState;
|
||||
|
||||
fn with_state<F, T>(&self, mut func: F) -> T
|
||||
where
|
||||
F: FnMut(&mut Self::State) -> T,
|
||||
{
|
||||
self.user_data()
|
||||
.insert_if_missing(RefCell::<Self::State>::default);
|
||||
|
||||
let state = self
|
||||
.user_data()
|
||||
.get::<RefCell<Self::State>>()
|
||||
.expect("RefCell not in data map");
|
||||
|
||||
func(&mut state.borrow_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WindowState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
use smithay::{
|
||||
desktop::space::SpaceElement,
|
||||
input::{pointer::Focus, Seat},
|
||||
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel,
|
||||
reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel,
|
||||
wayland_server::protocol::wl_surface::WlSurface,
|
||||
},
|
||||
utils::Rectangle,
|
||||
wayland::shell::xdg::ToplevelSurface,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
grab::{move_grab::MoveSurfaceGrab, resize_grab::ResizeSurfaceGrab},
|
||||
grab::{
|
||||
move_grab::MoveSurfaceGrab,
|
||||
resize_grab::{ResizeEdge, ResizeSurfaceGrab},
|
||||
},
|
||||
state::{State, WithState},
|
||||
window::WindowElement,
|
||||
};
|
||||
|
||||
pub fn move_request<B: Backend>(
|
||||
|
@ -19,8 +27,6 @@ pub fn move_request<B: Backend>(
|
|||
seat: &Seat<State<B>>,
|
||||
serial: smithay::utils::Serial,
|
||||
) {
|
||||
println!("move_request started");
|
||||
|
||||
let wl_surface = surface.wl_surface();
|
||||
|
||||
let pointer = seat.get_pointer().unwrap();
|
||||
|
@ -38,23 +44,19 @@ pub fn move_request<B: Backend>(
|
|||
|
||||
pointer.set_grab(state, grab, serial, Focus::Clear);
|
||||
} else {
|
||||
println!("no grab start data");
|
||||
tracing::warn!("no grab start data");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: see how this interacts with drag and drop and other grabs
|
||||
pub fn move_request_force<B: Backend>(
|
||||
state: &mut State<B>,
|
||||
surface: &ToplevelSurface,
|
||||
surface: &WlSurface,
|
||||
seat: &Seat<State<B>>,
|
||||
serial: smithay::utils::Serial,
|
||||
) {
|
||||
println!("move_request_force started");
|
||||
|
||||
let wl_surface = surface.wl_surface();
|
||||
|
||||
let pointer = seat.get_pointer().unwrap();
|
||||
let window = state.window_for_surface(wl_surface).unwrap();
|
||||
let window = state.window_for_surface(surface).unwrap();
|
||||
|
||||
let initial_window_loc = state.space.element_location(&window).unwrap();
|
||||
|
||||
|
@ -77,19 +79,16 @@ pub fn move_request_force<B: Backend>(
|
|||
|
||||
pub fn resize_request<B: Backend>(
|
||||
state: &mut State<B>,
|
||||
surface: &ToplevelSurface,
|
||||
surface: &WlSurface,
|
||||
seat: &Seat<State<B>>,
|
||||
serial: smithay::utils::Serial,
|
||||
edges: xdg_toplevel::ResizeEdge,
|
||||
button_used: u32,
|
||||
) {
|
||||
let wl_surface = surface.wl_surface();
|
||||
|
||||
let pointer = seat.get_pointer().unwrap();
|
||||
|
||||
if let Some(start_data) = crate::pointer::pointer_grab_start_data(&pointer, wl_surface, serial)
|
||||
{
|
||||
let window = state.window_for_surface(wl_surface).unwrap();
|
||||
if let Some(start_data) = crate::pointer::pointer_grab_start_data(&pointer, surface, serial) {
|
||||
let window = state.window_for_surface(surface).unwrap();
|
||||
if window.with_state(|state| state.floating.is_tiled()) {
|
||||
return;
|
||||
}
|
||||
|
@ -97,37 +96,39 @@ pub fn resize_request<B: Backend>(
|
|||
let initial_window_loc = state.space.element_location(&window).unwrap();
|
||||
let initial_window_size = window.geometry().size;
|
||||
|
||||
surface.with_pending_state(|state| {
|
||||
state.states.set(xdg_toplevel::State::Resizing);
|
||||
});
|
||||
if let Some(WindowElement::Wayland(window)) = state.window_for_surface(surface) {
|
||||
window.toplevel().with_pending_state(|state| {
|
||||
state.states.set(xdg_toplevel::State::Resizing);
|
||||
});
|
||||
|
||||
surface.send_pending_configure();
|
||||
window.toplevel().send_pending_configure();
|
||||
}
|
||||
|
||||
let grab = ResizeSurfaceGrab::start(
|
||||
start_data,
|
||||
window,
|
||||
edges,
|
||||
ResizeEdge(edges),
|
||||
Rectangle::from_loc_and_size(initial_window_loc, initial_window_size),
|
||||
button_used,
|
||||
);
|
||||
|
||||
pointer.set_grab(state, grab, serial, Focus::Clear);
|
||||
if let Some(grab) = grab {
|
||||
pointer.set_grab(state, grab, serial, Focus::Clear);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize_request_force<B: Backend>(
|
||||
state: &mut State<B>,
|
||||
surface: &ToplevelSurface,
|
||||
surface: &WlSurface,
|
||||
seat: &Seat<State<B>>,
|
||||
serial: smithay::utils::Serial,
|
||||
edges: xdg_toplevel::ResizeEdge,
|
||||
button_used: u32,
|
||||
) {
|
||||
let wl_surface = surface.wl_surface();
|
||||
|
||||
let pointer = seat.get_pointer().unwrap();
|
||||
|
||||
let window = state.window_for_surface(wl_surface).unwrap();
|
||||
let window = state.window_for_surface(surface).unwrap();
|
||||
|
||||
if window.with_state(|state| state.floating.is_tiled()) {
|
||||
return;
|
||||
|
@ -136,12 +137,13 @@ pub fn resize_request_force<B: Backend>(
|
|||
let initial_window_loc = state.space.element_location(&window).unwrap();
|
||||
let initial_window_size = window.geometry().size;
|
||||
|
||||
surface.with_pending_state(|state| {
|
||||
println!("setting xdg state to Resizing");
|
||||
state.states.set(xdg_toplevel::State::Resizing);
|
||||
});
|
||||
if let Some(WindowElement::Wayland(window)) = state.window_for_surface(surface) {
|
||||
window.toplevel().with_pending_state(|state| {
|
||||
state.states.set(xdg_toplevel::State::Resizing);
|
||||
});
|
||||
|
||||
surface.send_pending_configure();
|
||||
window.toplevel().send_pending_configure();
|
||||
}
|
||||
|
||||
let start_data = smithay::input::pointer::GrabStartData {
|
||||
focus: pointer
|
||||
|
@ -154,10 +156,12 @@ pub fn resize_request_force<B: Backend>(
|
|||
let grab = ResizeSurfaceGrab::start(
|
||||
start_data,
|
||||
window,
|
||||
edges,
|
||||
ResizeEdge(edges),
|
||||
Rectangle::from_loc_and_size(initial_window_loc, initial_window_size),
|
||||
button_used,
|
||||
);
|
||||
|
||||
pointer.set_grab(state, grab, serial, Focus::Clear);
|
||||
if let Some(grab) = grab {
|
||||
pointer.set_grab(state, grab, serial, Focus::Clear);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue