Move Wayland backend to winit-wayland (#4252)

This commit is contained in:
Mads Marquart 2025-05-25 16:48:07 +02:00 committed by GitHub
parent 927af44aa4
commit 1126e9ea2f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 181 additions and 138 deletions

View file

@ -1,815 +0,0 @@
//! The event-loop routines.
use std::cell::{Cell, RefCell};
use std::io::Result as IOResult;
use std::mem;
use std::os::fd::OwnedFd;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::sync::atomic::Ordering;
use std::sync::{Arc, Condvar, Mutex};
use std::thread::JoinHandle;
use std::time::{Duration, Instant};
use calloop::ping::Ping;
use dpi::LogicalSize;
use rustix::event::{PollFd, PollFlags};
use rustix::pipe::{self, PipeFlags};
use sctk::reexports::calloop_wayland_source::WaylandSource;
use sctk::reexports::client::{globals, Connection, QueueHandle};
use tracing::warn;
use winit_core::application::ApplicationHandler;
use winit_core::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource};
use winit_core::error::{EventLoopError, NotSupportedError, OsError, RequestError};
use winit_core::event::{DeviceEvent, StartCause, SurfaceSizeWriter, WindowEvent};
use winit_core::event_loop::pump_events::PumpStatus;
use winit_core::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use winit_core::monitor::MonitorHandle as CoreMonitorHandle;
use winit_core::window::Theme;
use crate::platform_impl::platform::min_timeout;
use crate::platform_impl::wayland::types::cursor::WaylandCustomCursor;
mod proxy;
pub mod sink;
use proxy::EventLoopProxy;
use sink::EventSink;
pub use winit_core::event_loop::EventLoopProxy as CoreEventLoopProxy;
use super::output::MonitorHandle;
use super::state::{WindowCompositorUpdate, WinitState};
use super::window::state::FrameCallbackState;
use super::{logical_to_physical_rounded, WindowId};
type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>;
#[derive(Debug)]
pub(crate) enum Event {
WindowEvent { window_id: WindowId, event: WindowEvent },
DeviceEvent { event: DeviceEvent },
}
/// The Wayland event loop.
#[derive(Debug)]
pub struct EventLoop {
/// Has `run` or `run_on_demand` been called or a call to `pump_events` that starts the loop
loop_running: bool,
buffer_sink: EventSink,
compositor_updates: Vec<WindowCompositorUpdate>,
window_ids: Vec<WindowId>,
/// The Wayland dispatcher to has raw access to the queue when needed, such as
/// when creating a new window.
wayland_dispatcher: WaylandDispatcher,
/// Connection to the wayland server.
handle: Arc<OwnedDisplayHandle>,
/// Event loop window target.
active_event_loop: ActiveEventLoop,
// XXX drop after everything else, just to be safe.
/// Calloop's event loop.
event_loop: calloop::EventLoop<'static, WinitState>,
pump_event_notifier: Option<PumpEventNotifier>,
}
impl EventLoop {
pub fn new() -> Result<EventLoop, EventLoopError> {
let connection = Connection::connect_to_env().map_err(|err| os_error!(err))?;
let (globals, mut event_queue) =
globals::registry_queue_init(&connection).map_err(|err| os_error!(err))?;
let queue_handle = event_queue.handle();
let event_loop =
calloop::EventLoop::<WinitState>::try_new().map_err(|err| os_error!(err))?;
let mut winit_state = WinitState::new(&globals, &queue_handle, event_loop.handle())?;
// NOTE: do a roundtrip after binding the globals to prevent potential
// races with the server.
event_queue.roundtrip(&mut winit_state).map_err(|err| os_error!(err))?;
// Register Wayland source.
let wayland_source = WaylandSource::new(connection.clone(), event_queue);
let wayland_dispatcher =
calloop::Dispatcher::new(wayland_source, |_, queue, winit_state: &mut WinitState| {
let result = queue.dispatch_pending(winit_state);
if result.is_ok()
&& (!winit_state.events_sink.is_empty()
|| !winit_state.window_compositor_updates.is_empty())
{
winit_state.dispatched_events = true;
}
result
});
event_loop
.handle()
.register_dispatcher(wayland_dispatcher.clone())
.map_err(|err| os_error!(err))?;
// Setup the user proxy.
let (ping, ping_source) = calloop::ping::make_ping().unwrap();
event_loop
.handle()
.insert_source(ping_source, move |_, _, winit_state: &mut WinitState| {
winit_state.dispatched_events = true;
winit_state.proxy_wake_up = true;
})
.map_err(|err| os_error!(err))?;
// An event's loop awakener to wake up for window events from winit's windows.
let (event_loop_awakener, event_loop_awakener_source) =
calloop::ping::make_ping().map_err(|err| os_error!(err))?;
event_loop
.handle()
.insert_source(event_loop_awakener_source, move |_, _, winit_state: &mut WinitState| {
// Mark that we have something to dispatch.
winit_state.dispatched_events = true;
})
.map_err(|err| os_error!(err))?;
let handle = Arc::new(OwnedDisplayHandle::new(connection));
let active_event_loop = ActiveEventLoop {
handle: handle.clone(),
wayland_dispatcher: wayland_dispatcher.clone(),
event_loop_awakener,
event_loop_proxy: EventLoopProxy::new(ping).into(),
queue_handle,
control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(None),
state: RefCell::new(winit_state),
};
let event_loop = Self {
loop_running: false,
compositor_updates: Vec::new(),
buffer_sink: EventSink::default(),
window_ids: Vec::new(),
handle,
wayland_dispatcher,
event_loop,
active_event_loop,
pump_event_notifier: None,
};
Ok(event_loop)
}
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> {
self.run_app_on_demand(app)
}
pub fn run_app_on_demand<A: ApplicationHandler>(
&mut self,
mut app: A,
) -> Result<(), EventLoopError> {
self.active_event_loop.clear_exit();
let exit = loop {
match self.pump_app_events(None, &mut app) {
PumpStatus::Exit(0) => {
break Ok(());
},
PumpStatus::Exit(code) => {
break Err(EventLoopError::ExitFailure(code));
},
_ => {
continue;
},
}
};
// Applications aren't allowed to carry windows between separate
// `run_on_demand` calls but if they have only just dropped their
// windows we need to make sure those last requests are sent to the
// compositor.
let _ = self.roundtrip().map_err(EventLoopError::Os);
exit
}
pub fn pump_app_events<A: ApplicationHandler>(
&mut self,
timeout: Option<Duration>,
mut app: A,
) -> PumpStatus {
if !self.loop_running {
self.loop_running = true;
// Run the initial loop iteration.
self.single_iteration(&mut app, StartCause::Init);
}
// Consider the possibility that the `StartCause::Init` iteration could
// request to Exit.
if !self.exiting() {
self.poll_events_with_timeout(timeout, &mut app);
}
if let Some(code) = self.exit_code() {
self.loop_running = false;
PumpStatus::Exit(code)
} else {
// NOTE: spawn a wake-up thread, thus if we have code reading the wayland connection
// in parallel to winit, we ensure that the loop itself is marked as having events.
if timeout.is_some() && self.pump_event_notifier.is_none() {
self.pump_event_notifier = Some(PumpEventNotifier::spawn(
self.active_event_loop.handle.connection.clone(),
self.active_event_loop.event_loop_awakener.clone(),
));
}
if let Some(pump_event_notifier) = self.pump_event_notifier.as_ref() {
// Notify that we don't have to wait, since we're out of winit.
*pump_event_notifier.control.0.lock().unwrap() = PumpEventNotifierAction::Monitor;
pump_event_notifier.control.1.notify_one();
}
PumpStatus::Continue
}
}
fn poll_events_with_timeout<A: ApplicationHandler>(
&mut self,
mut timeout: Option<Duration>,
app: &mut A,
) {
let cause = loop {
let start = Instant::now();
timeout = {
let control_flow_timeout = match self.control_flow() {
ControlFlow::Wait => None,
ControlFlow::Poll => Some(Duration::ZERO),
ControlFlow::WaitUntil(wait_deadline) => {
Some(wait_deadline.saturating_duration_since(start))
},
};
min_timeout(control_flow_timeout, timeout)
};
// NOTE Ideally we should flush as the last thing we do before polling
// to wait for events, and this should be done by the calloop
// WaylandSource but we currently need to flush writes manually.
//
// Checking for flush error is essential to perform an exit with error, since
// once we have a protocol error, we could get stuck retrying...
if self.handle.connection.flush().is_err() {
self.set_exit_code(1);
return;
}
if let Err(error) = self.loop_dispatch(timeout) {
// NOTE We exit on errors from dispatches, since if we've got protocol error
// libwayland-client/wayland-rs will inform us anyway, but crashing downstream is
// not really an option. Instead we inform that the event loop got
// destroyed. We may communicate an error that something was
// terminated, but winit doesn't provide us with an API to do that
// via some event. Still, we set the exit code to the error's OS
// error code, or to 1 if not possible.
let exit_code = error.raw_os_error().unwrap_or(1);
self.set_exit_code(exit_code);
return;
}
// NB: `StartCause::Init` is handled as a special case and doesn't need
// to be considered here
let cause = match self.control_flow() {
ControlFlow::Poll => StartCause::Poll,
ControlFlow::Wait => StartCause::WaitCancelled { start, requested_resume: None },
ControlFlow::WaitUntil(deadline) => {
if Instant::now() < deadline {
StartCause::WaitCancelled { start, requested_resume: Some(deadline) }
} else {
StartCause::ResumeTimeReached { start, requested_resume: deadline }
}
},
};
// Reduce spurious wake-ups.
let dispatched_events = self.with_state(|state| state.dispatched_events);
if matches!(cause, StartCause::WaitCancelled { .. })
&& !dispatched_events
&& timeout.is_none()
{
continue;
}
break cause;
};
self.single_iteration(app, cause);
}
fn single_iteration<A: ApplicationHandler>(&mut self, app: &mut A, cause: StartCause) {
// NOTE currently just indented to simplify the diff
// We retain these grow-only scratch buffers as part of the EventLoop
// for the sake of avoiding lots of reallocs. We take them here to avoid
// trying to mutably borrow `self` more than once and we swap them back
// when finished.
let mut compositor_updates = std::mem::take(&mut self.compositor_updates);
let mut buffer_sink = std::mem::take(&mut self.buffer_sink);
let mut window_ids = std::mem::take(&mut self.window_ids);
app.new_events(&self.active_event_loop, cause);
// NB: For consistency all platforms must call `can_create_surfaces` even though Wayland
// applications don't themselves have a formal surface destroy/create lifecycle.
if cause == StartCause::Init {
app.can_create_surfaces(&self.active_event_loop);
}
// Indicate user wake up.
if self.with_state(|state| mem::take(&mut state.proxy_wake_up)) {
app.proxy_wake_up(&self.active_event_loop);
}
// Drain the pending compositor updates.
self.with_state(|state| compositor_updates.append(&mut state.window_compositor_updates));
for mut compositor_update in compositor_updates.drain(..) {
let window_id = compositor_update.window_id;
if compositor_update.scale_changed {
let (physical_size, scale_factor) = self.with_state(|state| {
let windows = state.windows.get_mut();
let window = windows.get(&window_id).unwrap().lock().unwrap();
let scale_factor = window.scale_factor();
let size = logical_to_physical_rounded(window.surface_size(), scale_factor);
(size, scale_factor)
});
// Stash the old window size.
let old_physical_size = physical_size;
let new_surface_size = Arc::new(Mutex::new(physical_size));
let event = WindowEvent::ScaleFactorChanged {
scale_factor,
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)),
};
app.window_event(&self.active_event_loop, window_id, event);
let physical_size = *new_surface_size.lock().unwrap();
drop(new_surface_size);
// Resize the window when user altered the size.
if old_physical_size != physical_size {
self.with_state(|state| {
let windows = state.windows.get_mut();
let mut window = windows.get(&window_id).unwrap().lock().unwrap();
let new_logical_size: LogicalSize<f64> =
physical_size.to_logical(scale_factor);
window.request_surface_size(new_logical_size.into());
});
// Make it queue resize.
compositor_update.resized = true;
}
}
// NOTE: Rescale changed the physical size which winit operates in, thus we should
// resize.
if compositor_update.resized || compositor_update.scale_changed {
let physical_size = self.with_state(|state| {
let windows = state.windows.get_mut();
let window = windows.get(&window_id).unwrap().lock().unwrap();
let scale_factor = window.scale_factor();
let size = logical_to_physical_rounded(window.surface_size(), scale_factor);
// Mark the window as needed a redraw.
state
.window_requests
.get_mut()
.get_mut(&window_id)
.unwrap()
.redraw_requested
.store(true, Ordering::Relaxed);
size
});
let event = WindowEvent::SurfaceResized(physical_size);
app.window_event(&self.active_event_loop, window_id, event);
}
if compositor_update.close_window {
app.window_event(&self.active_event_loop, window_id, WindowEvent::CloseRequested);
}
}
// Push the events directly from the window.
self.with_state(|state| {
buffer_sink.append(&mut state.window_events_sink.lock().unwrap());
});
for event in buffer_sink.drain() {
match event {
Event::WindowEvent { window_id, event } => {
app.window_event(&self.active_event_loop, window_id, event)
},
Event::DeviceEvent { event } => {
app.device_event(&self.active_event_loop, None, event)
},
}
}
// Handle non-synthetic events.
self.with_state(|state| {
buffer_sink.append(&mut state.events_sink);
});
for event in buffer_sink.drain() {
match event {
Event::WindowEvent { window_id, event } => {
app.window_event(&self.active_event_loop, window_id, event)
},
Event::DeviceEvent { event } => {
app.device_event(&self.active_event_loop, None, event)
},
}
}
// Collect the window ids
self.with_state(|state| {
window_ids.extend(state.window_requests.get_mut().keys());
});
for window_id in window_ids.iter() {
let event = self.with_state(|state| {
let window_requests = state.window_requests.get_mut();
if window_requests.get(window_id).unwrap().take_closed() {
mem::drop(window_requests.remove(window_id));
mem::drop(state.windows.get_mut().remove(window_id));
return Some(WindowEvent::Destroyed);
}
let mut window =
state.windows.get_mut().get_mut(window_id).unwrap().lock().unwrap();
if window.frame_callback_state() == FrameCallbackState::Requested {
return None;
}
// Reset the frame callbacks state.
window.frame_callback_reset();
let mut redraw_requested =
window_requests.get(window_id).unwrap().take_redraw_requested();
// Redraw the frame while at it.
redraw_requested |= window.refresh_frame();
redraw_requested.then_some(WindowEvent::RedrawRequested)
});
if let Some(event) = event {
app.window_event(&self.active_event_loop, *window_id, event);
}
}
// Reset the hint that we've dispatched events.
self.with_state(|state| {
state.dispatched_events = false;
});
// This is always the last event we dispatch before poll again
app.about_to_wait(&self.active_event_loop);
// Update the window frames and schedule redraws.
let mut wake_up = false;
for window_id in window_ids.drain(..) {
wake_up |= self.with_state(|state| match state.windows.get_mut().get_mut(&window_id) {
Some(window) => {
let refresh = window.lock().unwrap().refresh_frame();
if refresh {
state
.window_requests
.get_mut()
.get_mut(&window_id)
.unwrap()
.redraw_requested
.store(true, Ordering::Relaxed);
}
refresh
},
None => false,
});
}
// Wakeup event loop if needed.
//
// If the user draws from the `AboutToWait` this is likely not required, however
// we can't do much about it.
if wake_up {
self.active_event_loop.event_loop_awakener.ping();
}
std::mem::swap(&mut self.compositor_updates, &mut compositor_updates);
std::mem::swap(&mut self.buffer_sink, &mut buffer_sink);
std::mem::swap(&mut self.window_ids, &mut window_ids);
}
#[inline]
pub fn window_target(&self) -> &dyn RootActiveEventLoop {
&self.active_event_loop
}
fn with_state<'a, U: 'a, F: FnOnce(&'a mut WinitState) -> U>(&'a mut self, callback: F) -> U {
let state = self.active_event_loop.state.get_mut();
callback(state)
}
fn loop_dispatch<D: Into<Option<std::time::Duration>>>(&mut self, timeout: D) -> IOResult<()> {
let state = &mut self.active_event_loop.state.get_mut();
self.event_loop.dispatch(timeout, state).map_err(|error| {
tracing::error!("Error dispatching event loop: {}", error);
error.into()
})
}
fn roundtrip(&mut self) -> Result<usize, OsError> {
let state = &mut self.active_event_loop.state.get_mut();
let mut wayland_source = self.wayland_dispatcher.as_source_mut();
let event_queue = wayland_source.queue();
event_queue.roundtrip(state).map_err(|err| os_error!(err))
}
fn control_flow(&self) -> ControlFlow {
self.active_event_loop.control_flow()
}
fn exiting(&self) -> bool {
self.active_event_loop.exiting()
}
fn set_exit_code(&self, code: i32) {
self.active_event_loop.set_exit_code(code)
}
fn exit_code(&self) -> Option<i32> {
self.active_event_loop.exit_code()
}
}
impl AsFd for EventLoop {
fn as_fd(&self) -> BorrowedFd<'_> {
self.event_loop.as_fd()
}
}
impl AsRawFd for EventLoop {
fn as_raw_fd(&self) -> RawFd {
self.event_loop.as_raw_fd()
}
}
#[derive(Debug)]
pub struct ActiveEventLoop {
/// Event loop proxy
event_loop_proxy: CoreEventLoopProxy,
/// The event loop wakeup source.
pub event_loop_awakener: Ping,
/// The main queue used by the event loop.
pub queue_handle: QueueHandle<WinitState>,
/// The application's latest control_flow state
pub(crate) control_flow: Cell<ControlFlow>,
/// The application's exit state.
pub(crate) exit: Cell<Option<i32>>,
// TODO remove that RefCell once we can pass `&mut` in `Window::new`.
/// Winit state.
pub state: RefCell<WinitState>,
/// Dispatcher of Wayland events.
pub wayland_dispatcher: WaylandDispatcher,
/// Handle for the underlying event loop.
pub handle: Arc<OwnedDisplayHandle>,
}
impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> CoreEventLoopProxy {
self.event_loop_proxy.clone()
}
fn set_control_flow(&self, control_flow: ControlFlow) {
self.control_flow.set(control_flow)
}
fn control_flow(&self) -> ControlFlow {
self.control_flow.get()
}
fn exit(&self) {
self.exit.set(Some(0))
}
fn exiting(&self) -> bool {
self.exit.get().is_some()
}
#[inline]
fn listen_device_events(&self, _allowed: DeviceEvents) {}
fn create_custom_cursor(
&self,
cursor: CustomCursorSource,
) -> Result<CoreCustomCursor, RequestError> {
let cursor_image = match cursor {
CustomCursorSource::Image(cursor_image) => cursor_image,
CustomCursorSource::Animation { .. } | CustomCursorSource::Url { .. } => {
return Err(NotSupportedError::new("unsupported cursor kind").into())
},
};
Ok(CoreCustomCursor(Arc::new(WaylandCustomCursor(cursor_image))))
}
#[inline]
fn system_theme(&self) -> Option<Theme> {
None
}
fn create_window(
&self,
window_attributes: winit_core::window::WindowAttributes,
) -> Result<Box<dyn winit_core::window::Window>, RequestError> {
let window = crate::platform_impl::wayland::Window::new(self, window_attributes)?;
Ok(Box::new(window))
}
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
Box::new(
self.state
.borrow()
.output_state
.outputs()
.map(MonitorHandle::new)
.map(|inner| CoreMonitorHandle(Arc::new(inner))),
)
}
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
// There's no primary monitor on Wayland.
None
}
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
CoreOwnedDisplayHandle::new(self.handle.clone())
}
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
}
impl ActiveEventLoop {
fn clear_exit(&self) {
self.exit.set(None)
}
fn set_exit_code(&self, code: i32) {
self.exit.set(Some(code))
}
fn exit_code(&self) -> Option<i32> {
self.exit.get()
}
}
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
self.handle.display_handle()
}
}
#[derive(Debug)]
pub struct OwnedDisplayHandle {
pub(crate) connection: Connection,
}
impl OwnedDisplayHandle {
fn new(connection: Connection) -> Self {
Self { connection }
}
}
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
use sctk::reexports::client::Proxy;
let raw = rwh_06::WaylandDisplayHandle::new({
let ptr = self.connection.display().id().as_ptr();
std::ptr::NonNull::new(ptr as *mut _).expect("wl_display should never be null")
});
Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw.into()) })
}
}
#[derive(Debug)]
struct PumpEventNotifier {
/// Whether we're in winit or not.
control: Arc<(Mutex<PumpEventNotifierAction>, Condvar)>,
/// Waker handle for the working thread.
worker_waker: Option<OwnedFd>,
/// Thread handle.
handle: Option<JoinHandle<()>>,
}
impl Drop for PumpEventNotifier {
fn drop(&mut self) {
// Wake-up the thread.
if let Some(worker_waker) = self.worker_waker.as_ref() {
let _ = rustix::io::write(worker_waker.as_fd(), &[0u8]);
}
*self.control.0.lock().unwrap() = PumpEventNotifierAction::Shutdown;
self.control.1.notify_one();
if let Some(handle) = self.handle.take() {
let _ = handle.join();
}
}
}
impl PumpEventNotifier {
fn spawn(connection: Connection, awakener: Ping) -> Self {
// Start from the waiting state.
let control = Arc::new((Mutex::new(PumpEventNotifierAction::Pause), Condvar::new()));
let control_thread = Arc::clone(&control);
let (read, write) = match pipe::pipe_with(PipeFlags::CLOEXEC | PipeFlags::NONBLOCK) {
Ok((read, write)) => (read, write),
Err(_) => return Self { control, handle: None, worker_waker: None },
};
let handle =
std::thread::Builder::new().name(String::from("pump_events mon")).spawn(move || {
let (lock, cvar) = &*control_thread;
'outer: loop {
let mut wait = lock.lock().unwrap();
while *wait == PumpEventNotifierAction::Pause {
wait = cvar.wait(wait).unwrap();
}
// Exit the loop when we're asked to. Given that we poll
// only once we can take the `prepare_read`, but in some cases
// it could be not possible, we may block on `join`.
if *wait == PumpEventNotifierAction::Shutdown {
break 'outer;
}
// Wake-up the main loop and put this one back to sleep.
*wait = PumpEventNotifierAction::Pause;
drop(wait);
while let Some(read_guard) = connection.prepare_read() {
let _ = connection.flush();
let poll_fd = PollFd::from_borrowed_fd(connection.as_fd(), PollFlags::IN);
let pipe_poll_fd = PollFd::from_borrowed_fd(read.as_fd(), PollFlags::IN);
// Read from the `fd` before going back to poll.
if Ok(1) == rustix::io::read(read.as_fd(), &mut [0u8; 1]) {
break 'outer;
}
let _ = rustix::event::poll(&mut [poll_fd, pipe_poll_fd], -1);
// Non-blocking read the connection.
let _ = read_guard.read_without_dispatch();
}
awakener.ping();
}
});
if let Some(err) = handle.as_ref().err() {
warn!("failed to spawn pump_events wake-up thread: {err}");
}
PumpEventNotifier { control, handle: handle.ok(), worker_waker: Some(write) }
}
}
#[derive(Debug, PartialEq, Eq)]
enum PumpEventNotifierAction {
/// Monitor the wayland queue.
Monitor,
/// Pause monitoring.
Pause,
/// Shutdown the thread.
Shutdown,
}

