mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-11 13:31:29 +11:00
Update SCTK to 0.11.0
* Update SCTK to 0.11.0 Updates smithay-client-toolkit to 0.11.0. The major highlight of that updated, is update of wayland-rs to 0.27.0. Switching to wayland-cursor, instead of using libwayland-cursor. It also fixes the following bugs: - Disabled repeat rate not being handled. - Decoration buttons not working after tty switch. - Scaling not being applied on output reenable. - Crash when `XCURSOR_SIZE` is `0`. - Pointer getting created in some cases without pointer capability. - On kwin, fix space between window and decorations on startup. - Incorrect size event when entering fullscreen when using client side decorations. - Client side decorations not being hided properly in fullscreen. - Size tracking between fullscreen/tiled state changes. - Repeat rate triggering multiple times from slow callback handler. - Resizable attribute not being applied properly on startup. - Not working IME Besides those fixes it also adds a bunch of missing virtual key codes, implements proper cursor grabbing, adds right click on decorations to open application menu, disabled maximize button for non-resizeable window, and fall back for cursor icon to similar ones, if the requested is missing. It also adds new methods to a `Theme` trait, such as: - `title_font(&self) -> Option<(String, f32)>` - The font for a title. - `title_color(&self, window_active: bool) -> [u8; 4]` - The color of the text in the title. Fixes #1680. Fixes #1678. Fixes #1676. Fixes #1646. Fixes #1614. Fixes #1601. Fixes #1533. Fixes #1509. Fixes #952. Fixes #947.
This commit is contained in:
parent
471b1e003a
commit
3d85af04be
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -44,6 +44,23 @@
|
||||||
- On Web, `WindowEvent::Resized` is now emitted when `Window::set_inner_size` is called.
|
- On Web, `WindowEvent::Resized` is now emitted when `Window::set_inner_size` is called.
|
||||||
- **Breaking:** `Fullscreen` enum now uses `Borderless(Option<MonitorHandle>)` instead of `Borderless(MonitorHandle)` to allow picking the current monitor.
|
- **Breaking:** `Fullscreen` enum now uses `Borderless(Option<MonitorHandle>)` instead of `Borderless(MonitorHandle)` to allow picking the current monitor.
|
||||||
- On MacOS, fix `WindowEvent::Moved` ignoring the scale factor.
|
- On MacOS, fix `WindowEvent::Moved` ignoring the scale factor.
|
||||||
|
- On Wayland, add missing virtual keycodes.
|
||||||
|
- On Wayland, implement proper `set_cursor_grab`.
|
||||||
|
- On Wayland, the cursor will use similar icons if the requested one isn't available.
|
||||||
|
- On Wayland, right clicking on client side decorations will request application menu.
|
||||||
|
- On Wayland, fix tracking of window size after state changes.
|
||||||
|
- On Wayland, fix client side decorations not being hidden properly in fullscreen.
|
||||||
|
- On Wayland, fix incorrect size event when entering fullscreen with client side decorations.
|
||||||
|
- On Wayland, fix `resizable` attribute not being applied properly on startup.
|
||||||
|
- On Wayland, fix disabled repeat rate not being handled.
|
||||||
|
- On Wayland, fix decoration buttons not working after tty switch.
|
||||||
|
- On Wayland, fix scaling not being applied on output re-enable.
|
||||||
|
- On Wayland, fix crash when `XCURSOR_SIZE` is `0`.
|
||||||
|
- On Wayland, fix pointer getting created in some cases without pointer capability.
|
||||||
|
- On Wayland, on kwin, fix space between window and decorations on startup.
|
||||||
|
- **Breaking:** On Wayland, `Theme` trait was reworked.
|
||||||
|
- On Wayland, disable maximize button for non-resizable window.
|
||||||
|
- On Wayland, added support for `set_ime_position`.
|
||||||
|
|
||||||
# 0.22.2 (2020-05-16)
|
# 0.22.2 (2020-05-16)
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ default = ["x11", "wayland"]
|
||||||
web-sys = ["web_sys", "wasm-bindgen", "instant/wasm-bindgen"]
|
web-sys = ["web_sys", "wasm-bindgen", "instant/wasm-bindgen"]
|
||||||
stdweb = ["std_web", "instant/stdweb"]
|
stdweb = ["std_web", "instant/stdweb"]
|
||||||
x11 = ["x11-dl"]
|
x11 = ["x11-dl"]
|
||||||
wayland = ["wayland-client", "smithay-client-toolkit"]
|
wayland = ["wayland-client", "sctk"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
instant = "0.1"
|
instant = "0.1"
|
||||||
|
@ -81,10 +81,10 @@ features = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
|
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
|
||||||
wayland-client = { version = "0.23.0", features = [ "dlopen", "egl", "cursor", "eventloop"] , optional = true }
|
wayland-client = { version = "0.27", features = [ "dlopen"] , optional = true }
|
||||||
|
sctk = { package = "smithay-client-toolkit", version = "0.11", optional = true }
|
||||||
mio = "0.6"
|
mio = "0.6"
|
||||||
mio-extras = "2.0"
|
mio-extras = "2.0"
|
||||||
smithay-client-toolkit = { version = "^0.6.6", optional = true }
|
|
||||||
x11-dl = { version = "2.18.5", optional = true }
|
x11-dl = { version = "2.18.5", optional = true }
|
||||||
percent-encoding = "2.0"
|
percent-encoding = "2.0"
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
target_os = "windows",
|
target_os = "windows",
|
||||||
target_os = "macos",
|
target_os = "macos",
|
||||||
target_os = "android",
|
target_os = "android",
|
||||||
target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"
|
target_os = "linux",
|
||||||
|
target_os = "dragonfly",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "netbsd",
|
||||||
|
target_os = "openbsd"
|
||||||
))]
|
))]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
#![cfg(any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "dragonfly",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "netbsd",
|
||||||
|
target_os = "openbsd"
|
||||||
|
))]
|
||||||
|
|
||||||
use std::os::raw;
|
use std::os::raw;
|
||||||
#[cfg(feature = "x11")]
|
#[cfg(feature = "x11")]
|
||||||
use std::{ptr, sync::Arc};
|
use std::{ptr, sync::Arc};
|
||||||
|
|
||||||
#[cfg(feature = "wayland")]
|
|
||||||
use smithay_client_toolkit::window::{ButtonState as SCTKButtonState, Theme as SCTKTheme};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
event_loop::{EventLoop, EventLoopWindowTarget},
|
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||||
monitor::MonitorHandle,
|
monitor::MonitorHandle,
|
||||||
|
@ -328,7 +331,7 @@ impl WindowExtUnix for Window {
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
fn wayland_display(&self) -> Option<*mut raw::c_void> {
|
fn wayland_display(&self) -> Option<*mut raw::c_void> {
|
||||||
match self.window {
|
match self.window {
|
||||||
LinuxWindow::Wayland(ref w) => Some(w.display().as_ref().c_ptr() as *mut _),
|
LinuxWindow::Wayland(ref w) => Some(w.display().get_display_ptr() as *mut _),
|
||||||
#[cfg(feature = "x11")]
|
#[cfg(feature = "x11")]
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -338,7 +341,7 @@ impl WindowExtUnix for Window {
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
fn set_wayland_theme<T: Theme>(&self, theme: T) {
|
fn set_wayland_theme<T: Theme>(&self, theme: T) {
|
||||||
match self.window {
|
match self.window {
|
||||||
LinuxWindow::Wayland(ref w) => w.set_theme(WaylandTheme(theme)),
|
LinuxWindow::Wayland(ref w) => w.set_theme(theme),
|
||||||
#[cfg(feature = "x11")]
|
#[cfg(feature = "x11")]
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -466,83 +469,50 @@ impl MonitorHandleExtUnix for MonitorHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper for implementing SCTK's theme trait.
|
/// A theme for a Wayland's client side decorations.
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
struct WaylandTheme<T: Theme>(T);
|
|
||||||
|
|
||||||
pub trait Theme: Send + 'static {
|
pub trait Theme: Send + 'static {
|
||||||
/// Primary color of the scheme.
|
/// Title bar color.
|
||||||
fn primary_color(&self, window_active: bool) -> [u8; 4];
|
fn element_color(&self, element: Element, window_active: bool) -> ARGBColor;
|
||||||
|
|
||||||
/// Secondary color of the scheme.
|
/// Color for a given button part.
|
||||||
fn secondary_color(&self, window_active: bool) -> [u8; 4];
|
fn button_color(
|
||||||
|
&self,
|
||||||
|
button: Button,
|
||||||
|
state: ButtonState,
|
||||||
|
foreground: bool,
|
||||||
|
window_active: bool,
|
||||||
|
) -> ARGBColor;
|
||||||
|
|
||||||
/// Color for the close button.
|
/// Font name and the size for the title bar.
|
||||||
fn close_button_color(&self, status: ButtonState) -> [u8; 4];
|
///
|
||||||
|
/// By default the font is `sans-serif` at the size of 11.
|
||||||
/// Icon color for the close button, defaults to the secondary color.
|
///
|
||||||
#[allow(unused_variables)]
|
/// Returning `None` means that title won't be drawn.
|
||||||
fn close_button_icon_color(&self, status: ButtonState) -> [u8; 4] {
|
fn font(&self) -> Option<(String, f32)> {
|
||||||
self.secondary_color(true)
|
// Not having any title isn't something desirable for the users, so setting it to
|
||||||
}
|
// something generic.
|
||||||
|
Some((String::from("sans-serif"), 11.))
|
||||||
/// Background color for the maximize button.
|
|
||||||
fn maximize_button_color(&self, status: ButtonState) -> [u8; 4];
|
|
||||||
|
|
||||||
/// Icon color for the maximize button, defaults to the secondary color.
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn maximize_button_icon_color(&self, status: ButtonState) -> [u8; 4] {
|
|
||||||
self.secondary_color(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Background color for the minimize button.
|
|
||||||
fn minimize_button_color(&self, status: ButtonState) -> [u8; 4];
|
|
||||||
|
|
||||||
/// Icon color for the minimize button, defaults to the secondary color.
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn minimize_button_icon_color(&self, status: ButtonState) -> [u8; 4] {
|
|
||||||
self.secondary_color(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A button on Wayland's client side decorations.
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
impl<T: Theme> SCTKTheme for WaylandTheme<T> {
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
fn get_primary_color(&self, active: bool) -> [u8; 4] {
|
pub enum Button {
|
||||||
self.0.primary_color(active)
|
/// Button that maximizes the window.
|
||||||
}
|
Maximize,
|
||||||
|
|
||||||
fn get_secondary_color(&self, active: bool) -> [u8; 4] {
|
/// Button that minimizes the window.
|
||||||
self.0.secondary_color(active)
|
Minimize,
|
||||||
}
|
|
||||||
|
|
||||||
fn get_close_button_color(&self, status: SCTKButtonState) -> [u8; 4] {
|
/// Button that closes the window.
|
||||||
self.0.close_button_color(ButtonState::from_sctk(status))
|
Close,
|
||||||
}
|
|
||||||
|
|
||||||
fn get_close_button_icon_color(&self, status: SCTKButtonState) -> [u8; 4] {
|
|
||||||
self.0
|
|
||||||
.close_button_icon_color(ButtonState::from_sctk(status))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_maximize_button_color(&self, status: SCTKButtonState) -> [u8; 4] {
|
|
||||||
self.0.maximize_button_color(ButtonState::from_sctk(status))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_maximize_button_icon_color(&self, status: SCTKButtonState) -> [u8; 4] {
|
|
||||||
self.0
|
|
||||||
.maximize_button_icon_color(ButtonState::from_sctk(status))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_minimize_button_color(&self, status: SCTKButtonState) -> [u8; 4] {
|
|
||||||
self.0.minimize_button_color(ButtonState::from_sctk(status))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_minimize_button_icon_color(&self, status: SCTKButtonState) -> [u8; 4] {
|
|
||||||
self.0
|
|
||||||
.minimize_button_icon_color(ButtonState::from_sctk(status))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A button state of the button on Wayland's client side decorations.
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum ButtonState {
|
pub enum ButtonState {
|
||||||
/// Button is being hovered over by pointer.
|
/// Button is being hovered over by pointer.
|
||||||
Hovered,
|
Hovered,
|
||||||
|
@ -553,12 +523,23 @@ pub enum ButtonState {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
impl ButtonState {
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
fn from_sctk(button_state: SCTKButtonState) -> Self {
|
pub enum Element {
|
||||||
match button_state {
|
/// Bar itself.
|
||||||
SCTKButtonState::Hovered => Self::Hovered,
|
Bar,
|
||||||
SCTKButtonState::Idle => Self::Idle,
|
|
||||||
SCTKButtonState::Disabled => Self::Disabled,
|
/// Separator between window and title bar.
|
||||||
}
|
Separator,
|
||||||
}
|
|
||||||
|
/// Title bar text.
|
||||||
|
Text,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct ARGBColor {
|
||||||
|
pub a: u8,
|
||||||
|
pub r: u8,
|
||||||
|
pub g: u8,
|
||||||
|
pub b: u8,
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#[cfg(all(not(feature = "x11"), not(feature = "wayland")))]
|
#[cfg(all(not(feature = "x11"), not(feature = "wayland")))]
|
||||||
compile_error!("Please select a feature to build for unix: `x11`, `wayland`");
|
compile_error!("Please select a feature to build for unix: `x11`, `wayland`");
|
||||||
|
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
use std::error::Error;
|
||||||
use std::{collections::VecDeque, env, fmt};
|
use std::{collections::VecDeque, env, fmt};
|
||||||
#[cfg(feature = "x11")]
|
#[cfg(feature = "x11")]
|
||||||
use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Arc};
|
use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Arc};
|
||||||
|
@ -16,8 +18,6 @@ use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Arc};
|
||||||
#[cfg(feature = "x11")]
|
#[cfg(feature = "x11")]
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use raw_window_handle::RawWindowHandle;
|
use raw_window_handle::RawWindowHandle;
|
||||||
#[cfg(feature = "wayland")]
|
|
||||||
use smithay_client_toolkit::reexports::client::ConnectError;
|
|
||||||
|
|
||||||
#[cfg(feature = "x11")]
|
#[cfg(feature = "x11")]
|
||||||
pub use self::x11::XNotSupported;
|
pub use self::x11::XNotSupported;
|
||||||
|
@ -108,6 +108,8 @@ pub enum OsError {
|
||||||
XError(XError),
|
XError(XError),
|
||||||
#[cfg(feature = "x11")]
|
#[cfg(feature = "x11")]
|
||||||
XMisc(&'static str),
|
XMisc(&'static str),
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
WaylandMisc(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for OsError {
|
impl fmt::Display for OsError {
|
||||||
|
@ -117,6 +119,8 @@ impl fmt::Display for OsError {
|
||||||
OsError::XError(ref e) => _f.pad(&e.description),
|
OsError::XError(ref e) => _f.pad(&e.description),
|
||||||
#[cfg(feature = "x11")]
|
#[cfg(feature = "x11")]
|
||||||
OsError::XMisc(ref e) => _f.pad(e),
|
OsError::XMisc(ref e) => _f.pad(e),
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
OsError::WaylandMisc(ref e) => _f.pad(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -410,13 +414,8 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_ime_position(&self, _position: Position) {
|
pub fn set_ime_position(&self, position: Position) {
|
||||||
match self {
|
x11_or_wayland!(match self; Window(w) => w.set_ime_position(position))
|
||||||
#[cfg(feature = "x11")]
|
|
||||||
&Window::X(ref w) => w.set_ime_position(_position),
|
|
||||||
#[cfg(feature = "wayland")]
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -597,14 +596,14 @@ impl<T: 'static> EventLoop<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
pub fn new_wayland() -> Result<EventLoop<T>, ConnectError> {
|
pub fn new_wayland() -> Result<EventLoop<T>, Box<dyn Error>> {
|
||||||
assert_is_main_thread("new_wayland_any_thread");
|
assert_is_main_thread("new_wayland_any_thread");
|
||||||
|
|
||||||
EventLoop::new_wayland_any_thread()
|
EventLoop::new_wayland_any_thread()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
pub fn new_wayland_any_thread() -> Result<EventLoop<T>, ConnectError> {
|
pub fn new_wayland_any_thread() -> Result<EventLoop<T>, Box<dyn Error>> {
|
||||||
wayland::EventLoop::new().map(EventLoop::Wayland)
|
wayland::EventLoop::new().map(EventLoop::Wayland)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
149
src/platform_impl/linux/wayland/env.rs
Normal file
149
src/platform_impl/linux/wayland/env.rs
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
//! SCTK environment setup.
|
||||||
|
|
||||||
|
use sctk::reexports::client::protocol::wl_compositor::WlCompositor;
|
||||||
|
use sctk::reexports::client::protocol::wl_output::WlOutput;
|
||||||
|
use sctk::reexports::protocols::unstable::xdg_shell::v6::client::zxdg_shell_v6::ZxdgShellV6;
|
||||||
|
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||||
|
use sctk::reexports::protocols::unstable::xdg_decoration::v1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1;
|
||||||
|
use sctk::reexports::client::protocol::wl_shell::WlShell;
|
||||||
|
use sctk::reexports::client::protocol::wl_subcompositor::WlSubcompositor;
|
||||||
|
use sctk::reexports::client::{Attached, DispatchData};
|
||||||
|
use sctk::reexports::client::protocol::wl_shm::WlShm;
|
||||||
|
use sctk::reexports::protocols::xdg_shell::client::xdg_wm_base::XdgWmBase;
|
||||||
|
use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1;
|
||||||
|
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1;
|
||||||
|
use sctk::reexports::protocols::unstable::text_input::v3::client::zwp_text_input_manager_v3::ZwpTextInputManagerV3;
|
||||||
|
|
||||||
|
use sctk::environment::{Environment, SimpleGlobal};
|
||||||
|
use sctk::output::{OutputHandler, OutputHandling, OutputInfo, OutputStatusListener};
|
||||||
|
use sctk::seat::{SeatData, SeatHandler, SeatHandling, SeatListener};
|
||||||
|
use sctk::shell::{Shell, ShellHandler, ShellHandling};
|
||||||
|
use sctk::shm::ShmHandler;
|
||||||
|
|
||||||
|
/// Set of extra features that are supported by the compositor.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct WindowingFeatures {
|
||||||
|
cursor_grab: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowingFeatures {
|
||||||
|
/// Create `WindowingFeatures` based on the presented interfaces.
|
||||||
|
pub fn new(env: &Environment<WinitEnv>) -> Self {
|
||||||
|
let cursor_grab = env.get_global::<ZwpPointerConstraintsV1>().is_some();
|
||||||
|
Self { cursor_grab }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cursor_grab(&self) -> bool {
|
||||||
|
self.cursor_grab
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sctk::environment!(WinitEnv,
|
||||||
|
singles = [
|
||||||
|
WlShm => shm,
|
||||||
|
WlCompositor => compositor,
|
||||||
|
WlSubcompositor => subcompositor,
|
||||||
|
WlShell => shell,
|
||||||
|
XdgWmBase => shell,
|
||||||
|
ZxdgShellV6 => shell,
|
||||||
|
ZxdgDecorationManagerV1 => decoration_manager,
|
||||||
|
ZwpRelativePointerManagerV1 => relative_pointer_manager,
|
||||||
|
ZwpPointerConstraintsV1 => pointer_constraints,
|
||||||
|
ZwpTextInputManagerV3 => text_input_manager,
|
||||||
|
],
|
||||||
|
multis = [
|
||||||
|
WlSeat => seats,
|
||||||
|
WlOutput => outputs,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
/// The environment that we utilize.
|
||||||
|
pub struct WinitEnv {
|
||||||
|
seats: SeatHandler,
|
||||||
|
|
||||||
|
outputs: OutputHandler,
|
||||||
|
|
||||||
|
shm: ShmHandler,
|
||||||
|
|
||||||
|
compositor: SimpleGlobal<WlCompositor>,
|
||||||
|
|
||||||
|
subcompositor: SimpleGlobal<WlSubcompositor>,
|
||||||
|
|
||||||
|
shell: ShellHandler,
|
||||||
|
|
||||||
|
relative_pointer_manager: SimpleGlobal<ZwpRelativePointerManagerV1>,
|
||||||
|
|
||||||
|
pointer_constraints: SimpleGlobal<ZwpPointerConstraintsV1>,
|
||||||
|
|
||||||
|
text_input_manager: SimpleGlobal<ZwpTextInputManagerV3>,
|
||||||
|
|
||||||
|
decoration_manager: SimpleGlobal<ZxdgDecorationManagerV1>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WinitEnv {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
// Output tracking for available_monitors, etc.
|
||||||
|
let outputs = OutputHandler::new();
|
||||||
|
|
||||||
|
// Keyboard/Pointer/Touch input.
|
||||||
|
let seats = SeatHandler::new();
|
||||||
|
|
||||||
|
// Essential globals.
|
||||||
|
let shm = ShmHandler::new();
|
||||||
|
let compositor = SimpleGlobal::new();
|
||||||
|
let subcompositor = SimpleGlobal::new();
|
||||||
|
|
||||||
|
// Gracefully handle shell picking, since SCTK automatically supports multiple
|
||||||
|
// backends.
|
||||||
|
let shell = ShellHandler::new();
|
||||||
|
|
||||||
|
// Server side decorations.
|
||||||
|
let decoration_manager = SimpleGlobal::new();
|
||||||
|
|
||||||
|
// Device events for pointer.
|
||||||
|
let relative_pointer_manager = SimpleGlobal::new();
|
||||||
|
|
||||||
|
// Pointer grab functionality.
|
||||||
|
let pointer_constraints = SimpleGlobal::new();
|
||||||
|
|
||||||
|
// IME handling.
|
||||||
|
let text_input_manager = SimpleGlobal::new();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
seats,
|
||||||
|
outputs,
|
||||||
|
shm,
|
||||||
|
compositor,
|
||||||
|
subcompositor,
|
||||||
|
shell,
|
||||||
|
decoration_manager,
|
||||||
|
relative_pointer_manager,
|
||||||
|
pointer_constraints,
|
||||||
|
text_input_manager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShellHandling for WinitEnv {
|
||||||
|
fn get_shell(&self) -> Option<Shell> {
|
||||||
|
self.shell.get_shell()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SeatHandling for WinitEnv {
|
||||||
|
fn listen<F: FnMut(Attached<WlSeat>, &SeatData, DispatchData<'_>) + 'static>(
|
||||||
|
&mut self,
|
||||||
|
f: F,
|
||||||
|
) -> SeatListener {
|
||||||
|
self.seats.listen(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputHandling for WinitEnv {
|
||||||
|
fn listen<F: FnMut(WlOutput, &OutputInfo, DispatchData<'_>) + 'static>(
|
||||||
|
&mut self,
|
||||||
|
f: F,
|
||||||
|
) -> OutputStatusListener {
|
||||||
|
self.outputs.listen(f)
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
539
src/platform_impl/linux/wayland/event_loop/mod.rs
Normal file
539
src/platform_impl/linux/wayland/event_loop/mod.rs
Normal file
|
@ -0,0 +1,539 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::process;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use sctk::reexports::client::protocol::wl_compositor::WlCompositor;
|
||||||
|
use sctk::reexports::client::protocol::wl_shm::WlShm;
|
||||||
|
use sctk::reexports::client::Display;
|
||||||
|
|
||||||
|
use sctk::reexports::calloop;
|
||||||
|
|
||||||
|
use sctk::environment::Environment;
|
||||||
|
use sctk::seat::pointer::{ThemeManager, ThemeSpec};
|
||||||
|
use sctk::WaylandSource;
|
||||||
|
|
||||||
|
use crate::event::{Event, StartCause, WindowEvent};
|
||||||
|
use crate::event_loop::{ControlFlow, EventLoopWindowTarget as RootEventLoopWindowTarget};
|
||||||
|
use crate::platform_impl::platform::sticky_exit_callback;
|
||||||
|
|
||||||
|
use super::env::{WindowingFeatures, WinitEnv};
|
||||||
|
use super::output::OutputManager;
|
||||||
|
use super::seat::SeatManager;
|
||||||
|
use super::window::shim::{self, WindowUpdate};
|
||||||
|
use super::{DeviceId, WindowId};
|
||||||
|
|
||||||
|
mod proxy;
|
||||||
|
mod sink;
|
||||||
|
mod state;
|
||||||
|
|
||||||
|
pub use proxy::EventLoopProxy;
|
||||||
|
pub use state::WinitState;
|
||||||
|
|
||||||
|
use sink::EventSink;
|
||||||
|
|
||||||
|
pub struct EventLoopWindowTarget<T> {
|
||||||
|
/// Wayland display.
|
||||||
|
pub display: Display,
|
||||||
|
|
||||||
|
/// Environment to handle object creation, etc.
|
||||||
|
pub env: Environment<WinitEnv>,
|
||||||
|
|
||||||
|
/// Event loop handle.
|
||||||
|
pub event_loop_handle: calloop::LoopHandle<WinitState>,
|
||||||
|
|
||||||
|
/// Output manager.
|
||||||
|
pub output_manager: OutputManager,
|
||||||
|
|
||||||
|
/// State that we share across callbacks.
|
||||||
|
pub state: RefCell<WinitState>,
|
||||||
|
|
||||||
|
/// Wayland source.
|
||||||
|
pub wayland_source: Rc<calloop::Source<WaylandSource>>,
|
||||||
|
|
||||||
|
/// A proxy to wake up event loop.
|
||||||
|
pub event_loop_awakener: calloop::ping::Ping,
|
||||||
|
|
||||||
|
/// The available windowing features.
|
||||||
|
pub windowing_features: WindowingFeatures,
|
||||||
|
|
||||||
|
/// Theme manager to manage cursors.
|
||||||
|
///
|
||||||
|
/// It's being shared amoung all windows to avoid loading
|
||||||
|
/// multiple similar themes.
|
||||||
|
pub theme_manager: ThemeManager,
|
||||||
|
|
||||||
|
_marker: std::marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EventLoop<T: 'static> {
|
||||||
|
/// Event loop.
|
||||||
|
event_loop: calloop::EventLoop<WinitState>,
|
||||||
|
|
||||||
|
/// Wayland display.
|
||||||
|
display: Display,
|
||||||
|
|
||||||
|
/// Pending user events.
|
||||||
|
pending_user_events: Rc<RefCell<Vec<T>>>,
|
||||||
|
|
||||||
|
/// Sender of user events.
|
||||||
|
user_events_sender: calloop::channel::Sender<T>,
|
||||||
|
|
||||||
|
/// Wayland source of events.
|
||||||
|
wayland_source: Rc<calloop::Source<WaylandSource>>,
|
||||||
|
|
||||||
|
/// Window target.
|
||||||
|
window_target: RootEventLoopWindowTarget<T>,
|
||||||
|
|
||||||
|
/// Output manager.
|
||||||
|
_seat_manager: SeatManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> EventLoop<T> {
|
||||||
|
pub fn new() -> Result<EventLoop<T>, Box<dyn Error>> {
|
||||||
|
// Connect to wayland server and setup event queue.
|
||||||
|
let display = Display::connect_to_env()?;
|
||||||
|
let mut event_queue = display.create_event_queue();
|
||||||
|
let display_proxy = display.attach(event_queue.token());
|
||||||
|
|
||||||
|
// Setup environment.
|
||||||
|
let env = Environment::init(&display_proxy, WinitEnv::new());
|
||||||
|
|
||||||
|
// Issue 2 sync roundtrips to initialize environment.
|
||||||
|
event_queue.sync_roundtrip(&mut (), |_, _, _| unreachable!())?;
|
||||||
|
event_queue.sync_roundtrip(&mut (), |_, _, _| unreachable!())?;
|
||||||
|
|
||||||
|
// Create event loop.
|
||||||
|
let event_loop = calloop::EventLoop::<WinitState>::new()?;
|
||||||
|
// Build windowing features.
|
||||||
|
let windowing_features = WindowingFeatures::new(&env);
|
||||||
|
|
||||||
|
// Create a theme manager.
|
||||||
|
let compositor = env.require_global::<WlCompositor>();
|
||||||
|
let shm = env.require_global::<WlShm>();
|
||||||
|
let theme_manager = ThemeManager::init(ThemeSpec::System, compositor, shm);
|
||||||
|
|
||||||
|
// Setup theme seat and output managers.
|
||||||
|
let seat_manager = SeatManager::new(&env, event_loop.handle(), theme_manager.clone());
|
||||||
|
let output_manager = OutputManager::new(&env);
|
||||||
|
|
||||||
|
// A source of events that we plug into our event loop.
|
||||||
|
let wayland_source = WaylandSource::new(event_queue).quick_insert(event_loop.handle())?;
|
||||||
|
let wayland_source = Rc::new(wayland_source);
|
||||||
|
|
||||||
|
// A source of user events.
|
||||||
|
let pending_user_events = Rc::new(RefCell::new(Vec::new()));
|
||||||
|
let pending_user_events_clone = pending_user_events.clone();
|
||||||
|
let (user_events_sender, user_events_channel) = calloop::channel::channel();
|
||||||
|
|
||||||
|
// User events channel.
|
||||||
|
event_loop
|
||||||
|
.handle()
|
||||||
|
.insert_source(user_events_channel, move |event, _, _| {
|
||||||
|
if let calloop::channel::Event::Msg(msg) = event {
|
||||||
|
pending_user_events_clone.borrow_mut().push(msg);
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// 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()?;
|
||||||
|
|
||||||
|
// Handler of window requests.
|
||||||
|
event_loop.handle().insert_source(
|
||||||
|
event_loop_awakener_source,
|
||||||
|
move |_, _, winit_state| {
|
||||||
|
shim::handle_window_requests(winit_state);
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let event_loop_handle = event_loop.handle();
|
||||||
|
let window_map = HashMap::new();
|
||||||
|
let event_sink = EventSink::new();
|
||||||
|
let window_updates = HashMap::new();
|
||||||
|
|
||||||
|
// Create event loop window target.
|
||||||
|
let event_loop_window_target = EventLoopWindowTarget {
|
||||||
|
display: display.clone(),
|
||||||
|
env,
|
||||||
|
state: RefCell::new(WinitState {
|
||||||
|
window_map,
|
||||||
|
event_sink,
|
||||||
|
window_updates,
|
||||||
|
}),
|
||||||
|
event_loop_handle,
|
||||||
|
output_manager,
|
||||||
|
event_loop_awakener,
|
||||||
|
wayland_source: wayland_source.clone(),
|
||||||
|
windowing_features,
|
||||||
|
theme_manager,
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create event loop itself.
|
||||||
|
let event_loop = Self {
|
||||||
|
event_loop,
|
||||||
|
display,
|
||||||
|
pending_user_events,
|
||||||
|
wayland_source,
|
||||||
|
_seat_manager: seat_manager,
|
||||||
|
user_events_sender,
|
||||||
|
window_target: RootEventLoopWindowTarget {
|
||||||
|
p: crate::platform_impl::EventLoopWindowTarget::Wayland(event_loop_window_target),
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(event_loop)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run<F>(mut self, callback: F) -> !
|
||||||
|
where
|
||||||
|
F: FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow) + 'static,
|
||||||
|
{
|
||||||
|
self.run_return(callback);
|
||||||
|
process::exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_return<F>(&mut self, mut callback: F)
|
||||||
|
where
|
||||||
|
F: FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
||||||
|
{
|
||||||
|
// Send pending events to the server.
|
||||||
|
let _ = self.display.flush();
|
||||||
|
|
||||||
|
let mut control_flow = ControlFlow::default();
|
||||||
|
|
||||||
|
let pending_user_events = self.pending_user_events.clone();
|
||||||
|
|
||||||
|
callback(
|
||||||
|
Event::NewEvents(StartCause::Init),
|
||||||
|
&self.window_target,
|
||||||
|
&mut control_flow,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut window_updates: Vec<(WindowId, WindowUpdate)> = Vec::new();
|
||||||
|
let mut event_sink_back_buffer = Vec::new();
|
||||||
|
|
||||||
|
// NOTE We break 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.
|
||||||
|
loop {
|
||||||
|
// Handle pending user events. We don't need back buffer, since we can't dispatch
|
||||||
|
// user events indirectly via callback to the user.
|
||||||
|
for user_event in pending_user_events.borrow_mut().drain(..) {
|
||||||
|
sticky_exit_callback(
|
||||||
|
Event::UserEvent(user_event),
|
||||||
|
&self.window_target,
|
||||||
|
&mut control_flow,
|
||||||
|
&mut callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process 'new' pending updates.
|
||||||
|
self.with_state(|state| {
|
||||||
|
window_updates.clear();
|
||||||
|
window_updates.extend(
|
||||||
|
state
|
||||||
|
.window_updates
|
||||||
|
.iter_mut()
|
||||||
|
.map(|(wid, window_update)| (*wid, window_update.take())),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (window_id, window_update) in window_updates.iter_mut() {
|
||||||
|
if let Some(scale_factor) = window_update.scale_factor.map(|f| f as f64) {
|
||||||
|
let mut physical_size = self.with_state(|state| {
|
||||||
|
let window_handle = state.window_map.get(&window_id).unwrap();
|
||||||
|
let mut size = window_handle.size.lock().unwrap();
|
||||||
|
|
||||||
|
// Update the new logical size if it was changed.
|
||||||
|
let window_size = window_update.size.unwrap_or(*size);
|
||||||
|
*size = window_size;
|
||||||
|
|
||||||
|
window_size.to_physical(scale_factor)
|
||||||
|
});
|
||||||
|
|
||||||
|
sticky_exit_callback(
|
||||||
|
Event::WindowEvent {
|
||||||
|
window_id: crate::window::WindowId(
|
||||||
|
crate::platform_impl::WindowId::Wayland(*window_id),
|
||||||
|
),
|
||||||
|
event: WindowEvent::ScaleFactorChanged {
|
||||||
|
scale_factor,
|
||||||
|
new_inner_size: &mut physical_size,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&self.window_target,
|
||||||
|
&mut control_flow,
|
||||||
|
&mut callback,
|
||||||
|
);
|
||||||
|
|
||||||
|
// We don't update size on a window handle since we'll do that later
|
||||||
|
// when handling size update.
|
||||||
|
let new_logical_size = physical_size.to_logical(scale_factor);
|
||||||
|
window_update.size = Some(new_logical_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(size) = window_update.size.take() {
|
||||||
|
let physical_size = self.with_state(|state| {
|
||||||
|
let window_handle = state.window_map.get_mut(&window_id).unwrap();
|
||||||
|
let mut window_size = window_handle.size.lock().unwrap();
|
||||||
|
|
||||||
|
// Always issue resize event on scale factor change.
|
||||||
|
let physical_size =
|
||||||
|
if window_update.scale_factor.is_none() && *window_size == size {
|
||||||
|
// The size hasn't changed, don't inform downstream about that.
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
*window_size = size;
|
||||||
|
let scale_factor =
|
||||||
|
sctk::get_surface_scale_factor(&window_handle.window.surface());
|
||||||
|
let physical_size = size.to_physical(scale_factor as f64);
|
||||||
|
Some(physical_size)
|
||||||
|
};
|
||||||
|
|
||||||
|
// We still perform all of those resize related logic even if the size
|
||||||
|
// hasn't changed, since GNOME relies on `set_geometry` calls after
|
||||||
|
// configures.
|
||||||
|
window_handle.window.resize(size.width, size.height);
|
||||||
|
window_handle.window.refresh();
|
||||||
|
|
||||||
|
// Mark that refresh isn't required, since we've done it right now.
|
||||||
|
window_update.refresh_frame = false;
|
||||||
|
|
||||||
|
physical_size
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(physical_size) = physical_size {
|
||||||
|
sticky_exit_callback(
|
||||||
|
Event::WindowEvent {
|
||||||
|
window_id: crate::window::WindowId(
|
||||||
|
crate::platform_impl::WindowId::Wayland(*window_id),
|
||||||
|
),
|
||||||
|
event: WindowEvent::Resized(physical_size),
|
||||||
|
},
|
||||||
|
&self.window_target,
|
||||||
|
&mut control_flow,
|
||||||
|
&mut callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if window_update.close_window {
|
||||||
|
sticky_exit_callback(
|
||||||
|
Event::WindowEvent {
|
||||||
|
window_id: crate::window::WindowId(
|
||||||
|
crate::platform_impl::WindowId::Wayland(*window_id),
|
||||||
|
),
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
},
|
||||||
|
&self.window_target,
|
||||||
|
&mut control_flow,
|
||||||
|
&mut callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The purpose of the back buffer and that swap is to not hold borrow_mut when
|
||||||
|
// we're doing callback to the user, since we can double borrow if the user decides
|
||||||
|
// to create a window in one of those callbacks.
|
||||||
|
self.with_state(|state| {
|
||||||
|
std::mem::swap(
|
||||||
|
&mut event_sink_back_buffer,
|
||||||
|
&mut state.event_sink.window_events,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle pending window events.
|
||||||
|
for event in event_sink_back_buffer.drain(..) {
|
||||||
|
let event = event.map_nonuser_event().unwrap();
|
||||||
|
sticky_exit_callback(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 RedrawRequested events.
|
||||||
|
for (window_id, window_update) in window_updates.iter() {
|
||||||
|
// Handle refresh of the frame.
|
||||||
|
if window_update.refresh_frame {
|
||||||
|
self.with_state(|state| {
|
||||||
|
let window_handle = state.window_map.get_mut(&window_id).unwrap();
|
||||||
|
window_handle.window.refresh();
|
||||||
|
if !window_update.redraw_requested {
|
||||||
|
window_handle.window.surface().commit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle redraw request.
|
||||||
|
if window_update.redraw_requested {
|
||||||
|
sticky_exit_callback(
|
||||||
|
Event::RedrawRequested(crate::window::WindowId(
|
||||||
|
crate::platform_impl::WindowId::Wayland(*window_id),
|
||||||
|
)),
|
||||||
|
&self.window_target,
|
||||||
|
&mut control_flow,
|
||||||
|
&mut callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send RedrawEventCleared.
|
||||||
|
sticky_exit_callback(
|
||||||
|
Event::RedrawEventsCleared,
|
||||||
|
&self.window_target,
|
||||||
|
&mut control_flow,
|
||||||
|
&mut callback,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Send pending events to the server.
|
||||||
|
let _ = self.display.flush();
|
||||||
|
|
||||||
|
// 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 delaying the
|
||||||
|
// dispatch of these events until we're woken up again.
|
||||||
|
let instant_wakeup = {
|
||||||
|
let handle = self.event_loop.handle();
|
||||||
|
let source = self.wayland_source.clone();
|
||||||
|
let dispatched = handle.with_source(&source, |wayland_source| {
|
||||||
|
let queue = wayland_source.queue();
|
||||||
|
self.with_state(|state| {
|
||||||
|
queue.dispatch_pending(state, |_, _, _| unimplemented!())
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Ok(dispatched) = dispatched {
|
||||||
|
dispatched > 0
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match control_flow {
|
||||||
|
ControlFlow::Exit => break,
|
||||||
|
ControlFlow::Poll => {
|
||||||
|
// Non-blocking dispatch.
|
||||||
|
let timeout = Duration::from_millis(0);
|
||||||
|
if self.loop_dispatch(Some(timeout)).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(
|
||||||
|
Event::NewEvents(StartCause::Poll),
|
||||||
|
&self.window_target,
|
||||||
|
&mut control_flow,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ControlFlow::Wait => {
|
||||||
|
let timeout = if instant_wakeup {
|
||||||
|
Some(Duration::from_millis(0))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.loop_dispatch(timeout).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 amount of time we'll block for.
|
||||||
|
let duration = if deadline > start && !instant_wakeup {
|
||||||
|
deadline - start
|
||||||
|
} else {
|
||||||
|
Duration::from_millis(0)
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.loop_dispatch(Some(duration)).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||||
|
EventLoopProxy::new(self.user_events_sender.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> {
|
||||||
|
&self.window_target
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_state<U, F: FnOnce(&mut WinitState) -> U>(&mut self, f: F) -> U {
|
||||||
|
let state = match &mut self.window_target.p {
|
||||||
|
crate::platform_impl::EventLoopWindowTarget::Wayland(ref mut window_target) => {
|
||||||
|
window_target.state.get_mut()
|
||||||
|
}
|
||||||
|
#[cfg(feature = "x11")]
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
f(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loop_dispatch<D: Into<Option<std::time::Duration>>>(
|
||||||
|
&mut self,
|
||||||
|
timeout: D,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
|
let mut state = match &mut self.window_target.p {
|
||||||
|
crate::platform_impl::EventLoopWindowTarget::Wayland(ref mut window_target) => {
|
||||||
|
window_target.state.get_mut()
|
||||||
|
}
|
||||||
|
#[cfg(feature = "x11")]
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.event_loop.dispatch(timeout, &mut state)
|
||||||
|
}
|
||||||
|
}
|
32
src/platform_impl/linux/wayland/event_loop/proxy.rs
Normal file
32
src/platform_impl/linux/wayland/event_loop/proxy.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
//! An event loop proxy.
|
||||||
|
|
||||||
|
use std::sync::mpsc::SendError;
|
||||||
|
|
||||||
|
use sctk::reexports::calloop::channel::Sender;
|
||||||
|
|
||||||
|
use crate::event_loop::EventLoopClosed;
|
||||||
|
|
||||||
|
/// A handle that can be sent across the threads and used to wake up the `EventLoop`.
|
||||||
|
pub struct EventLoopProxy<T: 'static> {
|
||||||
|
user_events_sender: Sender<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
EventLoopProxy {
|
||||||
|
user_events_sender: self.user_events_sender.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> EventLoopProxy<T> {
|
||||||
|
pub fn new(user_events_sender: Sender<T>) -> Self {
|
||||||
|
Self { user_events_sender }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||||
|
self.user_events_sender
|
||||||
|
.send(event)
|
||||||
|
.map_err(|SendError(error)| EventLoopClosed(error))
|
||||||
|
}
|
||||||
|
}
|
36
src/platform_impl/linux/wayland/event_loop/sink.rs
Normal file
36
src/platform_impl/linux/wayland/event_loop/sink.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
//! An event loop's sink to deliver events from the Wayland event callbacks.
|
||||||
|
|
||||||
|
use crate::event::{DeviceEvent, DeviceId as RootDeviceId, Event, WindowEvent};
|
||||||
|
use crate::platform_impl::platform::{DeviceId as PlatformDeviceId, WindowId as PlatformWindowId};
|
||||||
|
use crate::window::WindowId as RootWindowId;
|
||||||
|
|
||||||
|
use super::{DeviceId, WindowId};
|
||||||
|
|
||||||
|
/// An event loop's sink to deliver events from the Wayland event callbacks
|
||||||
|
/// to the winit's user.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct EventSink {
|
||||||
|
pub window_events: Vec<Event<'static, ()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventSink {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add new device event to a queue.
|
||||||
|
pub fn push_device_event(&mut self, event: DeviceEvent, device_id: DeviceId) {
|
||||||
|
self.window_events.push(Event::DeviceEvent {
|
||||||
|
event,
|
||||||
|
device_id: RootDeviceId(PlatformDeviceId::Wayland(device_id)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add new window event to a queue.
|
||||||
|
pub fn push_window_event(&mut self, event: WindowEvent<'static>, window_id: WindowId) {
|
||||||
|
self.window_events.push(Event::WindowEvent {
|
||||||
|
event,
|
||||||
|
window_id: RootWindowId(PlatformWindowId::Wayland(window_id)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
25
src/platform_impl/linux/wayland/event_loop/state.rs
Normal file
25
src/platform_impl/linux/wayland/event_loop/state.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
//! A state that we pass around in a dispatch.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use super::EventSink;
|
||||||
|
use crate::platform_impl::wayland::window::shim::{WindowHandle, WindowUpdate};
|
||||||
|
use crate::platform_impl::wayland::WindowId;
|
||||||
|
|
||||||
|
/// Wrapper to carry winit's state.
|
||||||
|
pub struct WinitState {
|
||||||
|
/// A sink for window and device events that is being filled during dispatching
|
||||||
|
/// event loop and forwarded downstream afterwards.
|
||||||
|
pub event_sink: EventSink,
|
||||||
|
|
||||||
|
/// Window updates, which are coming from SCTK or the compositor, which require
|
||||||
|
/// calling back to the winit's downstream. They are handled right in the event loop,
|
||||||
|
/// unlike the ones coming from buffers on the `WindowHandle`'s.
|
||||||
|
pub window_updates: HashMap<WindowId, WindowUpdate>,
|
||||||
|
|
||||||
|
/// Window map containing all SCTK windows. Since those windows aren't allowed
|
||||||
|
/// to be sent to other threads, they live on the event loop's thread
|
||||||
|
/// and requests from winit's windows are being forwarded to them either via
|
||||||
|
/// `WindowUpdate` or buffer on the associated with it `WindowHandle`.
|
||||||
|
pub window_map: HashMap<WindowId, WindowHandle>,
|
||||||
|
}
|
|
@ -1,396 +0,0 @@
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use super::{event_loop::EventsSink, make_wid, DeviceId};
|
|
||||||
use smithay_client_toolkit::{
|
|
||||||
keyboard::{
|
|
||||||
self, map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatEvent, KeyRepeatKind,
|
|
||||||
},
|
|
||||||
reexports::client::protocol::{wl_keyboard, wl_seat},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent};
|
|
||||||
|
|
||||||
pub fn init_keyboard(
|
|
||||||
seat: &wl_seat::WlSeat,
|
|
||||||
sink: EventsSink,
|
|
||||||
modifiers_tracker: Arc<Mutex<ModifiersState>>,
|
|
||||||
) -> wl_keyboard::WlKeyboard {
|
|
||||||
// { variables to be captured by the closures
|
|
||||||
let target = Arc::new(Mutex::new(None));
|
|
||||||
let my_sink = sink.clone();
|
|
||||||
let repeat_sink = sink.clone();
|
|
||||||
let repeat_target = target.clone();
|
|
||||||
let my_modifiers = modifiers_tracker.clone();
|
|
||||||
// }
|
|
||||||
let ret = map_keyboard_auto_with_repeat(
|
|
||||||
seat,
|
|
||||||
KeyRepeatKind::System,
|
|
||||||
move |evt: KbEvent<'_>, _| {
|
|
||||||
match evt {
|
|
||||||
KbEvent::Enter { surface, .. } => {
|
|
||||||
let wid = make_wid(&surface);
|
|
||||||
my_sink.send_window_event(WindowEvent::Focused(true), wid);
|
|
||||||
*target.lock().unwrap() = Some(wid);
|
|
||||||
|
|
||||||
let modifiers = *modifiers_tracker.lock().unwrap();
|
|
||||||
|
|
||||||
if !modifiers.is_empty() {
|
|
||||||
my_sink.send_window_event(WindowEvent::ModifiersChanged(modifiers), wid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
KbEvent::Leave { surface, .. } => {
|
|
||||||
let wid = make_wid(&surface);
|
|
||||||
let modifiers = *modifiers_tracker.lock().unwrap();
|
|
||||||
|
|
||||||
if !modifiers.is_empty() {
|
|
||||||
my_sink.send_window_event(
|
|
||||||
WindowEvent::ModifiersChanged(ModifiersState::empty()),
|
|
||||||
wid,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
my_sink.send_window_event(WindowEvent::Focused(false), wid);
|
|
||||||
*target.lock().unwrap() = None;
|
|
||||||
}
|
|
||||||
KbEvent::Key {
|
|
||||||
rawkey,
|
|
||||||
keysym,
|
|
||||||
state,
|
|
||||||
utf8,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
if let Some(wid) = *target.lock().unwrap() {
|
|
||||||
let state = match state {
|
|
||||||
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
|
|
||||||
wl_keyboard::KeyState::Released => ElementState::Released,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
let vkcode = key_to_vkey(rawkey, keysym);
|
|
||||||
my_sink.send_window_event(
|
|
||||||
#[allow(deprecated)]
|
|
||||||
WindowEvent::KeyboardInput {
|
|
||||||
device_id: crate::event::DeviceId(
|
|
||||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
|
||||||
),
|
|
||||||
input: KeyboardInput {
|
|
||||||
state,
|
|
||||||
scancode: rawkey,
|
|
||||||
virtual_keycode: vkcode,
|
|
||||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
|
||||||
},
|
|
||||||
is_synthetic: false,
|
|
||||||
},
|
|
||||||
wid,
|
|
||||||
);
|
|
||||||
// send char event only on key press, not release
|
|
||||||
if let ElementState::Released = state {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if let Some(txt) = utf8 {
|
|
||||||
for chr in txt.chars() {
|
|
||||||
my_sink.send_window_event(WindowEvent::ReceivedCharacter(chr), wid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
KbEvent::RepeatInfo { .. } => { /* Handled by smithay client toolkit */ }
|
|
||||||
KbEvent::Modifiers {
|
|
||||||
modifiers: event_modifiers,
|
|
||||||
} => {
|
|
||||||
let modifiers = ModifiersState::from_wayland(event_modifiers);
|
|
||||||
|
|
||||||
*modifiers_tracker.lock().unwrap() = modifiers;
|
|
||||||
|
|
||||||
if let Some(wid) = *target.lock().unwrap() {
|
|
||||||
my_sink.send_window_event(WindowEvent::ModifiersChanged(modifiers), wid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
move |repeat_event: KeyRepeatEvent, _| {
|
|
||||||
if let Some(wid) = *repeat_target.lock().unwrap() {
|
|
||||||
let state = ElementState::Pressed;
|
|
||||||
let vkcode = key_to_vkey(repeat_event.rawkey, repeat_event.keysym);
|
|
||||||
repeat_sink.send_window_event(
|
|
||||||
#[allow(deprecated)]
|
|
||||||
WindowEvent::KeyboardInput {
|
|
||||||
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
|
||||||
DeviceId,
|
|
||||||
)),
|
|
||||||
input: KeyboardInput {
|
|
||||||
state,
|
|
||||||
scancode: repeat_event.rawkey,
|
|
||||||
virtual_keycode: vkcode,
|
|
||||||
modifiers: my_modifiers.lock().unwrap().clone(),
|
|
||||||
},
|
|
||||||
is_synthetic: false,
|
|
||||||
},
|
|
||||||
wid,
|
|
||||||
);
|
|
||||||
if let Some(txt) = repeat_event.utf8 {
|
|
||||||
for chr in txt.chars() {
|
|
||||||
repeat_sink.send_window_event(WindowEvent::ReceivedCharacter(chr), wid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
match ret {
|
|
||||||
Ok(keyboard) => keyboard,
|
|
||||||
Err(_) => {
|
|
||||||
// This is a fallback impl if libxkbcommon was not available
|
|
||||||
// This case should probably never happen, as most wayland
|
|
||||||
// compositors _need_ libxkbcommon anyway...
|
|
||||||
//
|
|
||||||
// In this case, we don't have the keymap information (it is
|
|
||||||
// supposed to be serialized by the compositor using libxkbcommon)
|
|
||||||
|
|
||||||
seat.get_keyboard(|keyboard| {
|
|
||||||
// { variables to be captured by the closure
|
|
||||||
let mut target = None;
|
|
||||||
let my_sink = sink;
|
|
||||||
// }
|
|
||||||
|
|
||||||
keyboard.implement_closure(
|
|
||||||
move |evt, _| match evt {
|
|
||||||
wl_keyboard::Event::Enter { surface, .. } => {
|
|
||||||
let wid = make_wid(&surface);
|
|
||||||
my_sink.send_window_event(WindowEvent::Focused(true), wid);
|
|
||||||
target = Some(wid);
|
|
||||||
}
|
|
||||||
wl_keyboard::Event::Leave { surface, .. } => {
|
|
||||||
let wid = make_wid(&surface);
|
|
||||||
my_sink.send_window_event(WindowEvent::Focused(false), wid);
|
|
||||||
target = None;
|
|
||||||
}
|
|
||||||
wl_keyboard::Event::Key { key, state, .. } => {
|
|
||||||
if let Some(wid) = target {
|
|
||||||
let state = match state {
|
|
||||||
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
|
|
||||||
wl_keyboard::KeyState::Released => ElementState::Released,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
my_sink.send_window_event(
|
|
||||||
#[allow(deprecated)]
|
|
||||||
WindowEvent::KeyboardInput {
|
|
||||||
device_id: crate::event::DeviceId(
|
|
||||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
|
||||||
),
|
|
||||||
input: KeyboardInput {
|
|
||||||
state,
|
|
||||||
scancode: key,
|
|
||||||
virtual_keycode: None,
|
|
||||||
modifiers: ModifiersState::default(),
|
|
||||||
},
|
|
||||||
is_synthetic: false,
|
|
||||||
},
|
|
||||||
wid,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
},
|
|
||||||
(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn key_to_vkey(rawkey: u32, keysym: u32) -> Option<VirtualKeyCode> {
|
|
||||||
match rawkey {
|
|
||||||
1 => Some(VirtualKeyCode::Escape),
|
|
||||||
2 => Some(VirtualKeyCode::Key1),
|
|
||||||
3 => Some(VirtualKeyCode::Key2),
|
|
||||||
4 => Some(VirtualKeyCode::Key3),
|
|
||||||
5 => Some(VirtualKeyCode::Key4),
|
|
||||||
6 => Some(VirtualKeyCode::Key5),
|
|
||||||
7 => Some(VirtualKeyCode::Key6),
|
|
||||||
8 => Some(VirtualKeyCode::Key7),
|
|
||||||
9 => Some(VirtualKeyCode::Key8),
|
|
||||||
10 => Some(VirtualKeyCode::Key9),
|
|
||||||
11 => Some(VirtualKeyCode::Key0),
|
|
||||||
_ => keysym_to_vkey(keysym),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
|
||||||
use smithay_client_toolkit::keyboard::keysyms;
|
|
||||||
match keysym {
|
|
||||||
// letters
|
|
||||||
keysyms::XKB_KEY_A | keysyms::XKB_KEY_a => Some(VirtualKeyCode::A),
|
|
||||||
keysyms::XKB_KEY_B | keysyms::XKB_KEY_b => Some(VirtualKeyCode::B),
|
|
||||||
keysyms::XKB_KEY_C | keysyms::XKB_KEY_c => Some(VirtualKeyCode::C),
|
|
||||||
keysyms::XKB_KEY_D | keysyms::XKB_KEY_d => Some(VirtualKeyCode::D),
|
|
||||||
keysyms::XKB_KEY_E | keysyms::XKB_KEY_e => Some(VirtualKeyCode::E),
|
|
||||||
keysyms::XKB_KEY_F | keysyms::XKB_KEY_f => Some(VirtualKeyCode::F),
|
|
||||||
keysyms::XKB_KEY_G | keysyms::XKB_KEY_g => Some(VirtualKeyCode::G),
|
|
||||||
keysyms::XKB_KEY_H | keysyms::XKB_KEY_h => Some(VirtualKeyCode::H),
|
|
||||||
keysyms::XKB_KEY_I | keysyms::XKB_KEY_i => Some(VirtualKeyCode::I),
|
|
||||||
keysyms::XKB_KEY_J | keysyms::XKB_KEY_j => Some(VirtualKeyCode::J),
|
|
||||||
keysyms::XKB_KEY_K | keysyms::XKB_KEY_k => Some(VirtualKeyCode::K),
|
|
||||||
keysyms::XKB_KEY_L | keysyms::XKB_KEY_l => Some(VirtualKeyCode::L),
|
|
||||||
keysyms::XKB_KEY_M | keysyms::XKB_KEY_m => Some(VirtualKeyCode::M),
|
|
||||||
keysyms::XKB_KEY_N | keysyms::XKB_KEY_n => Some(VirtualKeyCode::N),
|
|
||||||
keysyms::XKB_KEY_O | keysyms::XKB_KEY_o => Some(VirtualKeyCode::O),
|
|
||||||
keysyms::XKB_KEY_P | keysyms::XKB_KEY_p => Some(VirtualKeyCode::P),
|
|
||||||
keysyms::XKB_KEY_Q | keysyms::XKB_KEY_q => Some(VirtualKeyCode::Q),
|
|
||||||
keysyms::XKB_KEY_R | keysyms::XKB_KEY_r => Some(VirtualKeyCode::R),
|
|
||||||
keysyms::XKB_KEY_S | keysyms::XKB_KEY_s => Some(VirtualKeyCode::S),
|
|
||||||
keysyms::XKB_KEY_T | keysyms::XKB_KEY_t => Some(VirtualKeyCode::T),
|
|
||||||
keysyms::XKB_KEY_U | keysyms::XKB_KEY_u => Some(VirtualKeyCode::U),
|
|
||||||
keysyms::XKB_KEY_V | keysyms::XKB_KEY_v => Some(VirtualKeyCode::V),
|
|
||||||
keysyms::XKB_KEY_W | keysyms::XKB_KEY_w => Some(VirtualKeyCode::W),
|
|
||||||
keysyms::XKB_KEY_X | keysyms::XKB_KEY_x => Some(VirtualKeyCode::X),
|
|
||||||
keysyms::XKB_KEY_Y | keysyms::XKB_KEY_y => Some(VirtualKeyCode::Y),
|
|
||||||
keysyms::XKB_KEY_Z | keysyms::XKB_KEY_z => Some(VirtualKeyCode::Z),
|
|
||||||
// F--
|
|
||||||
keysyms::XKB_KEY_F1 => Some(VirtualKeyCode::F1),
|
|
||||||
keysyms::XKB_KEY_F2 => Some(VirtualKeyCode::F2),
|
|
||||||
keysyms::XKB_KEY_F3 => Some(VirtualKeyCode::F3),
|
|
||||||
keysyms::XKB_KEY_F4 => Some(VirtualKeyCode::F4),
|
|
||||||
keysyms::XKB_KEY_F5 => Some(VirtualKeyCode::F5),
|
|
||||||
keysyms::XKB_KEY_F6 => Some(VirtualKeyCode::F6),
|
|
||||||
keysyms::XKB_KEY_F7 => Some(VirtualKeyCode::F7),
|
|
||||||
keysyms::XKB_KEY_F8 => Some(VirtualKeyCode::F8),
|
|
||||||
keysyms::XKB_KEY_F9 => Some(VirtualKeyCode::F9),
|
|
||||||
keysyms::XKB_KEY_F10 => Some(VirtualKeyCode::F10),
|
|
||||||
keysyms::XKB_KEY_F11 => Some(VirtualKeyCode::F11),
|
|
||||||
keysyms::XKB_KEY_F12 => Some(VirtualKeyCode::F12),
|
|
||||||
keysyms::XKB_KEY_F13 => Some(VirtualKeyCode::F13),
|
|
||||||
keysyms::XKB_KEY_F14 => Some(VirtualKeyCode::F14),
|
|
||||||
keysyms::XKB_KEY_F15 => Some(VirtualKeyCode::F15),
|
|
||||||
keysyms::XKB_KEY_F16 => Some(VirtualKeyCode::F16),
|
|
||||||
keysyms::XKB_KEY_F17 => Some(VirtualKeyCode::F17),
|
|
||||||
keysyms::XKB_KEY_F18 => Some(VirtualKeyCode::F18),
|
|
||||||
keysyms::XKB_KEY_F19 => Some(VirtualKeyCode::F19),
|
|
||||||
keysyms::XKB_KEY_F20 => Some(VirtualKeyCode::F20),
|
|
||||||
keysyms::XKB_KEY_F21 => Some(VirtualKeyCode::F21),
|
|
||||||
keysyms::XKB_KEY_F22 => Some(VirtualKeyCode::F22),
|
|
||||||
keysyms::XKB_KEY_F23 => Some(VirtualKeyCode::F23),
|
|
||||||
keysyms::XKB_KEY_F24 => Some(VirtualKeyCode::F24),
|
|
||||||
// flow control
|
|
||||||
keysyms::XKB_KEY_Print => Some(VirtualKeyCode::Snapshot),
|
|
||||||
keysyms::XKB_KEY_Scroll_Lock => Some(VirtualKeyCode::Scroll),
|
|
||||||
keysyms::XKB_KEY_Pause => Some(VirtualKeyCode::Pause),
|
|
||||||
keysyms::XKB_KEY_Insert => Some(VirtualKeyCode::Insert),
|
|
||||||
keysyms::XKB_KEY_Home => Some(VirtualKeyCode::Home),
|
|
||||||
keysyms::XKB_KEY_Delete => Some(VirtualKeyCode::Delete),
|
|
||||||
keysyms::XKB_KEY_End => Some(VirtualKeyCode::End),
|
|
||||||
keysyms::XKB_KEY_Page_Down => Some(VirtualKeyCode::PageDown),
|
|
||||||
keysyms::XKB_KEY_Page_Up => Some(VirtualKeyCode::PageUp),
|
|
||||||
// arrows
|
|
||||||
keysyms::XKB_KEY_Left => Some(VirtualKeyCode::Left),
|
|
||||||
keysyms::XKB_KEY_Up => Some(VirtualKeyCode::Up),
|
|
||||||
keysyms::XKB_KEY_Right => Some(VirtualKeyCode::Right),
|
|
||||||
keysyms::XKB_KEY_Down => Some(VirtualKeyCode::Down),
|
|
||||||
//
|
|
||||||
keysyms::XKB_KEY_BackSpace => Some(VirtualKeyCode::Back),
|
|
||||||
keysyms::XKB_KEY_Return => Some(VirtualKeyCode::Return),
|
|
||||||
keysyms::XKB_KEY_space => Some(VirtualKeyCode::Space),
|
|
||||||
// keypad
|
|
||||||
keysyms::XKB_KEY_Num_Lock => Some(VirtualKeyCode::Numlock),
|
|
||||||
keysyms::XKB_KEY_KP_0 => Some(VirtualKeyCode::Numpad0),
|
|
||||||
keysyms::XKB_KEY_KP_1 => Some(VirtualKeyCode::Numpad1),
|
|
||||||
keysyms::XKB_KEY_KP_2 => Some(VirtualKeyCode::Numpad2),
|
|
||||||
keysyms::XKB_KEY_KP_3 => Some(VirtualKeyCode::Numpad3),
|
|
||||||
keysyms::XKB_KEY_KP_4 => Some(VirtualKeyCode::Numpad4),
|
|
||||||
keysyms::XKB_KEY_KP_5 => Some(VirtualKeyCode::Numpad5),
|
|
||||||
keysyms::XKB_KEY_KP_6 => Some(VirtualKeyCode::Numpad6),
|
|
||||||
keysyms::XKB_KEY_KP_7 => Some(VirtualKeyCode::Numpad7),
|
|
||||||
keysyms::XKB_KEY_KP_8 => Some(VirtualKeyCode::Numpad8),
|
|
||||||
keysyms::XKB_KEY_KP_9 => Some(VirtualKeyCode::Numpad9),
|
|
||||||
// misc
|
|
||||||
// => Some(VirtualKeyCode::AbntC1),
|
|
||||||
// => Some(VirtualKeyCode::AbntC2),
|
|
||||||
keysyms::XKB_KEY_plus => Some(VirtualKeyCode::Plus),
|
|
||||||
keysyms::XKB_KEY_apostrophe => Some(VirtualKeyCode::Apostrophe),
|
|
||||||
// => Some(VirtualKeyCode::Apps),
|
|
||||||
// => Some(VirtualKeyCode::At),
|
|
||||||
// => Some(VirtualKeyCode::Ax),
|
|
||||||
keysyms::XKB_KEY_backslash => Some(VirtualKeyCode::Backslash),
|
|
||||||
// => Some(VirtualKeyCode::Calculator),
|
|
||||||
// => Some(VirtualKeyCode::Capital),
|
|
||||||
keysyms::XKB_KEY_colon => Some(VirtualKeyCode::Colon),
|
|
||||||
keysyms::XKB_KEY_comma => Some(VirtualKeyCode::Comma),
|
|
||||||
// => Some(VirtualKeyCode::Convert),
|
|
||||||
keysyms::XKB_KEY_equal => Some(VirtualKeyCode::Equals),
|
|
||||||
// => Some(VirtualKeyCode::Grave),
|
|
||||||
// => Some(VirtualKeyCode::Kana),
|
|
||||||
// => Some(VirtualKeyCode::Kanji),
|
|
||||||
keysyms::XKB_KEY_Alt_L => Some(VirtualKeyCode::LAlt),
|
|
||||||
// => Some(VirtualKeyCode::LBracket),
|
|
||||||
keysyms::XKB_KEY_Control_L => Some(VirtualKeyCode::LControl),
|
|
||||||
keysyms::XKB_KEY_Shift_L => Some(VirtualKeyCode::LShift),
|
|
||||||
// => Some(VirtualKeyCode::LWin),
|
|
||||||
// => Some(VirtualKeyCode::Mail),
|
|
||||||
// => Some(VirtualKeyCode::MediaSelect),
|
|
||||||
// => Some(VirtualKeyCode::MediaStop),
|
|
||||||
keysyms::XKB_KEY_minus => Some(VirtualKeyCode::Minus),
|
|
||||||
keysyms::XKB_KEY_asterisk => Some(VirtualKeyCode::Asterisk),
|
|
||||||
// => Some(VirtualKeyCode::Mute),
|
|
||||||
// => Some(VirtualKeyCode::MyComputer),
|
|
||||||
// => Some(VirtualKeyCode::NextTrack),
|
|
||||||
// => Some(VirtualKeyCode::NoConvert),
|
|
||||||
keysyms::XKB_KEY_KP_Separator => Some(VirtualKeyCode::NumpadComma),
|
|
||||||
keysyms::XKB_KEY_KP_Enter => Some(VirtualKeyCode::NumpadEnter),
|
|
||||||
keysyms::XKB_KEY_KP_Equal => Some(VirtualKeyCode::NumpadEquals),
|
|
||||||
keysyms::XKB_KEY_KP_Add => Some(VirtualKeyCode::NumpadAdd),
|
|
||||||
keysyms::XKB_KEY_KP_Subtract => Some(VirtualKeyCode::NumpadSubtract),
|
|
||||||
keysyms::XKB_KEY_KP_Multiply => Some(VirtualKeyCode::NumpadMultiply),
|
|
||||||
keysyms::XKB_KEY_KP_Decimal => Some(VirtualKeyCode::NumpadDecimal),
|
|
||||||
keysyms::XKB_KEY_KP_Divide => Some(VirtualKeyCode::NumpadDivide),
|
|
||||||
keysyms::XKB_KEY_KP_Page_Up => Some(VirtualKeyCode::PageUp),
|
|
||||||
keysyms::XKB_KEY_KP_Page_Down => Some(VirtualKeyCode::PageDown),
|
|
||||||
keysyms::XKB_KEY_KP_Home => Some(VirtualKeyCode::Home),
|
|
||||||
keysyms::XKB_KEY_KP_End => Some(VirtualKeyCode::End),
|
|
||||||
// => Some(VirtualKeyCode::OEM102),
|
|
||||||
// => Some(VirtualKeyCode::Period),
|
|
||||||
// => Some(VirtualKeyCode::Playpause),
|
|
||||||
// => Some(VirtualKeyCode::Power),
|
|
||||||
// => Some(VirtualKeyCode::Prevtrack),
|
|
||||||
keysyms::XKB_KEY_Alt_R => Some(VirtualKeyCode::RAlt),
|
|
||||||
// => Some(VirtualKeyCode::RBracket),
|
|
||||||
keysyms::XKB_KEY_Control_R => Some(VirtualKeyCode::RControl),
|
|
||||||
keysyms::XKB_KEY_Shift_R => Some(VirtualKeyCode::RShift),
|
|
||||||
// => Some(VirtualKeyCode::RWin),
|
|
||||||
keysyms::XKB_KEY_semicolon => Some(VirtualKeyCode::Semicolon),
|
|
||||||
keysyms::XKB_KEY_slash => Some(VirtualKeyCode::Slash),
|
|
||||||
// => Some(VirtualKeyCode::Sleep),
|
|
||||||
// => Some(VirtualKeyCode::Stop),
|
|
||||||
// => Some(VirtualKeyCode::Sysrq),
|
|
||||||
keysyms::XKB_KEY_Tab => Some(VirtualKeyCode::Tab),
|
|
||||||
keysyms::XKB_KEY_ISO_Left_Tab => Some(VirtualKeyCode::Tab),
|
|
||||||
// => Some(VirtualKeyCode::Underline),
|
|
||||||
// => Some(VirtualKeyCode::Unlabeled),
|
|
||||||
keysyms::XKB_KEY_XF86AudioLowerVolume => Some(VirtualKeyCode::VolumeDown),
|
|
||||||
keysyms::XKB_KEY_XF86AudioRaiseVolume => Some(VirtualKeyCode::VolumeUp),
|
|
||||||
// => Some(VirtualKeyCode::Wake),
|
|
||||||
// => Some(VirtualKeyCode::Webback),
|
|
||||||
// => Some(VirtualKeyCode::WebFavorites),
|
|
||||||
// => Some(VirtualKeyCode::WebForward),
|
|
||||||
// => Some(VirtualKeyCode::WebHome),
|
|
||||||
// => Some(VirtualKeyCode::WebRefresh),
|
|
||||||
// => Some(VirtualKeyCode::WebSearch),
|
|
||||||
// => Some(VirtualKeyCode::WebStop),
|
|
||||||
// => Some(VirtualKeyCode::Yen),
|
|
||||||
keysyms::XKB_KEY_XF86Copy => Some(VirtualKeyCode::Copy),
|
|
||||||
keysyms::XKB_KEY_XF86Paste => Some(VirtualKeyCode::Paste),
|
|
||||||
keysyms::XKB_KEY_XF86Cut => Some(VirtualKeyCode::Cut),
|
|
||||||
// fallback
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModifiersState {
|
|
||||||
pub(crate) fn from_wayland(mods: keyboard::ModifiersState) -> ModifiersState {
|
|
||||||
let mut m = ModifiersState::empty();
|
|
||||||
m.set(ModifiersState::SHIFT, mods.shift);
|
|
||||||
m.set(ModifiersState::CTRL, mods.ctrl);
|
|
||||||
m.set(ModifiersState::ALT, mods.alt);
|
|
||||||
m.set(ModifiersState::LOGO, mods.logo);
|
|
||||||
m
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +1,21 @@
|
||||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd",
|
#![cfg(any(
|
||||||
target_os = "netbsd", target_os = "openbsd"))]
|
target_os = "linux",
|
||||||
|
target_os = "dragonfly",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "netbsd",
|
||||||
|
target_os = "openbsd"
|
||||||
|
))]
|
||||||
|
|
||||||
pub use self::{
|
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||||
event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget, MonitorHandle, VideoMode},
|
|
||||||
window::Window,
|
|
||||||
};
|
|
||||||
|
|
||||||
use smithay_client_toolkit::reexports::client::protocol::wl_surface;
|
pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
|
||||||
|
pub use output::{MonitorHandle, VideoMode};
|
||||||
|
pub use window::Window;
|
||||||
|
|
||||||
|
mod env;
|
||||||
mod event_loop;
|
mod event_loop;
|
||||||
mod keyboard;
|
mod output;
|
||||||
mod pointer;
|
mod seat;
|
||||||
mod touch;
|
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
@ -33,6 +37,6 @@ impl WindowId {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn make_wid(s: &wl_surface::WlSurface) -> WindowId {
|
fn make_wid(surface: &WlSurface) -> WindowId {
|
||||||
WindowId(s.as_ref().c_ptr() as usize)
|
WindowId(surface.as_ref().c_ptr() as usize)
|
||||||
}
|
}
|
||||||
|
|
240
src/platform_impl/linux/wayland/output.rs
Normal file
240
src/platform_impl/linux/wayland/output.rs
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use sctk::reexports::client::protocol::wl_output::WlOutput;
|
||||||
|
use sctk::reexports::client::Display;
|
||||||
|
|
||||||
|
use sctk::environment::Environment;
|
||||||
|
use sctk::output::OutputStatusListener;
|
||||||
|
|
||||||
|
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||||
|
use crate::monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode};
|
||||||
|
use crate::platform_impl::platform::{
|
||||||
|
MonitorHandle as PlatformMonitorHandle, VideoMode as PlatformVideoMode,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::env::WinitEnv;
|
||||||
|
use super::event_loop::EventLoopWindowTarget;
|
||||||
|
|
||||||
|
/// Output manager.
|
||||||
|
pub struct OutputManager {
|
||||||
|
/// A handle that actually performs all operations on outputs.
|
||||||
|
handle: OutputManagerHandle,
|
||||||
|
|
||||||
|
_output_listener: OutputStatusListener,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputManager {
|
||||||
|
pub fn new(env: &Environment<WinitEnv>) -> Self {
|
||||||
|
let handle = OutputManagerHandle::new();
|
||||||
|
|
||||||
|
// Handle existing outputs.
|
||||||
|
for output in env.get_all_outputs() {
|
||||||
|
match sctk::output::with_output_info(&output, |info| info.obsolete) {
|
||||||
|
Some(false) => (),
|
||||||
|
// The output is obsolete or we've failed to access its data, skipping.
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
// The output is present and unusable, add it to the output manager manager.
|
||||||
|
handle.add_output(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
let handle_for_listener = handle.clone();
|
||||||
|
|
||||||
|
let output_listener = env.listen_for_outputs(move |output, info, _| {
|
||||||
|
if info.obsolete {
|
||||||
|
handle_for_listener.remove_output(output)
|
||||||
|
} else {
|
||||||
|
handle_for_listener.add_output(output)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
handle,
|
||||||
|
_output_listener: output_listener,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(&self) -> OutputManagerHandle {
|
||||||
|
self.handle.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A handle to output manager.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct OutputManagerHandle {
|
||||||
|
outputs: Arc<Mutex<VecDeque<MonitorHandle>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputManagerHandle {
|
||||||
|
fn new() -> Self {
|
||||||
|
let outputs = Arc::new(Mutex::new(VecDeque::new()));
|
||||||
|
Self { outputs }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle addition of the output.
|
||||||
|
fn add_output(&self, output: WlOutput) {
|
||||||
|
let mut outputs = self.outputs.lock().unwrap();
|
||||||
|
let position = outputs.iter().position(|handle| handle.proxy == output);
|
||||||
|
if position.is_none() {
|
||||||
|
outputs.push_back(MonitorHandle::new(output));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle removal of the output.
|
||||||
|
fn remove_output(&self, output: WlOutput) {
|
||||||
|
let mut outputs = self.outputs.lock().unwrap();
|
||||||
|
let position = outputs.iter().position(|handle| handle.proxy == output);
|
||||||
|
if let Some(position) = position {
|
||||||
|
outputs.remove(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all observed outputs.
|
||||||
|
pub fn available_outputs(&self) -> VecDeque<MonitorHandle> {
|
||||||
|
self.outputs.lock().unwrap().clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MonitorHandle {
|
||||||
|
pub(crate) proxy: WlOutput,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 MonitorHandle {
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn new(proxy: WlOutput) -> Self {
|
||||||
|
Self { proxy }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn name(&self) -> Option<String> {
|
||||||
|
sctk::output::with_output_info(&self.proxy, |info| {
|
||||||
|
format!("{} ({})", info.model, info.make)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn native_identifier(&self) -> u32 {
|
||||||
|
sctk::output::with_output_info(&self.proxy, |info| info.id).unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn size(&self) -> PhysicalSize<u32> {
|
||||||
|
match sctk::output::with_output_info(&self.proxy, |info| {
|
||||||
|
info.modes
|
||||||
|
.iter()
|
||||||
|
.find(|mode| mode.is_current)
|
||||||
|
.map(|mode| mode.dimensions)
|
||||||
|
}) {
|
||||||
|
Some(Some((w, h))) => (w as u32, h as u32),
|
||||||
|
_ => (0, 0),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn position(&self) -> PhysicalPosition<i32> {
|
||||||
|
sctk::output::with_output_info(&self.proxy, |info| info.location)
|
||||||
|
.unwrap_or((0, 0))
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn scale_factor(&self) -> i32 {
|
||||||
|
sctk::output::with_output_info(&self.proxy, |info| info.scale_factor).unwrap_or(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn video_modes(&self) -> impl Iterator<Item = RootVideoMode> {
|
||||||
|
let modes = sctk::output::with_output_info(&self.proxy, |info| info.modes.clone())
|
||||||
|
.unwrap_or_else(Vec::new);
|
||||||
|
|
||||||
|
let monitor = self.clone();
|
||||||
|
|
||||||
|
modes.into_iter().map(move |mode| RootVideoMode {
|
||||||
|
video_mode: PlatformVideoMode::Wayland(VideoMode {
|
||||||
|
size: (mode.dimensions.0 as u32, mode.dimensions.1 as u32).into(),
|
||||||
|
refresh_rate: (mode.refresh_rate as f32 / 1000.0).round() as u16,
|
||||||
|
bit_depth: 32,
|
||||||
|
monitor: monitor.clone(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct VideoMode {
|
||||||
|
pub(crate) size: PhysicalSize<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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn bit_depth(&self) -> u16 {
|
||||||
|
self.bit_depth
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn refresh_rate(&self) -> u16 {
|
||||||
|
self.refresh_rate
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn monitor(&self) -> RootMonitorHandle {
|
||||||
|
RootMonitorHandle {
|
||||||
|
inner: PlatformMonitorHandle::Wayland(self.monitor.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> EventLoopWindowTarget<T> {
|
||||||
|
#[inline]
|
||||||
|
pub fn display(&self) -> &Display {
|
||||||
|
&self.display
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||||
|
self.output_manager.handle.available_outputs()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn primary_monitor(&self) -> Option<RootMonitorHandle> {
|
||||||
|
// There's no primary monitor on Wayland.
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,290 +0,0 @@
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use crate::dpi::LogicalPosition;
|
|
||||||
use crate::event::{
|
|
||||||
DeviceEvent, ElementState, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase,
|
|
||||||
WindowEvent,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
event_loop::{CursorManager, EventsSink},
|
|
||||||
make_wid,
|
|
||||||
window::WindowStore,
|
|
||||||
DeviceId,
|
|
||||||
};
|
|
||||||
|
|
||||||
use smithay_client_toolkit::surface;
|
|
||||||
|
|
||||||
use smithay_client_toolkit::reexports::client::protocol::{
|
|
||||||
wl_pointer::{self, Event as PtrEvent, WlPointer},
|
|
||||||
wl_seat,
|
|
||||||
};
|
|
||||||
|
|
||||||
use smithay_client_toolkit::reexports::protocols::unstable::relative_pointer::v1::client::{
|
|
||||||
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1, zwp_relative_pointer_v1::Event,
|
|
||||||
zwp_relative_pointer_v1::ZwpRelativePointerV1,
|
|
||||||
};
|
|
||||||
|
|
||||||
use smithay_client_toolkit::reexports::protocols::unstable::pointer_constraints::v1::client::{
|
|
||||||
zwp_locked_pointer_v1::ZwpLockedPointerV1, zwp_pointer_constraints_v1::Lifetime,
|
|
||||||
zwp_pointer_constraints_v1::ZwpPointerConstraintsV1,
|
|
||||||
};
|
|
||||||
|
|
||||||
use smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface;
|
|
||||||
|
|
||||||
pub fn implement_pointer(
|
|
||||||
seat: &wl_seat::WlSeat,
|
|
||||||
sink: EventsSink,
|
|
||||||
store: Arc<Mutex<WindowStore>>,
|
|
||||||
modifiers_tracker: Arc<Mutex<ModifiersState>>,
|
|
||||||
cursor_manager: Arc<Mutex<CursorManager>>,
|
|
||||||
) -> WlPointer {
|
|
||||||
seat.get_pointer(|pointer| {
|
|
||||||
// Currently focused winit surface
|
|
||||||
let mut mouse_focus = None;
|
|
||||||
let mut axis_buffer = None;
|
|
||||||
let mut axis_discrete_buffer = None;
|
|
||||||
let mut axis_state = TouchPhase::Ended;
|
|
||||||
|
|
||||||
pointer.implement_closure(
|
|
||||||
move |evt, pointer| {
|
|
||||||
let store = store.lock().unwrap();
|
|
||||||
let mut cursor_manager = cursor_manager.lock().unwrap();
|
|
||||||
match evt {
|
|
||||||
PtrEvent::Enter {
|
|
||||||
surface,
|
|
||||||
surface_x,
|
|
||||||
surface_y,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let wid = store.find_wid(&surface);
|
|
||||||
|
|
||||||
if let Some(wid) = wid {
|
|
||||||
let scale_factor = surface::get_dpi_factor(&surface) as f64;
|
|
||||||
mouse_focus = Some(surface);
|
|
||||||
|
|
||||||
// Reload cursor style only when we enter winit's surface. Calling
|
|
||||||
// this function every time on `PtrEvent::Enter` could interfere with
|
|
||||||
// SCTK CSD handling, since it changes cursor icons when you hover
|
|
||||||
// cursor over the window borders.
|
|
||||||
cursor_manager.reload_cursor_style();
|
|
||||||
|
|
||||||
sink.send_window_event(
|
|
||||||
WindowEvent::CursorEntered {
|
|
||||||
device_id: crate::event::DeviceId(
|
|
||||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
wid,
|
|
||||||
);
|
|
||||||
|
|
||||||
let position = LogicalPosition::new(surface_x, surface_y)
|
|
||||||
.to_physical(scale_factor);
|
|
||||||
|
|
||||||
sink.send_window_event(
|
|
||||||
WindowEvent::CursorMoved {
|
|
||||||
device_id: crate::event::DeviceId(
|
|
||||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
|
||||||
),
|
|
||||||
position,
|
|
||||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
|
||||||
},
|
|
||||||
wid,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PtrEvent::Leave { surface, .. } => {
|
|
||||||
mouse_focus = None;
|
|
||||||
let wid = store.find_wid(&surface);
|
|
||||||
if let Some(wid) = wid {
|
|
||||||
sink.send_window_event(
|
|
||||||
WindowEvent::CursorLeft {
|
|
||||||
device_id: crate::event::DeviceId(
|
|
||||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
wid,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PtrEvent::Motion {
|
|
||||||
surface_x,
|
|
||||||
surface_y,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
if let Some(surface) = mouse_focus.as_ref() {
|
|
||||||
let wid = make_wid(surface);
|
|
||||||
|
|
||||||
let scale_factor = surface::get_dpi_factor(&surface) as f64;
|
|
||||||
let position = LogicalPosition::new(surface_x, surface_y)
|
|
||||||
.to_physical(scale_factor);
|
|
||||||
|
|
||||||
sink.send_window_event(
|
|
||||||
WindowEvent::CursorMoved {
|
|
||||||
device_id: crate::event::DeviceId(
|
|
||||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
|
||||||
),
|
|
||||||
position,
|
|
||||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
|
||||||
},
|
|
||||||
wid,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PtrEvent::Button { button, state, .. } => {
|
|
||||||
if let Some(surface) = mouse_focus.as_ref() {
|
|
||||||
let state = match state {
|
|
||||||
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
|
|
||||||
wl_pointer::ButtonState::Released => ElementState::Released,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
let button = match button {
|
|
||||||
0x110 => MouseButton::Left,
|
|
||||||
0x111 => MouseButton::Right,
|
|
||||||
0x112 => MouseButton::Middle,
|
|
||||||
// TODO figure out the translation ?
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
sink.send_window_event(
|
|
||||||
WindowEvent::MouseInput {
|
|
||||||
device_id: crate::event::DeviceId(
|
|
||||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
|
||||||
),
|
|
||||||
state,
|
|
||||||
button,
|
|
||||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
|
||||||
},
|
|
||||||
make_wid(surface),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PtrEvent::Axis { axis, value, .. } => {
|
|
||||||
if let Some(surface) = mouse_focus.as_ref() {
|
|
||||||
let wid = make_wid(surface);
|
|
||||||
if pointer.as_ref().version() < 5 {
|
|
||||||
let (mut x, mut y) = (0.0, 0.0);
|
|
||||||
// old seat compatibility
|
|
||||||
match axis {
|
|
||||||
// wayland vertical sign convention is the inverse of winit
|
|
||||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
|
||||||
wl_pointer::Axis::HorizontalScroll => x += value as f32,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
let scale_factor = surface::get_dpi_factor(&surface) as f64;
|
|
||||||
let delta = LogicalPosition::new(x as f64, y as f64)
|
|
||||||
.to_physical(scale_factor);
|
|
||||||
sink.send_window_event(
|
|
||||||
WindowEvent::MouseWheel {
|
|
||||||
device_id: crate::event::DeviceId(
|
|
||||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
|
||||||
),
|
|
||||||
delta: MouseScrollDelta::PixelDelta(delta),
|
|
||||||
phase: TouchPhase::Moved,
|
|
||||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
|
||||||
},
|
|
||||||
wid,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let (mut x, mut y) = axis_buffer.unwrap_or((0.0, 0.0));
|
|
||||||
match axis {
|
|
||||||
// wayland vertical sign convention is the inverse of winit
|
|
||||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
|
||||||
wl_pointer::Axis::HorizontalScroll => x += value as f32,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
axis_buffer = Some((x, y));
|
|
||||||
axis_state = match axis_state {
|
|
||||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
|
||||||
_ => TouchPhase::Started,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PtrEvent::Frame => {
|
|
||||||
let axis_buffer = axis_buffer.take();
|
|
||||||
let axis_discrete_buffer = axis_discrete_buffer.take();
|
|
||||||
if let Some(surface) = mouse_focus.as_ref() {
|
|
||||||
let wid = make_wid(surface);
|
|
||||||
if let Some((x, y)) = axis_discrete_buffer {
|
|
||||||
sink.send_window_event(
|
|
||||||
WindowEvent::MouseWheel {
|
|
||||||
device_id: crate::event::DeviceId(
|
|
||||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
|
||||||
),
|
|
||||||
delta: MouseScrollDelta::LineDelta(x, y),
|
|
||||||
phase: axis_state,
|
|
||||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
|
||||||
},
|
|
||||||
wid,
|
|
||||||
);
|
|
||||||
} else if let Some((x, y)) = axis_buffer {
|
|
||||||
let scale_factor = surface::get_dpi_factor(&surface) as f64;
|
|
||||||
let delta = LogicalPosition::new(x, y).to_physical(scale_factor);
|
|
||||||
sink.send_window_event(
|
|
||||||
WindowEvent::MouseWheel {
|
|
||||||
device_id: crate::event::DeviceId(
|
|
||||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
|
||||||
),
|
|
||||||
delta: MouseScrollDelta::PixelDelta(delta),
|
|
||||||
phase: axis_state,
|
|
||||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
|
||||||
},
|
|
||||||
wid,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PtrEvent::AxisSource { .. } => (),
|
|
||||||
PtrEvent::AxisStop { .. } => {
|
|
||||||
axis_state = TouchPhase::Ended;
|
|
||||||
}
|
|
||||||
PtrEvent::AxisDiscrete { axis, discrete } => {
|
|
||||||
let (mut x, mut y) = axis_discrete_buffer.unwrap_or((0.0, 0.0));
|
|
||||||
match axis {
|
|
||||||
// wayland vertical sign convention is the inverse of winit
|
|
||||||
wl_pointer::Axis::VerticalScroll => y -= discrete as f32,
|
|
||||||
wl_pointer::Axis::HorizontalScroll => x += discrete as f32,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
axis_discrete_buffer = Some((x, y));
|
|
||||||
axis_state = match axis_state {
|
|
||||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
|
||||||
_ => TouchPhase::Started,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn implement_relative_pointer(
|
|
||||||
sink: EventsSink,
|
|
||||||
pointer: &WlPointer,
|
|
||||||
manager: &ZwpRelativePointerManagerV1,
|
|
||||||
) -> Result<ZwpRelativePointerV1, ()> {
|
|
||||||
manager.get_relative_pointer(pointer, |rel_pointer| {
|
|
||||||
rel_pointer.implement_closure(
|
|
||||||
move |evt, _rel_pointer| match evt {
|
|
||||||
Event::RelativeMotion { dx, dy, .. } => {
|
|
||||||
sink.send_device_event(DeviceEvent::MouseMotion { delta: (dx, dy) }, DeviceId)
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn implement_locked_pointer(
|
|
||||||
surface: &WlSurface,
|
|
||||||
pointer: &WlPointer,
|
|
||||||
constraints: &ZwpPointerConstraintsV1,
|
|
||||||
) -> Result<ZwpLockedPointerV1, ()> {
|
|
||||||
constraints.lock_pointer(surface, pointer, None, Lifetime::Persistent.to_raw(), |c| {
|
|
||||||
c.implement_closure(|_, _| (), ())
|
|
||||||
})
|
|
||||||
}
|
|
151
src/platform_impl/linux/wayland/seat/keyboard/handlers.rs
Normal file
151
src/platform_impl/linux/wayland/seat/keyboard/handlers.rs
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
//! Handling of various keyboard events.
|
||||||
|
|
||||||
|
use sctk::reexports::client::protocol::wl_keyboard::KeyState;
|
||||||
|
|
||||||
|
use sctk::seat::keyboard::Event as KeyboardEvent;
|
||||||
|
|
||||||
|
use crate::event::{ElementState, KeyboardInput, ModifiersState, WindowEvent};
|
||||||
|
use crate::platform_impl::wayland::event_loop::WinitState;
|
||||||
|
use crate::platform_impl::wayland::{self, DeviceId};
|
||||||
|
|
||||||
|
use super::keymap;
|
||||||
|
use super::KeyboardInner;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn handle_keyboard(
|
||||||
|
event: KeyboardEvent<'_>,
|
||||||
|
inner: &mut KeyboardInner,
|
||||||
|
winit_state: &mut WinitState,
|
||||||
|
) {
|
||||||
|
let event_sink = &mut winit_state.event_sink;
|
||||||
|
match event {
|
||||||
|
KeyboardEvent::Enter { surface, .. } => {
|
||||||
|
let window_id = wayland::make_wid(&surface);
|
||||||
|
|
||||||
|
// Window gained focus.
|
||||||
|
event_sink.push_window_event(WindowEvent::Focused(true), window_id);
|
||||||
|
|
||||||
|
// Dispatch modifers changes that we've received before getting `Enter` event.
|
||||||
|
if let Some(modifiers) = inner.pending_modifers_state.take() {
|
||||||
|
*inner.modifiers_state.borrow_mut() = modifiers;
|
||||||
|
event_sink.push_window_event(WindowEvent::ModifiersChanged(modifiers), window_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
inner.target_window_id = Some(window_id);
|
||||||
|
}
|
||||||
|
KeyboardEvent::Leave { surface, .. } => {
|
||||||
|
let window_id = wayland::make_wid(&surface);
|
||||||
|
|
||||||
|
// Notify that no modifiers are being pressed.
|
||||||
|
if !inner.modifiers_state.borrow().is_empty() {
|
||||||
|
event_sink.push_window_event(
|
||||||
|
WindowEvent::ModifiersChanged(ModifiersState::empty()),
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Window lost focus.
|
||||||
|
event_sink.push_window_event(WindowEvent::Focused(false), window_id);
|
||||||
|
|
||||||
|
// Reset the id.
|
||||||
|
inner.target_window_id = None;
|
||||||
|
}
|
||||||
|
KeyboardEvent::Key {
|
||||||
|
rawkey,
|
||||||
|
keysym,
|
||||||
|
state,
|
||||||
|
utf8,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let window_id = match inner.target_window_id {
|
||||||
|
Some(window_id) => window_id,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let state = match state {
|
||||||
|
KeyState::Pressed => ElementState::Pressed,
|
||||||
|
KeyState::Released => ElementState::Released,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let virtual_keycode = keymap::keysym_to_vkey(keysym);
|
||||||
|
|
||||||
|
event_sink.push_window_event(
|
||||||
|
#[allow(deprecated)]
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||||
|
DeviceId,
|
||||||
|
)),
|
||||||
|
input: KeyboardInput {
|
||||||
|
state,
|
||||||
|
scancode: rawkey,
|
||||||
|
virtual_keycode,
|
||||||
|
modifiers: *inner.modifiers_state.borrow(),
|
||||||
|
},
|
||||||
|
is_synthetic: false,
|
||||||
|
},
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Send ReceivedCharacter event only on ElementState::Pressed.
|
||||||
|
if ElementState::Released == state {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(txt) = utf8 {
|
||||||
|
for ch in txt.chars() {
|
||||||
|
event_sink.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyboardEvent::Repeat {
|
||||||
|
rawkey,
|
||||||
|
keysym,
|
||||||
|
utf8,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let window_id = match inner.target_window_id {
|
||||||
|
Some(window_id) => window_id,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let virtual_keycode = keymap::keysym_to_vkey(keysym);
|
||||||
|
|
||||||
|
event_sink.push_window_event(
|
||||||
|
#[allow(deprecated)]
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||||
|
DeviceId,
|
||||||
|
)),
|
||||||
|
input: KeyboardInput {
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
scancode: rawkey,
|
||||||
|
virtual_keycode,
|
||||||
|
modifiers: *inner.modifiers_state.borrow(),
|
||||||
|
},
|
||||||
|
is_synthetic: false,
|
||||||
|
},
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(txt) = utf8 {
|
||||||
|
for ch in txt.chars() {
|
||||||
|
event_sink.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyboardEvent::Modifiers { modifiers } => {
|
||||||
|
let modifiers = ModifiersState::from(modifiers);
|
||||||
|
if let Some(window_id) = inner.target_window_id {
|
||||||
|
*inner.modifiers_state.borrow_mut() = modifiers;
|
||||||
|
|
||||||
|
event_sink.push_window_event(WindowEvent::ModifiersChanged(modifiers), window_id);
|
||||||
|
} else {
|
||||||
|
// Compositor must send modifiers after wl_keyboard::enter, however certain
|
||||||
|
// compositors are still sending it before, so stash such events and send
|
||||||
|
// them on wl_keyboard::enter.
|
||||||
|
inner.pending_modifers_state = Some(modifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
188
src/platform_impl/linux/wayland/seat/keyboard/keymap.rs
Normal file
188
src/platform_impl/linux/wayland/seat/keyboard/keymap.rs
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
//! Convert Wayland keys to winit keys.
|
||||||
|
|
||||||
|
use crate::event::VirtualKeyCode;
|
||||||
|
|
||||||
|
pub fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
||||||
|
use sctk::seat::keyboard::keysyms;
|
||||||
|
match keysym {
|
||||||
|
// Numbers.
|
||||||
|
keysyms::XKB_KEY_1 => Some(VirtualKeyCode::Key1),
|
||||||
|
keysyms::XKB_KEY_2 => Some(VirtualKeyCode::Key2),
|
||||||
|
keysyms::XKB_KEY_3 => Some(VirtualKeyCode::Key3),
|
||||||
|
keysyms::XKB_KEY_4 => Some(VirtualKeyCode::Key4),
|
||||||
|
keysyms::XKB_KEY_5 => Some(VirtualKeyCode::Key5),
|
||||||
|
keysyms::XKB_KEY_6 => Some(VirtualKeyCode::Key6),
|
||||||
|
keysyms::XKB_KEY_7 => Some(VirtualKeyCode::Key7),
|
||||||
|
keysyms::XKB_KEY_8 => Some(VirtualKeyCode::Key8),
|
||||||
|
keysyms::XKB_KEY_9 => Some(VirtualKeyCode::Key9),
|
||||||
|
keysyms::XKB_KEY_0 => Some(VirtualKeyCode::Key0),
|
||||||
|
// Letters.
|
||||||
|
keysyms::XKB_KEY_A | keysyms::XKB_KEY_a => Some(VirtualKeyCode::A),
|
||||||
|
keysyms::XKB_KEY_B | keysyms::XKB_KEY_b => Some(VirtualKeyCode::B),
|
||||||
|
keysyms::XKB_KEY_C | keysyms::XKB_KEY_c => Some(VirtualKeyCode::C),
|
||||||
|
keysyms::XKB_KEY_D | keysyms::XKB_KEY_d => Some(VirtualKeyCode::D),
|
||||||
|
keysyms::XKB_KEY_E | keysyms::XKB_KEY_e => Some(VirtualKeyCode::E),
|
||||||
|
keysyms::XKB_KEY_F | keysyms::XKB_KEY_f => Some(VirtualKeyCode::F),
|
||||||
|
keysyms::XKB_KEY_G | keysyms::XKB_KEY_g => Some(VirtualKeyCode::G),
|
||||||
|
keysyms::XKB_KEY_H | keysyms::XKB_KEY_h => Some(VirtualKeyCode::H),
|
||||||
|
keysyms::XKB_KEY_I | keysyms::XKB_KEY_i => Some(VirtualKeyCode::I),
|
||||||
|
keysyms::XKB_KEY_J | keysyms::XKB_KEY_j => Some(VirtualKeyCode::J),
|
||||||
|
keysyms::XKB_KEY_K | keysyms::XKB_KEY_k => Some(VirtualKeyCode::K),
|
||||||
|
keysyms::XKB_KEY_L | keysyms::XKB_KEY_l => Some(VirtualKeyCode::L),
|
||||||
|
keysyms::XKB_KEY_M | keysyms::XKB_KEY_m => Some(VirtualKeyCode::M),
|
||||||
|
keysyms::XKB_KEY_N | keysyms::XKB_KEY_n => Some(VirtualKeyCode::N),
|
||||||
|
keysyms::XKB_KEY_O | keysyms::XKB_KEY_o => Some(VirtualKeyCode::O),
|
||||||
|
keysyms::XKB_KEY_P | keysyms::XKB_KEY_p => Some(VirtualKeyCode::P),
|
||||||
|
keysyms::XKB_KEY_Q | keysyms::XKB_KEY_q => Some(VirtualKeyCode::Q),
|
||||||
|
keysyms::XKB_KEY_R | keysyms::XKB_KEY_r => Some(VirtualKeyCode::R),
|
||||||
|
keysyms::XKB_KEY_S | keysyms::XKB_KEY_s => Some(VirtualKeyCode::S),
|
||||||
|
keysyms::XKB_KEY_T | keysyms::XKB_KEY_t => Some(VirtualKeyCode::T),
|
||||||
|
keysyms::XKB_KEY_U | keysyms::XKB_KEY_u => Some(VirtualKeyCode::U),
|
||||||
|
keysyms::XKB_KEY_V | keysyms::XKB_KEY_v => Some(VirtualKeyCode::V),
|
||||||
|
keysyms::XKB_KEY_W | keysyms::XKB_KEY_w => Some(VirtualKeyCode::W),
|
||||||
|
keysyms::XKB_KEY_X | keysyms::XKB_KEY_x => Some(VirtualKeyCode::X),
|
||||||
|
keysyms::XKB_KEY_Y | keysyms::XKB_KEY_y => Some(VirtualKeyCode::Y),
|
||||||
|
keysyms::XKB_KEY_Z | keysyms::XKB_KEY_z => Some(VirtualKeyCode::Z),
|
||||||
|
// Escape.
|
||||||
|
keysyms::XKB_KEY_Escape => Some(VirtualKeyCode::Escape),
|
||||||
|
// Function keys.
|
||||||
|
keysyms::XKB_KEY_F1 => Some(VirtualKeyCode::F1),
|
||||||
|
keysyms::XKB_KEY_F2 => Some(VirtualKeyCode::F2),
|
||||||
|
keysyms::XKB_KEY_F3 => Some(VirtualKeyCode::F3),
|
||||||
|
keysyms::XKB_KEY_F4 => Some(VirtualKeyCode::F4),
|
||||||
|
keysyms::XKB_KEY_F5 => Some(VirtualKeyCode::F5),
|
||||||
|
keysyms::XKB_KEY_F6 => Some(VirtualKeyCode::F6),
|
||||||
|
keysyms::XKB_KEY_F7 => Some(VirtualKeyCode::F7),
|
||||||
|
keysyms::XKB_KEY_F8 => Some(VirtualKeyCode::F8),
|
||||||
|
keysyms::XKB_KEY_F9 => Some(VirtualKeyCode::F9),
|
||||||
|
keysyms::XKB_KEY_F10 => Some(VirtualKeyCode::F10),
|
||||||
|
keysyms::XKB_KEY_F11 => Some(VirtualKeyCode::F11),
|
||||||
|
keysyms::XKB_KEY_F12 => Some(VirtualKeyCode::F12),
|
||||||
|
keysyms::XKB_KEY_F13 => Some(VirtualKeyCode::F13),
|
||||||
|
keysyms::XKB_KEY_F14 => Some(VirtualKeyCode::F14),
|
||||||
|
keysyms::XKB_KEY_F15 => Some(VirtualKeyCode::F15),
|
||||||
|
keysyms::XKB_KEY_F16 => Some(VirtualKeyCode::F16),
|
||||||
|
keysyms::XKB_KEY_F17 => Some(VirtualKeyCode::F17),
|
||||||
|
keysyms::XKB_KEY_F18 => Some(VirtualKeyCode::F18),
|
||||||
|
keysyms::XKB_KEY_F19 => Some(VirtualKeyCode::F19),
|
||||||
|
keysyms::XKB_KEY_F20 => Some(VirtualKeyCode::F20),
|
||||||
|
keysyms::XKB_KEY_F21 => Some(VirtualKeyCode::F21),
|
||||||
|
keysyms::XKB_KEY_F22 => Some(VirtualKeyCode::F22),
|
||||||
|
keysyms::XKB_KEY_F23 => Some(VirtualKeyCode::F23),
|
||||||
|
keysyms::XKB_KEY_F24 => Some(VirtualKeyCode::F24),
|
||||||
|
// Flow control.
|
||||||
|
keysyms::XKB_KEY_Print => Some(VirtualKeyCode::Snapshot),
|
||||||
|
keysyms::XKB_KEY_Scroll_Lock => Some(VirtualKeyCode::Scroll),
|
||||||
|
keysyms::XKB_KEY_Pause => Some(VirtualKeyCode::Pause),
|
||||||
|
keysyms::XKB_KEY_Insert => Some(VirtualKeyCode::Insert),
|
||||||
|
keysyms::XKB_KEY_Home => Some(VirtualKeyCode::Home),
|
||||||
|
keysyms::XKB_KEY_Delete => Some(VirtualKeyCode::Delete),
|
||||||
|
keysyms::XKB_KEY_End => Some(VirtualKeyCode::End),
|
||||||
|
keysyms::XKB_KEY_Page_Down => Some(VirtualKeyCode::PageDown),
|
||||||
|
keysyms::XKB_KEY_Page_Up => Some(VirtualKeyCode::PageUp),
|
||||||
|
// Arrows.
|
||||||
|
keysyms::XKB_KEY_Left => Some(VirtualKeyCode::Left),
|
||||||
|
keysyms::XKB_KEY_Up => Some(VirtualKeyCode::Up),
|
||||||
|
keysyms::XKB_KEY_Right => Some(VirtualKeyCode::Right),
|
||||||
|
keysyms::XKB_KEY_Down => Some(VirtualKeyCode::Down),
|
||||||
|
|
||||||
|
keysyms::XKB_KEY_BackSpace => Some(VirtualKeyCode::Back),
|
||||||
|
keysyms::XKB_KEY_Return => Some(VirtualKeyCode::Return),
|
||||||
|
keysyms::XKB_KEY_space => Some(VirtualKeyCode::Space),
|
||||||
|
|
||||||
|
keysyms::XKB_KEY_Multi_key => Some(VirtualKeyCode::Compose),
|
||||||
|
keysyms::XKB_KEY_caret => Some(VirtualKeyCode::Caret),
|
||||||
|
|
||||||
|
// Keypad.
|
||||||
|
keysyms::XKB_KEY_Num_Lock => Some(VirtualKeyCode::Numlock),
|
||||||
|
keysyms::XKB_KEY_KP_0 => Some(VirtualKeyCode::Numpad0),
|
||||||
|
keysyms::XKB_KEY_KP_1 => Some(VirtualKeyCode::Numpad1),
|
||||||
|
keysyms::XKB_KEY_KP_2 => Some(VirtualKeyCode::Numpad2),
|
||||||
|
keysyms::XKB_KEY_KP_3 => Some(VirtualKeyCode::Numpad3),
|
||||||
|
keysyms::XKB_KEY_KP_4 => Some(VirtualKeyCode::Numpad4),
|
||||||
|
keysyms::XKB_KEY_KP_5 => Some(VirtualKeyCode::Numpad5),
|
||||||
|
keysyms::XKB_KEY_KP_6 => Some(VirtualKeyCode::Numpad6),
|
||||||
|
keysyms::XKB_KEY_KP_7 => Some(VirtualKeyCode::Numpad7),
|
||||||
|
keysyms::XKB_KEY_KP_8 => Some(VirtualKeyCode::Numpad8),
|
||||||
|
keysyms::XKB_KEY_KP_9 => Some(VirtualKeyCode::Numpad9),
|
||||||
|
// Misc.
|
||||||
|
// => Some(VirtualKeyCode::AbntC1),
|
||||||
|
// => Some(VirtualKeyCode::AbntC2),
|
||||||
|
keysyms::XKB_KEY_plus => Some(VirtualKeyCode::Plus),
|
||||||
|
keysyms::XKB_KEY_apostrophe => Some(VirtualKeyCode::Apostrophe),
|
||||||
|
// => Some(VirtualKeyCode::Apps),
|
||||||
|
keysyms::XKB_KEY_at => Some(VirtualKeyCode::At),
|
||||||
|
// => Some(VirtualKeyCode::Ax),
|
||||||
|
keysyms::XKB_KEY_backslash => Some(VirtualKeyCode::Backslash),
|
||||||
|
keysyms::XKB_KEY_XF86Calculator => Some(VirtualKeyCode::Calculator),
|
||||||
|
// => Some(VirtualKeyCode::Capital),
|
||||||
|
keysyms::XKB_KEY_colon => Some(VirtualKeyCode::Colon),
|
||||||
|
keysyms::XKB_KEY_comma => Some(VirtualKeyCode::Comma),
|
||||||
|
// => Some(VirtualKeyCode::Convert),
|
||||||
|
keysyms::XKB_KEY_equal => Some(VirtualKeyCode::Equals),
|
||||||
|
keysyms::XKB_KEY_grave => Some(VirtualKeyCode::Grave),
|
||||||
|
// => Some(VirtualKeyCode::Kana),
|
||||||
|
keysyms::XKB_KEY_Kanji => Some(VirtualKeyCode::Kanji),
|
||||||
|
keysyms::XKB_KEY_Alt_L => Some(VirtualKeyCode::LAlt),
|
||||||
|
keysyms::XKB_KEY_bracketleft => Some(VirtualKeyCode::LBracket),
|
||||||
|
keysyms::XKB_KEY_Control_L => Some(VirtualKeyCode::LControl),
|
||||||
|
keysyms::XKB_KEY_Shift_L => Some(VirtualKeyCode::LShift),
|
||||||
|
keysyms::XKB_KEY_Super_L => Some(VirtualKeyCode::LWin),
|
||||||
|
keysyms::XKB_KEY_XF86Mail => Some(VirtualKeyCode::Mail),
|
||||||
|
// => Some(VirtualKeyCode::MediaSelect),
|
||||||
|
// => Some(VirtualKeyCode::MediaStop),
|
||||||
|
keysyms::XKB_KEY_minus => Some(VirtualKeyCode::Minus),
|
||||||
|
keysyms::XKB_KEY_asterisk => Some(VirtualKeyCode::Asterisk),
|
||||||
|
keysyms::XKB_KEY_XF86AudioMute => Some(VirtualKeyCode::Mute),
|
||||||
|
// => Some(VirtualKeyCode::MyComputer),
|
||||||
|
keysyms::XKB_KEY_XF86AudioNext => Some(VirtualKeyCode::NextTrack),
|
||||||
|
// => Some(VirtualKeyCode::NoConvert),
|
||||||
|
keysyms::XKB_KEY_KP_Separator => Some(VirtualKeyCode::NumpadComma),
|
||||||
|
keysyms::XKB_KEY_KP_Enter => Some(VirtualKeyCode::NumpadEnter),
|
||||||
|
keysyms::XKB_KEY_KP_Equal => Some(VirtualKeyCode::NumpadEquals),
|
||||||
|
keysyms::XKB_KEY_KP_Add => Some(VirtualKeyCode::NumpadAdd),
|
||||||
|
keysyms::XKB_KEY_KP_Subtract => Some(VirtualKeyCode::NumpadSubtract),
|
||||||
|
keysyms::XKB_KEY_KP_Multiply => Some(VirtualKeyCode::NumpadMultiply),
|
||||||
|
keysyms::XKB_KEY_KP_Divide => Some(VirtualKeyCode::NumpadDivide),
|
||||||
|
keysyms::XKB_KEY_KP_Decimal => Some(VirtualKeyCode::NumpadDecimal),
|
||||||
|
keysyms::XKB_KEY_KP_Page_Up => Some(VirtualKeyCode::PageUp),
|
||||||
|
keysyms::XKB_KEY_KP_Page_Down => Some(VirtualKeyCode::PageDown),
|
||||||
|
keysyms::XKB_KEY_KP_Home => Some(VirtualKeyCode::Home),
|
||||||
|
keysyms::XKB_KEY_KP_End => Some(VirtualKeyCode::End),
|
||||||
|
// => Some(VirtualKeyCode::OEM102),
|
||||||
|
keysyms::XKB_KEY_period => Some(VirtualKeyCode::Period),
|
||||||
|
// => Some(VirtualKeyCode::Playpause),
|
||||||
|
keysyms::XKB_KEY_XF86PowerOff => Some(VirtualKeyCode::Power),
|
||||||
|
keysyms::XKB_KEY_XF86AudioPrev => Some(VirtualKeyCode::PrevTrack),
|
||||||
|
keysyms::XKB_KEY_Alt_R => Some(VirtualKeyCode::RAlt),
|
||||||
|
keysyms::XKB_KEY_bracketright => Some(VirtualKeyCode::RBracket),
|
||||||
|
keysyms::XKB_KEY_Control_R => Some(VirtualKeyCode::RControl),
|
||||||
|
keysyms::XKB_KEY_Shift_R => Some(VirtualKeyCode::RShift),
|
||||||
|
keysyms::XKB_KEY_Super_R => Some(VirtualKeyCode::RWin),
|
||||||
|
keysyms::XKB_KEY_semicolon => Some(VirtualKeyCode::Semicolon),
|
||||||
|
keysyms::XKB_KEY_slash => Some(VirtualKeyCode::Slash),
|
||||||
|
keysyms::XKB_KEY_XF86Sleep => Some(VirtualKeyCode::Sleep),
|
||||||
|
// => Some(VirtualKeyCode::Stop),
|
||||||
|
// => Some(VirtualKeyCode::Sysrq),
|
||||||
|
keysyms::XKB_KEY_Tab => Some(VirtualKeyCode::Tab),
|
||||||
|
keysyms::XKB_KEY_ISO_Left_Tab => Some(VirtualKeyCode::Tab),
|
||||||
|
keysyms::XKB_KEY_underscore => Some(VirtualKeyCode::Underline),
|
||||||
|
// => Some(VirtualKeyCode::Unlabeled),
|
||||||
|
keysyms::XKB_KEY_XF86AudioLowerVolume => Some(VirtualKeyCode::VolumeDown),
|
||||||
|
keysyms::XKB_KEY_XF86AudioRaiseVolume => Some(VirtualKeyCode::VolumeUp),
|
||||||
|
// => Some(VirtualKeyCode::Wake),
|
||||||
|
// => Some(VirtualKeyCode::Webback),
|
||||||
|
// => Some(VirtualKeyCode::WebFavorites),
|
||||||
|
// => Some(VirtualKeyCode::WebForward),
|
||||||
|
// => Some(VirtualKeyCode::WebHome),
|
||||||
|
// => Some(VirtualKeyCode::WebRefresh),
|
||||||
|
// => Some(VirtualKeyCode::WebSearch),
|
||||||
|
// => Some(VirtualKeyCode::WebStop),
|
||||||
|
keysyms::XKB_KEY_yen => Some(VirtualKeyCode::Yen),
|
||||||
|
keysyms::XKB_KEY_XF86Copy => Some(VirtualKeyCode::Copy),
|
||||||
|
keysyms::XKB_KEY_XF86Paste => Some(VirtualKeyCode::Paste),
|
||||||
|
keysyms::XKB_KEY_XF86Cut => Some(VirtualKeyCode::Cut),
|
||||||
|
// Fallback.
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
105
src/platform_impl/linux/wayland/seat/keyboard/mod.rs
Normal file
105
src/platform_impl/linux/wayland/seat/keyboard/mod.rs
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
//! Wayland keyboard handling.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use sctk::reexports::client::protocol::wl_keyboard::WlKeyboard;
|
||||||
|
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||||
|
use sctk::reexports::client::Attached;
|
||||||
|
|
||||||
|
use sctk::reexports::calloop::{LoopHandle, Source};
|
||||||
|
|
||||||
|
use sctk::seat::keyboard::{self, RepeatSource};
|
||||||
|
|
||||||
|
use crate::event::ModifiersState;
|
||||||
|
use crate::platform_impl::wayland::event_loop::WinitState;
|
||||||
|
use crate::platform_impl::wayland::WindowId;
|
||||||
|
|
||||||
|
mod handlers;
|
||||||
|
mod keymap;
|
||||||
|
|
||||||
|
pub(crate) struct Keyboard {
|
||||||
|
pub keyboard: WlKeyboard,
|
||||||
|
|
||||||
|
/// The source for repeat keys.
|
||||||
|
pub repeat_source: Option<Source<RepeatSource>>,
|
||||||
|
|
||||||
|
/// LoopHandle to drop `RepeatSource`, when dropping the keyboard.
|
||||||
|
pub loop_handle: LoopHandle<WinitState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Keyboard {
|
||||||
|
pub fn new(
|
||||||
|
seat: &Attached<WlSeat>,
|
||||||
|
loop_handle: LoopHandle<WinitState>,
|
||||||
|
modifiers_state: Rc<RefCell<ModifiersState>>,
|
||||||
|
) -> Option<Self> {
|
||||||
|
let mut inner = KeyboardInner::new(modifiers_state);
|
||||||
|
let keyboard_data = keyboard::map_keyboard_repeat(
|
||||||
|
loop_handle.clone(),
|
||||||
|
&seat,
|
||||||
|
None,
|
||||||
|
keyboard::RepeatKind::System,
|
||||||
|
move |event, _, mut dispatch_data| {
|
||||||
|
let winit_state = dispatch_data.get::<WinitState>().unwrap();
|
||||||
|
handlers::handle_keyboard(event, &mut inner, winit_state);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let (keyboard, repeat_source) = keyboard_data.ok()?;
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
keyboard,
|
||||||
|
loop_handle,
|
||||||
|
repeat_source: Some(repeat_source),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Keyboard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.keyboard.as_ref().version() >= 3 {
|
||||||
|
self.keyboard.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(repeat_source) = self.repeat_source.take() {
|
||||||
|
self.loop_handle.remove(repeat_source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct KeyboardInner {
|
||||||
|
/// Currently focused surface.
|
||||||
|
target_window_id: Option<WindowId>,
|
||||||
|
|
||||||
|
/// A pending state of modifiers.
|
||||||
|
///
|
||||||
|
/// This state is getting set if we've got a modifiers update
|
||||||
|
/// before `Enter` event, which shouldn't happen in general, however
|
||||||
|
/// some compositors are still doing so.
|
||||||
|
pending_modifers_state: Option<ModifiersState>,
|
||||||
|
|
||||||
|
/// Current state of modifiers keys.
|
||||||
|
modifiers_state: Rc<RefCell<ModifiersState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyboardInner {
|
||||||
|
fn new(modifiers_state: Rc<RefCell<ModifiersState>>) -> Self {
|
||||||
|
Self {
|
||||||
|
target_window_id: None,
|
||||||
|
pending_modifers_state: None,
|
||||||
|
modifiers_state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<keyboard::ModifiersState> for ModifiersState {
|
||||||
|
fn from(mods: keyboard::ModifiersState) -> ModifiersState {
|
||||||
|
let mut wl_mods = ModifiersState::empty();
|
||||||
|
wl_mods.set(ModifiersState::SHIFT, mods.shift);
|
||||||
|
wl_mods.set(ModifiersState::CTRL, mods.ctrl);
|
||||||
|
wl_mods.set(ModifiersState::ALT, mods.alt);
|
||||||
|
wl_mods.set(ModifiersState::LOGO, mods.logo);
|
||||||
|
wl_mods
|
||||||
|
}
|
||||||
|
}
|
208
src/platform_impl/linux/wayland/seat/mod.rs
Normal file
208
src/platform_impl/linux/wayland/seat/mod.rs
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
//! Seat handling and managing.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1;
|
||||||
|
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1;
|
||||||
|
use sctk::reexports::protocols::unstable::text_input::v3::client::zwp_text_input_manager_v3::ZwpTextInputManagerV3;
|
||||||
|
|
||||||
|
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||||
|
use sctk::reexports::client::Attached;
|
||||||
|
|
||||||
|
use sctk::environment::Environment;
|
||||||
|
use sctk::reexports::calloop::LoopHandle;
|
||||||
|
use sctk::seat::pointer::ThemeManager;
|
||||||
|
use sctk::seat::{SeatData, SeatListener};
|
||||||
|
|
||||||
|
use super::env::WinitEnv;
|
||||||
|
use super::event_loop::WinitState;
|
||||||
|
use crate::event::ModifiersState;
|
||||||
|
|
||||||
|
mod keyboard;
|
||||||
|
pub mod pointer;
|
||||||
|
pub mod text_input;
|
||||||
|
mod touch;
|
||||||
|
|
||||||
|
use keyboard::Keyboard;
|
||||||
|
use pointer::Pointers;
|
||||||
|
use text_input::TextInput;
|
||||||
|
use touch::Touch;
|
||||||
|
|
||||||
|
pub struct SeatManager {
|
||||||
|
/// Listener for seats.
|
||||||
|
_seat_listener: SeatListener,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SeatManager {
|
||||||
|
pub fn new(
|
||||||
|
env: &Environment<WinitEnv>,
|
||||||
|
loop_handle: LoopHandle<WinitState>,
|
||||||
|
theme_manager: ThemeManager,
|
||||||
|
) -> Self {
|
||||||
|
let relative_pointer_manager = env.get_global::<ZwpRelativePointerManagerV1>();
|
||||||
|
let pointer_constraints = env.get_global::<ZwpPointerConstraintsV1>();
|
||||||
|
let text_input_manager = env.get_global::<ZwpTextInputManagerV3>();
|
||||||
|
|
||||||
|
let mut inner = SeatManagerInner::new(
|
||||||
|
theme_manager,
|
||||||
|
relative_pointer_manager,
|
||||||
|
pointer_constraints,
|
||||||
|
text_input_manager,
|
||||||
|
loop_handle,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle existing seats.
|
||||||
|
for seat in env.get_all_seats() {
|
||||||
|
let seat_data = match sctk::seat::clone_seat_data(&seat) {
|
||||||
|
Some(seat_data) => seat_data,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
inner.process_seat_update(&seat, &seat_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
let seat_listener = env.listen_for_seats(move |seat, seat_data, _| {
|
||||||
|
inner.process_seat_update(&seat, &seat_data);
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
_seat_listener: seat_listener,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inner state of the seat manager.
|
||||||
|
struct SeatManagerInner {
|
||||||
|
/// Currently observed seats.
|
||||||
|
seats: Vec<SeatInfo>,
|
||||||
|
|
||||||
|
/// Loop handle.
|
||||||
|
loop_handle: LoopHandle<WinitState>,
|
||||||
|
|
||||||
|
/// Relative pointer manager.
|
||||||
|
relative_pointer_manager: Option<Attached<ZwpRelativePointerManagerV1>>,
|
||||||
|
|
||||||
|
/// Pointer constraints.
|
||||||
|
pointer_constraints: Option<Attached<ZwpPointerConstraintsV1>>,
|
||||||
|
|
||||||
|
/// Text input manager.
|
||||||
|
text_input_manager: Option<Attached<ZwpTextInputManagerV3>>,
|
||||||
|
|
||||||
|
/// A theme manager.
|
||||||
|
theme_manager: ThemeManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SeatManagerInner {
|
||||||
|
fn new(
|
||||||
|
theme_manager: ThemeManager,
|
||||||
|
relative_pointer_manager: Option<Attached<ZwpRelativePointerManagerV1>>,
|
||||||
|
pointer_constraints: Option<Attached<ZwpPointerConstraintsV1>>,
|
||||||
|
text_input_manager: Option<Attached<ZwpTextInputManagerV3>>,
|
||||||
|
loop_handle: LoopHandle<WinitState>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
seats: Vec::new(),
|
||||||
|
loop_handle,
|
||||||
|
relative_pointer_manager,
|
||||||
|
pointer_constraints,
|
||||||
|
text_input_manager,
|
||||||
|
theme_manager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle seats update from the `SeatListener`.
|
||||||
|
pub fn process_seat_update(&mut self, seat: &Attached<WlSeat>, seat_data: &SeatData) {
|
||||||
|
let detached_seat = seat.detach();
|
||||||
|
|
||||||
|
let position = self.seats.iter().position(|si| si.seat == detached_seat);
|
||||||
|
let index = position.unwrap_or_else(|| {
|
||||||
|
self.seats.push(SeatInfo::new(detached_seat));
|
||||||
|
self.seats.len() - 1
|
||||||
|
});
|
||||||
|
|
||||||
|
let seat_info = &mut self.seats[index];
|
||||||
|
|
||||||
|
// Pointer handling.
|
||||||
|
if seat_data.has_pointer && !seat_data.defunct {
|
||||||
|
if seat_info.pointer.is_none() {
|
||||||
|
seat_info.pointer = Some(Pointers::new(
|
||||||
|
&seat,
|
||||||
|
&self.theme_manager,
|
||||||
|
&self.relative_pointer_manager,
|
||||||
|
&self.pointer_constraints,
|
||||||
|
seat_info.modifiers_state.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
seat_info.pointer = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle keyboard.
|
||||||
|
if seat_data.has_keyboard && !seat_data.defunct {
|
||||||
|
if seat_info.keyboard.is_none() {
|
||||||
|
seat_info.keyboard = Keyboard::new(
|
||||||
|
&seat,
|
||||||
|
self.loop_handle.clone(),
|
||||||
|
seat_info.modifiers_state.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
seat_info.keyboard = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle touch.
|
||||||
|
if seat_data.has_touch && !seat_data.defunct {
|
||||||
|
if seat_info.touch.is_none() {
|
||||||
|
seat_info.touch = Some(Touch::new(&seat));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
seat_info.touch = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle text input.
|
||||||
|
if let Some(text_input_manager) = self.text_input_manager.as_ref() {
|
||||||
|
if seat_data.defunct {
|
||||||
|
seat_info.text_input = None;
|
||||||
|
} else if seat_info.text_input.is_none() {
|
||||||
|
seat_info.text_input = Some(TextInput::new(&seat, &text_input_manager));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resources associtated with a given seat.
|
||||||
|
struct SeatInfo {
|
||||||
|
/// Seat to which this `SeatInfo` belongs.
|
||||||
|
seat: WlSeat,
|
||||||
|
|
||||||
|
/// A keyboard handle with its repeat rate handling.
|
||||||
|
keyboard: Option<Keyboard>,
|
||||||
|
|
||||||
|
/// All pointers we're using on a seat.
|
||||||
|
pointer: Option<Pointers>,
|
||||||
|
|
||||||
|
/// Touch handling.
|
||||||
|
touch: Option<Touch>,
|
||||||
|
|
||||||
|
/// Text input handling aka IME.
|
||||||
|
text_input: Option<TextInput>,
|
||||||
|
|
||||||
|
/// The current state of modifiers observed in keyboard handler.
|
||||||
|
///
|
||||||
|
/// We keep modifiers state on a seat, since it's being used by pointer events as well.
|
||||||
|
modifiers_state: Rc<RefCell<ModifiersState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SeatInfo {
|
||||||
|
pub fn new(seat: WlSeat) -> Self {
|
||||||
|
Self {
|
||||||
|
seat,
|
||||||
|
keyboard: None,
|
||||||
|
pointer: None,
|
||||||
|
touch: None,
|
||||||
|
text_input: None,
|
||||||
|
modifiers_state: Rc::new(RefCell::new(ModifiersState::default())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
src/platform_impl/linux/wayland/seat/pointer/data.rs
Normal file
74
src/platform_impl/linux/wayland/seat/pointer/data.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
//! Data which is used in pointer callbacks.
|
||||||
|
|
||||||
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||||
|
use sctk::reexports::client::Attached;
|
||||||
|
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::{ZwpPointerConstraintsV1};
|
||||||
|
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1;
|
||||||
|
|
||||||
|
use crate::event::{ModifiersState, TouchPhase};
|
||||||
|
|
||||||
|
/// A data being used by pointer handlers.
|
||||||
|
pub(super) struct PointerData {
|
||||||
|
/// Winit's surface the pointer is currently over.
|
||||||
|
pub surface: Option<WlSurface>,
|
||||||
|
|
||||||
|
/// Current modifiers state.
|
||||||
|
///
|
||||||
|
/// This refers a state of modifiers from `WlKeyboard` on
|
||||||
|
/// the given seat.
|
||||||
|
pub modifiers_state: Rc<RefCell<ModifiersState>>,
|
||||||
|
|
||||||
|
/// Pointer constraints.
|
||||||
|
pub pointer_constraints: Option<Attached<ZwpPointerConstraintsV1>>,
|
||||||
|
|
||||||
|
pub confined_pointer: Rc<RefCell<Option<ZwpConfinedPointerV1>>>,
|
||||||
|
|
||||||
|
/// A latest event serial.
|
||||||
|
pub latest_serial: Rc<Cell<u32>>,
|
||||||
|
|
||||||
|
/// The currently accumulated axis data on a pointer.
|
||||||
|
pub axis_data: AxisData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointerData {
|
||||||
|
pub fn new(
|
||||||
|
confined_pointer: Rc<RefCell<Option<ZwpConfinedPointerV1>>>,
|
||||||
|
pointer_constraints: Option<Attached<ZwpPointerConstraintsV1>>,
|
||||||
|
modifiers_state: Rc<RefCell<ModifiersState>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
surface: None,
|
||||||
|
latest_serial: Rc::new(Cell::new(0)),
|
||||||
|
confined_pointer,
|
||||||
|
modifiers_state,
|
||||||
|
pointer_constraints,
|
||||||
|
axis_data: AxisData::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Axis data.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub(super) struct AxisData {
|
||||||
|
/// Current state of the axis.
|
||||||
|
pub axis_state: TouchPhase,
|
||||||
|
|
||||||
|
/// A buffer for `PixelDelta` event.
|
||||||
|
pub axis_buffer: Option<(f32, f32)>,
|
||||||
|
|
||||||
|
/// A buffer for `LineDelta` event.
|
||||||
|
pub axis_discrete_buffer: Option<(f32, f32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AxisData {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
axis_state: TouchPhase::Ended,
|
||||||
|
axis_buffer: None,
|
||||||
|
axis_discrete_buffer: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
297
src/platform_impl/linux/wayland/seat/pointer/handlers.rs
Normal file
297
src/platform_impl/linux/wayland/seat/pointer/handlers.rs
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
//! Handlers for the pointers we're using.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use sctk::reexports::client::protocol::wl_pointer::{self, Event as PointerEvent};
|
||||||
|
use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_relative_pointer_v1::Event as RelativePointerEvent;
|
||||||
|
|
||||||
|
use sctk::seat::pointer::ThemedPointer;
|
||||||
|
|
||||||
|
use crate::dpi::LogicalPosition;
|
||||||
|
use crate::event::{
|
||||||
|
DeviceEvent, ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent,
|
||||||
|
};
|
||||||
|
use crate::platform_impl::wayland::event_loop::WinitState;
|
||||||
|
use crate::platform_impl::wayland::{self, DeviceId};
|
||||||
|
|
||||||
|
use super::{PointerData, WinitPointer};
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn handle_pointer(
|
||||||
|
pointer: ThemedPointer,
|
||||||
|
event: PointerEvent,
|
||||||
|
pointer_data: &Rc<RefCell<PointerData>>,
|
||||||
|
winit_state: &mut WinitState,
|
||||||
|
) {
|
||||||
|
let event_sink = &mut winit_state.event_sink;
|
||||||
|
let mut pointer_data = pointer_data.borrow_mut();
|
||||||
|
match event {
|
||||||
|
PointerEvent::Enter {
|
||||||
|
surface,
|
||||||
|
surface_x,
|
||||||
|
surface_y,
|
||||||
|
serial,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
pointer_data.latest_serial.replace(serial);
|
||||||
|
|
||||||
|
let window_id = wayland::make_wid(&surface);
|
||||||
|
if !winit_state.window_map.contains_key(&window_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let window_handle = match winit_state.window_map.get_mut(&window_id) {
|
||||||
|
Some(window_handle) => window_handle,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let scale_factor = sctk::get_surface_scale_factor(&surface) as f64;
|
||||||
|
pointer_data.surface = Some(surface);
|
||||||
|
|
||||||
|
// Notify window that pointer entered the surface.
|
||||||
|
let winit_pointer = WinitPointer {
|
||||||
|
pointer,
|
||||||
|
confined_pointer: Rc::downgrade(&pointer_data.confined_pointer),
|
||||||
|
pointer_constraints: pointer_data.pointer_constraints.clone(),
|
||||||
|
latest_serial: pointer_data.latest_serial.clone(),
|
||||||
|
};
|
||||||
|
window_handle.pointer_entered(winit_pointer);
|
||||||
|
|
||||||
|
event_sink.push_window_event(
|
||||||
|
WindowEvent::CursorEntered {
|
||||||
|
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||||
|
DeviceId,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
let position = LogicalPosition::new(surface_x, surface_y).to_physical(scale_factor);
|
||||||
|
|
||||||
|
event_sink.push_window_event(
|
||||||
|
WindowEvent::CursorMoved {
|
||||||
|
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||||
|
DeviceId,
|
||||||
|
)),
|
||||||
|
position,
|
||||||
|
modifiers: *pointer_data.modifiers_state.borrow(),
|
||||||
|
},
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
PointerEvent::Leave { surface, serial } => {
|
||||||
|
pointer_data.surface = None;
|
||||||
|
pointer_data.latest_serial.replace(serial);
|
||||||
|
|
||||||
|
let window_id = wayland::make_wid(&surface);
|
||||||
|
|
||||||
|
let window_handle = match winit_state.window_map.get_mut(&window_id) {
|
||||||
|
Some(window_handle) => window_handle,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Notify a window that pointer is no longer observing it.
|
||||||
|
let winit_pointer = WinitPointer {
|
||||||
|
pointer,
|
||||||
|
confined_pointer: Rc::downgrade(&pointer_data.confined_pointer),
|
||||||
|
pointer_constraints: pointer_data.pointer_constraints.clone(),
|
||||||
|
latest_serial: pointer_data.latest_serial.clone(),
|
||||||
|
};
|
||||||
|
window_handle.pointer_left(winit_pointer);
|
||||||
|
|
||||||
|
event_sink.push_window_event(
|
||||||
|
WindowEvent::CursorLeft {
|
||||||
|
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||||
|
DeviceId,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
PointerEvent::Motion {
|
||||||
|
surface_x,
|
||||||
|
surface_y,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let surface = match pointer_data.surface.as_ref() {
|
||||||
|
Some(surface) => surface,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let window_id = wayland::make_wid(surface);
|
||||||
|
|
||||||
|
let scale_factor = sctk::get_surface_scale_factor(&surface) as f64;
|
||||||
|
let position = LogicalPosition::new(surface_x, surface_y).to_physical(scale_factor);
|
||||||
|
|
||||||
|
event_sink.push_window_event(
|
||||||
|
WindowEvent::CursorMoved {
|
||||||
|
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||||
|
DeviceId,
|
||||||
|
)),
|
||||||
|
position,
|
||||||
|
modifiers: *pointer_data.modifiers_state.borrow(),
|
||||||
|
},
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
PointerEvent::Button {
|
||||||
|
button,
|
||||||
|
state,
|
||||||
|
serial,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
pointer_data.latest_serial.replace(serial);
|
||||||
|
let window_id = match pointer_data.surface.as_ref().map(wayland::make_wid) {
|
||||||
|
Some(window_id) => window_id,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let state = match state {
|
||||||
|
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
|
||||||
|
wl_pointer::ButtonState::Released => ElementState::Released,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let button = match button {
|
||||||
|
0x110 => MouseButton::Left,
|
||||||
|
0x111 => MouseButton::Right,
|
||||||
|
0x112 => MouseButton::Middle,
|
||||||
|
// TODO - figure out the translation.
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
event_sink.push_window_event(
|
||||||
|
WindowEvent::MouseInput {
|
||||||
|
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||||
|
DeviceId,
|
||||||
|
)),
|
||||||
|
state,
|
||||||
|
button,
|
||||||
|
modifiers: *pointer_data.modifiers_state.borrow(),
|
||||||
|
},
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
PointerEvent::Axis { axis, value, .. } => {
|
||||||
|
let surface = match pointer_data.surface.as_ref() {
|
||||||
|
Some(surface) => surface,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let window_id = wayland::make_wid(&surface);
|
||||||
|
|
||||||
|
if pointer.as_ref().version() < 5 {
|
||||||
|
let (mut x, mut y) = (0.0, 0.0);
|
||||||
|
|
||||||
|
// Old seat compatibility.
|
||||||
|
match axis {
|
||||||
|
// Wayland vertical sign convention is the inverse of winit.
|
||||||
|
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||||
|
wl_pointer::Axis::HorizontalScroll => x += value as f32,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
let scale_factor = sctk::get_surface_scale_factor(&surface) as f64;
|
||||||
|
let delta = LogicalPosition::new(x as f64, y as f64).to_physical(scale_factor);
|
||||||
|
|
||||||
|
event_sink.push_window_event(
|
||||||
|
WindowEvent::MouseWheel {
|
||||||
|
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||||
|
DeviceId,
|
||||||
|
)),
|
||||||
|
delta: MouseScrollDelta::PixelDelta(delta),
|
||||||
|
phase: TouchPhase::Moved,
|
||||||
|
modifiers: *pointer_data.modifiers_state.borrow(),
|
||||||
|
},
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let (mut x, mut y) = pointer_data.axis_data.axis_buffer.unwrap_or((0.0, 0.0));
|
||||||
|
match axis {
|
||||||
|
// Wayland vertical sign convention is the inverse of winit.
|
||||||
|
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||||
|
wl_pointer::Axis::HorizontalScroll => x += value as f32,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer_data.axis_data.axis_buffer = Some((x, y));
|
||||||
|
|
||||||
|
pointer_data.axis_data.axis_state = match pointer_data.axis_data.axis_state {
|
||||||
|
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||||
|
_ => TouchPhase::Started,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PointerEvent::AxisDiscrete { axis, discrete } => {
|
||||||
|
let (mut x, mut y) = pointer_data
|
||||||
|
.axis_data
|
||||||
|
.axis_discrete_buffer
|
||||||
|
.unwrap_or((0., 0.));
|
||||||
|
|
||||||
|
match axis {
|
||||||
|
// Wayland vertical sign convention is the inverse of winit.
|
||||||
|
wl_pointer::Axis::VerticalScroll => y -= discrete as f32,
|
||||||
|
wl_pointer::Axis::HorizontalScroll => x += discrete as f32,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer_data.axis_data.axis_discrete_buffer = Some((x, y));
|
||||||
|
|
||||||
|
pointer_data.axis_data.axis_state = match pointer_data.axis_data.axis_state {
|
||||||
|
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||||
|
_ => TouchPhase::Started,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PointerEvent::AxisSource { .. } => (),
|
||||||
|
PointerEvent::AxisStop { .. } => {
|
||||||
|
pointer_data.axis_data.axis_state = TouchPhase::Ended;
|
||||||
|
}
|
||||||
|
PointerEvent::Frame => {
|
||||||
|
let axis_buffer = pointer_data.axis_data.axis_buffer.take();
|
||||||
|
let axis_discrete_buffer = pointer_data.axis_data.axis_discrete_buffer.take();
|
||||||
|
|
||||||
|
let surface = match pointer_data.surface.as_ref() {
|
||||||
|
Some(surface) => surface,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let window_id = wayland::make_wid(&surface);
|
||||||
|
|
||||||
|
let window_event = if let Some((x, y)) = axis_discrete_buffer {
|
||||||
|
WindowEvent::MouseWheel {
|
||||||
|
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||||
|
DeviceId,
|
||||||
|
)),
|
||||||
|
delta: MouseScrollDelta::LineDelta(x, y),
|
||||||
|
phase: pointer_data.axis_data.axis_state,
|
||||||
|
modifiers: *pointer_data.modifiers_state.borrow(),
|
||||||
|
}
|
||||||
|
} else if let Some((x, y)) = axis_buffer {
|
||||||
|
let scale_factor = sctk::get_surface_scale_factor(&surface) as f64;
|
||||||
|
let delta = LogicalPosition::new(x, y).to_physical(scale_factor);
|
||||||
|
|
||||||
|
WindowEvent::MouseWheel {
|
||||||
|
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||||
|
DeviceId,
|
||||||
|
)),
|
||||||
|
delta: MouseScrollDelta::PixelDelta(delta),
|
||||||
|
phase: pointer_data.axis_data.axis_state,
|
||||||
|
modifiers: *pointer_data.modifiers_state.borrow(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
event_sink.push_window_event(window_event, window_id);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn handle_relative_pointer(event: RelativePointerEvent, winit_state: &mut WinitState) {
|
||||||
|
if let RelativePointerEvent::RelativeMotion { dx, dy, .. } = event {
|
||||||
|
winit_state
|
||||||
|
.event_sink
|
||||||
|
.push_device_event(DeviceEvent::MouseMotion { delta: (dx, dy) }, DeviceId)
|
||||||
|
}
|
||||||
|
}
|
242
src/platform_impl/linux/wayland/seat/pointer/mod.rs
Normal file
242
src/platform_impl/linux/wayland/seat/pointer/mod.rs
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
//! All pointer related handling.
|
||||||
|
|
||||||
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::rc::{Rc, Weak};
|
||||||
|
|
||||||
|
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::Attached;
|
||||||
|
use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1;
|
||||||
|
use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_relative_pointer_v1::ZwpRelativePointerV1;
|
||||||
|
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::{ZwpPointerConstraintsV1, Lifetime};
|
||||||
|
use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1;
|
||||||
|
|
||||||
|
use sctk::seat::pointer::{ThemeManager, ThemedPointer};
|
||||||
|
|
||||||
|
use crate::event::ModifiersState;
|
||||||
|
use crate::platform_impl::wayland::event_loop::WinitState;
|
||||||
|
use crate::window::CursorIcon;
|
||||||
|
|
||||||
|
mod data;
|
||||||
|
mod handlers;
|
||||||
|
|
||||||
|
use data::PointerData;
|
||||||
|
|
||||||
|
/// A proxy to Wayland pointer, which serves requests from a `WindowHandle`.
|
||||||
|
pub struct WinitPointer {
|
||||||
|
pointer: ThemedPointer,
|
||||||
|
|
||||||
|
/// Create confined pointers.
|
||||||
|
pointer_constraints: Option<Attached<ZwpPointerConstraintsV1>>,
|
||||||
|
|
||||||
|
/// Cursor to handle confine requests.
|
||||||
|
confined_pointer: Weak<RefCell<Option<ZwpConfinedPointerV1>>>,
|
||||||
|
|
||||||
|
/// Latest observed serial in pointer events.
|
||||||
|
latest_serial: Rc<Cell<u32>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for WinitPointer {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
*self.pointer == *other.pointer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for WinitPointer {}
|
||||||
|
|
||||||
|
impl WinitPointer {
|
||||||
|
/// Set the cursor icon.
|
||||||
|
///
|
||||||
|
/// Providing `None` will hide the cursor.
|
||||||
|
pub fn set_cursor(&self, cursor_icon: Option<CursorIcon>) {
|
||||||
|
let cursor_icon = match cursor_icon {
|
||||||
|
Some(cursor_icon) => cursor_icon,
|
||||||
|
None => {
|
||||||
|
// Hide the cursor.
|
||||||
|
(*self.pointer).set_cursor(self.latest_serial.get(), None, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let cursors: &[&str] = match cursor_icon {
|
||||||
|
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 => &["openhand", "grab"],
|
||||||
|
CursorIcon::Grabbing => &["closedhand", "grabbing"],
|
||||||
|
CursorIcon::Progress => &["progress"],
|
||||||
|
CursorIcon::AllScroll => &["all-scroll"],
|
||||||
|
CursorIcon::ContextMenu => &["context-menu"],
|
||||||
|
|
||||||
|
CursorIcon::NoDrop => &["no-drop", "circle"],
|
||||||
|
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", "size_bdiag"],
|
||||||
|
CursorIcon::NeswResize => &["fd_double_arrow", "size_fdiag"],
|
||||||
|
CursorIcon::ColResize => &["split_h", "h_double_arrow"],
|
||||||
|
CursorIcon::RowResize => &["split_v", "v_double_arrow"],
|
||||||
|
CursorIcon::Text => &["text", "xterm"],
|
||||||
|
CursorIcon::VerticalText => &["vertical-text"],
|
||||||
|
|
||||||
|
CursorIcon::Wait => &["watch"],
|
||||||
|
|
||||||
|
CursorIcon::ZoomIn => &["zoom-in"],
|
||||||
|
CursorIcon::ZoomOut => &["zoom-out"],
|
||||||
|
};
|
||||||
|
|
||||||
|
let serial = Some(self.latest_serial.get());
|
||||||
|
for cursor in cursors {
|
||||||
|
if self.pointer.set_cursor(cursor, serial).is_ok() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Confine the pointer to a surface.
|
||||||
|
pub fn confine(&self, surface: &WlSurface) {
|
||||||
|
let pointer_constraints = match &self.pointer_constraints {
|
||||||
|
Some(pointer_constraints) => pointer_constraints,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let confined_pointer = match self.confined_pointer.upgrade() {
|
||||||
|
Some(confined_pointer) => confined_pointer,
|
||||||
|
// A pointer is gone.
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
*confined_pointer.borrow_mut() = Some(init_confined_pointer(
|
||||||
|
&pointer_constraints,
|
||||||
|
&surface,
|
||||||
|
&*self.pointer,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to unconfine the pointer if the current pointer is confined.
|
||||||
|
pub fn unconfine(&self) {
|
||||||
|
let confined_pointer = match self.confined_pointer.upgrade() {
|
||||||
|
Some(confined_pointer) => confined_pointer,
|
||||||
|
// A pointer is gone.
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut confined_pointer = confined_pointer.borrow_mut();
|
||||||
|
|
||||||
|
if let Some(confined_pointer) = confined_pointer.take() {
|
||||||
|
confined_pointer.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A pointer wrapper for easy releasing and managing pointers.
|
||||||
|
pub(super) struct Pointers {
|
||||||
|
/// A pointer itself.
|
||||||
|
pointer: ThemedPointer,
|
||||||
|
|
||||||
|
/// A relative pointer handler.
|
||||||
|
relative_pointer: Option<ZwpRelativePointerV1>,
|
||||||
|
|
||||||
|
/// Confined pointer.
|
||||||
|
confined_pointer: Rc<RefCell<Option<ZwpConfinedPointerV1>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pointers {
|
||||||
|
pub(super) fn new(
|
||||||
|
seat: &Attached<WlSeat>,
|
||||||
|
theme_manager: &ThemeManager,
|
||||||
|
relative_pointer_manager: &Option<Attached<ZwpRelativePointerManagerV1>>,
|
||||||
|
pointer_constraints: &Option<Attached<ZwpPointerConstraintsV1>>,
|
||||||
|
modifiers_state: Rc<RefCell<ModifiersState>>,
|
||||||
|
) -> Self {
|
||||||
|
let confined_pointer = Rc::new(RefCell::new(None));
|
||||||
|
let pointer_data = Rc::new(RefCell::new(PointerData::new(
|
||||||
|
confined_pointer.clone(),
|
||||||
|
pointer_constraints.clone(),
|
||||||
|
modifiers_state,
|
||||||
|
)));
|
||||||
|
let pointer = theme_manager.theme_pointer_with_impl(
|
||||||
|
seat,
|
||||||
|
move |event, pointer, mut dispatch_data| {
|
||||||
|
let winit_state = dispatch_data.get::<WinitState>().unwrap();
|
||||||
|
handlers::handle_pointer(pointer, event, &pointer_data, winit_state);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Setup relative_pointer if it's available.
|
||||||
|
let relative_pointer = match relative_pointer_manager.as_ref() {
|
||||||
|
Some(relative_pointer_manager) => {
|
||||||
|
Some(init_relative_pointer(&relative_pointer_manager, &*pointer))
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
pointer,
|
||||||
|
relative_pointer,
|
||||||
|
confined_pointer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Pointers {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Drop relative pointer.
|
||||||
|
if let Some(relative_pointer) = self.relative_pointer.take() {
|
||||||
|
relative_pointer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop confined pointer.
|
||||||
|
if let Some(confined_pointer) = self.confined_pointer.borrow_mut().take() {
|
||||||
|
confined_pointer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop the pointer itself in case it's possible.
|
||||||
|
if self.pointer.as_ref().version() >= 3 {
|
||||||
|
self.pointer.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn init_relative_pointer(
|
||||||
|
relative_pointer_manager: &ZwpRelativePointerManagerV1,
|
||||||
|
pointer: &WlPointer,
|
||||||
|
) -> ZwpRelativePointerV1 {
|
||||||
|
let relative_pointer = relative_pointer_manager.get_relative_pointer(&*pointer);
|
||||||
|
relative_pointer.quick_assign(move |_, event, mut dispatch_data| {
|
||||||
|
let winit_state = dispatch_data.get::<WinitState>().unwrap();
|
||||||
|
handlers::handle_relative_pointer(event, winit_state);
|
||||||
|
});
|
||||||
|
|
||||||
|
relative_pointer.detach()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn init_confined_pointer(
|
||||||
|
pointer_constraints: &Attached<ZwpPointerConstraintsV1>,
|
||||||
|
surface: &WlSurface,
|
||||||
|
pointer: &WlPointer,
|
||||||
|
) -> ZwpConfinedPointerV1 {
|
||||||
|
let confined_pointer =
|
||||||
|
pointer_constraints.confine_pointer(surface, pointer, None, Lifetime::Persistent.to_raw());
|
||||||
|
|
||||||
|
confined_pointer.quick_assign(move |_, _, _| {});
|
||||||
|
|
||||||
|
confined_pointer.detach()
|
||||||
|
}
|
78
src/platform_impl/linux/wayland/seat/text_input/handlers.rs
Normal file
78
src/platform_impl/linux/wayland/seat/text_input/handlers.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
//! Handling of IME events.
|
||||||
|
|
||||||
|
use sctk::reexports::client::Main;
|
||||||
|
use sctk::reexports::protocols::unstable::text_input::v3::client::zwp_text_input_v3::{
|
||||||
|
Event as TextInputEvent, ZwpTextInputV3,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::event::WindowEvent;
|
||||||
|
use crate::platform_impl::wayland;
|
||||||
|
use crate::platform_impl::wayland::event_loop::WinitState;
|
||||||
|
|
||||||
|
use super::{TextInputHandler, TextInputInner};
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn handle_text_input(
|
||||||
|
text_input: Main<ZwpTextInputV3>,
|
||||||
|
inner: &mut TextInputInner,
|
||||||
|
event: TextInputEvent,
|
||||||
|
winit_state: &mut WinitState,
|
||||||
|
) {
|
||||||
|
let event_sink = &mut winit_state.event_sink;
|
||||||
|
match event {
|
||||||
|
TextInputEvent::Enter { surface } => {
|
||||||
|
let window_id = wayland::make_wid(&surface);
|
||||||
|
|
||||||
|
let window_handle = match winit_state.window_map.get_mut(&window_id) {
|
||||||
|
Some(window_handle) => window_handle,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
inner.target_window_id = Some(window_id);
|
||||||
|
|
||||||
|
// Enable text input on that surface.
|
||||||
|
text_input.enable();
|
||||||
|
text_input.commit();
|
||||||
|
|
||||||
|
// Notify a window we're currently over about text input handler.
|
||||||
|
let text_input_handler = TextInputHandler {
|
||||||
|
text_input: text_input.detach(),
|
||||||
|
};
|
||||||
|
window_handle.text_input_entered(text_input_handler);
|
||||||
|
}
|
||||||
|
TextInputEvent::Leave { surface } => {
|
||||||
|
// Always issue a disable.
|
||||||
|
text_input.disable();
|
||||||
|
text_input.commit();
|
||||||
|
|
||||||
|
let window_id = wayland::make_wid(&surface);
|
||||||
|
|
||||||
|
let window_handle = match winit_state.window_map.get_mut(&window_id) {
|
||||||
|
Some(window_handle) => window_handle,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
inner.target_window_id = None;
|
||||||
|
|
||||||
|
// Remove text input handler from the window we're leaving.
|
||||||
|
let text_input_handler = TextInputHandler {
|
||||||
|
text_input: text_input.detach(),
|
||||||
|
};
|
||||||
|
window_handle.text_input_left(text_input_handler);
|
||||||
|
}
|
||||||
|
TextInputEvent::CommitString { text } => {
|
||||||
|
// Update currenly commited string.
|
||||||
|
inner.commit_string = text;
|
||||||
|
}
|
||||||
|
TextInputEvent::Done { .. } => {
|
||||||
|
let (window_id, text) = match (inner.target_window_id, inner.commit_string.take()) {
|
||||||
|
(Some(window_id), Some(text)) => (window_id, text),
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
for ch in text.chars() {
|
||||||
|
event_sink.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
66
src/platform_impl/linux/wayland/seat/text_input/mod.rs
Normal file
66
src/platform_impl/linux/wayland/seat/text_input/mod.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||||
|
use sctk::reexports::client::Attached;
|
||||||
|
use sctk::reexports::protocols::unstable::text_input::v3::client::zwp_text_input_manager_v3::ZwpTextInputManagerV3;
|
||||||
|
use sctk::reexports::protocols::unstable::text_input::v3::client::zwp_text_input_v3::ZwpTextInputV3;
|
||||||
|
|
||||||
|
use crate::platform_impl::wayland::event_loop::WinitState;
|
||||||
|
use crate::platform_impl::wayland::WindowId;
|
||||||
|
|
||||||
|
mod handlers;
|
||||||
|
|
||||||
|
/// A handler for text input that we're advertising for `WindowHandle`.
|
||||||
|
#[derive(Eq, PartialEq)]
|
||||||
|
pub struct TextInputHandler {
|
||||||
|
text_input: ZwpTextInputV3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextInputHandler {
|
||||||
|
#[inline]
|
||||||
|
pub fn set_ime_position(&self, x: i32, y: i32) {
|
||||||
|
self.text_input.set_cursor_rectangle(x, y, 0, 0);
|
||||||
|
self.text_input.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper around text input to automatically destroy the object on `Drop`.
|
||||||
|
pub struct TextInput {
|
||||||
|
text_input: Attached<ZwpTextInputV3>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextInput {
|
||||||
|
pub fn new(seat: &Attached<WlSeat>, text_input_manager: &ZwpTextInputManagerV3) -> Self {
|
||||||
|
let text_input = text_input_manager.get_text_input(seat);
|
||||||
|
let mut text_input_inner = TextInputInner::new();
|
||||||
|
text_input.quick_assign(move |text_input, event, mut dispatch_data| {
|
||||||
|
let winit_state = dispatch_data.get::<WinitState>().unwrap();
|
||||||
|
handlers::handle_text_input(text_input, &mut text_input_inner, event, winit_state);
|
||||||
|
});
|
||||||
|
|
||||||
|
let text_input: Attached<ZwpTextInputV3> = text_input.into();
|
||||||
|
|
||||||
|
Self { text_input }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TextInput {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.text_input.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TextInputInner {
|
||||||
|
/// Currently focused surface.
|
||||||
|
target_window_id: Option<WindowId>,
|
||||||
|
|
||||||
|
/// Pending string to commit.
|
||||||
|
commit_string: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextInputInner {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
target_window_id: None,
|
||||||
|
commit_string: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
122
src/platform_impl/linux/wayland/seat/touch/handlers.rs
Normal file
122
src/platform_impl/linux/wayland/seat/touch/handlers.rs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
//! Various handlers for touch events.
|
||||||
|
|
||||||
|
use sctk::reexports::client::protocol::wl_touch::Event as TouchEvent;
|
||||||
|
|
||||||
|
use crate::dpi::LogicalPosition;
|
||||||
|
use crate::event::{TouchPhase, WindowEvent};
|
||||||
|
|
||||||
|
use crate::platform_impl::wayland::event_loop::WinitState;
|
||||||
|
use crate::platform_impl::wayland::{self, DeviceId};
|
||||||
|
|
||||||
|
use super::{TouchInner, TouchPoint};
|
||||||
|
|
||||||
|
/// Handle WlTouch events.
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn handle_touch(
|
||||||
|
event: TouchEvent,
|
||||||
|
inner: &mut TouchInner,
|
||||||
|
winit_state: &mut WinitState,
|
||||||
|
) {
|
||||||
|
let event_sink = &mut winit_state.event_sink;
|
||||||
|
|
||||||
|
match event {
|
||||||
|
TouchEvent::Down {
|
||||||
|
surface, id, x, y, ..
|
||||||
|
} => {
|
||||||
|
let window_id = wayland::make_wid(&surface);
|
||||||
|
if !winit_state.window_map.contains_key(&window_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let scale_factor = sctk::get_surface_scale_factor(&surface) as f64;
|
||||||
|
let position = LogicalPosition::new(x, y);
|
||||||
|
|
||||||
|
event_sink.push_window_event(
|
||||||
|
WindowEvent::Touch(crate::event::Touch {
|
||||||
|
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||||
|
DeviceId,
|
||||||
|
)),
|
||||||
|
phase: TouchPhase::Started,
|
||||||
|
location: position.to_physical(scale_factor),
|
||||||
|
force: None, // TODO
|
||||||
|
id: id as u64,
|
||||||
|
}),
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
inner
|
||||||
|
.touch_points
|
||||||
|
.push(TouchPoint::new(surface, position, id));
|
||||||
|
}
|
||||||
|
TouchEvent::Up { id, .. } => {
|
||||||
|
let touch_point = match inner.touch_points.iter().find(|p| p.id == id) {
|
||||||
|
Some(touch_point) => touch_point,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let scale_factor = sctk::get_surface_scale_factor(&touch_point.surface) as f64;
|
||||||
|
let location = touch_point.position.to_physical(scale_factor);
|
||||||
|
let window_id = wayland::make_wid(&touch_point.surface);
|
||||||
|
|
||||||
|
event_sink.push_window_event(
|
||||||
|
WindowEvent::Touch(crate::event::Touch {
|
||||||
|
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||||
|
DeviceId,
|
||||||
|
)),
|
||||||
|
phase: TouchPhase::Ended,
|
||||||
|
location,
|
||||||
|
force: None, // TODO
|
||||||
|
id: id as u64,
|
||||||
|
}),
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
TouchEvent::Motion { id, x, y, .. } => {
|
||||||
|
let touch_point = match inner.touch_points.iter_mut().find(|p| p.id == id) {
|
||||||
|
Some(touch_point) => touch_point,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
touch_point.position = LogicalPosition::new(x, y);
|
||||||
|
|
||||||
|
let scale_factor = sctk::get_surface_scale_factor(&touch_point.surface) as f64;
|
||||||
|
let location = touch_point.position.to_physical(scale_factor);
|
||||||
|
let window_id = wayland::make_wid(&touch_point.surface);
|
||||||
|
|
||||||
|
event_sink.push_window_event(
|
||||||
|
WindowEvent::Touch(crate::event::Touch {
|
||||||
|
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||||
|
DeviceId,
|
||||||
|
)),
|
||||||
|
phase: TouchPhase::Moved,
|
||||||
|
location,
|
||||||
|
force: None, // TODO
|
||||||
|
id: id as u64,
|
||||||
|
}),
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
TouchEvent::Frame => (),
|
||||||
|
TouchEvent::Cancel => {
|
||||||
|
for touch_point in inner.touch_points.drain(..) {
|
||||||
|
let scale_factor = sctk::get_surface_scale_factor(&touch_point.surface) as f64;
|
||||||
|
let location = touch_point.position.to_physical(scale_factor);
|
||||||
|
let window_id = wayland::make_wid(&touch_point.surface);
|
||||||
|
|
||||||
|
event_sink.push_window_event(
|
||||||
|
WindowEvent::Touch(crate::event::Touch {
|
||||||
|
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||||
|
DeviceId,
|
||||||
|
)),
|
||||||
|
phase: TouchPhase::Cancelled,
|
||||||
|
location,
|
||||||
|
force: None, // TODO
|
||||||
|
id: touch_point.id as u64,
|
||||||
|
}),
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
78
src/platform_impl/linux/wayland/seat/touch/mod.rs
Normal file
78
src/platform_impl/linux/wayland/seat/touch/mod.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
//! Touch handling.
|
||||||
|
|
||||||
|
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::Attached;
|
||||||
|
|
||||||
|
use crate::dpi::LogicalPosition;
|
||||||
|
|
||||||
|
use crate::platform_impl::wayland::event_loop::WinitState;
|
||||||
|
|
||||||
|
mod handlers;
|
||||||
|
|
||||||
|
/// Wrapper around touch to handle release.
|
||||||
|
pub struct Touch {
|
||||||
|
/// Proxy to touch.
|
||||||
|
touch: WlTouch,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Touch {
|
||||||
|
pub fn new(seat: &Attached<WlSeat>) -> Self {
|
||||||
|
let touch = seat.get_touch();
|
||||||
|
let mut inner = TouchInner::new();
|
||||||
|
|
||||||
|
touch.quick_assign(move |_, event, mut dispatch_data| {
|
||||||
|
let winit_state = dispatch_data.get::<WinitState>().unwrap();
|
||||||
|
handlers::handle_touch(event, &mut inner, winit_state);
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
touch: touch.detach(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Touch {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.touch.as_ref().version() >= 3 {
|
||||||
|
self.touch.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The data used by touch handlers.
|
||||||
|
pub(super) struct TouchInner {
|
||||||
|
/// Current touch points.
|
||||||
|
touch_points: Vec<TouchPoint>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TouchInner {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
touch_points: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Location of touch press.
|
||||||
|
pub(super) struct TouchPoint {
|
||||||
|
/// A surface where the touch point is located.
|
||||||
|
surface: WlSurface,
|
||||||
|
|
||||||
|
/// Location of the touch point.
|
||||||
|
position: LogicalPosition<f64>,
|
||||||
|
|
||||||
|
/// Id.
|
||||||
|
id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TouchPoint {
|
||||||
|
pub fn new(surface: WlSurface, position: LogicalPosition<f64>, id: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
surface,
|
||||||
|
position,
|
||||||
|
id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,132 +0,0 @@
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use crate::dpi::LogicalPosition;
|
|
||||||
use crate::event::{TouchPhase, WindowEvent};
|
|
||||||
|
|
||||||
use super::{event_loop::EventsSink, make_wid, window::WindowStore, DeviceId};
|
|
||||||
|
|
||||||
use smithay_client_toolkit::surface;
|
|
||||||
|
|
||||||
use smithay_client_toolkit::reexports::client::protocol::{
|
|
||||||
wl_seat,
|
|
||||||
wl_surface::WlSurface,
|
|
||||||
wl_touch::{Event as TouchEvent, WlTouch},
|
|
||||||
};
|
|
||||||
|
|
||||||
// location is in logical coordinates.
|
|
||||||
struct TouchPoint {
|
|
||||||
surface: WlSurface,
|
|
||||||
position: LogicalPosition<f64>,
|
|
||||||
id: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn implement_touch(
|
|
||||||
seat: &wl_seat::WlSeat,
|
|
||||||
sink: EventsSink,
|
|
||||||
store: Arc<Mutex<WindowStore>>,
|
|
||||||
) -> WlTouch {
|
|
||||||
let mut pending_ids = Vec::new();
|
|
||||||
seat.get_touch(|touch| {
|
|
||||||
touch.implement_closure(
|
|
||||||
move |evt, _| {
|
|
||||||
let store = store.lock().unwrap();
|
|
||||||
match evt {
|
|
||||||
TouchEvent::Down {
|
|
||||||
surface, id, x, y, ..
|
|
||||||
} => {
|
|
||||||
let wid = store.find_wid(&surface);
|
|
||||||
if let Some(wid) = wid {
|
|
||||||
let scale_factor = surface::get_dpi_factor(&surface) as f64;
|
|
||||||
let position = LogicalPosition::new(x, y);
|
|
||||||
|
|
||||||
sink.send_window_event(
|
|
||||||
WindowEvent::Touch(crate::event::Touch {
|
|
||||||
device_id: crate::event::DeviceId(
|
|
||||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
|
||||||
),
|
|
||||||
phase: TouchPhase::Started,
|
|
||||||
location: position.to_physical(scale_factor),
|
|
||||||
force: None, // TODO
|
|
||||||
id: id as u64,
|
|
||||||
}),
|
|
||||||
wid,
|
|
||||||
);
|
|
||||||
pending_ids.push(TouchPoint {
|
|
||||||
surface,
|
|
||||||
position,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TouchEvent::Up { id, .. } => {
|
|
||||||
let idx = pending_ids.iter().position(|p| p.id == id);
|
|
||||||
if let Some(idx) = idx {
|
|
||||||
let pt = pending_ids.remove(idx);
|
|
||||||
|
|
||||||
let scale_factor = surface::get_dpi_factor(&pt.surface) as f64;
|
|
||||||
let location = pt.position.to_physical(scale_factor);
|
|
||||||
|
|
||||||
sink.send_window_event(
|
|
||||||
WindowEvent::Touch(crate::event::Touch {
|
|
||||||
device_id: crate::event::DeviceId(
|
|
||||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
|
||||||
),
|
|
||||||
phase: TouchPhase::Ended,
|
|
||||||
location,
|
|
||||||
force: None, // TODO
|
|
||||||
id: id as u64,
|
|
||||||
}),
|
|
||||||
make_wid(&pt.surface),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TouchEvent::Motion { id, x, y, .. } => {
|
|
||||||
let pt = pending_ids.iter_mut().find(|p| p.id == id);
|
|
||||||
if let Some(pt) = pt {
|
|
||||||
pt.position = LogicalPosition::new(x, y);
|
|
||||||
|
|
||||||
let scale_factor = surface::get_dpi_factor(&pt.surface) as f64;
|
|
||||||
let location = pt.position.to_physical(scale_factor);
|
|
||||||
|
|
||||||
sink.send_window_event(
|
|
||||||
WindowEvent::Touch(crate::event::Touch {
|
|
||||||
device_id: crate::event::DeviceId(
|
|
||||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
|
||||||
),
|
|
||||||
phase: TouchPhase::Moved,
|
|
||||||
location,
|
|
||||||
force: None, // TODO
|
|
||||||
id: id as u64,
|
|
||||||
}),
|
|
||||||
make_wid(&pt.surface),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TouchEvent::Frame => (),
|
|
||||||
TouchEvent::Cancel => {
|
|
||||||
for pt in pending_ids.drain(..) {
|
|
||||||
let scale_factor = surface::get_dpi_factor(&pt.surface) as f64;
|
|
||||||
let location = pt.position.to_physical(scale_factor);
|
|
||||||
|
|
||||||
sink.send_window_event(
|
|
||||||
WindowEvent::Touch(crate::event::Touch {
|
|
||||||
device_id: crate::event::DeviceId(
|
|
||||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
|
||||||
),
|
|
||||||
phase: TouchPhase::Cancelled,
|
|
||||||
location,
|
|
||||||
force: None, // TODO
|
|
||||||
id: pt.id as u64,
|
|
||||||
}),
|
|
||||||
make_wid(&pt.surface),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
|
@ -1,569 +0,0 @@
|
||||||
use raw_window_handle::unix::WaylandHandle;
|
|
||||||
use std::{
|
|
||||||
collections::VecDeque,
|
|
||||||
mem::replace,
|
|
||||||
sync::{Arc, Mutex, Weak},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
|
|
||||||
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
|
||||||
monitor::MonitorHandle as RootMonitorHandle,
|
|
||||||
platform_impl::{
|
|
||||||
platform::wayland::event_loop::available_monitors, MonitorHandle as PlatformMonitorHandle,
|
|
||||||
PlatformSpecificWindowBuilderAttributes as PlAttributes,
|
|
||||||
},
|
|
||||||
window::{CursorIcon, Fullscreen, WindowAttributes},
|
|
||||||
};
|
|
||||||
|
|
||||||
use smithay_client_toolkit::{
|
|
||||||
output::OutputMgr,
|
|
||||||
reexports::client::{
|
|
||||||
protocol::{wl_seat, wl_surface},
|
|
||||||
Display,
|
|
||||||
},
|
|
||||||
surface::{get_dpi_factor, get_outputs},
|
|
||||||
window::{ConceptFrame, Event as WEvent, State as WState, Theme, Window as SWindow},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{event_loop::CursorManager, make_wid, EventLoopWindowTarget, MonitorHandle, WindowId};
|
|
||||||
|
|
||||||
pub struct Window {
|
|
||||||
surface: wl_surface::WlSurface,
|
|
||||||
frame: Arc<Mutex<SWindow<ConceptFrame>>>,
|
|
||||||
cursor_manager: Arc<Mutex<CursorManager>>,
|
|
||||||
outputs: OutputMgr, // Access to info for all monitors
|
|
||||||
size: Arc<Mutex<(u32, u32)>>,
|
|
||||||
kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>),
|
|
||||||
display: Arc<Display>,
|
|
||||||
need_frame_refresh: Arc<Mutex<bool>>,
|
|
||||||
need_refresh: Arc<Mutex<bool>>,
|
|
||||||
fullscreen: Arc<Mutex<bool>>,
|
|
||||||
cursor_grab_changed: Arc<Mutex<Option<bool>>>, // Update grab state
|
|
||||||
decorated: Arc<Mutex<bool>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum DecorationsAction {
|
|
||||||
Hide,
|
|
||||||
Show,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Window {
|
|
||||||
pub fn new<T>(
|
|
||||||
evlp: &EventLoopWindowTarget<T>,
|
|
||||||
attributes: WindowAttributes,
|
|
||||||
pl_attribs: PlAttributes,
|
|
||||||
) -> Result<Window, RootOsError> {
|
|
||||||
// Create the surface first to get initial DPI
|
|
||||||
let window_store = evlp.store.clone();
|
|
||||||
let cursor_manager = evlp.cursor_manager.clone();
|
|
||||||
let surface = evlp.env.create_surface(move |scale_factor, surface| {
|
|
||||||
window_store
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.scale_factor_change(&surface, scale_factor);
|
|
||||||
surface.set_buffer_scale(scale_factor);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Always 1.
|
|
||||||
let scale_factor = get_dpi_factor(&surface);
|
|
||||||
|
|
||||||
let (width, height) = attributes
|
|
||||||
.inner_size
|
|
||||||
.map(|size| size.to_logical::<f64>(scale_factor as f64).into())
|
|
||||||
.unwrap_or((800, 600));
|
|
||||||
|
|
||||||
// Create the window
|
|
||||||
let size = Arc::new(Mutex::new((width, height)));
|
|
||||||
let fullscreen = Arc::new(Mutex::new(false));
|
|
||||||
|
|
||||||
let window_store = evlp.store.clone();
|
|
||||||
|
|
||||||
let decorated = Arc::new(Mutex::new(attributes.decorations));
|
|
||||||
let pending_decorations_action = Arc::new(Mutex::new(None));
|
|
||||||
|
|
||||||
let my_surface = surface.clone();
|
|
||||||
let mut frame = SWindow::<ConceptFrame>::init_from_env(
|
|
||||||
&evlp.env,
|
|
||||||
surface.clone(),
|
|
||||||
(width, height),
|
|
||||||
move |event| match event {
|
|
||||||
WEvent::Configure { new_size, states } => {
|
|
||||||
let mut store = window_store.lock().unwrap();
|
|
||||||
let is_fullscreen = states.contains(&WState::Fullscreen);
|
|
||||||
|
|
||||||
for window in &mut store.windows {
|
|
||||||
if window.surface.as_ref().equals(&my_surface.as_ref()) {
|
|
||||||
window.new_size = new_size;
|
|
||||||
*(window.need_refresh.lock().unwrap()) = true;
|
|
||||||
{
|
|
||||||
// Get whether we're in fullscreen
|
|
||||||
let mut fullscreen = window.fullscreen.lock().unwrap();
|
|
||||||
// Fullscreen state was changed, so update decorations
|
|
||||||
if *fullscreen != is_fullscreen {
|
|
||||||
let decorated = { *window.decorated.lock().unwrap() };
|
|
||||||
if decorated {
|
|
||||||
*window.pending_decorations_action.lock().unwrap() =
|
|
||||||
if is_fullscreen {
|
|
||||||
Some(DecorationsAction::Hide)
|
|
||||||
} else {
|
|
||||||
Some(DecorationsAction::Show)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*fullscreen = is_fullscreen;
|
|
||||||
}
|
|
||||||
*(window.need_frame_refresh.lock().unwrap()) = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WEvent::Refresh => {
|
|
||||||
let store = window_store.lock().unwrap();
|
|
||||||
for window in &store.windows {
|
|
||||||
if window.surface.as_ref().equals(&my_surface.as_ref()) {
|
|
||||||
*(window.need_frame_refresh.lock().unwrap()) = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WEvent::Close => {
|
|
||||||
let mut store = window_store.lock().unwrap();
|
|
||||||
for window in &mut store.windows {
|
|
||||||
if window.surface.as_ref().equals(&my_surface.as_ref()) {
|
|
||||||
window.closed = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if let Some(app_id) = pl_attribs.app_id {
|
|
||||||
frame.set_app_id(app_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
for &(_, ref seat) in evlp.seats.lock().unwrap().iter() {
|
|
||||||
frame.new_seat(seat);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for fullscreen requirements
|
|
||||||
match attributes.fullscreen {
|
|
||||||
Some(Fullscreen::Exclusive(_)) => {
|
|
||||||
panic!("Wayland doesn't support exclusive fullscreen")
|
|
||||||
}
|
|
||||||
Some(Fullscreen::Borderless(monitor)) => {
|
|
||||||
let monitor =
|
|
||||||
monitor.and_then(|RootMonitorHandle { inner: monitor }| match monitor {
|
|
||||||
PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
|
|
||||||
#[cfg(feature = "x11")]
|
|
||||||
PlatformMonitorHandle::X(_) => None,
|
|
||||||
});
|
|
||||||
|
|
||||||
frame.set_fullscreen(monitor.as_ref())
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
if attributes.maximized {
|
|
||||||
frame.set_maximized();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
frame.set_resizable(attributes.resizable);
|
|
||||||
|
|
||||||
// set decorations
|
|
||||||
frame.set_decorate(attributes.decorations);
|
|
||||||
|
|
||||||
// set title
|
|
||||||
frame.set_title(attributes.title);
|
|
||||||
|
|
||||||
// min-max dimensions
|
|
||||||
frame.set_min_size(
|
|
||||||
attributes
|
|
||||||
.min_inner_size
|
|
||||||
.map(|size| size.to_logical::<f64>(scale_factor as f64).into()),
|
|
||||||
);
|
|
||||||
frame.set_max_size(
|
|
||||||
attributes
|
|
||||||
.max_inner_size
|
|
||||||
.map(|size| size.to_logical::<f64>(scale_factor as f64).into()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let kill_switch = Arc::new(Mutex::new(false));
|
|
||||||
let need_frame_refresh = Arc::new(Mutex::new(true));
|
|
||||||
let frame = Arc::new(Mutex::new(frame));
|
|
||||||
let need_refresh = Arc::new(Mutex::new(true));
|
|
||||||
let cursor_grab_changed = Arc::new(Mutex::new(None));
|
|
||||||
|
|
||||||
evlp.store.lock().unwrap().windows.push(InternalWindow {
|
|
||||||
closed: false,
|
|
||||||
new_size: None,
|
|
||||||
size: size.clone(),
|
|
||||||
need_refresh: need_refresh.clone(),
|
|
||||||
fullscreen: fullscreen.clone(),
|
|
||||||
cursor_grab_changed: cursor_grab_changed.clone(),
|
|
||||||
need_frame_refresh: need_frame_refresh.clone(),
|
|
||||||
surface: surface.clone(),
|
|
||||||
kill_switch: kill_switch.clone(),
|
|
||||||
frame: Arc::downgrade(&frame),
|
|
||||||
current_scale_factor: scale_factor,
|
|
||||||
new_scale_factor: None,
|
|
||||||
decorated: decorated.clone(),
|
|
||||||
pending_decorations_action: pending_decorations_action.clone(),
|
|
||||||
});
|
|
||||||
evlp.evq.borrow_mut().sync_roundtrip().unwrap();
|
|
||||||
|
|
||||||
Ok(Window {
|
|
||||||
display: evlp.display.clone(),
|
|
||||||
surface,
|
|
||||||
frame,
|
|
||||||
outputs: evlp.env.outputs.clone(),
|
|
||||||
size,
|
|
||||||
kill_switch: (kill_switch, evlp.cleanup_needed.clone()),
|
|
||||||
need_frame_refresh,
|
|
||||||
need_refresh,
|
|
||||||
cursor_manager,
|
|
||||||
fullscreen,
|
|
||||||
cursor_grab_changed,
|
|
||||||
decorated,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn id(&self) -> WindowId {
|
|
||||||
make_wid(&self.surface)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_title(&self, title: &str) {
|
|
||||||
self.frame.lock().unwrap().set_title(title.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_visible(&self, _visible: bool) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
|
||||||
Err(NotSupportedError::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
|
||||||
Err(NotSupportedError::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_outer_position(&self, _pos: Position) {
|
|
||||||
// Not possible with wayland
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
|
||||||
let scale_factor = self.scale_factor() as f64;
|
|
||||||
let size = LogicalSize::<f64>::from(*self.size.lock().unwrap());
|
|
||||||
size.to_physical(scale_factor)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn request_redraw(&self) {
|
|
||||||
*self.need_refresh.lock().unwrap() = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
|
||||||
let scale_factor = self.scale_factor() as f64;
|
|
||||||
let (w, h) = self.size.lock().unwrap().clone();
|
|
||||||
// let (w, h) = super::wayland_window::add_borders(w as i32, h as i32);
|
|
||||||
let size = LogicalSize::<f64>::from((w, h));
|
|
||||||
size.to_physical(scale_factor)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
// NOTE: This will only resize the borders, the contents must be updated by the user
|
|
||||||
pub fn set_inner_size(&self, size: Size) {
|
|
||||||
let scale_factor = self.scale_factor() as f64;
|
|
||||||
let (w, h) = size.to_logical::<u32>(scale_factor).into();
|
|
||||||
self.frame.lock().unwrap().resize(w, h);
|
|
||||||
*(self.size.lock().unwrap()) = (w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
|
|
||||||
let scale_factor = self.scale_factor() as f64;
|
|
||||||
self.frame
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.set_min_size(dimensions.map(|dim| dim.to_logical::<f64>(scale_factor).into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
|
||||||
let scale_factor = self.scale_factor() as f64;
|
|
||||||
self.frame
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.set_max_size(dimensions.map(|dim| dim.to_logical::<f64>(scale_factor).into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_resizable(&self, resizable: bool) {
|
|
||||||
self.frame.lock().unwrap().set_resizable(resizable);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn scale_factor(&self) -> i32 {
|
|
||||||
get_dpi_factor(&self.surface)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_decorations(&self, decorate: bool) {
|
|
||||||
*(self.decorated.lock().unwrap()) = decorate;
|
|
||||||
self.frame.lock().unwrap().set_decorate(decorate);
|
|
||||||
*(self.need_frame_refresh.lock().unwrap()) = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_minimized(&self, minimized: bool) {
|
|
||||||
// An app cannot un-minimize itself on Wayland
|
|
||||||
if minimized {
|
|
||||||
self.frame.lock().unwrap().set_minimized();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_maximized(&self, maximized: bool) {
|
|
||||||
if maximized {
|
|
||||||
self.frame.lock().unwrap().set_maximized();
|
|
||||||
} else {
|
|
||||||
self.frame.lock().unwrap().unset_maximized();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fullscreen(&self) -> Option<Fullscreen> {
|
|
||||||
if *(self.fullscreen.lock().unwrap()) {
|
|
||||||
let current_monitor = self
|
|
||||||
.current_monitor()
|
|
||||||
.map(|current_monitor| RootMonitorHandle {
|
|
||||||
inner: PlatformMonitorHandle::Wayland(current_monitor),
|
|
||||||
});
|
|
||||||
|
|
||||||
Some(Fullscreen::Borderless(current_monitor))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
|
||||||
match fullscreen {
|
|
||||||
Some(Fullscreen::Exclusive(_)) => {
|
|
||||||
panic!("Wayland doesn't support exclusive fullscreen")
|
|
||||||
}
|
|
||||||
Some(Fullscreen::Borderless(monitor)) => {
|
|
||||||
let monitor =
|
|
||||||
monitor.and_then(|RootMonitorHandle { inner: monitor }| match monitor {
|
|
||||||
PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
|
|
||||||
#[cfg(feature = "x11")]
|
|
||||||
PlatformMonitorHandle::X(_) => None,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.frame.lock().unwrap().set_fullscreen(monitor.as_ref());
|
|
||||||
}
|
|
||||||
None => self.frame.lock().unwrap().unset_fullscreen(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_theme<T: Theme>(&self, theme: T) {
|
|
||||||
self.frame.lock().unwrap().set_theme(theme)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
|
|
||||||
let mut cursor_manager = self.cursor_manager.lock().unwrap();
|
|
||||||
cursor_manager.set_cursor_icon(cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_cursor_visible(&self, visible: bool) {
|
|
||||||
let mut cursor_manager = self.cursor_manager.lock().unwrap();
|
|
||||||
cursor_manager.set_cursor_visible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
|
||||||
*self.cursor_grab_changed.lock().unwrap() = Some(grab);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_cursor_position(&self, _pos: Position) -> Result<(), ExternalError> {
|
|
||||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn display(&self) -> &Display {
|
|
||||||
&*self.display
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn surface(&self) -> &wl_surface::WlSurface {
|
|
||||||
&self.surface
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn current_monitor(&self) -> Option<MonitorHandle> {
|
|
||||||
let output = get_outputs(&self.surface).last()?.clone();
|
|
||||||
Some(MonitorHandle {
|
|
||||||
proxy: output,
|
|
||||||
mgr: self.outputs.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
|
||||||
available_monitors(&self.outputs)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn primary_monitor(&self) -> Option<RootMonitorHandle> {
|
|
||||||
// Wayland doesn't have a notion of primary monitor.
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn raw_window_handle(&self) -> WaylandHandle {
|
|
||||||
WaylandHandle {
|
|
||||||
surface: self.surface().as_ref().c_ptr() as *mut _,
|
|
||||||
display: self.display().as_ref().c_ptr() as *mut _,
|
|
||||||
..WaylandHandle::empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Window {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
*(self.kill_switch.0.lock().unwrap()) = true;
|
|
||||||
*(self.kill_switch.1.lock().unwrap()) = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Internal store for windows
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct InternalWindow {
|
|
||||||
surface: wl_surface::WlSurface,
|
|
||||||
// TODO: CONVERT TO LogicalSize<u32>s
|
|
||||||
new_size: Option<(u32, u32)>,
|
|
||||||
size: Arc<Mutex<(u32, u32)>>,
|
|
||||||
need_refresh: Arc<Mutex<bool>>,
|
|
||||||
fullscreen: Arc<Mutex<bool>>,
|
|
||||||
need_frame_refresh: Arc<Mutex<bool>>,
|
|
||||||
cursor_grab_changed: Arc<Mutex<Option<bool>>>,
|
|
||||||
closed: bool,
|
|
||||||
kill_switch: Arc<Mutex<bool>>,
|
|
||||||
frame: Weak<Mutex<SWindow<ConceptFrame>>>,
|
|
||||||
current_scale_factor: i32,
|
|
||||||
new_scale_factor: Option<i32>,
|
|
||||||
decorated: Arc<Mutex<bool>>,
|
|
||||||
pending_decorations_action: Arc<Mutex<Option<DecorationsAction>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WindowStore {
|
|
||||||
windows: Vec<InternalWindow>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WindowStoreForEach<'a> {
|
|
||||||
pub new_size: Option<(u32, u32)>,
|
|
||||||
pub size: &'a Mutex<(u32, u32)>,
|
|
||||||
pub prev_scale_factor: i32,
|
|
||||||
pub new_scale_factor: Option<i32>,
|
|
||||||
pub closed: bool,
|
|
||||||
pub grab_cursor: Option<bool>,
|
|
||||||
pub surface: &'a wl_surface::WlSurface,
|
|
||||||
pub wid: WindowId,
|
|
||||||
pub frame: Option<Arc<Mutex<SWindow<ConceptFrame>>>>,
|
|
||||||
pub decorations_action: Option<DecorationsAction>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WindowStore {
|
|
||||||
pub fn new() -> WindowStore {
|
|
||||||
WindowStore {
|
|
||||||
windows: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_wid(&self, surface: &wl_surface::WlSurface) -> Option<WindowId> {
|
|
||||||
for window in &self.windows {
|
|
||||||
if surface.as_ref().equals(&window.surface.as_ref()) {
|
|
||||||
return Some(make_wid(surface));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cleanup(&mut self) -> Vec<WindowId> {
|
|
||||||
let mut pruned = Vec::new();
|
|
||||||
self.windows.retain(|w| {
|
|
||||||
if *w.kill_switch.lock().unwrap() {
|
|
||||||
// window is dead, cleanup
|
|
||||||
pruned.push(make_wid(&w.surface));
|
|
||||||
w.surface.destroy();
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
pruned
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_seat(&self, seat: &wl_seat::WlSeat) {
|
|
||||||
for window in &self.windows {
|
|
||||||
if let Some(w) = window.frame.upgrade() {
|
|
||||||
w.lock().unwrap().new_seat(seat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scale_factor_change(&mut self, surface: &wl_surface::WlSurface, new: i32) {
|
|
||||||
for window in &mut self.windows {
|
|
||||||
if surface.as_ref().equals(&window.surface.as_ref()) {
|
|
||||||
window.new_scale_factor = Some(new);
|
|
||||||
*(window.need_refresh.lock().unwrap()) = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn for_each<F>(&mut self, mut f: F)
|
|
||||||
where
|
|
||||||
F: FnMut(WindowStoreForEach<'_>),
|
|
||||||
{
|
|
||||||
for window in &mut self.windows {
|
|
||||||
let prev_scale_factor = window.current_scale_factor;
|
|
||||||
if let Some(scale_factor) = window.new_scale_factor {
|
|
||||||
window.current_scale_factor = scale_factor;
|
|
||||||
}
|
|
||||||
let frame = window.frame.upgrade();
|
|
||||||
let decorations_action = { window.pending_decorations_action.lock().unwrap().take() };
|
|
||||||
f(WindowStoreForEach {
|
|
||||||
new_size: window.new_size.take(),
|
|
||||||
size: &window.size,
|
|
||||||
prev_scale_factor,
|
|
||||||
new_scale_factor: window.new_scale_factor.take(),
|
|
||||||
closed: window.closed,
|
|
||||||
grab_cursor: window.cursor_grab_changed.lock().unwrap().take(),
|
|
||||||
surface: &window.surface,
|
|
||||||
wid: make_wid(&window.surface),
|
|
||||||
frame,
|
|
||||||
decorations_action,
|
|
||||||
});
|
|
||||||
// avoid re-spamming the event
|
|
||||||
window.closed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn for_each_redraw_trigger<F>(&mut self, mut f: F)
|
|
||||||
where
|
|
||||||
F: FnMut(bool, bool, WindowId, Option<Arc<Mutex<SWindow<ConceptFrame>>>>),
|
|
||||||
{
|
|
||||||
for window in &mut self.windows {
|
|
||||||
let frame = window.frame.upgrade();
|
|
||||||
f(
|
|
||||||
replace(&mut *window.need_refresh.lock().unwrap(), false),
|
|
||||||
replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
|
|
||||||
make_wid(&window.surface),
|
|
||||||
frame,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
656
src/platform_impl/linux/wayland/window/mod.rs
Normal file
656
src/platform_impl/linux/wayland/window/mod.rs
Normal file
|
@ -0,0 +1,656 @@
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||||
|
use sctk::reexports::client::Display;
|
||||||
|
|
||||||
|
use sctk::reexports::calloop;
|
||||||
|
|
||||||
|
use sctk::window::{
|
||||||
|
ARGBColor, ButtonColorSpec, ColorSpec, ConceptConfig, ConceptFrame, Decorations,
|
||||||
|
};
|
||||||
|
|
||||||
|
use raw_window_handle::unix::WaylandHandle;
|
||||||
|
|
||||||
|
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||||
|
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||||||
|
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||||
|
use crate::platform::unix::{ARGBColor as LocalARGBColor, Button, ButtonState, Element, Theme};
|
||||||
|
use crate::platform_impl::{
|
||||||
|
MonitorHandle as PlatformMonitorHandle, OsError,
|
||||||
|
PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
|
||||||
|
};
|
||||||
|
use crate::window::{CursorIcon, Fullscreen, WindowAttributes};
|
||||||
|
|
||||||
|
use super::env::WindowingFeatures;
|
||||||
|
use super::event_loop::WinitState;
|
||||||
|
use super::output::{MonitorHandle, OutputManagerHandle};
|
||||||
|
use super::{EventLoopWindowTarget, WindowId};
|
||||||
|
|
||||||
|
pub mod shim;
|
||||||
|
|
||||||
|
use shim::{WindowHandle, WindowRequest, WindowUpdate};
|
||||||
|
|
||||||
|
pub struct Window {
|
||||||
|
/// Window id.
|
||||||
|
window_id: WindowId,
|
||||||
|
|
||||||
|
/// The Wayland display.
|
||||||
|
display: Display,
|
||||||
|
|
||||||
|
/// The underlying wl_surface.
|
||||||
|
surface: WlSurface,
|
||||||
|
|
||||||
|
/// The current window size.
|
||||||
|
size: Arc<Mutex<LogicalSize<u32>>>,
|
||||||
|
|
||||||
|
/// A handle to output manager.
|
||||||
|
output_manager_handle: OutputManagerHandle,
|
||||||
|
|
||||||
|
/// Event loop proxy to wake it up.
|
||||||
|
event_loop_awakener: calloop::ping::Ping,
|
||||||
|
|
||||||
|
/// Fullscreen state.
|
||||||
|
fullscreen: Arc<AtomicBool>,
|
||||||
|
|
||||||
|
/// Available windowing features.
|
||||||
|
windowing_features: WindowingFeatures,
|
||||||
|
|
||||||
|
/// Requests that SCTK window should perform.
|
||||||
|
window_requests: Arc<Mutex<Vec<WindowRequest>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Window {
|
||||||
|
pub fn new<T>(
|
||||||
|
event_loop_window_target: &EventLoopWindowTarget<T>,
|
||||||
|
attributes: WindowAttributes,
|
||||||
|
platform_attributes: PlatformAttributes,
|
||||||
|
) -> Result<Self, RootOsError> {
|
||||||
|
let surface = event_loop_window_target
|
||||||
|
.env
|
||||||
|
.create_surface_with_scale_callback(move |scale, surface, mut dispatch_data| {
|
||||||
|
let winit_state = dispatch_data.get::<WinitState>().unwrap();
|
||||||
|
|
||||||
|
// Get the window that receiced the event.
|
||||||
|
let window_id = super::make_wid(&surface);
|
||||||
|
let mut window_update = winit_state.window_updates.get_mut(&window_id).unwrap();
|
||||||
|
|
||||||
|
// Set pending scale factor.
|
||||||
|
window_update.scale_factor = Some(scale);
|
||||||
|
window_update.redraw_requested = true;
|
||||||
|
|
||||||
|
surface.set_buffer_scale(scale);
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
let scale_factor = sctk::get_surface_scale_factor(&surface);
|
||||||
|
|
||||||
|
let window_id = super::make_wid(&surface);
|
||||||
|
let fullscreen = Arc::new(AtomicBool::new(false));
|
||||||
|
let fullscreen_clone = fullscreen.clone();
|
||||||
|
|
||||||
|
let (width, height) = attributes
|
||||||
|
.inner_size
|
||||||
|
.map(|size| size.to_logical::<f64>(scale_factor as f64).into())
|
||||||
|
.unwrap_or((800, 600));
|
||||||
|
|
||||||
|
let theme_manager = event_loop_window_target.theme_manager.clone();
|
||||||
|
let mut window = event_loop_window_target
|
||||||
|
.env
|
||||||
|
.create_window::<ConceptFrame, _>(
|
||||||
|
surface.clone(),
|
||||||
|
Some(theme_manager),
|
||||||
|
(width, height),
|
||||||
|
move |event, mut dispatch_data| {
|
||||||
|
use sctk::window::{Event, State};
|
||||||
|
|
||||||
|
let winit_state = dispatch_data.get::<WinitState>().unwrap();
|
||||||
|
let mut window_update = winit_state.window_updates.get_mut(&window_id).unwrap();
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::Refresh => {
|
||||||
|
window_update.refresh_frame = true;
|
||||||
|
}
|
||||||
|
Event::Configure { new_size, states } => {
|
||||||
|
let is_fullscreen = states.contains(&State::Fullscreen);
|
||||||
|
fullscreen_clone.store(is_fullscreen, Ordering::Relaxed);
|
||||||
|
|
||||||
|
window_update.refresh_frame = true;
|
||||||
|
window_update.redraw_requested = true;
|
||||||
|
if let Some((w, h)) = new_size {
|
||||||
|
window_update.size = Some(LogicalSize::new(w, h));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::Close => {
|
||||||
|
window_update.close_window = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(|_| os_error!(OsError::WaylandMisc("failed to create window.")))?;
|
||||||
|
|
||||||
|
// Set decorations.
|
||||||
|
if attributes.decorations {
|
||||||
|
window.set_decorate(Decorations::FollowServer);
|
||||||
|
} else {
|
||||||
|
window.set_decorate(Decorations::None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Min dimensions.
|
||||||
|
let min_size = attributes
|
||||||
|
.min_inner_size
|
||||||
|
.map(|size| size.to_logical::<f64>(scale_factor as f64).into());
|
||||||
|
window.set_min_size(min_size);
|
||||||
|
|
||||||
|
// Max dimensions.
|
||||||
|
let max_size = attributes
|
||||||
|
.min_inner_size
|
||||||
|
.map(|size| size.to_logical::<f64>(scale_factor as f64).into());
|
||||||
|
window.set_max_size(max_size);
|
||||||
|
|
||||||
|
// Set Wayland specific window attributes.
|
||||||
|
if let Some(app_id) = platform_attributes.app_id {
|
||||||
|
window.set_app_id(app_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set common window attributes.
|
||||||
|
//
|
||||||
|
// We set resizable after other attributes, since it touches min and max size under
|
||||||
|
// the hood.
|
||||||
|
window.set_resizable(attributes.resizable);
|
||||||
|
window.set_title(attributes.title);
|
||||||
|
|
||||||
|
// Set fullscreen/maximized if so was requested.
|
||||||
|
match attributes.fullscreen {
|
||||||
|
Some(Fullscreen::Exclusive(_)) => {
|
||||||
|
warn!("`Fullscreen::Exclusive` is ignored on Wayland")
|
||||||
|
}
|
||||||
|
Some(Fullscreen::Borderless(monitor)) => {
|
||||||
|
let monitor =
|
||||||
|
monitor.and_then(|RootMonitorHandle { inner: monitor }| match monitor {
|
||||||
|
PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
|
||||||
|
#[cfg(feature = "x11")]
|
||||||
|
PlatformMonitorHandle::X(_) => None,
|
||||||
|
});
|
||||||
|
|
||||||
|
window.set_fullscreen(monitor.as_ref());
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if attributes.maximized {
|
||||||
|
window.set_maximized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = Arc::new(Mutex::new(LogicalSize::new(width, height)));
|
||||||
|
|
||||||
|
// We should trigger redraw and commit the surface for the newly created window.
|
||||||
|
let mut window_update = WindowUpdate::new();
|
||||||
|
window_update.refresh_frame = true;
|
||||||
|
window_update.redraw_requested = true;
|
||||||
|
|
||||||
|
let window_id = super::make_wid(&surface);
|
||||||
|
let window_requests = Arc::new(Mutex::new(Vec::with_capacity(64)));
|
||||||
|
|
||||||
|
// Create a handle that performs all the requests on underlying sctk a window.
|
||||||
|
let window_handle = WindowHandle::new(window, size.clone(), window_requests.clone());
|
||||||
|
|
||||||
|
let mut winit_state = event_loop_window_target.state.borrow_mut();
|
||||||
|
|
||||||
|
winit_state.window_map.insert(window_id, window_handle);
|
||||||
|
|
||||||
|
winit_state
|
||||||
|
.window_updates
|
||||||
|
.insert(window_id, WindowUpdate::new());
|
||||||
|
|
||||||
|
let windowing_features = event_loop_window_target.windowing_features;
|
||||||
|
|
||||||
|
// Send all updates to the server.
|
||||||
|
let wayland_source = &event_loop_window_target.wayland_source;
|
||||||
|
let event_loop_handle = &event_loop_window_target.event_loop_handle;
|
||||||
|
|
||||||
|
// To make our window usable for drawing right away we must `ack` a `configure`
|
||||||
|
// from the server, the acking part here is done by SCTK window frame, so we just
|
||||||
|
// need to sync with server so it'll be done automatically for us.
|
||||||
|
event_loop_handle.with_source(&wayland_source, |event_queue| {
|
||||||
|
let event_queue = event_queue.queue();
|
||||||
|
let _ = event_queue.sync_roundtrip(&mut *winit_state, |_, _, _| unreachable!());
|
||||||
|
});
|
||||||
|
|
||||||
|
// We all praise GNOME for these 3 lines of pure magic. If we don't do that,
|
||||||
|
// GNOME will shrink our window a bit for the size of the decorations. I guess it
|
||||||
|
// happens because we haven't committed them with buffers to the server.
|
||||||
|
let window_handle = winit_state.window_map.get_mut(&window_id).unwrap();
|
||||||
|
window_handle.window.refresh();
|
||||||
|
|
||||||
|
let output_manager_handle = event_loop_window_target.output_manager.handle();
|
||||||
|
|
||||||
|
let window = Self {
|
||||||
|
window_id,
|
||||||
|
surface,
|
||||||
|
display: event_loop_window_target.display.clone(),
|
||||||
|
output_manager_handle,
|
||||||
|
size,
|
||||||
|
window_requests,
|
||||||
|
event_loop_awakener: event_loop_window_target.event_loop_awakener.clone(),
|
||||||
|
fullscreen,
|
||||||
|
windowing_features,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Window {
|
||||||
|
#[inline]
|
||||||
|
pub fn id(&self) -> WindowId {
|
||||||
|
self.window_id
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_title(&self, title: &str) {
|
||||||
|
let title_request = WindowRequest::Title(title.to_owned());
|
||||||
|
self.window_requests.lock().unwrap().push(title_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_visible(&self, _visible: bool) {
|
||||||
|
// Not possible on Wayland.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||||
|
Err(NotSupportedError::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||||
|
Err(NotSupportedError::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_outer_position(&self, _: Position) {
|
||||||
|
// Not possible on Wayland.
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||||
|
self.size
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.to_physical(self.scale_factor() as f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn request_redraw(&self) {
|
||||||
|
let redraw_request = WindowRequest::Redraw;
|
||||||
|
self.window_requests.lock().unwrap().push(redraw_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||||
|
self.size
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.to_physical(self.scale_factor() as f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_inner_size(&self, size: Size) {
|
||||||
|
let scale_factor = self.scale_factor() as f64;
|
||||||
|
|
||||||
|
let size = size.to_logical::<u32>(scale_factor);
|
||||||
|
*self.size.lock().unwrap() = size;
|
||||||
|
|
||||||
|
let frame_size_request = WindowRequest::FrameSize(size);
|
||||||
|
self.window_requests
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.push(frame_size_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
|
||||||
|
let scale_factor = self.scale_factor() as f64;
|
||||||
|
let size = dimensions.map(|size| size.to_logical::<u32>(scale_factor));
|
||||||
|
|
||||||
|
let min_size_request = WindowRequest::MinSize(size);
|
||||||
|
self.window_requests.lock().unwrap().push(min_size_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
||||||
|
let scale_factor = self.scale_factor() as f64;
|
||||||
|
let size = dimensions.map(|size| size.to_logical::<u32>(scale_factor));
|
||||||
|
|
||||||
|
let max_size_request = WindowRequest::MaxSize(size);
|
||||||
|
self.window_requests.lock().unwrap().push(max_size_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_resizable(&self, resizable: bool) {
|
||||||
|
let resizeable_request = WindowRequest::Resizeable(resizable);
|
||||||
|
self.window_requests
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.push(resizeable_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn scale_factor(&self) -> u32 {
|
||||||
|
// The scale factor from `get_surface_scale_factor` is always greater than zero, so
|
||||||
|
// u32 conversion is safe.
|
||||||
|
sctk::get_surface_scale_factor(&self.surface) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_decorations(&self, decorate: bool) {
|
||||||
|
let decorate_request = WindowRequest::Decorate(decorate);
|
||||||
|
self.window_requests.lock().unwrap().push(decorate_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_minimized(&self, minimized: bool) {
|
||||||
|
// You can't unminimize the window on Wayland.
|
||||||
|
if !minimized {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let minimize_request = WindowRequest::Minimize;
|
||||||
|
self.window_requests.lock().unwrap().push(minimize_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_maximized(&self, maximized: bool) {
|
||||||
|
let maximize_request = WindowRequest::Maximize(maximized);
|
||||||
|
self.window_requests.lock().unwrap().push(maximize_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn fullscreen(&self) -> Option<Fullscreen> {
|
||||||
|
if self.fullscreen.load(Ordering::Relaxed) {
|
||||||
|
let current_monitor = self.current_monitor().map(|monitor| RootMonitorHandle {
|
||||||
|
inner: PlatformMonitorHandle::Wayland(monitor),
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(Fullscreen::Borderless(current_monitor))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
||||||
|
let fullscreen_request = match fullscreen {
|
||||||
|
Some(Fullscreen::Exclusive(_)) => {
|
||||||
|
warn!("`Fullscreen::Exclusive` is ignored on Wayland");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Some(Fullscreen::Borderless(monitor)) => {
|
||||||
|
let monitor =
|
||||||
|
monitor.and_then(|RootMonitorHandle { inner: monitor }| match monitor {
|
||||||
|
PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
|
||||||
|
#[cfg(feature = "x11")]
|
||||||
|
PlatformMonitorHandle::X(_) => None,
|
||||||
|
});
|
||||||
|
|
||||||
|
WindowRequest::Fullscreen(monitor)
|
||||||
|
}
|
||||||
|
None => WindowRequest::UnsetFullscreen,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.window_requests
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.push(fullscreen_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_theme<T: Theme>(&self, theme: T) {
|
||||||
|
// First buttons is minimize, then maximize, and then close.
|
||||||
|
let buttons: Vec<(ButtonColorSpec, ButtonColorSpec)> =
|
||||||
|
[Button::Minimize, Button::Maximize, Button::Close]
|
||||||
|
.iter()
|
||||||
|
.map(|button| {
|
||||||
|
let button = *button;
|
||||||
|
let idle_active_bg = theme
|
||||||
|
.button_color(button, ButtonState::Idle, false, true)
|
||||||
|
.into();
|
||||||
|
let idle_inactive_bg = theme
|
||||||
|
.button_color(button, ButtonState::Idle, false, false)
|
||||||
|
.into();
|
||||||
|
let idle_active_icon = theme
|
||||||
|
.button_color(button, ButtonState::Idle, true, true)
|
||||||
|
.into();
|
||||||
|
let idle_inactive_icon = theme
|
||||||
|
.button_color(button, ButtonState::Idle, true, false)
|
||||||
|
.into();
|
||||||
|
let idle_bg = ColorSpec {
|
||||||
|
active: idle_active_bg,
|
||||||
|
inactive: idle_inactive_bg,
|
||||||
|
};
|
||||||
|
let idle_icon = ColorSpec {
|
||||||
|
active: idle_active_icon,
|
||||||
|
inactive: idle_inactive_icon,
|
||||||
|
};
|
||||||
|
|
||||||
|
let hovered_active_bg = theme
|
||||||
|
.button_color(button, ButtonState::Hovered, false, true)
|
||||||
|
.into();
|
||||||
|
let hovered_inactive_bg = theme
|
||||||
|
.button_color(button, ButtonState::Hovered, false, false)
|
||||||
|
.into();
|
||||||
|
let hovered_active_icon = theme
|
||||||
|
.button_color(button, ButtonState::Hovered, true, true)
|
||||||
|
.into();
|
||||||
|
let hovered_inactive_icon = theme
|
||||||
|
.button_color(button, ButtonState::Hovered, true, false)
|
||||||
|
.into();
|
||||||
|
let hovered_bg = ColorSpec {
|
||||||
|
active: hovered_active_bg,
|
||||||
|
inactive: hovered_inactive_bg,
|
||||||
|
};
|
||||||
|
let hovered_icon = ColorSpec {
|
||||||
|
active: hovered_active_icon,
|
||||||
|
inactive: hovered_inactive_icon,
|
||||||
|
};
|
||||||
|
|
||||||
|
let disabled_active_bg = theme
|
||||||
|
.button_color(button, ButtonState::Disabled, false, true)
|
||||||
|
.into();
|
||||||
|
let disabled_inactive_bg = theme
|
||||||
|
.button_color(button, ButtonState::Disabled, false, false)
|
||||||
|
.into();
|
||||||
|
let disabled_active_icon = theme
|
||||||
|
.button_color(button, ButtonState::Disabled, true, true)
|
||||||
|
.into();
|
||||||
|
let disabled_inactive_icon = theme
|
||||||
|
.button_color(button, ButtonState::Disabled, true, false)
|
||||||
|
.into();
|
||||||
|
let disabled_bg = ColorSpec {
|
||||||
|
active: disabled_active_bg,
|
||||||
|
inactive: disabled_inactive_bg,
|
||||||
|
};
|
||||||
|
let disabled_icon = ColorSpec {
|
||||||
|
active: disabled_active_icon,
|
||||||
|
inactive: disabled_inactive_icon,
|
||||||
|
};
|
||||||
|
|
||||||
|
let button_bg = ButtonColorSpec {
|
||||||
|
idle: idle_bg,
|
||||||
|
hovered: hovered_bg,
|
||||||
|
disabled: disabled_bg,
|
||||||
|
};
|
||||||
|
let button_icon = ButtonColorSpec {
|
||||||
|
idle: idle_icon,
|
||||||
|
hovered: hovered_icon,
|
||||||
|
disabled: disabled_icon,
|
||||||
|
};
|
||||||
|
|
||||||
|
(button_icon, button_bg)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let minimize_button = Some(buttons[0]);
|
||||||
|
let maximize_button = Some(buttons[1]);
|
||||||
|
let close_button = Some(buttons[2]);
|
||||||
|
|
||||||
|
// The first color is bar, then separator, and then text color.
|
||||||
|
let titlebar_colors: Vec<ColorSpec> = [Element::Bar, Element::Separator, Element::Text]
|
||||||
|
.iter()
|
||||||
|
.map(|element| {
|
||||||
|
let element = *element;
|
||||||
|
let active = theme.element_color(element, true).into();
|
||||||
|
let inactive = theme.element_color(element, false).into();
|
||||||
|
|
||||||
|
ColorSpec { active, inactive }
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let primary_color = titlebar_colors[0];
|
||||||
|
let secondary_color = titlebar_colors[1];
|
||||||
|
let title_color = titlebar_colors[2];
|
||||||
|
|
||||||
|
let title_font = theme.font();
|
||||||
|
|
||||||
|
let concept_config = ConceptConfig {
|
||||||
|
primary_color,
|
||||||
|
secondary_color,
|
||||||
|
title_color,
|
||||||
|
title_font,
|
||||||
|
minimize_button,
|
||||||
|
maximize_button,
|
||||||
|
close_button,
|
||||||
|
};
|
||||||
|
|
||||||
|
let theme_request = WindowRequest::Theme(concept_config);
|
||||||
|
self.window_requests.lock().unwrap().push(theme_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
|
||||||
|
let cursor_icon_request = WindowRequest::NewCursorIcon(cursor);
|
||||||
|
self.window_requests
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.push(cursor_icon_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cursor_visible(&self, visible: bool) {
|
||||||
|
let cursor_visible_request = WindowRequest::ShowCursor(visible);
|
||||||
|
self.window_requests
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.push(cursor_visible_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
||||||
|
if !self.windowing_features.cursor_grab() {
|
||||||
|
return Err(ExternalError::NotSupported(NotSupportedError::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cursor_grab_request = WindowRequest::GrabCursor(grab);
|
||||||
|
self.window_requests
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.push(cursor_grab_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cursor_position(&self, _: Position) -> Result<(), ExternalError> {
|
||||||
|
// XXX This is possible if the locked pointer is being used. We don't have any
|
||||||
|
// API for that right now, but it could be added in
|
||||||
|
// https://github.com/rust-windowing/winit/issues/1677.
|
||||||
|
//
|
||||||
|
// This function is essential for the locked pointer API.
|
||||||
|
//
|
||||||
|
// See pointer-constraints-unstable-v1.xml.
|
||||||
|
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_ime_position(&self, position: Position) {
|
||||||
|
let scale_factor = self.scale_factor() as f64;
|
||||||
|
let position = position.to_logical(scale_factor);
|
||||||
|
let ime_position_request = WindowRequest::IMEPosition(position);
|
||||||
|
self.window_requests
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.push(ime_position_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn display(&self) -> &Display {
|
||||||
|
&self.display
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn surface(&self) -> &WlSurface {
|
||||||
|
&self.surface
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn current_monitor(&self) -> Option<MonitorHandle> {
|
||||||
|
let output = sctk::get_surface_outputs(&self.surface).last()?.clone();
|
||||||
|
Some(MonitorHandle::new(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||||
|
self.output_manager_handle.available_outputs()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn primary_monitor(&self) -> Option<RootMonitorHandle> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn raw_window_handle(&self) -> WaylandHandle {
|
||||||
|
let display = self.display.get_display_ptr() as *mut _;
|
||||||
|
let surface = self.surface.as_ref().c_ptr() as *mut _;
|
||||||
|
|
||||||
|
WaylandHandle {
|
||||||
|
display,
|
||||||
|
surface,
|
||||||
|
..WaylandHandle::empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LocalARGBColor> for ARGBColor {
|
||||||
|
fn from(color: LocalARGBColor) -> Self {
|
||||||
|
let a = color.a;
|
||||||
|
let r = color.r;
|
||||||
|
let g = color.g;
|
||||||
|
let b = color.b;
|
||||||
|
Self { a, r, g, b }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Window {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let close_request = WindowRequest::Close;
|
||||||
|
self.window_requests.lock().unwrap().push(close_request);
|
||||||
|
self.event_loop_awakener.ping();
|
||||||
|
}
|
||||||
|
}
|
388
src/platform_impl/linux/wayland/window/shim.rs
Normal file
388
src/platform_impl/linux/wayland/window/shim.rs
Normal file
|
@ -0,0 +1,388 @@
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use sctk::reexports::client::protocol::wl_output::WlOutput;
|
||||||
|
|
||||||
|
use sctk::window::{ConceptConfig, ConceptFrame, Decorations, Window};
|
||||||
|
|
||||||
|
use crate::dpi::{LogicalPosition, LogicalSize};
|
||||||
|
|
||||||
|
use crate::event::WindowEvent;
|
||||||
|
use crate::platform_impl::wayland::event_loop::WinitState;
|
||||||
|
use crate::platform_impl::wayland::seat::pointer::WinitPointer;
|
||||||
|
use crate::platform_impl::wayland::seat::text_input::TextInputHandler;
|
||||||
|
use crate::platform_impl::wayland::WindowId;
|
||||||
|
use crate::window::CursorIcon;
|
||||||
|
|
||||||
|
/// A request to SCTK window from Winit window.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum WindowRequest {
|
||||||
|
/// Set fullscreen.
|
||||||
|
///
|
||||||
|
/// Passing `None` will set it on the current monitor.
|
||||||
|
Fullscreen(Option<WlOutput>),
|
||||||
|
|
||||||
|
/// Unset fullscreen.
|
||||||
|
UnsetFullscreen,
|
||||||
|
|
||||||
|
/// Show cursor for the certain window or not.
|
||||||
|
ShowCursor(bool),
|
||||||
|
|
||||||
|
/// Change the cursor icon.
|
||||||
|
NewCursorIcon(CursorIcon),
|
||||||
|
|
||||||
|
/// Grab cursor.
|
||||||
|
GrabCursor(bool),
|
||||||
|
|
||||||
|
/// Maximize the window.
|
||||||
|
Maximize(bool),
|
||||||
|
|
||||||
|
/// Minimize the window.
|
||||||
|
Minimize,
|
||||||
|
|
||||||
|
/// Request decorations change.
|
||||||
|
Decorate(bool),
|
||||||
|
|
||||||
|
/// Make the window resizeable.
|
||||||
|
Resizeable(bool),
|
||||||
|
|
||||||
|
/// Set the title for window.
|
||||||
|
Title(String),
|
||||||
|
|
||||||
|
/// Min size.
|
||||||
|
MinSize(Option<LogicalSize<u32>>),
|
||||||
|
|
||||||
|
/// Max size.
|
||||||
|
MaxSize(Option<LogicalSize<u32>>),
|
||||||
|
|
||||||
|
/// New frame size.
|
||||||
|
FrameSize(LogicalSize<u32>),
|
||||||
|
|
||||||
|
/// Set IME window position.
|
||||||
|
IMEPosition(LogicalPosition<u32>),
|
||||||
|
|
||||||
|
/// Redraw was requested.
|
||||||
|
Redraw,
|
||||||
|
|
||||||
|
/// A new theme for a concept frame was requested.
|
||||||
|
Theme(ConceptConfig),
|
||||||
|
|
||||||
|
/// Window should be closed.
|
||||||
|
Close,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pending update to a window from SCTK window.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct WindowUpdate {
|
||||||
|
/// New window size.
|
||||||
|
pub size: Option<LogicalSize<u32>>,
|
||||||
|
|
||||||
|
/// New scale factor.
|
||||||
|
pub scale_factor: Option<i32>,
|
||||||
|
|
||||||
|
/// Whether `redraw` was requested.
|
||||||
|
pub redraw_requested: bool,
|
||||||
|
|
||||||
|
/// Wether the frame should be refreshed.
|
||||||
|
pub refresh_frame: bool,
|
||||||
|
|
||||||
|
/// Close the window.
|
||||||
|
pub close_window: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowUpdate {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
size: None,
|
||||||
|
scale_factor: None,
|
||||||
|
redraw_requested: false,
|
||||||
|
refresh_frame: false,
|
||||||
|
close_window: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take(&mut self) -> Self {
|
||||||
|
let size = self.size.take();
|
||||||
|
let scale_factor = self.scale_factor.take();
|
||||||
|
|
||||||
|
let redraw_requested = self.redraw_requested;
|
||||||
|
self.redraw_requested = false;
|
||||||
|
|
||||||
|
let refresh_frame = self.refresh_frame;
|
||||||
|
self.refresh_frame = false;
|
||||||
|
|
||||||
|
let close_window = self.close_window;
|
||||||
|
self.close_window = false;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
size,
|
||||||
|
scale_factor,
|
||||||
|
redraw_requested,
|
||||||
|
refresh_frame,
|
||||||
|
close_window,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A handle to perform operations on SCTK window
|
||||||
|
/// and react to events.
|
||||||
|
pub struct WindowHandle {
|
||||||
|
/// An actual window.
|
||||||
|
pub window: Window<ConceptFrame>,
|
||||||
|
|
||||||
|
/// The current size of the window.
|
||||||
|
pub size: Arc<Mutex<LogicalSize<u32>>>,
|
||||||
|
|
||||||
|
/// A pending requests to SCTK window.
|
||||||
|
pub pending_window_requests: Arc<Mutex<Vec<WindowRequest>>>,
|
||||||
|
|
||||||
|
/// Current cursor icon.
|
||||||
|
pub cursor_icon: Cell<CursorIcon>,
|
||||||
|
|
||||||
|
/// Visible cursor or not.
|
||||||
|
cursor_visible: Cell<bool>,
|
||||||
|
|
||||||
|
/// Cursor confined to the surface.
|
||||||
|
confined: Cell<bool>,
|
||||||
|
|
||||||
|
/// Pointers over the current surface.
|
||||||
|
pointers: Vec<WinitPointer>,
|
||||||
|
|
||||||
|
/// Text inputs on the current surface.
|
||||||
|
text_inputs: Vec<TextInputHandler>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowHandle {
|
||||||
|
pub fn new(
|
||||||
|
window: Window<ConceptFrame>,
|
||||||
|
size: Arc<Mutex<LogicalSize<u32>>>,
|
||||||
|
pending_window_requests: Arc<Mutex<Vec<WindowRequest>>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
window,
|
||||||
|
size,
|
||||||
|
pending_window_requests,
|
||||||
|
cursor_icon: Cell::new(CursorIcon::Default),
|
||||||
|
confined: Cell::new(false),
|
||||||
|
cursor_visible: Cell::new(true),
|
||||||
|
pointers: Vec::new(),
|
||||||
|
text_inputs: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_cursor_grab(&self, grab: bool) {
|
||||||
|
// The new requested state matches the current confine status, return.
|
||||||
|
if self.confined.get() == grab {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.confined.replace(grab);
|
||||||
|
|
||||||
|
for pointer in self.pointers.iter() {
|
||||||
|
if self.confined.get() {
|
||||||
|
let surface = self.window.surface();
|
||||||
|
pointer.confine(&surface);
|
||||||
|
} else {
|
||||||
|
pointer.unconfine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pointer appeared over the window.
|
||||||
|
pub fn pointer_entered(&mut self, pointer: WinitPointer) {
|
||||||
|
let position = self.pointers.iter().position(|p| *p == pointer);
|
||||||
|
|
||||||
|
if position.is_none() {
|
||||||
|
if self.confined.get() {
|
||||||
|
let surface = self.window.surface();
|
||||||
|
pointer.confine(&surface);
|
||||||
|
}
|
||||||
|
self.pointers.push(pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the current cursor style.
|
||||||
|
self.set_cursor_visible(self.cursor_visible.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pointer left the window.
|
||||||
|
pub fn pointer_left(&mut self, pointer: WinitPointer) {
|
||||||
|
let position = self.pointers.iter().position(|p| *p == pointer);
|
||||||
|
|
||||||
|
if let Some(position) = position {
|
||||||
|
let pointer = self.pointers.remove(position);
|
||||||
|
|
||||||
|
// Drop the confined pointer.
|
||||||
|
if self.confined.get() {
|
||||||
|
pointer.unconfine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text_input_entered(&mut self, text_input: TextInputHandler) {
|
||||||
|
if self
|
||||||
|
.text_inputs
|
||||||
|
.iter()
|
||||||
|
.find(|t| *t == &text_input)
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
self.text_inputs.push(text_input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text_input_left(&mut self, text_input: TextInputHandler) {
|
||||||
|
if let Some(position) = self.text_inputs.iter().position(|t| *t == text_input) {
|
||||||
|
self.text_inputs.remove(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_ime_position(&self, position: LogicalPosition<u32>) {
|
||||||
|
// XXX This won't fly unless user will have a way to request IME window per seat, since
|
||||||
|
// the ime windows will be overlapping, but winit doesn't expose API to specify for
|
||||||
|
// which seat we're setting IME position.
|
||||||
|
let (x, y) = (position.x as i32, position.y as i32);
|
||||||
|
for text_input in self.text_inputs.iter() {
|
||||||
|
text_input.set_ime_position(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_cursor_visible(&self, visible: bool) {
|
||||||
|
self.cursor_visible.replace(visible);
|
||||||
|
let cursor_icon = match visible {
|
||||||
|
true => Some(self.cursor_icon.get()),
|
||||||
|
false => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
for pointer in self.pointers.iter() {
|
||||||
|
pointer.set_cursor(cursor_icon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_cursor_icon(&self, cursor_icon: CursorIcon) {
|
||||||
|
self.cursor_icon.replace(cursor_icon);
|
||||||
|
|
||||||
|
if !self.cursor_visible.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for pointer in self.pointers.iter() {
|
||||||
|
pointer.set_cursor(Some(cursor_icon));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn handle_window_requests(winit_state: &mut WinitState) {
|
||||||
|
let window_map = &mut winit_state.window_map;
|
||||||
|
let window_updates = &mut winit_state.window_updates;
|
||||||
|
let mut windows_to_close: Vec<WindowId> = Vec::new();
|
||||||
|
|
||||||
|
// Process the rest of the events.
|
||||||
|
for (window_id, window_handle) in window_map.iter_mut() {
|
||||||
|
let mut requests = window_handle.pending_window_requests.lock().unwrap();
|
||||||
|
for request in requests.drain(..) {
|
||||||
|
match request {
|
||||||
|
WindowRequest::Fullscreen(fullscreen) => {
|
||||||
|
window_handle.window.set_fullscreen(fullscreen.as_ref());
|
||||||
|
}
|
||||||
|
WindowRequest::UnsetFullscreen => {
|
||||||
|
window_handle.window.unset_fullscreen();
|
||||||
|
}
|
||||||
|
WindowRequest::ShowCursor(show_cursor) => {
|
||||||
|
window_handle.set_cursor_visible(show_cursor);
|
||||||
|
}
|
||||||
|
WindowRequest::NewCursorIcon(cursor_icon) => {
|
||||||
|
window_handle.set_cursor_icon(cursor_icon);
|
||||||
|
}
|
||||||
|
WindowRequest::IMEPosition(position) => {
|
||||||
|
window_handle.set_ime_position(position);
|
||||||
|
}
|
||||||
|
WindowRequest::GrabCursor(grab) => {
|
||||||
|
window_handle.set_cursor_grab(grab);
|
||||||
|
}
|
||||||
|
WindowRequest::Maximize(maximize) => {
|
||||||
|
if maximize {
|
||||||
|
window_handle.window.set_maximized();
|
||||||
|
} else {
|
||||||
|
window_handle.window.unset_maximized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowRequest::Minimize => {
|
||||||
|
window_handle.window.set_minimized();
|
||||||
|
}
|
||||||
|
WindowRequest::Decorate(decorate) => {
|
||||||
|
let decorations = match decorate {
|
||||||
|
true => Decorations::FollowServer,
|
||||||
|
false => Decorations::None,
|
||||||
|
};
|
||||||
|
|
||||||
|
window_handle.window.set_decorate(decorations);
|
||||||
|
|
||||||
|
// We should refresh the frame to apply decorations change.
|
||||||
|
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||||
|
window_update.refresh_frame = true;
|
||||||
|
}
|
||||||
|
WindowRequest::Resizeable(resizeable) => {
|
||||||
|
window_handle.window.set_resizable(resizeable);
|
||||||
|
|
||||||
|
// We should refresh the frame to update button state.
|
||||||
|
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||||
|
window_update.refresh_frame = true;
|
||||||
|
}
|
||||||
|
WindowRequest::Title(title) => {
|
||||||
|
window_handle.window.set_title(title);
|
||||||
|
|
||||||
|
// We should refresh the frame to draw new title.
|
||||||
|
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||||
|
window_update.refresh_frame = true;
|
||||||
|
}
|
||||||
|
WindowRequest::MinSize(size) => {
|
||||||
|
let size = size.map(|size| (size.width, size.height));
|
||||||
|
window_handle.window.set_min_size(size);
|
||||||
|
|
||||||
|
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||||
|
window_update.redraw_requested = true;
|
||||||
|
}
|
||||||
|
WindowRequest::MaxSize(size) => {
|
||||||
|
let size = size.map(|size| (size.width, size.height));
|
||||||
|
window_handle.window.set_max_size(size);
|
||||||
|
|
||||||
|
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||||
|
window_update.redraw_requested = true;
|
||||||
|
}
|
||||||
|
WindowRequest::FrameSize(size) => {
|
||||||
|
// Set new size.
|
||||||
|
window_handle.window.resize(size.width, size.height);
|
||||||
|
|
||||||
|
// We should refresh the frame after resize.
|
||||||
|
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||||
|
window_update.refresh_frame = true;
|
||||||
|
}
|
||||||
|
WindowRequest::Redraw => {
|
||||||
|
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||||
|
window_update.redraw_requested = true;
|
||||||
|
}
|
||||||
|
WindowRequest::Theme(concept_config) => {
|
||||||
|
window_handle.window.set_frame_config(concept_config);
|
||||||
|
|
||||||
|
// We should refresh the frame to apply new theme.
|
||||||
|
let window_update = window_updates.get_mut(&window_id).unwrap();
|
||||||
|
window_update.refresh_frame = true;
|
||||||
|
}
|
||||||
|
WindowRequest::Close => {
|
||||||
|
// The window was requested to be closed.
|
||||||
|
windows_to_close.push(*window_id);
|
||||||
|
|
||||||
|
// Send event that the window was destroyed.
|
||||||
|
let event_sink = &mut winit_state.event_sink;
|
||||||
|
event_sink.push_window_event(WindowEvent::Destroyed, *window_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the windows.
|
||||||
|
for window in windows_to_close {
|
||||||
|
let _ = window_map.remove(&window);
|
||||||
|
let _ = window_updates.remove(&window);
|
||||||
|
}
|
||||||
|
}
|
|
@ -614,7 +614,7 @@ impl Window {
|
||||||
///
|
///
|
||||||
/// The dock and the menu bar are always disabled in fullscreen mode.
|
/// The dock and the menu bar are always disabled in fullscreen mode.
|
||||||
/// - **iOS:** Can only be called on the main thread.
|
/// - **iOS:** Can only be called on the main thread.
|
||||||
/// - **Wayland:** Does not support exclusive fullscreen mode.
|
/// - **Wayland:** Does not support exclusive fullscreen mode and will no-op a request.
|
||||||
/// - **Windows:** Screen saver is disabled in fullscreen mode.
|
/// - **Windows:** Screen saver is disabled in fullscreen mode.
|
||||||
/// - **Android:** Unsupported.
|
/// - **Android:** Unsupported.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -677,7 +677,7 @@ impl Window {
|
||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - **iOS / Android / Web / Wayland / Windows:** Unsupported.
|
/// - **iOS / Android / Web / Windows:** Unsupported.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_ime_position<P: Into<Position>>(&self, position: P) {
|
pub fn set_ime_position<P: Into<Position>>(&self, position: P) {
|
||||||
self.window.set_ime_position(position.into())
|
self.window.set_ime_position(position.into())
|
||||||
|
@ -708,9 +708,12 @@ impl Window {
|
||||||
|
|
||||||
/// Grabs the cursor, preventing it from leaving the window.
|
/// Grabs the cursor, preventing it from leaving the window.
|
||||||
///
|
///
|
||||||
|
/// There's no guarantee that the cursor will be hidden. You should
|
||||||
|
/// hide it by yourself if you want so.
|
||||||
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - **macOS / Wayland:** This locks the cursor in a fixed location, which looks visually awkward.
|
/// - **macOS:** This locks the cursor in a fixed location, which looks visually awkward.
|
||||||
/// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`].
|
/// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
||||||
|
|
Loading…
Reference in a new issue