init commit

This commit is contained in:
IoIxD 2026-07-04 20:11:23 -07:00
commit ae577cdc2c
8 changed files with 3364 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

2395
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

11
Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "milkyway"
version = "0.1.0"
edition = "2024"
[dependencies]
parking_lot = "0.12.5"
smithay = { version = "0.7.0", features = ["wayland-backend", "xwayland"] }
wayland-protocols = { version = "0.32.13", features = ["wayland-server", "server"] }
wayland-server = { version = "0.31.13", features = ["dlopen"] }
winit = "0.30.13"

43
src/backend/mod.rs Normal file
View file

@ -0,0 +1,43 @@
use ::winit::raw_window_handle::{HasWindowHandle, RawWindowHandle};
use smithay::{
backend::{
renderer::{gles::GlesRenderer, RendererSuper},
winit::WinitGraphicsBackend,
},
reexports::calloop::LoopHandle,
utils::{Physical, Size},
};
use crate::state::State;
pub mod winit;
pub trait Backend {
fn get_fb(
&mut self,
) -> Result<
(
&mut GlesRenderer,
<GlesRenderer as RendererSuper>::Framebuffer<'_>,
),
Box<dyn std::error::Error>,
>;
fn bounds(&self) -> Size<i32, Physical>;
}
impl Backend for WinitGraphicsBackend<GlesRenderer> {
fn get_fb(
&mut self,
) -> Result<
(
&mut GlesRenderer,
<GlesRenderer as RendererSuper>::Framebuffer<'_>,
),
Box<dyn std::error::Error>,
> {
return Ok(self.bind()?);
}
fn bounds(&self) -> Size<i32, Physical> {
return self.window_size();
}
}

147
src/backend/winit.rs Normal file
View file

@ -0,0 +1,147 @@
use std::time::Duration;
use ::winit::platform::pump_events::PumpStatus;
use smithay::backend::input::ButtonState;
use smithay::reexports::calloop::EventLoop;
use smithay::{
backend::{
input::{AbsolutePositionEvent, Event, InputEvent, KeyboardKeyEvent, PointerButtonEvent},
renderer::gles::GlesRenderer,
winit::{self, WinitEvent},
},
input::keyboard::FilterResult,
reexports::wayland_server::Display,
utils::{Rectangle, Size},
};
use crate::state::State;
pub fn run_winit() -> Result<(), Box<dyn std::error::Error>> {
let mut display: Display<State> = Display::new()?;
let mut event_loop = EventLoop::try_new()?;
let mut state = State::new(&display, &event_loop)?;
state.setup_xwayland(&display)?;
let (mut backend, mut winit) = winit::init::<GlesRenderer>()?;
let keyboard = state
.seat
.add_keyboard(Default::default(), 600, 25)
.unwrap();
let pointer = state.seat.add_pointer();
unsafe { std::env::set_var("WAYLAND_DISPLAY", "wayland-5") };
std::process::Command::new("weston-terminal").spawn().ok();
let damage = Rectangle::from_size(backend.window_size());
let mut i = 0;
loop {
let status = winit.dispatch_new_events(|event| match event {
WinitEvent::Resized { .. } => {}
WinitEvent::Input(event) => match event {
InputEvent::Keyboard { event } => {
keyboard.input::<(), _>(
&mut state,
event.key_code(),
event.state(),
i.into(),
event.time_msec(),
|_, _, _| {
//
FilterResult::Forward
},
);
}
InputEvent::PointerMotionAbsolute { event } => {
let size = backend.window_size();
let location = event.position_transformed(Size::new(size.w, size.h));
let locationrel = event.position_transformed(Size::new(1, 1));
let ev = smithay::input::pointer::MotionEvent {
location,
serial: i.into(),
time: event.time_msec(),
};
state.motion_event(ev, &pointer, locationrel);
}
InputEvent::PointerButton { event } => {
let ev = smithay::input::pointer::ButtonEvent {
serial: i.into(),
time: event.time_msec(),
button: event.button_code(),
state: event.state(),
};
if event.state() == ButtonState::Pressed {
if let Some(surface) = state.focused_surface() {
{
if let Some(ref surface) = surface.surface {
keyboard.set_focus(
&mut state,
Some(surface.wl_surface().clone()),
i.into(),
);
}
if let Some(ref surface) = surface.xsurface {
keyboard.set_focus(&mut state, surface.wl_surface(), i.into());
}
}
{
let mut space = state.space.lock();
space.raise_element(&surface, true);
}
} else {
keyboard.set_focus(&mut state, None, i.into());
}
}
state.button_event(ev, &pointer);
}
// InputEvent::PointerAxis { event } => todo!(),
// InputEvent::DeviceAdded { device } => todo!(),
// InputEvent::DeviceRemoved { device } => todo!(),
// InputEvent::GestureSwipeBegin { event } => todo!(),
// InputEvent::GestureSwipeUpdate { event } => todo!(),
// InputEvent::GestureSwipeEnd { event } => todo!(),
// InputEvent::GesturePinchBegin { event } => todo!(),
// InputEvent::GesturePinchUpdate { event } => todo!(),
// InputEvent::GesturePinchEnd { event } => todo!(),
// InputEvent::GestureHoldBegin { event } => todo!(),
// InputEvent::GestureHoldEnd { event } => todo!(),
// InputEvent::TouchDown { event } => todo!(),
// InputEvent::TouchMotion { event } => todo!(),
// InputEvent::TouchUp { event } => todo!(),
// InputEvent::TouchCancel { event } => todo!(),
// InputEvent::TouchFrame { event } => todo!(),
// InputEvent::TabletToolAxis { event } => todo!(),
// InputEvent::TabletToolProximity { event } => todo!(),
// InputEvent::TabletToolTip { event } => todo!(),
// InputEvent::TabletToolButton { event } => todo!(),
// InputEvent::SwitchToggle { event } => todo!(),
// InputEvent::Special(_) => todo!(),
_ => {}
},
_ => (),
});
match status {
PumpStatus::Continue => (),
PumpStatus::Exit(_) => return Ok(()),
};
state.render(&mut backend, &display)?;
display.dispatch_clients(&mut state)?;
display.flush_clients()?;
// It is important that all events on the display have been dispatched and flushed to clients before
// swapping buffers because this operation may block.
backend.submit(Some(&[damage]))?;
event_loop.dispatch(Duration::from_millis(1), &mut state)?;
i += 1;
}
}