View file

@ -1,30 +0,0 @@
//! An event loop proxy.
use std::sync::Arc;
use sctk::reexports::calloop::ping::Ping;
use winit_core::event_loop::{EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider};
/// A handle that can be sent across the threads and used to wake up the `EventLoop`.
#[derive(Debug)]
pub struct EventLoopProxy {
ping: Ping,
}
impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
self.ping.ping();
}
}
impl EventLoopProxy {
pub fn new(ping: Ping) -> Self {
Self { ping }
}
}
impl From<EventLoopProxy> for CoreEventLoopProxy {
fn from(value: EventLoopProxy) -> Self {
CoreEventLoopProxy::new(Arc::new(value))
}
}

View file

@ -1,49 +0,0 @@
//! An event loop's sink to deliver events from the Wayland event callbacks.
use std::vec::Drain;
use winit_core::event::{DeviceEvent, WindowEvent};
use winit_core::window::WindowId;
use super::Event;
/// An event loop's sink to deliver events from the Wayland event callbacks
/// to the winit's user.
#[derive(Default, Debug)]
pub struct EventSink {
pub(crate) window_events: Vec<Event>,
}
impl EventSink {
pub fn new() -> Self {
Default::default()
}
/// Return `true` if there're pending events.
#[inline]
pub fn is_empty(&self) -> bool {
self.window_events.is_empty()
}
/// Add new device event to a queue.
#[inline]
pub fn push_device_event(&mut self, event: DeviceEvent) {
self.window_events.push(Event::DeviceEvent { event });
}
/// Add new window event to a queue.
#[inline]
pub fn push_window_event(&mut self, event: WindowEvent, window_id: WindowId) {
self.window_events.push(Event::WindowEvent { event, window_id });
}
#[inline]
pub fn append(&mut self, other: &mut Self) {
self.window_events.append(&mut other.window_events);
}
#[inline]
pub(crate) fn drain(&mut self) -> Drain<'_, Event> {
self.window_events.drain(..)
}
}

View file

@ -1,29 +0,0 @@
//! Winit's Wayland backend.
use dpi::{LogicalSize, PhysicalSize};
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::Proxy;
use winit_core::window::WindowId;
mod event_loop;
mod output;
mod seat;
mod state;
mod types;
mod window;
pub use event_loop::{ActiveEventLoop, EventLoop};
pub use window::Window;
/// Get the WindowId out of the surface.
#[inline]
fn make_wid(surface: &WlSurface) -> WindowId {
WindowId::from_raw(surface.id().as_ptr() as usize)
}
/// The default routine does floor, but we need round on Wayland.
fn logical_to_physical_rounded(size: LogicalSize<u32>, scale_factor: f64) -> PhysicalSize<u32> {
let width = size.width as f64 * scale_factor;
let height = size.height as f64 * scale_factor;
(width.round(), height.round()).into()
}

View file

@ -1,90 +0,0 @@
use std::borrow::Cow;
use std::num::NonZeroU32;
use dpi::{LogicalPosition, PhysicalPosition};
use sctk::output::{Mode, OutputData};
use sctk::reexports::client::protocol::wl_output::WlOutput;
use sctk::reexports::client::Proxy;
use winit_core::monitor::{MonitorHandleProvider as CoreMonitorHandle, VideoMode};
#[derive(Clone, Debug)]
pub struct MonitorHandle {
pub(crate) proxy: WlOutput,
}
impl MonitorHandle {
#[inline]
pub(crate) fn new(proxy: WlOutput) -> Self {
Self { proxy }
}
}
impl CoreMonitorHandle for MonitorHandle {
fn id(&self) -> u128 {
self.native_id() as _
}
fn native_id(&self) -> u64 {
let output_data = self.proxy.data::<OutputData>().unwrap();
output_data.with_output_info(|info| info.id as u64)
}
fn name(&self) -> Option<Cow<'_, str>> {
let output_data = self.proxy.data::<OutputData>().unwrap();
output_data.with_output_info(|info| info.name.clone().map(Cow::Owned))
}
fn position(&self) -> Option<PhysicalPosition<i32>> {
let output_data = self.proxy.data::<OutputData>().unwrap();
Some(output_data.with_output_info(|info| {
info.logical_position.map_or_else(
|| {
LogicalPosition::<i32>::from(info.location)
.to_physical(info.scale_factor as f64)
},
|logical_position| {
LogicalPosition::<i32>::from(logical_position)
.to_physical(info.scale_factor as f64)
},
)
}))
}
fn scale_factor(&self) -> f64 {
let output_data = self.proxy.data::<OutputData>().unwrap();
output_data.scale_factor() as f64
}
fn current_video_mode(&self) -> Option<winit_core::monitor::VideoMode> {
let output_data = self.proxy.data::<OutputData>().unwrap();
output_data.with_output_info(|info| {
let mode = info.modes.iter().find(|mode| mode.current).cloned();
mode.map(wayland_mode_to_core_mode)
})
}
fn video_modes(&self) -> Box<dyn Iterator<Item = VideoMode>> {
let output_data = self.proxy.data::<OutputData>().unwrap();
let modes = output_data.with_output_info(|info| info.modes.clone());
Box::new(modes.into_iter().map(wayland_mode_to_core_mode))
}
}
impl PartialEq for MonitorHandle {
fn eq(&self, other: &Self) -> bool {
self.native_id() == other.native_id()
}
}
impl Eq for MonitorHandle {}
/// Convert the wayland's [`Mode`] to winit's [`VideoMode`].
fn wayland_mode_to_core_mode(mode: Mode) -> VideoMode {
VideoMode::new(
(mode.dimensions.0, mode.dimensions.1).into(),
None,
NonZeroU32::new(mode.refresh_rate as u32),
)
}

View file

