feat: add Phase 1 interaction protocols

This commit is contained in:
Samuel Onoja committed 2026-09-24 05:46:00 +01:00
1 parent a941b80aa9
commit 37550f447d
8 files changed
+169 -6

No files matched your search

+1 -1
View File
@@ -6,7 +6,7 @@ license.workspace = true
rust-version.workspace = true
[dependencies]
libc.workspace = true
smithay.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
+59
View File
@@ -0,0 +1,59 @@
use std::time::Duration;
use smithay::{
reexports::wayland_server::protocol::wl_surface::WlSurface,
utils::SERIAL_COUNTER,
wayland::xdg_activation::{
XdgActivationHandler, XdgActivationState, XdgActivationToken, XdgActivationTokenData,
},
};
use crate::Ferese;
const TOKEN_MAX_AGE: Duration = Duration::from_secs(10);
impl XdgActivationHandler for Ferese {
fn activation_state(&mut self) -> &mut XdgActivationState {
&mut self.xdg_activation_state
}
fn token_created(&mut self, _token: XdgActivationToken, data: XdgActivationTokenData) -> bool {
data.serial.is_some() && data.timestamp.elapsed() <= TOKEN_MAX_AGE
}
fn request_activation(
&mut self,
token: XdgActivationToken,
data: XdgActivationTokenData,
surface: WlSurface,
) {
let valid = data.serial.is_some() && data.timestamp.elapsed() <= TOKEN_MAX_AGE;
self.xdg_activation_state.remove_token(&token);
if !valid {
tracing::debug!("ignored stale or non-interactive activation request");
return;
}
let Some(window) = self
.space
.elements()
.find(|window| {
window
.toplevel()
.is_some_and(|toplevel| toplevel.wl_surface() == &surface)
})
.cloned()
else {
return;
};
self.space.raise_element(&window, true);
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
keyboard.set_focus(self, Some(surface), SERIAL_COUNTER.next_serial());
self.space.elements().for_each(|window| {
if let Some(toplevel) = window.toplevel() {
toplevel.send_pending_configure();
}
});
}
}
+45 -1
View File
@@ -1,11 +1,18 @@
mod activation;
mod compositor;
mod xdg_shell;
use smithay::{
input::{Seat, SeatHandler, SeatState, pointer::CursorImageStatus},
input::{
Seat, SeatHandler, SeatState,
pointer::{CursorImageStatus, PointerHandle},
},
reexports::wayland_server::{Resource, protocol::wl_surface::WlSurface},
wayland::{
compositor::with_states,
fractional_scale::{FractionalScaleHandler, with_fractional_scale},
output::OutputHandler,
pointer_constraints::{PointerConstraintsHandler, with_pointer_constraint},
selection::{
SelectionHandler,
data_device::{
@@ -41,6 +48,37 @@ impl SeatHandler for Ferese {
impl OutputHandler for Ferese {}
impl FractionalScaleHandler for Ferese {
fn new_fractional_scale(&mut self, surface: WlSurface) {
with_states(&surface, |states| {
with_fractional_scale(states, |scale| scale.set_preferred_scale(1.0));
});
}
}
impl PointerConstraintsHandler for Ferese {
fn new_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle<Self>) {
let focused = pointer
.current_focus()
.is_some_and(|focused| focused == *surface);
if focused {
with_pointer_constraint(surface, pointer, |constraint| {
if let Some(constraint) = constraint {
constraint.activate();
}
});
}
}
fn cursor_position_hint(
&mut self,
_surface: &WlSurface,
_pointer: &PointerHandle<Self>,
_location: smithay::utils::Point<f64, smithay::utils::Logical>,
) {
}
}
impl SelectionHandler for Ferese {
type SelectionUserData = ();
}
@@ -62,9 +100,15 @@ impl PrimarySelectionHandler for Ferese {
smithay::delegate_compositor!(Ferese);
smithay::delegate_data_device!(Ferese);
smithay::delegate_fractional_scale!(Ferese);
smithay::delegate_output!(Ferese);
smithay::delegate_pointer_constraints!(Ferese);
smithay::delegate_presentation!(Ferese);
smithay::delegate_primary_selection!(Ferese);
smithay::delegate_relative_pointer!(Ferese);
smithay::delegate_seat!(Ferese);
smithay::delegate_shm!(Ferese);
smithay::delegate_viewporter!(Ferese);
smithay::delegate_xdg_activation!(Ferese);
smithay::delegate_xdg_decoration!(Ferese);
smithay::delegate_xdg_shell!(Ferese);
+16 -2
View File
@@ -1,11 +1,11 @@
use smithay::{
backend::input::{
AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputBackend, InputEvent,
KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent,
KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent, PointerMotionEvent,
},
input::{
keyboard::FilterResult,
pointer::{AxisFrame, ButtonEvent, MotionEvent},
pointer::{AxisFrame, ButtonEvent, MotionEvent, RelativeMotionEvent},
},
reexports::wayland_server::protocol::wl_surface::WlSurface,
utils::{SERIAL_COUNTER, Serial},
@@ -47,6 +47,20 @@ impl Ferese {
);
pointer.frame(self);
}
InputEvent::PointerMotion { event, .. } => {
let pointer = self.seat.get_pointer().expect("seat has a pointer");
let focus = self.surface_under(pointer.current_location());
pointer.relative_motion(
self,
focus,
&RelativeMotionEvent {
delta: event.delta(),
delta_unaccel: event.delta_unaccel(),
utime: (event.time_msec() as u64).saturating_mul(1_000),
},
);
pointer.frame(self);
}
InputEvent::PointerButton { event, .. } => {
let pointer = self.seat.get_pointer().expect("seat has a pointer");
let serial = SERIAL_COUNTER.next_serial();
+25
View File
@@ -14,12 +14,18 @@ use smithay::{
utils::{Logical, Point},
wayland::{
compositor::{CompositorClientState, CompositorState},
fractional_scale::FractionalScaleManagerState,
output::OutputManagerState,
pointer_constraints::PointerConstraintsState,
presentation::PresentationState,
relative_pointer::RelativePointerManagerState,
selection::{data_device::DataDeviceState, primary_selection::PrimarySelectionState},
shell::xdg::XdgShellState,
shell::xdg::decoration::XdgDecorationState,
shm::ShmState,
socket::ListeningSocketSource,
viewporter::ViewporterState,
xdg_activation::XdgActivationState,
},
};
@@ -34,10 +40,16 @@ pub struct Ferese {
pub compositor_state: CompositorState,
pub data_device_state: DataDeviceState,
pub decoration_state: XdgDecorationState,
pub fractional_scale_state: FractionalScaleManagerState,
pub output_manager_state: OutputManagerState,
pub pointer_constraints_state: PointerConstraintsState,
pub presentation_state: PresentationState,
pub primary_selection_state: PrimarySelectionState,
pub relative_pointer_state: RelativePointerManagerState,
pub seat_state: SeatState<Self>,
pub shm_state: ShmState,
pub viewporter_state: ViewporterState,
pub xdg_activation_state: XdgActivationState,
pub xdg_shell_state: XdgShellState,
}
@@ -50,10 +62,17 @@ impl Ferese {
let compositor_state = CompositorState::new::<Self>(&display_handle);
let xdg_shell_state = XdgShellState::new::<Self>(&display_handle);
let decoration_state = XdgDecorationState::new::<Self>(&display_handle);
let fractional_scale_state = FractionalScaleManagerState::new::<Self>(&display_handle);
let shm_state = ShmState::new::<Self>(&display_handle, Vec::new());
let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&display_handle);
let pointer_constraints_state = PointerConstraintsState::new::<Self>(&display_handle);
let presentation_state =
PresentationState::new::<Self>(&display_handle, libc::CLOCK_MONOTONIC as u32);
let data_device_state = DataDeviceState::new::<Self>(&display_handle);
let primary_selection_state = PrimarySelectionState::new::<Self>(&display_handle);
let relative_pointer_state = RelativePointerManagerState::new::<Self>(&display_handle);
let viewporter_state = ViewporterState::new::<Self>(&display_handle);
let xdg_activation_state = XdgActivationState::new::<Self>(&display_handle);
let mut seat_state = SeatState::new();
let mut seat = seat_state.new_wl_seat(&display_handle, "ferese-winit");
seat.add_keyboard(Default::default(), 200, 25)?;
@@ -71,10 +90,16 @@ impl Ferese {
compositor_state,
data_device_state,
decoration_state,
fractional_scale_state,
output_manager_state,
pointer_constraints_state,
presentation_state,
primary_selection_state,
relative_pointer_state,
seat_state,
shm_state,
viewporter_state,
xdg_activation_state,
xdg_shell_state,
})
}
+21 -1
View File
@@ -8,9 +8,12 @@ use smithay::{
},
winit::{self, WinitEvent},
},
desktop::utils::OutputPresentationFeedback,
output::{Mode, Output, PhysicalProperties, Subpixel},
reexports::calloop::EventLoop,
utils::{Rectangle, Transform},
reexports::wayland_protocols::wp::presentation_time::server::wp_presentation_feedback::Kind,
utils::{Clock, Monotonic, Rectangle, Transform},
wayland::presentation::Refresh,
};
use crate::Ferese;
@@ -41,6 +44,8 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
state.space.map_output(&output, (0, 0));
let mut damage_tracker = OutputDamageTracker::from_output(&output);
let clock = Clock::<Monotonic>::new();
let mut sequence = 0_u64;
event_loop
.handle()
.insert_source(event_source, move |event, _, state| match event {
@@ -56,6 +61,14 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
WinitEvent::Input(event) => state.process_input_event(event),
WinitEvent::Redraw => {
let damage = Rectangle::from_size(backend.window_size());
let mut presentation = OutputPresentationFeedback::new(&output);
state.space.elements().for_each(|window| {
window.take_presentation_feedback(
&mut presentation,
|_, _| Some(output.clone()),
|_, _| Kind::Vsync,
);
});
let rendered = (|| -> Result<(), Box<dyn Error>> {
{
let (renderer, mut framebuffer) = backend.bind()?;
@@ -84,6 +97,13 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
state.loop_signal.stop();
return;
}
sequence = sequence.wrapping_add(1);
presentation.presented(
clock.now(),
Refresh::fixed(Duration::from_nanos(1_000_000_000 / 60)),
sequence,
Kind::Vsync,
);
state.space.elements().for_each(|window| {
window.send_frame(
&output,