init commit
This commit is contained in:
commit
ae577cdc2c
8 changed files with 3364 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
2395
Cargo.lock
generated
Normal file
2395
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
11
Cargo.toml
Normal file
11
Cargo.toml
Normal 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
43
src/backend/mod.rs
Normal 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
147
src/backend/winit.rs
Normal 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
9
src/main.rs
Normal 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
624
src/state.rs
Normal 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
134
src/xwayland.rs
Normal 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);
|
||||||
Loading…
Add table
Add a link
Reference in a new issue