@ -1,377 +0,0 @@
//! The keyboard input handling.
use std::sync::Mutex;
use std::time::Duration;
use calloop::timer::{TimeoutAction, Timer};
use calloop::{LoopHandle, RegistrationToken};
use sctk::reexports::client::protocol::wl_keyboard::{
Event as WlKeyboardEvent, KeyState as WlKeyState, KeymapFormat as WlKeymapFormat, WlKeyboard,
};
use sctk::reexports::client::protocol::wl_seat::WlSeat;
use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle, WEnum};
use tracing::warn;
use winit_common::xkb::Context;
use winit_core::event::{ElementState, WindowEvent};
use winit_core::keyboard::ModifiersState;
use crate::platform_impl::wayland::event_loop::sink::EventSink;
use crate::platform_impl::wayland::state::WinitState;
use crate::platform_impl::wayland::{self, WindowId};
impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
fn event(
state: &mut WinitState,
wl_keyboard: &WlKeyboard,
event: <WlKeyboard as Proxy>::Event,
data: &KeyboardData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
let seat_state = match state.seats.get_mut(&data.seat.id()) {
Some(seat_state) => seat_state,
None => {
warn!("Received keyboard event {event:?} without seat");
return;
},
};
let keyboard_state = match seat_state.keyboard_state.as_mut() {
Some(keyboard_state) => keyboard_state,
None => {
warn!("Received keyboard event {event:?} without keyboard");
return;
},
};
match event {
WlKeyboardEvent::Keymap { format, fd, size } => match format {
WEnum::Value(format) => match format {
WlKeymapFormat::NoKeymap => {
warn!("non-xkb compatible keymap")
},
WlKeymapFormat::XkbV1 => {
let context = &mut keyboard_state.xkb_context;
context.set_keymap_from_fd(fd, size as usize);
},
_ => unreachable!(),
},
WEnum::Unknown(value) => {
warn!("unknown keymap format 0x{:x}", value)
},
},
WlKeyboardEvent::Enter { surface, .. } => {
let window_id = wayland::make_wid(&surface);
// Mark the window as focused.
let was_unfocused = match state.windows.get_mut().get(&window_id) {
Some(window) => {
let mut window = window.lock().unwrap();
let was_unfocused = !window.has_focus();
window.add_seat_focus(data.seat.id());
was_unfocused
},
None => return,
};
// Drop the repeat, if there were any.
keyboard_state.current_repeat = None;
if let Some(token) = keyboard_state.repeat_token.take() {
keyboard_state.loop_handle.remove(token);
}
*data.window_id.lock().unwrap() = Some(window_id);
// The keyboard focus is considered as general focus.
if was_unfocused {
state.events_sink.push_window_event(WindowEvent::Focused(true), window_id);
}
// HACK: this is just for GNOME not fixing their ordering issue of modifiers.
if std::mem::take(&mut seat_state.modifiers_pending) {
state.events_sink.push_window_event(
WindowEvent::ModifiersChanged(seat_state.modifiers.into()),
window_id,
);
}
},
WlKeyboardEvent::Leave { surface, .. } => {
let window_id = wayland::make_wid(&surface);
// NOTE: we should drop the repeat regardless whethere it was for the present
// window of for the window which just went gone.
keyboard_state.current_repeat = None;
if let Some(token) = keyboard_state.repeat_token.take() {
keyboard_state.loop_handle.remove(token);
}
// NOTE: The check whether the window exists is essential as we might get a
// nil surface, regardless of what protocol says.
let focused = match state.windows.get_mut().get(&window_id) {
Some(window) => {
let mut window = window.lock().unwrap();
window.remove_seat_focus(&data.seat.id());
window.has_focus()
},
None => return,
};
// We don't need to update it above, because the next `Enter` will overwrite
// anyway.
*data.window_id.lock().unwrap() = None;
if !focused {
// Notify that no modifiers are being pressed.
state.events_sink.push_window_event(
WindowEvent::ModifiersChanged(ModifiersState::empty().into()),
window_id,
);
state.events_sink.push_window_event(WindowEvent::Focused(false), window_id);
}
},
WlKeyboardEvent::Key { key, state: WEnum::Value(WlKeyState::Pressed), .. } => {
let key = key + 8;
key_input(
keyboard_state,
&mut state.events_sink,
data,
key,
ElementState::Pressed,
false,
);
let delay = match keyboard_state.repeat_info {
RepeatInfo::Repeat { delay, .. } => delay,
RepeatInfo::Disable => return,
};
if !keyboard_state.xkb_context.keymap_mut().unwrap().key_repeats(key) {
return;
}
keyboard_state.current_repeat = Some(key);
// NOTE terminate ongoing timer and start a new timer.
if let Some(token) = keyboard_state.repeat_token.take() {
keyboard_state.loop_handle.remove(token);
}
let timer = Timer::from_duration(delay);
let wl_keyboard = wl_keyboard.clone();
keyboard_state.repeat_token = keyboard_state
.loop_handle
.insert_source(timer, move |_, _, state| {
// Required to handle the wakeups from the repeat sources.
state.dispatched_events = true;
let data = wl_keyboard.data::<KeyboardData>().unwrap();
let seat_state = match state.seats.get_mut(&data.seat.id()) {
Some(seat_state) => seat_state,
None => return TimeoutAction::Drop,
};
let keyboard_state = match seat_state.keyboard_state.as_mut() {
Some(keyboard_state) => keyboard_state,
None => return TimeoutAction::Drop,
};
// NOTE: The removed on event source is batched, but key change to `None`
// is instant.
let repeat_keycode = match keyboard_state.current_repeat {
Some(repeat_keycode) => repeat_keycode,
None => return TimeoutAction::Drop,
};
key_input(
keyboard_state,
&mut state.events_sink,
data,
repeat_keycode,
ElementState::Pressed,
true,
);
// NOTE: the gap could change dynamically while repeat is going.
match keyboard_state.repeat_info {
RepeatInfo::Repeat { gap, .. } => TimeoutAction::ToDuration(gap),
RepeatInfo::Disable => TimeoutAction::Drop,
}
})
.ok();
},
WlKeyboardEvent::Key { key, state: WEnum::Value(WlKeyState::Released), .. } => {
let key = key + 8;
key_input(
keyboard_state,
&mut state.events_sink,
data,
key,
ElementState::Released,
false,
);
if keyboard_state.repeat_info != RepeatInfo::Disable
&& keyboard_state.xkb_context.keymap_mut().unwrap().key_repeats(key)
&& Some(key) == keyboard_state.current_repeat
{
keyboard_state.current_repeat = None;
if let Some(token) = keyboard_state.repeat_token.take() {
keyboard_state.loop_handle.remove(token);
}
}
},
WlKeyboardEvent::Modifiers {
mods_depressed, mods_latched, mods_locked, group, ..
} => {
let xkb_context = &mut keyboard_state.xkb_context;
let xkb_state = match xkb_context.state_mut() {
Some(state) => state,
None => return,
};
xkb_state.update_modifiers(mods_depressed, mods_latched, mods_locked, 0, 0, group);
seat_state.modifiers = xkb_state.modifiers().into();
// HACK: part of the workaround from `WlKeyboardEvent::Enter`.
let window_id = match *data.window_id.lock().unwrap() {
Some(window_id) => window_id,
None => {
seat_state.modifiers_pending = true;
return;
},
};
state.events_sink.push_window_event(
WindowEvent::ModifiersChanged(seat_state.modifiers.into()),
window_id,
);
},
WlKeyboardEvent::RepeatInfo { rate, delay } => {
keyboard_state.repeat_info = if rate == 0 {
// Stop the repeat once we get a disable event.
keyboard_state.current_repeat = None;
if let Some(repeat_token) = keyboard_state.repeat_token.take() {
keyboard_state.loop_handle.remove(repeat_token);
}
RepeatInfo::Disable
} else {
let gap = Duration::from_micros(1_000_000 / rate as u64);
let delay = Duration::from_millis(delay as u64);
RepeatInfo::Repeat { gap, delay }
};
},
_ => unreachable!(),
}
}
}
/// The state of the keyboard on the current seat.
#[derive(Debug)]
pub struct KeyboardState {
/// The underlying WlKeyboard.
pub keyboard: WlKeyboard,
/// Loop handle to handle key repeat.
pub loop_handle: LoopHandle<'static, WinitState>,
/// The state of the keyboard.
pub xkb_context: Context,
/// The information about the repeat rate obtained from the compositor.
pub repeat_info: RepeatInfo,
/// The token of the current handle inside the calloop's event loop.
pub repeat_token: Option<RegistrationToken>,
/// The current repeat raw key.
pub current_repeat: Option<u32>,
}
impl KeyboardState {
pub fn new(keyboard: WlKeyboard, loop_handle: LoopHandle<'static, WinitState>) -> Self {
Self {
keyboard,
loop_handle,
xkb_context: Context::new().unwrap(),
repeat_info: RepeatInfo::default(),
repeat_token: None,
current_repeat: None,
}
}
}
impl Drop for KeyboardState {
fn drop(&mut self) {
if self.keyboard.version() >= 3 {
self.keyboard.release();
}
if let Some(token) = self.repeat_token.take() {
self.loop_handle.remove(token);
}
}
}
/// The rate at which a pressed key is repeated.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RepeatInfo {
/// Keys will be repeated at the specified rate and delay.
Repeat {
/// The time between the key repeats.
gap: Duration,
/// Delay (in milliseconds) between a key press and the start of repetition.
delay: Duration,
},
/// Keys should not be repeated.
Disable,
}
impl Default for RepeatInfo {
/// The default repeat rate is 25 keys per second with the delay of 200ms.
///
/// The values are picked based on the default in various compositors and Xorg.
fn default() -> Self {
Self::Repeat { gap: Duration::from_millis(40), delay: Duration::from_millis(200) }
}
}
/// Keyboard user data.
#[derive(Debug)]
pub struct KeyboardData {
/// The currently focused window surface. Could be `None` on bugged compositors, like mutter.
window_id: Mutex<Option<WindowId>>,
/// The seat used to create this keyboard.
seat: WlSeat,
}
impl KeyboardData {
pub fn new(seat: WlSeat) -> Self {
Self { window_id: Default::default(), seat }
}
}
fn key_input(
keyboard_state: &mut KeyboardState,
event_sink: &mut EventSink,
data: &KeyboardData,
keycode: u32,
state: ElementState,
repeat: bool,
) {
let window_id = match *data.window_id.lock().unwrap() {
Some(window_id) => window_id,
None => return,
};
if let Some(mut key_context) = keyboard_state.xkb_context.key_context() {
let event = key_context.process_key_event(keycode, state, repeat);
let event = WindowEvent::KeyboardInput { device_id: None, event, is_synthetic: false };
event_sink.push_window_event(event, window_id);
}
}

View file

@ -1,235 +0,0 @@
//! Seat handling.
use std::sync::Arc;
use ahash::AHashMap;
use sctk::reexports::client::backend::ObjectId;
use sctk::reexports::client::protocol::wl_seat::WlSeat;
use sctk::reexports::client::protocol::wl_touch::WlTouch;
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
use sctk::reexports::protocols::wp::relative_pointer::zv1::client::zwp_relative_pointer_v1::ZwpRelativePointerV1;
use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::ZwpTextInputV3;
use sctk::seat::pointer::{ThemeSpec, ThemedPointer};
use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState};
use tracing::warn;
use winit_core::event::WindowEvent;
use winit_core::keyboard::ModifiersState;
use crate::platform_impl::wayland::state::WinitState;
mod keyboard;
mod pointer;
mod text_input;
mod touch;
use keyboard::{KeyboardData, KeyboardState};
pub use pointer::relative_pointer::RelativePointerState;
pub use pointer::{PointerConstraintsState, WinitPointerData, WinitPointerDataExt};
use text_input::TextInputData;
pub use text_input::{TextInputState, ZwpTextInputV3Ext};
use touch::TouchPoint;
#[derive(Debug, Default)]
pub struct WinitSeatState {
/// The pointer bound on the seat.
pointer: Option<Arc<ThemedPointer<WinitPointerData>>>,
/// The touch bound on the seat.
touch: Option<WlTouch>,
/// The mapping from touched points to the surfaces they're present.
touch_map: AHashMap<i32, TouchPoint>,
/// Id of the first touch event.
first_touch_id: Option<i32>,
/// The text input bound on the seat.
text_input: Option<Arc<ZwpTextInputV3>>,
/// The relative pointer bound on the seat.
relative_pointer: Option<ZwpRelativePointerV1>,
/// The keyboard bound on the seat.
keyboard_state: Option<KeyboardState>,
/// The current modifiers state on the seat.
modifiers: ModifiersState,
/// Whether we have pending modifiers.
modifiers_pending: bool,
}
impl WinitSeatState {
pub fn new() -> Self {
Default::default()
}
}
impl SeatHandler for WinitState {
fn seat_state(&mut self) -> &mut SeatState {
&mut self.seat_state
}
fn new_capability(
&mut self,
_: &Connection,
queue_handle: &QueueHandle<Self>,
seat: WlSeat,
capability: SeatCapability,
) {
let seat_state = match self.seats.get_mut(&seat.id()) {
Some(seat_state) => seat_state,
None => {
warn!("Received wl_seat::new_capability for unknown seat");
return;
},
};
match capability {
SeatCapability::Touch if seat_state.touch.is_none() => {
seat_state.touch = self.seat_state.get_touch(queue_handle, &seat).ok();
},
SeatCapability::Keyboard if seat_state.keyboard_state.is_none() => {
let keyboard = seat.get_keyboard(queue_handle, KeyboardData::new(seat.clone()));
seat_state.keyboard_state =
Some(KeyboardState::new(keyboard, self.loop_handle.clone()));
},
SeatCapability::Pointer if seat_state.pointer.is_none() => {
let surface = self.compositor_state.create_surface(queue_handle);
let viewport = self
.viewporter_state
.as_ref()
.map(|state| state.get_viewport(&surface, queue_handle));
let surface_id = surface.id();
let pointer_data = WinitPointerData::new(seat.clone(), viewport);
let themed_pointer = self
.seat_state
.get_pointer_with_theme_and_data(
queue_handle,
&seat,
self.shm.wl_shm(),
surface,
ThemeSpec::System,
pointer_data,
)
.expect("failed to create pointer with present capability.");
seat_state.relative_pointer = self.relative_pointer.as_ref().map(|manager| {
manager.get_relative_pointer(
themed_pointer.pointer(),
queue_handle,
sctk::globals::GlobalData,
)
});
let themed_pointer = Arc::new(themed_pointer);
// Register cursor surface.
self.pointer_surfaces.insert(surface_id, themed_pointer.clone());
seat_state.pointer = Some(themed_pointer);
},
_ => (),
}
if let Some(text_input_state) =
seat_state.text_input.is_none().then_some(self.text_input_state.as_ref()).flatten()
{
seat_state.text_input = Some(Arc::new(text_input_state.get_text_input(
&seat,
queue_handle,
TextInputData::default(),
)));
}
}
fn remove_capability(
&mut self,
_: &Connection,
_queue_handle: &QueueHandle<Self>,
seat: WlSeat,
capability: SeatCapability,
) {
let seat_state = match self.seats.get_mut(&seat.id()) {
Some(seat_state) => seat_state,
None => {
warn!("Received wl_seat::remove_capability for unknown seat");
return;
},
};
if let Some(text_input) = seat_state.text_input.take() {
text_input.destroy();
}
match capability {
SeatCapability::Touch => {
if let Some(touch) = seat_state.touch.take() {
if touch.version() >= 3 {
touch.release();
}
}
},
SeatCapability::Pointer => {
if let Some(relative_pointer) = seat_state.relative_pointer.take() {
relative_pointer.destroy();
}
if let Some(pointer) = seat_state.pointer.take() {
let pointer_data = pointer.pointer().winit_data();
// Remove the cursor from the mapping.
let surface_id = pointer.surface().id();
let _ = self.pointer_surfaces.remove(&surface_id);
// Remove the inner locks/confines before dropping the pointer.
pointer_data.unlock_pointer();
pointer_data.unconfine_pointer();
if pointer.pointer().version() >= 3 {
pointer.pointer().release();
}
}
},
SeatCapability::Keyboard => {
seat_state.keyboard_state = None;
self.on_keyboard_destroy(&seat.id());
},
_ => (),
}
}
fn new_seat(
&mut self,
_connection: &Connection,
_queue_handle: &QueueHandle<Self>,
seat: WlSeat,
) {
self.seats.insert(seat.id(), WinitSeatState::new());
}
fn remove_seat(
&mut self,
_connection: &Connection,
_queue_handle: &QueueHandle<Self>,
seat: WlSeat,
) {
let _ = self.seats.remove(&seat.id());
self.on_keyboard_destroy(&seat.id());
}
}
impl WinitState {
fn on_keyboard_destroy(&mut self, seat: &ObjectId) {
for (window_id, window) in self.windows.get_mut() {
let mut window = window.lock().unwrap();
let had_focus = window.has_focus();
window.remove_seat_focus(seat);
if had_focus != window.has_focus() {
self.events_sink.push_window_event(WindowEvent::Focused(false), *window_id);
}
}
}
}
sctk::delegate_seat!(WinitState);

