Get xterm to show up

This commit is contained in:
Ottatop 2023-07-24 18:59:05 -05:00
parent 52094c9504
commit f92153eb04
15 changed files with 1055 additions and 826 deletions

View file

@ -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"]

View file

@ -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>,

View file

@ -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

View file

@ -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);
}

View file

@ -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));

View file

@ -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),
);

View file

@ -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)

View file

@ -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
}

View file

@ -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);
}

View file

@ -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()

View file

@ -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()
}
}

View file

@ -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);

View file

@ -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");
}
});
});
}

View file

@ -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 {

View file

@ -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);
}
}