steal most code from smithay's smallvil example, merging our own xwayland code
This commit is contained in:
parent
fdd3706768
commit
4c64d9c266
17 changed files with 1553 additions and 959 deletions
106
Cargo.lock
generated
106
Cargo.lock
generated
|
|
@ -31,6 +31,15 @@ dependencies = [
|
|||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aliasable"
|
||||
version = "0.1.3"
|
||||
|
|
@ -801,6 +810,12 @@ version = "3.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.186"
|
||||
|
|
@ -892,6 +907,15 @@ version = "0.4.33"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||
dependencies = [
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.2"
|
||||
|
|
@ -911,8 +935,10 @@ dependencies = [
|
|||
name = "milkyway"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"parking_lot",
|
||||
"smithay",
|
||||
"tracing-subscriber",
|
||||
"wayland-protocols",
|
||||
"wayland-server",
|
||||
"winit 0.30.13",
|
||||
|
|
@ -948,6 +974,15 @@ dependencies = [
|
|||
"jni-sys 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
|
|
@ -1529,6 +1564,23 @@ dependencies = [
|
|||
"bitflags 2.13.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
|
||||
[[package]]
|
||||
name = "reis"
|
||||
version = "0.7.0"
|
||||
|
|
@ -1663,6 +1715,15 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "2.0.1"
|
||||
|
|
@ -1890,6 +1951,15 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-skia"
|
||||
version = "0.11.4"
|
||||
|
|
@ -1975,6 +2045,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex-automata",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2013,6 +2113,12 @@ version = "1.13.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
|
|
|
|||
|
|
@ -9,3 +9,5 @@ smithay = { path = "./smithay", features = ["desktop", "wayland-backend", "xwayl
|
|||
wayland-protocols = { version = "0.32.13", features = ["wayland-server", "server"] }
|
||||
wayland-server = { version = "0.31.13", features = ["dlopen"] }
|
||||
winit = "0.30.13"
|
||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||
bitflags = "2.2.1"
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
use smithay::{
|
||||
backend::{
|
||||
renderer::{gles::GlesRenderer, RendererSuper},
|
||||
winit::WinitGraphicsBackend,
|
||||
},
|
||||
utils::{Physical, Size},
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use smithay::backend::input::ButtonState;
|
||||
use smithay::input::pointer::MotionEvent;
|
||||
use smithay::reexports::calloop::EventLoop;
|
||||
use smithay::reexports::winit::event_loop::pump_events::PumpStatus;
|
||||
use smithay::utils::SERIAL_COUNTER;
|
||||
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 backend, mut winit) = winit::init::<GlesRenderer>()?;
|
||||
|
||||
let mut state = State::new(&display, &event_loop, backend.window_size())?;
|
||||
state.setup_xwayland(&display)?;
|
||||
|
||||
let keyboard = state
|
||||
.seat
|
||||
.add_keyboard(Default::default(), 600, 25)
|
||||
.unwrap();
|
||||
|
||||
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());
|
||||
|
||||
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(),
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
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: SERIAL_COUNTER.next_serial(),
|
||||
time: event.time_msec(),
|
||||
};
|
||||
state.motion_event(ev, locationrel);
|
||||
}
|
||||
InputEvent::PointerButton { event } => {
|
||||
let ev = smithay::input::pointer::ButtonEvent {
|
||||
serial: SERIAL_COUNTER.next_serial(),
|
||||
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()),
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
);
|
||||
}
|
||||
if let Some(ref surface) = surface.xsurface {
|
||||
keyboard.set_focus(
|
||||
&mut state,
|
||||
surface.wl_surface(),
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
);
|
||||
}
|
||||
}
|
||||
{
|
||||
let mut space = state.space.lock();
|
||||
space.raise_element(&surface, true);
|
||||
}
|
||||
} else {
|
||||
keyboard.set_focus(&mut state, None, SERIAL_COUNTER.next_serial());
|
||||
}
|
||||
}
|
||||
|
||||
state.button_event(ev);
|
||||
}
|
||||
|
||||
// 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]))?;
|
||||
|
||||
{
|
||||
let mut space = state.space.lock();
|
||||
space.refresh();
|
||||
}
|
||||
|
||||
event_loop.dispatch(Duration::from_millis(1), &mut state)?;
|
||||
}
|
||||
}
|
||||
5
src/grabs/mod.rs
Normal file
5
src/grabs/mod.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
pub mod move_grab;
|
||||
pub use move_grab::MoveSurfaceGrab;
|
||||
|
||||
pub mod resize_grab;
|
||||
pub use resize_grab::ResizeSurfaceGrab;
|
||||
160
src/grabs/move_grab.rs
Normal file
160
src/grabs/move_grab.rs
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
//! Move grab is the state of a composer during which the client window is being dragged around.
|
||||
//!
|
||||
//! eg. Usually whenever a user clicks on the app's titlebar and starts dragging, the compositors
|
||||
//! enters a MoveSurfaceGrab state.
|
||||
|
||||
use crate::State;
|
||||
use smithay::{
|
||||
desktop::Window,
|
||||
input::pointer::{
|
||||
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent,
|
||||
GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent,
|
||||
GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData,
|
||||
MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent,
|
||||
},
|
||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||
utils::{Logical, Point},
|
||||
};
|
||||
|
||||
pub struct MoveSurfaceGrab {
|
||||
pub start_data: PointerGrabStartData<State>,
|
||||
pub window: Window,
|
||||
pub initial_window_location: Point<i32, Logical>,
|
||||
}
|
||||
|
||||
impl PointerGrab<State> for MoveSurfaceGrab {
|
||||
fn motion(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
_focus: Option<(WlSurface, Point<f64, Logical>)>,
|
||||
event: &MotionEvent,
|
||||
) {
|
||||
// While the grab is active, no client has pointer focus
|
||||
handle.motion(data, None, event);
|
||||
|
||||
let delta = event.location - self.start_data.location;
|
||||
let new_location = self.initial_window_location.to_f64() + delta;
|
||||
data.space
|
||||
.map_element(self.window.clone(), new_location.to_i32_round(), true);
|
||||
}
|
||||
|
||||
fn relative_motion(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
focus: Option<(WlSurface, 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);
|
||||
|
||||
// The button is a button code as defined in the
|
||||
// Linux kernel's linux/input-event-codes.h header file, e.g. BTN_LEFT.
|
||||
const BTN_LEFT: u32 = 0x110;
|
||||
|
||||
if !handle.current_pressed().contains(&BTN_LEFT) {
|
||||
// No more buttons are pressed, release the grab.
|
||||
handle.unset_grab(self, data, event.serial, event.time, true);
|
||||
}
|
||||
}
|
||||
|
||||
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) -> &PointerGrabStartData<State> {
|
||||
&self.start_data
|
||||
}
|
||||
|
||||
fn unset(&mut self, _data: &mut State) {}
|
||||
}
|
||||
386
src/grabs/resize_grab.rs
Normal file
386
src/grabs/resize_grab.rs
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
//! Resize grab is the state of a composer during which the client window is being resized.
|
||||
//!
|
||||
//! eg. Usually whenever a user clicks on the app's border and starts dragging, the compositors
|
||||
//! enters a ResizeSurfaceGrab state.
|
||||
|
||||
use crate::State;
|
||||
use smithay::{
|
||||
desktop::{Space, Window},
|
||||
input::pointer::{
|
||||
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent,
|
||||
GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent,
|
||||
GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData,
|
||||
MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent,
|
||||
},
|
||||
reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel,
|
||||
wayland_server::protocol::wl_surface::WlSurface,
|
||||
},
|
||||
utils::{Logical, Point, Rectangle, Size},
|
||||
wayland::{compositor, shell::xdg::SurfaceCachedState},
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ResizeEdge: u32 {
|
||||
const TOP = 0b0001;
|
||||
const BOTTOM = 0b0010;
|
||||
const LEFT = 0b0100;
|
||||
const RIGHT = 0b1000;
|
||||
|
||||
const TOP_LEFT = Self::TOP.bits() | Self::LEFT.bits();
|
||||
const BOTTOM_LEFT = Self::BOTTOM.bits() | Self::LEFT.bits();
|
||||
|
||||
const TOP_RIGHT = Self::TOP.bits() | Self::RIGHT.bits();
|
||||
const BOTTOM_RIGHT = Self::BOTTOM.bits() | Self::RIGHT.bits();
|
||||
}
|
||||
}
|
||||
|
||||
impl From<xdg_toplevel::ResizeEdge> for ResizeEdge {
|
||||
#[inline]
|
||||
fn from(x: xdg_toplevel::ResizeEdge) -> Self {
|
||||
Self::from_bits(x as u32).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResizeSurfaceGrab {
|
||||
start_data: PointerGrabStartData<State>,
|
||||
window: Window,
|
||||
|
||||
edges: ResizeEdge,
|
||||
|
||||
initial_rect: Rectangle<i32, Logical>,
|
||||
last_window_size: Size<i32, Logical>,
|
||||
}
|
||||
|
||||
impl ResizeSurfaceGrab {
|
||||
pub fn start(
|
||||
start_data: PointerGrabStartData<State>,
|
||||
window: Window,
|
||||
edges: ResizeEdge,
|
||||
initial_window_rect: Rectangle<i32, Logical>,
|
||||
) -> Self {
|
||||
let initial_rect = initial_window_rect;
|
||||
|
||||
if let Some(toplevel) = window.toplevel() {
|
||||
ResizeSurfaceState::with(toplevel.wl_surface(), |state| {
|
||||
*state = ResizeSurfaceState::Resizing {
|
||||
edges,
|
||||
initial_rect,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
Self {
|
||||
start_data,
|
||||
window,
|
||||
edges,
|
||||
initial_rect,
|
||||
last_window_size: initial_rect.size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerGrab<State> for ResizeSurfaceGrab {
|
||||
fn motion(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
_focus: Option<(WlSurface, Point<f64, Logical>)>,
|
||||
event: &MotionEvent,
|
||||
) {
|
||||
// While the grab is active, no client has pointer focus
|
||||
handle.motion(data, None, event);
|
||||
|
||||
let mut delta = event.location - self.start_data.location;
|
||||
|
||||
let mut new_window_width = self.initial_rect.size.w;
|
||||
let mut new_window_height = self.initial_rect.size.h;
|
||||
|
||||
if self.edges.intersects(ResizeEdge::LEFT | ResizeEdge::RIGHT) {
|
||||
if self.edges.intersects(ResizeEdge::LEFT) {
|
||||
delta.x = -delta.x;
|
||||
}
|
||||
|
||||
new_window_width = (self.initial_rect.size.w as f64 + delta.x) as i32;
|
||||
}
|
||||
|
||||
if self.edges.intersects(ResizeEdge::TOP | ResizeEdge::BOTTOM) {
|
||||
if self.edges.intersects(ResizeEdge::TOP) {
|
||||
delta.y = -delta.y;
|
||||
}
|
||||
|
||||
new_window_height = (self.initial_rect.size.h as f64 + delta.y) as i32;
|
||||
}
|
||||
|
||||
let (min_size, max_size) =
|
||||
compositor::with_states(self.window.toplevel().unwrap().wl_surface(), |states| {
|
||||
let mut guard = states.cached_state.get::<SurfaceCachedState>();
|
||||
let data = guard.current();
|
||||
(data.min_size, data.max_size)
|
||||
});
|
||||
|
||||
let min_width = min_size.w.max(1);
|
||||
let min_height = min_size.h.max(1);
|
||||
|
||||
let max_width = if max_size.w == 0 {
|
||||
i32::MAX
|
||||
} else {
|
||||
max_size.w
|
||||
};
|
||||
let max_height = if max_size.h == 0 {
|
||||
i32::MAX
|
||||
} else {
|
||||
max_size.h
|
||||
};
|
||||
|
||||
self.last_window_size = Size::from((
|
||||
new_window_width.max(min_width).min(max_width),
|
||||
new_window_height.max(min_height).min(max_height),
|
||||
));
|
||||
|
||||
let xdg = self.window.toplevel().unwrap();
|
||||
xdg.with_pending_state(|state| {
|
||||
state.states.set(xdg_toplevel::State::Resizing);
|
||||
state.size = Some(self.last_window_size);
|
||||
});
|
||||
|
||||
xdg.send_pending_configure();
|
||||
}
|
||||
|
||||
fn relative_motion(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
focus: Option<(WlSurface, 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);
|
||||
|
||||
// The button is a button code as defined in the
|
||||
// Linux kernel's linux/input-event-codes.h header file, e.g. BTN_LEFT.
|
||||
const BTN_LEFT: u32 = 0x110;
|
||||
|
||||
if !handle.current_pressed().contains(&BTN_LEFT) {
|
||||
// No more buttons are pressed, release the grab.
|
||||
handle.unset_grab(self, data, event.serial, event.time, true);
|
||||
|
||||
if let Some(xdg) = self.window.toplevel() {
|
||||
xdg.with_pending_state(|state| {
|
||||
state.states.unset(xdg_toplevel::State::Resizing);
|
||||
state.size = Some(self.last_window_size);
|
||||
});
|
||||
|
||||
xdg.send_pending_configure();
|
||||
ResizeSurfaceState::with(xdg.wl_surface(), |state| {
|
||||
*state = ResizeSurfaceState::WaitingForLastCommit {
|
||||
edges: self.edges,
|
||||
initial_rect: self.initial_rect,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) -> &PointerGrabStartData<State> {
|
||||
&self.start_data
|
||||
}
|
||||
|
||||
fn unset(&mut self, _data: &mut State) {}
|
||||
}
|
||||
|
||||
/// State of the resize operation.
|
||||
///
|
||||
/// It is stored inside of WlSurface,
|
||||
/// and can be accessed using [`ResizeSurfaceState::with`]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
|
||||
enum ResizeSurfaceState {
|
||||
#[default]
|
||||
Idle,
|
||||
Resizing {
|
||||
edges: ResizeEdge,
|
||||
/// The initial window size and location.
|
||||
initial_rect: Rectangle<i32, Logical>,
|
||||
},
|
||||
/// Resize is done, we are now waiting for last commit, to do the final move
|
||||
WaitingForLastCommit {
|
||||
edges: ResizeEdge,
|
||||
/// The initial window size and location.
|
||||
initial_rect: Rectangle<i32, Logical>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ResizeSurfaceState {
|
||||
fn with<F, T>(surface: &WlSurface, cb: F) -> T
|
||||
where
|
||||
F: FnOnce(&mut Self) -> T,
|
||||
{
|
||||
compositor::with_states(surface, |states| {
|
||||
states.data_map.insert_if_missing(RefCell::<Self>::default);
|
||||
let state = states.data_map.get::<RefCell<Self>>().unwrap();
|
||||
|
||||
cb(&mut state.borrow_mut())
|
||||
})
|
||||
}
|
||||
|
||||
fn commit(&mut self) -> Option<(ResizeEdge, Rectangle<i32, Logical>)> {
|
||||
match *self {
|
||||
Self::Resizing {
|
||||
edges,
|
||||
initial_rect,
|
||||
} => Some((edges, initial_rect)),
|
||||
Self::WaitingForLastCommit {
|
||||
edges,
|
||||
initial_rect,
|
||||
} => {
|
||||
// The resize is done, let's go back to idle
|
||||
*self = Self::Idle;
|
||||
|
||||
Some((edges, initial_rect))
|
||||
}
|
||||
Self::Idle => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Should be called on `WlSurface::commit`
|
||||
pub fn handle_commit(space: &mut Space<Window>, surface: &WlSurface) -> Option<()> {
|
||||
let window = space
|
||||
.elements()
|
||||
.filter(|w| w.toplevel().is_some())
|
||||
.find(|w| w.toplevel().unwrap().wl_surface() == surface)
|
||||
.cloned()?;
|
||||
|
||||
let mut window_loc = space.element_location(&window)?;
|
||||
let geometry = window.geometry();
|
||||
|
||||
let new_loc: Point<Option<i32>, Logical> = ResizeSurfaceState::with(surface, |state| {
|
||||
state
|
||||
.commit()
|
||||
.and_then(|(edges, initial_rect)| {
|
||||
// If the window is being resized by top or left, its location must be adjusted
|
||||
// accordingly.
|
||||
edges.intersects(ResizeEdge::TOP_LEFT).then(|| {
|
||||
let new_x = edges
|
||||
.intersects(ResizeEdge::LEFT)
|
||||
.then_some(initial_rect.loc.x + (initial_rect.size.w - geometry.size.w));
|
||||
|
||||
let new_y = edges
|
||||
.intersects(ResizeEdge::TOP)
|
||||
.then_some(initial_rect.loc.y + (initial_rect.size.h - geometry.size.h));
|
||||
|
||||
(new_x, new_y).into()
|
||||
})
|
||||
})
|
||||
.unwrap_or_default()
|
||||
});
|
||||
|
||||
if let Some(new_x) = new_loc.x {
|
||||
window_loc.x = new_x;
|
||||
}
|
||||
if let Some(new_y) = new_loc.y {
|
||||
window_loc.y = new_y;
|
||||
}
|
||||
|
||||
if new_loc.x.is_some() || new_loc.y.is_some() {
|
||||
// If TOP or LEFT side of the window got resized, we have to move it
|
||||
space.map_element(window, window_loc, false);
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
64
src/handlers/compositor.rs
Normal file
64
src/handlers/compositor.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use crate::{grabs::resize_grab, state::ClientState, state::State};
|
||||
use smithay::{
|
||||
backend::renderer::utils::on_commit_buffer_handler,
|
||||
reexports::wayland_server::{
|
||||
protocol::{wl_buffer, wl_surface::WlSurface},
|
||||
Client,
|
||||
},
|
||||
wayland::{
|
||||
buffer::BufferHandler,
|
||||
compositor::{
|
||||
get_parent, is_sync_subsurface, CompositorClientState, CompositorHandler,
|
||||
CompositorState,
|
||||
},
|
||||
shm::{ShmHandler, ShmState},
|
||||
},
|
||||
xwayland::XWaylandClientData,
|
||||
};
|
||||
|
||||
use super::xdg_shell;
|
||||
|
||||
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>() {
|
||||
&state.compositor_state
|
||||
} else {
|
||||
&client.get_data::<ClientState>().unwrap().compositor_state
|
||||
}
|
||||
}
|
||||
|
||||
fn commit(&mut self, surface: &WlSurface) {
|
||||
on_commit_buffer_handler::<Self>(surface);
|
||||
if !is_sync_subsurface(surface) {
|
||||
let mut root = surface.clone();
|
||||
while let Some(parent) = get_parent(&root) {
|
||||
root = parent;
|
||||
}
|
||||
if let Some(window) = self
|
||||
.space
|
||||
.elements()
|
||||
.filter(|w| w.toplevel().is_some())
|
||||
.find(|w| w.toplevel().unwrap().wl_surface() == &root)
|
||||
{
|
||||
window.on_commit();
|
||||
}
|
||||
};
|
||||
|
||||
xdg_shell::handle_commit(&mut self.popups, &self.space, surface);
|
||||
resize_grab::handle_commit(&mut self.space, surface);
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferHandler for State {
|
||||
fn buffer_destroyed(&mut self, _buffer: &wl_buffer::WlBuffer) {}
|
||||
}
|
||||
|
||||
impl ShmHandler for State {
|
||||
fn shm_state(&self) -> &ShmState {
|
||||
&self.shm_state
|
||||
}
|
||||
}
|
||||
92
src/handlers/mod.rs
Normal file
92
src/handlers/mod.rs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
mod compositor;
|
||||
mod xdg_shell;
|
||||
|
||||
use crate::State;
|
||||
|
||||
//
|
||||
// Wl Seat
|
||||
//
|
||||
|
||||
use smithay::input::dnd::{DnDGrab, DndGrabHandler, GrabType, Source};
|
||||
use smithay::input::pointer::Focus;
|
||||
use smithay::input::{Seat, SeatHandler, SeatState};
|
||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
use smithay::reexports::wayland_server::Resource;
|
||||
use smithay::utils::Serial;
|
||||
use smithay::wayland::output::OutputHandler;
|
||||
use smithay::wayland::selection::data_device::{
|
||||
set_data_device_focus, DataDeviceHandler, DataDeviceState, WaylandDndGrabHandler,
|
||||
};
|
||||
use smithay::wayland::selection::SelectionHandler;
|
||||
|
||||
impl SeatHandler for State {
|
||||
type KeyboardFocus = WlSurface;
|
||||
type PointerFocus = WlSurface;
|
||||
type TouchFocus = WlSurface;
|
||||
|
||||
fn seat_state(&mut self) -> &mut SeatState<State> {
|
||||
&mut self.seat_state
|
||||
}
|
||||
|
||||
fn cursor_image(
|
||||
&mut self,
|
||||
_seat: &Seat<Self>,
|
||||
_image: smithay::input::pointer::CursorImageStatus,
|
||||
) {
|
||||
}
|
||||
|
||||
fn focus_changed(&mut self, seat: &Seat<Self>, focused: Option<&WlSurface>) {
|
||||
let dh = &self.display_handle;
|
||||
let client = focused.and_then(|s| dh.get_client(s.id()).ok());
|
||||
set_data_device_focus(dh, seat, client);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Wl Data Device
|
||||
//
|
||||
|
||||
impl SelectionHandler for State {
|
||||
type SelectionUserData = ();
|
||||
}
|
||||
|
||||
impl DataDeviceHandler for State {
|
||||
fn data_device_state(&mut self) -> &mut DataDeviceState {
|
||||
&mut self.data_device_state
|
||||
}
|
||||
}
|
||||
|
||||
impl DndGrabHandler for State {}
|
||||
impl WaylandDndGrabHandler for State {
|
||||
fn dnd_requested<S: Source>(
|
||||
&mut self,
|
||||
source: S,
|
||||
_icon: Option<WlSurface>,
|
||||
seat: Seat<Self>,
|
||||
serial: Serial,
|
||||
type_: GrabType,
|
||||
) {
|
||||
match type_ {
|
||||
GrabType::Pointer => {
|
||||
let ptr = seat.get_pointer().unwrap();
|
||||
let start_data = ptr.grab_start_data().unwrap();
|
||||
|
||||
// create a dnd grab to start the operation
|
||||
let grab = DnDGrab::new_pointer(&self.display_handle, start_data, source, seat);
|
||||
ptr.set_grab(self, grab, serial, Focus::Keep);
|
||||
}
|
||||
GrabType::Touch => {
|
||||
// smallvil lacks touch handling
|
||||
source.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Wl Output & Xdg Output
|
||||
//
|
||||
|
||||
impl OutputHandler for State {}
|
||||
|
||||
smithay::delegate_dispatch2!(State);
|
||||
225
src/handlers/xdg_shell.rs
Normal file
225
src/handlers/xdg_shell.rs
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
use smithay::{
|
||||
desktop::{
|
||||
find_popup_root_surface, get_popup_toplevel_coords, PopupKind, PopupManager, Space, Window,
|
||||
},
|
||||
input::{
|
||||
pointer::{Focus, GrabStartData as PointerGrabStartData},
|
||||
Seat,
|
||||
},
|
||||
reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel,
|
||||
wayland_server::{
|
||||
protocol::{wl_seat, wl_surface::WlSurface},
|
||||
Resource,
|
||||
},
|
||||
},
|
||||
utils::{Rectangle, Serial},
|
||||
wayland::{
|
||||
compositor::with_states,
|
||||
shell::xdg::{
|
||||
PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState,
|
||||
XdgToplevelSurfaceData,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
grabs::{MoveSurfaceGrab, ResizeSurfaceGrab},
|
||||
state::State,
|
||||
};
|
||||
|
||||
impl XdgShellHandler for State {
|
||||
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
|
||||
&mut self.xdg_shell_state
|
||||
}
|
||||
|
||||
fn new_toplevel(&mut self, surface: ToplevelSurface) {
|
||||
let window = Window::new_wayland_window(surface);
|
||||
self.space.map_element(window, (0, 0), false);
|
||||
}
|
||||
|
||||
fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) {
|
||||
self.unconstrain_popup(&surface);
|
||||
let _ = self.popups.track_popup(PopupKind::Xdg(surface));
|
||||
}
|
||||
|
||||
fn reposition_request(
|
||||
&mut self,
|
||||
surface: PopupSurface,
|
||||
positioner: PositionerState,
|
||||
token: u32,
|
||||
) {
|
||||
surface.with_pending_state(|state| {
|
||||
let geometry = positioner.get_geometry();
|
||||
state.geometry = geometry;
|
||||
state.positioner = positioner;
|
||||
});
|
||||
self.unconstrain_popup(&surface);
|
||||
surface.send_repositioned(token);
|
||||
}
|
||||
|
||||
fn move_request(&mut self, surface: ToplevelSurface, seat: wl_seat::WlSeat, serial: Serial) {
|
||||
let seat = Seat::from_resource(&seat).unwrap();
|
||||
|
||||
let wl_surface = surface.wl_surface();
|
||||
|
||||
if let Some(start_data) = check_grab(&seat, wl_surface, serial) {
|
||||
let pointer = seat.get_pointer().unwrap();
|
||||
|
||||
let window = self
|
||||
.space
|
||||
.elements()
|
||||
.filter(|w| w.toplevel().is_some())
|
||||
.find(|w| w.toplevel().unwrap().wl_surface() == wl_surface)
|
||||
.unwrap()
|
||||
.clone();
|
||||
let initial_window_location = self.space.element_location(&window).unwrap();
|
||||
|
||||
let grab = MoveSurfaceGrab {
|
||||
start_data,
|
||||
window,
|
||||
initial_window_location,
|
||||
};
|
||||
|
||||
pointer.set_grab(self, grab, serial, Focus::Clear);
|
||||
}
|
||||
}
|
||||
|
||||
fn resize_request(
|
||||
&mut self,
|
||||
surface: ToplevelSurface,
|
||||
seat: wl_seat::WlSeat,
|
||||
serial: Serial,
|
||||
edges: xdg_toplevel::ResizeEdge,
|
||||
) {
|
||||
let seat = Seat::from_resource(&seat).unwrap();
|
||||
|
||||
let wl_surface = surface.wl_surface();
|
||||
|
||||
if let Some(start_data) = check_grab(&seat, wl_surface, serial) {
|
||||
let pointer = seat.get_pointer().unwrap();
|
||||
|
||||
let window = self
|
||||
.space
|
||||
.elements()
|
||||
.filter(|w| w.toplevel().is_some())
|
||||
.find(|w| w.toplevel().unwrap().wl_surface() == wl_surface)
|
||||
.unwrap()
|
||||
.clone();
|
||||
let initial_window_location = self.space.element_location(&window).unwrap();
|
||||
let initial_window_size = window.geometry().size;
|
||||
|
||||
surface.with_pending_state(|state| {
|
||||
state.states.set(xdg_toplevel::State::Resizing);
|
||||
});
|
||||
|
||||
surface.send_pending_configure();
|
||||
|
||||
let grab = ResizeSurfaceGrab::start(
|
||||
start_data,
|
||||
window,
|
||||
edges.into(),
|
||||
Rectangle::new(initial_window_location, initial_window_size),
|
||||
);
|
||||
|
||||
pointer.set_grab(self, grab, serial, Focus::Clear);
|
||||
}
|
||||
}
|
||||
|
||||
fn grab(&mut self, _surface: PopupSurface, _seat: wl_seat::WlSeat, _serial: Serial) {
|
||||
// TODO popup grabs
|
||||
}
|
||||
}
|
||||
|
||||
fn check_grab(
|
||||
seat: &Seat<State>,
|
||||
surface: &WlSurface,
|
||||
serial: Serial,
|
||||
) -> Option<PointerGrabStartData<State>> {
|
||||
let pointer = seat.get_pointer()?;
|
||||
|
||||
// Check that this surface has a click grab.
|
||||
if !pointer.has_grab(serial) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let start_data = pointer.grab_start_data()?;
|
||||
|
||||
let (focus, _) = start_data.focus.as_ref()?;
|
||||
// If the focus was for a different surface, ignore the request.
|
||||
if !focus.id().same_client_as(&surface.id()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(start_data)
|
||||
}
|
||||
|
||||
/// Should be called on `WlSurface::commit`
|
||||
pub fn handle_commit(popups: &mut PopupManager, space: &Space<Window>, surface: &WlSurface) {
|
||||
// Handle toplevel commits.
|
||||
if let Some(window) = space
|
||||
.elements()
|
||||
.filter(|w| w.toplevel().is_some())
|
||||
.find(|w| w.toplevel().unwrap().wl_surface() == surface)
|
||||
.cloned()
|
||||
{
|
||||
let initial_configure_sent = with_states(surface, |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.initial_configure_sent
|
||||
});
|
||||
|
||||
if !initial_configure_sent {
|
||||
window.toplevel().unwrap().send_configure();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle popup commits.
|
||||
popups.commit(surface);
|
||||
if let Some(popup) = popups.find_popup(surface) {
|
||||
match popup {
|
||||
PopupKind::Xdg(ref xdg) => {
|
||||
if !xdg.is_initial_configure_sent() {
|
||||
// NOTE: This should never fail as the initial configure is always
|
||||
// allowed.
|
||||
xdg.send_configure().expect("initial configure failed");
|
||||
}
|
||||
}
|
||||
PopupKind::InputMethod(ref _input_method) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn unconstrain_popup(&self, popup: &PopupSurface) {
|
||||
let Ok(root) = find_popup_root_surface(&PopupKind::Xdg(popup.clone())) else {
|
||||
return;
|
||||
};
|
||||
let Some(window) = self
|
||||
.space
|
||||
.elements()
|
||||
.filter(|w| w.toplevel().is_some())
|
||||
.find(|w| w.toplevel().unwrap().wl_surface() == &root)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let output = self.space.outputs().next().unwrap();
|
||||
let output_geo = self.space.output_geometry(output).unwrap();
|
||||
let window_geo = self.space.element_geometry(window).unwrap();
|
||||
|
||||
// The target geometry for the positioner should be relative to its parent's geometry, so
|
||||
// we will compute that here.
|
||||
let mut target = output_geo;
|
||||
target.loc -= get_popup_toplevel_coords(&PopupKind::Xdg(popup.clone()));
|
||||
target.loc -= window_geo.loc;
|
||||
|
||||
popup.with_pending_state(|state| {
|
||||
state.geometry = state.positioner.get_unconstrained_geometry(target);
|
||||
});
|
||||
}
|
||||
}
|
||||
146
src/input.rs
Normal file
146
src/input.rs
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
use smithay::{
|
||||
backend::input::{
|
||||
AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputBackend, InputEvent,
|
||||
KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent,
|
||||
},
|
||||
input::{
|
||||
keyboard::FilterResult,
|
||||
pointer::{AxisFrame, ButtonEvent, MotionEvent},
|
||||
},
|
||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||
utils::SERIAL_COUNTER,
|
||||
};
|
||||
|
||||
use crate::state::State;
|
||||
|
||||
impl State {
|
||||
pub fn process_input_event<I: InputBackend>(&mut self, event: InputEvent<I>) {
|
||||
match event {
|
||||
InputEvent::Keyboard { event, .. } => {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let time = Event::time_msec(&event);
|
||||
|
||||
self.seat.get_keyboard().unwrap().input::<(), _>(
|
||||
self,
|
||||
event.key_code(),
|
||||
event.state(),
|
||||
serial,
|
||||
time,
|
||||
|_, _, _| FilterResult::Forward,
|
||||
);
|
||||
}
|
||||
InputEvent::PointerMotion { .. } => {}
|
||||
InputEvent::PointerMotionAbsolute { event, .. } => {
|
||||
let output = self.space.outputs().next().unwrap();
|
||||
|
||||
let output_geo = self.space.output_geometry(output).unwrap();
|
||||
|
||||
let pos = event.position_transformed(output_geo.size) + output_geo.loc.to_f64();
|
||||
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
|
||||
let pointer = self.seat.get_pointer().unwrap();
|
||||
|
||||
let under = self.surface_under(pos);
|
||||
|
||||
pointer.motion(
|
||||
self,
|
||||
under,
|
||||
&MotionEvent {
|
||||
location: pos,
|
||||
serial,
|
||||
time: event.time_msec(),
|
||||
},
|
||||
);
|
||||
pointer.frame(self);
|
||||
}
|
||||
InputEvent::PointerButton { event, .. } => {
|
||||
let pointer = self.seat.get_pointer().unwrap();
|
||||
let keyboard = self.seat.get_keyboard().unwrap();
|
||||
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
|
||||
let button = event.button_code();
|
||||
|
||||
let button_state = event.state();
|
||||
|
||||
if ButtonState::Pressed == button_state && !pointer.is_grabbed() {
|
||||
if let Some((window, _loc)) = self
|
||||
.space
|
||||
.element_under(pointer.current_location())
|
||||
.map(|(w, l)| (w.clone(), l))
|
||||
{
|
||||
self.space.raise_element(&window, true);
|
||||
if let Some(toplevel) = window.toplevel() {
|
||||
keyboard.set_focus(self, Some(toplevel.wl_surface().clone()), serial);
|
||||
}
|
||||
self.space.elements().for_each(|window| {
|
||||
if let Some(toplevel) = window.toplevel() {
|
||||
toplevel.send_pending_configure();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
self.space.elements().for_each(|window| {
|
||||
window.set_activated(false);
|
||||
if let Some(toplevel) = window.toplevel() {
|
||||
toplevel.send_pending_configure();
|
||||
}
|
||||
});
|
||||
keyboard.set_focus(self, Option::<WlSurface>::None, serial);
|
||||
}
|
||||
};
|
||||
|
||||
pointer.button(
|
||||
self,
|
||||
&ButtonEvent {
|
||||
button,
|
||||
state: button_state,
|
||||
serial,
|
||||
time: event.time_msec(),
|
||||
},
|
||||
);
|
||||
pointer.frame(self);
|
||||
}
|
||||
InputEvent::PointerAxis { event, .. } => {
|
||||
let source = event.source();
|
||||
|
||||
let horizontal_amount = event.amount(Axis::Horizontal).unwrap_or_else(|| {
|
||||
event.amount_v120(Axis::Horizontal).unwrap_or(0.0) * 15.0 / 120.
|
||||
});
|
||||
let vertical_amount = event.amount(Axis::Vertical).unwrap_or_else(|| {
|
||||
event.amount_v120(Axis::Vertical).unwrap_or(0.0) * 15.0 / 120.
|
||||
});
|
||||
let horizontal_amount_discrete = event.amount_v120(Axis::Horizontal);
|
||||
let vertical_amount_discrete = event.amount_v120(Axis::Vertical);
|
||||
|
||||
let mut frame = AxisFrame::new(event.time_msec()).source(source);
|
||||
if horizontal_amount != 0.0 {
|
||||
frame = frame.value(Axis::Horizontal, horizontal_amount);
|
||||
if let Some(discrete) = horizontal_amount_discrete {
|
||||
frame = frame.v120(Axis::Horizontal, discrete as i32);
|
||||
}
|
||||
}
|
||||
if vertical_amount != 0.0 {
|
||||
frame = frame.value(Axis::Vertical, vertical_amount);
|
||||
if let Some(discrete) = vertical_amount_discrete {
|
||||
frame = frame.v120(Axis::Vertical, discrete as i32);
|
||||
}
|
||||
}
|
||||
|
||||
if source == AxisSource::Finger {
|
||||
if event.amount(Axis::Horizontal) == Some(0.0) {
|
||||
frame = frame.stop(Axis::Horizontal);
|
||||
}
|
||||
if event.amount(Axis::Vertical) == Some(0.0) {
|
||||
frame = frame.stop(Axis::Vertical);
|
||||
}
|
||||
}
|
||||
|
||||
let pointer = self.seat.get_pointer().unwrap();
|
||||
pointer.axis(self, frame);
|
||||
pointer.frame(self);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
src/main.rs
59
src/main.rs
|
|
@ -1,8 +1,59 @@
|
|||
use backend::winit::run_winit;
|
||||
#![allow(irrefutable_let_patterns)]
|
||||
|
||||
pub mod backend;
|
||||
pub mod state;
|
||||
mod handlers;
|
||||
|
||||
mod grabs;
|
||||
mod input;
|
||||
mod state;
|
||||
mod winit;
|
||||
mod xwayland;
|
||||
|
||||
use smithay::reexports::{calloop::EventLoop, wayland_server::Display, winit::event_loop};
|
||||
pub use state::State;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
run_winit()
|
||||
init_logging();
|
||||
|
||||
let mut event_loop = EventLoop::try_new()?;
|
||||
let display: Display<State> = Display::new()?;
|
||||
|
||||
let mut state = State::new(display, event_loop.handle());
|
||||
|
||||
// Open a Wayland/X11 window for our nested compositor
|
||||
crate::winit::init_winit(&mut state)?;
|
||||
|
||||
// Set WAYLAND_DISPLAY to our socket name, so child processes connect to Smallvil rather
|
||||
// than the host compositor
|
||||
unsafe { std::env::set_var("WAYLAND_DISPLAY", &state.socket_name) };
|
||||
|
||||
spawn_client();
|
||||
|
||||
event_loop.run(None, &mut state, move |_| {
|
||||
// Smallvil is running
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_logging() {
|
||||
if let Ok(env_filter) = tracing_subscriber::EnvFilter::try_from_default_env() {
|
||||
tracing_subscriber::fmt().with_env_filter(env_filter).init();
|
||||
} else {
|
||||
tracing_subscriber::fmt().init();
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_client() {
|
||||
let mut args = std::env::args().skip(1);
|
||||
let flag = args.next();
|
||||
let arg = args.next();
|
||||
|
||||
match (flag.as_deref(), arg) {
|
||||
// (Some("-c") | Some("--command"), Some(command)) => {
|
||||
// std::process::Command::new(command).spawn().ok();
|
||||
// }
|
||||
_ => {
|
||||
std::process::Command::new("weston-terminal").spawn().ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
188
src/state.rs
Normal file
188
src/state.rs
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
use std::{ffi::OsString, sync::Arc, time::Duration};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use smithay::{
|
||||
desktop::{PopupManager, Space, Window, WindowSurfaceType},
|
||||
input::{Seat, SeatState},
|
||||
reexports::{
|
||||
calloop::{
|
||||
generic::Generic, EventLoop, Interest, LoopHandle, LoopSignal, Mode, PostAction,
|
||||
},
|
||||
wayland_server::{
|
||||
backend::{ClientData, ClientId, DisconnectReason},
|
||||
protocol::wl_surface::WlSurface,
|
||||
Display, DisplayHandle,
|
||||
},
|
||||
},
|
||||
utils::{Logical, Point},
|
||||
wayland::{
|
||||
compositor::{CompositorClientState, CompositorState},
|
||||
output::OutputManagerState,
|
||||
selection::data_device::DataDeviceState,
|
||||
shell::xdg::XdgShellState,
|
||||
shm::ShmState,
|
||||
socket::ListeningSocketSource,
|
||||
xwayland_shell::XWaylandShellState,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::xwayland::XWaylandState;
|
||||
|
||||
pub struct State {
|
||||
pub start_time: std::time::Instant,
|
||||
pub socket_name: OsString,
|
||||
pub display_handle: DisplayHandle,
|
||||
|
||||
pub space: Space<Window>,
|
||||
pub handle: LoopHandle<'static, Self>,
|
||||
// pub loop_signal: LoopSignal,
|
||||
|
||||
// Smithay State
|
||||
pub compositor_state: CompositorState,
|
||||
pub xdg_shell_state: XdgShellState,
|
||||
pub shm_state: ShmState,
|
||||
pub output_manager_state: OutputManagerState,
|
||||
pub seat_state: SeatState<State>,
|
||||
pub data_device_state: DataDeviceState,
|
||||
pub popups: PopupManager,
|
||||
|
||||
pub seat: Seat<Self>,
|
||||
|
||||
pub xwayland_state: XWaylandState,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new(display: Display<Self>, handle: LoopHandle<'static, Self>) -> Self {
|
||||
let start_time = std::time::Instant::now();
|
||||
|
||||
let dh = display.handle();
|
||||
|
||||
// Here we initialize implementations of some wayland protocols
|
||||
// Some of them require us to implement traits on the Smallvil state,
|
||||
// you can find those implementations in the `crate::handlers` module
|
||||
|
||||
// Initialize protocols needed for displaying windows
|
||||
let compositor_state = CompositorState::new::<Self>(&dh);
|
||||
let xdg_shell_state = XdgShellState::new::<Self>(&dh);
|
||||
let shm_state = ShmState::new::<Self>(&dh, vec![]);
|
||||
let popups = PopupManager::default();
|
||||
|
||||
let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&dh);
|
||||
|
||||
// Data device is responsible for clipboard and drag-and-drop
|
||||
let data_device_state = DataDeviceState::new::<Self>(&dh);
|
||||
|
||||
// A seat is a group of keyboards, pointer and touch devices.
|
||||
// A seat typically has a pointer and maintains a keyboard focus and a pointer focus.
|
||||
let mut seat_state = SeatState::new();
|
||||
let mut seat: Seat<Self> = seat_state.new_wl_seat(&dh, "winit");
|
||||
|
||||
// Notify clients that we have a keyboard, for the sake of the example we assume that keyboard is always present.
|
||||
// You may want to track keyboard hot-plug in real compositor.
|
||||
seat.add_keyboard(Default::default(), 200, 25).unwrap();
|
||||
|
||||
// Notify clients that we have a pointer (mouse)
|
||||
// Here we assume that there is always pointer plugged in
|
||||
seat.add_pointer();
|
||||
|
||||
// A space represents a two-dimensional plane. Windows and Outputs can be mapped onto it.
|
||||
//
|
||||
// Windows get a position and stacking order through mapping.
|
||||
// Outputs become views of a part of the Space and can be rendered via Space::render_output.
|
||||
let space = Space::default();
|
||||
Self::setup_xwayland(display.handle(), handle.clone());
|
||||
|
||||
let socket_name = Self::init_wayland_listener(display, handle.clone());
|
||||
|
||||
let xwayland_state = XWaylandState {
|
||||
xwm: None,
|
||||
xwayland_shell_state: XWaylandShellState::new::<State>(&dh),
|
||||
};
|
||||
|
||||
Self {
|
||||
start_time,
|
||||
display_handle: dh,
|
||||
|
||||
space,
|
||||
socket_name,
|
||||
|
||||
compositor_state,
|
||||
xdg_shell_state,
|
||||
shm_state,
|
||||
output_manager_state,
|
||||
seat_state,
|
||||
data_device_state,
|
||||
popups,
|
||||
seat,
|
||||
xwayland_state,
|
||||
handle,
|
||||
}
|
||||
}
|
||||
|
||||
fn init_wayland_listener(
|
||||
display: Display<State>,
|
||||
loop_handle: LoopHandle<'static, Self>,
|
||||
) -> OsString {
|
||||
// Creates a new listening socket, automatically choosing the next available `wayland` socket name.
|
||||
let listening_socket = ListeningSocketSource::new_auto().unwrap();
|
||||
|
||||
// Get the name of the listening socket.
|
||||
// Clients will connect to this socket.
|
||||
let socket_name = listening_socket.socket_name().to_os_string();
|
||||
|
||||
loop_handle
|
||||
.insert_source(listening_socket, move |client_stream, _, state| {
|
||||
// Inside the callback, you should insert the client into the display.
|
||||
//
|
||||
// You may also associate some data with the client when inserting the client.
|
||||
state
|
||||
.display_handle
|
||||
.insert_client(client_stream, Arc::new(ClientState::default()))
|
||||
.unwrap();
|
||||
})
|
||||
.expect("Failed to init the wayland event source.");
|
||||
|
||||
// You also need to add the display itself to the event loop, so that client events will be processed by wayland-server.
|
||||
loop_handle
|
||||
.insert_source(
|
||||
Generic::new(display, Interest::READ, Mode::Level),
|
||||
|_, display, state| {
|
||||
// Safety: we don't drop the display
|
||||
unsafe {
|
||||
display.get_mut().dispatch_clients(state).unwrap();
|
||||
}
|
||||
Ok(PostAction::Continue)
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
socket_name
|
||||
}
|
||||
|
||||
pub fn surface_under(
|
||||
&self,
|
||||
pos: Point<f64, Logical>,
|
||||
) -> Option<(WlSurface, Point<f64, Logical>)> {
|
||||
self.space
|
||||
.element_under(pos)
|
||||
.and_then(|(window, location)| {
|
||||
window
|
||||
.surface_under(pos - location.to_f64(), WindowSurfaceType::ALL)
|
||||
.map(|(s, p)| (s, (p + location).to_f64()))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {}
|
||||
}
|
||||
|
||||
/// Data associated with a wayland client that connects to Smallvil.
|
||||
/// One instance of this type per client.
|
||||
#[derive(Default)]
|
||||
pub struct ClientState {
|
||||
pub compositor_state: CompositorClientState,
|
||||
}
|
||||
|
||||
impl ClientData for ClientState {
|
||||
fn initialized(&self, _client_id: ClientId) {}
|
||||
fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {}
|
||||
}
|
||||
468
src/state/mod.rs
468
src/state/mod.rs
|
|
@ -1,468 +0,0 @@
|
|||
pub mod xdg;
|
||||
pub mod xwayland;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use std::{sync::Arc, time::Instant};
|
||||
|
||||
use smithay::{
|
||||
backend::{
|
||||
input::ButtonState,
|
||||
renderer::{
|
||||
element::{
|
||||
surface::{render_elements_from_surface_tree, WaylandSurfaceRenderElement},
|
||||
AsRenderElements, Kind,
|
||||
},
|
||||
gles::GlesRenderer,
|
||||
utils::{draw_render_elements, on_commit_buffer_handler},
|
||||
Color32F, Frame, Renderer,
|
||||
},
|
||||
},
|
||||
delegate_dispatch2,
|
||||
desktop::{
|
||||
layer_map_for_output, space::SpaceElement, LayerSurface, Space, Window, WindowSurfaceType,
|
||||
},
|
||||
input::{
|
||||
dnd::DndGrabHandler,
|
||||
pointer::{ButtonEvent, Focus, GrabStartData, MotionEvent, PointerHandle},
|
||||
Seat, SeatHandler, SeatState,
|
||||
},
|
||||
output::{Mode, Output, PhysicalProperties, Subpixel},
|
||||
reexports::{
|
||||
calloop::{EventLoop, LoopHandle},
|
||||
rustix::fs::Stat,
|
||||
wayland_server::Display,
|
||||
},
|
||||
utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Size, Transform},
|
||||
wayland::{
|
||||
buffer::BufferHandler,
|
||||
compositor::{
|
||||
with_surface_tree_downward, CompositorClientState, CompositorHandler, CompositorState,
|
||||
SurfaceAttributes, TraversalAction,
|
||||
},
|
||||
output::OutputHandler,
|
||||
selection::{
|
||||
data_device::{DataDeviceHandler, DataDeviceState, WaylandDndGrabHandler},
|
||||
SelectionHandler,
|
||||
},
|
||||
shell::xdg::{ToplevelSurface, XdgShellHandler, XdgShellState},
|
||||
shm::{ShmHandler, ShmState},
|
||||
xwayland_shell::XWaylandShellState,
|
||||
},
|
||||
xwayland::{X11Surface, XWaylandClientData},
|
||||
};
|
||||
use wayland_server::{
|
||||
backend::{ClientData, ClientId, DisconnectReason},
|
||||
protocol::{
|
||||
wl_buffer,
|
||||
wl_surface::{self, WlSurface},
|
||||
},
|
||||
Client, ListeningSocket,
|
||||
};
|
||||
|
||||
use crate::state::{xdg::MoveGrab, xwayland::XWaylandState};
|
||||
|
||||
pub struct State {
|
||||
output: Output,
|
||||
pub seat: Seat<Self>,
|
||||
|
||||
pub space: Mutex<Space<AppWrapper>>,
|
||||
clients: Mutex<Vec<Client>>,
|
||||
|
||||
pub pointer_location: Point<f64, Logical>,
|
||||
|
||||
start_time: Instant,
|
||||
listener: ListeningSocket,
|
||||
|
||||
pub event_loop_handle: LoopHandle<'static, Self>,
|
||||
pub xwayland_state: XWaylandState,
|
||||
|
||||
pointer: Arc<PointerHandle<State>>,
|
||||
|
||||
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>,
|
||||
size: Size<i32, Physical>,
|
||||
) -> 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 mut seat = seat_state.new_wl_seat(&dh, "milkyway_seat");
|
||||
let mut space = Space::default();
|
||||
let listener = ListeningSocket::bind("wayland-5").unwrap();
|
||||
|
||||
let event_loop_handle = event_loop.handle();
|
||||
|
||||
let pointer = Arc::new(seat.add_pointer());
|
||||
|
||||
let mode = Mode {
|
||||
size,
|
||||
refresh: 60_000,
|
||||
};
|
||||
let output = Output::new(
|
||||
"winit".to_string(),
|
||||
PhysicalProperties {
|
||||
size: (0, 0).into(),
|
||||
subpixel: Subpixel::Unknown,
|
||||
make: "Smithay".into(),
|
||||
model: "Winit".into(),
|
||||
serial_number: "Unknown".into(),
|
||||
},
|
||||
);
|
||||
let _global = output.create_global::<Self>(&dh);
|
||||
output.change_current_state(
|
||||
Some(mode),
|
||||
Some(Transform::Flipped180),
|
||||
None,
|
||||
Some((0, 0).into()),
|
||||
);
|
||||
output.set_preferred(mode);
|
||||
|
||||
space.map_output(&output, (0, 0));
|
||||
|
||||
Ok(State {
|
||||
output,
|
||||
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,
|
||||
pointer_location: 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| {
|
||||
let loc = space.element_location(element).unwrap_or(Point::new(0, 0));
|
||||
let position =
|
||||
loc.to_physical_precise_round(self.output.current_scale().fractional_scale());
|
||||
element
|
||||
.window
|
||||
.render_elements(renderer, position, Scale { x: 1.0, y: 1.0 }, 1.0)
|
||||
})
|
||||
.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) {
|
||||
let pointer = self.pointer.clone();
|
||||
pointer.button(self, &ev);
|
||||
pointer.frame(self);
|
||||
}
|
||||
|
||||
pub fn surface_under(
|
||||
&self,
|
||||
pos: Point<f64, Logical>,
|
||||
) -> Option<(WlSurface, Point<f64, Logical>)> {
|
||||
self.space
|
||||
.lock()
|
||||
.element_under(pos)
|
||||
.and_then(|(window, location)| {
|
||||
window
|
||||
.window
|
||||
.surface_under(pos - location.to_f64(), WindowSurfaceType::ALL)
|
||||
.map(|(s, p)| (s, (p + location).to_f64()))
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
// self.space
|
||||
// .lock()
|
||||
// .element_under(self.pointer_location)
|
||||
// .and_then(|(window, _location)| Some(window.clone()))
|
||||
}
|
||||
|
||||
pub fn motion_event(&mut self, ev: MotionEvent, locationrel: Point<f64, Logical>) {
|
||||
let pointer = self.pointer.clone();
|
||||
self.pointer_location = ev.location;
|
||||
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 SelectionHandler for State {
|
||||
type SelectionUserData = ();
|
||||
}
|
||||
impl WaylandDndGrabHandler for State {}
|
||||
|
||||
impl DataDeviceHandler for State {
|
||||
fn data_device_state(&mut self) -> &mut DataDeviceState {
|
||||
&mut 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,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl DndGrabHandler for 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");
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputHandler for State {}
|
||||
|
||||
// Macros used to delegate protocol handling to types in the app state.
|
||||
delegate_dispatch2!(State);
|
||||
283
src/state/xdg.rs
283
src/state/xdg.rs
|
|
@ -1,283 +0,0 @@
|
|||
use smithay::desktop::Window;
|
||||
use smithay::input::pointer::{
|
||||
AxisFrame, ButtonEvent, Focus, GestureHoldBeginEvent, GestureHoldEndEvent,
|
||||
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent,
|
||||
GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData, MotionEvent, PointerGrab,
|
||||
PointerInnerHandle, RelativeMotionEvent,
|
||||
};
|
||||
use smithay::input::SeatHandler;
|
||||
use smithay::utils::{Logical, Point};
|
||||
use smithay::wayland::shell::xdg::PopupSurface;
|
||||
use smithay::{
|
||||
utils::Serial,
|
||||
wayland::shell::xdg::{PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState},
|
||||
};
|
||||
use wayland_protocols::xdg::shell::server::xdg_toplevel;
|
||||
use wayland_server::protocol::{wl_output, wl_seat};
|
||||
|
||||
use super::{AppWrapper, State};
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fn move_request(&mut self, wl_surface: ToplevelSurface, seat: wl_seat::WlSeat, serial: Serial) {
|
||||
let window = {
|
||||
let space = self.space.lock();
|
||||
space
|
||||
.elements()
|
||||
.find(|w| {
|
||||
if let Some(surface) = w.surface.clone() {
|
||||
surface == wl_surface
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.clone()
|
||||
};
|
||||
let pointer = self.pointer.clone();
|
||||
let loc = {
|
||||
let space = self.space.lock();
|
||||
space.element_location(&window).unwrap()
|
||||
};
|
||||
|
||||
let start_data = self.pointer.grab_start_data().unwrap();
|
||||
|
||||
pointer.set_grab(
|
||||
self,
|
||||
MoveGrab {
|
||||
start_data,
|
||||
window: window.window.clone(),
|
||||
initial_window_location: loc,
|
||||
app: window,
|
||||
},
|
||||
serial,
|
||||
Focus::Clear,
|
||||
);
|
||||
}
|
||||
|
||||
fn resize_request(
|
||||
&mut self,
|
||||
surface: ToplevelSurface,
|
||||
seat: wl_seat::WlSeat,
|
||||
serial: Serial,
|
||||
edges: xdg_toplevel::ResizeEdge,
|
||||
) {
|
||||
// println!(
|
||||
// "resize {:?}",
|
||||
// self.pointer_location_og - self.pointer_location
|
||||
// );
|
||||
}
|
||||
|
||||
// fn maximize_request(&mut self, surface: ToplevelSurface) {
|
||||
// surface.send_configure();
|
||||
// }
|
||||
|
||||
// fn unmaximize_request(&mut self, surface: ToplevelSurface) {}
|
||||
|
||||
// fn fullscreen_request(
|
||||
// &mut self,
|
||||
// surface: ToplevelSurface,
|
||||
// output: Option<wl_output::WlOutput>,
|
||||
// ) {
|
||||
// surface.send_configure();
|
||||
// }
|
||||
|
||||
// fn unfullscreen_request(&mut self, surface: ToplevelSurface) {}
|
||||
|
||||
// fn minimize_request(&mut self, surface: ToplevelSurface) {}
|
||||
|
||||
// fn show_window_menu(
|
||||
// &mut self,
|
||||
// surface: ToplevelSurface,
|
||||
// seat: wl_seat::WlSeat,
|
||||
// serial: Serial,
|
||||
// location: smithay::utils::Point<i32, smithay::utils::Logical>,
|
||||
// ) {
|
||||
// }
|
||||
|
||||
// fn client_destroyed(&mut self, client: smithay::wayland::shell::xdg::ShellClient) {}
|
||||
|
||||
// fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {}
|
||||
|
||||
// fn popup_destroyed(&mut self, surface: PopupSurface) {}
|
||||
|
||||
// fn app_id_changed(&mut self, surface: ToplevelSurface) {}
|
||||
|
||||
// fn title_changed(&mut self, surface: ToplevelSurface) {}
|
||||
|
||||
// fn parent_changed(&mut self, surface: ToplevelSurface) {}
|
||||
}
|
||||
|
||||
pub struct MoveGrab {
|
||||
pub start_data: GrabStartData<State>,
|
||||
pub window: Window,
|
||||
pub initial_window_location: Point<i32, Logical>,
|
||||
pub app: AppWrapper,
|
||||
}
|
||||
|
||||
impl PointerGrab<State> for MoveGrab {
|
||||
fn motion(
|
||||
&mut self,
|
||||
state: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>,
|
||||
event: &MotionEvent,
|
||||
) {
|
||||
handle.motion(state, None, event);
|
||||
let delta = event.location - self.start_data.location;
|
||||
let new_location = self.initial_window_location.to_f64() + delta;
|
||||
|
||||
let mut space = state.space.lock();
|
||||
space.map_element(self.app.clone(), new_location.to_i32_round(), true);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// The button is a button code as defined in the
|
||||
// Linux kernel's linux/input-event-codes.h header file, e.g. BTN_LEFT.
|
||||
const BTN_LEFT: u32 = 0x110;
|
||||
|
||||
if !handle.current_pressed().contains(&BTN_LEFT) {
|
||||
// No more buttons are pressed, release the grab.
|
||||
handle.unset_grab(self, data, event.serial, event.time, true);
|
||||
}
|
||||
}
|
||||
|
||||
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) {}
|
||||
}
|
||||
114
src/winit.rs
Normal file
114
src/winit.rs
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use smithay::{
|
||||
backend::{
|
||||
renderer::{
|
||||
damage::OutputDamageTracker, element::surface::WaylandSurfaceRenderElement,
|
||||
gles::GlesRenderer,
|
||||
},
|
||||
winit::{self, WinitEvent},
|
||||
},
|
||||
output::{Mode, Output, PhysicalProperties, Subpixel},
|
||||
reexports::calloop::EventLoop,
|
||||
utils::{Rectangle, Transform},
|
||||
};
|
||||
|
||||
use crate::State;
|
||||
|
||||
pub fn init_winit(state: &mut State) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let (mut backend, winit) = winit::init()?;
|
||||
|
||||
let mode = Mode {
|
||||
size: backend.window_size(),
|
||||
refresh: 60_000,
|
||||
};
|
||||
|
||||
let output = Output::new(
|
||||
"winit".to_string(),
|
||||
PhysicalProperties {
|
||||
size: (0, 0).into(),
|
||||
subpixel: Subpixel::Unknown,
|
||||
make: "Milkyway".into(),
|
||||
model: "Winit".into(),
|
||||
serial_number: "Unknown".into(),
|
||||
},
|
||||
);
|
||||
let _global = output.create_global::<State>(&state.display_handle);
|
||||
output.change_current_state(
|
||||
Some(mode),
|
||||
Some(Transform::Flipped180),
|
||||
None,
|
||||
Some((0, 0).into()),
|
||||
);
|
||||
output.set_preferred(mode);
|
||||
|
||||
state.space.map_output(&output, (0, 0));
|
||||
|
||||
let mut damage_tracker = OutputDamageTracker::from_output(&output);
|
||||
|
||||
state.handle.insert_source(winit, move |event, _, state| {
|
||||
match event {
|
||||
WinitEvent::Resized { size, .. } => {
|
||||
output.change_current_state(
|
||||
Some(Mode {
|
||||
size,
|
||||
refresh: 60_000,
|
||||
}),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
WinitEvent::Input(event) => state.process_input_event(event),
|
||||
WinitEvent::Redraw => {
|
||||
let size = backend.window_size();
|
||||
let damage = Rectangle::from_size(size);
|
||||
|
||||
{
|
||||
let (renderer, mut framebuffer) = backend.bind().unwrap();
|
||||
smithay::desktop::space::render_output::<
|
||||
_,
|
||||
WaylandSurfaceRenderElement<GlesRenderer>,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
&output,
|
||||
renderer,
|
||||
&mut framebuffer,
|
||||
1.0,
|
||||
0,
|
||||
[&state.space],
|
||||
&[],
|
||||
&mut damage_tracker,
|
||||
[0.07, 0.16, 0.286, 1.0],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
backend.submit(Some(&[damage])).unwrap();
|
||||
|
||||
state.space.elements().for_each(|window| {
|
||||
window.send_frame(
|
||||
&output,
|
||||
state.start_time.elapsed(),
|
||||
Some(Duration::ZERO),
|
||||
|_, _| Some(output.clone()),
|
||||
)
|
||||
});
|
||||
|
||||
state.space.refresh();
|
||||
state.popups.cleanup();
|
||||
let _ = state.display_handle.flush_clients();
|
||||
|
||||
// Ask for redraw to schedule new frame.
|
||||
backend.window().request_redraw();
|
||||
}
|
||||
WinitEvent::CloseRequested => {
|
||||
std::process::exit(0);
|
||||
// state.loop_signal.stop();
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
use std::process::Stdio;
|
||||
|
||||
use smithay::{
|
||||
delegate_dispatch2,
|
||||
desktop::Window,
|
||||
reexports::calloop::{EventLoop, LoopHandle},
|
||||
utils::{Logical, Rectangle},
|
||||
wayland::xwayland_shell::{XWaylandShellHandler, XWaylandShellState},
|
||||
xwayland::{
|
||||
|
|
@ -9,9 +10,9 @@ use smithay::{
|
|||
X11Surface, X11Wm, XWayland, XWaylandEvent, XwmHandler,
|
||||
},
|
||||
};
|
||||
use wayland_server::Display;
|
||||
use wayland_server::{Display, DisplayHandle};
|
||||
|
||||
use crate::state::{AppWrapper, State};
|
||||
use crate::state::State;
|
||||
|
||||
pub struct XWaylandState {
|
||||
pub xwm: Option<X11Wm>,
|
||||
|
|
@ -30,9 +31,8 @@ impl XwmHandler for State {
|
|||
// grant it — the window will be mapped once you set this
|
||||
window.set_mapped(true).unwrap();
|
||||
|
||||
let mut space = self.space.lock();
|
||||
|
||||
space.map_element(AppWrapper::new_x11(window), (0, 0), true);
|
||||
self.space
|
||||
.map_element(Window::new_x11_window(window.clone()), (0, 0), true);
|
||||
|
||||
// insert it into your Space/window management here
|
||||
}
|
||||
|
|
@ -89,10 +89,9 @@ impl XWaylandShellHandler for State {
|
|||
|
||||
impl State {
|
||||
pub fn setup_xwayland(
|
||||
&mut self,
|
||||
display: &Display<State>,
|
||||
dh: DisplayHandle,
|
||||
loop_handle: LoopHandle<'static, Self>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let dh = display.handle();
|
||||
let (xwayland, client) = XWayland::spawn(
|
||||
&dh,
|
||||
None,
|
||||
|
|
@ -104,15 +103,13 @@ impl State {
|
|||
|_| {},
|
||||
)?;
|
||||
|
||||
let event_loop_handle = self.event_loop_handle.clone();
|
||||
event_loop_handle.insert_source(xwayland, move |event, _, state| {
|
||||
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, &dh, x11_socket, client.clone())
|
||||
let wm = X11Wm::start_wm(state.handle.clone(), &dh, x11_socket, client.clone())
|
||||
.expect("failed to start X11 wm");
|
||||
|
||||
state.xwayland_state.xwm = Some(wm);
|
||||
Loading…
Add table
Add a link
Reference in a new issue