View file

@ -1,524 +0,0 @@
//! The pointer events.
use std::ops::Deref;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tracing::warn;
use sctk::reexports::client::delegate_dispatch;
use sctk::reexports::client::protocol::wl_pointer::WlPointer;
use sctk::reexports::client::protocol::wl_seat::WlSeat;
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{Connection, Proxy, QueueHandle, Dispatch};
use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1;
use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_locked_pointer_v1::ZwpLockedPointerV1;
use sctk::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1;
use sctk::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_manager_v1::WpCursorShapeManagerV1;
use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_pointer_constraints_v1::{Lifetime, ZwpPointerConstraintsV1};
use sctk::reexports::client::globals::{BindError, GlobalList};
use sctk::reexports::csd_frame::FrameClick;
use sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport;
use sctk::compositor::SurfaceData;
use sctk::globals::GlobalData;
use sctk::seat::pointer::{
PointerData, PointerDataExt, PointerEvent, PointerEventKind, PointerHandler,
};
use sctk::seat::SeatState;
use dpi::{LogicalPosition, PhysicalPosition};
use winit_core::event::{
ElementState, MouseButton, MouseScrollDelta, PointerKind, PointerSource, TouchPhase,
WindowEvent,
};
use crate::platform_impl::wayland::state::WinitState;
use crate::platform_impl::wayland::{self, WindowId};
pub mod relative_pointer;
impl PointerHandler for WinitState {
fn pointer_frame(
&mut self,
connection: &Connection,
_: &QueueHandle<Self>,
pointer: &WlPointer,
events: &[PointerEvent],
) {
let seat = pointer.winit_data().seat();
let seat_state = match self.seats.get(&seat.id()) {
Some(seat_state) => seat_state,
None => {
warn!("Received pointer event without seat");
return;
},
};
let themed_pointer = match seat_state.pointer.as_ref() {
Some(pointer) => pointer,
None => {
warn!("Received pointer event without pointer");
return;
},
};
for event in events {
let surface = &event.surface;
// The parent surface.
let parent_surface = match event.surface.data::<SurfaceData>() {
Some(data) => data.parent_surface().unwrap_or(surface),
None => continue,
};
let window_id = wayland::make_wid(parent_surface);
// Ensure that window exists.
let mut window = match self.windows.get_mut().get_mut(&window_id) {
Some(window) => window.lock().unwrap(),
None => continue,
};
let scale_factor = window.scale_factor();
let position: PhysicalPosition<f64> =
LogicalPosition::new(event.position.0, event.position.1).to_physical(scale_factor);
match event.kind {
// Pointer movements on decorations.
PointerEventKind::Enter { .. } | PointerEventKind::Motion { .. }
if parent_surface != surface =>
{
if let Some(icon) = window.frame_point_moved(
seat,
surface,
Duration::ZERO,
event.position.0,
event.position.1,
) {
let _ = themed_pointer.set_cursor(connection, icon);
}
},
PointerEventKind::Leave { .. } if parent_surface != surface => {
window.frame_point_left();
},
ref kind @ PointerEventKind::Press { button, serial, time }
| ref kind @ PointerEventKind::Release { button, serial, time }
if parent_surface != surface =>
{
let click = match wayland_button_to_winit(button) {
MouseButton::Left => FrameClick::Normal,
MouseButton::Right => FrameClick::Alternate,
_ => continue,
};
let pressed = matches!(kind, PointerEventKind::Press { .. });
// Emulate click on the frame.
window.frame_click(
click,
pressed,
seat,
serial,
Duration::from_millis(time as u64),
window_id,
&mut self.window_compositor_updates,
);
},
// Regular events on the main surface.
PointerEventKind::Enter { .. } => {
self.events_sink.push_window_event(
WindowEvent::PointerEntered {
primary: true,
device_id: None,
position,
kind: PointerKind::Mouse,
},
window_id,
);
window.pointer_entered(Arc::downgrade(themed_pointer));
// Set the currently focused surface.
pointer.winit_data().inner.lock().unwrap().surface = Some(window_id);
},
PointerEventKind::Leave { .. } => {
window.pointer_left(Arc::downgrade(themed_pointer));
// Remove the active surface.
pointer.winit_data().inner.lock().unwrap().surface = None;
self.events_sink.push_window_event(
WindowEvent::PointerLeft {
primary: true,
device_id: None,
position: Some(position),
kind: PointerKind::Mouse,
},
window_id,
);
},
PointerEventKind::Motion { .. } => {
self.events_sink.push_window_event(
WindowEvent::PointerMoved {
primary: true,
device_id: None,
position,
source: PointerSource::Mouse,
},
window_id,
);
},
ref kind @ PointerEventKind::Press { button, serial, .. }
| ref kind @ PointerEventKind::Release { button, serial, .. } => {
// Update the last button serial.
pointer.winit_data().inner.lock().unwrap().latest_button_serial = serial;
let button = wayland_button_to_winit(button);
let state = if matches!(kind, PointerEventKind::Press { .. }) {
ElementState::Pressed
} else {
ElementState::Released
};
self.events_sink.push_window_event(
WindowEvent::PointerButton {
primary: true,
device_id: None,
state,
position,
button: button.into(),
},
window_id,
);
},
PointerEventKind::Axis { horizontal, vertical, .. } => {
// Get the current phase.
let mut pointer_data = pointer.winit_data().inner.lock().unwrap();
let has_discrete_scroll = horizontal.discrete != 0 || vertical.discrete != 0;
// Figure out what to do about start/ended phases here.
//
// Figure out how to deal with `Started`. Also the `Ended` is not guaranteed
// to be sent for mouse wheels.
let phase = if horizontal.stop || vertical.stop {
TouchPhase::Ended
} else {
match pointer_data.phase {
// Discrete scroll only results in moved events.
_ if has_discrete_scroll => TouchPhase::Moved,
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
_ => TouchPhase::Started,
}
};
// Update the phase.
pointer_data.phase = phase;
// Mice events have both pixel and discrete delta's at the same time. So prefer
// the discrete values if they are present.
let delta = if has_discrete_scroll {
// NOTE: Wayland sign convention is the inverse of winit.
MouseScrollDelta::LineDelta(
(-horizontal.discrete) as f32,
(-vertical.discrete) as f32,
)
} else {
// NOTE: Wayland sign convention is the inverse of winit.
MouseScrollDelta::PixelDelta(
LogicalPosition::new(-horizontal.absolute, -vertical.absolute)
.to_physical(scale_factor),
)
};
self.events_sink.push_window_event(
WindowEvent::MouseWheel { device_id: None, delta, phase },
window_id,
)
},
}
}
}
}
#[derive(Debug)]
pub struct WinitPointerData {
/// The inner winit data associated with the pointer.
inner: Mutex<WinitPointerDataInner>,
/// The data required by the sctk.
sctk_data: PointerData,
/// Viewport for fractional cursor.
viewport: Option<WpViewport>,
}
impl WinitPointerData {
pub fn new(seat: WlSeat, viewport: Option<WpViewport>) -> Self {
Self {
inner: Mutex::new(WinitPointerDataInner::default()),
sctk_data: PointerData::new(seat),
viewport,
}
}
pub fn lock_pointer(
&self,
pointer_constraints: &PointerConstraintsState,
surface: &WlSurface,
pointer: &WlPointer,
queue_handle: &QueueHandle<WinitState>,
) {
let mut inner = self.inner.lock().unwrap();
if inner.locked_pointer.is_none() {
inner.locked_pointer = Some(pointer_constraints.lock_pointer(
surface,
pointer,
None,
Lifetime::Persistent,
queue_handle,
GlobalData,
));
}
}
pub fn unlock_pointer(&self) {
let mut inner = self.inner.lock().unwrap();
if let Some(locked_pointer) = inner.locked_pointer.take() {
locked_pointer.destroy();
}
}
pub fn confine_pointer(
&self,
pointer_constraints: &PointerConstraintsState,
surface: &WlSurface,
pointer: &WlPointer,
queue_handle: &QueueHandle<WinitState>,
) {
self.inner.lock().unwrap().confined_pointer = Some(pointer_constraints.confine_pointer(
surface,
pointer,
None,
Lifetime::Persistent,
queue_handle,
GlobalData,
));
}
pub fn unconfine_pointer(&self) {
let inner = self.inner.lock().unwrap();
if let Some(confined_pointer) = inner.confined_pointer.as_ref() {
confined_pointer.destroy();
}
}
/// Seat associated with this pointer.
pub fn seat(&self) -> &WlSeat {
self.sctk_data.seat()
}
/// Active window.
pub fn focused_window(&self) -> Option<WindowId> {
self.inner.lock().unwrap().surface
}
/// Last button serial.
pub fn latest_button_serial(&self) -> u32 {
self.sctk_data.latest_button_serial().unwrap_or_default()
}
/// Last enter serial.
pub fn latest_enter_serial(&self) -> u32 {
self.sctk_data.latest_enter_serial().unwrap_or_default()
}
pub fn set_locked_cursor_position(&self, surface_x: f64, surface_y: f64) {
let inner = self.inner.lock().unwrap();
if let Some(locked_pointer) = inner.locked_pointer.as_ref() {
locked_pointer.set_cursor_position_hint(surface_x, surface_y);
}
}
pub fn viewport(&self) -> Option<&WpViewport> {
self.viewport.as_ref()
}
}
impl Drop for WinitPointerData {
fn drop(&mut self) {
if let Some(viewport) = self.viewport.take() {
viewport.destroy();
}
}
}
impl PointerDataExt for WinitPointerData {
fn pointer_data(&self) -> &PointerData {
&self.sctk_data
}
}
#[derive(Debug)]
pub struct WinitPointerDataInner {
/// The associated locked pointer.
locked_pointer: Option<ZwpLockedPointerV1>,
/// The associated confined pointer.
confined_pointer: Option<ZwpConfinedPointerV1>,
/// Serial of the last button event.
latest_button_serial: u32,
/// Currently focused window.
surface: Option<WindowId>,
/// Current axis phase.
phase: TouchPhase,
}
impl Drop for WinitPointerDataInner {
fn drop(&mut self) {
if let Some(locked_pointer) = self.locked_pointer.take() {
locked_pointer.destroy();
}
if let Some(confined_pointer) = self.confined_pointer.take() {
confined_pointer.destroy();
}
}
}
impl Default for WinitPointerDataInner {
fn default() -> Self {
Self {
surface: None,
locked_pointer: None,
confined_pointer: None,
latest_button_serial: 0,
phase: TouchPhase::Ended,
}
}
}
/// Convert the Wayland button into winit.
fn wayland_button_to_winit(button: u32) -> MouseButton {
// These values are coming from <linux/input-event-codes.h>.
const BTN_LEFT: u32 = 0x110;
const BTN_RIGHT: u32 = 0x111;
const BTN_MIDDLE: u32 = 0x112;
const BTN_SIDE: u32 = 0x113;
const BTN_EXTRA: u32 = 0x114;
const BTN_FORWARD: u32 = 0x115;
const BTN_BACK: u32 = 0x116;
match button {
BTN_LEFT => MouseButton::Left,
BTN_RIGHT => MouseButton::Right,
BTN_MIDDLE => MouseButton::Middle,
BTN_BACK | BTN_SIDE => MouseButton::Back,
BTN_FORWARD | BTN_EXTRA => MouseButton::Forward,
button => MouseButton::Other(button as u16),
}
}
pub trait WinitPointerDataExt {
fn winit_data(&self) -> &WinitPointerData;
}
impl WinitPointerDataExt for WlPointer {
fn winit_data(&self) -> &WinitPointerData {
self.data::<WinitPointerData>().expect("failed to get pointer data.")
}
}
#[derive(Debug)]
pub struct PointerConstraintsState {
pointer_constraints: ZwpPointerConstraintsV1,
}
impl PointerConstraintsState {
pub fn new(
globals: &GlobalList,
queue_handle: &QueueHandle<WinitState>,
) -> Result<Self, BindError> {
let pointer_constraints = globals.bind(queue_handle, 1..=1, GlobalData)?;
Ok(Self { pointer_constraints })
}
}
impl Deref for PointerConstraintsState {
type Target = ZwpPointerConstraintsV1;
fn deref(&self) -> &Self::Target {
&self.pointer_constraints
}
}
impl Dispatch<ZwpPointerConstraintsV1, GlobalData, WinitState> for PointerConstraintsState {
fn event(
_state: &mut WinitState,
_proxy: &ZwpPointerConstraintsV1,
_event: <ZwpPointerConstraintsV1 as wayland_client::Proxy>::Event,
_data: &GlobalData,
_conn: &Connection,
_qhandle: &QueueHandle<WinitState>,
) {
}
}
impl Dispatch<ZwpLockedPointerV1, GlobalData, WinitState> for PointerConstraintsState {
fn event(
_state: &mut WinitState,
_proxy: &ZwpLockedPointerV1,
_event: <ZwpLockedPointerV1 as wayland_client::Proxy>::Event,
_data: &GlobalData,
_conn: &Connection,
_qhandle: &QueueHandle<WinitState>,
) {
}
}
impl Dispatch<ZwpConfinedPointerV1, GlobalData, WinitState> for PointerConstraintsState {
fn event(
_state: &mut WinitState,
_proxy: &ZwpConfinedPointerV1,
_event: <ZwpConfinedPointerV1 as wayland_client::Proxy>::Event,
_data: &GlobalData,
_conn: &Connection,
_qhandle: &QueueHandle<WinitState>,
) {
}
}
impl Dispatch<WpCursorShapeDeviceV1, GlobalData, WinitState> for SeatState {
fn event(
_: &mut WinitState,
_: &WpCursorShapeDeviceV1,
_: <WpCursorShapeDeviceV1 as Proxy>::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
unreachable!("wp_cursor_shape_manager has no events")
}
}
impl Dispatch<WpCursorShapeManagerV1, GlobalData, WinitState> for SeatState {
fn event(
_: &mut WinitState,
_: &WpCursorShapeManagerV1,
_: <WpCursorShapeManagerV1 as Proxy>::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
unreachable!("wp_cursor_device_manager has no events")
}
}
delegate_dispatch!(WinitState: [ WlPointer: WinitPointerData] => SeatState);
delegate_dispatch!(WinitState: [ WpCursorShapeManagerV1: GlobalData] => SeatState);
delegate_dispatch!(WinitState: [ WpCursorShapeDeviceV1: GlobalData] => SeatState);
delegate_dispatch!(WinitState: [ZwpPointerConstraintsV1: GlobalData] => PointerConstraintsState);
delegate_dispatch!(WinitState: [ZwpLockedPointerV1: GlobalData] => PointerConstraintsState);
delegate_dispatch!(WinitState: [ZwpConfinedPointerV1: GlobalData] => PointerConstraintsState);

View file

@ -1,77 +0,0 @@
//! Relative pointer.
use std::ops::Deref;
use sctk::reexports::client::globals::{BindError, GlobalList};
use sctk::reexports::client::{delegate_dispatch, Dispatch};
use sctk::reexports::client::{Connection, QueueHandle};
use sctk::reexports::protocols::wp::relative_pointer::zv1::{
client::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
client::zwp_relative_pointer_v1::{self, ZwpRelativePointerV1},
};
use sctk::globals::GlobalData;
use winit_core::event::DeviceEvent;
use crate::platform_impl::wayland::state::WinitState;
/// Wrapper around the relative pointer.
#[derive(Debug)]
pub struct RelativePointerState {
manager: ZwpRelativePointerManagerV1,
}
impl RelativePointerState {
/// Create new relative pointer manager.
pub fn new(
globals: &GlobalList,
queue_handle: &QueueHandle<WinitState>,
) -> Result<Self, BindError> {
let manager = globals.bind(queue_handle, 1..=1, GlobalData)?;
Ok(Self { manager })
}
}
impl Deref for RelativePointerState {
type Target = ZwpRelativePointerManagerV1;
fn deref(&self) -> &Self::Target {
&self.manager
}
}
impl Dispatch<ZwpRelativePointerManagerV1, GlobalData, WinitState> for RelativePointerState {
fn event(
_state: &mut WinitState,
_proxy: &ZwpRelativePointerManagerV1,
_event: <ZwpRelativePointerManagerV1 as wayland_client::Proxy>::Event,
_data: &GlobalData,
_conn: &Connection,
_qhandle: &QueueHandle<WinitState>,
) {
}
}
impl Dispatch<ZwpRelativePointerV1, GlobalData, WinitState> for RelativePointerState {
fn event(
state: &mut WinitState,
_proxy: &ZwpRelativePointerV1,
event: <ZwpRelativePointerV1 as wayland_client::Proxy>::Event,
_data: &GlobalData,
_conn: &Connection,
_qhandle: &QueueHandle<WinitState>,
) {
let (dx_unaccel, dy_unaccel) = match event {
zwp_relative_pointer_v1::Event::RelativeMotion { dx_unaccel, dy_unaccel, .. } => {
(dx_unaccel, dy_unaccel)
},
_ => return,
};
state
.events_sink
.push_device_event(DeviceEvent::PointerMotion { delta: (dx_unaccel, dy_unaccel) });
}
}
delegate_dispatch!(WinitState: [ZwpRelativePointerV1: GlobalData] => RelativePointerState);
delegate_dispatch!(WinitState: [ZwpRelativePointerManagerV1: GlobalData] => RelativePointerState);

View file

@ -1,200 +0,0 @@
use std::ops::Deref;
use sctk::globals::GlobalData;
use sctk::reexports::client::globals::{BindError, GlobalList};
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_manager_v3::ZwpTextInputManagerV3;
use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::{
ContentHint, ContentPurpose, Event as TextInputEvent, ZwpTextInputV3,
};
use winit_core::event::{Ime, WindowEvent};
use winit_core::window::ImePurpose;
use crate::platform_impl::wayland;
use crate::platform_impl::wayland::state::WinitState;
#[derive(Debug)]
pub struct TextInputState {
text_input_manager: ZwpTextInputManagerV3,
}
impl TextInputState {
pub fn new(
globals: &GlobalList,
queue_handle: &QueueHandle<WinitState>,
) -> Result<Self, BindError> {
let text_input_manager = globals.bind(queue_handle, 1..=1, GlobalData)?;
Ok(Self { text_input_manager })
}
}
impl Deref for TextInputState {
type Target = ZwpTextInputManagerV3;
fn deref(&self) -> &Self::Target {
&self.text_input_manager
}
}
impl Dispatch<ZwpTextInputManagerV3, GlobalData, WinitState> for TextInputState {
fn event(
_state: &mut WinitState,
_proxy: &ZwpTextInputManagerV3,
_event: <ZwpTextInputManagerV3 as Proxy>::Event,
_data: &GlobalData,
_conn: &Connection,
_qhandle: &QueueHandle<WinitState>,
) {
}
}
impl Dispatch<ZwpTextInputV3, TextInputData, WinitState> for TextInputState {
fn event(
state: &mut WinitState,
text_input: &ZwpTextInputV3,
event: <ZwpTextInputV3 as Proxy>::Event,
data: &TextInputData,
_conn: &Connection,
_qhandle: &QueueHandle<WinitState>,
) {
let windows = state.windows.get_mut();
let mut text_input_data = data.inner.lock().unwrap();
match event {
TextInputEvent::Enter { surface } => {
let window_id = wayland::make_wid(&surface);
text_input_data.surface = Some(surface);
let mut window = match windows.get(&window_id) {
Some(window) => window.lock().unwrap(),
None => return,
};
if window.ime_allowed() {
text_input.enable();
text_input.set_content_type_by_purpose(window.ime_purpose());
text_input.commit();
state.events_sink.push_window_event(WindowEvent::Ime(Ime::Enabled), window_id);
}
window.text_input_entered(text_input);
},
TextInputEvent::Leave { surface } => {
text_input_data.surface = None;
// Always issue a disable.
text_input.disable();
text_input.commit();
let window_id = wayland::make_wid(&surface);
// XXX this check is essential, because `leave` could have a
// reference to nil surface...
let mut window = match windows.get(&window_id) {
Some(window) => window.lock().unwrap(),
None => return,
};
window.text_input_left(text_input);
state.events_sink.push_window_event(WindowEvent::Ime(Ime::Disabled), window_id);
},
TextInputEvent::PreeditString { text, cursor_begin, cursor_end } => {
let text = text.unwrap_or_default();
let cursor_begin = usize::try_from(cursor_begin)
.ok()
.and_then(|idx| text.is_char_boundary(idx).then_some(idx));
let cursor_end = usize::try_from(cursor_end)
.ok()
.and_then(|idx| text.is_char_boundary(idx).then_some(idx));
text_input_data.pending_preedit = Some(Preedit { text, cursor_begin, cursor_end })
},
TextInputEvent::CommitString { text } => {
text_input_data.pending_preedit = None;
text_input_data.pending_commit = text;
},
TextInputEvent::Done { .. } => {
let window_id = match text_input_data.surface.as_ref() {
Some(surface) => wayland::make_wid(surface),
None => return,
};
// Clear preedit, unless all we'll be doing next is sending a new preedit.
if text_input_data.pending_commit.is_some()
|| text_input_data.pending_preedit.is_none()
{
state.events_sink.push_window_event(
WindowEvent::Ime(Ime::Preedit(String::new(), None)),
window_id,
);
}
// Send `Commit`.
if let Some(text) = text_input_data.pending_commit.take() {
state
.events_sink
.push_window_event(WindowEvent::Ime(Ime::Commit(text)), window_id);
}
// Send preedit.
if let Some(preedit) = text_input_data.pending_preedit.take() {
let cursor_range =
preedit.cursor_begin.map(|b| (b, preedit.cursor_end.unwrap_or(b)));
state.events_sink.push_window_event(
WindowEvent::Ime(Ime::Preedit(preedit.text, cursor_range)),
window_id,
);
}
},
TextInputEvent::DeleteSurroundingText { .. } => {
// Not handled.
},
_ => {},
}
}
}
pub trait ZwpTextInputV3Ext {
fn set_content_type_by_purpose(&self, purpose: ImePurpose);
}
impl ZwpTextInputV3Ext for ZwpTextInputV3 {
fn set_content_type_by_purpose(&self, purpose: ImePurpose) {
let (hint, purpose) = match purpose {
ImePurpose::Password => (ContentHint::SensitiveData, ContentPurpose::Password),
ImePurpose::Terminal => (ContentHint::None, ContentPurpose::Terminal),
_ => (ContentHint::None, ContentPurpose::Normal),
};
self.set_content_type(hint, purpose);
}
}
/// The Data associated with the text input.
#[derive(Default)]
pub struct TextInputData {
inner: std::sync::Mutex<TextInputDataInner>,
}
#[derive(Default)]
pub struct TextInputDataInner {
/// The `WlSurface` we're performing input to.
surface: Option<WlSurface>,
/// The commit to submit on `done`.
pending_commit: Option<String>,
/// The preedit to submit on `done`.
pending_preedit: Option<Preedit>,
}
/// The state of the preedit.
struct Preedit {
text: String,
cursor_begin: Option<usize>,
cursor_end: Option<usize>,
}
delegate_dispatch!(WinitState: [ZwpTextInputManagerV3: GlobalData] => TextInputState);
delegate_dispatch!(WinitState: [ZwpTextInputV3: TextInputData] => TextInputState);

View file

@ -1,255 +0,0 @@
//! Touch handling.
use dpi::LogicalPosition;
use sctk::reexports::client::protocol::wl_seat::WlSeat;
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::protocol::wl_touch::WlTouch;
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
use sctk::seat::touch::{TouchData, TouchHandler};
use tracing::warn;
use winit_core::event::{
ButtonSource, ElementState, FingerId, PointerKind, PointerSource, WindowEvent,
};
use crate::platform_impl::wayland;
use crate::platform_impl::wayland::state::WinitState;
impl TouchHandler for WinitState {
fn down(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
touch: &WlTouch,
_: u32,
_: u32,
surface: WlSurface,
id: i32,
position: (f64, f64),
) {
let window_id = wayland::make_wid(&surface);
let scale_factor = match self.windows.get_mut().get(&window_id) {
Some(window) => window.lock().unwrap().scale_factor(),
None => return,
};
let seat_state = match self.seats.get_mut(&touch.seat().id()) {
Some(seat_state) => seat_state,
None => {
warn!("Received wl_touch::down without seat");
return;
},
};
// Update the state of the point.
let location = LogicalPosition::<f64>::from(position);
// Only update primary finger once we don't have any touch.
if seat_state.touch_map.is_empty() {
seat_state.first_touch_id = Some(id);
}
let primary = seat_state.first_touch_id == Some(id);
seat_state.touch_map.insert(id, TouchPoint { surface, location });
let position = location.to_physical(scale_factor);
let finger_id = FingerId::from_raw(id as usize);
self.events_sink.push_window_event(
WindowEvent::PointerEntered {
device_id: None,
primary,
position,
kind: PointerKind::Touch(finger_id),
},
window_id,
);
self.events_sink.push_window_event(
WindowEvent::PointerButton {
device_id: None,
primary,
state: ElementState::Pressed,
position,
button: ButtonSource::Touch { finger_id, force: None },
},
window_id,
);
}
fn up(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
touch: &WlTouch,
_: u32,
_: u32,
id: i32,
) {
let seat_state = match self.seats.get_mut(&touch.seat().id()) {
Some(seat_state) => seat_state,
None => {
warn!("Received wl_touch::up without seat");
return;
},
};
// Remove the touch point.
let touch_point = match seat_state.touch_map.remove(&id) {
Some(touch_point) => touch_point,
None => return,
};
// Update the primary touch point.
let primary = seat_state.first_touch_id == Some(id);
// Reset primary finger once all the other fingers are lifted to not transfer primary
// finger to some other finger and still accept it when it's briefly moved between the
// windows.
if seat_state.touch_map.is_empty() {
seat_state.first_touch_id = None;
}
let window_id = wayland::make_wid(&touch_point.surface);
let scale_factor = match self.windows.get_mut().get(&window_id) {
Some(window) => window.lock().unwrap().scale_factor(),
None => return,
};
let position = touch_point.location.to_physical(scale_factor);
let finger_id = FingerId::from_raw(id as usize);
self.events_sink.push_window_event(
WindowEvent::PointerButton {
device_id: None,
primary,
state: ElementState::Released,
position,
button: ButtonSource::Touch { finger_id, force: None },
},
window_id,
);
self.events_sink.push_window_event(
WindowEvent::PointerLeft {
device_id: None,
primary,
position: Some(position),
kind: PointerKind::Touch(finger_id),
},
window_id,
);
}
fn motion(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
touch: &WlTouch,
_: u32,
id: i32,
position: (f64, f64),
) {
let seat_state = match self.seats.get_mut(&touch.seat().id()) {
Some(seat_state) => seat_state,
None => {
warn!("Received wl_touch::motion without seat");
return;
},
};
// Remove the touch point.
let touch_point = match seat_state.touch_map.get_mut(&id) {
Some(touch_point) => touch_point,
None => return,
};
let primary = seat_state.first_touch_id == Some(id);
let window_id = wayland::make_wid(&touch_point.surface);
let scale_factor = match self.windows.get_mut().get(&window_id) {
Some(window) => window.lock().unwrap().scale_factor(),
None => return,
};
touch_point.location = LogicalPosition::<f64>::from(position);
self.events_sink.push_window_event(
WindowEvent::PointerMoved {
device_id: None,
primary,
position: touch_point.location.to_physical(scale_factor),
source: PointerSource::Touch {
finger_id: FingerId::from_raw(id as usize),
force: None,
},
},
window_id,
);
}
fn cancel(&mut self, _: &Connection, _: &QueueHandle<Self>, touch: &WlTouch) {
let seat_state = match self.seats.get_mut(&touch.seat().id()) {
Some(seat_state) => seat_state,
None => {
warn!("Received wl_touch::cancel without seat");
return;
},
};
for (id, touch_point) in seat_state.touch_map.drain() {
let window_id = wayland::make_wid(&touch_point.surface);
let scale_factor = match self.windows.get_mut().get(&window_id) {
Some(window) => window.lock().unwrap().scale_factor(),
None => return,
};
let primary = seat_state.first_touch_id == Some(id);
let position = touch_point.location.to_physical(scale_factor);
self.events_sink.push_window_event(
WindowEvent::PointerLeft {
device_id: None,
primary,
position: Some(position),
kind: PointerKind::Touch(FingerId::from_raw(id as usize)),
},
window_id,
);
}
seat_state.first_touch_id = None;
}
fn shape(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: &WlTouch,
_: i32,
_: f64,
_: f64,
) {
// Blank.
}
fn orientation(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlTouch, _: i32, _: f64) {
// Blank.
}
}
/// The state of the touch point.
#[derive(Debug)]
pub struct TouchPoint {
/// The surface on which the point is present.
pub surface: WlSurface,
/// The location of the point on the surface.
pub location: LogicalPosition<f64>,
}
pub trait TouchDataExt {
fn seat(&self) -> &WlSeat;
}
impl TouchDataExt for WlTouch {
fn seat(&self) -> &WlSeat {
self.data::<TouchData>().expect("failed to get touch data.").seat()
}
}
sctk::delegate_touch!(WinitState);

View file

@ -1,438 +0,0 @@
use std::cell::RefCell;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex};
use ahash::AHashMap;
use sctk::compositor::{CompositorHandler, CompositorState};
use sctk::output::{OutputHandler, OutputState};
use sctk::reexports::calloop::LoopHandle;
use sctk::reexports::client::backend::ObjectId;
use sctk::reexports::client::globals::GlobalList;
use sctk::reexports::client::protocol::wl_output::WlOutput;
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
use sctk::registry::{ProvidesRegistryState, RegistryState};
use sctk::seat::pointer::ThemedPointer;
use sctk::seat::SeatState;
use sctk::shell::xdg::window::{Window, WindowConfigure, WindowHandler};
use sctk::shell::xdg::XdgShell;
use sctk::shell::WaylandSurface;
use sctk::shm::slot::SlotPool;
use sctk::shm::{Shm, ShmHandler};
use sctk::subcompositor::SubcompositorState;
use winit_core::error::OsError;
use crate::platform_impl::wayland::event_loop::sink::EventSink;
use crate::platform_impl::wayland::output::MonitorHandle;
use crate::platform_impl::wayland::seat::{
PointerConstraintsState, RelativePointerState, TextInputState, WinitPointerData,
WinitPointerDataExt, WinitSeatState,
};
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
use crate::platform_impl::wayland::types::wp_fractional_scaling::FractionalScalingManager;
use crate::platform_impl::wayland::types::wp_viewporter::ViewporterState;
use crate::platform_impl::wayland::types::xdg_activation::XdgActivationState;
use crate::platform_impl::wayland::window::{WindowRequests, WindowState};
use crate::platform_impl::wayland::WindowId;
/// Winit's Wayland state.
#[derive(Debug)]
pub struct WinitState {
/// The WlRegistry.
pub registry_state: RegistryState,
/// The state of the WlOutput handling.
pub output_state: OutputState,
/// The compositor state which is used to create new windows and regions.
pub compositor_state: Arc<CompositorState>,
/// The state of the subcompositor.
pub subcompositor_state: Option<Arc<SubcompositorState>>,
/// The seat state responsible for all sorts of input.
pub seat_state: SeatState,
/// The shm for software buffers, such as cursors.
pub shm: Shm,
/// The pool where custom cursors are allocated.
pub custom_cursor_pool: Arc<Mutex<SlotPool>>,
/// The XDG shell that is used for windows.
pub xdg_shell: XdgShell,
/// The currently present windows.
pub windows: RefCell<AHashMap<WindowId, Arc<Mutex<WindowState>>>>,
/// The requests from the `Window` to EventLoop, such as close operations and redraw requests.
pub window_requests: RefCell<AHashMap<WindowId, Arc<WindowRequests>>>,
/// The events that were generated directly from the window.
pub window_events_sink: Arc<Mutex<EventSink>>,
/// The update for the `windows` coming from the compositor.
pub window_compositor_updates: Vec<WindowCompositorUpdate>,
/// Currently handled seats.
pub seats: AHashMap<ObjectId, WinitSeatState>,
/// Currently present cursor surfaces.
pub pointer_surfaces: AHashMap<ObjectId, Arc<ThemedPointer<WinitPointerData>>>,
/// The state of the text input on the client.
pub text_input_state: Option<TextInputState>,
/// Observed monitors.
pub monitors: Arc<Mutex<Vec<MonitorHandle>>>,
/// Sink to accumulate window events from the compositor, which is latter dispatched in
/// event loop run.
pub events_sink: EventSink,
/// Xdg activation.
pub xdg_activation: Option<XdgActivationState>,
/// Relative pointer.
pub relative_pointer: Option<RelativePointerState>,
/// Pointer constraints to handle pointer locking and confining.
pub pointer_constraints: Option<Arc<PointerConstraintsState>>,
/// Viewporter state on the given window.
pub viewporter_state: Option<ViewporterState>,
/// Fractional scaling manager.
pub fractional_scaling_manager: Option<FractionalScalingManager>,
/// KWin blur manager.
pub kwin_blur_manager: Option<KWinBlurManager>,
/// Loop handle to re-register event sources, such as keyboard repeat.
pub loop_handle: LoopHandle<'static, Self>,
/// Whether we have dispatched events to the user thus we want to
/// send `AboutToWait` and normally wakeup the user.
pub dispatched_events: bool,
/// Whether the user initiated a wake up.
pub proxy_wake_up: bool,
}
impl WinitState {
pub fn new(
globals: &GlobalList,
queue_handle: &QueueHandle<Self>,
loop_handle: LoopHandle<'static, WinitState>,
) -> Result<Self, OsError> {
let registry_state = RegistryState::new(globals);
let compositor_state =
CompositorState::bind(globals, queue_handle).map_err(|err| os_error!(err))?;
let subcompositor_state = match SubcompositorState::bind(
compositor_state.wl_compositor().clone(),
globals,
queue_handle,
) {
Ok(c) => Some(c),
Err(e) => {
tracing::warn!("Subcompositor protocol not available, ignoring CSD: {e:?}");
None
},
};
let output_state = OutputState::new(globals, queue_handle);
let monitors = output_state.outputs().map(MonitorHandle::new).collect();
let seat_state = SeatState::new(globals, queue_handle);
let mut seats = AHashMap::default();
for seat in seat_state.seats() {
seats.insert(seat.id(), WinitSeatState::new());
}
let (viewporter_state, fractional_scaling_manager) =
if let Ok(fsm) = FractionalScalingManager::new(globals, queue_handle) {
(ViewporterState::new(globals, queue_handle).ok(), Some(fsm))
} else {
(None, None)
};
let shm = Shm::bind(globals, queue_handle).map_err(|err| os_error!(err))?;
let custom_cursor_pool = Arc::new(Mutex::new(SlotPool::new(2, &shm).unwrap()));
Ok(Self {
registry_state,
compositor_state: Arc::new(compositor_state),
subcompositor_state: subcompositor_state.map(Arc::new),
output_state,
seat_state,
shm,
custom_cursor_pool,
xdg_shell: XdgShell::bind(globals, queue_handle).map_err(|err| os_error!(err))?,
xdg_activation: XdgActivationState::bind(globals, queue_handle).ok(),
windows: Default::default(),
window_requests: Default::default(),
window_compositor_updates: Vec::new(),
window_events_sink: Default::default(),
viewporter_state,
fractional_scaling_manager,
kwin_blur_manager: KWinBlurManager::new(globals, queue_handle).ok(),
seats,
text_input_state: TextInputState::new(globals, queue_handle).ok(),
relative_pointer: RelativePointerState::new(globals, queue_handle).ok(),
pointer_constraints: PointerConstraintsState::new(globals, queue_handle)
.map(Arc::new)
.ok(),
pointer_surfaces: Default::default(),
monitors: Arc::new(Mutex::new(monitors)),
events_sink: EventSink::new(),
loop_handle,
// Make it true by default.
dispatched_events: true,
proxy_wake_up: false,
})
}
pub fn scale_factor_changed(
&mut self,
surface: &WlSurface,
scale_factor: f64,
is_legacy: bool,
) {
// Check if the cursor surface.
let window_id = super::make_wid(surface);
if let Some(window) = self.windows.get_mut().get(&window_id) {
// Don't update the scaling factor, when legacy method is used.
if is_legacy && self.fractional_scaling_manager.is_some() {
return;
}
// The scale factor change is for the window.
let pos = if let Some(pos) = self
.window_compositor_updates
.iter()
.position(|update| update.window_id == window_id)
{
pos
} else {
self.window_compositor_updates.push(WindowCompositorUpdate::new(window_id));
self.window_compositor_updates.len() - 1
};
// Update the scale factor right away.
window.lock().unwrap().set_scale_factor(scale_factor);
self.window_compositor_updates[pos].scale_changed = true;
} else if let Some(pointer) = self.pointer_surfaces.get(&surface.id()) {
// Get the window, where the pointer resides right now.
let focused_window = match pointer.pointer().winit_data().focused_window() {
Some(focused_window) => focused_window,
None => return,
};
if let Some(window_state) = self.windows.get_mut().get(&focused_window) {
window_state.lock().unwrap().reload_cursor_style()
}
}
}
pub fn queue_close(updates: &mut Vec<WindowCompositorUpdate>, window_id: WindowId) {
let pos = if let Some(pos) = updates.iter().position(|update| update.window_id == window_id)
{
pos
} else {
updates.push(WindowCompositorUpdate::new(window_id));
updates.len() - 1
};
updates[pos].close_window = true;
}
}
impl ShmHandler for WinitState {
fn shm_state(&mut self) -> &mut Shm {
&mut self.shm
}
}
impl WindowHandler for WinitState {
fn request_close(&mut self, _: &Connection, _: &QueueHandle<Self>, window: &Window) {
let window_id = super::make_wid(window.wl_surface());
Self::queue_close(&mut self.window_compositor_updates, window_id);
}
fn configure(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
window: &Window,
configure: WindowConfigure,
_serial: u32,
) {
let window_id = super::make_wid(window.wl_surface());
let pos = if let Some(pos) =
self.window_compositor_updates.iter().position(|update| update.window_id == window_id)
{
pos
} else {
self.window_compositor_updates.push(WindowCompositorUpdate::new(window_id));
self.window_compositor_updates.len() - 1
};
// Populate the configure to the window.
self.window_compositor_updates[pos].resized |= self
.windows
.get_mut()
.get_mut(&window_id)
.expect("got configure for dead window.")
.lock()
.unwrap()
.configure(configure, &self.shm, &self.subcompositor_state);
// NOTE: configure demands wl_surface::commit, however winit doesn't commit on behalf of the
// users, since it can break a lot of things, thus it'll ask users to redraw instead.
self.window_requests
.get_mut()
.get(&window_id)
.unwrap()
.redraw_requested
.store(true, Ordering::Relaxed);
// Manually mark that we've got an event, since configure may not generate a resize.
self.dispatched_events = true;
}
}
impl OutputHandler for WinitState {
fn output_state(&mut self) -> &mut OutputState {
&mut self.output_state
}
fn new_output(&mut self, _: &Connection, _: &QueueHandle<Self>, output: WlOutput) {
self.monitors.lock().unwrap().push(MonitorHandle::new(output));
}
fn update_output(&mut self, _: &Connection, _: &QueueHandle<Self>, updated: WlOutput) {
let mut monitors = self.monitors.lock().unwrap();
let updated = MonitorHandle::new(updated);
if let Some(pos) = monitors.iter().position(|output| output == &updated) {
monitors[pos] = updated
} else {
monitors.push(updated)
}
}
fn output_destroyed(&mut self, _: &Connection, _: &QueueHandle<Self>, removed: WlOutput) {
let mut monitors = self.monitors.lock().unwrap();
let removed = MonitorHandle::new(removed);
if let Some(pos) = monitors.iter().position(|output| output == &removed) {
monitors.remove(pos);
}
}
}
impl CompositorHandler for WinitState {
fn transform_changed(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: &WlSurface,
_: wayland_client::protocol::wl_output::Transform,
) {
// TODO(kchibisov) we need to expose it somehow in winit.
}
fn surface_enter(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: &WlSurface,
_: &WlOutput,
) {
}
fn surface_leave(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: &WlSurface,
_: &WlOutput,
) {
}
fn scale_factor_changed(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
surface: &WlSurface,
scale_factor: i32,
) {
self.scale_factor_changed(surface, scale_factor as f64, true)
}
fn frame(&mut self, _: &Connection, _: &QueueHandle<Self>, surface: &WlSurface, _: u32) {
let window_id = super::make_wid(surface);
let window = match self.windows.get_mut().get(&window_id) {
Some(window) => window,
None => return,
};
// In case we have a redraw requested we must indicate the wake up.
if self
.window_requests
.get_mut()
.get(&window_id)
.unwrap()
.redraw_requested
.load(Ordering::Relaxed)
{
self.dispatched_events = true;
}
window.lock().unwrap().frame_callback_received();
}
}
impl ProvidesRegistryState for WinitState {
sctk::registry_handlers![OutputState, SeatState];
fn registry(&mut self) -> &mut RegistryState {
&mut self.registry_state
}
}
// The window update coming from the compositor.
#[derive(Debug, Clone, Copy)]
pub struct WindowCompositorUpdate {
/// The id of the window this updates belongs to.
pub window_id: WindowId,
/// New window size.
pub resized: bool,
/// New scale factor.
pub scale_changed: bool,
/// Close the window.
pub close_window: bool,
}
impl WindowCompositorUpdate {
fn new(window_id: WindowId) -> Self {
Self { window_id, resized: false, scale_changed: false, close_window: false }
}
}
sctk::delegate_subcompositor!(WinitState);
sctk::delegate_compositor!(WinitState);
sctk::delegate_output!(WinitState);
sctk::delegate_registry!(WinitState);
sctk::delegate_shm!(WinitState);
sctk::delegate_xdg_shell!(WinitState);
sctk::delegate_xdg_window!(WinitState);

View file

@ -1,67 +0,0 @@
use cursor_icon::CursorIcon;
use sctk::reexports::client::protocol::wl_shm::Format;
use sctk::shm::slot::{Buffer, SlotPool};
use winit_core::cursor::{CursorImage, CustomCursorProvider};
// Wrap in our own type to not impl trait on global type.
#[derive(Debug)]
pub struct WaylandCustomCursor(pub(crate) CursorImage);
impl CustomCursorProvider for WaylandCustomCursor {
fn is_animated(&self) -> bool {
false
}
}
#[derive(Debug)]
pub enum SelectedCursor {
Named(CursorIcon),
Custom(CustomCursor),
}
impl Default for SelectedCursor {
fn default() -> Self {
Self::Named(Default::default())
}
}
#[derive(Debug)]
pub struct CustomCursor {
pub buffer: Buffer,
pub w: i32,
pub h: i32,
pub hotspot_x: i32,
pub hotspot_y: i32,
}
impl CustomCursor {
pub(crate) fn new(pool: &mut SlotPool, image: &WaylandCustomCursor) -> Self {
let image = &image.0;
let (buffer, canvas) = pool
.create_buffer(
image.width() as i32,
image.height() as i32,
4 * (image.width() as i32),
Format::Argb8888,
)
.unwrap();
for (canvas_chunk, rgba) in canvas.chunks_exact_mut(4).zip(image.buffer().chunks_exact(4)) {
// Alpha in buffer is premultiplied.
let alpha = rgba[3] as f32 / 255.;
let r = (rgba[0] as f32 * alpha) as u32;
let g = (rgba[1] as f32 * alpha) as u32;
let b = (rgba[2] as f32 * alpha) as u32;
let color = ((rgba[3] as u32) << 24) + (r << 16) + (g << 8) + b;
let array: &mut [u8; 4] = canvas_chunk.try_into().unwrap();
*array = color.to_le_bytes();
}
CustomCursor {
buffer,
w: image.width() as i32,
h: image.height() as i32,
hotspot_x: image.hotspot_x() as i32,
hotspot_y: image.hotspot_y() as i32,
}
}
}

View file

@ -1,67 +0,0 @@
//! Handling of KDE-compatible blur.
use sctk::globals::GlobalData;
use sctk::reexports::client::globals::{BindError, GlobalList};
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur_manager::OrgKdeKwinBlurManager;
use crate::platform_impl::wayland::state::WinitState;
/// KWin blur manager.
#[derive(Debug, Clone)]
pub struct KWinBlurManager {
manager: OrgKdeKwinBlurManager,
}
impl KWinBlurManager {
pub fn new(
globals: &GlobalList,
queue_handle: &QueueHandle<WinitState>,
) -> Result<Self, BindError> {
let manager = globals.bind(queue_handle, 1..=1, GlobalData)?;
Ok(Self { manager })
}
pub fn blur(
&self,
surface: &WlSurface,
queue_handle: &QueueHandle<WinitState>,
) -> OrgKdeKwinBlur {
self.manager.create(surface, queue_handle, ())
}
pub fn unset(&self, surface: &WlSurface) {
self.manager.unset(surface)
}
}
impl Dispatch<OrgKdeKwinBlurManager, GlobalData, WinitState> for KWinBlurManager {
fn event(
_: &mut WinitState,
_: &OrgKdeKwinBlurManager,
_: <OrgKdeKwinBlurManager as Proxy>::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
unreachable!("no events defined for org_kde_kwin_blur_manager");
}
}
impl Dispatch<OrgKdeKwinBlur, (), WinitState> for KWinBlurManager {
fn event(
_: &mut WinitState,
_: &OrgKdeKwinBlur,
_: <OrgKdeKwinBlur as Proxy>::Event,
_: &(),
_: &Connection,
_: &QueueHandle<WinitState>,
) {
unreachable!("no events defined for org_kde_kwin_blur");
}
}
delegate_dispatch!(WinitState: [OrgKdeKwinBlurManager: GlobalData] => KWinBlurManager);
delegate_dispatch!(WinitState: [OrgKdeKwinBlur: ()] => KWinBlurManager);

View file

@ -1,7 +0,0 @@
//! Wayland protocol implementation boilerplate.
pub mod cursor;
pub mod kwin_blur;
pub mod wp_fractional_scaling;
pub mod wp_viewporter;
pub mod xdg_activation;

View file

@ -1,77 +0,0 @@
//! Handling of the fractional scaling.
use sctk::globals::GlobalData;
use sctk::reexports::client::globals::{BindError, GlobalList};
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
use sctk::reexports::protocols::wp::fractional_scale::v1::client::wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1;
use sctk::reexports::protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::{
Event as FractionalScalingEvent, WpFractionalScaleV1,
};
use crate::platform_impl::wayland::state::WinitState;
/// The scaling factor denominator.
const SCALE_DENOMINATOR: f64 = 120.;
/// Fractional scaling manager.
#[derive(Debug)]
pub struct FractionalScalingManager {
manager: WpFractionalScaleManagerV1,
}
pub struct FractionalScaling {
/// The surface used for scaling.
surface: WlSurface,
}
impl FractionalScalingManager {
/// Create new viewporter.
pub fn new(
globals: &GlobalList,
queue_handle: &QueueHandle<WinitState>,
) -> Result<Self, BindError> {
let manager = globals.bind(queue_handle, 1..=1, GlobalData)?;
Ok(Self { manager })
}
pub fn fractional_scaling(
&self,
surface: &WlSurface,
queue_handle: &QueueHandle<WinitState>,
) -> WpFractionalScaleV1 {
let data = FractionalScaling { surface: surface.clone() };
self.manager.get_fractional_scale(surface, queue_handle, data)
}
}
impl Dispatch<WpFractionalScaleManagerV1, GlobalData, WinitState> for FractionalScalingManager {
fn event(
_: &mut WinitState,
_: &WpFractionalScaleManagerV1,
_: <WpFractionalScaleManagerV1 as Proxy>::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
// No events.
}
}
impl Dispatch<WpFractionalScaleV1, FractionalScaling, WinitState> for FractionalScalingManager {
fn event(
state: &mut WinitState,
_: &WpFractionalScaleV1,
event: <WpFractionalScaleV1 as Proxy>::Event,
data: &FractionalScaling,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
if let FractionalScalingEvent::PreferredScale { scale } = event {
state.scale_factor_changed(&data.surface, scale as f64 / SCALE_DENOMINATOR, false);
}
}
}
delegate_dispatch!(WinitState: [WpFractionalScaleManagerV1: GlobalData] => FractionalScalingManager);
delegate_dispatch!(WinitState: [WpFractionalScaleV1: FractionalScaling] => FractionalScalingManager);

View file

@ -1,64 +0,0 @@
//! Handling of the wp-viewporter.
use sctk::globals::GlobalData;
use sctk::reexports::client::globals::{BindError, GlobalList};
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
use sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport;
use sctk::reexports::protocols::wp::viewporter::client::wp_viewporter::WpViewporter;
use crate::platform_impl::wayland::state::WinitState;
/// Viewporter.
#[derive(Debug)]
pub struct ViewporterState {
viewporter: WpViewporter,
}
impl ViewporterState {
/// Create new viewporter.
pub fn new(
globals: &GlobalList,
queue_handle: &QueueHandle<WinitState>,
) -> Result<Self, BindError> {
let viewporter = globals.bind(queue_handle, 1..=1, GlobalData)?;
Ok(Self { viewporter })
}
/// Get the viewport for the given object.
pub fn get_viewport(
&self,
surface: &WlSurface,
queue_handle: &QueueHandle<WinitState>,
) -> WpViewport {
self.viewporter.get_viewport(surface, queue_handle, GlobalData)
}
}
impl Dispatch<WpViewporter, GlobalData, WinitState> for ViewporterState {
fn event(
_: &mut WinitState,
_: &WpViewporter,
_: <WpViewporter as Proxy>::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
// No events.
}
}
impl Dispatch<WpViewport, GlobalData, WinitState> for ViewporterState {
fn event(
_: &mut WinitState,
_: &WpViewport,
_: <WpViewport as Proxy>::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
// No events.
}
}
delegate_dispatch!(WinitState: [WpViewporter: GlobalData] => ViewporterState);
delegate_dispatch!(WinitState: [WpViewport: GlobalData] => ViewporterState);

View file

@ -1,102 +0,0 @@
//! Handling of xdg activation, which is used for user attention requests.
use std::sync::atomic::AtomicBool;
use std::sync::Weak;
use sctk::globals::GlobalData;
use sctk::reexports::client::globals::{BindError, GlobalList};
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_token_v1::{
Event as ActivationTokenEvent, XdgActivationTokenV1,
};
use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_v1::XdgActivationV1;
use winit_core::event_loop::AsyncRequestSerial;
use winit_core::window::{ActivationToken, WindowId};
use crate::platform_impl::wayland::state::WinitState;
#[derive(Debug)]
pub struct XdgActivationState {
xdg_activation: XdgActivationV1,
}
impl XdgActivationState {
pub fn bind(
globals: &GlobalList,
queue_handle: &QueueHandle<WinitState>,
) -> Result<Self, BindError> {
let xdg_activation = globals.bind(queue_handle, 1..=1, GlobalData)?;
Ok(Self { xdg_activation })
}
pub fn global(&self) -> &XdgActivationV1 {
&self.xdg_activation
}
}
impl Dispatch<XdgActivationV1, GlobalData, WinitState> for XdgActivationState {
fn event(
_state: &mut WinitState,
_proxy: &XdgActivationV1,
_event: <XdgActivationV1 as Proxy>::Event,
_data: &GlobalData,
_conn: &Connection,
_qhandle: &QueueHandle<WinitState>,
) {
}
}
impl Dispatch<XdgActivationTokenV1, XdgActivationTokenData, WinitState> for XdgActivationState {
fn event(
state: &mut WinitState,
proxy: &XdgActivationTokenV1,
event: <XdgActivationTokenV1 as Proxy>::Event,
data: &XdgActivationTokenData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
let token = match event {
ActivationTokenEvent::Done { token } => token,
_ => return,
};
let global = state
.xdg_activation
.as_ref()
.expect("got xdg_activation event without global.")
.global();
match data {
XdgActivationTokenData::Attention((surface, fence)) => {
global.activate(token, surface);
// Mark that no request attention is in process.
if let Some(attention_requested) = fence.upgrade() {
attention_requested.store(false, std::sync::atomic::Ordering::Relaxed);
}
},
XdgActivationTokenData::Obtain((window_id, serial)) => {
state.events_sink.push_window_event(
winit_core::event::WindowEvent::ActivationTokenDone {
serial: *serial,
token: ActivationToken::from_raw(token),
},
*window_id,
);
},
}
proxy.destroy();
}
}
/// The data associated with the activation request.
pub enum XdgActivationTokenData {
/// Request user attention for the given surface.
Attention((WlSurface, Weak<AtomicBool>)),
/// Get a token to be passed outside of the winit.
Obtain((WindowId, AsyncRequestSerial)),
}
delegate_dispatch!(WinitState: [ XdgActivationV1: GlobalData] => XdgActivationState);
delegate_dispatch!(WinitState: [ XdgActivationTokenV1: XdgActivationTokenData] => XdgActivationState);

View file