9
src/main.rs Normal file
View file

@ -0,0 +1,9 @@
use backend::winit::run_winit;
pub mod backend;
pub mod state;
pub mod xwayland;
fn main() -> Result<(), Box<dyn std::error::Error>> {
run_winit()
}

624
src/state.rs Normal file
View file

@ -0,0 +1,624 @@
use parking_lot::Mutex;
use std::{sync::Arc, time::Instant};
use smithay::{
backend::{renderer::{
element::{
surface::{render_elements_from_surface_tree, WaylandSurfaceRenderElement},
AsRenderElements, Kind,
},
gles::GlesRenderer,
utils::{draw_render_elements, on_commit_buffer_handler},
Color32F, Frame, Renderer,
}, input::ButtonState},
delegate_compositor, delegate_data_device, delegate_seat, delegate_shm, delegate_xdg_shell,
desktop::{space::SpaceElement, Space, Window},
input::{
pointer::{
AxisFrame, ButtonEvent, Focus, GestureHoldBeginEvent, GestureHoldEndEvent,
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,
GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData,
MotionEvent, PointerGrab, PointerHandle, PointerInnerHandle, RelativeMotionEvent,
},
Seat, SeatHandler, SeatState,
},
reexports::{
calloop::{EventLoop, LoopHandle},
wayland_server::{protocol::wl_seat, Display},
},
utils::{IsAlive, Logical, Point, Rectangle, Scale, Serial, Transform},
wayland::{
buffer::BufferHandler,
compositor::{
with_surface_tree_downward, CompositorClientState, CompositorHandler, CompositorState,
SurfaceAttributes, TraversalAction,
},
seat::WaylandFocus,
selection::{
data_device::{
ClientDndGrabHandler, DataDeviceHandler, DataDeviceState, ServerDndGrabHandler,
},
SelectionHandler,
},
shell::xdg::{
PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState,
},
shm::{ShmHandler, ShmState},
xwayland_shell::XWaylandShellState,
},
xwayland::{X11Surface, XWaylandClientData, xwm::ResizeEdge},
};
use wayland_protocols::xdg::shell::server::xdg_toplevel;
use wayland_server::{
backend::{ClientData, ClientId, DisconnectReason},
protocol::{
wl_buffer,
wl_surface::{self, WlSurface},
},
Client, ListeningSocket,
};
use crate::xwayland::XWaylandState;
pub struct State {
pub seat: Seat<Self>,
pub space: Mutex<Space<AppWrapper>>,
clients: Mutex<Vec<Client>>,
pointer_location: Point<f64, Logical>,
pointer_location_rel: Point<f64, Logical>,
start_time: Instant,
listener: ListeningSocket,
pub event_loop_handle: LoopHandle<'static, Self>,
pub xwayland_state: XWaylandState,
start_data: GrabStartData<State>,
compositor_state: CompositorState,
xdg_shell_state: XdgShellState,
shm_state: ShmState,
seat_state: SeatState<Self>,
data_device_state: DataDeviceState,
}
impl State {
pub fn new(
display: &Display<State>,
event_loop: &EventLoop<'static, Self>,
) -> Result<Self, Box<dyn std::error::Error>> {
let dh = display.handle();
let compositor_state = CompositorState::new::<State>(&dh);
let shm_state = ShmState::new::<State>(&dh, vec![]);
let mut seat_state = SeatState::new();
let seat = seat_state.new_wl_seat(&dh, "milkyway_seat");
let space = Space::default();
let listener = ListeningSocket::bind("wayland-5").unwrap();
let event_loop_handle = event_loop.handle();
Ok(State {
compositor_state,
xdg_shell_state: XdgShellState::new::<State>(&dh),
shm_state,
seat_state,
data_device_state: DataDeviceState::new::<State>(&dh),
seat,
start_time: Instant::now(),
listener,
clients: Mutex::new(vec![]),
space: Mutex::new(space),
pointer_location: Point::new(0.0, 0.0),
pointer_location_rel: Point::new(0.0, 0.0),
event_loop_handle,
xwayland_state: XWaylandState {
xwm: None,
xwayland_shell_state: XWaylandShellState::new::<State>(&dh),
},
start_data: GrabStartData {
focus: None,
button: 0,
location: Point::new(0.0, 0.0),
},
})
}
pub fn render<A>(
&self,
backend: &mut A,
display: &Display<Self>,
) -> Result<(), Box<dyn std::error::Error>>
where
A: crate::backend::Backend,
{
let space = self.space.lock();
let size = backend.bounds();
let damage = Rectangle::from_size(size);
let (renderer, mut framebuffer) = backend.get_fb().unwrap();
let mut elements = space
.elements()
.flat_map(|element| {
if let Some(surface) = element.surface.clone() {
return render_elements_from_surface_tree(
renderer,
surface.wl_surface(),
(0, 0),
1.0,
1.0,
Kind::Unspecified,
);
}
if let Some(surface) = element.xsurface.clone() {
return surface.render_elements(
renderer,
Point::new(0, 0),
Scale { x: 1.0, y: 1.0 },
1.0,
);
}
panic!()
})
.collect::<Vec<WaylandSurfaceRenderElement<GlesRenderer>>>();
elements.reverse();
let mut frame = renderer
.render(&mut framebuffer, size, Transform::Flipped180)
.unwrap();
frame
.clear(Color32F::new(0.1, 0.0, 0.0, 1.0), &[damage])
.unwrap();
draw_render_elements(&mut frame, 1.0, &elements, &[damage]).unwrap();
// We rely on the nested compositor to do the sync for us
let _ = frame.finish().unwrap();
for surface in self.xdg_shell_state.toplevel_surfaces() {
self.send_frames_surface_tree(surface.wl_surface());
}
if let Some(stream) = self.listener.accept()? {
let client = display
.handle()
.insert_client(stream, Arc::new(ClientState::default()))
.unwrap();
let mut clients = self.clients.lock();
clients.push(client);
}
Ok(())
}
pub fn focused_surface(&mut self) -> Option<AppWrapper> {
let space = self.space.lock();
// let mut surfaces = vec![];
let mut elements = space.elements().collect::<Vec<_>>();
elements.reverse();
for window in elements {
if window.is_in_input_region(&self.pointer_location) {
return Some(window.clone());
}
}
None
}
pub fn button_event(&mut self, ev: ButtonEvent, pointer: &PointerHandle<Self>) {
if ev.state == ButtonState::Pressed && let Some(app) = self.focused_surface() {
let space = self.space.lock();
let initial_rect = space.element_geometry(&app).unwrap();
println!("{:?}",initial_rect);
};
pointer.button(self, &ev);
}
pub fn motion_event(
&mut self,
ev: MotionEvent,
pointer: &PointerHandle<Self>,
locationrel: Point<f64, Logical>,
) {
self.pointer_location = ev.location;
self.pointer_location_rel = locationrel;
if let Some(surface) = self.focused_surface() {
if let Some(surface) = surface.surface {
pointer.motion(self, Some((surface.wl_surface().clone(), locationrel)), &ev);
}
if let Some(surface) = surface.xsurface {
if let Some(surface) = surface.wl_surface() {
pointer.motion(self, Some((surface, locationrel)), &ev);
}
}
pointer.frame(self);
} else {
pointer.motion(self, None, &ev);
pointer.frame(self);
}
}
fn send_frames_surface_tree(&self, surface: &wl_surface::WlSurface) {
let time = self.start_time.elapsed().as_millis() as u32;
with_surface_tree_downward(
surface,
(),
|_, _, &()| TraversalAction::DoChildren(()),
|_surf, states, &()| {
// the surface may not have any user_data if it is a subsurface and has not
// yet been committed
for callback in states
.cached_state
.get::<SurfaceAttributes>()
.current()
.frame_callbacks
.drain(..)
{
callback.done(time);
}
},
|_, _, &()| true,
);
}
}
impl BufferHandler for State {
fn buffer_destroyed(&mut self, _buffer: &wl_buffer::WlBuffer) {}
}
impl XdgShellHandler for State {
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
&mut self.xdg_shell_state
}
fn new_toplevel(&mut self, surface: ToplevelSurface) {
surface.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Activated);
});
surface.send_configure();
let mut space = self.space.lock();
space.map_element(AppWrapper::new_wayland(surface), (0, 0), true);
}
fn new_popup(&mut self, _surface: PopupSurface, _positioner: PositionerState) {
// Handle popup creation here
}
fn grab(&mut self, _surface: PopupSurface, _seat: wl_seat::WlSeat, _serial: Serial) {
// Handle popup grab here
}
fn reposition_request(
&mut self,
_surface: PopupSurface,
_positioner: PositionerState,
_token: u32,
) {
// Handle popup reposition here
}
}
impl SelectionHandler for State {
type SelectionUserData = ();
}
impl ClientDndGrabHandler for State {}
impl ServerDndGrabHandler for State {}
impl DataDeviceHandler for State {
fn data_device_state(&self) -> &DataDeviceState {
&self.data_device_state
}
}
impl CompositorHandler for State {
fn compositor_state(&mut self) -> &mut CompositorState {
&mut self.compositor_state
}
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
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 state type?");
}
fn commit(&mut self, surface: &WlSurface) {
on_commit_buffer_handler::<Self>(surface);
}
}
impl ShmHandler for State {
fn shm_state(&self) -> &ShmState {
&self.shm_state
}
}
impl SeatHandler for State {
type KeyboardFocus = WlSurface;
type PointerFocus = WlSurface;
type TouchFocus = WlSurface;
fn seat_state(&mut self) -> &mut SeatState<Self> {
&mut self.seat_state
}
fn focus_changed(&mut self, _seat: &Seat<Self>, _focused: Option<&WlSurface>) {}
fn cursor_image(
&mut self,
_seat: &Seat<Self>,
_image: smithay::input::pointer::CursorImageStatus,
) {
}
}
pub struct MoveGrab {
start_data: GrabStartData<State>,
window: Window,
initial_window_location: Point<i32, Logical>,
}
impl PointerGrab<State> for MoveGrab {
fn motion(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
_focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>,
event: &MotionEvent,
) {
handle.motion(data, None, event);
// let delta = event.location - self.start_data.location;
// let mut new_width = self.initial_rect.size.w;
// let mut new_height = self.initial_rect.size.h;
// if self.edges.intersects(ResizeEdge::LEFT | ResizeEdge::RIGHT) {
// new_width = if self.edges.intersects(ResizeEdge::LEFT) {
// (self.initial_rect.size.w as f64 - delta.x) as i32
// } else {
// (self.initial_rect.size.w as f64 + delta.x) as i32
// };
// }
// if self.edges.intersects(ResizeEdge::TOP | ResizeEdge::BOTTOM) {
// new_height = if self.edges.intersects(ResizeEdge::TOP) {
// (self.initial_rect.size.h as f64 - delta.y) as i32
// } else {
// (self.initial_rect.size.h as f64 + delta.y) as i32
// };
// }
// // clamp to whatever min/max you enforce
// let (min_size, max_size) = (Size::from((100, 100)), Size::from((i32::MAX, i32::MAX)));
// new_width = new_width.clamp(min_size.w, max_size.w);
// new_height = new_height.clamp(min_size.h, max_size.h);
// self.last_window_size = (new_width, new_height).into();
// if let Some(toplevel) = self.window.toplevel() {
// toplevel.with_pending_state(|state| {
// state.size = Some(self.last_window_size);
// state.states.set(xdg_toplevel::State::Resizing);
// });
// toplevel.send_pending_configure();
// }
}
fn relative_motion(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>,
event: &RelativeMotionEvent,
) {
handle.relative_motion(data, focus, event);
}
fn button(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &ButtonEvent,
) {
handle.button(data, event);
}
fn axis(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
details: AxisFrame,
) {
handle.axis(data, details)
}
fn frame(&mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>) {
handle.frame(data);
}
fn gesture_swipe_begin(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureSwipeBeginEvent,
) {
handle.gesture_swipe_begin(data, event);
}
fn gesture_swipe_update(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureSwipeUpdateEvent,
) {
handle.gesture_swipe_update(data, event);
}
fn gesture_swipe_end(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureSwipeEndEvent,
) {
handle.gesture_swipe_end(data, event);
}
fn gesture_pinch_begin(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GesturePinchBeginEvent,
) {
handle.gesture_pinch_begin(data, event);
}
fn gesture_pinch_update(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GesturePinchUpdateEvent,
) {
handle.gesture_pinch_update(data, event);
}
fn gesture_pinch_end(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GesturePinchEndEvent,
) {
handle.gesture_pinch_end(data, event);
}
fn gesture_hold_begin(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureHoldBeginEvent,
) {
handle.gesture_hold_begin(data, event);
}
fn gesture_hold_end(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureHoldEndEvent,
) {
handle.gesture_hold_end(data, event);
}
fn start_data(&self) -> &GrabStartData<State> {
&self.start_data
}
fn unset(&mut self, data: &mut State) {}
}
#[derive(PartialEq, Clone)]
pub struct AppWrapper {
pub surface: Option<ToplevelSurface>,
pub xsurface: Option<X11Surface>,
pub window: Window,
}
impl AppWrapper {
pub fn new_wayland(surface: ToplevelSurface) -> Self {
let window = Window::new_wayland_window(surface.clone());
Self {
surface: Some(surface),
xsurface: None,
window,
}
}
pub fn new_x11(xsurface: X11Surface) -> Self {
let window = Window::new_x11_window(xsurface.clone());
Self {
surface: None,
xsurface: Some(xsurface),
window,
}
}
pub fn from_window(window: Window) -> Self {
if let Some(xsurface) = window.x11_surface() {
Self {
surface: None,
xsurface: Some(xsurface.clone()),
window,
}
} else if let Some(surface) = window.toplevel() {
Self {
surface: Some(surface.clone()),
xsurface: None,
window,
}
} else {
panic!();
}
}
}
impl SpaceElement for AppWrapper {
fn bbox(&self) -> Rectangle<i32, smithay::utils::Logical> {
return SpaceElement::bbox(&self.window);
}
fn is_in_input_region(
&self,
point: &smithay::utils::Point<f64, smithay::utils::Logical>,
) -> bool {
return self.window.is_in_input_region(point);
}
fn set_activate(&self, activated: bool) {
return self.window.set_activate(activated);
}
fn output_enter(
&self,
output: &smithay::output::Output,
overlap: Rectangle<i32, smithay::utils::Logical>,
) {
return self.window.output_enter(output, overlap);
}
fn output_leave(&self, output: &smithay::output::Output) {
return self.window.output_leave(output);
}
}
impl IsAlive for AppWrapper {
fn alive(&self) -> bool {
self.window.alive()
}
}
#[derive(Default)]
pub struct ClientState {
compositor_state: CompositorClientState,
}
impl ClientData for ClientState {
fn initialized(&self, _client_id: ClientId) {
println!("initialized");
}
fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {
println!("disconnected");
}
}
// Macros used to delegate protocol handling to types in the app state.
delegate_xdg_shell!(State);
delegate_compositor!(State);
delegate_shm!(State);
delegate_seat!(State);
delegate_data_device!(State);

