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" } futures-lite = { version = "1.13.0" }
async-process = { version = "1.7.0" } async-process = { version = "1.7.0" }
itertools = { version = "0.11.0" } itertools = { version = "0.11.0" }
x11rb = { version = "0.11.1", default-features = false, features = ["composite"], optional = true }
[features] [features]
default = ["egl", "winit", "udev"] default = ["egl", "winit", "udev", "xwayland"]
egl = ["smithay/use_system_lib", "smithay/backend_egl"] egl = ["smithay/use_system_lib", "smithay/backend_egl"]
udev = [ udev = [
"smithay-drm-extras", "smithay-drm-extras",
@ -40,3 +41,4 @@ udev = [
"xcursor", "xcursor",
] ]
winit = ["smithay/backend_winit", "smithay/backend_drm"] 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::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
error::Error, error::Error,
ffi::OsString,
os::fd::FromRawFd, os::fd::FromRawFd,
path::Path, path::Path,
sync::Mutex, sync::Mutex,
@ -52,7 +53,7 @@ use smithay::{
desktop::{ desktop::{
space::{self, SurfaceTree}, space::{self, SurfaceTree},
utils::{self, surface_primary_scanout_output, OutputPresentationFeedback}, utils::{self, surface_primary_scanout_output, OutputPresentationFeedback},
Space, Window, Space,
}, },
input::pointer::{CursorImageAttributes, CursorImageStatus}, input::pointer::{CursorImageAttributes, CursorImageStatus},
output::{Output, PhysicalProperties, Subpixel}, output::{Output, PhysicalProperties, Subpixel},
@ -100,6 +101,7 @@ use crate::{
api::msg::{Args, OutgoingMsg}, api::msg::{Args, OutgoingMsg},
render::{pointer::PointerElement, CustomRenderElements, OutputRenderElements}, render::{pointer::PointerElement, CustomRenderElements, OutputRenderElements},
state::{take_presentation_feedback, CalloopData, State, SurfaceDmabufFeedback}, state::{take_presentation_feedback, CalloopData, State, SurfaceDmabufFeedback},
window::WindowElement,
}; };
use super::Backend; 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( event_loop.run(
Some(Duration::from_millis(6)), Some(Duration::from_millis(6)),
&mut CalloopData { state, display }, &mut CalloopData { state, display },
@ -1457,7 +1469,7 @@ impl State<UdevData> {
fn render_surface<'a>( fn render_surface<'a>(
surface: &'a mut SurfaceData, surface: &'a mut SurfaceData,
renderer: &mut UdevRenderer<'a, '_>, renderer: &mut UdevRenderer<'a, '_>,
space: &Space<Window>, space: &Space<WindowElement>,
output: &Output, output: &Output,
input_method: &InputMethodHandle, input_method: &InputMethodHandle,
pointer_location: Point<f64, Logical>, pointer_location: Point<f64, Logical>,

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later // 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::{ use smithay::{
backend::{ backend::{
@ -192,6 +192,16 @@ pub fn run_winit() -> Result<(), Box<dyn Error>> {
state.space.map_output(&output, (0, 0)); 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(); let mut pointer_element = PointerElement::<GlesTexture>::new();
// TODO: pointer // TODO: pointer

View file

@ -1,10 +1,12 @@
// SPDX-License-Identifier: GPL-3.0-or-later // 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)] #[derive(Default)]
pub struct FocusState { pub struct FocusState {
focus_stack: Vec<Window>, focus_stack: Vec<WindowElement>,
pub focused_output: Option<Output>, pub focused_output: Option<Output>,
} }
@ -15,7 +17,7 @@ impl FocusState {
// TODO: how does this work with unmapped windows? // TODO: how does this work with unmapped windows?
/// Get the currently focused window. If there is none, the previous focus is returned. /// 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() { while let Some(window) = self.focus_stack.last() {
if window.alive() { if window.alive() {
return Some(window.clone()); return Some(window.clone());
@ -26,7 +28,7 @@ impl FocusState {
} }
/// Set the currently focused window. /// 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.retain(|win| win != &window);
self.focus_stack.push(window); self.focus_stack.push(window);
} }

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
use smithay::{ use smithay::{
desktop::Window, desktop::space::SpaceElement,
// NOTE: maybe alias this to PointerGrabStartData because there's another GrabStartData in // NOTE: maybe alias this to PointerGrabStartData because there's another GrabStartData in
// | input::keyboard // | input::keyboard
input::{ input::{
@ -18,12 +18,12 @@ use smithay::{
use crate::{ use crate::{
backend::Backend, backend::Backend,
state::{State, WithState}, state::{State, WithState},
window::window_state::WindowResizeState, window::{window_state::WindowResizeState, WindowElement},
}; };
pub struct MoveSurfaceGrab<S: SeatHandler> { pub struct MoveSurfaceGrab<S: SeatHandler> {
pub start_data: GrabStartData<S>, pub start_data: GrabStartData<S>,
pub window: Window, pub window: WindowElement,
pub initial_window_loc: Point<i32, Logical>, 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); 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!("window geo is: {:?}", self.window.geometry());
// tracing::info!("loc is: {:?}", data.space.element_location(&self.window)); // tracing::info!("loc is: {:?}", data.space.element_location(&self.window));

View file

@ -1,27 +1,47 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
use smithay::{ use smithay::{
desktop::Window, desktop::space::SpaceElement,
input::{ input::{
pointer::{AxisFrame, ButtonEvent, GrabStartData, PointerGrab, PointerInnerHandle}, pointer::{AxisFrame, ButtonEvent, GrabStartData, PointerGrab, PointerInnerHandle},
SeatHandler, SeatHandler,
}, },
reexports::{ reexports::{
wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge}, wayland_protocols::xdg::shell::server::xdg_toplevel::{self},
wayland_server::protocol::wl_surface::WlSurface, wayland_server::protocol::wl_surface::WlSurface,
}, },
utils::{IsAlive, Logical, Point, Rectangle, Size}, utils::{IsAlive, Logical, Point, Rectangle, Size},
wayland::{compositor, seat::WaylandFocus, shell::xdg::SurfaceCachedState}, wayland::{compositor, shell::xdg::SurfaceCachedState},
xwayland,
}; };
use crate::{ use crate::{
backend::Backend, backend::Backend,
state::{State, WithState}, 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> { pub struct ResizeSurfaceGrab<S: SeatHandler> {
start_data: GrabStartData<S>, start_data: GrabStartData<S>,
window: Window, window: WindowElement,
edges: ResizeEdge, edges: ResizeEdge,
@ -34,26 +54,26 @@ pub struct ResizeSurfaceGrab<S: SeatHandler> {
impl<S: SeatHandler> ResizeSurfaceGrab<S> { impl<S: SeatHandler> ResizeSurfaceGrab<S> {
pub fn start( pub fn start(
start_data: GrabStartData<S>, start_data: GrabStartData<S>,
window: Window, window: WindowElement,
edges: ResizeEdge, edges: ResizeEdge,
initial_window_rect: Rectangle<i32, Logical>, initial_window_rect: Rectangle<i32, Logical>,
button_used: u32, button_used: u32,
) -> Self { ) -> Option<Self> {
window.toplevel().wl_surface().with_state(|state| { window.wl_surface()?.with_state(|state| {
state.resize_state = ResizeSurfaceState::Resizing { state.resize_state = ResizeSurfaceState::Resizing {
edges, edges,
initial_window_rect, initial_window_rect,
}; };
}); });
Self { Some(Self {
start_data, start_data,
window, window,
edges, edges,
initial_window_rect, initial_window_rect,
last_window_size: initial_window_rect.size, last_window_size: initial_window_rect.size,
button_used, 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_width = self.initial_window_rect.size.w;
let mut new_window_height = self.initial_window_rect.size.h; 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; 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; 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; 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; new_window_height = self.initial_window_rect.size.h + delta.y;
} }
@ -129,7 +161,9 @@ impl<B: Backend> PointerGrab<State<B>> for ResizeSurfaceGrab<State<B>> {
new_window_height.clamp(min_height, max_height), 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| { toplevel_surface.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Resizing); state.states.set(xdg_toplevel::State::Resizing);
@ -138,6 +172,17 @@ impl<B: Backend> PointerGrab<State<B>> for ResizeSurfaceGrab<State<B>> {
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( fn relative_motion(
&mut self, &mut self,
@ -160,7 +205,13 @@ impl<B: Backend> PointerGrab<State<B>> for ResizeSurfaceGrab<State<B>> {
if !handle.current_pressed().contains(&self.button_used) { if !handle.current_pressed().contains(&self.button_used) {
handle.unset_grab(data, event.serial, event.time); handle.unset_grab(data, event.serial, event.time);
let toplevel_surface = self.window.toplevel(); if !self.window.alive() {
return;
}
match &self.window {
WindowElement::Wayland(window) => {
let toplevel_surface = window.toplevel();
toplevel_surface.with_pending_state(|state| { toplevel_surface.with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Resizing); state.states.unset(xdg_toplevel::State::Resizing);
state.size = Some(self.last_window_size); state.size = Some(self.last_window_size);
@ -169,12 +220,24 @@ impl<B: Backend> PointerGrab<State<B>> for ResizeSurfaceGrab<State<B>> {
toplevel_surface.send_pending_configure(); toplevel_surface.send_pending_configure();
toplevel_surface.wl_surface().with_state(|state| { toplevel_surface.wl_surface().with_state(|state| {
// TODO: validate resize state
state.resize_state = ResizeSurfaceState::WaitingForLastCommit { state.resize_state = ResizeSurfaceState::WaitingForLastCommit {
edges: self.edges, edges: self.edges,
initial_window_rect: self.initial_window_rect, 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,
};
});
}
}
}
} }
fn axis( fn axis(
@ -236,12 +299,18 @@ pub fn handle_commit<B: Backend>(state: &mut State<B>, surface: &WlSurface) -> O
.map(|(edges, initial_window_rect)| { .map(|(edges, initial_window_rect)| {
let mut new_x: Option<i32> = None; let mut new_x: Option<i32> = None;
let mut new_y: 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( new_x = Some(
initial_window_rect.loc.x + (initial_window_rect.size.w - geometry.size.w), 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( new_y = Some(
initial_window_rect.loc.y + (initial_window_rect.size.h - geometry.size.h), initial_window_rect.loc.y + (initial_window_rect.size.h - geometry.size.h),
); );

View file

@ -43,12 +43,13 @@ use smithay::{
}, },
shm::{ShmHandler, ShmState}, shm::{ShmHandler, ShmState},
}, },
xwayland::{X11Wm, XWaylandClientData},
}; };
use crate::{ use crate::{
backend::Backend, backend::Backend,
state::{ClientState, State, WithState}, state::{CalloopData, ClientState, State, WithState},
window::{window_state::WindowResizeState, WindowBlocker, BLOCKER_COUNTER}, window::{window_state::WindowResizeState, WindowBlocker, WindowElement, BLOCKER_COUNTER},
}; };
impl<B: Backend> BufferHandler for State<B> { impl<B: Backend> BufferHandler for State<B> {
@ -96,6 +97,8 @@ impl<B: Backend> CompositorHandler for State<B> {
fn commit(&mut self, surface: &WlSurface) { fn commit(&mut self, surface: &WlSurface) {
// tracing::debug!("commit"); // tracing::debug!("commit");
X11Wm::commit_hook::<CalloopData<B>>(surface);
utils::on_commit_buffer_handler::<Self>(surface); utils::on_commit_buffer_handler::<Self>(surface);
if !compositor::is_sync_subsurface(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) { while let Some(parent) = compositor::get_parent(&root) {
root = parent; 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(); window.on_commit();
} }
}; };
@ -131,16 +134,20 @@ impl<B: Backend> CompositorHandler for State<B> {
} }
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState { fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
&client if let Some(state) = client.get_data::<XWaylandClientData>() {
.get_data::<ClientState>() return &state.compositor_state;
.expect("ClientState wasn't in client's data map") }
.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>); delegate_compositor!(@<B: Backend> State<B>);
fn ensure_initial_configure<B: Backend>(surface: &WlSurface, state: &mut State<B>) { fn ensure_initial_configure<B: Backend>(surface: &WlSurface, state: &mut State<B>) {
if let Some(window) = state.window_for_surface(surface) { if let Some(window) = state.window_for_surface(surface) {
if let WindowElement::Wayland(window) = &window {
let initial_configure_sent = compositor::with_states(surface, |states| { let initial_configure_sent = compositor::with_states(surface, |states| {
states states
.data_map .data_map
@ -150,12 +157,12 @@ fn ensure_initial_configure<B: Backend>(surface: &WlSurface, state: &mut State<B
.expect("Failed to lock Mutex<XdgToplevelSurfaceData>") .expect("Failed to lock Mutex<XdgToplevelSurfaceData>")
.initial_configure_sent .initial_configure_sent
}); });
// println!("initial_configure_sent is {}", initial_configure_sent);
if !initial_configure_sent { if !initial_configure_sent {
tracing::debug!("Initial configure"); tracing::debug!("Initial configure");
window.toplevel().send_configure(); window.toplevel().send_configure();
} }
}
return; return;
} }
@ -227,14 +234,18 @@ impl<B: Backend> XdgShellHandler for State<B> {
} }
fn new_toplevel(&mut self, surface: ToplevelSurface) { 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| { window.toplevel().with_pending_state(|tl_state| {
tl_state.states.set(xdg_toplevel::State::TiledTop); tl_state.states.set(xdg_toplevel::State::TiledTop);
tl_state.states.set(xdg_toplevel::State::TiledBottom); tl_state.states.set(xdg_toplevel::State::TiledBottom);
tl_state.states.set(xdg_toplevel::State::TiledLeft); tl_state.states.set(xdg_toplevel::State::TiledLeft);
tl_state.states.set(xdg_toplevel::State::TiledRight); tl_state.states.set(xdg_toplevel::State::TiledRight);
}); });
}
window.with_state(|state| { window.with_state(|state| {
state.tags = match ( state.tags = match (
&self.focus_state.focused_output, &self.focus_state.focused_output,
@ -296,7 +307,9 @@ impl<B: Backend> XdgShellHandler for State<B> {
BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst) BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst)
); );
for win in windows_on_output.iter() { 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(); let clone = window.clone();
self.loop_handle.insert_idle(|data| { self.loop_handle.insert_idle(|data| {
@ -308,7 +321,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
); );
for client in windows_on_output for client in windows_on_output
.iter() .iter()
.filter_map(|win| win.toplevel().wl_surface().client()) .filter_map(|win| win.wl_surface()?.client())
{ {
data.state data.state
.client_compositor_state(&client) .client_compositor_state(&client)
@ -324,7 +337,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
.expect("Seat had no keyboard") // FIXME: actually handle error .expect("Seat had no keyboard") // FIXME: actually handle error
.set_focus( .set_focus(
&mut data.state, &mut data.state,
Some(window.toplevel().wl_surface().clone()), window.wl_surface(),
SERIAL_COUNTER.next_serial(), SERIAL_COUNTER.next_serial(),
); );
}); });
@ -332,7 +345,11 @@ impl<B: Backend> XdgShellHandler for State<B> {
fn toplevel_destroyed(&mut self, surface: ToplevelSurface) { fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {
tracing::debug!("toplevel destroyed"); 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() { if let Some(focused_output) = self.focus_state.focused_output.as_ref() {
focused_output.with_state(|state| { focused_output.with_state(|state| {
let first_tag = state.focused_tags().next(); let first_tag = state.focused_tags().next();
@ -353,7 +370,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
let focus = self let focus = self
.focus_state .focus_state
.current_focus() .current_focus()
.map(|win| win.toplevel().wl_surface().clone()); .and_then(|win| win.wl_surface());
self.seat self.seat
.get_keyboard() .get_keyboard()
.expect("Seat had no keyboard") .expect("Seat had no keyboard")
@ -385,7 +402,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
const BUTTON_LEFT: u32 = 0x110; const BUTTON_LEFT: u32 = 0x110;
crate::xdg::request::resize_request( crate::xdg::request::resize_request(
self, self,
&surface, surface.wl_surface(),
&Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"), &Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"),
serial, serial,
edges, edges,
@ -413,12 +430,11 @@ impl<B: Backend> XdgShellHandler for State<B> {
.ok() .ok()
.and_then(|root| self.window_for_surface(&root)) .and_then(|root| self.window_for_surface(&root))
{ {
if let Ok(mut grab) = self.popup_manager.grab_popup( let Some(wl_surface) = root.wl_surface() else { return };
root.toplevel().wl_surface().clone(), if let Ok(mut grab) = self
popup_kind, .popup_manager
&seat, .grab_popup(wl_surface, popup_kind, &seat, serial)
serial, {
) {
if let Some(keyboard) = seat.get_keyboard() { if let Some(keyboard) = seat.get_keyboard() {
if keyboard.is_grabbed() if keyboard.is_grabbed()
&& !(keyboard.has_grab(serial) && !(keyboard.has_grab(serial)

View file

@ -4,66 +4,181 @@
// //
// SPDX-License-Identifier: MPL-2.0 // 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> { impl<B: Backend> XwmHandler for CalloopData<B> {
fn xwm_state(&mut self, xwm: smithay::xwayland::xwm::XwmId) -> &mut smithay::xwayland::X11Wm { fn xwm_state(&mut self, xwm: XwmId) -> &mut X11Wm {
todo!() self.state.xwm.as_mut().expect("xwm not in state")
} }
fn new_window( fn new_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {}
&mut self,
xwm: smithay::xwayland::xwm::XwmId,
window: smithay::xwayland::X11Surface,
) {
todo!()
}
fn new_override_redirect_window( fn new_override_redirect_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {}
&mut self,
xwm: smithay::xwayland::xwm::XwmId,
window: smithay::xwayland::X11Surface,
) {
todo!()
}
fn map_window_request( fn map_window_request(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {
&mut self, tracing::debug!("new x11 window from map_window_request");
xwm: smithay::xwayland::xwm::XwmId, window.set_mapped(true).expect("failed to map x11 window");
window: smithay::xwayland::X11Surface, 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(),
) { ) {
todo!() (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( fn mapped_override_redirect_window(
&mut self, &mut self,
xwm: smithay::xwayland::xwm::XwmId, xwm: XwmId,
window: smithay::xwayland::X11Surface, 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( fn unmapped_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {
&mut self, let win = self
xwm: smithay::xwayland::xwm::XwmId, .state
window: smithay::xwayland::X11Surface, .space
) { .elements()
todo!() .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( fn destroyed_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {}
&mut self,
xwm: smithay::xwayland::xwm::XwmId,
window: smithay::xwayland::X11Surface,
) {
todo!()
}
fn configure_request( fn configure_request(
&mut self, &mut self,
xwm: smithay::xwayland::xwm::XwmId, xwm: XwmId,
window: smithay::xwayland::X11Surface, window: smithay::xwayland::X11Surface,
x: Option<i32>, x: Option<i32>,
y: Option<i32>, y: Option<i32>,
@ -71,35 +186,112 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
h: Option<u32>, h: Option<u32>,
reorder: Option<smithay::xwayland::xwm::Reorder>, 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( fn configure_notify(
&mut self, &mut self,
xwm: smithay::xwayland::xwm::XwmId, xwm: XwmId,
window: smithay::xwayland::X11Surface, window: smithay::xwayland::X11Surface,
geometry: smithay::utils::Rectangle<i32, smithay::utils::Logical>, geometry: smithay::utils::Rectangle<i32, smithay::utils::Logical>,
above: Option<smithay::reexports::x11rb::protocol::xproto::Window>, 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( fn resize_request(
&mut self, &mut self,
xwm: smithay::xwayland::xwm::XwmId, xwm: XwmId,
window: smithay::xwayland::X11Surface, window: smithay::xwayland::X11Surface,
button: u32, button: u32,
resize_edge: smithay::xwayland::xwm::ResizeEdge, 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!() todo!()
} }
fn move_request( // TODO: allow_selection_access
&mut self,
xwm: smithay::xwayland::xwm::XwmId, // TODO: send_selection
window: smithay::xwayland::X11Surface,
button: u32, // TODO: new_selection
) {
todo!() // TODO: cleared_selection
}
} }

View file

@ -2,20 +2,22 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::api::msg::{CallbackId, Modifier, ModifierMask, OutgoingMsg}; use crate::{
api::msg::{CallbackId, Modifier, ModifierMask, OutgoingMsg},
window::WindowElement,
};
use smithay::{ use smithay::{
backend::input::{ backend::input::{
AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputBackend, InputEvent, AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputBackend, InputEvent,
KeyState, KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent, PointerMotionEvent, KeyState, KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent, PointerMotionEvent,
}, },
desktop::{Window, WindowSurfaceType}, desktop::{space::SpaceElement, WindowSurfaceType},
input::{ input::{
keyboard::{keysyms, FilterResult}, keyboard::{keysyms, FilterResult},
pointer::{AxisFrame, ButtonEvent, MotionEvent}, pointer::{AxisFrame, ButtonEvent, MotionEvent},
}, },
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge, reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
utils::{Logical, Point, SERIAL_COUNTER}, utils::{Logical, Point, SERIAL_COUNTER},
wayland::seat::WaylandFocus,
}; };
use crate::{ use crate::{
@ -38,7 +40,7 @@ impl InputState {
} }
impl<B: Backend> State<B> { 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 where
P: Into<Point<f64, Logical>>, P: Into<Point<f64, Logical>>,
{ {
@ -74,12 +76,14 @@ impl<B: Backend> State<B> {
const BUTTON_RIGHT: u32 = 0x111; const BUTTON_RIGHT: u32 = 0x111;
if self.move_mode { if self.move_mode {
if event.button_code() == BUTTON_LEFT { if event.button_code() == BUTTON_LEFT {
if let Some(wl_surf) = window.wl_surface() {
crate::xdg::request::move_request_force( crate::xdg::request::move_request_force(
self, self,
window.toplevel(), &wl_surf,
&self.seat.clone(), &self.seat.clone(),
serial, serial,
); );
}
return; // TODO: kinda ugly return here return; // TODO: kinda ugly return here
} else if event.button_code() == BUTTON_RIGHT { } else if event.button_code() == BUTTON_RIGHT {
let window_geometry = window.geometry(); let window_geometry = window.geometry();
@ -120,29 +124,48 @@ impl<B: Backend> State<B> {
_ => ResizeEdge::None, _ => ResizeEdge::None,
}; };
if let Some(wl_surf) = window.wl_surface() {
crate::xdg::request::resize_request_force( crate::xdg::request::resize_request_force(
self, self,
window.toplevel(), &wl_surf,
&self.seat.clone(), &self.seat.clone(),
serial, serial,
edges, edges,
BUTTON_RIGHT, BUTTON_RIGHT,
); );
} }
}
} else { } else {
// Move window to top of stack. // Move window to top of stack.
self.space.raise_element(&window, true); 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| { self.space.elements().for_each(|window| {
if let WindowElement::Wayland(window) = window {
window.toplevel().send_configure(); window.toplevel().send_configure();
}
}); });
} }
} else { } else {
self.space.elements().for_each(|window| { self.space.elements().for_each(|window| match window {
WindowElement::Wayland(window) => {
window.set_activated(false); window.set_activated(false);
window.toplevel().send_configure(); 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); keyboard.set_focus(self, None, serial);
} }

View file

@ -2,7 +2,7 @@
use itertools::{Either, Itertools}; use itertools::{Either, Itertools};
use smithay::{ use smithay::{
desktop::{Space, Window}, desktop::Space,
output::Output, output::Output,
utils::{Logical, Size}, utils::{Logical, Size},
}; };
@ -11,7 +11,7 @@ use crate::{
backend::Backend, backend::Backend,
state::{State, WithState}, state::{State, WithState},
tag::Tag, tag::Tag,
window::window_state::WindowResizeState, window::WindowElement,
}; };
// TODO: couple this with the layouts // TODO: couple this with the layouts
@ -29,9 +29,9 @@ pub enum Layout {
impl Layout { impl Layout {
pub fn layout( pub fn layout(
&self, &self,
windows: Vec<Window>, windows: Vec<WindowElement>,
tags: Vec<Tag>, tags: Vec<Tag>,
space: &Space<Window>, space: &Space<WindowElement>,
output: &Output, output: &Output,
) { ) {
let windows = filter_windows(&windows, tags); let windows = filter_windows(&windows, tags);
@ -44,7 +44,25 @@ impl Layout {
let output_loc = output.current_location(); let output_loc = output.current_location();
match self { match self {
Layout::MasterStack => { 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) => corner(layout, windows, space, output),
}
}
}
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 master = windows.first();
let stack = windows.iter().skip(1); let stack = windows.iter().skip(1);
@ -54,28 +72,11 @@ impl Layout {
if stack_count == 0 { if stack_count == 0 {
// one window // one window
master.toplevel().with_pending_state(|state| { master.request_size_change(output_loc, output_geo.size);
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 { } else {
let new_master_size: Size<i32, Logical> = let loc = (output_loc.x, output_loc.y).into();
(output_geo.size.w / 2, output_geo.size.h).into(); let new_master_size: Size<i32, Logical> = (output_geo.size.w / 2, output_geo.size.h).into();
master.toplevel().with_pending_state(|state| { master.request_size_change(loc, new_master_size);
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 stack_count = stack_count;
@ -91,40 +92,31 @@ impl Layout {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for (i, win) in stack.enumerate() { for (i, win) in stack.enumerate() {
win.toplevel().with_pending_state(|state| { win.request_size_change(
// INFO: Some windows crash the compositor if they become too short in height, (output_geo.size.w / 2 + output_loc.x, y_s[i] + output_loc.y).into(),
// | so they're limited to a minimum of 40 pixels as a workaround. (output_geo.size.w / 2, i32::max(heights[i], 40)).into(),
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 => {
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(); let mut iter = windows.windows(2).peekable();
if iter.peek().is_none() { if iter.peek().is_none() {
if let Some(window) = windows.first() { if let Some(window) = windows.first() {
window.toplevel().with_pending_state(|state| { window.request_size_change(output_loc, output_geo.size);
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 { } else {
let mut win1_size = output_geo.size;
let mut win1_loc = output_loc;
for (i, wins) in iter.enumerate() { for (i, wins) in iter.enumerate() {
let win1 = &wins[0]; let win1 = &wins[0];
let win2 = &wins[1]; let win2 = &wins[1];
@ -140,100 +132,56 @@ impl Layout {
Slice::Below 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 { match slice {
Slice::Right => { Slice::Right => {
let width_partition = win1_size.w / 2; let width_partition = win1_size.w / 2;
win1.toplevel().with_pending_state(|state| {
state.size = Some( win1.request_size_change(
(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, win1_loc,
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(),
); );
});
win2.toplevel().with_pending_state(|state| { win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into();
state.size = win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
Some((width_partition, i32::max(win1_size.h, 40)).into());
}); win2.request_size_change(win1_loc, win1_size);
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 => { Slice::Below => {
let height_partition = win1_size.h / 2; let height_partition = win1_size.h / 2;
win1.toplevel().with_pending_state(|state| {
state.size = Some( win1.request_size_change(
(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, win1_loc,
(win1_size.w, i32::max(win1_size.h - height_partition, 40)).into(),
); );
});
win2.toplevel().with_pending_state(|state| { win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into();
state.size = win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
Some((win1_size.w, i32::max(height_partition, 40)).into());
}); win2.request_size_change(win1_loc, win1_size);
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 => {
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(); let mut iter = windows.windows(2).peekable();
if iter.peek().is_none() { if iter.peek().is_none() {
if let Some(window) = windows.first() { if let Some(window) = windows.first() {
window.toplevel().with_pending_state(|state| { window.request_size_change(output_loc, output_geo.size);
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 { } else {
let mut win1_loc = output_loc;
let mut win1_size = output_geo.size;
for (i, wins) in iter.enumerate() { for (i, wins) in iter.enumerate() {
let win1 = &wins[0]; let win1 = &wins[0];
let win2 = &wins[1]; let win2 = &wins[1];
@ -253,174 +201,90 @@ impl Layout {
_ => unreachable!(), _ => 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 { match slice {
Slice::Above => { Slice::Above => {
let height_partition = win1_size.h / 2; let height_partition = win1_size.h / 2;
win1.toplevel().with_pending_state(|state| {
state.size = Some( win1.request_size_change(
(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(), (win1_loc.x, win1_loc.y + height_partition).into(),
(win1_size.w, i32::max(win1_size.h - height_partition, 40)).into(),
); );
});
win2.toplevel().with_pending_state(|state| { win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
state.size = win2.request_size_change(win1_loc, win1_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 => { Slice::Below => {
let height_partition = win1_size.h / 2; let height_partition = win1_size.h / 2;
win1.toplevel().with_pending_state(|state| {
state.size = Some( win1.request_size_change(
(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, win1_loc,
(win1_size.w, win1_size.h - i32::max(height_partition, 40)).into(),
); );
});
win2.toplevel().with_pending_state(|state| { win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into();
state.size = win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
Some((win1_size.w, i32::max(height_partition, 40)).into()); win2.request_size_change(win1_loc, win1_size);
});
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 => { Slice::Left => {
let width_partition = win1_size.w / 2; let width_partition = win1_size.w / 2;
win1.toplevel().with_pending_state(|state| {
state.size = Some( win1.request_size_change(
(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(), (win1_loc.x + width_partition, win1_loc.y).into(),
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(),
); );
});
win2.toplevel().with_pending_state(|state| { win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
state.size = win2.request_size_change(win1_loc, win1_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 => { Slice::Right => {
let width_partition = win1_size.w / 2; let width_partition = win1_size.w / 2;
win1.toplevel().with_pending_state(|state| {
state.size = Some( win1.request_size_change(
(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, win1_loc,
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(),
); );
});
win2.toplevel().with_pending_state(|state| { win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into();
state.size = win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
Some((width_partition, i32::max(win1_size.h, 40)).into()); win2.request_size_change(win1_loc, win1_size);
});
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 @ (Layout::CornerTopLeft
| Layout::CornerTopRight fn corner(
| Layout::CornerBottomLeft layout: &Layout,
| Layout::CornerBottomRight) => match windows.len() { 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 => (), 0 => (),
1 => { 1 => {
windows[0].toplevel().with_pending_state(|state| { windows[0].request_size_change(output_loc, output_geo.size);
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 => { 2 => {
windows[0].toplevel().with_pending_state(|state| { windows[0].request_size_change(
state.size = Some((output_geo.size.w / 2, output_geo.size.h).into()); output_loc,
}); (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| { windows[1].request_size_change(
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(), (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 mut windows = windows.into_iter();
let Some(corner) = windows.next() else { unreachable!() }; let Some(corner) = windows.next() else { unreachable!() };
let (horiz_stack, vert_stack): (Vec<Window>, Vec<Window>) = let (horiz_stack, vert_stack): (Vec<WindowElement>, Vec<WindowElement>) =
windows.enumerate().partition_map(|(i, win)| { windows.enumerate().partition_map(|(i, win)| {
if i % 2 == 0 { if i % 2 == 0 {
Either::Left(win) Either::Left(win)
@ -431,41 +295,30 @@ impl Layout {
let div_factor = 2; let div_factor = 2;
corner.toplevel().with_pending_state(|state| { corner.request_size_change(
state.size = Some( 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.w / div_factor,
output_geo.size.h / div_factor, output_geo.size.h / div_factor,
) )
.into(), .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 vert_stack_count = vert_stack.len();
@ -481,31 +334,20 @@ impl Layout {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for (i, win) in vert_stack.iter().enumerate() { for (i, win) in vert_stack.iter().enumerate() {
win.toplevel().with_pending_state(|state| { win.request_size_change(
// 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 { match layout {
Layout::CornerTopLeft | Layout::CornerBottomLeft => { Layout::CornerTopLeft | Layout::CornerBottomLeft => {
output_geo.size.w / 2 + output_loc.x output_geo.size.w / 2 + output_loc.x
} }
Layout::CornerTopRight | Layout::CornerBottomRight => { Layout::CornerTopRight | Layout::CornerBottomRight => output_loc.x,
output_loc.x
}
_ => unreachable!(), _ => unreachable!(),
}, },
y_s[i] + output_loc.y, y_s[i] + output_loc.y,
) )
.into(), .into(),
(output_geo.size.w / 2, i32::max(heights[i], 40)).into(),
); );
});
} }
let horiz_stack_count = horiz_stack.len(); let horiz_stack_count = horiz_stack.len();
@ -524,45 +366,30 @@ impl Layout {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for (i, win) in horiz_stack.iter().enumerate() { for (i, win) in horiz_stack.iter().enumerate() {
win.toplevel().with_pending_state(|state| { win.request_size_change(
// 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 { match layout {
Layout::CornerTopLeft => ( Layout::CornerTopLeft => {
x_s[i] + output_loc.x, (x_s[i] + output_loc.x, output_loc.y + output_geo.size.h / 2)
output_loc.y + output_geo.size.h / 2, }
),
Layout::CornerTopRight => ( Layout::CornerTopRight => (
x_s[i] + output_loc.x + output_geo.size.w / 2, x_s[i] + output_loc.x + output_geo.size.w / 2,
output_loc.y + output_geo.size.h / 2, output_loc.y + output_geo.size.h / 2,
), ),
Layout::CornerBottomLeft => { Layout::CornerBottomLeft => (x_s[i] + output_loc.x, output_loc.y),
(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)
} }
Layout::CornerBottomRight => (
x_s[i] + output_loc.x + output_geo.size.w / 2,
output_loc.y,
),
_ => unreachable!(), _ => unreachable!(),
} }
.into(), .into(),
(i32::max(widths[i], 1), output_geo.size.h / 2).into(),
); );
});
} }
} }
},
}
} }
} }
fn filter_windows(windows: &[Window], tags: Vec<Tag>) -> Vec<Window> { fn filter_windows(windows: &[WindowElement], tags: Vec<Tag>) -> Vec<WindowElement> {
windows windows
.iter() .iter()
.filter(|window| { .filter(|window| {
@ -582,7 +409,7 @@ fn filter_windows(windows: &[Window], tags: Vec<Tag>) -> Vec<Window> {
} }
impl<B: Backend> State<B> { 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 let mut elems = self
.windows .windows
.iter_mut() .iter_mut()

View file

@ -2,13 +2,16 @@
use smithay::{ use smithay::{
backend::renderer::{ backend::renderer::{
element::{surface::WaylandSurfaceRenderElement, Wrap}, element::{surface::WaylandSurfaceRenderElement, AsRenderElements, Wrap},
ImportAll, ImportMem, ImportAll, ImportMem, Renderer, Texture,
}, },
desktop::space::SpaceRenderElements, desktop::space::{SpaceElement, SpaceRenderElements},
render_elements, render_elements,
utils::{Physical, Point, Scale},
}; };
use crate::window::WindowElement;
use self::pointer::PointerRenderElement; use self::pointer::PointerRenderElement;
pub mod pointer; pub mod pointer;
@ -26,3 +29,34 @@ render_elements! {
Custom=CustomRenderElements<R>, Custom=CustomRenderElements<R>,
// TODO: preview // 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, focus::FocusState,
grab::resize_grab::ResizeSurfaceState, grab::resize_grab::ResizeSurfaceState,
tag::Tag, tag::Tag,
window::window_state::WindowResizeState, window::{window_state::WindowResizeState, WindowElement},
}; };
use calloop::futures::Scheduler; use calloop::futures::Scheduler;
use futures_lite::AsyncBufReadExt; use futures_lite::AsyncBufReadExt;
use smithay::{ use smithay::{
backend::renderer::element::RenderElementStates, backend::renderer::element::RenderElementStates,
desktop::{ desktop::{
space::SpaceElement,
utils::{ utils::{
surface_presentation_feedback_flags_from_states, surface_primary_scanout_output, surface_presentation_feedback_flags_from_states, surface_primary_scanout_output,
OutputPresentationFeedback, OutputPresentationFeedback,
}, },
PopupManager, Space, Window, PopupManager, Space,
}, },
input::{keyboard::XkbConfig, pointer::CursorImageStatus, Seat, SeatState}, input::{keyboard::XkbConfig, pointer::CursorImageStatus, Seat, SeatState},
output::Output, output::Output,
@ -71,7 +72,7 @@ pub struct State<B: Backend> {
pub loop_handle: LoopHandle<'static, CalloopData<B>>, pub loop_handle: LoopHandle<'static, CalloopData<B>>,
pub clock: Clock<Monotonic>, pub clock: Clock<Monotonic>,
pub space: Space<Window>, pub space: Space<WindowElement>,
pub move_mode: bool, pub move_mode: bool,
pub socket_name: String, pub socket_name: String,
@ -93,7 +94,7 @@ pub struct State<B: Backend> {
pub cursor_status: CursorImageStatus, pub cursor_status: CursorImageStatus,
pub pointer_location: Point<f64, Logical>, pub pointer_location: Point<f64, Logical>,
pub windows: Vec<Window>, pub windows: Vec<WindowElement>,
pub async_scheduler: Scheduler<()>, pub async_scheduler: Scheduler<()>,
@ -123,7 +124,12 @@ impl<B: Backend> State<B> {
Msg::SetMousebind { button: _ } => todo!(), Msg::SetMousebind { button: _ } => todo!(),
Msg::CloseWindow { window_id } => { Msg::CloseWindow { window_id } => {
if let Some(window) = window_id.window(self) { 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 } => { Msg::ToggleFloating { window_id } => {
@ -147,19 +153,13 @@ impl<B: Backend> State<B> {
let Some(window) = window_id.window(self) else { return }; let Some(window) = window_id.window(self) else { return };
// TODO: tiled vs floating // 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; let window_size = window.geometry().size;
window.toplevel().with_pending_state(|state| { window.request_size_change(window_loc, window_size);
// 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();
} }
Msg::MoveWindowToTag { window_id, tag_id } => { Msg::MoveWindowToTag { window_id, tag_id } => {
let Some(window) = window_id.window(self) else { return }; let Some(window) = window_id.window(self) else { return };
@ -305,8 +305,10 @@ impl<B: Backend> State<B> {
.as_ref() .as_ref()
.and_then(|win| self.space.element_location(win)) .and_then(|win| self.space.element_location(win))
.map(|loc| (loc.x, loc.y)); .map(|loc| (loc.x, loc.y));
let (class, title) = window.as_ref().map_or((None, None), |win| { let (class, title) = window.as_ref().and_then(|win| win.wl_surface()).map_or(
compositor::with_states(win.toplevel().wl_surface(), |states| { (None, None),
|wl_surf| {
compositor::with_states(&wl_surf, |states| {
let lock = states let lock = states
.data_map .data_map
.get::<XdgToplevelSurfaceData>() .get::<XdgToplevelSurfaceData>()
@ -315,7 +317,8 @@ impl<B: Backend> State<B> {
.expect("failed to acquire lock"); .expect("failed to acquire lock");
(lock.app_id.clone(), lock.title.clone()) (lock.app_id.clone(), lock.title.clone())
}) })
}); },
);
let floating = window let floating = window
.as_ref() .as_ref()
.map(|win| win.with_state(|state| state.floating.is_floating())); .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 program = OsString::from(program);
let Ok(mut child) = async_process::Command::new(&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() { .stdin(if callback_id.is_some() {
Stdio::piped() Stdio::piped()
} else { } else {
@ -670,7 +679,7 @@ impl<B: Backend> State<B> {
/// idle. /// idle.
pub fn schedule_on_commit<F, B: Backend>( pub fn schedule_on_commit<F, B: Backend>(
data: &mut CalloopData<B>, data: &mut CalloopData<B>,
windows: Vec<Window>, windows: Vec<WindowElement>,
on_commit: F, on_commit: F,
) where ) where
F: FnOnce(&mut CalloopData<B>) + 'static, F: FnOnce(&mut CalloopData<B>) + 'static,
@ -804,17 +813,19 @@ impl<B: Backend> State<B> {
.expect("failed to insert rx_channel into loop"); .expect("failed to insert rx_channel into loop");
}); });
tracing::debug!("before xwayland");
let xwayland = { let xwayland = {
let (xwayland, channel) = XWayland::new(&display_handle); let (xwayland, channel) = XWayland::new(&display_handle);
let clone = display_handle.clone(); let clone = display_handle.clone();
let res = tracing::debug!("inserting into loop");
loop_handle.insert_source(channel, move |event, _metadata, data| match event { let res = loop_handle.insert_source(channel, move |event, _, data| match event {
XWaylandEvent::Ready { XWaylandEvent::Ready {
connection, connection,
client, client,
client_fd: _, client_fd: _,
display, display,
} => { } => {
tracing::debug!("XWaylandEvent ready");
let mut wm = X11Wm::start_wm( let mut wm = X11Wm::start_wm(
data.state.loop_handle.clone(), data.state.loop_handle.clone(),
clone.clone(), clone.clone(),
@ -830,6 +841,7 @@ impl<B: Backend> State<B> {
Point::from((image.xhot as u16, image.yhot as u16)), Point::from((image.xhot as u16, image.yhot as u16)),
) )
.expect("failed to set xwayland default cursor"); .expect("failed to set xwayland default cursor");
tracing::debug!("setting xwm and xdisplay");
data.state.xwm = Some(wm); data.state.xwm = Some(wm);
data.state.xdisplay = Some(display); data.state.xdisplay = Some(display);
} }
@ -842,6 +854,7 @@ impl<B: Backend> State<B> {
} }
xwayland xwayland
}; };
tracing::debug!("after xwayland");
Ok(Self { Ok(Self {
backend_data, backend_data,
@ -853,7 +866,7 @@ impl<B: Backend> State<B> {
seat_state, seat_state,
pointer_location: (0.0, 0.0).into(), pointer_location: (0.0, 0.0).into(),
shm_state: ShmState::new::<Self>(&display_handle, vec![]), shm_state: ShmState::new::<Self>(&display_handle, vec![]),
space: Space::<Window>::default(), space: Space::<WindowElement>::default(),
cursor_status: CursorImageStatus::Default, cursor_status: CursorImageStatus::Default,
output_manager_state: OutputManagerState::new_with_xdg_output::<Self>(&display_handle), output_manager_state: OutputManagerState::new_with_xdg_output::<Self>(&display_handle),
xdg_shell_state: XdgShellState::new::<Self>(&display_handle), xdg_shell_state: XdgShellState::new::<Self>(&display_handle),
@ -908,7 +921,7 @@ pub struct SurfaceDmabufFeedback<'a> {
// TODO: docs // TODO: docs
pub fn take_presentation_feedback( pub fn take_presentation_feedback(
output: &Output, output: &Output,
space: &Space<Window>, space: &Space<WindowElement>,
render_element_states: &RenderElementStates, render_element_states: &RenderElementStates,
) -> OutputPresentationFeedback { ) -> OutputPresentationFeedback {
let mut output_presentation_feedback = OutputPresentationFeedback::new(output); let mut output_presentation_feedback = OutputPresentationFeedback::new(output);

View file

@ -8,8 +8,8 @@ use smithay::{
space::SpaceElement, space::SpaceElement,
utils::{ utils::{
send_dmabuf_feedback_surface_tree, send_frames_surface_tree, send_dmabuf_feedback_surface_tree, send_frames_surface_tree,
take_presentation_feedback_surface_tree, with_surfaces_surface_tree, take_presentation_feedback_surface_tree, under_from_surface_tree,
OutputPresentationFeedback, with_surfaces_surface_tree, OutputPresentationFeedback,
}, },
Window, WindowSurfaceType, Window, WindowSurfaceType,
}, },
@ -26,7 +26,7 @@ use smithay::{
}, },
wayland_server::protocol::wl_surface::WlSurface, 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::{ wayland::{
compositor::{Blocker, BlockerState, SurfaceData}, compositor::{Blocker, BlockerState, SurfaceData},
dmabuf::DmabufFeedback, dmabuf::DmabufFeedback,
@ -40,7 +40,7 @@ use crate::{
state::{State, WithState}, state::{State, WithState},
}; };
use self::window_state::{Float, WindowState}; use self::window_state::{Float, WindowResizeState, WindowState};
pub mod window_state; pub mod window_state;
@ -53,10 +53,15 @@ pub enum WindowElement {
impl WindowElement { impl WindowElement {
pub fn surface_under( pub fn surface_under(
&self, &self,
location: Point<i32, Logical>, location: Point<f64, Logical>,
window_type: WindowSurfaceType, window_type: WindowSurfaceType,
) -> Option<(WlSurface, Point<i32, Logical>)> { ) -> 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) pub fn with_surfaces<F>(&self, processor: F)
@ -174,6 +179,29 @@ impl WindowElement {
WindowElement::X11(surface) => surface.user_data(), 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 { impl IsAlive for WindowElement {
@ -393,7 +421,7 @@ impl WithState for WindowElement {
impl<B: Backend> State<B> { impl<B: Backend> State<B> {
/// Returns the [Window] associated with a given [WlSurface]. /// 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 self.space
.elements() .elements()
.find(|window| window.wl_surface().map(|s| s == *surface).unwrap_or(false)) .find(|window| window.wl_surface().map(|s| s == *surface).unwrap_or(false))
@ -401,35 +429,31 @@ impl<B: Backend> State<B> {
.or_else(|| { .or_else(|| {
self.windows self.windows
.iter() .iter()
.find(|&win| win.toplevel().wl_surface() == surface) .find(|&win| win.wl_surface().is_some_and(|surf| &surf == surface))
.cloned() .cloned()
}) })
} }
} }
/// Toggle a window's floating status. /// 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| { window.with_state(|window_state| {
match window_state.floating { match window_state.floating {
Float::Tiled(prev_loc_and_size) => { Float::Tiled(prev_loc_and_size) => {
if let Some((prev_loc, prev_size)) = prev_loc_and_size { if let Some((prev_loc, prev_size)) = prev_loc_and_size {
window.toplevel().with_pending_state(|state| { resize = Some((prev_loc, prev_size));
state.size = Some(prev_size);
});
window.toplevel().send_pending_configure();
state.space.map_element(window.clone(), prev_loc, false);
// TODO: should it activate?
} }
window_state.floating = Float::Floating; window_state.floating = Float::Floating;
if let WindowElement::Wayland(window) = window {
window.toplevel().with_pending_state(|tl_state| { window.toplevel().with_pending_state(|tl_state| {
tl_state.states.unset(xdg_toplevel::State::TiledTop); tl_state.states.unset(xdg_toplevel::State::TiledTop);
tl_state.states.unset(xdg_toplevel::State::TiledBottom); tl_state.states.unset(xdg_toplevel::State::TiledBottom);
tl_state.states.unset(xdg_toplevel::State::TiledLeft); tl_state.states.unset(xdg_toplevel::State::TiledLeft);
tl_state.states.unset(xdg_toplevel::State::TiledRight); tl_state.states.unset(xdg_toplevel::State::TiledRight);
}); });
} // TODO: tiled states for x11
} }
Float::Floating => { Float::Floating => {
window_state.floating = Float::Tiled(Some(( window_state.floating = Float::Tiled(Some((
@ -438,6 +462,8 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &Window) {
state.space.element_location(window).unwrap(), state.space.element_location(window).unwrap(),
window.geometry().size, window.geometry().size,
))); )));
if let WindowElement::Wayland(window) = window {
window.toplevel().with_pending_state(|tl_state| { window.toplevel().with_pending_state(|tl_state| {
tl_state.states.set(xdg_toplevel::State::TiledTop); tl_state.states.set(xdg_toplevel::State::TiledTop);
tl_state.states.set(xdg_toplevel::State::TiledBottom); tl_state.states.set(xdg_toplevel::State::TiledBottom);
@ -446,8 +472,13 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &Window) {
}); });
} }
} }
}
}); });
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(); let output = state.focus_state.focused_output.clone().unwrap();
state.re_layout(&output); 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| { state.loop_handle.insert_idle(move |data| {
crate::state::schedule_on_commit(data, render, move |dt| { crate::state::schedule_on_commit(data, render, move |dt| {
dt.state.space.raise_element(&clone, true); 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 // SPDX-License-Identifier: GPL-3.0-or-later
use std::{ use std::{
cell::RefCell,
fmt, fmt,
sync::atomic::{AtomicU32, Ordering}, sync::atomic::{AtomicU32, Ordering},
}; };
use smithay::{ use smithay::utils::{Logical, Point, Serial, Size};
desktop::Window,
utils::{Logical, Point, Serial, Size},
};
use crate::{ use crate::{
backend::Backend, backend::Backend,
@ -17,6 +13,8 @@ use crate::{
tag::Tag, tag::Tag,
}; };
use super::WindowElement;
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct WindowId(u32); pub struct WindowId(u32);
@ -28,7 +26,7 @@ impl WindowId {
} }
/// Get the window that has this 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 state
.windows .windows
.iter() .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 { impl Default for WindowState {
fn default() -> Self { fn default() -> Self {
Self { Self {

View file

@ -1,16 +1,24 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
use smithay::{ use smithay::{
desktop::space::SpaceElement,
input::{pointer::Focus, Seat}, 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, utils::Rectangle,
wayland::shell::xdg::ToplevelSurface, wayland::shell::xdg::ToplevelSurface,
}; };
use crate::{ use crate::{
backend::Backend, backend::Backend,
grab::{move_grab::MoveSurfaceGrab, resize_grab::ResizeSurfaceGrab}, grab::{
move_grab::MoveSurfaceGrab,
resize_grab::{ResizeEdge, ResizeSurfaceGrab},
},
state::{State, WithState}, state::{State, WithState},
window::WindowElement,
}; };
pub fn move_request<B: Backend>( pub fn move_request<B: Backend>(
@ -19,8 +27,6 @@ pub fn move_request<B: Backend>(
seat: &Seat<State<B>>, seat: &Seat<State<B>>,
serial: smithay::utils::Serial, serial: smithay::utils::Serial,
) { ) {
println!("move_request started");
let wl_surface = surface.wl_surface(); let wl_surface = surface.wl_surface();
let pointer = seat.get_pointer().unwrap(); let pointer = seat.get_pointer().unwrap();
@ -38,23 +44,19 @@ pub fn move_request<B: Backend>(
pointer.set_grab(state, grab, serial, Focus::Clear); pointer.set_grab(state, grab, serial, Focus::Clear);
} else { } else {
println!("no grab start data"); tracing::warn!("no grab start data");
} }
} }
// TODO: see how this interacts with drag and drop and other grabs // TODO: see how this interacts with drag and drop and other grabs
pub fn move_request_force<B: Backend>( pub fn move_request_force<B: Backend>(
state: &mut State<B>, state: &mut State<B>,
surface: &ToplevelSurface, surface: &WlSurface,
seat: &Seat<State<B>>, seat: &Seat<State<B>>,
serial: smithay::utils::Serial, serial: smithay::utils::Serial,
) { ) {
println!("move_request_force started");
let wl_surface = surface.wl_surface();
let pointer = seat.get_pointer().unwrap(); 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(); 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>( pub fn resize_request<B: Backend>(
state: &mut State<B>, state: &mut State<B>,
surface: &ToplevelSurface, surface: &WlSurface,
seat: &Seat<State<B>>, seat: &Seat<State<B>>,
serial: smithay::utils::Serial, serial: smithay::utils::Serial,
edges: xdg_toplevel::ResizeEdge, edges: xdg_toplevel::ResizeEdge,
button_used: u32, button_used: u32,
) { ) {
let wl_surface = surface.wl_surface();
let pointer = seat.get_pointer().unwrap(); let pointer = seat.get_pointer().unwrap();
if let Some(start_data) = crate::pointer::pointer_grab_start_data(&pointer, wl_surface, serial) if let Some(start_data) = crate::pointer::pointer_grab_start_data(&pointer, surface, serial) {
{ let window = state.window_for_surface(surface).unwrap();
let window = state.window_for_surface(wl_surface).unwrap();
if window.with_state(|state| state.floating.is_tiled()) { if window.with_state(|state| state.floating.is_tiled()) {
return; return;
} }
@ -97,37 +96,39 @@ pub fn resize_request<B: Backend>(
let initial_window_loc = state.space.element_location(&window).unwrap(); let initial_window_loc = state.space.element_location(&window).unwrap();
let initial_window_size = window.geometry().size; let initial_window_size = window.geometry().size;
surface.with_pending_state(|state| { if let Some(WindowElement::Wayland(window)) = state.window_for_surface(surface) {
window.toplevel().with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Resizing); state.states.set(xdg_toplevel::State::Resizing);
}); });
surface.send_pending_configure(); window.toplevel().send_pending_configure();
}
let grab = ResizeSurfaceGrab::start( let grab = ResizeSurfaceGrab::start(
start_data, start_data,
window, window,
edges, ResizeEdge(edges),
Rectangle::from_loc_and_size(initial_window_loc, initial_window_size), Rectangle::from_loc_and_size(initial_window_loc, initial_window_size),
button_used, button_used,
); );
if let Some(grab) = grab {
pointer.set_grab(state, grab, serial, Focus::Clear); pointer.set_grab(state, grab, serial, Focus::Clear);
} }
} }
}
pub fn resize_request_force<B: Backend>( pub fn resize_request_force<B: Backend>(
state: &mut State<B>, state: &mut State<B>,
surface: &ToplevelSurface, surface: &WlSurface,
seat: &Seat<State<B>>, seat: &Seat<State<B>>,
serial: smithay::utils::Serial, serial: smithay::utils::Serial,
edges: xdg_toplevel::ResizeEdge, edges: xdg_toplevel::ResizeEdge,
button_used: u32, button_used: u32,
) { ) {
let wl_surface = surface.wl_surface();
let pointer = seat.get_pointer().unwrap(); 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()) { if window.with_state(|state| state.floating.is_tiled()) {
return; 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_loc = state.space.element_location(&window).unwrap();
let initial_window_size = window.geometry().size; let initial_window_size = window.geometry().size;
surface.with_pending_state(|state| { if let Some(WindowElement::Wayland(window)) = state.window_for_surface(surface) {
println!("setting xdg state to Resizing"); window.toplevel().with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Resizing); state.states.set(xdg_toplevel::State::Resizing);
}); });
surface.send_pending_configure(); window.toplevel().send_pending_configure();
}
let start_data = smithay::input::pointer::GrabStartData { let start_data = smithay::input::pointer::GrabStartData {
focus: pointer focus: pointer
@ -154,10 +156,12 @@ pub fn resize_request_force<B: Backend>(
let grab = ResizeSurfaceGrab::start( let grab = ResizeSurfaceGrab::start(
start_data, start_data,
window, window,
edges, ResizeEdge(edges),
Rectangle::from_loc_and_size(initial_window_loc, initial_window_size), Rectangle::from_loc_and_size(initial_window_loc, initial_window_size),
button_used, button_used,
); );
if let Some(grab) = grab {
pointer.set_grab(state, grab, serial, Focus::Clear); pointer.set_grab(state, grab, serial, Focus::Clear);
} }
}