@ -1,687 +0,0 @@
//! The Wayland window.
use std::ffi::c_void;
use std::ptr::NonNull;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use dpi::{LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use sctk::compositor::{CompositorState, Region, SurfaceData};
use sctk::reexports::client::protocol::wl_display::WlDisplay;
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{Proxy, QueueHandle};
use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_v1::XdgActivationV1;
use sctk::shell::xdg::window::{Window as SctkWindow, WindowDecorations};
use sctk::shell::WaylandSurface;
use tracing::warn;
use winit_core::cursor::Cursor;
use winit_core::error::{NotSupportedError, RequestError};
use winit_core::event::{Ime, WindowEvent};
use winit_core::event_loop::AsyncRequestSerial;
use winit_core::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle};
use winit_core::window::{
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow,
WindowAttributes, WindowButtons, WindowId, WindowLevel,
};
use super::event_loop::sink::EventSink;
use super::output::MonitorHandle;
use super::state::WinitState;
use super::types::xdg_activation::XdgActivationTokenData;
use super::ActiveEventLoop;
use crate::platform::wayland::WindowAttributesWayland;
use crate::platform_impl::wayland::output;
pub(crate) mod state;
pub use state::WindowState;
/// The Wayland window.
#[derive(Debug)]
pub struct Window {
/// Reference to the underlying SCTK window.
window: SctkWindow,
/// Window id.
window_id: WindowId,
/// The state of the window.
window_state: Arc<Mutex<WindowState>>,
/// Compositor to handle WlRegion stuff.
compositor: Arc<CompositorState>,
/// The wayland display used solely for raw window handle.
#[allow(dead_code)]
display: WlDisplay,
/// Xdg activation to request user attention.
xdg_activation: Option<XdgActivationV1>,
/// The state of the requested attention from the `xdg_activation`.
attention_requested: Arc<AtomicBool>,
/// Handle to the main queue to perform requests.
queue_handle: QueueHandle<WinitState>,
/// Window requests to the event loop.
window_requests: Arc<WindowRequests>,
/// Observed monitors.
monitors: Arc<Mutex<Vec<MonitorHandle>>>,
/// Source to wake-up the event-loop for window requests.
event_loop_awakener: calloop::ping::Ping,
/// The event sink to deliver synthetic events.
window_events_sink: Arc<Mutex<EventSink>>,
}
impl Window {
pub(crate) fn new(
event_loop_window_target: &ActiveEventLoop,
mut attributes: WindowAttributes,
) -> Result<Self, RequestError> {
let queue_handle = event_loop_window_target.queue_handle.clone();
let mut state = event_loop_window_target.state.borrow_mut();
let monitors = state.monitors.clone();
let surface = state.compositor_state.create_surface(&queue_handle);
let compositor = state.compositor_state.clone();
let xdg_activation =
state.xdg_activation.as_ref().map(|activation_state| activation_state.global().clone());
let display = event_loop_window_target.handle.connection.display();
let size: Size = attributes.surface_size.unwrap_or(LogicalSize::new(800., 600.).into());
// We prefer server side decorations, however to not have decorations we ask for client
// side decorations instead.
let default_decorations = if attributes.decorations {
WindowDecorations::RequestServer
} else {
WindowDecorations::RequestClient
};
let window =
state.xdg_shell.create_window(surface.clone(), default_decorations, &queue_handle);
let mut window_state = WindowState::new(
event_loop_window_target.handle.clone(),
&event_loop_window_target.queue_handle,
&state,
size,
window.clone(),
attributes.preferred_theme,
);
// Set transparency hint.
window_state.set_transparent(attributes.transparent);
window_state.set_blur(attributes.blur);
// Set the decorations hint.
window_state.set_decorate(attributes.decorations);
let (app_name, activation_token) =
match attributes.platform.take().and_then(|p| p.cast::<WindowAttributesWayland>().ok())
{
Some(attrs) => (attrs.name, attrs.activation_token),
None => (None, None),
};
// Set the app_id.
if let Some(name) = app_name.map(|name| name.general) {
window.set_app_id(name);
}
// Set the window title.
window_state.set_title(attributes.title);
// Set the min and max sizes. We must set the hints upon creating a window, so
// we use the default `1.` scaling...
let min_size = attributes.min_surface_size.map(|size| size.to_logical(1.));
let max_size = attributes.max_surface_size.map(|size| size.to_logical(1.));
window_state.set_min_surface_size(min_size);
window_state.set_max_surface_size(max_size);
// Non-resizable implies that the min and max sizes are set to the same value.
window_state.set_resizable(attributes.resizable);
// Set startup mode.
match attributes.fullscreen {
Some(Fullscreen::Exclusive(..)) => {
warn!("`Fullscreen::Exclusive` is ignored on Wayland");
},
#[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))]
Some(Fullscreen::Borderless(monitor)) => {
let output = monitor.as_ref().and_then(|monitor| {
monitor.cast_ref::<output::MonitorHandle>().map(|handle| &handle.proxy)
});
window.set_fullscreen(output)
},
_ if attributes.maximized => window.set_maximized(),
_ => (),
};
match attributes.cursor {
Cursor::Icon(icon) => window_state.set_cursor(icon),
Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
}
// Activate the window when the token is passed.
if let (Some(xdg_activation), Some(token)) = (xdg_activation.as_ref(), activation_token) {
xdg_activation.activate(token.into_raw(), &surface);
}
// XXX Do initial commit.
window.commit();
// Add the window and window requests into the state.
let window_state = Arc::new(Mutex::new(window_state));
let window_id = super::make_wid(&surface);
state.windows.get_mut().insert(window_id, window_state.clone());
let window_requests = WindowRequests {
redraw_requested: AtomicBool::new(true),
closed: AtomicBool::new(false),
};
let window_requests = Arc::new(window_requests);
state.window_requests.get_mut().insert(window_id, window_requests.clone());
// Setup the event sync to insert `WindowEvents` right from the window.
let window_events_sink = state.window_events_sink.clone();
let mut wayland_source = event_loop_window_target.wayland_dispatcher.as_source_mut();
let event_queue = wayland_source.queue();
// Do a roundtrip.
event_queue.roundtrip(&mut state).map_err(|err| os_error!(err))?;
// XXX Wait for the initial configure to arrive.
while !window_state.lock().unwrap().is_configured() {
event_queue.blocking_dispatch(&mut state).map_err(|err| os_error!(err))?;
}
// Wake-up event loop, so it'll send initial redraw requested.
let event_loop_awakener = event_loop_window_target.event_loop_awakener.clone();
event_loop_awakener.ping();
Ok(Self {
window,
display,
monitors,
window_id,
compositor,
window_state,
queue_handle,
xdg_activation,
attention_requested: Arc::new(AtomicBool::new(false)),
event_loop_awakener,
window_requests,
window_events_sink,
})
}
pub(crate) fn xdg_toplevel(&self) -> Option<NonNull<c_void>> {
NonNull::new(self.window.xdg_toplevel().id().as_ptr().cast())
}
}
impl Window {
pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError> {
let xdg_activation = match self.xdg_activation.as_ref() {
Some(xdg_activation) => xdg_activation,
None => return Err(NotSupportedError::new("xdg_activation_v1 is not available").into()),
};
let serial = AsyncRequestSerial::get();
let data = XdgActivationTokenData::Obtain((self.window_id, serial));
let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
xdg_activation_token.set_surface(self.surface());
xdg_activation_token.commit();
Ok(serial)
}
#[inline]
pub fn surface(&self) -> &WlSurface {
self.window.wl_surface()
}
}
impl Drop for Window {
fn drop(&mut self) {
self.window_requests.closed.store(true, Ordering::Relaxed);
self.event_loop_awakener.ping();
}
}
impl rwh_06::HasWindowHandle for Window {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::WaylandWindowHandle::new({
let ptr = self.window.wl_surface().id().as_ptr();
std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
});
unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw.into())) }
}
}
impl rwh_06::HasDisplayHandle for Window {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = rwh_06::WaylandDisplayHandle::new({
let ptr = self.display.id().as_ptr();
std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null")
});
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw.into())) }
}
}
impl CoreWindow for Window {
fn id(&self) -> WindowId {
self.window_id
}
fn request_redraw(&self) {
// NOTE: try to not wake up the loop when the event was already scheduled and not yet
// processed by the loop, because if at this point the value was `true` it could only
// mean that the loop still haven't dispatched the value to the client and will do
// eventually, resetting it to `false`.
if self
.window_requests
.redraw_requested
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
self.event_loop_awakener.ping();
}
}
#[inline]
fn title(&self) -> String {
self.window_state.lock().unwrap().title().to_owned()
}
fn pre_present_notify(&self) {
self.window_state.lock().unwrap().request_frame_callback();
}
fn reset_dead_keys(&self) {
winit_common::xkb::reset_dead_keys()
}
fn surface_position(&self) -> PhysicalPosition<i32> {
(0, 0).into()
}
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
Err(NotSupportedError::new("window position information is not available on Wayland")
.into())
}
fn set_outer_position(&self, _position: Position) {
// Not possible.
}
fn surface_size(&self) -> PhysicalSize<u32> {
let window_state = self.window_state.lock().unwrap();
let scale_factor = window_state.scale_factor();
super::logical_to_physical_rounded(window_state.surface_size(), scale_factor)
}
fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
let mut window_state = self.window_state.lock().unwrap();
let new_size = window_state.request_surface_size(size);
self.request_redraw();
Some(new_size)
}
fn outer_size(&self) -> PhysicalSize<u32> {
let window_state = self.window_state.lock().unwrap();
let scale_factor = window_state.scale_factor();
super::logical_to_physical_rounded(window_state.outer_size(), scale_factor)
}
fn safe_area(&self) -> PhysicalInsets<u32> {
PhysicalInsets::new(0, 0, 0, 0)
}
fn set_min_surface_size(&self, min_size: Option<Size>) {
let scale_factor = self.scale_factor();
let min_size = min_size.map(|size| size.to_logical(scale_factor));
self.window_state.lock().unwrap().set_min_surface_size(min_size);
// NOTE: Requires commit to be applied.
self.request_redraw();
}
/// Set the maximum surface size for the window.
#[inline]
fn set_max_surface_size(&self, max_size: Option<Size>) {
let scale_factor = self.scale_factor();
let max_size = max_size.map(|size| size.to_logical(scale_factor));
self.window_state.lock().unwrap().set_max_surface_size(max_size);
// NOTE: Requires commit to be applied.
self.request_redraw();
}
fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
None
}
fn set_surface_resize_increments(&self, _increments: Option<Size>) {
warn!("`set_surface_resize_increments` is not implemented for Wayland");
}
fn set_title(&self, title: &str) {
let new_title = title.to_string();
self.window_state.lock().unwrap().set_title(new_title);
}
#[inline]
fn set_transparent(&self, transparent: bool) {
self.window_state.lock().unwrap().set_transparent(transparent);
}
fn set_visible(&self, _visible: bool) {
// Not possible on Wayland.
}
fn is_visible(&self) -> Option<bool> {
None
}
fn set_resizable(&self, resizable: bool) {
if self.window_state.lock().unwrap().set_resizable(resizable) {
// NOTE: Requires commit to be applied.
self.request_redraw();
}
}
fn is_resizable(&self) -> bool {
self.window_state.lock().unwrap().resizable()
}
fn set_enabled_buttons(&self, _buttons: WindowButtons) {
// TODO(kchibisov) v5 of the xdg_shell allows that.
}
fn enabled_buttons(&self) -> WindowButtons {
// TODO(kchibisov) v5 of the xdg_shell allows that.
WindowButtons::all()
}
fn set_minimized(&self, minimized: bool) {
// You can't unminimize the window on Wayland.
if !minimized {
warn!("Unminimizing is ignored on Wayland.");
return;
}
self.window.set_minimized();
}
fn is_minimized(&self) -> Option<bool> {
// XXX clients don't know whether they are minimized or not.
None
}
fn set_maximized(&self, maximized: bool) {
if maximized {
self.window.set_maximized()
} else {
self.window.unset_maximized()
}
}
fn is_maximized(&self) -> bool {
self.window_state
.lock()
.unwrap()
.last_configure
.as_ref()
.map(|last_configure| last_configure.is_maximized())
.unwrap_or_default()
}
fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
match fullscreen {
Some(Fullscreen::Exclusive(..)) => {
warn!("`Fullscreen::Exclusive` is ignored on Wayland");
},
#[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))]
Some(Fullscreen::Borderless(monitor)) => {
let output = monitor.as_ref().and_then(|monitor| {
monitor.cast_ref::<output::MonitorHandle>().map(|handle| &handle.proxy)
});
self.window.set_fullscreen(output)
},
None => self.window.unset_fullscreen(),
}
}
fn fullscreen(&self) -> Option<Fullscreen> {
let is_fullscreen = self
.window_state
.lock()
.unwrap()
.last_configure
.as_ref()
.map(|last_configure| last_configure.is_fullscreen())
.unwrap_or_default();
if is_fullscreen {
let current_monitor = self.current_monitor();
Some(Fullscreen::Borderless(current_monitor))
} else {
None
}
}
#[inline]
fn scale_factor(&self) -> f64 {
self.window_state.lock().unwrap().scale_factor()
}
#[inline]
fn set_blur(&self, blur: bool) {
self.window_state.lock().unwrap().set_blur(blur);
}
#[inline]
fn set_decorations(&self, decorate: bool) {
self.window_state.lock().unwrap().set_decorate(decorate)
}
#[inline]
fn is_decorated(&self) -> bool {
self.window_state.lock().unwrap().is_decorated()
}
fn set_window_level(&self, _level: WindowLevel) {}
fn set_window_icon(&self, _window_icon: Option<winit_core::icon::Icon>) {}
#[inline]
fn set_ime_cursor_area(&self, position: Position, size: Size) {
let window_state = self.window_state.lock().unwrap();
if window_state.ime_allowed() {
let scale_factor = window_state.scale_factor();
let position = position.to_logical(scale_factor);
let size = size.to_logical(scale_factor);
window_state.set_ime_cursor_area(position, size);
}
}
#[inline]
fn set_ime_allowed(&self, allowed: bool) {
let mut window_state = self.window_state.lock().unwrap();
if window_state.ime_allowed() != allowed && window_state.set_ime_allowed(allowed) {
let event = WindowEvent::Ime(if allowed { Ime::Enabled } else { Ime::Disabled });
self.window_events_sink.lock().unwrap().push_window_event(event, self.window_id);
self.event_loop_awakener.ping();
}
}
#[inline]
fn set_ime_purpose(&self, purpose: ImePurpose) {
self.window_state.lock().unwrap().set_ime_purpose(purpose);
}
fn focus_window(&self) {}
fn has_focus(&self) -> bool {
self.window_state.lock().unwrap().has_focus()
}
fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
let xdg_activation = match self.xdg_activation.as_ref() {
Some(xdg_activation) => xdg_activation,
None => {
warn!("`request_user_attention` isn't supported");
return;
},
};
// Urgency is only removed by the compositor and there's no need to raise urgency when it
// was already raised.
if request_type.is_none() || self.attention_requested.load(Ordering::Relaxed) {
return;
}
self.attention_requested.store(true, Ordering::Relaxed);
let surface = self.surface().clone();
let data = XdgActivationTokenData::Attention((
surface.clone(),
Arc::downgrade(&self.attention_requested),
));
let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
xdg_activation_token.set_surface(&surface);
xdg_activation_token.commit();
}
fn set_theme(&self, theme: Option<Theme>) {
self.window_state.lock().unwrap().set_theme(theme)
}
fn theme(&self) -> Option<Theme> {
self.window_state.lock().unwrap().theme()
}
fn set_content_protected(&self, _protected: bool) {}
fn set_cursor(&self, cursor: Cursor) {
let window_state = &mut self.window_state.lock().unwrap();
match cursor {
Cursor::Icon(icon) => window_state.set_cursor(icon),
Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
}
}
fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
let scale_factor = self.scale_factor();
let position = position.to_logical(scale_factor);
self.window_state
.lock()
.unwrap()
.set_cursor_position(position)
// Request redraw on success, since the state is double buffered.
.map(|_| self.request_redraw())
}
fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
self.window_state.lock().unwrap().set_cursor_grab(mode)
}
fn set_cursor_visible(&self, visible: bool) {
self.window_state.lock().unwrap().set_cursor_visible(visible);
}
fn drag_window(&self) -> Result<(), RequestError> {
self.window_state.lock().unwrap().drag_window()
}
fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
self.window_state.lock().unwrap().drag_resize_window(direction)
}
fn show_window_menu(&self, position: Position) {
let scale_factor = self.scale_factor();
let position = position.to_logical(scale_factor);
self.window_state.lock().unwrap().show_window_menu(position);
}
fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
let surface = self.window.wl_surface();
if hittest {
surface.set_input_region(None);
Ok(())
} else {
let region = Region::new(&*self.compositor).map_err(|err| os_error!(err))?;
region.add(0, 0, 0, 0);
surface.set_input_region(Some(region.wl_region()));
Ok(())
}
}
fn current_monitor(&self) -> Option<CoreMonitorHandle> {
let data = self.window.wl_surface().data::<SurfaceData>()?;
data.outputs()
.next()
.map(MonitorHandle::new)
.map(|monitor| CoreMonitorHandle(Arc::new(monitor)))
}
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
Box::new(
self.monitors
.lock()
.unwrap()
.clone()
.into_iter()
.map(|inner| CoreMonitorHandle(Arc::new(inner))),
)
}
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
// NOTE: There's no such concept on Wayland.
None
}
/// Get the raw-window-handle v0.6 display handle.
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
self
}
/// Get the raw-window-handle v0.6 window handle.
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
self
}
}
/// The request from the window to the event loop.
#[derive(Debug)]
pub struct WindowRequests {
/// The window was closed.
pub closed: AtomicBool,
/// Redraw Requested.
pub redraw_requested: AtomicBool,
}
impl WindowRequests {
pub fn take_closed(&self) -> bool {
self.closed.swap(false, Ordering::Relaxed)
}
pub fn take_redraw_requested(&self) -> bool {
self.redraw_requested.swap(false, Ordering::Relaxed)
}
}

File diff suppressed because it is too large Load diff