134
src/xwayland.rs Normal file
View file

@ -0,0 +1,134 @@
use std::process::Stdio;
use smithay::{
delegate_xwayland_shell,
reexports::calloop::EventLoop,
utils::{Logical, Rectangle},
wayland::xwayland_shell::{XWaylandShellHandler, XWaylandShellState},
xwayland::{
xwm::{Reorder, ResizeEdge, XwmId},
X11Surface, X11Wm, XWayland, XWaylandEvent, XwmHandler,
},
};
use wayland_server::Display;
use crate::state::{AppWrapper, State};
pub struct XWaylandState {
pub xwm: Option<X11Wm>,
pub xwayland_shell_state: XWaylandShellState,
}
impl XwmHandler for State {
fn xwm_state(&mut self, _xwm: XwmId) -> &mut X11Wm {
self.xwayland_state.xwm.as_mut().unwrap()
}
fn new_window(&mut self, _xwm: XwmId, _window: X11Surface) {}
fn new_override_redirect_window(&mut self, _xwm: XwmId, _window: X11Surface) {}
fn map_window_request(&mut self, _xwm: XwmId, window: X11Surface) {
// grant it — the window will be mapped once you set this
window.set_mapped(true).unwrap();
let mut space = self.space.lock();
let z_index = space.elements().len();
space.map_element(AppWrapper::new_x11(window), (0, 0), true);
// insert it into your Space/window management here
}
fn mapped_override_redirect_window(&mut self, _xwm: XwmId, _window: X11Surface) {}
fn unmapped_window(&mut self, _xwm: XwmId, _window: X11Surface) {
let mut space = self.space.lock();
}
fn destroyed_window(&mut self, _xwm: XwmId, _window: X11Surface) {}
fn configure_request(
&mut self,
_xwm: XwmId,
window: X11Surface,
x: Option<i32>,
y: Option<i32>,
w: Option<u32>,
h: Option<u32>,
_reorder: Option<Reorder>,
) {
// Usually you just approve the requested geometry
let _ = window.configure(None);
}
fn configure_notify(
&mut self,
_xwm: XwmId,
_window: X11Surface,
_geo: Rectangle<i32, Logical>,
_above: Option<u32>,
) {
}
fn resize_request(
&mut self,
_xwm: XwmId,
_window: X11Surface,
_button: u32,
_edge: ResizeEdge,
) {
}
fn move_request(&mut self, _xwm: XwmId, _window: X11Surface, _button: u32) {}
}
impl XWaylandShellHandler for State {
fn xwayland_shell_state(
&mut self,
) -> &mut smithay::wayland::xwayland_shell::XWaylandShellState {
&mut self.xwayland_state.xwayland_shell_state
}
}
impl State {
pub fn setup_xwayland(
&mut self,
display: &Display<State>,
) -> Result<(), Box<dyn std::error::Error>> {
let dh = display.handle();
let (xwayland, client) = XWayland::spawn(
&dh,
None,
std::iter::empty::<(String, String)>(),
true,
Stdio::null(),
Stdio::null(),
|_| {},
)?;
let event_loop_handle = self.event_loop_handle.clone();
event_loop_handle.insert_source(xwayland, move |event, _, state| {
match event {
XWaylandEvent::Ready {
x11_socket,
display_number,
} => {
let event_loop = state.event_loop_handle.clone();
let wm = X11Wm::start_wm(event_loop, x11_socket, client.clone())
.expect("failed to start X11 wm");
state.xwayland_state.xwm = Some(wm);
println!("XWayland started on display :{display_number}");
unsafe { std::env::set_var("DISPLAY", format!(":{display_number}")) };
}
XWaylandEvent::Error => {
// warn!("XWayland crashed on startup");
}
}
})?;
Ok(())
}
}
delegate_xwayland_shell!(State);