mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-31 01:01:30 +11:00
3a1e694c2f
* Begin implementing DPI generics * Fix multithreaded example * Format * Fix serde test * hopefully fix most of the errors * Fix dpi module errors * More error fixings * Format * fix macos errors * Another error pass * Replace bad type signatures * more fixins
1108 lines
37 KiB
Rust
1108 lines
37 KiB
Rust
use std::{
|
|
cell::RefCell,
|
|
collections::VecDeque,
|
|
fmt,
|
|
io::ErrorKind,
|
|
rc::Rc,
|
|
sync::{Arc, Mutex},
|
|
time::{Duration, Instant},
|
|
};
|
|
|
|
use mio::{Events, Poll, PollOpt, Ready, Token};
|
|
|
|
use mio_extras::channel::{channel, Receiver, SendError, Sender};
|
|
|
|
use smithay_client_toolkit::reexports::protocols::unstable::pointer_constraints::v1::client::{
|
|
zwp_locked_pointer_v1::ZwpLockedPointerV1, zwp_pointer_constraints_v1::ZwpPointerConstraintsV1,
|
|
};
|
|
use smithay_client_toolkit::reexports::protocols::unstable::relative_pointer::v1::client::{
|
|
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
|
|
zwp_relative_pointer_v1::ZwpRelativePointerV1,
|
|
};
|
|
|
|
use smithay_client_toolkit::pointer::{AutoPointer, AutoThemer};
|
|
use smithay_client_toolkit::reexports::client::protocol::{
|
|
wl_compositor::WlCompositor, wl_shm::WlShm, wl_surface::WlSurface,
|
|
};
|
|
|
|
use crate::{
|
|
dpi::{LogicalSize, PhysicalPosition, PhysicalSize},
|
|
event::{
|
|
DeviceEvent, DeviceId as RootDeviceId, Event, ModifiersState, StartCause, WindowEvent,
|
|
},
|
|
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
|
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
|
|
platform_impl::platform::{
|
|
sticky_exit_callback, DeviceId as PlatformDeviceId, MonitorHandle as PlatformMonitorHandle,
|
|
VideoMode as PlatformVideoMode, WindowId as PlatformWindowId,
|
|
},
|
|
window::{CursorIcon, WindowId as RootWindowId},
|
|
};
|
|
|
|
use super::{window::WindowStore, DeviceId, WindowId};
|
|
|
|
use smithay_client_toolkit::{
|
|
output::OutputMgr,
|
|
reexports::client::{
|
|
protocol::{wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat, wl_touch},
|
|
ConnectError, Display, EventQueue, GlobalEvent,
|
|
},
|
|
Environment,
|
|
};
|
|
|
|
const KBD_TOKEN: Token = Token(0);
|
|
const USER_TOKEN: Token = Token(1);
|
|
const EVQ_TOKEN: Token = Token(2);
|
|
|
|
#[derive(Clone)]
|
|
pub struct EventsSink {
|
|
sender: Sender<Event<'static, ()>>,
|
|
}
|
|
|
|
impl EventsSink {
|
|
pub fn new(sender: Sender<Event<'static, ()>>) -> EventsSink {
|
|
EventsSink { sender }
|
|
}
|
|
|
|
pub fn send_event(&self, event: Event<'static, ()>) {
|
|
self.sender.send(event).unwrap()
|
|
}
|
|
|
|
pub fn send_device_event(&self, event: DeviceEvent, device_id: DeviceId) {
|
|
self.send_event(Event::DeviceEvent {
|
|
event,
|
|
device_id: RootDeviceId(PlatformDeviceId::Wayland(device_id)),
|
|
});
|
|
}
|
|
|
|
pub fn send_window_event(&self, event: WindowEvent<'static>, window_id: WindowId) {
|
|
self.send_event(Event::WindowEvent {
|
|
event,
|
|
window_id: RootWindowId(PlatformWindowId::Wayland(window_id)),
|
|
});
|
|
}
|
|
}
|
|
|
|
pub struct CursorManager {
|
|
pointer_constraints_proxy: Arc<Mutex<Option<ZwpPointerConstraintsV1>>>,
|
|
auto_themer: Option<AutoThemer>,
|
|
pointers: Vec<AutoPointer>,
|
|
locked_pointers: Vec<ZwpLockedPointerV1>,
|
|
cursor_visible: bool,
|
|
current_cursor: CursorIcon,
|
|
}
|
|
|
|
impl CursorManager {
|
|
fn new(constraints: Arc<Mutex<Option<ZwpPointerConstraintsV1>>>) -> CursorManager {
|
|
CursorManager {
|
|
pointer_constraints_proxy: constraints,
|
|
auto_themer: None,
|
|
pointers: Vec::new(),
|
|
locked_pointers: Vec::new(),
|
|
cursor_visible: true,
|
|
current_cursor: CursorIcon::default(),
|
|
}
|
|
}
|
|
|
|
fn register_pointer(&mut self, pointer: wl_pointer::WlPointer) {
|
|
let auto_themer = self
|
|
.auto_themer
|
|
.as_ref()
|
|
.expect("AutoThemer not initialized. Server did not advertise shm or compositor?");
|
|
self.pointers.push(auto_themer.theme_pointer(pointer));
|
|
}
|
|
|
|
fn set_auto_themer(&mut self, auto_themer: AutoThemer) {
|
|
self.auto_themer = Some(auto_themer);
|
|
}
|
|
|
|
pub fn set_cursor_visible(&mut self, visible: bool) {
|
|
if !visible {
|
|
for pointer in self.pointers.iter() {
|
|
(**pointer).set_cursor(0, None, 0, 0);
|
|
}
|
|
} else {
|
|
self.set_cursor_icon_impl(self.current_cursor);
|
|
}
|
|
self.cursor_visible = visible;
|
|
}
|
|
|
|
/// A helper function to restore cursor styles on PtrEvent::Enter.
|
|
pub fn reload_cursor_style(&mut self) {
|
|
if !self.cursor_visible {
|
|
self.set_cursor_visible(false);
|
|
} else {
|
|
self.set_cursor_icon_impl(self.current_cursor);
|
|
}
|
|
}
|
|
|
|
pub fn set_cursor_icon(&mut self, cursor: CursorIcon) {
|
|
if cursor != self.current_cursor {
|
|
self.current_cursor = cursor;
|
|
if self.cursor_visible {
|
|
self.set_cursor_icon_impl(cursor);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn set_cursor_icon_impl(&mut self, cursor: CursorIcon) {
|
|
let cursor = match cursor {
|
|
CursorIcon::Alias => "link",
|
|
CursorIcon::Arrow => "arrow",
|
|
CursorIcon::Cell => "plus",
|
|
CursorIcon::Copy => "copy",
|
|
CursorIcon::Crosshair => "crosshair",
|
|
CursorIcon::Default => "left_ptr",
|
|
CursorIcon::Hand => "hand",
|
|
CursorIcon::Help => "question_arrow",
|
|
CursorIcon::Move => "move",
|
|
CursorIcon::Grab => "grab",
|
|
CursorIcon::Grabbing => "grabbing",
|
|
CursorIcon::Progress => "progress",
|
|
CursorIcon::AllScroll => "all-scroll",
|
|
CursorIcon::ContextMenu => "context-menu",
|
|
|
|
CursorIcon::NoDrop => "no-drop",
|
|
CursorIcon::NotAllowed => "crossed_circle",
|
|
|
|
// Resize cursors
|
|
CursorIcon::EResize => "right_side",
|
|
CursorIcon::NResize => "top_side",
|
|
CursorIcon::NeResize => "top_right_corner",
|
|
CursorIcon::NwResize => "top_left_corner",
|
|
CursorIcon::SResize => "bottom_side",
|
|
CursorIcon::SeResize => "bottom_right_corner",
|
|
CursorIcon::SwResize => "bottom_left_corner",
|
|
CursorIcon::WResize => "left_side",
|
|
CursorIcon::EwResize => "h_double_arrow",
|
|
CursorIcon::NsResize => "v_double_arrow",
|
|
CursorIcon::NwseResize => "bd_double_arrow",
|
|
CursorIcon::NeswResize => "fd_double_arrow",
|
|
CursorIcon::ColResize => "h_double_arrow",
|
|
CursorIcon::RowResize => "v_double_arrow",
|
|
|
|
CursorIcon::Text => "text",
|
|
CursorIcon::VerticalText => "vertical-text",
|
|
|
|
CursorIcon::Wait => "watch",
|
|
|
|
CursorIcon::ZoomIn => "zoom-in",
|
|
CursorIcon::ZoomOut => "zoom-out",
|
|
};
|
|
|
|
for pointer in self.pointers.iter() {
|
|
// Ignore erros, since we don't want to fail hard in case we can't find a proper cursor
|
|
// in a given theme.
|
|
let _ = pointer.set_cursor(cursor, None);
|
|
}
|
|
}
|
|
|
|
// This function can only be called from a thread on which `pointer_constraints_proxy` event
|
|
// queue is located, so calling it directly from a Window doesn't work well, in case
|
|
// you've sent your window to another thread, so we need to pass cursor grab updates to
|
|
// the event loop and call this function from there.
|
|
fn grab_pointer(&mut self, surface: Option<&WlSurface>) {
|
|
for locked_pointer in self.locked_pointers.drain(..) {
|
|
locked_pointer.destroy();
|
|
}
|
|
|
|
if let Some(surface) = surface {
|
|
for pointer in self.pointers.iter() {
|
|
let locked_pointer = self
|
|
.pointer_constraints_proxy
|
|
.try_lock()
|
|
.unwrap()
|
|
.as_ref()
|
|
.and_then(|pointer_constraints| {
|
|
super::pointer::implement_locked_pointer(
|
|
surface,
|
|
&**pointer,
|
|
pointer_constraints,
|
|
)
|
|
.ok()
|
|
});
|
|
|
|
if let Some(locked_pointer) = locked_pointer {
|
|
self.locked_pointers.push(locked_pointer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct EventLoop<T: 'static> {
|
|
// Poll instance
|
|
poll: Poll,
|
|
// The wayland display
|
|
pub display: Arc<Display>,
|
|
// The output manager
|
|
pub outputs: OutputMgr,
|
|
// The cursor manager
|
|
cursor_manager: Arc<Mutex<CursorManager>>,
|
|
kbd_channel: Receiver<Event<'static, ()>>,
|
|
user_channel: Receiver<T>,
|
|
user_sender: Sender<T>,
|
|
window_target: RootELW<T>,
|
|
}
|
|
|
|
// A handle that can be sent across threads and used to wake up the `EventLoop`.
|
|
//
|
|
// We should only try and wake up the `EventLoop` if it still exists, so we hold Weak ptrs.
|
|
pub struct EventLoopProxy<T: 'static> {
|
|
user_sender: Sender<T>,
|
|
}
|
|
|
|
pub struct EventLoopWindowTarget<T> {
|
|
// the event queue
|
|
pub evq: RefCell<EventQueue>,
|
|
// The window store
|
|
pub store: Arc<Mutex<WindowStore>>,
|
|
// The cursor manager
|
|
pub cursor_manager: Arc<Mutex<CursorManager>>,
|
|
// The env
|
|
pub env: Environment,
|
|
// A cleanup switch to prune dead windows
|
|
pub cleanup_needed: Arc<Mutex<bool>>,
|
|
// The wayland display
|
|
pub display: Arc<Display>,
|
|
// The list of seats
|
|
pub seats: Arc<Mutex<Vec<(u32, wl_seat::WlSeat)>>>,
|
|
_marker: ::std::marker::PhantomData<T>,
|
|
}
|
|
|
|
impl<T: 'static> Clone for EventLoopProxy<T> {
|
|
fn clone(&self) -> Self {
|
|
EventLoopProxy {
|
|
user_sender: self.user_sender.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: 'static> EventLoopProxy<T> {
|
|
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
|
self.user_sender.send(event).map_err(|e| {
|
|
EventLoopClosed(if let SendError::Disconnected(x) = e {
|
|
x
|
|
} else {
|
|
unreachable!()
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<T: 'static> EventLoop<T> {
|
|
pub fn new() -> Result<EventLoop<T>, ConnectError> {
|
|
let (display, mut event_queue) = Display::connect_to_env()?;
|
|
|
|
let display = Arc::new(display);
|
|
let store = Arc::new(Mutex::new(WindowStore::new()));
|
|
let seats = Arc::new(Mutex::new(Vec::new()));
|
|
|
|
let poll = Poll::new().unwrap();
|
|
|
|
let (kbd_sender, kbd_channel) = channel();
|
|
|
|
let sink = EventsSink::new(kbd_sender);
|
|
|
|
poll.register(&kbd_channel, KBD_TOKEN, Ready::readable(), PollOpt::level())
|
|
.unwrap();
|
|
|
|
let pointer_constraints_proxy = Arc::new(Mutex::new(None));
|
|
|
|
let mut seat_manager = SeatManager {
|
|
sink,
|
|
store: store.clone(),
|
|
seats: seats.clone(),
|
|
relative_pointer_manager_proxy: Rc::new(RefCell::new(None)),
|
|
pointer_constraints_proxy: pointer_constraints_proxy.clone(),
|
|
cursor_manager: Arc::new(Mutex::new(CursorManager::new(pointer_constraints_proxy))),
|
|
};
|
|
|
|
let cursor_manager = seat_manager.cursor_manager.clone();
|
|
let cursor_manager_clone = cursor_manager.clone();
|
|
|
|
let shm_cell = Rc::new(RefCell::new(None));
|
|
let compositor_cell = Rc::new(RefCell::new(None));
|
|
|
|
let env = Environment::from_display_with_cb(
|
|
&display,
|
|
&mut event_queue,
|
|
move |event, registry| match event {
|
|
GlobalEvent::New {
|
|
id,
|
|
ref interface,
|
|
version,
|
|
} => {
|
|
if interface == "zwp_relative_pointer_manager_v1" {
|
|
let relative_pointer_manager_proxy = registry
|
|
.bind(version, id, move |pointer_manager| {
|
|
pointer_manager.implement_closure(|_, _| (), ())
|
|
})
|
|
.unwrap();
|
|
|
|
*seat_manager
|
|
.relative_pointer_manager_proxy
|
|
.try_borrow_mut()
|
|
.unwrap() = Some(relative_pointer_manager_proxy);
|
|
}
|
|
if interface == "zwp_pointer_constraints_v1" {
|
|
let pointer_constraints_proxy = registry
|
|
.bind(version, id, move |pointer_constraints| {
|
|
pointer_constraints.implement_closure(|_, _| (), ())
|
|
})
|
|
.unwrap();
|
|
|
|
*seat_manager.pointer_constraints_proxy.lock().unwrap() =
|
|
Some(pointer_constraints_proxy);
|
|
}
|
|
if interface == "wl_shm" {
|
|
let shm: WlShm = registry
|
|
.bind(version, id, move |shm| shm.implement_closure(|_, _| (), ()))
|
|
.unwrap();
|
|
|
|
(*shm_cell.borrow_mut()) = Some(shm);
|
|
}
|
|
if interface == "wl_compositor" {
|
|
let compositor: WlCompositor = registry
|
|
.bind(version, id, move |compositor| {
|
|
compositor.implement_closure(|_, _| (), ())
|
|
})
|
|
.unwrap();
|
|
(*compositor_cell.borrow_mut()) = Some(compositor);
|
|
}
|
|
|
|
if compositor_cell.borrow().is_some() && shm_cell.borrow().is_some() {
|
|
let compositor = compositor_cell.borrow_mut().take().unwrap();
|
|
let shm = shm_cell.borrow_mut().take().unwrap();
|
|
let auto_themer = AutoThemer::init(None, compositor, &shm);
|
|
cursor_manager_clone
|
|
.lock()
|
|
.unwrap()
|
|
.set_auto_themer(auto_themer);
|
|
}
|
|
|
|
if interface == "wl_seat" {
|
|
seat_manager.add_seat(id, version, registry)
|
|
}
|
|
}
|
|
GlobalEvent::Removed { id, ref interface } => {
|
|
if interface == "wl_seat" {
|
|
seat_manager.remove_seat(id)
|
|
}
|
|
}
|
|
},
|
|
)
|
|
.unwrap();
|
|
|
|
poll.register(&event_queue, EVQ_TOKEN, Ready::readable(), PollOpt::level())
|
|
.unwrap();
|
|
|
|
let (user_sender, user_channel) = channel();
|
|
|
|
poll.register(
|
|
&user_channel,
|
|
USER_TOKEN,
|
|
Ready::readable(),
|
|
PollOpt::level(),
|
|
)
|
|
.unwrap();
|
|
|
|
let cursor_manager_clone = cursor_manager.clone();
|
|
Ok(EventLoop {
|
|
poll,
|
|
display: display.clone(),
|
|
outputs: env.outputs.clone(),
|
|
user_sender,
|
|
user_channel,
|
|
kbd_channel,
|
|
cursor_manager,
|
|
window_target: RootELW {
|
|
p: crate::platform_impl::EventLoopWindowTarget::Wayland(EventLoopWindowTarget {
|
|
evq: RefCell::new(event_queue),
|
|
store,
|
|
env,
|
|
cursor_manager: cursor_manager_clone,
|
|
cleanup_needed: Arc::new(Mutex::new(false)),
|
|
seats,
|
|
display,
|
|
_marker: ::std::marker::PhantomData,
|
|
}),
|
|
_marker: ::std::marker::PhantomData,
|
|
},
|
|
})
|
|
}
|
|
|
|
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
|
EventLoopProxy {
|
|
user_sender: self.user_sender.clone(),
|
|
}
|
|
}
|
|
|
|
pub fn run<F>(mut self, callback: F) -> !
|
|
where
|
|
F: 'static + FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
|
{
|
|
self.run_return(callback);
|
|
std::process::exit(0);
|
|
}
|
|
|
|
pub fn run_return<F>(&mut self, mut callback: F)
|
|
where
|
|
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
|
{
|
|
// send pending events to the server
|
|
self.display.flush().expect("Wayland connection lost.");
|
|
|
|
let mut control_flow = ControlFlow::default();
|
|
let mut events = Events::with_capacity(8);
|
|
|
|
callback(
|
|
Event::NewEvents(StartCause::Init),
|
|
&self.window_target,
|
|
&mut control_flow,
|
|
);
|
|
|
|
loop {
|
|
// Read events from the event queue
|
|
{
|
|
let mut evq = get_target(&self.window_target).evq.borrow_mut();
|
|
|
|
evq.dispatch_pending()
|
|
.expect("failed to dispatch wayland events");
|
|
|
|
if let Some(read) = evq.prepare_read() {
|
|
if let Err(e) = read.read_events() {
|
|
if e.kind() != ErrorKind::WouldBlock {
|
|
panic!("failed to read wayland events: {}", e);
|
|
}
|
|
}
|
|
|
|
evq.dispatch_pending()
|
|
.expect("failed to dispatch wayland events");
|
|
}
|
|
}
|
|
|
|
self.post_dispatch_triggers(&mut callback, &mut control_flow);
|
|
|
|
while let Ok(event) = self.kbd_channel.try_recv() {
|
|
let event = event.map_nonuser_event().unwrap();
|
|
sticky_exit_callback(event, &self.window_target, &mut control_flow, &mut callback);
|
|
}
|
|
|
|
while let Ok(event) = self.user_channel.try_recv() {
|
|
sticky_exit_callback(
|
|
Event::UserEvent(event),
|
|
&self.window_target,
|
|
&mut control_flow,
|
|
&mut callback,
|
|
);
|
|
}
|
|
|
|
// send Events cleared
|
|
{
|
|
sticky_exit_callback(
|
|
Event::MainEventsCleared,
|
|
&self.window_target,
|
|
&mut control_flow,
|
|
&mut callback,
|
|
);
|
|
}
|
|
|
|
// handle request-redraw
|
|
{
|
|
self.redraw_triggers(|wid, window_target| {
|
|
sticky_exit_callback(
|
|
Event::RedrawRequested(crate::window::WindowId(
|
|
crate::platform_impl::WindowId::Wayland(wid),
|
|
)),
|
|
window_target,
|
|
&mut control_flow,
|
|
&mut callback,
|
|
);
|
|
});
|
|
}
|
|
|
|
// send RedrawEventsCleared
|
|
{
|
|
sticky_exit_callback(
|
|
Event::RedrawEventsCleared,
|
|
&self.window_target,
|
|
&mut control_flow,
|
|
&mut callback,
|
|
);
|
|
}
|
|
|
|
// send pending events to the server
|
|
self.display.flush().expect("Wayland connection lost.");
|
|
|
|
// During the run of the user callback, some other code monitoring and reading the
|
|
// wayland socket may have been run (mesa for example does this with vsync), if that
|
|
// is the case, some events may have been enqueued in our event queue.
|
|
//
|
|
// If some messages are there, the event loop needs to behave as if it was instantly
|
|
// woken up by messages arriving from the wayland socket, to avoid getting stuck.
|
|
let instant_wakeup = {
|
|
let window_target = match self.window_target.p {
|
|
crate::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt,
|
|
_ => unreachable!(),
|
|
};
|
|
let dispatched = window_target
|
|
.evq
|
|
.borrow_mut()
|
|
.dispatch_pending()
|
|
.expect("Wayland connection lost.");
|
|
dispatched > 0
|
|
};
|
|
|
|
match control_flow {
|
|
ControlFlow::Exit => break,
|
|
ControlFlow::Poll => {
|
|
// non-blocking dispatch
|
|
self.poll
|
|
.poll(&mut events, Some(Duration::from_millis(0)))
|
|
.unwrap();
|
|
events.clear();
|
|
|
|
callback(
|
|
Event::NewEvents(StartCause::Poll),
|
|
&self.window_target,
|
|
&mut control_flow,
|
|
);
|
|
}
|
|
ControlFlow::Wait => {
|
|
if !instant_wakeup {
|
|
self.poll.poll(&mut events, None).unwrap();
|
|
events.clear();
|
|
}
|
|
|
|
callback(
|
|
Event::NewEvents(StartCause::WaitCancelled {
|
|
start: Instant::now(),
|
|
requested_resume: None,
|
|
}),
|
|
&self.window_target,
|
|
&mut control_flow,
|
|
);
|
|
}
|
|
ControlFlow::WaitUntil(deadline) => {
|
|
let start = Instant::now();
|
|
// compute the blocking duration
|
|
let duration = if deadline > start && !instant_wakeup {
|
|
deadline - start
|
|
} else {
|
|
Duration::from_millis(0)
|
|
};
|
|
self.poll.poll(&mut events, Some(duration)).unwrap();
|
|
events.clear();
|
|
|
|
let now = Instant::now();
|
|
if now < deadline {
|
|
callback(
|
|
Event::NewEvents(StartCause::WaitCancelled {
|
|
start,
|
|
requested_resume: Some(deadline),
|
|
}),
|
|
&self.window_target,
|
|
&mut control_flow,
|
|
);
|
|
} else {
|
|
callback(
|
|
Event::NewEvents(StartCause::ResumeTimeReached {
|
|
start,
|
|
requested_resume: deadline,
|
|
}),
|
|
&self.window_target,
|
|
&mut control_flow,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
callback(Event::LoopDestroyed, &self.window_target, &mut control_flow);
|
|
}
|
|
|
|
pub fn primary_monitor(&self) -> MonitorHandle {
|
|
primary_monitor(&self.outputs)
|
|
}
|
|
|
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
|
available_monitors(&self.outputs)
|
|
}
|
|
|
|
pub fn window_target(&self) -> &RootELW<T> {
|
|
&self.window_target
|
|
}
|
|
}
|
|
|
|
impl<T> EventLoopWindowTarget<T> {
|
|
pub fn display(&self) -> &Display {
|
|
&*self.display
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Private EventLoop Internals
|
|
*/
|
|
|
|
impl<T> EventLoop<T> {
|
|
fn redraw_triggers<F>(&mut self, mut callback: F)
|
|
where
|
|
F: FnMut(WindowId, &RootELW<T>),
|
|
{
|
|
let window_target = match self.window_target.p {
|
|
crate::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt,
|
|
_ => unreachable!(),
|
|
};
|
|
window_target.store.lock().unwrap().for_each_redraw_trigger(
|
|
|refresh, frame_refresh, wid, frame| {
|
|
if let Some(frame) = frame {
|
|
if frame_refresh {
|
|
frame.refresh();
|
|
if !refresh {
|
|
frame.surface().commit()
|
|
}
|
|
}
|
|
}
|
|
if refresh {
|
|
callback(wid, &self.window_target);
|
|
}
|
|
},
|
|
)
|
|
}
|
|
|
|
fn post_dispatch_triggers<F>(&mut self, mut callback: F, control_flow: &mut ControlFlow)
|
|
where
|
|
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
|
{
|
|
let window_target = match self.window_target.p {
|
|
crate::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt,
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
let mut callback = |event: Event<'_, T>| {
|
|
sticky_exit_callback(event, &self.window_target, control_flow, &mut callback);
|
|
};
|
|
|
|
// prune possible dead windows
|
|
{
|
|
let mut cleanup_needed = window_target.cleanup_needed.lock().unwrap();
|
|
if *cleanup_needed {
|
|
let pruned = window_target.store.lock().unwrap().cleanup();
|
|
*cleanup_needed = false;
|
|
for wid in pruned {
|
|
callback(Event::WindowEvent {
|
|
window_id: crate::window::WindowId(
|
|
crate::platform_impl::WindowId::Wayland(wid),
|
|
),
|
|
event: WindowEvent::Destroyed,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
// process pending resize/refresh
|
|
window_target.store.lock().unwrap().for_each(|window| {
|
|
let window_id =
|
|
crate::window::WindowId(crate::platform_impl::WindowId::Wayland(window.wid));
|
|
if let Some(frame) = window.frame {
|
|
if let Some((w, h)) = window.newsize {
|
|
// mutter (GNOME Wayland) relies on `set_geometry` to reposition window in case
|
|
// it overlaps mutter's `bounding box`, so we can't avoid this resize call,
|
|
// which calls `set_geometry` under the hood, for now.
|
|
frame.resize(w, h);
|
|
frame.refresh();
|
|
|
|
// Don't send resize event downstream if the new size is identical to the
|
|
// current one.
|
|
if (w, h) != *window.size {
|
|
let logical_size = crate::dpi::LogicalSize::new(w as f64, h as f64);
|
|
let physical_size = logical_size
|
|
.to_physical(window.new_dpi.unwrap_or(window.prev_dpi) as f64);
|
|
|
|
callback(Event::WindowEvent {
|
|
window_id,
|
|
event: WindowEvent::Resized(physical_size),
|
|
});
|
|
*window.size = (w, h);
|
|
}
|
|
}
|
|
|
|
if let Some(dpi) = window.new_dpi {
|
|
let dpi = dpi as f64;
|
|
let logical_size = LogicalSize::<f64>::from(*window.size);
|
|
let mut new_inner_size = Some(logical_size.to_physical(dpi));
|
|
|
|
callback(Event::WindowEvent {
|
|
window_id,
|
|
event: WindowEvent::HiDpiFactorChanged {
|
|
hidpi_factor: dpi,
|
|
new_inner_size: &mut new_inner_size,
|
|
},
|
|
});
|
|
|
|
if let Some(new_size) = new_inner_size {
|
|
let (w, h) = new_size.to_logical::<u32>(dpi).into();
|
|
frame.resize(w, h);
|
|
*window.size = (w, h);
|
|
}
|
|
}
|
|
}
|
|
if window.closed {
|
|
callback(Event::WindowEvent {
|
|
window_id,
|
|
event: WindowEvent::CloseRequested,
|
|
});
|
|
}
|
|
|
|
if let Some(grab_cursor) = window.grab_cursor {
|
|
let surface = if grab_cursor {
|
|
Some(window.surface)
|
|
} else {
|
|
None
|
|
};
|
|
self.cursor_manager.lock().unwrap().grab_pointer(surface);
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
fn get_target<T>(target: &RootELW<T>) -> &EventLoopWindowTarget<T> {
|
|
match target.p {
|
|
crate::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Wayland protocol implementations
|
|
*/
|
|
|
|
struct SeatManager {
|
|
sink: EventsSink,
|
|
store: Arc<Mutex<WindowStore>>,
|
|
seats: Arc<Mutex<Vec<(u32, wl_seat::WlSeat)>>>,
|
|
relative_pointer_manager_proxy: Rc<RefCell<Option<ZwpRelativePointerManagerV1>>>,
|
|
pointer_constraints_proxy: Arc<Mutex<Option<ZwpPointerConstraintsV1>>>,
|
|
cursor_manager: Arc<Mutex<CursorManager>>,
|
|
}
|
|
|
|
impl SeatManager {
|
|
fn add_seat(&mut self, id: u32, version: u32, registry: wl_registry::WlRegistry) {
|
|
use std::cmp::min;
|
|
|
|
let mut seat_data = SeatData {
|
|
sink: self.sink.clone(),
|
|
store: self.store.clone(),
|
|
pointer: None,
|
|
relative_pointer: None,
|
|
relative_pointer_manager_proxy: self.relative_pointer_manager_proxy.clone(),
|
|
keyboard: None,
|
|
touch: None,
|
|
modifiers_tracker: Arc::new(Mutex::new(ModifiersState::default())),
|
|
cursor_manager: self.cursor_manager.clone(),
|
|
};
|
|
let seat = registry
|
|
.bind(min(version, 5), id, move |seat| {
|
|
seat.implement_closure(move |event, seat| seat_data.receive(event, seat), ())
|
|
})
|
|
.unwrap();
|
|
self.store.lock().unwrap().new_seat(&seat);
|
|
self.seats.lock().unwrap().push((id, seat));
|
|
}
|
|
|
|
fn remove_seat(&mut self, id: u32) {
|
|
let mut seats = self.seats.lock().unwrap();
|
|
if let Some(idx) = seats.iter().position(|&(i, _)| i == id) {
|
|
let (_, seat) = seats.swap_remove(idx);
|
|
if seat.as_ref().version() >= 5 {
|
|
seat.release();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct SeatData {
|
|
sink: EventsSink,
|
|
store: Arc<Mutex<WindowStore>>,
|
|
pointer: Option<wl_pointer::WlPointer>,
|
|
relative_pointer: Option<ZwpRelativePointerV1>,
|
|
relative_pointer_manager_proxy: Rc<RefCell<Option<ZwpRelativePointerManagerV1>>>,
|
|
keyboard: Option<wl_keyboard::WlKeyboard>,
|
|
touch: Option<wl_touch::WlTouch>,
|
|
modifiers_tracker: Arc<Mutex<ModifiersState>>,
|
|
cursor_manager: Arc<Mutex<CursorManager>>,
|
|
}
|
|
|
|
impl SeatData {
|
|
fn receive(&mut self, evt: wl_seat::Event, seat: wl_seat::WlSeat) {
|
|
match evt {
|
|
wl_seat::Event::Name { .. } => (),
|
|
wl_seat::Event::Capabilities { capabilities } => {
|
|
// create pointer if applicable
|
|
if capabilities.contains(wl_seat::Capability::Pointer) && self.pointer.is_none() {
|
|
self.pointer = Some(super::pointer::implement_pointer(
|
|
&seat,
|
|
self.sink.clone(),
|
|
self.store.clone(),
|
|
self.modifiers_tracker.clone(),
|
|
self.cursor_manager.clone(),
|
|
));
|
|
|
|
self.cursor_manager
|
|
.lock()
|
|
.unwrap()
|
|
.register_pointer(self.pointer.as_ref().unwrap().clone());
|
|
|
|
self.relative_pointer = self
|
|
.relative_pointer_manager_proxy
|
|
.try_borrow()
|
|
.unwrap()
|
|
.as_ref()
|
|
.and_then(|manager| {
|
|
super::pointer::implement_relative_pointer(
|
|
self.sink.clone(),
|
|
self.pointer.as_ref().unwrap(),
|
|
manager,
|
|
)
|
|
.ok()
|
|
})
|
|
}
|
|
// destroy pointer if applicable
|
|
if !capabilities.contains(wl_seat::Capability::Pointer) {
|
|
if let Some(pointer) = self.pointer.take() {
|
|
if pointer.as_ref().version() >= 3 {
|
|
pointer.release();
|
|
}
|
|
}
|
|
}
|
|
// create keyboard if applicable
|
|
if capabilities.contains(wl_seat::Capability::Keyboard) && self.keyboard.is_none() {
|
|
self.keyboard = Some(super::keyboard::init_keyboard(
|
|
&seat,
|
|
self.sink.clone(),
|
|
self.modifiers_tracker.clone(),
|
|
))
|
|
}
|
|
// destroy keyboard if applicable
|
|
if !capabilities.contains(wl_seat::Capability::Keyboard) {
|
|
if let Some(kbd) = self.keyboard.take() {
|
|
if kbd.as_ref().version() >= 3 {
|
|
kbd.release();
|
|
}
|
|
}
|
|
}
|
|
// create touch if applicable
|
|
if capabilities.contains(wl_seat::Capability::Touch) && self.touch.is_none() {
|
|
self.touch = Some(super::touch::implement_touch(
|
|
&seat,
|
|
self.sink.clone(),
|
|
self.store.clone(),
|
|
))
|
|
}
|
|
// destroy touch if applicable
|
|
if !capabilities.contains(wl_seat::Capability::Touch) {
|
|
if let Some(touch) = self.touch.take() {
|
|
if touch.as_ref().version() >= 3 {
|
|
touch.release();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Drop for SeatData {
|
|
fn drop(&mut self) {
|
|
if let Some(pointer) = self.pointer.take() {
|
|
if pointer.as_ref().version() >= 3 {
|
|
pointer.release();
|
|
}
|
|
}
|
|
if let Some(kbd) = self.keyboard.take() {
|
|
if kbd.as_ref().version() >= 3 {
|
|
kbd.release();
|
|
}
|
|
}
|
|
if let Some(touch) = self.touch.take() {
|
|
if touch.as_ref().version() >= 3 {
|
|
touch.release();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Monitor stuff
|
|
*/
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub struct VideoMode {
|
|
pub(crate) size: (u32, u32),
|
|
pub(crate) bit_depth: u16,
|
|
pub(crate) refresh_rate: u16,
|
|
pub(crate) monitor: MonitorHandle,
|
|
}
|
|
|
|
impl VideoMode {
|
|
#[inline]
|
|
pub fn size(&self) -> PhysicalSize<u32> {
|
|
self.size.into()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn bit_depth(&self) -> u16 {
|
|
self.bit_depth
|
|
}
|
|
|
|
#[inline]
|
|
pub fn refresh_rate(&self) -> u16 {
|
|
self.refresh_rate
|
|
}
|
|
|
|
#[inline]
|
|
pub fn monitor(&self) -> RootMonitorHandle {
|
|
RootMonitorHandle {
|
|
inner: PlatformMonitorHandle::Wayland(self.monitor.clone()),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct MonitorHandle {
|
|
pub(crate) proxy: wl_output::WlOutput,
|
|
pub(crate) mgr: OutputMgr,
|
|
}
|
|
|
|
impl PartialEq for MonitorHandle {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.native_identifier() == other.native_identifier()
|
|
}
|
|
}
|
|
|
|
impl Eq for MonitorHandle {}
|
|
|
|
impl PartialOrd for MonitorHandle {
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
Some(self.cmp(&other))
|
|
}
|
|
}
|
|
|
|
impl Ord for MonitorHandle {
|
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
self.native_identifier().cmp(&other.native_identifier())
|
|
}
|
|
}
|
|
|
|
impl std::hash::Hash for MonitorHandle {
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
self.native_identifier().hash(state);
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for MonitorHandle {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
#[derive(Debug)]
|
|
struct MonitorHandle {
|
|
name: Option<String>,
|
|
native_identifier: u32,
|
|
size: PhysicalSize<u32>,
|
|
position: PhysicalPosition<i32>,
|
|
hidpi_factor: i32,
|
|
}
|
|
|
|
let monitor_id_proxy = MonitorHandle {
|
|
name: self.name(),
|
|
native_identifier: self.native_identifier(),
|
|
size: self.size(),
|
|
position: self.position(),
|
|
hidpi_factor: self.hidpi_factor(),
|
|
};
|
|
|
|
monitor_id_proxy.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl MonitorHandle {
|
|
pub fn name(&self) -> Option<String> {
|
|
self.mgr.with_info(&self.proxy, |_, info| {
|
|
format!("{} ({})", info.model, info.make)
|
|
})
|
|
}
|
|
|
|
#[inline]
|
|
pub fn native_identifier(&self) -> u32 {
|
|
self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0)
|
|
}
|
|
|
|
pub fn size(&self) -> PhysicalSize<u32> {
|
|
match self.mgr.with_info(&self.proxy, |_, info| {
|
|
info.modes
|
|
.iter()
|
|
.find(|m| m.is_current)
|
|
.map(|m| m.dimensions)
|
|
}) {
|
|
Some(Some((w, h))) => (w as u32, h as u32),
|
|
_ => (0, 0),
|
|
}
|
|
.into()
|
|
}
|
|
|
|
pub fn position(&self) -> PhysicalPosition<i32> {
|
|
self.mgr
|
|
.with_info(&self.proxy, |_, info| info.location)
|
|
.unwrap_or((0, 0))
|
|
.into()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn hidpi_factor(&self) -> i32 {
|
|
self.mgr
|
|
.with_info(&self.proxy, |_, info| info.scale_factor)
|
|
.unwrap_or(1)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn video_modes(&self) -> impl Iterator<Item = RootVideoMode> {
|
|
let monitor = self.clone();
|
|
|
|
self.mgr
|
|
.with_info(&self.proxy, |_, info| info.modes.clone())
|
|
.unwrap_or(vec![])
|
|
.into_iter()
|
|
.map(move |x| RootVideoMode {
|
|
video_mode: PlatformVideoMode::Wayland(VideoMode {
|
|
size: (x.dimensions.0 as u32, x.dimensions.1 as u32),
|
|
refresh_rate: (x.refresh_rate as f32 / 1000.0).round() as u16,
|
|
bit_depth: 32,
|
|
monitor: monitor.clone(),
|
|
}),
|
|
})
|
|
}
|
|
}
|
|
|
|
pub fn primary_monitor(outputs: &OutputMgr) -> MonitorHandle {
|
|
outputs.with_all(|list| {
|
|
if let Some(&(_, ref proxy, _)) = list.first() {
|
|
MonitorHandle {
|
|
proxy: proxy.clone(),
|
|
mgr: outputs.clone(),
|
|
}
|
|
} else {
|
|
panic!("No monitor is available.")
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn available_monitors(outputs: &OutputMgr) -> VecDeque<MonitorHandle> {
|
|
outputs.with_all(|list| {
|
|
list.iter()
|
|
.map(|&(_, ref proxy, _)| MonitorHandle {
|
|
proxy: proxy.clone(),
|
|
mgr: outputs.clone(),
|
|
})
|
|
.collect()
|
|
})
|
|
}
|