Skip to content
Extraits de code Groupes Projets
Vérifiée Valider d0170799 rédigé par salixor's avatar salixor
Parcourir les fichiers

Startup the project (aka just rip off smallvil)

parent
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
/target
Ce diff est replié.
[package]
name = "custom-wayland-compositor"
version = "0.1.0"
edition = "2021"
[dependencies]
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
bitflags = "2.2.1"
[dependencies.smithay]
git = "https://github.com/Smithay/smithay.git"
default-features = false
features = ["backend_winit", "wayland_frontend", "desktop"]
pub mod move_grab;
pub use move_grab::MoveSurfaceGrab;
pub mod resize_grab;
pub use resize_grab::ResizeSurfaceGrab;
use crate::Smallvil;
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<Smallvil>,
pub window: Window,
pub initial_window_location: Point<i32, Logical>,
}
impl PointerGrab<Smallvil> for MoveSurfaceGrab {
fn motion(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
_focus: Option<(WlSurface, Point<i32, 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 Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
focus: Option<(WlSurface, Point<i32, Logical>)>,
event: &RelativeMotionEvent,
) {
handle.relative_motion(data, focus, event);
}
fn button(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
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(data, event.serial, event.time, true);
}
}
fn axis(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
details: AxisFrame,
) {
handle.axis(data, details)
}
fn frame(&mut self, data: &mut Smallvil, handle: &mut PointerInnerHandle<'_, Smallvil>) {
handle.frame(data);
}
fn gesture_swipe_begin(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureSwipeBeginEvent,
) {
handle.gesture_swipe_begin(data, event)
}
fn gesture_swipe_update(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureSwipeUpdateEvent,
) {
handle.gesture_swipe_update(data, event)
}
fn gesture_swipe_end(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureSwipeEndEvent,
) {
handle.gesture_swipe_end(data, event)
}
fn gesture_pinch_begin(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GesturePinchBeginEvent,
) {
handle.gesture_pinch_begin(data, event)
}
fn gesture_pinch_update(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GesturePinchUpdateEvent,
) {
handle.gesture_pinch_update(data, event)
}
fn gesture_pinch_end(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GesturePinchEndEvent,
) {
handle.gesture_pinch_end(data, event)
}
fn gesture_hold_begin(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureHoldBeginEvent,
) {
handle.gesture_hold_begin(data, event)
}
fn gesture_hold_end(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureHoldEndEvent,
) {
handle.gesture_hold_end(data, event)
}
fn start_data(&self) -> &PointerGrabStartData<Smallvil> {
&self.start_data
}
}
use crate::Smallvil;
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<Smallvil>,
window: Window,
edges: ResizeEdge,
initial_rect: Rectangle<i32, Logical>,
last_window_size: Size<i32, Logical>,
}
impl ResizeSurfaceGrab {
pub fn start(
start_data: PointerGrabStartData<Smallvil>,
window: Window,
edges: ResizeEdge,
initial_window_rect: Rectangle<i32, Logical>,
) -> Self {
let initial_rect = initial_window_rect;
ResizeSurfaceState::with(window.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<Smallvil> for ResizeSurfaceGrab {
fn motion(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
_focus: Option<(WlSurface, Point<i32, 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().wl_surface(), |states| {
let data = states.cached_state.current::<SurfaceCachedState>();
(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 = (max_size.w == 0).then(i32::max_value).unwrap_or(max_size.w);
let max_height = (max_size.h == 0).then(i32::max_value).unwrap_or(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();
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 Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
focus: Option<(WlSurface, Point<i32, Logical>)>,
event: &RelativeMotionEvent,
) {
handle.relative_motion(data, focus, event);
}
fn button(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
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(data, event.serial, event.time, true);
let 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 Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
details: AxisFrame,
) {
handle.axis(data, details)
}
fn frame(&mut self, data: &mut Smallvil, handle: &mut PointerInnerHandle<'_, Smallvil>) {
handle.frame(data);
}
fn gesture_swipe_begin(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureSwipeBeginEvent,
) {
handle.gesture_swipe_begin(data, event)
}
fn gesture_swipe_update(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureSwipeUpdateEvent,
) {
handle.gesture_swipe_update(data, event)
}
fn gesture_swipe_end(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureSwipeEndEvent,
) {
handle.gesture_swipe_end(data, event)
}
fn gesture_pinch_begin(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GesturePinchBeginEvent,
) {
handle.gesture_pinch_begin(data, event)
}
fn gesture_pinch_update(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GesturePinchUpdateEvent,
) {
handle.gesture_pinch_update(data, event)
}
fn gesture_pinch_end(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GesturePinchEndEvent,
) {
handle.gesture_pinch_end(data, event)
}
fn gesture_hold_begin(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureHoldBeginEvent,
) {
handle.gesture_hold_begin(data, event)
}
fn gesture_hold_end(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureHoldEndEvent,
) {
handle.gesture_hold_end(data, event)
}
fn start_data(&self) -> &PointerGrabStartData<Smallvil> {
&self.start_data
}
}
/// 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()
.find(|w| w.toplevel().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(())
}
use crate::{grabs::resize_grab, state::ClientState, Smallvil};
use smithay::{
backend::renderer::utils::on_commit_buffer_handler,
delegate_compositor, delegate_shm,
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},
},
};
use super::xdg_shell;
impl CompositorHandler for Smallvil {
fn compositor_state(&mut self) -> &mut CompositorState {
&mut self.compositor_state
}
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
&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().find(|w| w.toplevel().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 Smallvil {
fn buffer_destroyed(&mut self, _buffer: &wl_buffer::WlBuffer) {}
}
impl ShmHandler for Smallvil {
fn shm_state(&self) -> &ShmState {
&self.shm_state
}
}
delegate_compositor!(Smallvil);
delegate_shm!(Smallvil);
mod compositor;
mod xdg_shell;
use crate::Smallvil;
//
// Wl Seat
//
use smithay::input::{Seat, SeatHandler, SeatState};
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::reexports::wayland_server::Resource;
use smithay::wayland::selection::data_device::{
set_data_device_focus, ClientDndGrabHandler, DataDeviceHandler, DataDeviceState, ServerDndGrabHandler,
};
use smithay::wayland::selection::SelectionHandler;
use smithay::{delegate_data_device, delegate_output, delegate_seat};
impl SeatHandler for Smallvil {
type KeyboardFocus = WlSurface;
type PointerFocus = WlSurface;
fn seat_state(&mut self) -> &mut SeatState<Smallvil> {
&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);
}
}
delegate_seat!(Smallvil);
//
// Wl Data Device
//
impl SelectionHandler for Smallvil {
type SelectionUserData = ();
}
impl DataDeviceHandler for Smallvil {
fn data_device_state(&self) -> &DataDeviceState {
&self.data_device_state
}
}
impl ClientDndGrabHandler for Smallvil {}
impl ServerDndGrabHandler for Smallvil {}
delegate_data_device!(Smallvil);
//
// Wl Output & Xdg Output
//
delegate_output!(Smallvil);
use smithay::{
delegate_xdg_shell,
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, XdgPopupSurfaceData, XdgShellHandler,
XdgShellState, XdgToplevelSurfaceData,
},
},
};
use crate::{
grabs::{MoveSurfaceGrab, ResizeSurfaceGrab},
Smallvil,
};
impl XdgShellHandler for Smallvil {
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
&mut self.xdg_shell_state
}
fn new_toplevel(&mut self, surface: ToplevelSurface) {
let window = Window::new(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()
.find(|w| w.toplevel().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()
.find(|w| w.toplevel().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::from_loc_and_size(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
}
}
// Xdg Shell
delegate_xdg_shell!(Smallvil);
fn check_grab(
seat: &Seat<Smallvil>,
surface: &WlSurface,
serial: Serial,
) -> Option<PointerGrabStartData<Smallvil>> {
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()
.find(|w| w.toplevel().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().send_configure();
}
}
// Handle popup commits.
popups.commit(surface);
if let Some(popup) = popups.find_popup(surface) {
match popup {
PopupKind::Xdg(ref xdg) => {
let initial_configure_sent = with_states(surface, |states| {
states
.data_map
.get::<XdgPopupSurfaceData>()
.unwrap()
.lock()
.unwrap()
.initial_configure_sent
});
if !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 Smallvil {
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().find(|w| w.toplevel().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);
});
}
}
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::Smallvil;
impl Smallvil {
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);
keyboard.set_focus(
self,
Some(window.toplevel().wl_surface().clone()),
serial,
);
self.space.elements().for_each(|window| {
window.toplevel().send_pending_configure();
});
} else {
self.space.elements().for_each(|window| {
window.set_activated(false);
window.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) * 3.0 / 120.
});
let vertical_amount = event.amount(Axis::Vertical).unwrap_or_else(|| {
event.amount_v120(Axis::Vertical).unwrap_or(0.0) * 3.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);
}
_ => {}
}
}
}
#![allow(irrefutable_let_patterns)]
mod handlers;
mod grabs;
mod input;
mod state;
mod winit;
use smithay::reexports::{
calloop::EventLoop,
wayland_server::{Display, DisplayHandle},
};
pub use state::Smallvil;
pub struct CalloopData {
state: Smallvil,
display_handle: DisplayHandle,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
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();
}
let mut event_loop: EventLoop<CalloopData> = EventLoop::try_new()?;
let display: Display<Smallvil> = Display::new()?;
let display_handle = display.handle();
let state = Smallvil::new(&mut event_loop, display);
let mut data = CalloopData {
state,
display_handle,
};
crate::winit::init_winit(&mut event_loop, &mut data)?;
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();
}
}
event_loop.run(None, &mut data, move |_| {
// Smallvil is running
})?;
Ok(())
}
use std::{ffi::OsString, sync::Arc};
use smithay::{
desktop::{PopupManager, Space, Window, WindowSurfaceType},
input::{Seat, SeatState},
reexports::{
calloop::{generic::Generic, EventLoop, Interest, 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,
},
};
use crate::CalloopData;
pub struct Smallvil {
pub start_time: std::time::Instant,
pub socket_name: OsString,
pub display_handle: DisplayHandle,
pub space: Space<Window>,
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<Smallvil>,
pub data_device_state: DataDeviceState,
pub popups: PopupManager,
pub seat: Seat<Self>,
}
impl Smallvil {
pub fn new(event_loop: &mut EventLoop<CalloopData>, display: Display<Self>) -> Self {
let start_time = std::time::Instant::now();
let dh = display.handle();
let compositor_state = CompositorState::new::<Self>(&dh);
let xdg_shell_state = XdgShellState::new::<Self>(&dh);
let shm_state = ShmState::new::<Self>(&dh, vec![]);
let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&dh);
let mut seat_state = SeatState::new();
let data_device_state = DataDeviceState::new::<Self>(&dh);
let popups = PopupManager::default();
// 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: 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();
let socket_name = Self::init_wayland_listener(display, event_loop);
// Get the loop signal, used to stop the event loop
let loop_signal = event_loop.get_signal();
Self {
start_time,
display_handle: dh,
space,
loop_signal,
socket_name,
compositor_state,
xdg_shell_state,
shm_state,
output_manager_state,
seat_state,
data_device_state,
popups,
seat,
}
}
fn init_wayland_listener(
display: Display<Smallvil>,
event_loop: &mut EventLoop<CalloopData>,
) -> 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();
let handle = event_loop.handle();
event_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.
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(&mut state.state).unwrap();
}
Ok(PostAction::Continue)
},
)
.unwrap();
socket_name
}
pub fn surface_under(&self, pos: Point<f64, Logical>) -> Option<(WlSurface, Point<i32, 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))
})
}
}
#[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) {}
}
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::{CalloopData, Smallvil};
pub fn init_winit(
event_loop: &mut EventLoop<CalloopData>,
data: &mut CalloopData,
) -> Result<(), Box<dyn std::error::Error>> {
let display_handle = &mut data.display_handle;
let state = &mut data.state;
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: "Smithay".into(),
model: "Winit".into(),
},
);
let _global = output.create_global::<Smallvil>(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);
std::env::set_var("WAYLAND_DISPLAY", &state.socket_name);
event_loop.handle().insert_source(winit, move |event, _, data| {
let display = &mut data.display_handle;
let state = &mut data.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_loc_and_size((0, 0), size);
backend.bind().unwrap();
smithay::desktop::space::render_output::<_, WaylandSurfaceRenderElement<GlesRenderer>, _, _>(
&output,
backend.renderer(),
1.0,
0,
[&state.space],
&[],
&mut damage_tracker,
[0.1, 0.1, 0.1, 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 _ = display.flush_clients();
// Ask for redraw to schedule new frame.
backend.window().request_redraw();
}
WinitEvent::CloseRequested => {
state.loop_signal.stop();
}
_ => (),
};
})?;
Ok(())
}
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter