From 2eaec664223b6787006ee4ee91a211346dae07c3 Mon Sep 17 00:00:00 2001 From: Ngo Iok Ui Date: Sat, 1 May 2021 19:07:15 +0800 Subject: [PATCH] Initial gtk port --- src/platform/run_return.rs | 5 - src/platform/unix.rs | 522 ------- src/platform_impl/linux/event_loop.rs | 567 +++++++ src/platform_impl/linux/mod.rs | 768 +-------- src/platform_impl/linux/monitor.rs | 60 + src/platform_impl/linux/wayland/env.rs | 149 -- .../linux/wayland/event_loop/mod.rs | 535 ------- .../linux/wayland/event_loop/proxy.rs | 32 - .../linux/wayland/event_loop/sink.rs | 36 - .../linux/wayland/event_loop/state.rs | 25 - src/platform_impl/linux/wayland/mod.rs | 42 - src/platform_impl/linux/wayland/output.rs | 240 --- .../linux/wayland/seat/keyboard/handlers.rs | 151 -- .../linux/wayland/seat/keyboard/keymap.rs | 192 --- .../linux/wayland/seat/keyboard/mod.rs | 105 -- src/platform_impl/linux/wayland/seat/mod.rs | 208 --- .../linux/wayland/seat/pointer/data.rs | 74 - .../linux/wayland/seat/pointer/handlers.rs | 305 ---- .../linux/wayland/seat/pointer/mod.rs | 257 --- .../linux/wayland/seat/text_input/handlers.rs | 78 - .../linux/wayland/seat/text_input/mod.rs | 66 - .../linux/wayland/seat/touch/handlers.rs | 122 -- .../linux/wayland/seat/touch/mod.rs | 78 - src/platform_impl/linux/wayland/window/mod.rs | 668 -------- .../linux/wayland/window/shim.rs | 400 ----- src/platform_impl/linux/window.rs | 640 ++++++++ src/platform_impl/linux/x11/dnd.rs | 219 --- .../linux/x11/event_processor.rs | 1300 ---------------- src/platform_impl/linux/x11/events.rs | 1008 ------------ src/platform_impl/linux/x11/ffi.rs | 4 - src/platform_impl/linux/x11/ime/callbacks.rs | 175 --- src/platform_impl/linux/x11/ime/context.rs | 139 -- src/platform_impl/linux/x11/ime/inner.rs | 68 - .../linux/x11/ime/input_method.rs | 278 ---- src/platform_impl/linux/x11/ime/mod.rs | 163 -- src/platform_impl/linux/x11/mod.rs | 704 --------- src/platform_impl/linux/x11/monitor.rs | 314 ---- src/platform_impl/linux/x11/util/atom.rs | 71 - .../linux/x11/util/client_msg.rs | 46 - src/platform_impl/linux/x11/util/cursor.rs | 129 -- src/platform_impl/linux/x11/util/format.rs | 55 - src/platform_impl/linux/x11/util/geometry.rs | 389 ----- src/platform_impl/linux/x11/util/hint.rs | 341 ---- src/platform_impl/linux/x11/util/icon.rs | 35 - src/platform_impl/linux/x11/util/input.rs | 190 --- src/platform_impl/linux/x11/util/keys.rs | 92 -- src/platform_impl/linux/x11/util/memory.rs | 59 - src/platform_impl/linux/x11/util/mod.rs | 97 -- src/platform_impl/linux/x11/util/modifiers.rs | 187 --- src/platform_impl/linux/x11/util/randr.rs | 220 --- .../linux/x11/util/window_property.rs | 142 -- src/platform_impl/linux/x11/util/wm.rs | 119 -- src/platform_impl/linux/x11/window.rs | 1378 ----------------- src/platform_impl/linux/x11/xdisplay.rs | 165 -- 54 files changed, 1279 insertions(+), 13133 deletions(-) create mode 100644 src/platform_impl/linux/event_loop.rs create mode 100644 src/platform_impl/linux/monitor.rs delete mode 100644 src/platform_impl/linux/wayland/env.rs delete mode 100644 src/platform_impl/linux/wayland/event_loop/mod.rs delete mode 100644 src/platform_impl/linux/wayland/event_loop/proxy.rs delete mode 100644 src/platform_impl/linux/wayland/event_loop/sink.rs delete mode 100644 src/platform_impl/linux/wayland/event_loop/state.rs delete mode 100644 src/platform_impl/linux/wayland/mod.rs delete mode 100644 src/platform_impl/linux/wayland/output.rs delete mode 100644 src/platform_impl/linux/wayland/seat/keyboard/handlers.rs delete mode 100644 src/platform_impl/linux/wayland/seat/keyboard/keymap.rs delete mode 100644 src/platform_impl/linux/wayland/seat/keyboard/mod.rs delete mode 100644 src/platform_impl/linux/wayland/seat/mod.rs delete mode 100644 src/platform_impl/linux/wayland/seat/pointer/data.rs delete mode 100644 src/platform_impl/linux/wayland/seat/pointer/handlers.rs delete mode 100644 src/platform_impl/linux/wayland/seat/pointer/mod.rs delete mode 100644 src/platform_impl/linux/wayland/seat/text_input/handlers.rs delete mode 100644 src/platform_impl/linux/wayland/seat/text_input/mod.rs delete mode 100644 src/platform_impl/linux/wayland/seat/touch/handlers.rs delete mode 100644 src/platform_impl/linux/wayland/seat/touch/mod.rs delete mode 100644 src/platform_impl/linux/wayland/window/mod.rs delete mode 100644 src/platform_impl/linux/wayland/window/shim.rs create mode 100644 src/platform_impl/linux/window.rs delete mode 100644 src/platform_impl/linux/x11/dnd.rs delete mode 100644 src/platform_impl/linux/x11/event_processor.rs delete mode 100644 src/platform_impl/linux/x11/events.rs delete mode 100644 src/platform_impl/linux/x11/ffi.rs delete mode 100644 src/platform_impl/linux/x11/ime/callbacks.rs delete mode 100644 src/platform_impl/linux/x11/ime/context.rs delete mode 100644 src/platform_impl/linux/x11/ime/inner.rs delete mode 100644 src/platform_impl/linux/x11/ime/input_method.rs delete mode 100644 src/platform_impl/linux/x11/ime/mod.rs delete mode 100644 src/platform_impl/linux/x11/mod.rs delete mode 100644 src/platform_impl/linux/x11/monitor.rs delete mode 100644 src/platform_impl/linux/x11/util/atom.rs delete mode 100644 src/platform_impl/linux/x11/util/client_msg.rs delete mode 100644 src/platform_impl/linux/x11/util/cursor.rs delete mode 100644 src/platform_impl/linux/x11/util/format.rs delete mode 100644 src/platform_impl/linux/x11/util/geometry.rs delete mode 100644 src/platform_impl/linux/x11/util/hint.rs delete mode 100644 src/platform_impl/linux/x11/util/icon.rs delete mode 100644 src/platform_impl/linux/x11/util/input.rs delete mode 100644 src/platform_impl/linux/x11/util/keys.rs delete mode 100644 src/platform_impl/linux/x11/util/memory.rs delete mode 100644 src/platform_impl/linux/x11/util/mod.rs delete mode 100644 src/platform_impl/linux/x11/util/modifiers.rs delete mode 100644 src/platform_impl/linux/x11/util/randr.rs delete mode 100644 src/platform_impl/linux/x11/util/window_property.rs delete mode 100644 src/platform_impl/linux/x11/util/wm.rs delete mode 100644 src/platform_impl/linux/x11/window.rs delete mode 100644 src/platform_impl/linux/x11/xdisplay.rs diff --git a/src/platform/run_return.rs b/src/platform/run_return.rs index 932d9e38..2f720e52 100644 --- a/src/platform/run_return.rs +++ b/src/platform/run_return.rs @@ -2,11 +2,6 @@ target_os = "windows", target_os = "macos", target_os = "android", - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" ))] use crate::{ diff --git a/src/platform/unix.rs b/src/platform/unix.rs index 8662e456..5830db52 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -6,526 +6,4 @@ target_os = "openbsd" ))] -use std::os::raw; -#[cfg(feature = "x11")] -use std::{ptr, sync::Arc}; -use crate::{ - event_loop::{EventLoop, EventLoopWindowTarget}, - monitor::MonitorHandle, - window::{Window, WindowBuilder}, -}; - -#[cfg(feature = "x11")] -use crate::dpi::Size; -#[cfg(feature = "x11")] -use crate::platform_impl::x11::{ffi::XVisualInfo, XConnection}; -use crate::platform_impl::{ - EventLoop as LinuxEventLoop, EventLoopWindowTarget as LinuxEventLoopWindowTarget, - Window as LinuxWindow, -}; - -// TODO: stupid hack so that glutin can do its work -#[doc(hidden)] -#[cfg(feature = "x11")] -pub use crate::platform_impl::x11; -#[cfg(feature = "x11")] -pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupported}; - -/// Additional methods on `EventLoopWindowTarget` that are specific to Unix. -pub trait EventLoopWindowTargetExtUnix { - /// True if the `EventLoopWindowTarget` uses Wayland. - #[cfg(feature = "wayland")] - fn is_wayland(&self) -> bool; - - /// True if the `EventLoopWindowTarget` uses X11. - #[cfg(feature = "x11")] - fn is_x11(&self) -> bool; - - #[doc(hidden)] - #[cfg(feature = "x11")] - fn xlib_xconnection(&self) -> Option>; - - /// Returns a pointer to the `wl_display` object of wayland that is used by this - /// `EventLoopWindowTarget`. - /// - /// Returns `None` if the `EventLoop` doesn't use wayland (if it uses xlib for example). - /// - /// The pointer will become invalid when the winit `EventLoop` is destroyed. - #[cfg(feature = "wayland")] - fn wayland_display(&self) -> Option<*mut raw::c_void>; -} - -impl EventLoopWindowTargetExtUnix for EventLoopWindowTarget { - #[inline] - #[cfg(feature = "wayland")] - fn is_wayland(&self) -> bool { - self.p.is_wayland() - } - - #[inline] - #[cfg(feature = "x11")] - fn is_x11(&self) -> bool { - !self.p.is_wayland() - } - - #[inline] - #[doc(hidden)] - #[cfg(feature = "x11")] - fn xlib_xconnection(&self) -> Option> { - match self.p { - LinuxEventLoopWindowTarget::X(ref e) => Some(e.x_connection().clone()), - #[cfg(feature = "wayland")] - _ => None, - } - } - - #[inline] - #[cfg(feature = "wayland")] - fn wayland_display(&self) -> Option<*mut raw::c_void> { - match self.p { - LinuxEventLoopWindowTarget::Wayland(ref p) => { - Some(p.display().get_display_ptr() as *mut _) - } - #[cfg(feature = "x11")] - _ => None, - } - } -} - -/// Additional methods on `EventLoop` that are specific to Unix. -pub trait EventLoopExtUnix { - /// Builds a new `EventLoop` that is forced to use X11. - /// - /// # Panics - /// - /// If called outside the main thread. To initialize an X11 event loop outside - /// the main thread, use [`new_x11_any_thread`](#tymethod.new_x11_any_thread). - #[cfg(feature = "x11")] - fn new_x11() -> Result - where - Self: Sized; - - /// Builds a new `EventLoop` that is forced to use Wayland. - /// - /// # Panics - /// - /// If called outside the main thread. To initialize a Wayland event loop outside - /// the main thread, use [`new_wayland_any_thread`](#tymethod.new_wayland_any_thread). - #[cfg(feature = "wayland")] - fn new_wayland() -> Self - where - Self: Sized; - - /// Builds a new `EventLoop` on any thread. - /// - /// This method bypasses the cross-platform compatibility requirement - /// that `EventLoop` be created on the main thread. - fn new_any_thread() -> Self - where - Self: Sized; - - /// Builds a new X11 `EventLoop` on any thread. - /// - /// This method bypasses the cross-platform compatibility requirement - /// that `EventLoop` be created on the main thread. - #[cfg(feature = "x11")] - fn new_x11_any_thread() -> Result - where - Self: Sized; - - /// Builds a new Wayland `EventLoop` on any thread. - /// - /// This method bypasses the cross-platform compatibility requirement - /// that `EventLoop` be created on the main thread. - #[cfg(feature = "wayland")] - fn new_wayland_any_thread() -> Self - where - Self: Sized; -} - -fn wrap_ev(event_loop: LinuxEventLoop) -> EventLoop { - EventLoop { - event_loop, - _marker: std::marker::PhantomData, - } -} - -impl EventLoopExtUnix for EventLoop { - #[inline] - fn new_any_thread() -> Self { - wrap_ev(LinuxEventLoop::new_any_thread()) - } - - #[inline] - #[cfg(feature = "x11")] - fn new_x11_any_thread() -> Result { - LinuxEventLoop::new_x11_any_thread().map(wrap_ev) - } - - #[inline] - #[cfg(feature = "wayland")] - fn new_wayland_any_thread() -> Self { - wrap_ev( - LinuxEventLoop::new_wayland_any_thread() - // TODO: propagate - .expect("failed to open Wayland connection"), - ) - } - - #[inline] - #[cfg(feature = "x11")] - fn new_x11() -> Result { - LinuxEventLoop::new_x11().map(wrap_ev) - } - - #[inline] - #[cfg(feature = "wayland")] - fn new_wayland() -> Self { - wrap_ev( - LinuxEventLoop::new_wayland() - // TODO: propagate - .expect("failed to open Wayland connection"), - ) - } -} - -/// Additional methods on `Window` that are specific to Unix. -pub trait WindowExtUnix { - /// Returns the ID of the `Window` xlib object that is used by this window. - /// - /// Returns `None` if the window doesn't use xlib (if it uses wayland for example). - #[cfg(feature = "x11")] - fn xlib_window(&self) -> Option; - - /// Returns a pointer to the `Display` object of xlib that is used by this window. - /// - /// Returns `None` if the window doesn't use xlib (if it uses wayland for example). - /// - /// The pointer will become invalid when the glutin `Window` is destroyed. - #[cfg(feature = "x11")] - fn xlib_display(&self) -> Option<*mut raw::c_void>; - - #[cfg(feature = "x11")] - fn xlib_screen_id(&self) -> Option; - - #[doc(hidden)] - #[cfg(feature = "x11")] - fn xlib_xconnection(&self) -> Option>; - - /// This function returns the underlying `xcb_connection_t` of an xlib `Display`. - /// - /// Returns `None` if the window doesn't use xlib (if it uses wayland for example). - /// - /// The pointer will become invalid when the glutin `Window` is destroyed. - #[cfg(feature = "x11")] - fn xcb_connection(&self) -> Option<*mut raw::c_void>; - - /// Returns a pointer to the `wl_surface` object of wayland that is used by this window. - /// - /// Returns `None` if the window doesn't use wayland (if it uses xlib for example). - /// - /// The pointer will become invalid when the glutin `Window` is destroyed. - #[cfg(feature = "wayland")] - fn wayland_surface(&self) -> Option<*mut raw::c_void>; - - /// Returns a pointer to the `wl_display` object of wayland that is used by this window. - /// - /// Returns `None` if the window doesn't use wayland (if it uses xlib for example). - /// - /// The pointer will become invalid when the glutin `Window` is destroyed. - #[cfg(feature = "wayland")] - fn wayland_display(&self) -> Option<*mut raw::c_void>; - - /// Sets the color theme of the client side window decorations on wayland - #[cfg(feature = "wayland")] - fn set_wayland_theme(&self, theme: T); - - /// Check if the window is ready for drawing - /// - /// It is a remnant of a previous implementation detail for the - /// wayland backend, and is no longer relevant. - /// - /// Always return true. - #[deprecated] - fn is_ready(&self) -> bool; -} - -impl WindowExtUnix for Window { - #[inline] - #[cfg(feature = "x11")] - fn xlib_window(&self) -> Option { - match self.window { - LinuxWindow::X(ref w) => Some(w.xlib_window()), - #[cfg(feature = "wayland")] - _ => None, - } - } - - #[inline] - #[cfg(feature = "x11")] - fn xlib_display(&self) -> Option<*mut raw::c_void> { - match self.window { - LinuxWindow::X(ref w) => Some(w.xlib_display()), - #[cfg(feature = "wayland")] - _ => None, - } - } - - #[inline] - #[cfg(feature = "x11")] - fn xlib_screen_id(&self) -> Option { - match self.window { - LinuxWindow::X(ref w) => Some(w.xlib_screen_id()), - #[cfg(feature = "wayland")] - _ => None, - } - } - - #[inline] - #[doc(hidden)] - #[cfg(feature = "x11")] - fn xlib_xconnection(&self) -> Option> { - match self.window { - LinuxWindow::X(ref w) => Some(w.xlib_xconnection()), - #[cfg(feature = "wayland")] - _ => None, - } - } - - #[inline] - #[cfg(feature = "x11")] - fn xcb_connection(&self) -> Option<*mut raw::c_void> { - match self.window { - LinuxWindow::X(ref w) => Some(w.xcb_connection()), - #[cfg(feature = "wayland")] - _ => None, - } - } - - #[inline] - #[cfg(feature = "wayland")] - fn wayland_surface(&self) -> Option<*mut raw::c_void> { - match self.window { - LinuxWindow::Wayland(ref w) => Some(w.surface().as_ref().c_ptr() as *mut _), - #[cfg(feature = "x11")] - _ => None, - } - } - - #[inline] - #[cfg(feature = "wayland")] - fn wayland_display(&self) -> Option<*mut raw::c_void> { - match self.window { - LinuxWindow::Wayland(ref w) => Some(w.display().get_display_ptr() as *mut _), - #[cfg(feature = "x11")] - _ => None, - } - } - - #[inline] - #[cfg(feature = "wayland")] - fn set_wayland_theme(&self, theme: T) { - match self.window { - LinuxWindow::Wayland(ref w) => w.set_theme(theme), - #[cfg(feature = "x11")] - _ => {} - } - } - - #[inline] - fn is_ready(&self) -> bool { - true - } -} - -/// Additional methods on `WindowBuilder` that are specific to Unix. -pub trait WindowBuilderExtUnix { - #[cfg(feature = "x11")] - fn with_x11_visual(self, visual_infos: *const T) -> Self; - #[cfg(feature = "x11")] - fn with_x11_screen(self, screen_id: i32) -> Self; - - /// Build window with `WM_CLASS` hint; defaults to the name of the binary. Only relevant on X11. - #[cfg(feature = "x11")] - fn with_class(self, class: String, instance: String) -> Self; - /// Build window with override-redirect flag; defaults to false. Only relevant on X11. - #[cfg(feature = "x11")] - fn with_override_redirect(self, override_redirect: bool) -> Self; - /// Build window with `_NET_WM_WINDOW_TYPE` hints; defaults to `Normal`. Only relevant on X11. - #[cfg(feature = "x11")] - fn with_x11_window_type(self, x11_window_type: Vec) -> Self; - /// Build window with `_GTK_THEME_VARIANT` hint set to the specified value. Currently only relevant on X11. - #[cfg(feature = "x11")] - fn with_gtk_theme_variant(self, variant: String) -> Self; - /// Build window with resize increment hint. Only implemented on X11. - #[cfg(feature = "x11")] - fn with_resize_increments>(self, increments: S) -> Self; - /// Build window with base size hint. Only implemented on X11. - #[cfg(feature = "x11")] - fn with_base_size>(self, base_size: S) -> Self; - - /// Build window with a given application ID. It should match the `.desktop` file distributed with - /// your program. Only relevant on Wayland. - /// - /// For details about application ID conventions, see the - /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id) - #[cfg(feature = "wayland")] - fn with_app_id(self, app_id: String) -> Self; -} - -impl WindowBuilderExtUnix for WindowBuilder { - #[inline] - #[cfg(feature = "x11")] - fn with_x11_visual(mut self, visual_infos: *const T) -> Self { - { - self.platform_specific.visual_infos = - Some(unsafe { ptr::read(visual_infos as *const XVisualInfo) }); - } - self - } - - #[inline] - #[cfg(feature = "x11")] - fn with_x11_screen(mut self, screen_id: i32) -> Self { - self.platform_specific.screen_id = Some(screen_id); - self - } - - #[inline] - #[cfg(feature = "x11")] - fn with_class(mut self, instance: String, class: String) -> Self { - self.platform_specific.class = Some((instance, class)); - self - } - - #[inline] - #[cfg(feature = "x11")] - fn with_override_redirect(mut self, override_redirect: bool) -> Self { - self.platform_specific.override_redirect = override_redirect; - self - } - - #[inline] - #[cfg(feature = "x11")] - fn with_x11_window_type(mut self, x11_window_types: Vec) -> Self { - self.platform_specific.x11_window_types = x11_window_types; - self - } - - #[inline] - #[cfg(feature = "x11")] - fn with_gtk_theme_variant(mut self, variant: String) -> Self { - self.platform_specific.gtk_theme_variant = Some(variant); - self - } - - #[inline] - #[cfg(feature = "x11")] - fn with_resize_increments>(mut self, increments: S) -> Self { - self.platform_specific.resize_increments = Some(increments.into()); - self - } - - #[inline] - #[cfg(feature = "x11")] - fn with_base_size>(mut self, base_size: S) -> Self { - self.platform_specific.base_size = Some(base_size.into()); - self - } - - #[inline] - #[cfg(feature = "wayland")] - fn with_app_id(mut self, app_id: String) -> Self { - self.platform_specific.app_id = Some(app_id); - self - } -} - -/// Additional methods on `MonitorHandle` that are specific to Linux. -pub trait MonitorHandleExtUnix { - /// Returns the inner identifier of the monitor. - fn native_id(&self) -> u32; -} - -impl MonitorHandleExtUnix for MonitorHandle { - #[inline] - fn native_id(&self) -> u32 { - self.inner.native_identifier() - } -} - -/// A theme for a Wayland's client side decorations. -#[cfg(feature = "wayland")] -pub trait Theme: Send + 'static { - /// Title bar color. - fn element_color(&self, element: Element, window_active: bool) -> ARGBColor; - - /// Color for a given button part. - fn button_color( - &self, - button: Button, - state: ButtonState, - foreground: bool, - window_active: bool, - ) -> ARGBColor; - - /// Font name and the size for the title bar. - /// - /// By default the font is `sans-serif` at the size of 17. - /// - /// Returning `None` means that title won't be drawn. - fn font(&self) -> Option<(String, f32)> { - // Not having any title isn't something desirable for the users, so setting it to - // something generic. - Some((String::from("sans-serif"), 17.)) - } -} - -/// A button on Wayland's client side decorations. -#[cfg(feature = "wayland")] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Button { - /// Button that maximizes the window. - Maximize, - - /// Button that minimizes the window. - Minimize, - - /// Button that closes the window. - Close, -} - -/// A button state of the button on Wayland's client side decorations. -#[cfg(feature = "wayland")] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum ButtonState { - /// Button is being hovered over by pointer. - Hovered, - /// Button is not being hovered over by pointer. - Idle, - /// Button is disabled. - Disabled, -} - -#[cfg(feature = "wayland")] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Element { - /// Bar itself. - Bar, - - /// 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, -} diff --git a/src/platform_impl/linux/event_loop.rs b/src/platform_impl/linux/event_loop.rs new file mode 100644 index 00000000..ffb06902 --- /dev/null +++ b/src/platform_impl/linux/event_loop.rs @@ -0,0 +1,567 @@ +// Copyright 2019-2021 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use std::{ + cell::RefCell, + collections::{HashSet, VecDeque}, + error::Error, + process, + rc::Rc, + sync::mpsc::{channel, Receiver, SendError, Sender}, +}; + +use gdk::{Cursor, CursorType, WindowExt, WindowState}; +use gio::{prelude::*, Cancellable}; +use glib::{source::idle_add_local, Continue, MainContext}; +use gtk::{prelude::*, ApplicationWindow, Inhibit}; + +use crate::{ + dpi::{PhysicalPosition, PhysicalSize}, + event::{DeviceId as RootDeviceId, ElementState, Event, ModifiersState, MouseButton, StartCause, WindowEvent}, + event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, + monitor::MonitorHandle as RootMonitorHandle, + window::{CursorIcon, WindowId as RootWindowId}, +}; + +use super::{ + DeviceId, + window::{WindowId, WindowRequest}, + monitor::MonitorHandle +}; + +pub struct EventLoopWindowTarget { + /// Gtk application + pub(crate) app: gtk::Application, + /// Window Ids of the application + pub(crate) windows: Rc>>, + /// Window requests sender + pub(crate) window_requests_tx: Sender<(WindowId, WindowRequest)>, + /// Window requests receiver + pub(crate) window_requests_rx: Receiver<(WindowId, WindowRequest)>, + _marker: std::marker::PhantomData, +} + +impl EventLoopWindowTarget { + #[inline] + pub fn available_monitors(&self) -> VecDeque { + todo!() + } + + #[inline] + pub fn primary_monitor(&self) -> Option { + todo!() + } +} + +pub struct EventLoop { + /// Window target. + window_target: RootELW, + /// User event sender for EventLoopProxy + user_event_tx: Sender, + /// User event receiver + user_event_rx: Receiver, +} + +impl EventLoop { + pub fn new() -> EventLoop { + assert_is_main_thread("new_any_thread"); + EventLoop::new_gtk().expect("Failed to initialize any backend!") + } + + fn new_gtk() -> Result, Box> { + let app = gtk::Application::new(Some("org.tauri"), gio::ApplicationFlags::empty())?; + let cancellable: Option<&Cancellable> = None; + app.register(cancellable)?; + + // Create event loop window target. + let (window_requests_tx, window_requests_rx) = channel(); + let window_target = EventLoopWindowTarget { + app, + windows: Rc::new(RefCell::new(HashSet::new())), + window_requests_tx, + window_requests_rx, + _marker: std::marker::PhantomData, + }; + + // Create user event channel + let (user_event_tx, user_event_rx) = channel(); + + // Create event loop itself. + let event_loop = Self { + window_target: RootELW { + p: window_target, + _marker: std::marker::PhantomData, + }, + user_event_tx, + user_event_rx, + }; + + Ok(event_loop) + } + + #[inline] + pub fn run(self, callback: F) -> ! + where + F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow) + 'static, + { + self.run_return(callback); + process::exit(0) + } + + pub(crate) fn run_return(self, mut callback: F) + where + F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow) + 'static, + { + let mut control_flow = ControlFlow::default(); + let window_target = self.window_target; + let (event_tx, event_rx) = channel::>(); + + // Send StartCause::Init event + let tx_clone = event_tx.clone(); + window_target.p.app.connect_activate(move |_| { + if let Err(e) = tx_clone.send(Event::NewEvents(StartCause::Init)) { + log::warn!("Failed to send init event to event channel: {}", e); + } + }); + window_target.p.app.activate(); + + let context = MainContext::default(); + context.push_thread_default(); + let keep_running = Rc::new(RefCell::new(true)); + let keep_running_ = keep_running.clone(); + let user_event_rx = self.user_event_rx; + idle_add_local(move || { + // User event + if let Ok(event) = user_event_rx.try_recv() { + if let Err(e) = event_tx.send(Event::UserEvent(event)) { + log::warn!("Failed to send user event to event channel: {}", e); + } + } + + // Widnow Request + if let Ok((id, request)) = window_target.p.window_requests_rx.try_recv() { + let window = window_target + .p.app + .get_window_by_id(id.0) + .expect("Failed to send closed window event!"); + + match request { + WindowRequest::Title(title) => window.set_title(&title), + WindowRequest::Position((x, y)) => window.move_(x, y), + WindowRequest::Size((w, h)) => window.resize(w, h), + WindowRequest::MinSize((min_width, min_height)) => window + .set_geometry_hints::( + None, + Some(&gdk::Geometry { + min_width, + min_height, + max_width: 0, + max_height: 0, + base_width: 0, + base_height: 0, + width_inc: 0, + height_inc: 0, + min_aspect: 0f64, + max_aspect: 0f64, + win_gravity: gdk::Gravity::Center, + }), + gdk::WindowHints::MIN_SIZE, + ), + WindowRequest::MaxSize((max_width, max_height)) => window + .set_geometry_hints::( + None, + Some(&gdk::Geometry { + min_width: 0, + min_height: 0, + max_width, + max_height, + base_width: 0, + base_height: 0, + width_inc: 0, + height_inc: 0, + min_aspect: 0f64, + max_aspect: 0f64, + win_gravity: gdk::Gravity::Center, + }), + gdk::WindowHints::MAX_SIZE, + ), + WindowRequest::Visible(visible) => { + if visible { + window.show(); + } else { + window.hide(); + } + } + WindowRequest::Resizable(resizable) => window.set_resizable(resizable), + WindowRequest::Minimized(minimized) => { + if minimized { + window.iconify(); + } else { + window.deiconify(); + } + } + WindowRequest::Maximized(maximized) => { + if maximized { + window.maximize(); + } else { + window.unmaximize(); + } + } + WindowRequest::DragWindow => { + let display = window.get_display(); + if let Some(cursor) = display + .get_device_manager() + .and_then(|device_manager| device_manager.get_client_pointer()) + { + let (_, x, y) = cursor.get_position(); + window.begin_move_drag(1, x, y, 0); + } + } + WindowRequest::Fullscreen(fullscreen) => match fullscreen { + Some(_) => window.fullscreen(), + None => window.unfullscreen(), + }, + WindowRequest::Decorations(decorations) => window.set_decorated(decorations), + WindowRequest::AlwaysOnTop(always_on_top) => window.set_keep_above(always_on_top), + WindowRequest::WindowIcon(window_icon) => { + if let Some(icon) = window_icon { + window.set_icon(Some(&icon.inner.into())); + } + } + WindowRequest::UserAttention(request_type) => { + if request_type.is_some() { + window.set_urgency_hint(true) + } + } + WindowRequest::SkipTaskbar => window.set_skip_taskbar_hint(true), + WindowRequest::CursorIcon(cursor) => { + if let Some(gdk_window) = window.get_window() { + let display = window.get_display(); + match cursor { + Some(cr) => gdk_window.set_cursor( + Cursor::from_name( + &display, + match cr { + CursorIcon::Crosshair => "crosshair", + CursorIcon::Hand => "pointer", + CursorIcon::Arrow => "crosshair", + CursorIcon::Move => "move", + CursorIcon::Text => "text", + CursorIcon::Wait => "wait", + CursorIcon::Help => "help", + CursorIcon::Progress => "progress", + CursorIcon::NotAllowed => "not-allowed", + CursorIcon::ContextMenu => "context-menu", + CursorIcon::Cell => "cell", + CursorIcon::VerticalText => "vertical-text", + CursorIcon::Alias => "alias", + CursorIcon::Copy => "copy", + CursorIcon::NoDrop => "no-drop", + CursorIcon::Grab => "grab", + CursorIcon::Grabbing => "grabbing", + CursorIcon::AllScroll => "all-scroll", + CursorIcon::ZoomIn => "zoom-in", + CursorIcon::ZoomOut => "zoom-out", + CursorIcon::EResize => "e-resize", + CursorIcon::NResize => "n-resize", + CursorIcon::NeResize => "ne-resize", + CursorIcon::NwResize => "nw-resize", + CursorIcon::SResize => "s-resize", + CursorIcon::SeResize => "se-resize", + CursorIcon::SwResize => "sw-resize", + CursorIcon::WResize => "w-resize", + CursorIcon::EwResize => "ew-resize", + CursorIcon::NsResize => "ns-resize", + CursorIcon::NeswResize => "nesw-resize", + CursorIcon::NwseResize => "nwse-resize", + CursorIcon::ColResize => "col-resize", + CursorIcon::RowResize => "row-resize", + CursorIcon::Default => "default", + }, + ) + .as_ref(), + ), + None => gdk_window.set_cursor(Some(&Cursor::new_for_display( + &display, + CursorType::BlankCursor, + ))), + } + }; + } + WindowRequest::WireUpEvents => { + let windows_rc = window_target.p.windows.clone(); + let tx_clone = event_tx.clone(); + + window.connect_delete_event(move |_, _| { + windows_rc.borrow_mut().remove(&id); + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CloseRequested, + }) { + log::warn!("Failed to send window close event to event channel: {}", e); + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + window.connect_configure_event(move |_, event| { + let (x, y) = event.get_position(); + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Moved(PhysicalPosition::new(x, y)), + }) { + log::warn!("Failed to send window moved event to event channel: {}", e); + } + + let (w, h) = event.get_size(); + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Resized(PhysicalSize::new(w, h)), + }) { + log::warn!( + "Failed to send window resized event to event channel: {}", + e + ); + } + false + }); + + let tx_clone = event_tx.clone(); + window.connect_window_state_event(move |_window, event| { + let state = event.get_new_window_state(); + + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Focused(state.contains(WindowState::FOCUSED)), + }) { + log::warn!( + "Failed to send window focused event to event channel: {}", + e + ); + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + window.connect_destroy_event(move |_, _| { + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Destroyed, + }) { + log::warn!( + "Failed to send window destroyed event to event channel: {}", + e + ); + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + window.connect_enter_notify_event(move |_, _| { + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CursorEntered { + // FIXME: currently we use a dummy device id, find if we can get device id from gtk + device_id: RootDeviceId(DeviceId(0)), + }, + }) { + log::warn!( + "Failed to send cursor entered event to event channel: {}", + e + ); + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + window.connect_motion_notify_event(move |window, _| { + let display = window.get_display(); + if let Some(cursor) = display + .get_device_manager() + .and_then(|device_manager| device_manager.get_client_pointer()) + { + let (_, x, y) = cursor.get_position(); + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CursorMoved { + position: PhysicalPosition::new(x as f64, y as f64), + // FIXME: currently we use a dummy device id, find if we can get device id from gtk + device_id: RootDeviceId(DeviceId(0)), + // this field is depracted so it is fine to pass empty state + modifiers: ModifiersState::empty(), + }, + }) { + log::warn!("Failed to send cursor moved event to event channel: {}", e); + } + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + window.connect_leave_notify_event(move |_, _| { + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CursorLeft { + // FIXME: currently we use a dummy device id, find if we can get device id from gtk + device_id: RootDeviceId(DeviceId(0)), + }, + }) { + log::warn!("Failed to send cursor left event to event channel: {}", e); + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + window.connect_button_press_event(move |_, event| { + let button = event.get_button(); + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::MouseInput { + button: match button { + 1 => MouseButton::Left, + 2 => MouseButton::Middle, + 3 => MouseButton::Right, + _ => MouseButton::Other(button as u16), + }, + state: ElementState::Pressed, + // FIXME: currently we use a dummy device id, find if we can get device id from gtk + device_id: RootDeviceId(DeviceId(0)), + // this field is depracted so it is fine to pass empty state + modifiers: ModifiersState::empty(), + }, + }) { + log::warn!( + "Failed to send mouse input preseed event to event channel: {}", + e + ); + } + Inhibit(false) + }); + + let tx_clone = event_tx.clone(); + window.connect_button_release_event(move |_, event| { + let button = event.get_button(); + if let Err(e) = tx_clone.send(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::MouseInput { + button: match button { + 1 => MouseButton::Left, + 2 => MouseButton::Middle, + 3 => MouseButton::Right, + _ => MouseButton::Other(button as u16), + }, + state: ElementState::Released, + // FIXME: currently we use a dummy device id, find if we can get device id from gtk + device_id: RootDeviceId(DeviceId(0)), + // this field is depracted so it is fine to pass empty state + modifiers: ModifiersState::empty(), + }, + }) { + log::warn!( + "Failed to send mouse input released event to event channel: {}", + e + ); + } + Inhibit(false) + }); + } + WindowRequest::Redraw => window.queue_draw(), + } + } + + // Event control flow + match control_flow { + ControlFlow::Exit => { + keep_running_.replace(false); + Continue(false) + } + // TODO better control flow handling + _ => { + if let Ok(event) = event_rx.try_recv() { + callback(event, &window_target, &mut control_flow); + } else { + callback(Event::MainEventsCleared, &window_target, &mut control_flow); + } + Continue(true) + } + } + }); + context.pop_thread_default(); + + while *keep_running.borrow() { + gtk::main_iteration(); + } + } + + #[inline] + pub fn window_target(&self) -> &RootELW { + &self.window_target + } + + /// Creates an `EventLoopProxy` that can be used to dispatch user events to the main event loop. + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy { + user_event_tx: self.user_event_tx.clone(), + } + } +} + +/// Used to send custom events to `EventLoop`. +#[derive(Debug)] +pub struct EventLoopProxy { + user_event_tx: Sender, +} + +impl Clone for EventLoopProxy { + fn clone(&self) -> Self { + Self { + user_event_tx: self.user_event_tx.clone() + } + } +} + +impl EventLoopProxy { + /// Send an event to the `EventLoop` from which this proxy was created. This emits a + /// `UserEvent(event)` event in the event loop, where `event` is the value passed to this + /// function. + /// + /// Returns an `Err` if the associated `EventLoop` no longer exists. + pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { + self + .user_event_tx + .send(event) + .map_err(|SendError(error)| EventLoopClosed(error)) + } +} + +fn assert_is_main_thread(suggested_method: &str) { + if !is_main_thread() { + panic!( + "Initializing the event loop outside of the main thread is a significant \ + cross-platform compatibility hazard. If you really, absolutely need to create an \ + EventLoop on a different thread, please use the `EventLoopExtUnix::{}` function.", + suggested_method + ); + } +} + +#[cfg(target_os = "linux")] +fn is_main_thread() -> bool { + use libc::{c_long, getpid, syscall, SYS_gettid}; + + unsafe { syscall(SYS_gettid) == getpid() as c_long } +} + +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))] +fn is_main_thread() -> bool { + use libc::pthread_main_np; + + unsafe { pthread_main_np() == 1 } +} + +#[cfg(target_os = "netbsd")] +fn is_main_thread() -> bool { + std::thread::current().name() == Some("main") +} + diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 3bf2b047..358a6c16 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -6,772 +6,28 @@ target_os = "openbsd" ))] -#[cfg(all(not(feature = "x11"), not(feature = "wayland")))] -compile_error!("Please select a feature to build for unix: `x11`, `wayland`"); +mod event_loop; +mod monitor; +mod window; -#[cfg(feature = "wayland")] -use std::error::Error; -use std::{collections::VecDeque, env, fmt}; -#[cfg(feature = "x11")] -use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Arc}; - -#[cfg(feature = "x11")] -use parking_lot::Mutex; -use raw_window_handle::RawWindowHandle; - -#[cfg(feature = "x11")] -pub use self::x11::XNotSupported; -#[cfg(feature = "x11")] -use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, XConnection, XError}; -use crate::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - error::{ExternalError, NotSupportedError, OsError as RootOsError}, - event::Event, - event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, - icon::Icon, - monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, - window::{CursorIcon, Fullscreen, UserAttentionType, WindowAttributes}, -}; - -pub(crate) use crate::icon::RgbaIcon as PlatformIcon; - -#[cfg(feature = "wayland")] -pub mod wayland; -#[cfg(feature = "x11")] -pub mod x11; - -/// Environment variable specifying which backend should be used on unix platform. -/// -/// Legal values are x11 and wayland. If this variable is set only the named backend -/// will be tried by winit. If it is not set, winit will try to connect to a wayland connection, -/// and if it fails will fallback on x11. -/// -/// If this variable is set with any other value, winit will panic. -const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND"; - -#[derive(Clone)] -pub struct PlatformSpecificWindowBuilderAttributes { - #[cfg(feature = "x11")] - pub visual_infos: Option, - #[cfg(feature = "x11")] - pub screen_id: Option, - #[cfg(feature = "x11")] - pub resize_increments: Option, - #[cfg(feature = "x11")] - pub base_size: Option, - #[cfg(feature = "x11")] - pub class: Option<(String, String)>, - #[cfg(feature = "x11")] - pub override_redirect: bool, - #[cfg(feature = "x11")] - pub x11_window_types: Vec, - #[cfg(feature = "x11")] - pub gtk_theme_variant: Option, - #[cfg(feature = "wayland")] - pub app_id: Option, -} - -impl Default for PlatformSpecificWindowBuilderAttributes { - fn default() -> Self { - Self { - #[cfg(feature = "x11")] - visual_infos: None, - #[cfg(feature = "x11")] - screen_id: None, - #[cfg(feature = "x11")] - resize_increments: None, - #[cfg(feature = "x11")] - base_size: None, - #[cfg(feature = "x11")] - class: None, - #[cfg(feature = "x11")] - override_redirect: false, - #[cfg(feature = "x11")] - x11_window_types: vec![XWindowType::Normal], - #[cfg(feature = "x11")] - gtk_theme_variant: None, - #[cfg(feature = "wayland")] - app_id: None, - } - } -} - -#[cfg(feature = "x11")] -lazy_static! { - pub static ref X11_BACKEND: Mutex, XNotSupported>> = - Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)); -} +pub use window::{WindowId, Window, PlatformSpecificWindowBuilderAttributes, PlatformIcon}; +pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}; +pub use monitor::{MonitorHandle, VideoMode}; #[derive(Debug, Clone)] -pub enum OsError { - #[cfg(feature = "x11")] - XError(XError), - #[cfg(feature = "x11")] - XMisc(&'static str), - #[cfg(feature = "wayland")] - WaylandMisc(&'static str), -} +pub struct OsError; -impl fmt::Display for OsError { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match *self { - #[cfg(feature = "x11")] - OsError::XError(ref e) => _f.pad(&e.description), - #[cfg(feature = "x11")] - OsError::XMisc(ref e) => _f.pad(e), - #[cfg(feature = "wayland")] - OsError::WaylandMisc(ref e) => _f.pad(e), - } - } -} - -pub enum Window { - #[cfg(feature = "x11")] - X(x11::Window), - #[cfg(feature = "wayland")] - Wayland(wayland::Window), -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum WindowId { - #[cfg(feature = "x11")] - X(x11::WindowId), - #[cfg(feature = "wayland")] - Wayland(wayland::WindowId), -} - -impl WindowId { - pub unsafe fn dummy() -> Self { - #[cfg(feature = "wayland")] - return WindowId::Wayland(wayland::WindowId::dummy()); - #[cfg(all(not(feature = "wayland"), feature = "x11"))] - return WindowId::X(x11::WindowId::dummy()); +impl std::fmt::Display for OsError { + fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + Ok(()) } } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum DeviceId { - #[cfg(feature = "x11")] - X(x11::DeviceId), - #[cfg(feature = "wayland")] - Wayland(wayland::DeviceId), -} +pub struct DeviceId(usize); impl DeviceId { pub unsafe fn dummy() -> Self { - #[cfg(feature = "wayland")] - return DeviceId::Wayland(wayland::DeviceId::dummy()); - #[cfg(all(not(feature = "wayland"), feature = "x11"))] - return DeviceId::X(x11::DeviceId::dummy()); + Self(0) } } - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum MonitorHandle { - #[cfg(feature = "x11")] - X(x11::MonitorHandle), - #[cfg(feature = "wayland")] - Wayland(wayland::MonitorHandle), -} - -/// `x11_or_wayland!(match expr; Enum(foo) => foo.something())` -/// expands to the equivalent of -/// ```ignore -/// match self { -/// Enum::X(foo) => foo.something(), -/// Enum::Wayland(foo) => foo.something(), -/// } -/// ``` -/// The result can be converted to another enum by adding `; as AnotherEnum` -macro_rules! x11_or_wayland { - (match $what:expr; $enum:ident ( $($c1:tt)* ) => $x:expr; as $enum2:ident ) => { - match $what { - #[cfg(feature = "x11")] - $enum::X($($c1)*) => $enum2::X($x), - #[cfg(feature = "wayland")] - $enum::Wayland($($c1)*) => $enum2::Wayland($x), - } - }; - (match $what:expr; $enum:ident ( $($c1:tt)* ) => $x:expr) => { - match $what { - #[cfg(feature = "x11")] - $enum::X($($c1)*) => $x, - #[cfg(feature = "wayland")] - $enum::Wayland($($c1)*) => $x, - } - }; -} - -impl MonitorHandle { - #[inline] - pub fn name(&self) -> Option { - x11_or_wayland!(match self; MonitorHandle(m) => m.name()) - } - - #[inline] - pub fn native_identifier(&self) -> u32 { - x11_or_wayland!(match self; MonitorHandle(m) => m.native_identifier()) - } - - #[inline] - pub fn size(&self) -> PhysicalSize { - x11_or_wayland!(match self; MonitorHandle(m) => m.size()) - } - - #[inline] - pub fn position(&self) -> PhysicalPosition { - x11_or_wayland!(match self; MonitorHandle(m) => m.position()) - } - - #[inline] - pub fn scale_factor(&self) -> f64 { - x11_or_wayland!(match self; MonitorHandle(m) => m.scale_factor() as f64) - } - - #[inline] - pub fn video_modes(&self) -> Box> { - x11_or_wayland!(match self; MonitorHandle(m) => Box::new(m.video_modes())) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum VideoMode { - #[cfg(feature = "x11")] - X(x11::VideoMode), - #[cfg(feature = "wayland")] - Wayland(wayland::VideoMode), -} - -impl VideoMode { - #[inline] - pub fn size(&self) -> PhysicalSize { - x11_or_wayland!(match self; VideoMode(m) => m.size()) - } - - #[inline] - pub fn bit_depth(&self) -> u16 { - x11_or_wayland!(match self; VideoMode(m) => m.bit_depth()) - } - - #[inline] - pub fn refresh_rate(&self) -> u16 { - x11_or_wayland!(match self; VideoMode(m) => m.refresh_rate()) - } - - #[inline] - pub fn monitor(&self) -> RootMonitorHandle { - x11_or_wayland!(match self; VideoMode(m) => m.monitor()) - } -} - -impl Window { - #[inline] - pub fn new( - window_target: &EventLoopWindowTarget, - attribs: WindowAttributes, - pl_attribs: PlatformSpecificWindowBuilderAttributes, - ) -> Result { - match *window_target { - #[cfg(feature = "wayland")] - EventLoopWindowTarget::Wayland(ref window_target) => { - wayland::Window::new(window_target, attribs, pl_attribs).map(Window::Wayland) - } - #[cfg(feature = "x11")] - EventLoopWindowTarget::X(ref window_target) => { - x11::Window::new(window_target, attribs, pl_attribs).map(Window::X) - } - } - } - - #[inline] - pub fn id(&self) -> WindowId { - x11_or_wayland!(match self; Window(w) => w.id(); as WindowId) - } - - #[inline] - pub fn set_title(&self, title: &str) { - x11_or_wayland!(match self; Window(w) => w.set_title(title)); - } - - #[inline] - pub fn set_visible(&self, visible: bool) { - x11_or_wayland!(match self; Window(w) => w.set_visible(visible)) - } - - #[inline] - pub fn outer_position(&self) -> Result, NotSupportedError> { - x11_or_wayland!(match self; Window(w) => w.outer_position()) - } - - #[inline] - pub fn inner_position(&self) -> Result, NotSupportedError> { - x11_or_wayland!(match self; Window(w) => w.inner_position()) - } - - #[inline] - pub fn set_outer_position(&self, position: Position) { - x11_or_wayland!(match self; Window(w) => w.set_outer_position(position)) - } - - #[inline] - pub fn inner_size(&self) -> PhysicalSize { - x11_or_wayland!(match self; Window(w) => w.inner_size()) - } - - #[inline] - pub fn outer_size(&self) -> PhysicalSize { - x11_or_wayland!(match self; Window(w) => w.outer_size()) - } - - #[inline] - pub fn set_inner_size(&self, size: Size) { - x11_or_wayland!(match self; Window(w) => w.set_inner_size(size)) - } - - #[inline] - pub fn set_min_inner_size(&self, dimensions: Option) { - x11_or_wayland!(match self; Window(w) => w.set_min_inner_size(dimensions)) - } - - #[inline] - pub fn set_max_inner_size(&self, dimensions: Option) { - x11_or_wayland!(match self; Window(w) => w.set_max_inner_size(dimensions)) - } - - #[inline] - pub fn set_resizable(&self, resizable: bool) { - x11_or_wayland!(match self; Window(w) => w.set_resizable(resizable)) - } - - #[inline] - pub fn set_cursor_icon(&self, cursor: CursorIcon) { - x11_or_wayland!(match self; Window(w) => w.set_cursor_icon(cursor)) - } - - #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { - x11_or_wayland!(match self; Window(window) => window.set_cursor_grab(grab)) - } - - #[inline] - pub fn set_cursor_visible(&self, visible: bool) { - x11_or_wayland!(match self; Window(window) => window.set_cursor_visible(visible)) - } - - #[inline] - pub fn drag_window(&self) -> Result<(), ExternalError> { - x11_or_wayland!(match self; Window(window) => window.drag_window()) - } - - #[inline] - pub fn scale_factor(&self) -> f64 { - x11_or_wayland!(match self; Window(w) => w.scale_factor() as f64) - } - - #[inline] - pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> { - x11_or_wayland!(match self; Window(w) => w.set_cursor_position(position)) - } - - #[inline] - pub fn set_maximized(&self, maximized: bool) { - x11_or_wayland!(match self; Window(w) => w.set_maximized(maximized)) - } - - #[inline] - pub fn is_maximized(&self) -> bool { - // TODO: Not implemented - false - } - - #[inline] - pub fn set_minimized(&self, minimized: bool) { - x11_or_wayland!(match self; Window(w) => w.set_minimized(minimized)) - } - - #[inline] - pub fn fullscreen(&self) -> Option { - x11_or_wayland!(match self; Window(w) => w.fullscreen()) - } - - #[inline] - pub fn set_fullscreen(&self, monitor: Option) { - x11_or_wayland!(match self; Window(w) => w.set_fullscreen(monitor)) - } - - #[inline] - pub fn set_decorations(&self, decorations: bool) { - x11_or_wayland!(match self; Window(w) => w.set_decorations(decorations)) - } - - #[inline] - pub fn set_always_on_top(&self, _always_on_top: bool) { - match self { - #[cfg(feature = "x11")] - &Window::X(ref w) => w.set_always_on_top(_always_on_top), - #[cfg(feature = "wayland")] - _ => (), - } - } - - #[inline] - pub fn set_window_icon(&self, _window_icon: Option) { - match self { - #[cfg(feature = "x11")] - &Window::X(ref w) => w.set_window_icon(_window_icon), - #[cfg(feature = "wayland")] - _ => (), - } - } - - #[inline] - pub fn set_ime_position(&self, position: Position) { - x11_or_wayland!(match self; Window(w) => w.set_ime_position(position)) - } - - #[inline] - pub fn request_user_attention(&self, _request_type: Option) { - match self { - #[cfg(feature = "x11")] - &Window::X(ref w) => w.request_user_attention(_request_type), - #[cfg(feature = "wayland")] - _ => (), - } - } - - #[inline] - pub fn request_redraw(&self) { - x11_or_wayland!(match self; Window(w) => w.request_redraw()) - } - - #[inline] - pub fn current_monitor(&self) -> Option { - match self { - #[cfg(feature = "x11")] - &Window::X(ref window) => { - let current_monitor = MonitorHandle::X(window.current_monitor()); - Some(RootMonitorHandle { - inner: current_monitor, - }) - } - #[cfg(feature = "wayland")] - &Window::Wayland(ref window) => { - let current_monitor = MonitorHandle::Wayland(window.current_monitor()?); - Some(RootMonitorHandle { - inner: current_monitor, - }) - } - } - } - - #[inline] - pub fn available_monitors(&self) -> VecDeque { - match self { - #[cfg(feature = "x11")] - &Window::X(ref window) => window - .available_monitors() - .into_iter() - .map(MonitorHandle::X) - .collect(), - #[cfg(feature = "wayland")] - &Window::Wayland(ref window) => window - .available_monitors() - .into_iter() - .map(MonitorHandle::Wayland) - .collect(), - } - } - - #[inline] - pub fn primary_monitor(&self) -> Option { - match self { - #[cfg(feature = "x11")] - &Window::X(ref window) => { - let primary_monitor = MonitorHandle::X(window.primary_monitor()); - Some(RootMonitorHandle { - inner: primary_monitor, - }) - } - #[cfg(feature = "wayland")] - &Window::Wayland(ref window) => window.primary_monitor(), - } - } - - pub fn raw_window_handle(&self) -> RawWindowHandle { - match self { - #[cfg(feature = "x11")] - &Window::X(ref window) => RawWindowHandle::Xlib(window.raw_window_handle()), - #[cfg(feature = "wayland")] - &Window::Wayland(ref window) => RawWindowHandle::Wayland(window.raw_window_handle()), - } - } -} - -#[cfg(feature = "x11")] -unsafe extern "C" fn x_error_callback( - display: *mut x11::ffi::Display, - event: *mut x11::ffi::XErrorEvent, -) -> c_int { - let xconn_lock = X11_BACKEND.lock(); - if let Ok(ref xconn) = *xconn_lock { - // `assume_init` is safe here because the array consists of `MaybeUninit` values, - // which do not require initialization. - let mut buf: [MaybeUninit; 1024] = MaybeUninit::uninit().assume_init(); - (xconn.xlib.XGetErrorText)( - display, - (*event).error_code as c_int, - buf.as_mut_ptr() as *mut c_char, - buf.len() as c_int, - ); - let description = CStr::from_ptr(buf.as_ptr() as *const c_char).to_string_lossy(); - - let error = XError { - description: description.into_owned(), - error_code: (*event).error_code, - request_code: (*event).request_code, - minor_code: (*event).minor_code, - }; - - error!("X11 error: {:#?}", error); - - *xconn.latest_error.lock() = Some(error); - } - // Fun fact: this return value is completely ignored. - 0 -} - -pub enum EventLoop { - #[cfg(feature = "wayland")] - Wayland(wayland::EventLoop), - #[cfg(feature = "x11")] - X(x11::EventLoop), -} - -pub enum EventLoopProxy { - #[cfg(feature = "x11")] - X(x11::EventLoopProxy), - #[cfg(feature = "wayland")] - Wayland(wayland::EventLoopProxy), -} - -impl Clone for EventLoopProxy { - fn clone(&self) -> Self { - x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.clone(); as EventLoopProxy) - } -} - -impl EventLoop { - pub fn new() -> EventLoop { - assert_is_main_thread("new_any_thread"); - - EventLoop::new_any_thread() - } - - pub fn new_any_thread() -> EventLoop { - if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) { - match env_var.as_str() { - "x11" => { - // TODO: propagate - #[cfg(feature = "x11")] - return EventLoop::new_x11_any_thread() - .expect("Failed to initialize X11 backend"); - #[cfg(not(feature = "x11"))] - panic!("x11 feature is not enabled") - } - "wayland" => { - #[cfg(feature = "wayland")] - return EventLoop::new_wayland_any_thread() - .expect("Failed to initialize Wayland backend"); - #[cfg(not(feature = "wayland"))] - panic!("wayland feature is not enabled"); - } - _ => panic!( - "Unknown environment variable value for {}, try one of `x11`,`wayland`", - BACKEND_PREFERENCE_ENV_VAR, - ), - } - } - - #[cfg(feature = "wayland")] - let wayland_err = match EventLoop::new_wayland_any_thread() { - Ok(event_loop) => return event_loop, - Err(err) => err, - }; - - #[cfg(feature = "x11")] - let x11_err = match EventLoop::new_x11_any_thread() { - Ok(event_loop) => return event_loop, - Err(err) => err, - }; - - #[cfg(not(feature = "wayland"))] - let wayland_err = "backend disabled"; - #[cfg(not(feature = "x11"))] - let x11_err = "backend disabled"; - - panic!( - "Failed to initialize any backend! Wayland status: {:?} X11 status: {:?}", - wayland_err, x11_err, - ); - } - - #[cfg(feature = "wayland")] - pub fn new_wayland() -> Result, Box> { - assert_is_main_thread("new_wayland_any_thread"); - - EventLoop::new_wayland_any_thread() - } - - #[cfg(feature = "wayland")] - pub fn new_wayland_any_thread() -> Result, Box> { - wayland::EventLoop::new().map(EventLoop::Wayland) - } - - #[cfg(feature = "x11")] - pub fn new_x11() -> Result, XNotSupported> { - assert_is_main_thread("new_x11_any_thread"); - - EventLoop::new_x11_any_thread() - } - - #[cfg(feature = "x11")] - pub fn new_x11_any_thread() -> Result, XNotSupported> { - let xconn = match X11_BACKEND.lock().as_ref() { - Ok(xconn) => xconn.clone(), - Err(err) => return Err(err.clone()), - }; - - Ok(EventLoop::X(x11::EventLoop::new(xconn))) - } - - pub fn create_proxy(&self) -> EventLoopProxy { - x11_or_wayland!(match self; EventLoop(evlp) => evlp.create_proxy(); as EventLoopProxy) - } - - pub fn run_return(&mut self, callback: F) - where - F: FnMut(crate::event::Event<'_, T>, &RootELW, &mut ControlFlow), - { - x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_return(callback)) - } - - pub fn run(self, callback: F) -> ! - where - F: 'static + FnMut(crate::event::Event<'_, T>, &RootELW, &mut ControlFlow), - { - x11_or_wayland!(match self; EventLoop(evlp) => evlp.run(callback)) - } - - pub fn window_target(&self) -> &crate::event_loop::EventLoopWindowTarget { - x11_or_wayland!(match self; EventLoop(evl) => evl.window_target()) - } -} - -impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.send_event(event)) - } -} - -pub enum EventLoopWindowTarget { - #[cfg(feature = "wayland")] - Wayland(wayland::EventLoopWindowTarget), - #[cfg(feature = "x11")] - X(x11::EventLoopWindowTarget), -} - -impl EventLoopWindowTarget { - #[inline] - pub fn is_wayland(&self) -> bool { - match *self { - #[cfg(feature = "wayland")] - EventLoopWindowTarget::Wayland(_) => true, - #[cfg(feature = "x11")] - _ => false, - } - } - - #[inline] - pub fn available_monitors(&self) -> VecDeque { - match *self { - #[cfg(feature = "wayland")] - EventLoopWindowTarget::Wayland(ref evlp) => evlp - .available_monitors() - .into_iter() - .map(MonitorHandle::Wayland) - .collect(), - #[cfg(feature = "x11")] - EventLoopWindowTarget::X(ref evlp) => evlp - .x_connection() - .available_monitors() - .into_iter() - .map(MonitorHandle::X) - .collect(), - } - } - - #[inline] - pub fn primary_monitor(&self) -> Option { - match *self { - #[cfg(feature = "wayland")] - EventLoopWindowTarget::Wayland(ref evlp) => evlp.primary_monitor(), - #[cfg(feature = "x11")] - EventLoopWindowTarget::X(ref evlp) => { - let primary_monitor = MonitorHandle::X(evlp.x_connection().primary_monitor()); - Some(RootMonitorHandle { - inner: primary_monitor, - }) - } - } - } -} - -fn sticky_exit_callback( - evt: Event<'_, T>, - target: &RootELW, - control_flow: &mut ControlFlow, - callback: &mut F, -) where - F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), -{ - // make ControlFlow::Exit sticky by providing a dummy - // control flow reference if it is already Exit. - let mut dummy = ControlFlow::Exit; - let cf = if *control_flow == ControlFlow::Exit { - &mut dummy - } else { - control_flow - }; - // user callback - callback(evt, target, cf) -} - -fn assert_is_main_thread(suggested_method: &str) { - if !is_main_thread() { - panic!( - "Initializing the event loop outside of the main thread is a significant \ - cross-platform compatibility hazard. If you really, absolutely need to create an \ - EventLoop on a different thread, please use the `EventLoopExtUnix::{}` function.", - suggested_method - ); - } -} - -#[cfg(target_os = "linux")] -fn is_main_thread() -> bool { - use libc::{c_long, getpid, syscall, SYS_gettid}; - - unsafe { syscall(SYS_gettid) == getpid() as c_long } -} - -#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))] -fn is_main_thread() -> bool { - use libc::pthread_main_np; - - unsafe { pthread_main_np() == 1 } -} - -#[cfg(target_os = "netbsd")] -fn is_main_thread() -> bool { - std::thread::current().name() == Some("main") -} diff --git a/src/platform_impl/linux/monitor.rs b/src/platform_impl/linux/monitor.rs new file mode 100644 index 00000000..2c995852 --- /dev/null +++ b/src/platform_impl/linux/monitor.rs @@ -0,0 +1,60 @@ +use crate::{ + dpi::{PhysicalSize, PhysicalPosition}, + monitor::{ MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, +}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct MonitorHandle; + +impl MonitorHandle { + #[inline] + pub fn name(&self) -> Option { + todo!() + } + + #[inline] + pub fn size(&self) -> PhysicalSize { + todo!() + } + + #[inline] + pub fn position(&self) -> PhysicalPosition { + todo!() + } + + #[inline] + pub fn scale_factor(&self) -> f64 { + todo!() + } + + #[inline] + pub fn video_modes(&self) -> Box> { + todo!() + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct VideoMode; + +impl VideoMode { + #[inline] + pub fn size(&self) -> PhysicalSize { + todo!() + } + + #[inline] + pub fn bit_depth(&self) -> u16 { + todo!() + } + + #[inline] + pub fn refresh_rate(&self) -> u16 { + todo!() + } + + #[inline] + pub fn monitor(&self) -> RootMonitorHandle { + todo!() + } +} + diff --git a/src/platform_impl/linux/wayland/env.rs b/src/platform_impl/linux/wayland/env.rs deleted file mode 100644 index 1cb2745b..00000000 --- a/src/platform_impl/linux/wayland/env.rs +++ /dev/null @@ -1,149 +0,0 @@ -//! 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) -> Self { - let cursor_grab = env.get_global::().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, - - subcompositor: SimpleGlobal, - - shell: ShellHandler, - - relative_pointer_manager: SimpleGlobal, - - pointer_constraints: SimpleGlobal, - - text_input_manager: SimpleGlobal, - - decoration_manager: SimpleGlobal, -} - -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 { - self.shell.get_shell() - } -} - -impl SeatHandling for WinitEnv { - fn listen, &SeatData, DispatchData<'_>) + 'static>( - &mut self, - f: F, - ) -> SeatListener { - self.seats.listen(f) - } -} - -impl OutputHandling for WinitEnv { - fn listen) + 'static>( - &mut self, - f: F, - ) -> OutputStatusListener { - self.outputs.listen(f) - } -} diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs deleted file mode 100644 index 8c2d86a5..00000000 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ /dev/null @@ -1,535 +0,0 @@ -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 { - /// Wayland display. - pub display: Display, - - /// Environment to handle object creation, etc. - pub env: Environment, - - /// Event loop handle. - pub event_loop_handle: calloop::LoopHandle, - - /// Output manager. - pub output_manager: OutputManager, - - /// State that we share across callbacks. - pub state: RefCell, - - /// Wayland source. - pub wayland_source: Rc>, - - /// 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, -} - -pub struct EventLoop { - /// Event loop. - event_loop: calloop::EventLoop, - - /// Wayland display. - display: Display, - - /// Pending user events. - pending_user_events: Rc>>, - - /// Sender of user events. - user_events_sender: calloop::channel::Sender, - - /// Wayland source of events. - wayland_source: Rc>, - - /// Window target. - window_target: RootEventLoopWindowTarget, - - /// Output manager. - _seat_manager: SeatManager, -} - -impl EventLoop { - pub fn new() -> Result, Box> { - // 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::new(&display_proxy, &mut event_queue, WinitEnv::new())?; - - // Create event loop. - let event_loop = calloop::EventLoop::::new()?; - // Build windowing features. - let windowing_features = WindowingFeatures::new(&env); - - // Create a theme manager. - let compositor = env.require_global::(); - let shm = env.require_global::(); - 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(mut self, callback: F) -> ! - where - F: FnMut(Event<'_, T>, &RootEventLoopWindowTarget, &mut ControlFlow) + 'static, - { - self.run_return(callback); - process::exit(0) - } - - pub fn run_return(&mut self, mut callback: F) - where - F: FnMut(Event<'_, T>, &RootEventLoopWindowTarget, &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 { - EventLoopProxy::new(self.user_events_sender.clone()) - } - - #[inline] - pub fn window_target(&self) -> &RootEventLoopWindowTarget { - &self.window_target - } - - fn with_state 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>>( - &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) - } -} diff --git a/src/platform_impl/linux/wayland/event_loop/proxy.rs b/src/platform_impl/linux/wayland/event_loop/proxy.rs deleted file mode 100644 index dad64ef2..00000000 --- a/src/platform_impl/linux/wayland/event_loop/proxy.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! 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 { - user_events_sender: Sender, -} - -impl Clone for EventLoopProxy { - fn clone(&self) -> Self { - EventLoopProxy { - user_events_sender: self.user_events_sender.clone(), - } - } -} - -impl EventLoopProxy { - pub fn new(user_events_sender: Sender) -> Self { - Self { user_events_sender } - } - - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.user_events_sender - .send(event) - .map_err(|SendError(error)| EventLoopClosed(error)) - } -} diff --git a/src/platform_impl/linux/wayland/event_loop/sink.rs b/src/platform_impl/linux/wayland/event_loop/sink.rs deleted file mode 100644 index 303ab826..00000000 --- a/src/platform_impl/linux/wayland/event_loop/sink.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! 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>, -} - -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)), - }); - } -} diff --git a/src/platform_impl/linux/wayland/event_loop/state.rs b/src/platform_impl/linux/wayland/event_loop/state.rs deleted file mode 100644 index 7aad9ecc..00000000 --- a/src/platform_impl/linux/wayland/event_loop/state.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! 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, - - /// 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, -} diff --git a/src/platform_impl/linux/wayland/mod.rs b/src/platform_impl/linux/wayland/mod.rs deleted file mode 100644 index a63a6f1e..00000000 --- a/src/platform_impl/linux/wayland/mod.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" -))] - -use sctk::reexports::client::protocol::wl_surface::WlSurface; - -pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}; -pub use output::{MonitorHandle, VideoMode}; -pub use window::Window; - -mod env; -mod event_loop; -mod output; -mod seat; -mod window; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId; - -impl DeviceId { - pub unsafe fn dummy() -> Self { - DeviceId - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WindowId(usize); - -impl WindowId { - pub unsafe fn dummy() -> Self { - WindowId(0) - } -} - -#[inline] -fn make_wid(surface: &WlSurface) -> WindowId { - WindowId(surface.as_ref().c_ptr() as usize) -} diff --git a/src/platform_impl/linux/wayland/output.rs b/src/platform_impl/linux/wayland/output.rs deleted file mode 100644 index fc4d0873..00000000 --- a/src/platform_impl/linux/wayland/output.rs +++ /dev/null @@ -1,240 +0,0 @@ -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) -> 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>>, -} - -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 { - 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 { - 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(&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 { - 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 { - 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 { - 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 { - 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, - pub(crate) bit_depth: u16, - pub(crate) refresh_rate: u16, - pub(crate) monitor: MonitorHandle, -} - -impl VideoMode { - #[inline] - pub fn size(&self) -> PhysicalSize { - 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 EventLoopWindowTarget { - #[inline] - pub fn display(&self) -> &Display { - &self.display - } - - #[inline] - pub fn available_monitors(&self) -> VecDeque { - self.output_manager.handle.available_outputs() - } - - #[inline] - pub fn primary_monitor(&self) -> Option { - // There's no primary monitor on Wayland. - None - } -} diff --git a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs deleted file mode 100644 index 7c320973..00000000 --- a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs +++ /dev/null @@ -1,151 +0,0 @@ -//! 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); - } - } - } -} diff --git a/src/platform_impl/linux/wayland/seat/keyboard/keymap.rs b/src/platform_impl/linux/wayland/seat/keyboard/keymap.rs deleted file mode 100644 index 991afff2..00000000 --- a/src/platform_impl/linux/wayland/seat/keyboard/keymap.rs +++ /dev/null @@ -1,192 +0,0 @@ -//! Convert Wayland keys to winit keys. - -use crate::event::VirtualKeyCode; - -pub fn keysym_to_vkey(keysym: u32) -> Option { - 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), - keysyms::XKB_KEY_KP_Left => Some(VirtualKeyCode::Left), - keysyms::XKB_KEY_KP_Up => Some(VirtualKeyCode::Up), - keysyms::XKB_KEY_KP_Right => Some(VirtualKeyCode::Right), - keysyms::XKB_KEY_KP_Down => Some(VirtualKeyCode::Down), - // => 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, - } -} diff --git a/src/platform_impl/linux/wayland/seat/keyboard/mod.rs b/src/platform_impl/linux/wayland/seat/keyboard/mod.rs deleted file mode 100644 index 1362dcf7..00000000 --- a/src/platform_impl/linux/wayland/seat/keyboard/mod.rs +++ /dev/null @@ -1,105 +0,0 @@ -//! 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>, - - /// LoopHandle to drop `RepeatSource`, when dropping the keyboard. - pub loop_handle: LoopHandle, -} - -impl Keyboard { - pub fn new( - seat: &Attached, - loop_handle: LoopHandle, - modifiers_state: Rc>, - ) -> Option { - 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::().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, - - /// 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, - - /// Current state of modifiers keys. - modifiers_state: Rc>, -} - -impl KeyboardInner { - fn new(modifiers_state: Rc>) -> Self { - Self { - target_window_id: None, - pending_modifers_state: None, - modifiers_state, - } - } -} - -impl From 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 - } -} diff --git a/src/platform_impl/linux/wayland/seat/mod.rs b/src/platform_impl/linux/wayland/seat/mod.rs deleted file mode 100644 index 23098d08..00000000 --- a/src/platform_impl/linux/wayland/seat/mod.rs +++ /dev/null @@ -1,208 +0,0 @@ -//! 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, - loop_handle: LoopHandle, - theme_manager: ThemeManager, - ) -> Self { - let relative_pointer_manager = env.get_global::(); - let pointer_constraints = env.get_global::(); - let text_input_manager = env.get_global::(); - - 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, - - /// Loop handle. - loop_handle: LoopHandle, - - /// Relative pointer manager. - relative_pointer_manager: Option>, - - /// Pointer constraints. - pointer_constraints: Option>, - - /// Text input manager. - text_input_manager: Option>, - - /// A theme manager. - theme_manager: ThemeManager, -} - -impl SeatManagerInner { - fn new( - theme_manager: ThemeManager, - relative_pointer_manager: Option>, - pointer_constraints: Option>, - text_input_manager: Option>, - loop_handle: LoopHandle, - ) -> 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, 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, - - /// All pointers we're using on a seat. - pointer: Option, - - /// Touch handling. - touch: Option, - - /// Text input handling aka IME. - text_input: Option, - - /// 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>, -} - -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())), - } - } -} diff --git a/src/platform_impl/linux/wayland/seat/pointer/data.rs b/src/platform_impl/linux/wayland/seat/pointer/data.rs deleted file mode 100644 index 1da60d35..00000000 --- a/src/platform_impl/linux/wayland/seat/pointer/data.rs +++ /dev/null @@ -1,74 +0,0 @@ -//! 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, - - /// Current modifiers state. - /// - /// This refers a state of modifiers from `WlKeyboard` on - /// the given seat. - pub modifiers_state: Rc>, - - /// Pointer constraints. - pub pointer_constraints: Option>, - - pub confined_pointer: Rc>>, - - /// A latest event serial. - pub latest_serial: Rc>, - - /// The currently accumulated axis data on a pointer. - pub axis_data: AxisData, -} - -impl PointerData { - pub fn new( - confined_pointer: Rc>>, - pointer_constraints: Option>, - modifiers_state: Rc>, - ) -> 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, - } - } -} diff --git a/src/platform_impl/linux/wayland/seat/pointer/handlers.rs b/src/platform_impl/linux/wayland/seat/pointer/handlers.rs deleted file mode 100644 index 62b21143..00000000 --- a/src/platform_impl/linux/wayland/seat/pointer/handlers.rs +++ /dev/null @@ -1,305 +0,0 @@ -//! 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::client::protocol::wl_seat::WlSeat; -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}; - -// These values are comming from . -const BTN_LEFT: u32 = 0x110; -const BTN_RIGHT: u32 = 0x111; -const BTN_MIDDLE: u32 = 0x112; - -#[inline] -pub(super) fn handle_pointer( - pointer: ThemedPointer, - event: PointerEvent, - pointer_data: &Rc>, - winit_state: &mut WinitState, - seat: WlSeat, -) { - 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(), - seat, - }; - 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(), - seat, - }; - 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 { - BTN_LEFT => MouseButton::Left, - BTN_RIGHT => MouseButton::Right, - BTN_MIDDLE => MouseButton::Middle, - button => MouseButton::Other(button as u16), - }; - - 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) - } -} diff --git a/src/platform_impl/linux/wayland/seat/pointer/mod.rs b/src/platform_impl/linux/wayland/seat/pointer/mod.rs deleted file mode 100644 index 7ae1f251..00000000 --- a/src/platform_impl/linux/wayland/seat/pointer/mod.rs +++ /dev/null @@ -1,257 +0,0 @@ -//! 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 sctk::window::{ConceptFrame, Window}; - -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>, - - /// Cursor to handle confine requests. - confined_pointer: Weak>>, - - /// Latest observed serial in pointer events. - latest_serial: Rc>, - - /// Seat. - seat: WlSeat, -} - -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) { - 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(); - } - } - - pub fn drag_window(&self, window: &Window) { - window.start_interactive_move(&self.seat, self.latest_serial.get()); - } -} - -/// 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, - - /// Confined pointer. - confined_pointer: Rc>>, -} - -impl Pointers { - pub(super) fn new( - seat: &Attached, - theme_manager: &ThemeManager, - relative_pointer_manager: &Option>, - pointer_constraints: &Option>, - modifiers_state: Rc>, - ) -> 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_seat = seat.detach(); - let pointer = theme_manager.theme_pointer_with_impl( - seat, - move |event, pointer, mut dispatch_data| { - let winit_state = dispatch_data.get::().unwrap(); - handlers::handle_pointer( - pointer, - event, - &pointer_data, - winit_state, - pointer_seat.clone(), - ); - }, - ); - - // 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::().unwrap(); - handlers::handle_relative_pointer(event, winit_state); - }); - - relative_pointer.detach() -} - -pub(super) fn init_confined_pointer( - pointer_constraints: &Attached, - 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() -} diff --git a/src/platform_impl/linux/wayland/seat/text_input/handlers.rs b/src/platform_impl/linux/wayland/seat/text_input/handlers.rs deleted file mode 100644 index 4ba13d67..00000000 --- a/src/platform_impl/linux/wayland/seat/text_input/handlers.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! 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, - 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); - } - } - _ => (), - } -} diff --git a/src/platform_impl/linux/wayland/seat/text_input/mod.rs b/src/platform_impl/linux/wayland/seat/text_input/mod.rs deleted file mode 100644 index 77f4ff08..00000000 --- a/src/platform_impl/linux/wayland/seat/text_input/mod.rs +++ /dev/null @@ -1,66 +0,0 @@ -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, -} - -impl TextInput { - pub fn new(seat: &Attached, 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::().unwrap(); - handlers::handle_text_input(text_input, &mut text_input_inner, event, winit_state); - }); - - let text_input: Attached = 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, - - /// Pending string to commit. - commit_string: Option, -} - -impl TextInputInner { - fn new() -> Self { - Self { - target_window_id: None, - commit_string: None, - } - } -} diff --git a/src/platform_impl/linux/wayland/seat/touch/handlers.rs b/src/platform_impl/linux/wayland/seat/touch/handlers.rs deleted file mode 100644 index 8a17b393..00000000 --- a/src/platform_impl/linux/wayland/seat/touch/handlers.rs +++ /dev/null @@ -1,122 +0,0 @@ -//! 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, - ); - } - } - _ => (), - } -} diff --git a/src/platform_impl/linux/wayland/seat/touch/mod.rs b/src/platform_impl/linux/wayland/seat/touch/mod.rs deleted file mode 100644 index 197e9ac9..00000000 --- a/src/platform_impl/linux/wayland/seat/touch/mod.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! 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) -> 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::().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, -} - -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, - - /// Id. - id: i32, -} - -impl TouchPoint { - pub fn new(surface: WlSurface, position: LogicalPosition, id: i32) -> Self { - Self { - surface, - position, - id, - } - } -} diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs deleted file mode 100644 index 5f59651e..00000000 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ /dev/null @@ -1,668 +0,0 @@ -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>>, - - /// 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, - - /// Available windowing features. - windowing_features: WindowingFeatures, - - /// Requests that SCTK window should perform. - window_requests: Arc>>, -} - -impl Window { - pub fn new( - event_loop_window_target: &EventLoopWindowTarget, - attributes: WindowAttributes, - platform_attributes: PlatformAttributes, - ) -> Result { - let surface = event_loop_window_target - .env - .create_surface_with_scale_callback(move |scale, surface, mut dispatch_data| { - let winit_state = dispatch_data.get::().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::(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::( - surface.clone(), - Some(theme_manager), - (width, height), - move |event, mut dispatch_data| { - use sctk::window::{Event, State}; - - let winit_state = dispatch_data.get::().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::(scale_factor as f64).into()); - window.set_min_size(min_size); - - // Max dimensions. - let max_size = attributes - .max_inner_size - .map(|size| size.to_logical::(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, NotSupportedError> { - Err(NotSupportedError::new()) - } - - #[inline] - pub fn inner_position(&self) -> Result, NotSupportedError> { - Err(NotSupportedError::new()) - } - - #[inline] - pub fn set_outer_position(&self, _: Position) { - // Not possible on Wayland. - } - - pub fn inner_size(&self) -> PhysicalSize { - 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 { - 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::(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) { - let scale_factor = self.scale_factor() as f64; - let size = dimensions.map(|size| size.to_logical::(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) { - let scale_factor = self.scale_factor() as f64; - let size = dimensions.map(|size| size.to_logical::(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 { - 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) { - 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(&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 = [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 drag_window(&self) -> Result<(), ExternalError> { - let drag_window_request = WindowRequest::DragWindow; - self.window_requests - .lock() - .unwrap() - .push(drag_window_request); - self.event_loop_awakener.ping(); - - Ok(()) - } - - #[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 { - let output = sctk::get_surface_outputs(&self.surface).last()?.clone(); - Some(MonitorHandle::new(output)) - } - - #[inline] - pub fn available_monitors(&self) -> VecDeque { - self.output_manager_handle.available_outputs() - } - - #[inline] - pub fn primary_monitor(&self) -> Option { - 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 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(); - } -} diff --git a/src/platform_impl/linux/wayland/window/shim.rs b/src/platform_impl/linux/wayland/window/shim.rs deleted file mode 100644 index 9397559f..00000000 --- a/src/platform_impl/linux/wayland/window/shim.rs +++ /dev/null @@ -1,400 +0,0 @@ -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), - - /// Unset fullscreen. - UnsetFullscreen, - - /// Show cursor for the certain window or not. - ShowCursor(bool), - - /// Change the cursor icon. - NewCursorIcon(CursorIcon), - - /// Grab cursor. - GrabCursor(bool), - - /// Drag window. - DragWindow, - - /// 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>), - - /// Max size. - MaxSize(Option>), - - /// New frame size. - FrameSize(LogicalSize), - - /// Set IME window position. - IMEPosition(LogicalPosition), - - /// 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>, - - /// New scale factor. - pub scale_factor: Option, - - /// 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, - - /// The current size of the window. - pub size: Arc>>, - - /// A pending requests to SCTK window. - pub pending_window_requests: Arc>>, - - /// Current cursor icon. - pub cursor_icon: Cell, - - /// Visible cursor or not. - cursor_visible: Cell, - - /// Cursor confined to the surface. - confined: Cell, - - /// Pointers over the current surface. - pointers: Vec, - - /// Text inputs on the current surface. - text_inputs: Vec, -} - -impl WindowHandle { - pub fn new( - window: Window, - size: Arc>>, - pending_window_requests: Arc>>, - ) -> 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) { - // 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)); - } - } - - pub fn drag_window(&self) { - for pointer in self.pointers.iter() { - pointer.drag_window(&self.window); - } - } -} - -#[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 = 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::DragWindow => { - window_handle.drag_window(); - } - 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); - } -} diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs new file mode 100644 index 00000000..0a45acfa --- /dev/null +++ b/src/platform_impl/linux/window.rs @@ -0,0 +1,640 @@ +// Copyright 2019-2021 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use std::{ + cell::RefCell, + io, + rc::Rc, + sync::{ + atomic::{AtomicBool, AtomicI32, Ordering}, + mpsc::Sender, + }, + collections::VecDeque, +}; + +use gdk::{Cursor, EventMask, WindowEdge, WindowExt, WindowState}; +use gtk::{prelude::*, ApplicationWindow}; +use gdk_pixbuf::Pixbuf; + +use crate::{ + dpi::{PhysicalPosition, PhysicalSize, Position, Size}, + error::{ExternalError, NotSupportedError, OsError as RootOsError}, + icon::{Icon, BadIcon}, + monitor::MonitorHandle as RootMonitorHandle, + window::{CursorIcon, Fullscreen, UserAttentionType, WindowAttributes}, +}; + +use super::event_loop::EventLoopWindowTarget; +use super::monitor::MonitorHandle; + +#[derive(Clone, Default)] +pub struct PlatformSpecificWindowBuilderAttributes{} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct WindowId(pub(crate) u32); + +impl WindowId { + pub unsafe fn dummy() -> Self { + WindowId(0) + } +} + +/// An icon used for the window titlebar, taskbar, etc. +#[derive(Debug, Clone)] +pub struct PlatformIcon { + raw: Vec, + width: i32, + height: i32, + row_stride: i32, +} + +impl From for Pixbuf { + fn from(icon: PlatformIcon) -> Self { + Pixbuf::from_mut_slice( + icon.raw, + gdk_pixbuf::Colorspace::Rgb, + true, + 8, + icon.width, + icon.height, + icon.row_stride, + ) + } +} + +impl PlatformIcon { + /// Creates an `Icon` from 32bpp RGBA data. + /// + /// The length of `rgba` must be divisible by 4, and `width * height` must equal + /// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error. + pub fn from_rgba(rgba: Vec, width: u32, height: u32) -> Result { + let image = image::load_from_memory(&rgba) + .map_err(|_| { + BadIcon::OsError(io::Error::new( + io::ErrorKind::InvalidData, + "Invalid icon data!", + )) + })? + .into_rgba8(); + let row_stride = image.sample_layout().height_stride; + Ok(Self { + raw: image.into_raw(), + width: width as i32, + height: height as i32, + row_stride: row_stride as i32, + }) + } +} + +pub struct Window { + /// Window id. + pub(crate) window_id: WindowId, + /// Gtk application window. + pub(crate) window: gtk::ApplicationWindow, + /// Window requests sender + pub(crate) window_requests_tx: Sender<(WindowId, WindowRequest)>, + scale_factor: Rc, + position: Rc<(AtomicI32, AtomicI32)>, + size: Rc<(AtomicI32, AtomicI32)>, + maximized: Rc, + fullscreen: RefCell>, +} + +impl Window { + pub(crate) fn new( + event_loop_window_target: &EventLoopWindowTarget, + attributes: WindowAttributes, + _pl_attribs: PlatformSpecificWindowBuilderAttributes, + ) -> Result { + let app = &event_loop_window_target.app; + let window = gtk::ApplicationWindow::new(app); + let window_id = WindowId(window.get_id()); + event_loop_window_target + .windows + .borrow_mut() + .insert(window_id); + + // Set Width/Height & Resizable + let win_scale_factor = window.get_scale_factor(); + let (width, height) = attributes + .inner_size + .map(|size| size.to_logical::(win_scale_factor as f64).into()) + .unwrap_or((800, 600)); + window.set_resizable(attributes.resizable); + if attributes.resizable { + window.set_default_size(width, height); + } else { + window.set_size_request(width, height); + } + + // Set Min/Max Size + let geom_mask = (if attributes.min_inner_size.is_some() { + gdk::WindowHints::MIN_SIZE + } else { + gdk::WindowHints::empty() + }) | (if attributes.max_inner_size.is_some() { + gdk::WindowHints::MAX_SIZE + } else { + gdk::WindowHints::empty() + }); + let (min_width, min_height) = attributes + .min_inner_size + .map(|size| size.to_logical::(win_scale_factor as f64).into()) + .unwrap_or_default(); + let (max_width, max_height) = attributes + .max_inner_size + .map(|size| size.to_logical::(win_scale_factor as f64).into()) + .unwrap_or_default(); + window.set_geometry_hints::( + None, + Some(&gdk::Geometry { + min_width, + min_height, + max_width, + max_height, + base_width: 0, + base_height: 0, + width_inc: 0, + height_inc: 0, + min_aspect: 0f64, + max_aspect: 0f64, + win_gravity: gdk::Gravity::Center, + }), + geom_mask, + ); + + // Set Position + if let Some(position) = attributes.position { + let (x, y): (i32, i32) = position.to_physical::(win_scale_factor as f64).into(); + window.move_(x, y); + } + + // Set Transparent + if attributes.transparent { + if let Some(screen) = window.get_screen() { + if let Some(visual) = screen.get_rgba_visual() { + window.set_visual(Some(&visual)); + } + } + + window.connect_draw(|_, cr| { + cr.set_source_rgba(0., 0., 0., 0.); + cr.set_operator(cairo::Operator::Source); + cr.paint(); + cr.set_operator(cairo::Operator::Over); + Inhibit(false) + }); + window.set_app_paintable(true); + } + + // Rest attributes + window.set_title(&attributes.title); + if attributes.fullscreen.is_some() { + window.fullscreen(); + } + if attributes.maximized { + window.maximize(); + } + window.set_visible(attributes.visible); + window.set_decorated(attributes.decorations); + + if !attributes.decorations && attributes.resizable { + window.add_events(EventMask::POINTER_MOTION_MASK | EventMask::BUTTON_MOTION_MASK); + + window.connect_motion_notify_event(|window, event| { + if let Some(gdk_window) = window.get_window() { + let (cx, cy) = event.get_root(); + hit_test(&gdk_window, cx, cy); + } + Inhibit(false) + }); + + window.connect_button_press_event(|window, event| { + if event.get_button() == 1 { + if let Some(gdk_window) = window.get_window() { + let (cx, cy) = event.get_root(); + let result = hit_test(&gdk_window, cx, cy); + + // this check is necessary, otherwise the window won't recieve the click properly when resize isn't needed + if result != WindowEdge::__Unknown(8) { + window.begin_resize_drag(result, 1, cx as i32, cy as i32, event.get_time()); + } + } + } + Inhibit(false) + }); + } + + window.set_keep_above(attributes.always_on_top); + if let Some(icon) = attributes.window_icon { + window.set_icon(Some(&icon.inner.into())); + } + + window.show_all(); + + let window_requests_tx = event_loop_window_target.window_requests_tx.clone(); + + let w_pos = window.get_position(); + let position: Rc<(AtomicI32, AtomicI32)> = Rc::new((w_pos.0.into(), w_pos.1.into())); + let position_clone = position.clone(); + + let w_size = window.get_size(); + let size: Rc<(AtomicI32, AtomicI32)> = Rc::new((w_size.0.into(), w_size.1.into())); + let size_clone = size.clone(); + + window.connect_configure_event(move |_window, event| { + let (x, y) = event.get_position(); + position_clone.0.store(x, Ordering::Release); + position_clone.1.store(y, Ordering::Release); + + let (w, h) = event.get_size(); + size_clone.0.store(w as i32, Ordering::Release); + size_clone.1.store(h as i32, Ordering::Release); + + false + }); + + let w_max = window.get_property_is_maximized(); + let maximized: Rc = Rc::new(w_max.into()); + let max_clone = maximized.clone(); + + window.connect_window_state_event(move |_window, event| { + let state = event.get_new_window_state(); + max_clone.store(state.contains(WindowState::MAXIMIZED), Ordering::Release); + + Inhibit(false) + }); + + let scale_factor: Rc = Rc::new(win_scale_factor.into()); + let scale_factor_clone = scale_factor.clone(); + window.connect_property_scale_factor_notify(move |window| { + scale_factor_clone.store(window.get_scale_factor(), Ordering::Release); + }); + + if let Err(e) = window_requests_tx.send((window_id, WindowRequest::WireUpEvents)) { + log::warn!("Fail to send wire up events request: {}", e); + } + + window.queue_draw(); + Ok(Self { + window_id, + window, + window_requests_tx, + scale_factor, + position, + size, + maximized, + fullscreen: RefCell::new(attributes.fullscreen), + }) + } + + pub fn id(&self) -> WindowId { + self.window_id + } + + pub fn scale_factor(&self) -> f64 { + self.scale_factor.load(Ordering::Acquire) as f64 + } + + pub fn request_redraw(&self) { + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::Redraw)) + { + log::warn!("Fail to send redraw request: {}", e); + } + } + + pub fn inner_position(&self) -> Result, NotSupportedError> { + let (x, y) = &*self.position; + Ok(PhysicalPosition::new( + x.load(Ordering::Acquire), + y.load(Ordering::Acquire), + )) + } + + pub fn outer_position(&self) -> Result, NotSupportedError> { + let (x, y) = &*self.position; + Ok(PhysicalPosition::new( + x.load(Ordering::Acquire), + y.load(Ordering::Acquire), + )) + } + + pub fn set_outer_position>(&self, position: P) { + let (x, y): (i32, i32) = position + .into() + .to_physical::(self.scale_factor()) + .into(); + + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::Position((x, y)))) + { + log::warn!("Fail to send position request: {}", e); + } + } + + pub fn inner_size(&self) -> PhysicalSize { + let (width, height) = &*self.size; + + PhysicalSize::new( + width.load(Ordering::Acquire) as u32, + height.load(Ordering::Acquire) as u32, + ) + } + + pub fn set_inner_size>(&self, size: S) { + let (width, height) = size.into().to_logical::(self.scale_factor()).into(); + + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::Size((width, height)))) + { + log::warn!("Fail to send size request: {}", e); + } + } + + pub fn outer_size(&self) -> PhysicalSize { + let (width, height) = &*self.size; + + PhysicalSize::new( + width.load(Ordering::Acquire) as u32, + height.load(Ordering::Acquire) as u32, + ) + } + + pub fn set_min_inner_size>(&self, min_size: Option) { + if let Some(size) = min_size { + let (min_width, min_height) = size.into().to_logical::(self.scale_factor()).into(); + + if let Err(e) = self.window_requests_tx.send(( + self.window_id, + WindowRequest::MinSize((min_width, min_height)), + )) { + log::warn!("Fail to send min size request: {}", e); + } + } + } + pub fn set_max_inner_size>(&self, max_size: Option) { + if let Some(size) = max_size { + let (max_width, max_height) = size.into().to_logical::(self.scale_factor()).into(); + + if let Err(e) = self.window_requests_tx.send(( + self.window_id, + WindowRequest::MaxSize((max_width, max_height)), + )) { + log::warn!("Fail to send max size request: {}", e); + } + } + } + + pub fn set_title(&self, title: &str) { + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::Title(title.to_string()))) + { + log::warn!("Fail to send title request: {}", e); + } + } + + pub fn set_visible(&self, visible: bool) { + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::Visible(visible))) + { + log::warn!("Fail to send visible request: {}", e); + } + } + + pub fn set_resizable(&self, resizable: bool) { + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::Resizable(resizable))) + { + log::warn!("Fail to send resizable request: {}", e); + } + } + + pub fn set_minimized(&self, minimized: bool) { + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::Minimized(minimized))) + { + log::warn!("Fail to send minimized request: {}", e); + } + } + + pub fn set_maximized(&self, maximized: bool) { + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::Maximized(maximized))) + { + log::warn!("Fail to send maximized request: {}", e); + } + } + + pub fn is_maximized(&self) -> bool { + self.maximized.load(Ordering::Acquire) + } + + pub fn drag_window(&self) -> Result<(), ExternalError> { + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::DragWindow)) + { + log::warn!("Fail to send drag window request: {}", e); + } + Ok(()) + } + + pub fn set_fullscreen(&self, fullscreen: Option) { + self.fullscreen.replace(fullscreen.clone()); + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::Fullscreen(fullscreen))) + { + log::warn!("Fail to send fullscreen request: {}", e); + } + } + + pub fn fullscreen(&self) -> Option { + self.fullscreen.borrow().clone() + } + + pub fn set_decorations(&self, decorations: bool) { + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::Decorations(decorations))) + { + log::warn!("Fail to send decorations request: {}", e); + } + } + + pub fn set_always_on_top(&self, always_on_top: bool) { + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::AlwaysOnTop(always_on_top))) + { + log::warn!("Fail to send always on top request: {}", e); + } + } + + pub fn set_window_icon(&self, window_icon: Option) { + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::WindowIcon(window_icon))) + { + log::warn!("Fail to send window icon request: {}", e); + } + } + + pub fn set_ime_position>(&self, _position: P) { + todo!() + } + + pub fn request_user_attention(&self, request_type: Option) { + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::UserAttention(request_type))) + { + log::warn!("Fail to send user attention request: {}", e); + } + } + + pub fn set_cursor_icon(&self, cursor: CursorIcon) { + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::CursorIcon(Some(cursor)))) + { + log::warn!("Fail to send cursor icon request: {}", e); + } + } + + pub fn set_cursor_position>(&self, _position: P) -> Result<(), ExternalError> { + todo!() + } + + pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), ExternalError> { + todo!() + } + + pub fn set_cursor_visible(&self, visible: bool) { + let cursor = if visible { + Some(CursorIcon::Default) + } else { + None + }; + if let Err(e) = self + .window_requests_tx + .send((self.window_id, WindowRequest::CursorIcon(cursor))) + { + log::warn!("Fail to send cursor visibility request: {}", e); + } + } + + pub fn current_monitor(&self) -> Option { + todo!() + } + + #[inline] + pub fn available_monitors(&self) -> VecDeque { + todo!() + } + + pub fn primary_monitor(&self) -> Option { + todo!() + } + + pub fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { + todo!() + } +} + +// We need GtkWindow to initialize WebView, so we have to keep it in the field. +// It is called on any method. +unsafe impl Send for Window {} +unsafe impl Sync for Window {} + +pub enum WindowRequest { + Title(String), + Position((i32, i32)), + Size((i32, i32)), + MinSize((i32, i32)), + MaxSize((i32, i32)), + Visible(bool), + Resizable(bool), + Minimized(bool), + Maximized(bool), + DragWindow, + Fullscreen(Option), + Decorations(bool), + AlwaysOnTop(bool), + WindowIcon(Option), + UserAttention(Option), + SkipTaskbar, + CursorIcon(Option), + WireUpEvents, + Redraw, +} + +fn hit_test(window: &gdk::Window, cx: f64, cy: f64) -> WindowEdge { + let (left, top) = window.get_position(); + let (w, h) = (window.get_width(), window.get_height()); + let (right, bottom) = (left + w, top + h); + let (cx, cy) = (cx as i32, cy as i32); + + let fake_border = 5; // change this to manipulate how far inside the window, the resize can happen + + let display = window.get_display(); + + const LEFT: i32 = 0b00001; + const RIGHT: i32 = 0b0010; + const TOP: i32 = 0b0100; + const BOTTOM: i32 = 0b1000; + const TOPLEFT: i32 = TOP | LEFT; + const TOPRIGHT: i32 = TOP | RIGHT; + const BOTTOMLEFT: i32 = BOTTOM | LEFT; + const BOTTOMRIGHT: i32 = BOTTOM | RIGHT; + + let result = (LEFT * (if cx < (left + fake_border) { 1 } else { 0 })) + | (RIGHT * (if cx >= (right - fake_border) { 1 } else { 0 })) + | (TOP * (if cy < (top + fake_border) { 1 } else { 0 })) + | (BOTTOM * (if cy >= (bottom - fake_border) { 1 } else { 0 })); + + let edge = match result { + LEFT => WindowEdge::West, + TOP => WindowEdge::North, + RIGHT => WindowEdge::East, + BOTTOM => WindowEdge::South, + TOPLEFT => WindowEdge::NorthWest, + TOPRIGHT => WindowEdge::NorthEast, + BOTTOMLEFT => WindowEdge::SouthWest, + BOTTOMRIGHT => WindowEdge::SouthEast, + // has to be bigger than 7. otherwise it will match the number with a variant of WindowEdge enum and we don't want to do that + // also if the number ever change, makke sure to change it in the connect_button_press_event for window and webview + _ => WindowEdge::__Unknown(8), + }; + + // FIXME: calling `window.begin_resize_drag` seems to revert the cursor back to normal style + window.set_cursor( + Cursor::from_name( + &display, + match edge { + WindowEdge::North => "n-resize", + WindowEdge::South => "s-resize", + WindowEdge::East => "e-resize", + WindowEdge::West => "w-resize", + WindowEdge::NorthWest => "nw-resize", + WindowEdge::NorthEast => "ne-resize", + WindowEdge::SouthEast => "se-resize", + WindowEdge::SouthWest => "sw-resize", + _ => "default", + }, + ) + .as_ref(), + ); + + edge +} diff --git a/src/platform_impl/linux/x11/dnd.rs b/src/platform_impl/linux/x11/dnd.rs deleted file mode 100644 index 997228cd..00000000 --- a/src/platform_impl/linux/x11/dnd.rs +++ /dev/null @@ -1,219 +0,0 @@ -use std::{ - io, - os::raw::*, - path::{Path, PathBuf}, - str::Utf8Error, - sync::Arc, -}; - -use percent_encoding::percent_decode; - -use super::{ffi, util, XConnection, XError}; - -#[derive(Debug)] -pub struct DndAtoms { - pub aware: ffi::Atom, - pub enter: ffi::Atom, - pub leave: ffi::Atom, - pub drop: ffi::Atom, - pub position: ffi::Atom, - pub status: ffi::Atom, - pub action_private: ffi::Atom, - pub selection: ffi::Atom, - pub finished: ffi::Atom, - pub type_list: ffi::Atom, - pub uri_list: ffi::Atom, - pub none: ffi::Atom, -} - -impl DndAtoms { - pub fn new(xconn: &Arc) -> Result { - let names = [ - b"XdndAware\0".as_ptr() as *mut c_char, - b"XdndEnter\0".as_ptr() as *mut c_char, - b"XdndLeave\0".as_ptr() as *mut c_char, - b"XdndDrop\0".as_ptr() as *mut c_char, - b"XdndPosition\0".as_ptr() as *mut c_char, - b"XdndStatus\0".as_ptr() as *mut c_char, - b"XdndActionPrivate\0".as_ptr() as *mut c_char, - b"XdndSelection\0".as_ptr() as *mut c_char, - b"XdndFinished\0".as_ptr() as *mut c_char, - b"XdndTypeList\0".as_ptr() as *mut c_char, - b"text/uri-list\0".as_ptr() as *mut c_char, - b"None\0".as_ptr() as *mut c_char, - ]; - let atoms = unsafe { xconn.get_atoms(&names) }?; - Ok(DndAtoms { - aware: atoms[0], - enter: atoms[1], - leave: atoms[2], - drop: atoms[3], - position: atoms[4], - status: atoms[5], - action_private: atoms[6], - selection: atoms[7], - finished: atoms[8], - type_list: atoms[9], - uri_list: atoms[10], - none: atoms[11], - }) - } -} - -#[derive(Debug, Clone, Copy)] -pub enum DndState { - Accepted, - Rejected, -} - -#[derive(Debug)] -pub enum DndDataParseError { - EmptyData, - InvalidUtf8(Utf8Error), - HostnameSpecified(String), - UnexpectedProtocol(String), - UnresolvablePath(io::Error), -} - -impl From for DndDataParseError { - fn from(e: Utf8Error) -> Self { - DndDataParseError::InvalidUtf8(e) - } -} - -impl From for DndDataParseError { - fn from(e: io::Error) -> Self { - DndDataParseError::UnresolvablePath(e) - } -} - -pub struct Dnd { - xconn: Arc, - pub atoms: DndAtoms, - // Populated by XdndEnter event handler - pub version: Option, - pub type_list: Option>, - // Populated by XdndPosition event handler - pub source_window: Option, - // Populated by SelectionNotify event handler (triggered by XdndPosition event handler) - pub result: Option, DndDataParseError>>, -} - -impl Dnd { - pub fn new(xconn: Arc) -> Result { - let atoms = DndAtoms::new(&xconn)?; - Ok(Dnd { - xconn, - atoms, - version: None, - type_list: None, - source_window: None, - result: None, - }) - } - - pub fn reset(&mut self) { - self.version = None; - self.type_list = None; - self.source_window = None; - self.result = None; - } - - pub unsafe fn send_status( - &self, - this_window: c_ulong, - target_window: c_ulong, - state: DndState, - ) -> Result<(), XError> { - let (accepted, action) = match state { - DndState::Accepted => (1, self.atoms.action_private as c_long), - DndState::Rejected => (0, self.atoms.none as c_long), - }; - self.xconn - .send_client_msg( - target_window, - target_window, - self.atoms.status, - None, - [this_window as c_long, accepted, 0, 0, action], - ) - .flush() - } - - pub unsafe fn send_finished( - &self, - this_window: c_ulong, - target_window: c_ulong, - state: DndState, - ) -> Result<(), XError> { - let (accepted, action) = match state { - DndState::Accepted => (1, self.atoms.action_private as c_long), - DndState::Rejected => (0, self.atoms.none as c_long), - }; - self.xconn - .send_client_msg( - target_window, - target_window, - self.atoms.finished, - None, - [this_window as c_long, accepted, action, 0, 0], - ) - .flush() - } - - pub unsafe fn get_type_list( - &self, - source_window: c_ulong, - ) -> Result, util::GetPropertyError> { - self.xconn - .get_property(source_window, self.atoms.type_list, ffi::XA_ATOM) - } - - pub unsafe fn convert_selection(&self, window: c_ulong, time: c_ulong) { - (self.xconn.xlib.XConvertSelection)( - self.xconn.display, - self.atoms.selection, - self.atoms.uri_list, - self.atoms.selection, - window, - time, - ); - } - - pub unsafe fn read_data( - &self, - window: c_ulong, - ) -> Result, util::GetPropertyError> { - self.xconn - .get_property(window, self.atoms.selection, self.atoms.uri_list) - } - - pub fn parse_data(&self, data: &mut Vec) -> Result, DndDataParseError> { - if !data.is_empty() { - let mut path_list = Vec::new(); - let decoded = percent_decode(data).decode_utf8()?.into_owned(); - for uri in decoded.split("\r\n").filter(|u| !u.is_empty()) { - // The format is specified as protocol://host/path - // However, it's typically simply protocol:///path - let path_str = if uri.starts_with("file://") { - let path_str = uri.replace("file://", ""); - if !path_str.starts_with('/') { - // A hostname is specified - // Supporting this case is beyond the scope of my mental health - return Err(DndDataParseError::HostnameSpecified(path_str)); - } - path_str - } else { - // Only the file protocol is supported - return Err(DndDataParseError::UnexpectedProtocol(uri.to_owned())); - }; - - let path = Path::new(&path_str).canonicalize()?; - path_list.push(path); - } - Ok(path_list) - } else { - Err(DndDataParseError::EmptyData) - } - } -} diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs deleted file mode 100644 index 65ed4d9f..00000000 --- a/src/platform_impl/linux/x11/event_processor.rs +++ /dev/null @@ -1,1300 +0,0 @@ -use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc}; - -use libc::{c_char, c_int, c_long, c_uint, c_ulong}; - -use parking_lot::MutexGuard; - -use super::{ - events, ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd, - DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId, - XExtension, -}; - -use util::modifiers::{ModifierKeyState, ModifierKeymap}; - -use crate::{ - dpi::{PhysicalPosition, PhysicalSize}, - event::{ - DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, TouchPhase, WindowEvent, - }, - event_loop::EventLoopWindowTarget as RootELW, -}; - -/// The X11 documentation states: "Keycodes lie in the inclusive range [8,255]". -const KEYCODE_OFFSET: u8 = 8; - -pub(super) struct EventProcessor { - pub(super) dnd: Dnd, - pub(super) ime_receiver: ImeReceiver, - pub(super) randr_event_offset: c_int, - pub(super) devices: RefCell>, - pub(super) xi2ext: XExtension, - pub(super) target: Rc>, - pub(super) mod_keymap: ModifierKeymap, - pub(super) device_mod_state: ModifierKeyState, - // Number of touch events currently in progress - pub(super) num_touch: u32, - pub(super) first_touch: Option, - // Currently focused window belonging to this process - pub(super) active_window: Option, -} - -impl EventProcessor { - pub(super) fn init_device(&self, device: c_int) { - let wt = get_xtarget(&self.target); - let mut devices = self.devices.borrow_mut(); - if let Some(info) = DeviceInfo::get(&wt.xconn, device) { - for info in info.iter() { - devices.insert(DeviceId(info.deviceid), Device::new(&self, info)); - } - } - } - - fn with_window(&self, window_id: ffi::Window, callback: F) -> Option - where - F: Fn(&Arc) -> Ret, - { - let mut deleted = false; - let window_id = WindowId(window_id); - let wt = get_xtarget(&self.target); - let result = wt - .windows - .borrow() - .get(&window_id) - .and_then(|window| { - let arc = window.upgrade(); - deleted = arc.is_none(); - arc - }) - .map(|window| callback(&window)); - if deleted { - // Garbage collection - wt.windows.borrow_mut().remove(&window_id); - } - result - } - - fn window_exists(&self, window_id: ffi::Window) -> bool { - self.with_window(window_id, |_| ()).is_some() - } - - pub(super) fn poll(&self) -> bool { - let wt = get_xtarget(&self.target); - let result = unsafe { (wt.xconn.xlib.XPending)(wt.xconn.display) }; - - result != 0 - } - - pub(super) unsafe fn poll_one_event(&mut self, event_ptr: *mut ffi::XEvent) -> bool { - let wt = get_xtarget(&self.target); - // This function is used to poll and remove a single event - // from the Xlib event queue in a non-blocking, atomic way. - // XCheckIfEvent is non-blocking and removes events from queue. - // XNextEvent can't be used because it blocks while holding the - // global Xlib mutex. - // XPeekEvent does not remove events from the queue. - unsafe extern "C" fn predicate( - _display: *mut ffi::Display, - _event: *mut ffi::XEvent, - _arg: *mut c_char, - ) -> c_int { - // This predicate always returns "true" (1) to accept all events - 1 - } - - let result = (wt.xconn.xlib.XCheckIfEvent)( - wt.xconn.display, - event_ptr, - Some(predicate), - std::ptr::null_mut(), - ); - - result != 0 - } - - pub(super) fn process_event(&mut self, xev: &mut ffi::XEvent, mut callback: F) - where - F: FnMut(Event<'_, T>), - { - let wt = get_xtarget(&self.target); - // XFilterEvent tells us when an event has been discarded by the input method. - // Specifically, this involves all of the KeyPress events in compose/pre-edit sequences, - // along with an extra copy of the KeyRelease events. This also prevents backspace and - // arrow keys from being detected twice. - if ffi::True - == unsafe { - (wt.xconn.xlib.XFilterEvent)(xev, { - let xev: &ffi::XAnyEvent = xev.as_ref(); - xev.window - }) - } - { - return; - } - - // We can't call a `&mut self` method because of the above borrow, - // so we use this macro for repeated modifier state updates. - macro_rules! update_modifiers { - ( $state:expr , $modifier:expr ) => {{ - match ($state, $modifier) { - (state, modifier) => { - if let Some(modifiers) = - self.device_mod_state.update_state(&state, modifier) - { - if let Some(window_id) = self.active_window { - callback(Event::WindowEvent { - window_id: mkwid(window_id), - event: WindowEvent::ModifiersChanged(modifiers), - }); - } - } - } - } - }}; - } - - let event_type = xev.get_type(); - match event_type { - ffi::MappingNotify => { - let mapping: &ffi::XMappingEvent = xev.as_ref(); - - if mapping.request == ffi::MappingModifier - || mapping.request == ffi::MappingKeyboard - { - unsafe { - (wt.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut()); - } - wt.xconn - .check_errors() - .expect("Failed to call XRefreshKeyboardMapping"); - - self.mod_keymap.reset_from_x_connection(&wt.xconn); - self.device_mod_state.update_keymap(&self.mod_keymap); - } - } - - ffi::ClientMessage => { - let client_msg: &ffi::XClientMessageEvent = xev.as_ref(); - - let window = client_msg.window; - let window_id = mkwid(window); - - if client_msg.data.get_long(0) as ffi::Atom == wt.wm_delete_window { - callback(Event::WindowEvent { - window_id, - event: WindowEvent::CloseRequested, - }); - } else if client_msg.data.get_long(0) as ffi::Atom == wt.net_wm_ping { - let response_msg: &mut ffi::XClientMessageEvent = xev.as_mut(); - response_msg.window = wt.root; - wt.xconn - .send_event( - wt.root, - Some(ffi::SubstructureNotifyMask | ffi::SubstructureRedirectMask), - *response_msg, - ) - .queue(); - } else if client_msg.message_type == self.dnd.atoms.enter { - let source_window = client_msg.data.get_long(0) as c_ulong; - let flags = client_msg.data.get_long(1); - let version = flags >> 24; - self.dnd.version = Some(version); - let has_more_types = flags - (flags & (c_long::max_value() - 1)) == 1; - if !has_more_types { - let type_list = vec![ - client_msg.data.get_long(2) as c_ulong, - client_msg.data.get_long(3) as c_ulong, - client_msg.data.get_long(4) as c_ulong, - ]; - self.dnd.type_list = Some(type_list); - } else if let Ok(more_types) = unsafe { self.dnd.get_type_list(source_window) } - { - self.dnd.type_list = Some(more_types); - } - } else if client_msg.message_type == self.dnd.atoms.position { - // This event occurs every time the mouse moves while a file's being dragged - // over our window. We emit HoveredFile in response; while the macOS backend - // does that upon a drag entering, XDND doesn't have access to the actual drop - // data until this event. For parity with other platforms, we only emit - // `HoveredFile` the first time, though if winit's API is later extended to - // supply position updates with `HoveredFile` or another event, implementing - // that here would be trivial. - - let source_window = client_msg.data.get_long(0) as c_ulong; - - // Equivalent to `(x << shift) | y` - // where `shift = mem::size_of::() * 8` - // Note that coordinates are in "desktop space", not "window space" - // (in X11 parlance, they're root window coordinates) - //let packed_coordinates = client_msg.data.get_long(2); - //let shift = mem::size_of::() * 8; - //let x = packed_coordinates >> shift; - //let y = packed_coordinates & !(x << shift); - - // By our own state flow, `version` should never be `None` at this point. - let version = self.dnd.version.unwrap_or(5); - - // Action is specified in versions 2 and up, though we don't need it anyway. - //let action = client_msg.data.get_long(4); - - let accepted = if let Some(ref type_list) = self.dnd.type_list { - type_list.contains(&self.dnd.atoms.uri_list) - } else { - false - }; - - if accepted { - self.dnd.source_window = Some(source_window); - unsafe { - if self.dnd.result.is_none() { - let time = if version >= 1 { - client_msg.data.get_long(3) as c_ulong - } else { - // In version 0, time isn't specified - ffi::CurrentTime - }; - // This results in the `SelectionNotify` event below - self.dnd.convert_selection(window, time); - } - self.dnd - .send_status(window, source_window, DndState::Accepted) - .expect("Failed to send `XdndStatus` message."); - } - } else { - unsafe { - self.dnd - .send_status(window, source_window, DndState::Rejected) - .expect("Failed to send `XdndStatus` message."); - } - self.dnd.reset(); - } - } else if client_msg.message_type == self.dnd.atoms.drop { - let (source_window, state) = if let Some(source_window) = self.dnd.source_window - { - if let Some(Ok(ref path_list)) = self.dnd.result { - for path in path_list { - callback(Event::WindowEvent { - window_id, - event: WindowEvent::DroppedFile(path.clone()), - }); - } - } - (source_window, DndState::Accepted) - } else { - // `source_window` won't be part of our DND state if we already rejected the drop in our - // `XdndPosition` handler. - let source_window = client_msg.data.get_long(0) as c_ulong; - (source_window, DndState::Rejected) - }; - unsafe { - self.dnd - .send_finished(window, source_window, state) - .expect("Failed to send `XdndFinished` message."); - } - self.dnd.reset(); - } else if client_msg.message_type == self.dnd.atoms.leave { - self.dnd.reset(); - callback(Event::WindowEvent { - window_id, - event: WindowEvent::HoveredFileCancelled, - }); - } - } - - ffi::SelectionNotify => { - let xsel: &ffi::XSelectionEvent = xev.as_ref(); - - let window = xsel.requestor; - let window_id = mkwid(window); - - if xsel.property == self.dnd.atoms.selection { - let mut result = None; - - // This is where we receive data from drag and drop - if let Ok(mut data) = unsafe { self.dnd.read_data(window) } { - let parse_result = self.dnd.parse_data(&mut data); - if let Ok(ref path_list) = parse_result { - for path in path_list { - callback(Event::WindowEvent { - window_id, - event: WindowEvent::HoveredFile(path.clone()), - }); - } - } - result = Some(parse_result); - } - - self.dnd.result = result; - } - } - - ffi::ConfigureNotify => { - let xev: &ffi::XConfigureEvent = xev.as_ref(); - let xwindow = xev.window; - let window_id = mkwid(xwindow); - - if let Some(window) = self.with_window(xwindow, Arc::clone) { - // So apparently... - // `XSendEvent` (synthetic `ConfigureNotify`) -> position relative to root - // `XConfigureNotify` (real `ConfigureNotify`) -> position relative to parent - // https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.5 - // We don't want to send `Moved` when this is false, since then every `Resized` - // (whether the window moved or not) is accompanied by an extraneous `Moved` event - // that has a position relative to the parent window. - let is_synthetic = xev.send_event == ffi::True; - - // These are both in physical space. - let new_inner_size = (xev.width as u32, xev.height as u32); - let new_inner_position = (xev.x as i32, xev.y as i32); - - let mut shared_state_lock = window.shared_state.lock(); - - let (mut resized, moved) = { - let resized = - util::maybe_change(&mut shared_state_lock.size, new_inner_size); - let moved = if is_synthetic { - util::maybe_change( - &mut shared_state_lock.inner_position, - new_inner_position, - ) - } else { - // Detect when frame extents change. - // Since this isn't synthetic, as per the notes above, this position is relative to the - // parent window. - let rel_parent = new_inner_position; - if util::maybe_change( - &mut shared_state_lock.inner_position_rel_parent, - rel_parent, - ) { - // This ensures we process the next `Moved`. - shared_state_lock.inner_position = None; - // Extra insurance against stale frame extents. - shared_state_lock.frame_extents = None; - } - false - }; - (resized, moved) - }; - - let new_outer_position = if moved || shared_state_lock.position.is_none() { - // We need to convert client area position to window position. - let frame_extents = shared_state_lock - .frame_extents - .as_ref() - .cloned() - .unwrap_or_else(|| { - let frame_extents = - wt.xconn.get_frame_extents_heuristic(xwindow, wt.root); - shared_state_lock.frame_extents = Some(frame_extents.clone()); - frame_extents - }); - let outer = frame_extents - .inner_pos_to_outer(new_inner_position.0, new_inner_position.1); - shared_state_lock.position = Some(outer); - if moved { - // Temporarily unlock shared state to prevent deadlock - MutexGuard::unlocked(&mut shared_state_lock, || { - callback(Event::WindowEvent { - window_id, - event: WindowEvent::Moved(outer.into()), - }); - }); - } - outer - } else { - shared_state_lock.position.unwrap() - }; - - if is_synthetic { - // If we don't use the existing adjusted value when available, then the user can screw up the - // resizing by dragging across monitors *without* dropping the window. - let (width, height) = shared_state_lock - .dpi_adjusted - .unwrap_or_else(|| (xev.width as u32, xev.height as u32)); - - let last_scale_factor = shared_state_lock.last_monitor.scale_factor; - let new_scale_factor = { - let window_rect = util::AaRect::new(new_outer_position, new_inner_size); - let monitor = wt.xconn.get_monitor_for_window(Some(window_rect)); - - if monitor.is_dummy() { - // Avoid updating monitor using a dummy monitor handle - last_scale_factor - } else { - shared_state_lock.last_monitor = monitor.clone(); - monitor.scale_factor - } - }; - if last_scale_factor != new_scale_factor { - let (new_width, new_height) = window.adjust_for_dpi( - last_scale_factor, - new_scale_factor, - width, - height, - &shared_state_lock, - ); - - let old_inner_size = PhysicalSize::new(width, height); - let mut new_inner_size = PhysicalSize::new(new_width, new_height); - - // Temporarily unlock shared state to prevent deadlock - MutexGuard::unlocked(&mut shared_state_lock, || { - callback(Event::WindowEvent { - window_id, - event: WindowEvent::ScaleFactorChanged { - scale_factor: new_scale_factor, - new_inner_size: &mut new_inner_size, - }, - }); - }); - - if new_inner_size != old_inner_size { - window.set_inner_size_physical( - new_inner_size.width, - new_inner_size.height, - ); - shared_state_lock.dpi_adjusted = Some(new_inner_size.into()); - // if the DPI factor changed, force a resize event to ensure the logical - // size is computed with the right DPI factor - resized = true; - } - } - } - - // This is a hack to ensure that the DPI adjusted resize is actually applied on all WMs. KWin - // doesn't need this, but Xfwm does. The hack should not be run on other WMs, since tiling - // WMs constrain the window size, making the resize fail. This would cause an endless stream of - // XResizeWindow requests, making Xorg, the winit client, and the WM consume 100% of CPU. - if let Some(adjusted_size) = shared_state_lock.dpi_adjusted { - if new_inner_size == adjusted_size || !util::wm_name_is_one_of(&["Xfwm4"]) { - // When this finally happens, the event will not be synthetic. - shared_state_lock.dpi_adjusted = None; - } else { - window.set_inner_size_physical(adjusted_size.0, adjusted_size.1); - } - } - - if resized { - // Drop the shared state lock to prevent deadlock - drop(shared_state_lock); - - callback(Event::WindowEvent { - window_id, - event: WindowEvent::Resized(new_inner_size.into()), - }); - } - } - } - - ffi::ReparentNotify => { - let xev: &ffi::XReparentEvent = xev.as_ref(); - - // This is generally a reliable way to detect when the window manager's been - // replaced, though this event is only fired by reparenting window managers - // (which is almost all of them). Failing to correctly update WM info doesn't - // really have much impact, since on the WMs affected (xmonad, dwm, etc.) the only - // effect is that we waste some time trying to query unsupported properties. - wt.xconn.update_cached_wm_info(wt.root); - - self.with_window(xev.window, |window| { - window.invalidate_cached_frame_extents(); - }); - } - - ffi::DestroyNotify => { - let xev: &ffi::XDestroyWindowEvent = xev.as_ref(); - - let window = xev.window; - let window_id = mkwid(window); - - // In the event that the window's been destroyed without being dropped first, we - // cleanup again here. - wt.windows.borrow_mut().remove(&WindowId(window)); - - // Since all XIM stuff needs to happen from the same thread, we destroy the input - // context here instead of when dropping the window. - wt.ime - .borrow_mut() - .remove_context(window) - .expect("Failed to destroy input context"); - - callback(Event::WindowEvent { - window_id, - event: WindowEvent::Destroyed, - }); - } - - ffi::VisibilityNotify => { - let xev: &ffi::XVisibilityEvent = xev.as_ref(); - let xwindow = xev.window; - - self.with_window(xwindow, |window| window.visibility_notify()); - } - - ffi::Expose => { - let xev: &ffi::XExposeEvent = xev.as_ref(); - - // Multiple Expose events may be received for subareas of a window. - // We issue `RedrawRequested` only for the last event of such a series. - if xev.count == 0 { - let window = xev.window; - let window_id = mkwid(window); - - callback(Event::RedrawRequested(window_id)); - } - } - - ffi::KeyPress | ffi::KeyRelease => { - use crate::event::ElementState::{Pressed, Released}; - - // Note that in compose/pre-edit sequences, this will always be Released. - let state = if xev.get_type() == ffi::KeyPress { - Pressed - } else { - Released - }; - - let xkev: &mut ffi::XKeyEvent = xev.as_mut(); - - let window = xkev.window; - let window_id = mkwid(window); - - // Standard virtual core keyboard ID. XInput2 needs to be used to get a reliable - // value, though this should only be an issue under multiseat configurations. - let device = util::VIRTUAL_CORE_KEYBOARD; - let device_id = mkdid(device); - let keycode = xkev.keycode; - - // When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with - // a keycode of 0. - if keycode != 0 { - let scancode = keycode - KEYCODE_OFFSET as u32; - let keysym = wt.xconn.lookup_keysym(xkev); - let virtual_keycode = events::keysym_to_element(keysym as c_uint); - - update_modifiers!( - ModifiersState::from_x11_mask(xkev.state), - self.mod_keymap.get_modifier(xkev.keycode as ffi::KeyCode) - ); - - let modifiers = self.device_mod_state.modifiers(); - - #[allow(deprecated)] - callback(Event::WindowEvent { - window_id, - event: WindowEvent::KeyboardInput { - device_id, - input: KeyboardInput { - state, - scancode, - virtual_keycode, - modifiers, - }, - is_synthetic: false, - }, - }); - } - - if state == Pressed { - let written = if let Some(ic) = wt.ime.borrow().get_context(window) { - wt.xconn.lookup_utf8(ic, xkev) - } else { - return; - }; - - for chr in written.chars() { - let event = Event::WindowEvent { - window_id, - event: WindowEvent::ReceivedCharacter(chr), - }; - callback(event); - } - } - } - - ffi::GenericEvent => { - let guard = if let Some(e) = GenericEventCookie::from_event(&wt.xconn, *xev) { - e - } else { - return; - }; - let xev = &guard.cookie; - if self.xi2ext.opcode != xev.extension { - return; - } - - use crate::event::{ - ElementState::{Pressed, Released}, - MouseButton::{Left, Middle, Other, Right}, - MouseScrollDelta::LineDelta, - Touch, - WindowEvent::{ - AxisMotion, CursorEntered, CursorLeft, CursorMoved, Focused, MouseInput, - MouseWheel, - }, - }; - - match xev.evtype { - ffi::XI_ButtonPress | ffi::XI_ButtonRelease => { - let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; - let window_id = mkwid(xev.event); - let device_id = mkdid(xev.deviceid); - if (xev.flags & ffi::XIPointerEmulated) != 0 { - // Deliver multi-touch events instead of emulated mouse events. - return; - } - - let modifiers = ModifiersState::from_x11(&xev.mods); - update_modifiers!(modifiers, None); - - let state = if xev.evtype == ffi::XI_ButtonPress { - Pressed - } else { - Released - }; - match xev.detail as u32 { - ffi::Button1 => callback(Event::WindowEvent { - window_id, - event: MouseInput { - device_id, - state, - button: Left, - modifiers, - }, - }), - ffi::Button2 => callback(Event::WindowEvent { - window_id, - event: MouseInput { - device_id, - state, - button: Middle, - modifiers, - }, - }), - ffi::Button3 => callback(Event::WindowEvent { - window_id, - event: MouseInput { - device_id, - state, - button: Right, - modifiers, - }, - }), - - // Suppress emulated scroll wheel clicks, since we handle the real motion events for those. - // In practice, even clicky scroll wheels appear to be reported by evdev (and XInput2 in - // turn) as axis motion, so we don't otherwise special-case these button presses. - 4 | 5 | 6 | 7 => { - if xev.flags & ffi::XIPointerEmulated == 0 { - callback(Event::WindowEvent { - window_id, - event: MouseWheel { - device_id, - delta: match xev.detail { - 4 => LineDelta(0.0, 1.0), - 5 => LineDelta(0.0, -1.0), - 6 => LineDelta(-1.0, 0.0), - 7 => LineDelta(1.0, 0.0), - _ => unreachable!(), - }, - phase: TouchPhase::Moved, - modifiers, - }, - }); - } - } - - x => callback(Event::WindowEvent { - window_id, - event: MouseInput { - device_id, - state, - button: Other(x as u16), - modifiers, - }, - }), - } - } - ffi::XI_Motion => { - let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; - let device_id = mkdid(xev.deviceid); - let window_id = mkwid(xev.event); - let new_cursor_pos = (xev.event_x, xev.event_y); - - let modifiers = ModifiersState::from_x11(&xev.mods); - update_modifiers!(modifiers, None); - - let cursor_moved = self.with_window(xev.event, |window| { - let mut shared_state_lock = window.shared_state.lock(); - util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos) - }); - if cursor_moved == Some(true) { - let position = PhysicalPosition::new(xev.event_x, xev.event_y); - - callback(Event::WindowEvent { - window_id, - event: CursorMoved { - device_id, - position, - modifiers, - }, - }); - } else if cursor_moved.is_none() { - return; - } - - // More gymnastics, for self.devices - let mut events = Vec::new(); - { - let mask = unsafe { - slice::from_raw_parts( - xev.valuators.mask, - xev.valuators.mask_len as usize, - ) - }; - let mut devices = self.devices.borrow_mut(); - let physical_device = match devices.get_mut(&DeviceId(xev.sourceid)) { - Some(device) => device, - None => return, - }; - - let mut value = xev.valuators.values; - for i in 0..xev.valuators.mask_len * 8 { - if ffi::XIMaskIsSet(mask, i) { - let x = unsafe { *value }; - if let Some(&mut (_, ref mut info)) = physical_device - .scroll_axes - .iter_mut() - .find(|&&mut (axis, _)| axis == i) - { - let delta = (x - info.position) / info.increment; - info.position = x; - events.push(Event::WindowEvent { - window_id, - event: MouseWheel { - device_id, - delta: match info.orientation { - ScrollOrientation::Horizontal => { - LineDelta(delta as f32, 0.0) - } - // X11 vertical scroll coordinates are opposite to winit's - ScrollOrientation::Vertical => { - LineDelta(0.0, -delta as f32) - } - }, - phase: TouchPhase::Moved, - modifiers, - }, - }); - } else { - events.push(Event::WindowEvent { - window_id, - event: AxisMotion { - device_id, - axis: i as u32, - value: unsafe { *value }, - }, - }); - } - value = unsafe { value.offset(1) }; - } - } - } - for event in events { - callback(event); - } - } - - ffi::XI_Enter => { - let xev: &ffi::XIEnterEvent = unsafe { &*(xev.data as *const _) }; - - let window_id = mkwid(xev.event); - let device_id = mkdid(xev.deviceid); - - if let Some(all_info) = DeviceInfo::get(&wt.xconn, ffi::XIAllDevices) { - let mut devices = self.devices.borrow_mut(); - for device_info in all_info.iter() { - if device_info.deviceid == xev.sourceid - // This is needed for resetting to work correctly on i3, and - // presumably some other WMs. On those, `XI_Enter` doesn't include - // the physical device ID, so both `sourceid` and `deviceid` are - // the virtual device. - || device_info.attachment == xev.sourceid - { - let device_id = DeviceId(device_info.deviceid); - if let Some(device) = devices.get_mut(&device_id) { - device.reset_scroll_position(device_info); - } - } - } - } - - if self.window_exists(xev.event) { - callback(Event::WindowEvent { - window_id, - event: CursorEntered { device_id }, - }); - - let position = PhysicalPosition::new(xev.event_x, xev.event_y); - - // The mods field on this event isn't actually populated, so query the - // pointer device. In the future, we can likely remove this round-trip by - // relying on `Xkb` for modifier values. - // - // This needs to only be done after confirming the window still exists, - // since otherwise we risk getting a `BadWindow` error if the window was - // dropped with queued events. - let modifiers = wt - .xconn - .query_pointer(xev.event, xev.deviceid) - .expect("Failed to query pointer device") - .get_modifier_state(); - - callback(Event::WindowEvent { - window_id, - event: CursorMoved { - device_id, - position, - modifiers, - }, - }); - } - } - ffi::XI_Leave => { - let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) }; - - // Leave, FocusIn, and FocusOut can be received by a window that's already - // been destroyed, which the user presumably doesn't want to deal with. - let window_closed = !self.window_exists(xev.event); - if !window_closed { - callback(Event::WindowEvent { - window_id: mkwid(xev.event), - event: CursorLeft { - device_id: mkdid(xev.deviceid), - }, - }); - } - } - ffi::XI_FocusIn => { - let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) }; - - wt.ime - .borrow_mut() - .focus(xev.event) - .expect("Failed to focus input context"); - - let modifiers = ModifiersState::from_x11(&xev.mods); - - self.device_mod_state.update_state(&modifiers, None); - - if self.active_window != Some(xev.event) { - self.active_window = Some(xev.event); - - let window_id = mkwid(xev.event); - let position = PhysicalPosition::new(xev.event_x, xev.event_y); - - callback(Event::WindowEvent { - window_id, - event: Focused(true), - }); - - if !modifiers.is_empty() { - callback(Event::WindowEvent { - window_id, - event: WindowEvent::ModifiersChanged(modifiers), - }); - } - - // The deviceid for this event is for a keyboard instead of a pointer, - // so we have to do a little extra work. - let pointer_id = self - .devices - .borrow() - .get(&DeviceId(xev.deviceid)) - .map(|device| device.attachment) - .unwrap_or(2); - - callback(Event::WindowEvent { - window_id, - event: CursorMoved { - device_id: mkdid(pointer_id), - position, - modifiers, - }, - }); - - // Issue key press events for all pressed keys - Self::handle_pressed_keys( - &wt, - window_id, - ElementState::Pressed, - &self.mod_keymap, - &mut self.device_mod_state, - &mut callback, - ); - } - } - ffi::XI_FocusOut => { - let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) }; - if !self.window_exists(xev.event) { - return; - } - wt.ime - .borrow_mut() - .unfocus(xev.event) - .expect("Failed to unfocus input context"); - - if self.active_window.take() == Some(xev.event) { - let window_id = mkwid(xev.event); - - // Issue key release events for all pressed keys - Self::handle_pressed_keys( - &wt, - window_id, - ElementState::Released, - &self.mod_keymap, - &mut self.device_mod_state, - &mut callback, - ); - - callback(Event::WindowEvent { - window_id, - event: WindowEvent::ModifiersChanged(ModifiersState::empty()), - }); - - callback(Event::WindowEvent { - window_id, - event: Focused(false), - }) - } - } - - ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => { - let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; - let window_id = mkwid(xev.event); - let phase = match xev.evtype { - ffi::XI_TouchBegin => TouchPhase::Started, - ffi::XI_TouchUpdate => TouchPhase::Moved, - ffi::XI_TouchEnd => TouchPhase::Ended, - _ => unreachable!(), - }; - if self.window_exists(xev.event) { - let id = xev.detail as u64; - let modifiers = self.device_mod_state.modifiers(); - let location = - PhysicalPosition::new(xev.event_x as f64, xev.event_y as f64); - - // Mouse cursor position changes when touch events are received. - // Only the first concurrently active touch ID moves the mouse cursor. - if is_first_touch(&mut self.first_touch, &mut self.num_touch, id, phase) - { - callback(Event::WindowEvent { - window_id, - event: WindowEvent::CursorMoved { - device_id: mkdid(util::VIRTUAL_CORE_POINTER), - position: location.cast(), - modifiers, - }, - }); - } - - callback(Event::WindowEvent { - window_id, - event: WindowEvent::Touch(Touch { - device_id: mkdid(xev.deviceid), - phase, - location, - force: None, // TODO - id, - }), - }) - } - } - - ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => { - let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; - if xev.flags & ffi::XIPointerEmulated == 0 { - callback(Event::DeviceEvent { - device_id: mkdid(xev.deviceid), - event: DeviceEvent::Button { - button: xev.detail as u32, - state: match xev.evtype { - ffi::XI_RawButtonPress => Pressed, - ffi::XI_RawButtonRelease => Released, - _ => unreachable!(), - }, - }, - }); - } - } - - ffi::XI_RawMotion => { - let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; - let did = mkdid(xev.deviceid); - - let mask = unsafe { - slice::from_raw_parts( - xev.valuators.mask, - xev.valuators.mask_len as usize, - ) - }; - let mut value = xev.raw_values; - let mut mouse_delta = (0.0, 0.0); - let mut scroll_delta = (0.0, 0.0); - for i in 0..xev.valuators.mask_len * 8 { - if ffi::XIMaskIsSet(mask, i) { - let x = unsafe { *value }; - // We assume that every XInput2 device with analog axes is a pointing device emitting - // relative coordinates. - match i { - 0 => mouse_delta.0 = x, - 1 => mouse_delta.1 = x, - 2 => scroll_delta.0 = x as f32, - 3 => scroll_delta.1 = x as f32, - _ => {} - } - callback(Event::DeviceEvent { - device_id: did, - event: DeviceEvent::Motion { - axis: i as u32, - value: x, - }, - }); - value = unsafe { value.offset(1) }; - } - } - if mouse_delta != (0.0, 0.0) { - callback(Event::DeviceEvent { - device_id: did, - event: DeviceEvent::MouseMotion { delta: mouse_delta }, - }); - } - if scroll_delta != (0.0, 0.0) { - callback(Event::DeviceEvent { - device_id: did, - event: DeviceEvent::MouseWheel { - delta: LineDelta(scroll_delta.0, scroll_delta.1), - }, - }); - } - } - - ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => { - let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; - - let state = match xev.evtype { - ffi::XI_RawKeyPress => Pressed, - ffi::XI_RawKeyRelease => Released, - _ => unreachable!(), - }; - - let device_id = mkdid(xev.sourceid); - let keycode = xev.detail; - let scancode = keycode - KEYCODE_OFFSET as i32; - if scancode < 0 { - return; - } - let keysym = wt.xconn.keycode_to_keysym(keycode as ffi::KeyCode); - let virtual_keycode = events::keysym_to_element(keysym as c_uint); - let modifiers = self.device_mod_state.modifiers(); - - #[allow(deprecated)] - callback(Event::DeviceEvent { - device_id, - event: DeviceEvent::Key(KeyboardInput { - scancode: scancode as u32, - virtual_keycode, - state, - modifiers, - }), - }); - - if let Some(modifier) = - self.mod_keymap.get_modifier(keycode as ffi::KeyCode) - { - self.device_mod_state.key_event( - state, - keycode as ffi::KeyCode, - modifier, - ); - - let new_modifiers = self.device_mod_state.modifiers(); - - if modifiers != new_modifiers { - if let Some(window_id) = self.active_window { - callback(Event::WindowEvent { - window_id: mkwid(window_id), - event: WindowEvent::ModifiersChanged(new_modifiers), - }); - } - } - } - } - - ffi::XI_HierarchyChanged => { - let xev: &ffi::XIHierarchyEvent = unsafe { &*(xev.data as *const _) }; - for info in - unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) } - { - if 0 != info.flags & (ffi::XISlaveAdded | ffi::XIMasterAdded) { - self.init_device(info.deviceid); - callback(Event::DeviceEvent { - device_id: mkdid(info.deviceid), - event: DeviceEvent::Added, - }); - } else if 0 != info.flags & (ffi::XISlaveRemoved | ffi::XIMasterRemoved) - { - callback(Event::DeviceEvent { - device_id: mkdid(info.deviceid), - event: DeviceEvent::Removed, - }); - let mut devices = self.devices.borrow_mut(); - devices.remove(&DeviceId(info.deviceid)); - } - } - } - - _ => {} - } - } - _ => { - if event_type == self.randr_event_offset { - // In the future, it would be quite easy to emit monitor hotplug events. - let prev_list = monitor::invalidate_cached_monitor_list(); - if let Some(prev_list) = prev_list { - let new_list = wt.xconn.available_monitors(); - for new_monitor in new_list { - prev_list - .iter() - .find(|prev_monitor| prev_monitor.name == new_monitor.name) - .map(|prev_monitor| { - if new_monitor.scale_factor != prev_monitor.scale_factor { - for (window_id, window) in wt.windows.borrow().iter() { - if let Some(window) = window.upgrade() { - // Check if the window is on this monitor - let monitor = window.current_monitor(); - if monitor.name == new_monitor.name { - let (width, height) = - window.inner_size_physical(); - let (new_width, new_height) = window - .adjust_for_dpi( - prev_monitor.scale_factor, - new_monitor.scale_factor, - width, - height, - &*window.shared_state.lock(), - ); - - let window_id = crate::window::WindowId( - crate::platform_impl::platform::WindowId::X( - *window_id, - ), - ); - let old_inner_size = - PhysicalSize::new(width, height); - let mut new_inner_size = - PhysicalSize::new(new_width, new_height); - - callback(Event::WindowEvent { - window_id, - event: WindowEvent::ScaleFactorChanged { - scale_factor: new_monitor.scale_factor, - new_inner_size: &mut new_inner_size, - }, - }); - - if new_inner_size != old_inner_size { - let (new_width, new_height) = - new_inner_size.into(); - window.set_inner_size_physical( - new_width, new_height, - ); - } - } - } - } - } - }); - } - } - } - } - } - - match self.ime_receiver.try_recv() { - Ok((window_id, x, y)) => { - wt.ime.borrow_mut().send_xim_spot(window_id, x, y); - } - Err(_) => (), - } - } - - fn handle_pressed_keys( - wt: &super::EventLoopWindowTarget, - window_id: crate::window::WindowId, - state: ElementState, - mod_keymap: &ModifierKeymap, - device_mod_state: &mut ModifierKeyState, - callback: &mut F, - ) where - F: FnMut(Event<'_, T>), - { - let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD); - let modifiers = device_mod_state.modifiers(); - - // Update modifiers state and emit key events based on which keys are currently pressed. - for keycode in wt - .xconn - .query_keymap() - .into_iter() - .filter(|k| *k >= KEYCODE_OFFSET) - { - let scancode = (keycode - KEYCODE_OFFSET) as u32; - let keysym = wt.xconn.keycode_to_keysym(keycode); - let virtual_keycode = events::keysym_to_element(keysym as c_uint); - - if let Some(modifier) = mod_keymap.get_modifier(keycode as ffi::KeyCode) { - device_mod_state.key_event( - ElementState::Pressed, - keycode as ffi::KeyCode, - modifier, - ); - } - - #[allow(deprecated)] - callback(Event::WindowEvent { - window_id, - event: WindowEvent::KeyboardInput { - device_id, - input: KeyboardInput { - scancode, - state, - virtual_keycode, - modifiers, - }, - is_synthetic: true, - }, - }); - } - } -} - -fn is_first_touch(first: &mut Option, num: &mut u32, id: u64, phase: TouchPhase) -> bool { - match phase { - TouchPhase::Started => { - if *num == 0 { - *first = Some(id); - } - *num += 1; - } - TouchPhase::Cancelled | TouchPhase::Ended => { - if *first == Some(id) { - *first = None; - } - *num = num.saturating_sub(1); - } - _ => (), - } - - *first == Some(id) -} diff --git a/src/platform_impl/linux/x11/events.rs b/src/platform_impl/linux/x11/events.rs deleted file mode 100644 index 0c02ca0c..00000000 --- a/src/platform_impl/linux/x11/events.rs +++ /dev/null @@ -1,1008 +0,0 @@ -use super::ffi; -use crate::event::VirtualKeyCode; -use libc; - -pub fn keysym_to_element(keysym: libc::c_uint) -> Option { - Some(match keysym { - ffi::XK_BackSpace => VirtualKeyCode::Back, - ffi::XK_Tab => VirtualKeyCode::Tab, - //ffi::XK_Linefeed => VirtualKeyCode::Linefeed, - //ffi::XK_Clear => VirtualKeyCode::Clear, - ffi::XK_Return => VirtualKeyCode::Return, - //ffi::XK_Pause => VirtualKeyCode::Pause, - //ffi::XK_Scroll_Lock => VirtualKeyCode::Scroll_lock, - //ffi::XK_Sys_Req => VirtualKeyCode::Sys_req, - ffi::XK_Escape => VirtualKeyCode::Escape, - ffi::XK_Delete => VirtualKeyCode::Delete, - ffi::XK_Multi_key => VirtualKeyCode::Compose, - //ffi::XK_Kanji => VirtualKeyCode::Kanji, - //ffi::XK_Muhenkan => VirtualKeyCode::Muhenkan, - //ffi::XK_Henkan_Mode => VirtualKeyCode::Henkan_mode, - //ffi::XK_Henkan => VirtualKeyCode::Henkan, - //ffi::XK_Romaji => VirtualKeyCode::Romaji, - //ffi::XK_Hiragana => VirtualKeyCode::Hiragana, - //ffi::XK_Katakana => VirtualKeyCode::Katakana, - //ffi::XK_Hiragana_Katakana => VirtualKeyCode::Hiragana_katakana, - //ffi::XK_Zenkaku => VirtualKeyCode::Zenkaku, - //ffi::XK_Hankaku => VirtualKeyCode::Hankaku, - //ffi::XK_Zenkaku_Hankaku => VirtualKeyCode::Zenkaku_hankaku, - //ffi::XK_Touroku => VirtualKeyCode::Touroku, - //ffi::XK_Massyo => VirtualKeyCode::Massyo, - //ffi::XK_Kana_Lock => VirtualKeyCode::Kana_lock, - //ffi::XK_Kana_Shift => VirtualKeyCode::Kana_shift, - //ffi::XK_Eisu_Shift => VirtualKeyCode::Eisu_shift, - //ffi::XK_Eisu_toggle => VirtualKeyCode::Eisu_toggle, - ffi::XK_Home => VirtualKeyCode::Home, - ffi::XK_Left => VirtualKeyCode::Left, - ffi::XK_Up => VirtualKeyCode::Up, - ffi::XK_Right => VirtualKeyCode::Right, - ffi::XK_Down => VirtualKeyCode::Down, - //ffi::XK_Prior => VirtualKeyCode::Prior, - ffi::XK_Page_Up => VirtualKeyCode::PageUp, - //ffi::XK_Next => VirtualKeyCode::Next, - ffi::XK_Page_Down => VirtualKeyCode::PageDown, - ffi::XK_End => VirtualKeyCode::End, - //ffi::XK_Begin => VirtualKeyCode::Begin, - //ffi::XK_Win_L => VirtualKeyCode::Win_l, - //ffi::XK_Win_R => VirtualKeyCode::Win_r, - //ffi::XK_App => VirtualKeyCode::App, - //ffi::XK_Select => VirtualKeyCode::Select, - //ffi::XK_Print => VirtualKeyCode::Print, - //ffi::XK_Execute => VirtualKeyCode::Execute, - ffi::XK_Insert => VirtualKeyCode::Insert, - //ffi::XK_Undo => VirtualKeyCode::Undo, - //ffi::XK_Redo => VirtualKeyCode::Redo, - //ffi::XK_Menu => VirtualKeyCode::Menu, - //ffi::XK_Find => VirtualKeyCode::Find, - //ffi::XK_Cancel => VirtualKeyCode::Cancel, - //ffi::XK_Help => VirtualKeyCode::Help, - //ffi::XK_Break => VirtualKeyCode::Break, - //ffi::XK_Mode_switch => VirtualKeyCode::Mode_switch, - //ffi::XK_script_switch => VirtualKeyCode::Script_switch, - //ffi::XK_Num_Lock => VirtualKeyCode::Num_lock, - //ffi::XK_KP_Space => VirtualKeyCode::Kp_space, - //ffi::XK_KP_Tab => VirtualKeyCode::Kp_tab, - //ffi::XK_KP_Enter => VirtualKeyCode::Kp_enter, - //ffi::XK_KP_F1 => VirtualKeyCode::Kp_f1, - //ffi::XK_KP_F2 => VirtualKeyCode::Kp_f2, - //ffi::XK_KP_F3 => VirtualKeyCode::Kp_f3, - //ffi::XK_KP_F4 => VirtualKeyCode::Kp_f4, - ffi::XK_KP_Home => VirtualKeyCode::Home, - ffi::XK_KP_Left => VirtualKeyCode::Left, - ffi::XK_KP_Up => VirtualKeyCode::Up, - ffi::XK_KP_Right => VirtualKeyCode::Right, - ffi::XK_KP_Down => VirtualKeyCode::Down, - //ffi::XK_KP_Prior => VirtualKeyCode::Kp_prior, - ffi::XK_KP_Page_Up => VirtualKeyCode::PageUp, - //ffi::XK_KP_Next => VirtualKeyCode::Kp_next, - ffi::XK_KP_Page_Down => VirtualKeyCode::PageDown, - ffi::XK_KP_End => VirtualKeyCode::End, - //ffi::XK_KP_Begin => VirtualKeyCode::Kp_begin, - ffi::XK_KP_Insert => VirtualKeyCode::Insert, - ffi::XK_KP_Delete => VirtualKeyCode::Delete, - ffi::XK_KP_Equal => VirtualKeyCode::NumpadEquals, - ffi::XK_KP_Multiply => VirtualKeyCode::NumpadMultiply, - ffi::XK_KP_Add => VirtualKeyCode::NumpadAdd, - //ffi::XK_KP_Separator => VirtualKeyCode::Kp_separator, - ffi::XK_KP_Subtract => VirtualKeyCode::NumpadSubtract, - ffi::XK_KP_Decimal => VirtualKeyCode::NumpadDecimal, - ffi::XK_KP_Divide => VirtualKeyCode::NumpadDivide, - ffi::XK_KP_0 => VirtualKeyCode::Numpad0, - ffi::XK_KP_1 => VirtualKeyCode::Numpad1, - ffi::XK_KP_2 => VirtualKeyCode::Numpad2, - ffi::XK_KP_3 => VirtualKeyCode::Numpad3, - ffi::XK_KP_4 => VirtualKeyCode::Numpad4, - ffi::XK_KP_5 => VirtualKeyCode::Numpad5, - ffi::XK_KP_6 => VirtualKeyCode::Numpad6, - ffi::XK_KP_7 => VirtualKeyCode::Numpad7, - ffi::XK_KP_8 => VirtualKeyCode::Numpad8, - ffi::XK_KP_9 => VirtualKeyCode::Numpad9, - ffi::XK_F1 => VirtualKeyCode::F1, - ffi::XK_F2 => VirtualKeyCode::F2, - ffi::XK_F3 => VirtualKeyCode::F3, - ffi::XK_F4 => VirtualKeyCode::F4, - ffi::XK_F5 => VirtualKeyCode::F5, - ffi::XK_F6 => VirtualKeyCode::F6, - ffi::XK_F7 => VirtualKeyCode::F7, - ffi::XK_F8 => VirtualKeyCode::F8, - ffi::XK_F9 => VirtualKeyCode::F9, - ffi::XK_F10 => VirtualKeyCode::F10, - ffi::XK_F11 => VirtualKeyCode::F11, - //ffi::XK_L1 => VirtualKeyCode::L1, - ffi::XK_F12 => VirtualKeyCode::F12, - //ffi::XK_L2 => VirtualKeyCode::L2, - ffi::XK_F13 => VirtualKeyCode::F13, - //ffi::XK_L3 => VirtualKeyCode::L3, - ffi::XK_F14 => VirtualKeyCode::F14, - //ffi::XK_L4 => VirtualKeyCode::L4, - ffi::XK_F15 => VirtualKeyCode::F15, - //ffi::XK_L5 => VirtualKeyCode::L5, - ffi::XK_F16 => VirtualKeyCode::F16, - //ffi::XK_L6 => VirtualKeyCode::L6, - ffi::XK_F17 => VirtualKeyCode::F17, - //ffi::XK_L7 => VirtualKeyCode::L7, - ffi::XK_F18 => VirtualKeyCode::F18, - //ffi::XK_L8 => VirtualKeyCode::L8, - ffi::XK_F19 => VirtualKeyCode::F19, - //ffi::XK_L9 => VirtualKeyCode::L9, - ffi::XK_F20 => VirtualKeyCode::F20, - //ffi::XK_L10 => VirtualKeyCode::L10, - ffi::XK_F21 => VirtualKeyCode::F21, - //ffi::XK_R1 => VirtualKeyCode::R1, - ffi::XK_F22 => VirtualKeyCode::F22, - //ffi::XK_R2 => VirtualKeyCode::R2, - ffi::XK_F23 => VirtualKeyCode::F23, - //ffi::XK_R3 => VirtualKeyCode::R3, - ffi::XK_F24 => VirtualKeyCode::F24, - //ffi::XK_R4 => VirtualKeyCode::R4, - //ffi::XK_F25 => VirtualKeyCode::F25, - //ffi::XK_R5 => VirtualKeyCode::R5, - //ffi::XK_F26 => VirtualKeyCode::F26, - //ffi::XK_R6 => VirtualKeyCode::R6, - //ffi::XK_F27 => VirtualKeyCode::F27, - //ffi::XK_R7 => VirtualKeyCode::R7, - //ffi::XK_F28 => VirtualKeyCode::F28, - //ffi::XK_R8 => VirtualKeyCode::R8, - //ffi::XK_F29 => VirtualKeyCode::F29, - //ffi::XK_R9 => VirtualKeyCode::R9, - //ffi::XK_F30 => VirtualKeyCode::F30, - //ffi::XK_R10 => VirtualKeyCode::R10, - //ffi::XK_F31 => VirtualKeyCode::F31, - //ffi::XK_R11 => VirtualKeyCode::R11, - //ffi::XK_F32 => VirtualKeyCode::F32, - //ffi::XK_R12 => VirtualKeyCode::R12, - //ffi::XK_F33 => VirtualKeyCode::F33, - //ffi::XK_R13 => VirtualKeyCode::R13, - //ffi::XK_F34 => VirtualKeyCode::F34, - //ffi::XK_R14 => VirtualKeyCode::R14, - //ffi::XK_F35 => VirtualKeyCode::F35, - //ffi::XK_R15 => VirtualKeyCode::R15, - ffi::XK_Shift_L => VirtualKeyCode::LShift, - ffi::XK_Shift_R => VirtualKeyCode::RShift, - ffi::XK_Control_L => VirtualKeyCode::LControl, - ffi::XK_Control_R => VirtualKeyCode::RControl, - //ffi::XK_Caps_Lock => VirtualKeyCode::Caps_lock, - //ffi::XK_Shift_Lock => VirtualKeyCode::Shift_lock, - //ffi::XK_Meta_L => VirtualKeyCode::Meta_l, - //ffi::XK_Meta_R => VirtualKeyCode::Meta_r, - ffi::XK_Alt_L => VirtualKeyCode::LAlt, - ffi::XK_Alt_R => VirtualKeyCode::RAlt, - //ffi::XK_Super_L => VirtualKeyCode::Super_l, - //ffi::XK_Super_R => VirtualKeyCode::Super_r, - //ffi::XK_Hyper_L => VirtualKeyCode::Hyper_l, - //ffi::XK_Hyper_R => VirtualKeyCode::Hyper_r, - ffi::XK_ISO_Left_Tab => VirtualKeyCode::Tab, - ffi::XK_space => VirtualKeyCode::Space, - //ffi::XK_exclam => VirtualKeyCode::Exclam, - //ffi::XK_quotedbl => VirtualKeyCode::Quotedbl, - //ffi::XK_numbersign => VirtualKeyCode::Numbersign, - //ffi::XK_dollar => VirtualKeyCode::Dollar, - //ffi::XK_percent => VirtualKeyCode::Percent, - //ffi::XK_ampersand => VirtualKeyCode::Ampersand, - ffi::XK_apostrophe => VirtualKeyCode::Apostrophe, - //ffi::XK_quoteright => VirtualKeyCode::Quoteright, - //ffi::XK_parenleft => VirtualKeyCode::Parenleft, - //ffi::XK_parenright => VirtualKeyCode::Parenright, - ffi::XK_asterisk => VirtualKeyCode::Asterisk, - ffi::XK_plus => VirtualKeyCode::Plus, - ffi::XK_comma => VirtualKeyCode::Comma, - ffi::XK_minus => VirtualKeyCode::Minus, - ffi::XK_period => VirtualKeyCode::Period, - ffi::XK_slash => VirtualKeyCode::Slash, - ffi::XK_0 => VirtualKeyCode::Key0, - ffi::XK_1 => VirtualKeyCode::Key1, - ffi::XK_2 => VirtualKeyCode::Key2, - ffi::XK_3 => VirtualKeyCode::Key3, - ffi::XK_4 => VirtualKeyCode::Key4, - ffi::XK_5 => VirtualKeyCode::Key5, - ffi::XK_6 => VirtualKeyCode::Key6, - ffi::XK_7 => VirtualKeyCode::Key7, - ffi::XK_8 => VirtualKeyCode::Key8, - ffi::XK_9 => VirtualKeyCode::Key9, - ffi::XK_colon => VirtualKeyCode::Colon, - ffi::XK_semicolon => VirtualKeyCode::Semicolon, - //ffi::XK_less => VirtualKeyCode::Less, - ffi::XK_equal => VirtualKeyCode::Equals, - //ffi::XK_greater => VirtualKeyCode::Greater, - //ffi::XK_question => VirtualKeyCode::Question, - ffi::XK_at => VirtualKeyCode::At, - ffi::XK_A => VirtualKeyCode::A, - ffi::XK_B => VirtualKeyCode::B, - ffi::XK_C => VirtualKeyCode::C, - ffi::XK_D => VirtualKeyCode::D, - ffi::XK_E => VirtualKeyCode::E, - ffi::XK_F => VirtualKeyCode::F, - ffi::XK_G => VirtualKeyCode::G, - ffi::XK_H => VirtualKeyCode::H, - ffi::XK_I => VirtualKeyCode::I, - ffi::XK_J => VirtualKeyCode::J, - ffi::XK_K => VirtualKeyCode::K, - ffi::XK_L => VirtualKeyCode::L, - ffi::XK_M => VirtualKeyCode::M, - ffi::XK_N => VirtualKeyCode::N, - ffi::XK_O => VirtualKeyCode::O, - ffi::XK_P => VirtualKeyCode::P, - ffi::XK_Q => VirtualKeyCode::Q, - ffi::XK_R => VirtualKeyCode::R, - ffi::XK_S => VirtualKeyCode::S, - ffi::XK_T => VirtualKeyCode::T, - ffi::XK_U => VirtualKeyCode::U, - ffi::XK_V => VirtualKeyCode::V, - ffi::XK_W => VirtualKeyCode::W, - ffi::XK_X => VirtualKeyCode::X, - ffi::XK_Y => VirtualKeyCode::Y, - ffi::XK_Z => VirtualKeyCode::Z, - ffi::XK_bracketleft => VirtualKeyCode::LBracket, - ffi::XK_backslash => VirtualKeyCode::Backslash, - ffi::XK_bracketright => VirtualKeyCode::RBracket, - //ffi::XK_asciicircum => VirtualKeyCode::Asciicircum, - //ffi::XK_underscore => VirtualKeyCode::Underscore, - ffi::XK_grave => VirtualKeyCode::Grave, - //ffi::XK_quoteleft => VirtualKeyCode::Quoteleft, - ffi::XK_a => VirtualKeyCode::A, - ffi::XK_b => VirtualKeyCode::B, - ffi::XK_c => VirtualKeyCode::C, - ffi::XK_d => VirtualKeyCode::D, - ffi::XK_e => VirtualKeyCode::E, - ffi::XK_f => VirtualKeyCode::F, - ffi::XK_g => VirtualKeyCode::G, - ffi::XK_h => VirtualKeyCode::H, - ffi::XK_i => VirtualKeyCode::I, - ffi::XK_j => VirtualKeyCode::J, - ffi::XK_k => VirtualKeyCode::K, - ffi::XK_l => VirtualKeyCode::L, - ffi::XK_m => VirtualKeyCode::M, - ffi::XK_n => VirtualKeyCode::N, - ffi::XK_o => VirtualKeyCode::O, - ffi::XK_p => VirtualKeyCode::P, - ffi::XK_q => VirtualKeyCode::Q, - ffi::XK_r => VirtualKeyCode::R, - ffi::XK_s => VirtualKeyCode::S, - ffi::XK_t => VirtualKeyCode::T, - ffi::XK_u => VirtualKeyCode::U, - ffi::XK_v => VirtualKeyCode::V, - ffi::XK_w => VirtualKeyCode::W, - ffi::XK_x => VirtualKeyCode::X, - ffi::XK_y => VirtualKeyCode::Y, - ffi::XK_z => VirtualKeyCode::Z, - //ffi::XK_braceleft => VirtualKeyCode::Braceleft, - //ffi::XK_bar => VirtualKeyCode::Bar, - //ffi::XK_braceright => VirtualKeyCode::Braceright, - //ffi::XK_asciitilde => VirtualKeyCode::Asciitilde, - //ffi::XK_nobreakspace => VirtualKeyCode::Nobreakspace, - //ffi::XK_exclamdown => VirtualKeyCode::Exclamdown, - //ffi::XK_cent => VirtualKeyCode::Cent, - //ffi::XK_sterling => VirtualKeyCode::Sterling, - //ffi::XK_currency => VirtualKeyCode::Currency, - //ffi::XK_yen => VirtualKeyCode::Yen, - //ffi::XK_brokenbar => VirtualKeyCode::Brokenbar, - //ffi::XK_section => VirtualKeyCode::Section, - //ffi::XK_diaeresis => VirtualKeyCode::Diaeresis, - //ffi::XK_copyright => VirtualKeyCode::Copyright, - //ffi::XK_ordfeminine => VirtualKeyCode::Ordfeminine, - //ffi::XK_guillemotleft => VirtualKeyCode::Guillemotleft, - //ffi::XK_notsign => VirtualKeyCode::Notsign, - //ffi::XK_hyphen => VirtualKeyCode::Hyphen, - //ffi::XK_registered => VirtualKeyCode::Registered, - //ffi::XK_macron => VirtualKeyCode::Macron, - //ffi::XK_degree => VirtualKeyCode::Degree, - //ffi::XK_plusminus => VirtualKeyCode::Plusminus, - //ffi::XK_twosuperior => VirtualKeyCode::Twosuperior, - //ffi::XK_threesuperior => VirtualKeyCode::Threesuperior, - //ffi::XK_acute => VirtualKeyCode::Acute, - //ffi::XK_mu => VirtualKeyCode::Mu, - //ffi::XK_paragraph => VirtualKeyCode::Paragraph, - //ffi::XK_periodcentered => VirtualKeyCode::Periodcentered, - //ffi::XK_cedilla => VirtualKeyCode::Cedilla, - //ffi::XK_onesuperior => VirtualKeyCode::Onesuperior, - //ffi::XK_masculine => VirtualKeyCode::Masculine, - //ffi::XK_guillemotright => VirtualKeyCode::Guillemotright, - //ffi::XK_onequarter => VirtualKeyCode::Onequarter, - //ffi::XK_onehalf => VirtualKeyCode::Onehalf, - //ffi::XK_threequarters => VirtualKeyCode::Threequarters, - //ffi::XK_questiondown => VirtualKeyCode::Questiondown, - //ffi::XK_Agrave => VirtualKeyCode::Agrave, - //ffi::XK_Aacute => VirtualKeyCode::Aacute, - //ffi::XK_Acircumflex => VirtualKeyCode::Acircumflex, - //ffi::XK_Atilde => VirtualKeyCode::Atilde, - //ffi::XK_Adiaeresis => VirtualKeyCode::Adiaeresis, - //ffi::XK_Aring => VirtualKeyCode::Aring, - //ffi::XK_AE => VirtualKeyCode::Ae, - //ffi::XK_Ccedilla => VirtualKeyCode::Ccedilla, - //ffi::XK_Egrave => VirtualKeyCode::Egrave, - //ffi::XK_Eacute => VirtualKeyCode::Eacute, - //ffi::XK_Ecircumflex => VirtualKeyCode::Ecircumflex, - //ffi::XK_Ediaeresis => VirtualKeyCode::Ediaeresis, - //ffi::XK_Igrave => VirtualKeyCode::Igrave, - //ffi::XK_Iacute => VirtualKeyCode::Iacute, - //ffi::XK_Icircumflex => VirtualKeyCode::Icircumflex, - //ffi::XK_Idiaeresis => VirtualKeyCode::Idiaeresis, - //ffi::XK_ETH => VirtualKeyCode::Eth, - //ffi::XK_Eth => VirtualKeyCode::Eth, - //ffi::XK_Ntilde => VirtualKeyCode::Ntilde, - //ffi::XK_Ograve => VirtualKeyCode::Ograve, - //ffi::XK_Oacute => VirtualKeyCode::Oacute, - //ffi::XK_Ocircumflex => VirtualKeyCode::Ocircumflex, - //ffi::XK_Otilde => VirtualKeyCode::Otilde, - //ffi::XK_Odiaeresis => VirtualKeyCode::Odiaeresis, - //ffi::XK_multiply => VirtualKeyCode::Multiply, - //ffi::XK_Ooblique => VirtualKeyCode::Ooblique, - //ffi::XK_Ugrave => VirtualKeyCode::Ugrave, - //ffi::XK_Uacute => VirtualKeyCode::Uacute, - //ffi::XK_Ucircumflex => VirtualKeyCode::Ucircumflex, - //ffi::XK_Udiaeresis => VirtualKeyCode::Udiaeresis, - //ffi::XK_Yacute => VirtualKeyCode::Yacute, - //ffi::XK_THORN => VirtualKeyCode::Thorn, - //ffi::XK_Thorn => VirtualKeyCode::Thorn, - //ffi::XK_ssharp => VirtualKeyCode::Ssharp, - //ffi::XK_agrave => VirtualKeyCode::Agrave, - //ffi::XK_aacute => VirtualKeyCode::Aacute, - //ffi::XK_acircumflex => VirtualKeyCode::Acircumflex, - //ffi::XK_atilde => VirtualKeyCode::Atilde, - //ffi::XK_adiaeresis => VirtualKeyCode::Adiaeresis, - //ffi::XK_aring => VirtualKeyCode::Aring, - //ffi::XK_ae => VirtualKeyCode::Ae, - //ffi::XK_ccedilla => VirtualKeyCode::Ccedilla, - //ffi::XK_egrave => VirtualKeyCode::Egrave, - //ffi::XK_eacute => VirtualKeyCode::Eacute, - //ffi::XK_ecircumflex => VirtualKeyCode::Ecircumflex, - //ffi::XK_ediaeresis => VirtualKeyCode::Ediaeresis, - //ffi::XK_igrave => VirtualKeyCode::Igrave, - //ffi::XK_iacute => VirtualKeyCode::Iacute, - //ffi::XK_icircumflex => VirtualKeyCode::Icircumflex, - //ffi::XK_idiaeresis => VirtualKeyCode::Idiaeresis, - //ffi::XK_eth => VirtualKeyCode::Eth, - //ffi::XK_ntilde => VirtualKeyCode::Ntilde, - //ffi::XK_ograve => VirtualKeyCode::Ograve, - //ffi::XK_oacute => VirtualKeyCode::Oacute, - //ffi::XK_ocircumflex => VirtualKeyCode::Ocircumflex, - //ffi::XK_otilde => VirtualKeyCode::Otilde, - //ffi::XK_odiaeresis => VirtualKeyCode::Odiaeresis, - //ffi::XK_division => VirtualKeyCode::Division, - //ffi::XK_oslash => VirtualKeyCode::Oslash, - //ffi::XK_ugrave => VirtualKeyCode::Ugrave, - //ffi::XK_uacute => VirtualKeyCode::Uacute, - //ffi::XK_ucircumflex => VirtualKeyCode::Ucircumflex, - //ffi::XK_udiaeresis => VirtualKeyCode::Udiaeresis, - //ffi::XK_yacute => VirtualKeyCode::Yacute, - //ffi::XK_thorn => VirtualKeyCode::Thorn, - //ffi::XK_ydiaeresis => VirtualKeyCode::Ydiaeresis, - //ffi::XK_Aogonek => VirtualKeyCode::Aogonek, - //ffi::XK_breve => VirtualKeyCode::Breve, - //ffi::XK_Lstroke => VirtualKeyCode::Lstroke, - //ffi::XK_Lcaron => VirtualKeyCode::Lcaron, - //ffi::XK_Sacute => VirtualKeyCode::Sacute, - //ffi::XK_Scaron => VirtualKeyCode::Scaron, - //ffi::XK_Scedilla => VirtualKeyCode::Scedilla, - //ffi::XK_Tcaron => VirtualKeyCode::Tcaron, - //ffi::XK_Zacute => VirtualKeyCode::Zacute, - //ffi::XK_Zcaron => VirtualKeyCode::Zcaron, - //ffi::XK_Zabovedot => VirtualKeyCode::Zabovedot, - //ffi::XK_aogonek => VirtualKeyCode::Aogonek, - //ffi::XK_ogonek => VirtualKeyCode::Ogonek, - //ffi::XK_lstroke => VirtualKeyCode::Lstroke, - //ffi::XK_lcaron => VirtualKeyCode::Lcaron, - //ffi::XK_sacute => VirtualKeyCode::Sacute, - //ffi::XK_caron => VirtualKeyCode::Caron, - //ffi::XK_scaron => VirtualKeyCode::Scaron, - //ffi::XK_scedilla => VirtualKeyCode::Scedilla, - //ffi::XK_tcaron => VirtualKeyCode::Tcaron, - //ffi::XK_zacute => VirtualKeyCode::Zacute, - //ffi::XK_doubleacute => VirtualKeyCode::Doubleacute, - //ffi::XK_zcaron => VirtualKeyCode::Zcaron, - //ffi::XK_zabovedot => VirtualKeyCode::Zabovedot, - //ffi::XK_Racute => VirtualKeyCode::Racute, - //ffi::XK_Abreve => VirtualKeyCode::Abreve, - //ffi::XK_Lacute => VirtualKeyCode::Lacute, - //ffi::XK_Cacute => VirtualKeyCode::Cacute, - //ffi::XK_Ccaron => VirtualKeyCode::Ccaron, - //ffi::XK_Eogonek => VirtualKeyCode::Eogonek, - //ffi::XK_Ecaron => VirtualKeyCode::Ecaron, - //ffi::XK_Dcaron => VirtualKeyCode::Dcaron, - //ffi::XK_Dstroke => VirtualKeyCode::Dstroke, - //ffi::XK_Nacute => VirtualKeyCode::Nacute, - //ffi::XK_Ncaron => VirtualKeyCode::Ncaron, - //ffi::XK_Odoubleacute => VirtualKeyCode::Odoubleacute, - //ffi::XK_Rcaron => VirtualKeyCode::Rcaron, - //ffi::XK_Uring => VirtualKeyCode::Uring, - //ffi::XK_Udoubleacute => VirtualKeyCode::Udoubleacute, - //ffi::XK_Tcedilla => VirtualKeyCode::Tcedilla, - //ffi::XK_racute => VirtualKeyCode::Racute, - //ffi::XK_abreve => VirtualKeyCode::Abreve, - //ffi::XK_lacute => VirtualKeyCode::Lacute, - //ffi::XK_cacute => VirtualKeyCode::Cacute, - //ffi::XK_ccaron => VirtualKeyCode::Ccaron, - //ffi::XK_eogonek => VirtualKeyCode::Eogonek, - //ffi::XK_ecaron => VirtualKeyCode::Ecaron, - //ffi::XK_dcaron => VirtualKeyCode::Dcaron, - //ffi::XK_dstroke => VirtualKeyCode::Dstroke, - //ffi::XK_nacute => VirtualKeyCode::Nacute, - //ffi::XK_ncaron => VirtualKeyCode::Ncaron, - //ffi::XK_odoubleacute => VirtualKeyCode::Odoubleacute, - //ffi::XK_udoubleacute => VirtualKeyCode::Udoubleacute, - //ffi::XK_rcaron => VirtualKeyCode::Rcaron, - //ffi::XK_uring => VirtualKeyCode::Uring, - //ffi::XK_tcedilla => VirtualKeyCode::Tcedilla, - //ffi::XK_abovedot => VirtualKeyCode::Abovedot, - //ffi::XK_Hstroke => VirtualKeyCode::Hstroke, - //ffi::XK_Hcircumflex => VirtualKeyCode::Hcircumflex, - //ffi::XK_Iabovedot => VirtualKeyCode::Iabovedot, - //ffi::XK_Gbreve => VirtualKeyCode::Gbreve, - //ffi::XK_Jcircumflex => VirtualKeyCode::Jcircumflex, - //ffi::XK_hstroke => VirtualKeyCode::Hstroke, - //ffi::XK_hcircumflex => VirtualKeyCode::Hcircumflex, - //ffi::XK_idotless => VirtualKeyCode::Idotless, - //ffi::XK_gbreve => VirtualKeyCode::Gbreve, - //ffi::XK_jcircumflex => VirtualKeyCode::Jcircumflex, - //ffi::XK_Cabovedot => VirtualKeyCode::Cabovedot, - //ffi::XK_Ccircumflex => VirtualKeyCode::Ccircumflex, - //ffi::XK_Gabovedot => VirtualKeyCode::Gabovedot, - //ffi::XK_Gcircumflex => VirtualKeyCode::Gcircumflex, - //ffi::XK_Ubreve => VirtualKeyCode::Ubreve, - //ffi::XK_Scircumflex => VirtualKeyCode::Scircumflex, - //ffi::XK_cabovedot => VirtualKeyCode::Cabovedot, - //ffi::XK_ccircumflex => VirtualKeyCode::Ccircumflex, - //ffi::XK_gabovedot => VirtualKeyCode::Gabovedot, - //ffi::XK_gcircumflex => VirtualKeyCode::Gcircumflex, - //ffi::XK_ubreve => VirtualKeyCode::Ubreve, - //ffi::XK_scircumflex => VirtualKeyCode::Scircumflex, - //ffi::XK_kra => VirtualKeyCode::Kra, - //ffi::XK_kappa => VirtualKeyCode::Kappa, - //ffi::XK_Rcedilla => VirtualKeyCode::Rcedilla, - //ffi::XK_Itilde => VirtualKeyCode::Itilde, - //ffi::XK_Lcedilla => VirtualKeyCode::Lcedilla, - //ffi::XK_Emacron => VirtualKeyCode::Emacron, - //ffi::XK_Gcedilla => VirtualKeyCode::Gcedilla, - //ffi::XK_Tslash => VirtualKeyCode::Tslash, - //ffi::XK_rcedilla => VirtualKeyCode::Rcedilla, - //ffi::XK_itilde => VirtualKeyCode::Itilde, - //ffi::XK_lcedilla => VirtualKeyCode::Lcedilla, - //ffi::XK_emacron => VirtualKeyCode::Emacron, - //ffi::XK_gcedilla => VirtualKeyCode::Gcedilla, - //ffi::XK_tslash => VirtualKeyCode::Tslash, - //ffi::XK_ENG => VirtualKeyCode::Eng, - //ffi::XK_eng => VirtualKeyCode::Eng, - //ffi::XK_Amacron => VirtualKeyCode::Amacron, - //ffi::XK_Iogonek => VirtualKeyCode::Iogonek, - //ffi::XK_Eabovedot => VirtualKeyCode::Eabovedot, - //ffi::XK_Imacron => VirtualKeyCode::Imacron, - //ffi::XK_Ncedilla => VirtualKeyCode::Ncedilla, - //ffi::XK_Omacron => VirtualKeyCode::Omacron, - //ffi::XK_Kcedilla => VirtualKeyCode::Kcedilla, - //ffi::XK_Uogonek => VirtualKeyCode::Uogonek, - //ffi::XK_Utilde => VirtualKeyCode::Utilde, - //ffi::XK_Umacron => VirtualKeyCode::Umacron, - //ffi::XK_amacron => VirtualKeyCode::Amacron, - //ffi::XK_iogonek => VirtualKeyCode::Iogonek, - //ffi::XK_eabovedot => VirtualKeyCode::Eabovedot, - //ffi::XK_imacron => VirtualKeyCode::Imacron, - //ffi::XK_ncedilla => VirtualKeyCode::Ncedilla, - //ffi::XK_omacron => VirtualKeyCode::Omacron, - //ffi::XK_kcedilla => VirtualKeyCode::Kcedilla, - //ffi::XK_uogonek => VirtualKeyCode::Uogonek, - //ffi::XK_utilde => VirtualKeyCode::Utilde, - //ffi::XK_umacron => VirtualKeyCode::Umacron, - //ffi::XK_overline => VirtualKeyCode::Overline, - //ffi::XK_kana_fullstop => VirtualKeyCode::Kana_fullstop, - //ffi::XK_kana_openingbracket => VirtualKeyCode::Kana_openingbracket, - //ffi::XK_kana_closingbracket => VirtualKeyCode::Kana_closingbracket, - //ffi::XK_kana_comma => VirtualKeyCode::Kana_comma, - //ffi::XK_kana_conjunctive => VirtualKeyCode::Kana_conjunctive, - //ffi::XK_kana_middledot => VirtualKeyCode::Kana_middledot, - //ffi::XK_kana_WO => VirtualKeyCode::Kana_wo, - //ffi::XK_kana_a => VirtualKeyCode::Kana_a, - //ffi::XK_kana_i => VirtualKeyCode::Kana_i, - //ffi::XK_kana_u => VirtualKeyCode::Kana_u, - //ffi::XK_kana_e => VirtualKeyCode::Kana_e, - //ffi::XK_kana_o => VirtualKeyCode::Kana_o, - //ffi::XK_kana_ya => VirtualKeyCode::Kana_ya, - //ffi::XK_kana_yu => VirtualKeyCode::Kana_yu, - //ffi::XK_kana_yo => VirtualKeyCode::Kana_yo, - //ffi::XK_kana_tsu => VirtualKeyCode::Kana_tsu, - //ffi::XK_kana_tu => VirtualKeyCode::Kana_tu, - //ffi::XK_prolongedsound => VirtualKeyCode::Prolongedsound, - //ffi::XK_kana_A => VirtualKeyCode::Kana_a, - //ffi::XK_kana_I => VirtualKeyCode::Kana_i, - //ffi::XK_kana_U => VirtualKeyCode::Kana_u, - //ffi::XK_kana_E => VirtualKeyCode::Kana_e, - //ffi::XK_kana_O => VirtualKeyCode::Kana_o, - //ffi::XK_kana_KA => VirtualKeyCode::Kana_ka, - //ffi::XK_kana_KI => VirtualKeyCode::Kana_ki, - //ffi::XK_kana_KU => VirtualKeyCode::Kana_ku, - //ffi::XK_kana_KE => VirtualKeyCode::Kana_ke, - //ffi::XK_kana_KO => VirtualKeyCode::Kana_ko, - //ffi::XK_kana_SA => VirtualKeyCode::Kana_sa, - //ffi::XK_kana_SHI => VirtualKeyCode::Kana_shi, - //ffi::XK_kana_SU => VirtualKeyCode::Kana_su, - //ffi::XK_kana_SE => VirtualKeyCode::Kana_se, - //ffi::XK_kana_SO => VirtualKeyCode::Kana_so, - //ffi::XK_kana_TA => VirtualKeyCode::Kana_ta, - //ffi::XK_kana_CHI => VirtualKeyCode::Kana_chi, - //ffi::XK_kana_TI => VirtualKeyCode::Kana_ti, - //ffi::XK_kana_TSU => VirtualKeyCode::Kana_tsu, - //ffi::XK_kana_TU => VirtualKeyCode::Kana_tu, - //ffi::XK_kana_TE => VirtualKeyCode::Kana_te, - //ffi::XK_kana_TO => VirtualKeyCode::Kana_to, - //ffi::XK_kana_NA => VirtualKeyCode::Kana_na, - //ffi::XK_kana_NI => VirtualKeyCode::Kana_ni, - //ffi::XK_kana_NU => VirtualKeyCode::Kana_nu, - //ffi::XK_kana_NE => VirtualKeyCode::Kana_ne, - //ffi::XK_kana_NO => VirtualKeyCode::Kana_no, - //ffi::XK_kana_HA => VirtualKeyCode::Kana_ha, - //ffi::XK_kana_HI => VirtualKeyCode::Kana_hi, - //ffi::XK_kana_FU => VirtualKeyCode::Kana_fu, - //ffi::XK_kana_HU => VirtualKeyCode::Kana_hu, - //ffi::XK_kana_HE => VirtualKeyCode::Kana_he, - //ffi::XK_kana_HO => VirtualKeyCode::Kana_ho, - //ffi::XK_kana_MA => VirtualKeyCode::Kana_ma, - //ffi::XK_kana_MI => VirtualKeyCode::Kana_mi, - //ffi::XK_kana_MU => VirtualKeyCode::Kana_mu, - //ffi::XK_kana_ME => VirtualKeyCode::Kana_me, - //ffi::XK_kana_MO => VirtualKeyCode::Kana_mo, - //ffi::XK_kana_YA => VirtualKeyCode::Kana_ya, - //ffi::XK_kana_YU => VirtualKeyCode::Kana_yu, - //ffi::XK_kana_YO => VirtualKeyCode::Kana_yo, - //ffi::XK_kana_RA => VirtualKeyCode::Kana_ra, - //ffi::XK_kana_RI => VirtualKeyCode::Kana_ri, - //ffi::XK_kana_RU => VirtualKeyCode::Kana_ru, - //ffi::XK_kana_RE => VirtualKeyCode::Kana_re, - //ffi::XK_kana_RO => VirtualKeyCode::Kana_ro, - //ffi::XK_kana_WA => VirtualKeyCode::Kana_wa, - //ffi::XK_kana_N => VirtualKeyCode::Kana_n, - //ffi::XK_voicedsound => VirtualKeyCode::Voicedsound, - //ffi::XK_semivoicedsound => VirtualKeyCode::Semivoicedsound, - //ffi::XK_kana_switch => VirtualKeyCode::Kana_switch, - //ffi::XK_Arabic_comma => VirtualKeyCode::Arabic_comma, - //ffi::XK_Arabic_semicolon => VirtualKeyCode::Arabic_semicolon, - //ffi::XK_Arabic_question_mark => VirtualKeyCode::Arabic_question_mark, - //ffi::XK_Arabic_hamza => VirtualKeyCode::Arabic_hamza, - //ffi::XK_Arabic_maddaonalef => VirtualKeyCode::Arabic_maddaonalef, - //ffi::XK_Arabic_hamzaonalef => VirtualKeyCode::Arabic_hamzaonalef, - //ffi::XK_Arabic_hamzaonwaw => VirtualKeyCode::Arabic_hamzaonwaw, - //ffi::XK_Arabic_hamzaunderalef => VirtualKeyCode::Arabic_hamzaunderalef, - //ffi::XK_Arabic_hamzaonyeh => VirtualKeyCode::Arabic_hamzaonyeh, - //ffi::XK_Arabic_alef => VirtualKeyCode::Arabic_alef, - //ffi::XK_Arabic_beh => VirtualKeyCode::Arabic_beh, - //ffi::XK_Arabic_tehmarbuta => VirtualKeyCode::Arabic_tehmarbuta, - //ffi::XK_Arabic_teh => VirtualKeyCode::Arabic_teh, - //ffi::XK_Arabic_theh => VirtualKeyCode::Arabic_theh, - //ffi::XK_Arabic_jeem => VirtualKeyCode::Arabic_jeem, - //ffi::XK_Arabic_hah => VirtualKeyCode::Arabic_hah, - //ffi::XK_Arabic_khah => VirtualKeyCode::Arabic_khah, - //ffi::XK_Arabic_dal => VirtualKeyCode::Arabic_dal, - //ffi::XK_Arabic_thal => VirtualKeyCode::Arabic_thal, - //ffi::XK_Arabic_ra => VirtualKeyCode::Arabic_ra, - //ffi::XK_Arabic_zain => VirtualKeyCode::Arabic_zain, - //ffi::XK_Arabic_seen => VirtualKeyCode::Arabic_seen, - //ffi::XK_Arabic_sheen => VirtualKeyCode::Arabic_sheen, - //ffi::XK_Arabic_sad => VirtualKeyCode::Arabic_sad, - //ffi::XK_Arabic_dad => VirtualKeyCode::Arabic_dad, - //ffi::XK_Arabic_tah => VirtualKeyCode::Arabic_tah, - //ffi::XK_Arabic_zah => VirtualKeyCode::Arabic_zah, - //ffi::XK_Arabic_ain => VirtualKeyCode::Arabic_ain, - //ffi::XK_Arabic_ghain => VirtualKeyCode::Arabic_ghain, - //ffi::XK_Arabic_tatweel => VirtualKeyCode::Arabic_tatweel, - //ffi::XK_Arabic_feh => VirtualKeyCode::Arabic_feh, - //ffi::XK_Arabic_qaf => VirtualKeyCode::Arabic_qaf, - //ffi::XK_Arabic_kaf => VirtualKeyCode::Arabic_kaf, - //ffi::XK_Arabic_lam => VirtualKeyCode::Arabic_lam, - //ffi::XK_Arabic_meem => VirtualKeyCode::Arabic_meem, - //ffi::XK_Arabic_noon => VirtualKeyCode::Arabic_noon, - //ffi::XK_Arabic_ha => VirtualKeyCode::Arabic_ha, - //ffi::XK_Arabic_heh => VirtualKeyCode::Arabic_heh, - //ffi::XK_Arabic_waw => VirtualKeyCode::Arabic_waw, - //ffi::XK_Arabic_alefmaksura => VirtualKeyCode::Arabic_alefmaksura, - //ffi::XK_Arabic_yeh => VirtualKeyCode::Arabic_yeh, - //ffi::XK_Arabic_fathatan => VirtualKeyCode::Arabic_fathatan, - //ffi::XK_Arabic_dammatan => VirtualKeyCode::Arabic_dammatan, - //ffi::XK_Arabic_kasratan => VirtualKeyCode::Arabic_kasratan, - //ffi::XK_Arabic_fatha => VirtualKeyCode::Arabic_fatha, - //ffi::XK_Arabic_damma => VirtualKeyCode::Arabic_damma, - //ffi::XK_Arabic_kasra => VirtualKeyCode::Arabic_kasra, - //ffi::XK_Arabic_shadda => VirtualKeyCode::Arabic_shadda, - //ffi::XK_Arabic_sukun => VirtualKeyCode::Arabic_sukun, - //ffi::XK_Arabic_switch => VirtualKeyCode::Arabic_switch, - //ffi::XK_Serbian_dje => VirtualKeyCode::Serbian_dje, - //ffi::XK_Macedonia_gje => VirtualKeyCode::Macedonia_gje, - //ffi::XK_Cyrillic_io => VirtualKeyCode::Cyrillic_io, - //ffi::XK_Ukrainian_ie => VirtualKeyCode::Ukrainian_ie, - //ffi::XK_Ukranian_je => VirtualKeyCode::Ukranian_je, - //ffi::XK_Macedonia_dse => VirtualKeyCode::Macedonia_dse, - //ffi::XK_Ukrainian_i => VirtualKeyCode::Ukrainian_i, - //ffi::XK_Ukranian_i => VirtualKeyCode::Ukranian_i, - //ffi::XK_Ukrainian_yi => VirtualKeyCode::Ukrainian_yi, - //ffi::XK_Ukranian_yi => VirtualKeyCode::Ukranian_yi, - //ffi::XK_Cyrillic_je => VirtualKeyCode::Cyrillic_je, - //ffi::XK_Serbian_je => VirtualKeyCode::Serbian_je, - //ffi::XK_Cyrillic_lje => VirtualKeyCode::Cyrillic_lje, - //ffi::XK_Serbian_lje => VirtualKeyCode::Serbian_lje, - //ffi::XK_Cyrillic_nje => VirtualKeyCode::Cyrillic_nje, - //ffi::XK_Serbian_nje => VirtualKeyCode::Serbian_nje, - //ffi::XK_Serbian_tshe => VirtualKeyCode::Serbian_tshe, - //ffi::XK_Macedonia_kje => VirtualKeyCode::Macedonia_kje, - //ffi::XK_Byelorussian_shortu => VirtualKeyCode::Byelorussian_shortu, - //ffi::XK_Cyrillic_dzhe => VirtualKeyCode::Cyrillic_dzhe, - //ffi::XK_Serbian_dze => VirtualKeyCode::Serbian_dze, - //ffi::XK_numerosign => VirtualKeyCode::Numerosign, - //ffi::XK_Serbian_DJE => VirtualKeyCode::Serbian_dje, - //ffi::XK_Macedonia_GJE => VirtualKeyCode::Macedonia_gje, - //ffi::XK_Cyrillic_IO => VirtualKeyCode::Cyrillic_io, - //ffi::XK_Ukrainian_IE => VirtualKeyCode::Ukrainian_ie, - //ffi::XK_Ukranian_JE => VirtualKeyCode::Ukranian_je, - //ffi::XK_Macedonia_DSE => VirtualKeyCode::Macedonia_dse, - //ffi::XK_Ukrainian_I => VirtualKeyCode::Ukrainian_i, - //ffi::XK_Ukranian_I => VirtualKeyCode::Ukranian_i, - //ffi::XK_Ukrainian_YI => VirtualKeyCode::Ukrainian_yi, - //ffi::XK_Ukranian_YI => VirtualKeyCode::Ukranian_yi, - //ffi::XK_Cyrillic_JE => VirtualKeyCode::Cyrillic_je, - //ffi::XK_Serbian_JE => VirtualKeyCode::Serbian_je, - //ffi::XK_Cyrillic_LJE => VirtualKeyCode::Cyrillic_lje, - //ffi::XK_Serbian_LJE => VirtualKeyCode::Serbian_lje, - //ffi::XK_Cyrillic_NJE => VirtualKeyCode::Cyrillic_nje, - //ffi::XK_Serbian_NJE => VirtualKeyCode::Serbian_nje, - //ffi::XK_Serbian_TSHE => VirtualKeyCode::Serbian_tshe, - //ffi::XK_Macedonia_KJE => VirtualKeyCode::Macedonia_kje, - //ffi::XK_Byelorussian_SHORTU => VirtualKeyCode::Byelorussian_shortu, - //ffi::XK_Cyrillic_DZHE => VirtualKeyCode::Cyrillic_dzhe, - //ffi::XK_Serbian_DZE => VirtualKeyCode::Serbian_dze, - //ffi::XK_Cyrillic_yu => VirtualKeyCode::Cyrillic_yu, - //ffi::XK_Cyrillic_a => VirtualKeyCode::Cyrillic_a, - //ffi::XK_Cyrillic_be => VirtualKeyCode::Cyrillic_be, - //ffi::XK_Cyrillic_tse => VirtualKeyCode::Cyrillic_tse, - //ffi::XK_Cyrillic_de => VirtualKeyCode::Cyrillic_de, - //ffi::XK_Cyrillic_ie => VirtualKeyCode::Cyrillic_ie, - //ffi::XK_Cyrillic_ef => VirtualKeyCode::Cyrillic_ef, - //ffi::XK_Cyrillic_ghe => VirtualKeyCode::Cyrillic_ghe, - //ffi::XK_Cyrillic_ha => VirtualKeyCode::Cyrillic_ha, - //ffi::XK_Cyrillic_i => VirtualKeyCode::Cyrillic_i, - //ffi::XK_Cyrillic_shorti => VirtualKeyCode::Cyrillic_shorti, - //ffi::XK_Cyrillic_ka => VirtualKeyCode::Cyrillic_ka, - //ffi::XK_Cyrillic_el => VirtualKeyCode::Cyrillic_el, - //ffi::XK_Cyrillic_em => VirtualKeyCode::Cyrillic_em, - //ffi::XK_Cyrillic_en => VirtualKeyCode::Cyrillic_en, - //ffi::XK_Cyrillic_o => VirtualKeyCode::Cyrillic_o, - //ffi::XK_Cyrillic_pe => VirtualKeyCode::Cyrillic_pe, - //ffi::XK_Cyrillic_ya => VirtualKeyCode::Cyrillic_ya, - //ffi::XK_Cyrillic_er => VirtualKeyCode::Cyrillic_er, - //ffi::XK_Cyrillic_es => VirtualKeyCode::Cyrillic_es, - //ffi::XK_Cyrillic_te => VirtualKeyCode::Cyrillic_te, - //ffi::XK_Cyrillic_u => VirtualKeyCode::Cyrillic_u, - //ffi::XK_Cyrillic_zhe => VirtualKeyCode::Cyrillic_zhe, - //ffi::XK_Cyrillic_ve => VirtualKeyCode::Cyrillic_ve, - //ffi::XK_Cyrillic_softsign => VirtualKeyCode::Cyrillic_softsign, - //ffi::XK_Cyrillic_yeru => VirtualKeyCode::Cyrillic_yeru, - //ffi::XK_Cyrillic_ze => VirtualKeyCode::Cyrillic_ze, - //ffi::XK_Cyrillic_sha => VirtualKeyCode::Cyrillic_sha, - //ffi::XK_Cyrillic_e => VirtualKeyCode::Cyrillic_e, - //ffi::XK_Cyrillic_shcha => VirtualKeyCode::Cyrillic_shcha, - //ffi::XK_Cyrillic_che => VirtualKeyCode::Cyrillic_che, - //ffi::XK_Cyrillic_hardsign => VirtualKeyCode::Cyrillic_hardsign, - //ffi::XK_Cyrillic_YU => VirtualKeyCode::Cyrillic_yu, - //ffi::XK_Cyrillic_A => VirtualKeyCode::Cyrillic_a, - //ffi::XK_Cyrillic_BE => VirtualKeyCode::Cyrillic_be, - //ffi::XK_Cyrillic_TSE => VirtualKeyCode::Cyrillic_tse, - //ffi::XK_Cyrillic_DE => VirtualKeyCode::Cyrillic_de, - //ffi::XK_Cyrillic_IE => VirtualKeyCode::Cyrillic_ie, - //ffi::XK_Cyrillic_EF => VirtualKeyCode::Cyrillic_ef, - //ffi::XK_Cyrillic_GHE => VirtualKeyCode::Cyrillic_ghe, - //ffi::XK_Cyrillic_HA => VirtualKeyCode::Cyrillic_ha, - //ffi::XK_Cyrillic_I => VirtualKeyCode::Cyrillic_i, - //ffi::XK_Cyrillic_SHORTI => VirtualKeyCode::Cyrillic_shorti, - //ffi::XK_Cyrillic_KA => VirtualKeyCode::Cyrillic_ka, - //ffi::XK_Cyrillic_EL => VirtualKeyCode::Cyrillic_el, - //ffi::XK_Cyrillic_EM => VirtualKeyCode::Cyrillic_em, - //ffi::XK_Cyrillic_EN => VirtualKeyCode::Cyrillic_en, - //ffi::XK_Cyrillic_O => VirtualKeyCode::Cyrillic_o, - //ffi::XK_Cyrillic_PE => VirtualKeyCode::Cyrillic_pe, - //ffi::XK_Cyrillic_YA => VirtualKeyCode::Cyrillic_ya, - //ffi::XK_Cyrillic_ER => VirtualKeyCode::Cyrillic_er, - //ffi::XK_Cyrillic_ES => VirtualKeyCode::Cyrillic_es, - //ffi::XK_Cyrillic_TE => VirtualKeyCode::Cyrillic_te, - //ffi::XK_Cyrillic_U => VirtualKeyCode::Cyrillic_u, - //ffi::XK_Cyrillic_ZHE => VirtualKeyCode::Cyrillic_zhe, - //ffi::XK_Cyrillic_VE => VirtualKeyCode::Cyrillic_ve, - //ffi::XK_Cyrillic_SOFTSIGN => VirtualKeyCode::Cyrillic_softsign, - //ffi::XK_Cyrillic_YERU => VirtualKeyCode::Cyrillic_yeru, - //ffi::XK_Cyrillic_ZE => VirtualKeyCode::Cyrillic_ze, - //ffi::XK_Cyrillic_SHA => VirtualKeyCode::Cyrillic_sha, - //ffi::XK_Cyrillic_E => VirtualKeyCode::Cyrillic_e, - //ffi::XK_Cyrillic_SHCHA => VirtualKeyCode::Cyrillic_shcha, - //ffi::XK_Cyrillic_CHE => VirtualKeyCode::Cyrillic_che, - //ffi::XK_Cyrillic_HARDSIGN => VirtualKeyCode::Cyrillic_hardsign, - //ffi::XK_Greek_ALPHAaccent => VirtualKeyCode::Greek_alphaaccent, - //ffi::XK_Greek_EPSILONaccent => VirtualKeyCode::Greek_epsilonaccent, - //ffi::XK_Greek_ETAaccent => VirtualKeyCode::Greek_etaaccent, - //ffi::XK_Greek_IOTAaccent => VirtualKeyCode::Greek_iotaaccent, - //ffi::XK_Greek_IOTAdiaeresis => VirtualKeyCode::Greek_iotadiaeresis, - //ffi::XK_Greek_OMICRONaccent => VirtualKeyCode::Greek_omicronaccent, - //ffi::XK_Greek_UPSILONaccent => VirtualKeyCode::Greek_upsilonaccent, - //ffi::XK_Greek_UPSILONdieresis => VirtualKeyCode::Greek_upsilondieresis, - //ffi::XK_Greek_OMEGAaccent => VirtualKeyCode::Greek_omegaaccent, - //ffi::XK_Greek_accentdieresis => VirtualKeyCode::Greek_accentdieresis, - //ffi::XK_Greek_horizbar => VirtualKeyCode::Greek_horizbar, - //ffi::XK_Greek_alphaaccent => VirtualKeyCode::Greek_alphaaccent, - //ffi::XK_Greek_epsilonaccent => VirtualKeyCode::Greek_epsilonaccent, - //ffi::XK_Greek_etaaccent => VirtualKeyCode::Greek_etaaccent, - //ffi::XK_Greek_iotaaccent => VirtualKeyCode::Greek_iotaaccent, - //ffi::XK_Greek_iotadieresis => VirtualKeyCode::Greek_iotadieresis, - //ffi::XK_Greek_iotaaccentdieresis => VirtualKeyCode::Greek_iotaaccentdieresis, - //ffi::XK_Greek_omicronaccent => VirtualKeyCode::Greek_omicronaccent, - //ffi::XK_Greek_upsilonaccent => VirtualKeyCode::Greek_upsilonaccent, - //ffi::XK_Greek_upsilondieresis => VirtualKeyCode::Greek_upsilondieresis, - //ffi::XK_Greek_upsilonaccentdieresis => VirtualKeyCode::Greek_upsilonaccentdieresis, - //ffi::XK_Greek_omegaaccent => VirtualKeyCode::Greek_omegaaccent, - //ffi::XK_Greek_ALPHA => VirtualKeyCode::Greek_alpha, - //ffi::XK_Greek_BETA => VirtualKeyCode::Greek_beta, - //ffi::XK_Greek_GAMMA => VirtualKeyCode::Greek_gamma, - //ffi::XK_Greek_DELTA => VirtualKeyCode::Greek_delta, - //ffi::XK_Greek_EPSILON => VirtualKeyCode::Greek_epsilon, - //ffi::XK_Greek_ZETA => VirtualKeyCode::Greek_zeta, - //ffi::XK_Greek_ETA => VirtualKeyCode::Greek_eta, - //ffi::XK_Greek_THETA => VirtualKeyCode::Greek_theta, - //ffi::XK_Greek_IOTA => VirtualKeyCode::Greek_iota, - //ffi::XK_Greek_KAPPA => VirtualKeyCode::Greek_kappa, - //ffi::XK_Greek_LAMDA => VirtualKeyCode::Greek_lamda, - //ffi::XK_Greek_LAMBDA => VirtualKeyCode::Greek_lambda, - //ffi::XK_Greek_MU => VirtualKeyCode::Greek_mu, - //ffi::XK_Greek_NU => VirtualKeyCode::Greek_nu, - //ffi::XK_Greek_XI => VirtualKeyCode::Greek_xi, - //ffi::XK_Greek_OMICRON => VirtualKeyCode::Greek_omicron, - //ffi::XK_Greek_PI => VirtualKeyCode::Greek_pi, - //ffi::XK_Greek_RHO => VirtualKeyCode::Greek_rho, - //ffi::XK_Greek_SIGMA => VirtualKeyCode::Greek_sigma, - //ffi::XK_Greek_TAU => VirtualKeyCode::Greek_tau, - //ffi::XK_Greek_UPSILON => VirtualKeyCode::Greek_upsilon, - //ffi::XK_Greek_PHI => VirtualKeyCode::Greek_phi, - //ffi::XK_Greek_CHI => VirtualKeyCode::Greek_chi, - //ffi::XK_Greek_PSI => VirtualKeyCode::Greek_psi, - //ffi::XK_Greek_OMEGA => VirtualKeyCode::Greek_omega, - //ffi::XK_Greek_alpha => VirtualKeyCode::Greek_alpha, - //ffi::XK_Greek_beta => VirtualKeyCode::Greek_beta, - //ffi::XK_Greek_gamma => VirtualKeyCode::Greek_gamma, - //ffi::XK_Greek_delta => VirtualKeyCode::Greek_delta, - //ffi::XK_Greek_epsilon => VirtualKeyCode::Greek_epsilon, - //ffi::XK_Greek_zeta => VirtualKeyCode::Greek_zeta, - //ffi::XK_Greek_eta => VirtualKeyCode::Greek_eta, - //ffi::XK_Greek_theta => VirtualKeyCode::Greek_theta, - //ffi::XK_Greek_iota => VirtualKeyCode::Greek_iota, - //ffi::XK_Greek_kappa => VirtualKeyCode::Greek_kappa, - //ffi::XK_Greek_lamda => VirtualKeyCode::Greek_lamda, - //ffi::XK_Greek_lambda => VirtualKeyCode::Greek_lambda, - //ffi::XK_Greek_mu => VirtualKeyCode::Greek_mu, - //ffi::XK_Greek_nu => VirtualKeyCode::Greek_nu, - //ffi::XK_Greek_xi => VirtualKeyCode::Greek_xi, - //ffi::XK_Greek_omicron => VirtualKeyCode::Greek_omicron, - //ffi::XK_Greek_pi => VirtualKeyCode::Greek_pi, - //ffi::XK_Greek_rho => VirtualKeyCode::Greek_rho, - //ffi::XK_Greek_sigma => VirtualKeyCode::Greek_sigma, - //ffi::XK_Greek_finalsmallsigma => VirtualKeyCode::Greek_finalsmallsigma, - //ffi::XK_Greek_tau => VirtualKeyCode::Greek_tau, - //ffi::XK_Greek_upsilon => VirtualKeyCode::Greek_upsilon, - //ffi::XK_Greek_phi => VirtualKeyCode::Greek_phi, - //ffi::XK_Greek_chi => VirtualKeyCode::Greek_chi, - //ffi::XK_Greek_psi => VirtualKeyCode::Greek_psi, - //ffi::XK_Greek_omega => VirtualKeyCode::Greek_omega, - //ffi::XK_Greek_switch => VirtualKeyCode::Greek_switch, - //ffi::XK_leftradical => VirtualKeyCode::Leftradical, - //ffi::XK_topleftradical => VirtualKeyCode::Topleftradical, - //ffi::XK_horizconnector => VirtualKeyCode::Horizconnector, - //ffi::XK_topintegral => VirtualKeyCode::Topintegral, - //ffi::XK_botintegral => VirtualKeyCode::Botintegral, - //ffi::XK_vertconnector => VirtualKeyCode::Vertconnector, - //ffi::XK_topleftsqbracket => VirtualKeyCode::Topleftsqbracket, - //ffi::XK_botleftsqbracket => VirtualKeyCode::Botleftsqbracket, - //ffi::XK_toprightsqbracket => VirtualKeyCode::Toprightsqbracket, - //ffi::XK_botrightsqbracket => VirtualKeyCode::Botrightsqbracket, - //ffi::XK_topleftparens => VirtualKeyCode::Topleftparens, - //ffi::XK_botleftparens => VirtualKeyCode::Botleftparens, - //ffi::XK_toprightparens => VirtualKeyCode::Toprightparens, - //ffi::XK_botrightparens => VirtualKeyCode::Botrightparens, - //ffi::XK_leftmiddlecurlybrace => VirtualKeyCode::Leftmiddlecurlybrace, - //ffi::XK_rightmiddlecurlybrace => VirtualKeyCode::Rightmiddlecurlybrace, - //ffi::XK_topleftsummation => VirtualKeyCode::Topleftsummation, - //ffi::XK_botleftsummation => VirtualKeyCode::Botleftsummation, - //ffi::XK_topvertsummationconnector => VirtualKeyCode::Topvertsummationconnector, - //ffi::XK_botvertsummationconnector => VirtualKeyCode::Botvertsummationconnector, - //ffi::XK_toprightsummation => VirtualKeyCode::Toprightsummation, - //ffi::XK_botrightsummation => VirtualKeyCode::Botrightsummation, - //ffi::XK_rightmiddlesummation => VirtualKeyCode::Rightmiddlesummation, - //ffi::XK_lessthanequal => VirtualKeyCode::Lessthanequal, - //ffi::XK_notequal => VirtualKeyCode::Notequal, - //ffi::XK_greaterthanequal => VirtualKeyCode::Greaterthanequal, - //ffi::XK_integral => VirtualKeyCode::Integral, - //ffi::XK_therefore => VirtualKeyCode::Therefore, - //ffi::XK_variation => VirtualKeyCode::Variation, - //ffi::XK_infinity => VirtualKeyCode::Infinity, - //ffi::XK_nabla => VirtualKeyCode::Nabla, - //ffi::XK_approximate => VirtualKeyCode::Approximate, - //ffi::XK_similarequal => VirtualKeyCode::Similarequal, - //ffi::XK_ifonlyif => VirtualKeyCode::Ifonlyif, - //ffi::XK_implies => VirtualKeyCode::Implies, - //ffi::XK_identical => VirtualKeyCode::Identical, - //ffi::XK_radical => VirtualKeyCode::Radical, - //ffi::XK_includedin => VirtualKeyCode::Includedin, - //ffi::XK_includes => VirtualKeyCode::Includes, - //ffi::XK_intersection => VirtualKeyCode::Intersection, - //ffi::XK_union => VirtualKeyCode::Union, - //ffi::XK_logicaland => VirtualKeyCode::Logicaland, - //ffi::XK_logicalor => VirtualKeyCode::Logicalor, - //ffi::XK_partialderivative => VirtualKeyCode::Partialderivative, - //ffi::XK_function => VirtualKeyCode::Function, - //ffi::XK_leftarrow => VirtualKeyCode::Leftarrow, - //ffi::XK_uparrow => VirtualKeyCode::Uparrow, - //ffi::XK_rightarrow => VirtualKeyCode::Rightarrow, - //ffi::XK_downarrow => VirtualKeyCode::Downarrow, - //ffi::XK_blank => VirtualKeyCode::Blank, - //ffi::XK_soliddiamond => VirtualKeyCode::Soliddiamond, - //ffi::XK_checkerboard => VirtualKeyCode::Checkerboard, - //ffi::XK_ht => VirtualKeyCode::Ht, - //ffi::XK_ff => VirtualKeyCode::Ff, - //ffi::XK_cr => VirtualKeyCode::Cr, - //ffi::XK_lf => VirtualKeyCode::Lf, - //ffi::XK_nl => VirtualKeyCode::Nl, - //ffi::XK_vt => VirtualKeyCode::Vt, - //ffi::XK_lowrightcorner => VirtualKeyCode::Lowrightcorner, - //ffi::XK_uprightcorner => VirtualKeyCode::Uprightcorner, - //ffi::XK_upleftcorner => VirtualKeyCode::Upleftcorner, - //ffi::XK_lowleftcorner => VirtualKeyCode::Lowleftcorner, - //ffi::XK_crossinglines => VirtualKeyCode::Crossinglines, - //ffi::XK_horizlinescan1 => VirtualKeyCode::Horizlinescan1, - //ffi::XK_horizlinescan3 => VirtualKeyCode::Horizlinescan3, - //ffi::XK_horizlinescan5 => VirtualKeyCode::Horizlinescan5, - //ffi::XK_horizlinescan7 => VirtualKeyCode::Horizlinescan7, - //ffi::XK_horizlinescan9 => VirtualKeyCode::Horizlinescan9, - //ffi::XK_leftt => VirtualKeyCode::Leftt, - //ffi::XK_rightt => VirtualKeyCode::Rightt, - //ffi::XK_bott => VirtualKeyCode::Bott, - //ffi::XK_topt => VirtualKeyCode::Topt, - //ffi::XK_vertbar => VirtualKeyCode::Vertbar, - //ffi::XK_emspace => VirtualKeyCode::Emspace, - //ffi::XK_enspace => VirtualKeyCode::Enspace, - //ffi::XK_em3space => VirtualKeyCode::Em3space, - //ffi::XK_em4space => VirtualKeyCode::Em4space, - //ffi::XK_digitspace => VirtualKeyCode::Digitspace, - //ffi::XK_punctspace => VirtualKeyCode::Punctspace, - //ffi::XK_thinspace => VirtualKeyCode::Thinspace, - //ffi::XK_hairspace => VirtualKeyCode::Hairspace, - //ffi::XK_emdash => VirtualKeyCode::Emdash, - //ffi::XK_endash => VirtualKeyCode::Endash, - //ffi::XK_signifblank => VirtualKeyCode::Signifblank, - //ffi::XK_ellipsis => VirtualKeyCode::Ellipsis, - //ffi::XK_doubbaselinedot => VirtualKeyCode::Doubbaselinedot, - //ffi::XK_onethird => VirtualKeyCode::Onethird, - //ffi::XK_twothirds => VirtualKeyCode::Twothirds, - //ffi::XK_onefifth => VirtualKeyCode::Onefifth, - //ffi::XK_twofifths => VirtualKeyCode::Twofifths, - //ffi::XK_threefifths => VirtualKeyCode::Threefifths, - //ffi::XK_fourfifths => VirtualKeyCode::Fourfifths, - //ffi::XK_onesixth => VirtualKeyCode::Onesixth, - //ffi::XK_fivesixths => VirtualKeyCode::Fivesixths, - //ffi::XK_careof => VirtualKeyCode::Careof, - //ffi::XK_figdash => VirtualKeyCode::Figdash, - //ffi::XK_leftanglebracket => VirtualKeyCode::Leftanglebracket, - //ffi::XK_decimalpoint => VirtualKeyCode::Decimalpoint, - //ffi::XK_rightanglebracket => VirtualKeyCode::Rightanglebracket, - //ffi::XK_marker => VirtualKeyCode::Marker, - //ffi::XK_oneeighth => VirtualKeyCode::Oneeighth, - //ffi::XK_threeeighths => VirtualKeyCode::Threeeighths, - //ffi::XK_fiveeighths => VirtualKeyCode::Fiveeighths, - //ffi::XK_seveneighths => VirtualKeyCode::Seveneighths, - //ffi::XK_trademark => VirtualKeyCode::Trademark, - //ffi::XK_signaturemark => VirtualKeyCode::Signaturemark, - //ffi::XK_trademarkincircle => VirtualKeyCode::Trademarkincircle, - //ffi::XK_leftopentriangle => VirtualKeyCode::Leftopentriangle, - //ffi::XK_rightopentriangle => VirtualKeyCode::Rightopentriangle, - //ffi::XK_emopencircle => VirtualKeyCode::Emopencircle, - //ffi::XK_emopenrectangle => VirtualKeyCode::Emopenrectangle, - //ffi::XK_leftsinglequotemark => VirtualKeyCode::Leftsinglequotemark, - //ffi::XK_rightsinglequotemark => VirtualKeyCode::Rightsinglequotemark, - //ffi::XK_leftdoublequotemark => VirtualKeyCode::Leftdoublequotemark, - //ffi::XK_rightdoublequotemark => VirtualKeyCode::Rightdoublequotemark, - //ffi::XK_prescription => VirtualKeyCode::Prescription, - //ffi::XK_minutes => VirtualKeyCode::Minutes, - //ffi::XK_seconds => VirtualKeyCode::Seconds, - //ffi::XK_latincross => VirtualKeyCode::Latincross, - //ffi::XK_hexagram => VirtualKeyCode::Hexagram, - //ffi::XK_filledrectbullet => VirtualKeyCode::Filledrectbullet, - //ffi::XK_filledlefttribullet => VirtualKeyCode::Filledlefttribullet, - //ffi::XK_filledrighttribullet => VirtualKeyCode::Filledrighttribullet, - //ffi::XK_emfilledcircle => VirtualKeyCode::Emfilledcircle, - //ffi::XK_emfilledrect => VirtualKeyCode::Emfilledrect, - //ffi::XK_enopencircbullet => VirtualKeyCode::Enopencircbullet, - //ffi::XK_enopensquarebullet => VirtualKeyCode::Enopensquarebullet, - //ffi::XK_openrectbullet => VirtualKeyCode::Openrectbullet, - //ffi::XK_opentribulletup => VirtualKeyCode::Opentribulletup, - //ffi::XK_opentribulletdown => VirtualKeyCode::Opentribulletdown, - //ffi::XK_openstar => VirtualKeyCode::Openstar, - //ffi::XK_enfilledcircbullet => VirtualKeyCode::Enfilledcircbullet, - //ffi::XK_enfilledsqbullet => VirtualKeyCode::Enfilledsqbullet, - //ffi::XK_filledtribulletup => VirtualKeyCode::Filledtribulletup, - //ffi::XK_filledtribulletdown => VirtualKeyCode::Filledtribulletdown, - //ffi::XK_leftpointer => VirtualKeyCode::Leftpointer, - //ffi::XK_rightpointer => VirtualKeyCode::Rightpointer, - //ffi::XK_club => VirtualKeyCode::Club, - //ffi::XK_diamond => VirtualKeyCode::Diamond, - //ffi::XK_heart => VirtualKeyCode::Heart, - //ffi::XK_maltesecross => VirtualKeyCode::Maltesecross, - //ffi::XK_dagger => VirtualKeyCode::Dagger, - //ffi::XK_doubledagger => VirtualKeyCode::Doubledagger, - //ffi::XK_checkmark => VirtualKeyCode::Checkmark, - //ffi::XK_ballotcross => VirtualKeyCode::Ballotcross, - //ffi::XK_musicalsharp => VirtualKeyCode::Musicalsharp, - //ffi::XK_musicalflat => VirtualKeyCode::Musicalflat, - //ffi::XK_malesymbol => VirtualKeyCode::Malesymbol, - //ffi::XK_femalesymbol => VirtualKeyCode::Femalesymbol, - //ffi::XK_telephone => VirtualKeyCode::Telephone, - //ffi::XK_telephonerecorder => VirtualKeyCode::Telephonerecorder, - //ffi::XK_phonographcopyright => VirtualKeyCode::Phonographcopyright, - //ffi::XK_caret => VirtualKeyCode::Caret, - //ffi::XK_singlelowquotemark => VirtualKeyCode::Singlelowquotemark, - //ffi::XK_doublelowquotemark => VirtualKeyCode::Doublelowquotemark, - //ffi::XK_cursor => VirtualKeyCode::Cursor, - //ffi::XK_leftcaret => VirtualKeyCode::Leftcaret, - //ffi::XK_rightcaret => VirtualKeyCode::Rightcaret, - //ffi::XK_downcaret => VirtualKeyCode::Downcaret, - //ffi::XK_upcaret => VirtualKeyCode::Upcaret, - //ffi::XK_overbar => VirtualKeyCode::Overbar, - //ffi::XK_downtack => VirtualKeyCode::Downtack, - //ffi::XK_upshoe => VirtualKeyCode::Upshoe, - //ffi::XK_downstile => VirtualKeyCode::Downstile, - //ffi::XK_underbar => VirtualKeyCode::Underbar, - //ffi::XK_jot => VirtualKeyCode::Jot, - //ffi::XK_quad => VirtualKeyCode::Quad, - //ffi::XK_uptack => VirtualKeyCode::Uptack, - //ffi::XK_circle => VirtualKeyCode::Circle, - //ffi::XK_upstile => VirtualKeyCode::Upstile, - //ffi::XK_downshoe => VirtualKeyCode::Downshoe, - //ffi::XK_rightshoe => VirtualKeyCode::Rightshoe, - //ffi::XK_leftshoe => VirtualKeyCode::Leftshoe, - //ffi::XK_lefttack => VirtualKeyCode::Lefttack, - //ffi::XK_righttack => VirtualKeyCode::Righttack, - //ffi::XK_hebrew_doublelowline => VirtualKeyCode::Hebrew_doublelowline, - //ffi::XK_hebrew_aleph => VirtualKeyCode::Hebrew_aleph, - //ffi::XK_hebrew_bet => VirtualKeyCode::Hebrew_bet, - //ffi::XK_hebrew_beth => VirtualKeyCode::Hebrew_beth, - //ffi::XK_hebrew_gimel => VirtualKeyCode::Hebrew_gimel, - //ffi::XK_hebrew_gimmel => VirtualKeyCode::Hebrew_gimmel, - //ffi::XK_hebrew_dalet => VirtualKeyCode::Hebrew_dalet, - //ffi::XK_hebrew_daleth => VirtualKeyCode::Hebrew_daleth, - //ffi::XK_hebrew_he => VirtualKeyCode::Hebrew_he, - //ffi::XK_hebrew_waw => VirtualKeyCode::Hebrew_waw, - //ffi::XK_hebrew_zain => VirtualKeyCode::Hebrew_zain, - //ffi::XK_hebrew_zayin => VirtualKeyCode::Hebrew_zayin, - //ffi::XK_hebrew_chet => VirtualKeyCode::Hebrew_chet, - //ffi::XK_hebrew_het => VirtualKeyCode::Hebrew_het, - //ffi::XK_hebrew_tet => VirtualKeyCode::Hebrew_tet, - //ffi::XK_hebrew_teth => VirtualKeyCode::Hebrew_teth, - //ffi::XK_hebrew_yod => VirtualKeyCode::Hebrew_yod, - //ffi::XK_hebrew_finalkaph => VirtualKeyCode::Hebrew_finalkaph, - //ffi::XK_hebrew_kaph => VirtualKeyCode::Hebrew_kaph, - //ffi::XK_hebrew_lamed => VirtualKeyCode::Hebrew_lamed, - //ffi::XK_hebrew_finalmem => VirtualKeyCode::Hebrew_finalmem, - //ffi::XK_hebrew_mem => VirtualKeyCode::Hebrew_mem, - //ffi::XK_hebrew_finalnun => VirtualKeyCode::Hebrew_finalnun, - //ffi::XK_hebrew_nun => VirtualKeyCode::Hebrew_nun, - //ffi::XK_hebrew_samech => VirtualKeyCode::Hebrew_samech, - //ffi::XK_hebrew_samekh => VirtualKeyCode::Hebrew_samekh, - //ffi::XK_hebrew_ayin => VirtualKeyCode::Hebrew_ayin, - //ffi::XK_hebrew_finalpe => VirtualKeyCode::Hebrew_finalpe, - //ffi::XK_hebrew_pe => VirtualKeyCode::Hebrew_pe, - //ffi::XK_hebrew_finalzade => VirtualKeyCode::Hebrew_finalzade, - //ffi::XK_hebrew_finalzadi => VirtualKeyCode::Hebrew_finalzadi, - //ffi::XK_hebrew_zade => VirtualKeyCode::Hebrew_zade, - //ffi::XK_hebrew_zadi => VirtualKeyCode::Hebrew_zadi, - //ffi::XK_hebrew_qoph => VirtualKeyCode::Hebrew_qoph, - //ffi::XK_hebrew_kuf => VirtualKeyCode::Hebrew_kuf, - //ffi::XK_hebrew_resh => VirtualKeyCode::Hebrew_resh, - //ffi::XK_hebrew_shin => VirtualKeyCode::Hebrew_shin, - //ffi::XK_hebrew_taw => VirtualKeyCode::Hebrew_taw, - //ffi::XK_hebrew_taf => VirtualKeyCode::Hebrew_taf, - //ffi::XK_Hebrew_switch => VirtualKeyCode::Hebrew_switch, - ffi::XF86XK_Back => VirtualKeyCode::NavigateBackward, - ffi::XF86XK_Forward => VirtualKeyCode::NavigateForward, - ffi::XF86XK_Copy => VirtualKeyCode::Copy, - ffi::XF86XK_Paste => VirtualKeyCode::Paste, - ffi::XF86XK_Cut => VirtualKeyCode::Cut, - _ => return None, - }) -} diff --git a/src/platform_impl/linux/x11/ffi.rs b/src/platform_impl/linux/x11/ffi.rs deleted file mode 100644 index 6d7c2089..00000000 --- a/src/platform_impl/linux/x11/ffi.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub use x11_dl::{ - error::OpenError, keysym::*, xcursor::*, xinput::*, xinput2::*, xlib::*, xlib_xcb::*, - xrandr::*, xrender::*, -}; diff --git a/src/platform_impl/linux/x11/ime/callbacks.rs b/src/platform_impl/linux/x11/ime/callbacks.rs deleted file mode 100644 index f254a04e..00000000 --- a/src/platform_impl/linux/x11/ime/callbacks.rs +++ /dev/null @@ -1,175 +0,0 @@ -use std::{collections::HashMap, os::raw::c_char, ptr, sync::Arc}; - -use super::{ffi, XConnection, XError}; - -use super::{ - context::{ImeContext, ImeContextCreationError}, - inner::{close_im, ImeInner}, - input_method::PotentialInputMethods, -}; - -pub unsafe fn xim_set_callback( - xconn: &Arc, - xim: ffi::XIM, - field: *const c_char, - callback: *mut ffi::XIMCallback, -) -> Result<(), XError> { - // It's advisable to wrap variadic FFI functions in our own functions, as we want to minimize - // access that isn't type-checked. - (xconn.xlib.XSetIMValues)(xim, field, callback, ptr::null_mut::<()>()); - xconn.check_errors() -} - -// Set a callback for when an input method matching the current locale modifiers becomes -// available. Note that this has nothing to do with what input methods are open or able to be -// opened, and simply uses the modifiers that are set when the callback is set. -// * This is called per locale modifier, not per input method opened with that locale modifier. -// * Trying to set this for multiple locale modifiers causes problems, i.e. one of the rebuilt -// input contexts would always silently fail to use the input method. -pub unsafe fn set_instantiate_callback( - xconn: &Arc, - client_data: ffi::XPointer, -) -> Result<(), XError> { - (xconn.xlib.XRegisterIMInstantiateCallback)( - xconn.display, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - Some(xim_instantiate_callback), - client_data, - ); - xconn.check_errors() -} - -pub unsafe fn unset_instantiate_callback( - xconn: &Arc, - client_data: ffi::XPointer, -) -> Result<(), XError> { - (xconn.xlib.XUnregisterIMInstantiateCallback)( - xconn.display, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - Some(xim_instantiate_callback), - client_data, - ); - xconn.check_errors() -} - -pub unsafe fn set_destroy_callback( - xconn: &Arc, - im: ffi::XIM, - inner: &ImeInner, -) -> Result<(), XError> { - xim_set_callback( - &xconn, - im, - ffi::XNDestroyCallback_0.as_ptr() as *const _, - &inner.destroy_callback as *const _ as *mut _, - ) -} - -#[derive(Debug)] -enum ReplaceImError { - MethodOpenFailed(PotentialInputMethods), - ContextCreationFailed(ImeContextCreationError), - SetDestroyCallbackFailed(XError), -} - -// Attempt to replace current IM (which may or may not be presently valid) with a new one. This -// includes replacing all existing input contexts and free'ing resources as necessary. This only -// modifies existing state if all operations succeed. -unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> { - let xconn = &(*inner).xconn; - - let (new_im, is_fallback) = { - let new_im = (*inner).potential_input_methods.open_im(xconn, None); - let is_fallback = new_im.is_fallback(); - ( - new_im.ok().ok_or_else(|| { - ReplaceImError::MethodOpenFailed((*inner).potential_input_methods.clone()) - })?, - is_fallback, - ) - }; - - // It's important to always set a destroy callback, since there's otherwise potential for us - // to try to use or free a resource that's already been destroyed on the server. - { - let result = set_destroy_callback(xconn, new_im.im, &*inner); - if result.is_err() { - let _ = close_im(xconn, new_im.im); - } - result - } - .map_err(ReplaceImError::SetDestroyCallbackFailed)?; - - let mut new_contexts = HashMap::new(); - for (window, old_context) in (*inner).contexts.iter() { - let spot = old_context.as_ref().map(|old_context| old_context.ic_spot); - let new_context = { - let result = ImeContext::new(xconn, new_im.im, *window, spot); - if result.is_err() { - let _ = close_im(xconn, new_im.im); - } - result.map_err(ReplaceImError::ContextCreationFailed)? - }; - new_contexts.insert(*window, Some(new_context)); - } - - // If we've made it this far, everything succeeded. - let _ = (*inner).destroy_all_contexts_if_necessary(); - let _ = (*inner).close_im_if_necessary(); - (*inner).im = new_im.im; - (*inner).contexts = new_contexts; - (*inner).is_destroyed = false; - (*inner).is_fallback = is_fallback; - Ok(()) -} - -pub unsafe extern "C" fn xim_instantiate_callback( - _display: *mut ffi::Display, - client_data: ffi::XPointer, - // This field is unsupplied. - _call_data: ffi::XPointer, -) { - let inner: *mut ImeInner = client_data as _; - if !inner.is_null() { - let xconn = &(*inner).xconn; - let result = replace_im(inner); - if result.is_ok() { - let _ = unset_instantiate_callback(xconn, client_data); - (*inner).is_fallback = false; - } else if result.is_err() && (*inner).is_destroyed { - // We have no usable input methods! - result.expect("Failed to reopen input method"); - } - } -} - -// This callback is triggered when the input method is closed on the server end. When this -// happens, XCloseIM/XDestroyIC doesn't need to be called, as the resources have already been -// free'd (attempting to do so causes our connection to freeze). -pub unsafe extern "C" fn xim_destroy_callback( - _xim: ffi::XIM, - client_data: ffi::XPointer, - // This field is unsupplied. - _call_data: ffi::XPointer, -) { - let inner: *mut ImeInner = client_data as _; - if !inner.is_null() { - (*inner).is_destroyed = true; - let xconn = &(*inner).xconn; - if !(*inner).is_fallback { - let _ = set_instantiate_callback(xconn, client_data); - // Attempt to open fallback input method. - let result = replace_im(inner); - if result.is_ok() { - (*inner).is_fallback = true; - } else { - // We have no usable input methods! - result.expect("Failed to open fallback input method"); - } - } - } -} diff --git a/src/platform_impl/linux/x11/ime/context.rs b/src/platform_impl/linux/x11/ime/context.rs deleted file mode 100644 index 8c2ff4cf..00000000 --- a/src/platform_impl/linux/x11/ime/context.rs +++ /dev/null @@ -1,139 +0,0 @@ -use std::{ - os::raw::{c_short, c_void}, - ptr, - sync::Arc, -}; - -use super::{ffi, util, XConnection, XError}; - -#[derive(Debug)] -pub enum ImeContextCreationError { - XError(XError), - Null, -} - -unsafe fn create_pre_edit_attr<'a>( - xconn: &'a Arc, - ic_spot: &'a ffi::XPoint, -) -> util::XSmartPointer<'a, c_void> { - util::XSmartPointer::new( - xconn, - (xconn.xlib.XVaCreateNestedList)( - 0, - ffi::XNSpotLocation_0.as_ptr() as *const _, - ic_spot, - ptr::null_mut::<()>(), - ), - ) - .expect("XVaCreateNestedList returned NULL") -} - -// WARNING: this struct doesn't destroy its XIC resource when dropped. -// This is intentional, as it doesn't have enough information to know whether or not the context -// still exists on the server. Since `ImeInner` has that awareness, destruction must be handled -// through `ImeInner`. -#[derive(Debug)] -pub struct ImeContext { - pub ic: ffi::XIC, - pub ic_spot: ffi::XPoint, -} - -impl ImeContext { - pub unsafe fn new( - xconn: &Arc, - im: ffi::XIM, - window: ffi::Window, - ic_spot: Option, - ) -> Result { - let ic = if let Some(ic_spot) = ic_spot { - ImeContext::create_ic_with_spot(xconn, im, window, ic_spot) - } else { - ImeContext::create_ic(xconn, im, window) - }; - - let ic = ic.ok_or(ImeContextCreationError::Null)?; - xconn - .check_errors() - .map_err(ImeContextCreationError::XError)?; - - Ok(ImeContext { - ic, - ic_spot: ic_spot.unwrap_or_else(|| ffi::XPoint { x: 0, y: 0 }), - }) - } - - unsafe fn create_ic( - xconn: &Arc, - im: ffi::XIM, - window: ffi::Window, - ) -> Option { - let ic = (xconn.xlib.XCreateIC)( - im, - ffi::XNInputStyle_0.as_ptr() as *const _, - ffi::XIMPreeditNothing | ffi::XIMStatusNothing, - ffi::XNClientWindow_0.as_ptr() as *const _, - window, - ptr::null_mut::<()>(), - ); - if ic.is_null() { - None - } else { - Some(ic) - } - } - - unsafe fn create_ic_with_spot( - xconn: &Arc, - im: ffi::XIM, - window: ffi::Window, - ic_spot: ffi::XPoint, - ) -> Option { - let pre_edit_attr = create_pre_edit_attr(xconn, &ic_spot); - let ic = (xconn.xlib.XCreateIC)( - im, - ffi::XNInputStyle_0.as_ptr() as *const _, - ffi::XIMPreeditNothing | ffi::XIMStatusNothing, - ffi::XNClientWindow_0.as_ptr() as *const _, - window, - ffi::XNPreeditAttributes_0.as_ptr() as *const _, - pre_edit_attr.ptr, - ptr::null_mut::<()>(), - ); - if ic.is_null() { - None - } else { - Some(ic) - } - } - - pub fn focus(&self, xconn: &Arc) -> Result<(), XError> { - unsafe { - (xconn.xlib.XSetICFocus)(self.ic); - } - xconn.check_errors() - } - - pub fn unfocus(&self, xconn: &Arc) -> Result<(), XError> { - unsafe { - (xconn.xlib.XUnsetICFocus)(self.ic); - } - xconn.check_errors() - } - - pub fn set_spot(&mut self, xconn: &Arc, x: c_short, y: c_short) { - if self.ic_spot.x == x && self.ic_spot.y == y { - return; - } - self.ic_spot = ffi::XPoint { x, y }; - - unsafe { - let pre_edit_attr = create_pre_edit_attr(xconn, &self.ic_spot); - (xconn.xlib.XSetICValues)( - self.ic, - ffi::XNPreeditAttributes_0.as_ptr() as *const _, - pre_edit_attr.ptr, - ptr::null_mut::<()>(), - ); - } - } -} diff --git a/src/platform_impl/linux/x11/ime/inner.rs b/src/platform_impl/linux/x11/ime/inner.rs deleted file mode 100644 index 011e22aa..00000000 --- a/src/platform_impl/linux/x11/ime/inner.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::{collections::HashMap, mem, ptr, sync::Arc}; - -use super::{ffi, XConnection, XError}; - -use super::{context::ImeContext, input_method::PotentialInputMethods}; - -pub unsafe fn close_im(xconn: &Arc, im: ffi::XIM) -> Result<(), XError> { - (xconn.xlib.XCloseIM)(im); - xconn.check_errors() -} - -pub unsafe fn destroy_ic(xconn: &Arc, ic: ffi::XIC) -> Result<(), XError> { - (xconn.xlib.XDestroyIC)(ic); - xconn.check_errors() -} - -pub struct ImeInner { - pub xconn: Arc, - // WARNING: this is initially null! - pub im: ffi::XIM, - pub potential_input_methods: PotentialInputMethods, - pub contexts: HashMap>, - // WARNING: this is initially zeroed! - pub destroy_callback: ffi::XIMCallback, - // Indicates whether or not the the input method was destroyed on the server end - // (i.e. if ibus/fcitx/etc. was terminated/restarted) - pub is_destroyed: bool, - pub is_fallback: bool, -} - -impl ImeInner { - pub fn new(xconn: Arc, potential_input_methods: PotentialInputMethods) -> Self { - ImeInner { - xconn, - im: ptr::null_mut(), - potential_input_methods, - contexts: HashMap::new(), - destroy_callback: unsafe { mem::zeroed() }, - is_destroyed: false, - is_fallback: false, - } - } - - pub unsafe fn close_im_if_necessary(&self) -> Result { - if !self.is_destroyed { - close_im(&self.xconn, self.im).map(|_| true) - } else { - Ok(false) - } - } - - pub unsafe fn destroy_ic_if_necessary(&self, ic: ffi::XIC) -> Result { - if !self.is_destroyed { - destroy_ic(&self.xconn, ic).map(|_| true) - } else { - Ok(false) - } - } - - pub unsafe fn destroy_all_contexts_if_necessary(&self) -> Result { - for context in self.contexts.values() { - if let &Some(ref context) = context { - self.destroy_ic_if_necessary(context.ic)?; - } - } - Ok(!self.is_destroyed) - } -} diff --git a/src/platform_impl/linux/x11/ime/input_method.rs b/src/platform_impl/linux/x11/ime/input_method.rs deleted file mode 100644 index 142c1501..00000000 --- a/src/platform_impl/linux/x11/ime/input_method.rs +++ /dev/null @@ -1,278 +0,0 @@ -use std::{ - env, - ffi::{CStr, CString, IntoStringError}, - fmt, - os::raw::c_char, - ptr, - sync::Arc, -}; - -use parking_lot::Mutex; - -use super::{ffi, util, XConnection, XError}; - -lazy_static! { - static ref GLOBAL_LOCK: Mutex<()> = Default::default(); -} - -unsafe fn open_im(xconn: &Arc, locale_modifiers: &CStr) -> Option { - let _lock = GLOBAL_LOCK.lock(); - - // XSetLocaleModifiers returns... - // * The current locale modifiers if it's given a NULL pointer. - // * The new locale modifiers if we succeeded in setting them. - // * NULL if the locale modifiers string is malformed or if the - // current locale is not supported by Xlib. - (xconn.xlib.XSetLocaleModifiers)(locale_modifiers.as_ptr()); - - let im = (xconn.xlib.XOpenIM)( - xconn.display, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ); - - if im.is_null() { - None - } else { - Some(im) - } -} - -#[derive(Debug)] -pub struct InputMethod { - pub im: ffi::XIM, - name: String, -} - -impl InputMethod { - fn new(im: ffi::XIM, name: String) -> Self { - InputMethod { im, name } - } -} - -#[derive(Debug)] -pub enum InputMethodResult { - /// Input method used locale modifier from `XMODIFIERS` environment variable. - XModifiers(InputMethod), - /// Input method used internal fallback locale modifier. - Fallback(InputMethod), - /// Input method could not be opened using any locale modifier tried. - Failure, -} - -impl InputMethodResult { - pub fn is_fallback(&self) -> bool { - if let &InputMethodResult::Fallback(_) = self { - true - } else { - false - } - } - - pub fn ok(self) -> Option { - use self::InputMethodResult::*; - match self { - XModifiers(im) | Fallback(im) => Some(im), - Failure => None, - } - } -} - -#[derive(Debug, Clone)] -enum GetXimServersError { - XError(XError), - GetPropertyError(util::GetPropertyError), - InvalidUtf8(IntoStringError), -} - -// The root window has a property named XIM_SERVERS, which contains a list of atoms represeting -// the availabile XIM servers. For instance, if you're using ibus, it would contain an atom named -// "@server=ibus". It's possible for this property to contain multiple atoms, though presumably -// rare. Note that we replace "@server=" with "@im=" in order to match the format of locale -// modifiers, since we don't want a user who's looking at logs to ask "am I supposed to set -// XMODIFIERS to `@server=ibus`?!?" -unsafe fn get_xim_servers(xconn: &Arc) -> Result, GetXimServersError> { - let servers_atom = xconn.get_atom_unchecked(b"XIM_SERVERS\0"); - - let root = (xconn.xlib.XDefaultRootWindow)(xconn.display); - - let mut atoms: Vec = xconn - .get_property(root, servers_atom, ffi::XA_ATOM) - .map_err(GetXimServersError::GetPropertyError)?; - - let mut names: Vec<*const c_char> = Vec::with_capacity(atoms.len()); - (xconn.xlib.XGetAtomNames)( - xconn.display, - atoms.as_mut_ptr(), - atoms.len() as _, - names.as_mut_ptr() as _, - ); - names.set_len(atoms.len()); - - let mut formatted_names = Vec::with_capacity(names.len()); - for name in names { - let string = CStr::from_ptr(name) - .to_owned() - .into_string() - .map_err(GetXimServersError::InvalidUtf8)?; - (xconn.xlib.XFree)(name as _); - formatted_names.push(string.replace("@server=", "@im=")); - } - xconn.check_errors().map_err(GetXimServersError::XError)?; - Ok(formatted_names) -} - -#[derive(Clone)] -struct InputMethodName { - c_string: CString, - string: String, -} - -impl InputMethodName { - pub fn from_string(string: String) -> Self { - let c_string = CString::new(string.clone()) - .expect("String used to construct CString contained null byte"); - InputMethodName { c_string, string } - } - - pub fn from_str(string: &str) -> Self { - let c_string = - CString::new(string).expect("String used to construct CString contained null byte"); - InputMethodName { - c_string, - string: string.to_owned(), - } - } -} - -impl fmt::Debug for InputMethodName { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.string.fmt(f) - } -} - -#[derive(Debug, Clone)] -struct PotentialInputMethod { - name: InputMethodName, - successful: Option, -} - -impl PotentialInputMethod { - pub fn from_string(string: String) -> Self { - PotentialInputMethod { - name: InputMethodName::from_string(string), - successful: None, - } - } - - pub fn from_str(string: &str) -> Self { - PotentialInputMethod { - name: InputMethodName::from_str(string), - successful: None, - } - } - - pub fn reset(&mut self) { - self.successful = None; - } - - pub fn open_im(&mut self, xconn: &Arc) -> Option { - let im = unsafe { open_im(xconn, &self.name.c_string) }; - self.successful = Some(im.is_some()); - im.map(|im| InputMethod::new(im, self.name.string.clone())) - } -} - -// By logging this struct, you get a sequential listing of every locale modifier tried, where it -// came from, and if it succceeded. -#[derive(Debug, Clone)] -pub struct PotentialInputMethods { - // On correctly configured systems, the XMODIFIERS environemnt variable tells us everything we - // need to know. - xmodifiers: Option, - // We have some standard options at our disposal that should ostensibly always work. For users - // who only need compose sequences, this ensures that the program launches without a hitch - // For users who need more sophisticated IME features, this is more or less a silent failure. - // Logging features should be added in the future to allow both audiences to be effectively - // served. - fallbacks: [PotentialInputMethod; 2], - // For diagnostic purposes, we include the list of XIM servers that the server reports as - // being available. - _xim_servers: Result, GetXimServersError>, -} - -impl PotentialInputMethods { - pub fn new(xconn: &Arc) -> Self { - let xmodifiers = env::var("XMODIFIERS") - .ok() - .map(PotentialInputMethod::from_string); - PotentialInputMethods { - // Since passing "" to XSetLocaleModifiers results in it defaulting to the value of - // XMODIFIERS, it's worth noting what happens if XMODIFIERS is also "". If simply - // running the program with `XMODIFIERS="" cargo run`, then assuming XMODIFIERS is - // defined in the profile (or parent environment) then that parent XMODIFIERS is used. - // If that XMODIFIERS value is also "" (i.e. if you ran `export XMODIFIERS=""`), then - // XSetLocaleModifiers uses the default local input method. Note that defining - // XMODIFIERS as "" is different from XMODIFIERS not being defined at all, since in - // that case, we get `None` and end up skipping ahead to the next method. - xmodifiers, - fallbacks: [ - // This is a standard input method that supports compose equences, which should - // always be available. `@im=none` appears to mean the same thing. - PotentialInputMethod::from_str("@im=local"), - // This explicitly specifies to use the implementation-dependent default, though - // that seems to be equivalent to just using the local input method. - PotentialInputMethod::from_str("@im="), - ], - // The XIM_SERVERS property can have surprising values. For instance, when I exited - // ibus to run fcitx, it retained the value denoting ibus. Even more surprising is - // that the fcitx input method could only be successfully opened using "@im=ibus". - // Presumably due to this quirk, it's actually possible to alternate between ibus and - // fcitx in a running application. - _xim_servers: unsafe { get_xim_servers(xconn) }, - } - } - - // This resets the `successful` field of every potential input method, ensuring we have - // accurate information when this struct is re-used by the destruction/instantiation callbacks. - fn reset(&mut self) { - if let Some(ref mut input_method) = self.xmodifiers { - input_method.reset(); - } - - for input_method in &mut self.fallbacks { - input_method.reset(); - } - } - - pub fn open_im( - &mut self, - xconn: &Arc, - callback: Option<&dyn Fn() -> ()>, - ) -> InputMethodResult { - use self::InputMethodResult::*; - - self.reset(); - - if let Some(ref mut input_method) = self.xmodifiers { - let im = input_method.open_im(xconn); - if let Some(im) = im { - return XModifiers(im); - } else { - if let Some(ref callback) = callback { - callback(); - } - } - } - - for input_method in &mut self.fallbacks { - let im = input_method.open_im(xconn); - if let Some(im) = im { - return Fallback(im); - } - } - - Failure - } -} diff --git a/src/platform_impl/linux/x11/ime/mod.rs b/src/platform_impl/linux/x11/ime/mod.rs deleted file mode 100644 index b95da711..00000000 --- a/src/platform_impl/linux/x11/ime/mod.rs +++ /dev/null @@ -1,163 +0,0 @@ -// Important: all XIM calls need to happen from the same thread! - -mod callbacks; -mod context; -mod inner; -mod input_method; - -use std::sync::{ - mpsc::{Receiver, Sender}, - Arc, -}; - -use super::{ffi, util, XConnection, XError}; - -pub use self::context::ImeContextCreationError; -use self::{ - callbacks::*, - context::ImeContext, - inner::{close_im, ImeInner}, - input_method::PotentialInputMethods, -}; - -pub type ImeReceiver = Receiver<(ffi::Window, i16, i16)>; -pub type ImeSender = Sender<(ffi::Window, i16, i16)>; - -#[derive(Debug)] -pub enum ImeCreationError { - OpenFailure(PotentialInputMethods), - SetDestroyCallbackFailed(XError), -} - -pub struct Ime { - xconn: Arc, - // The actual meat of this struct is boxed away, since it needs to have a fixed location in - // memory so we can pass a pointer to it around. - inner: Box, -} - -impl Ime { - pub fn new(xconn: Arc) -> Result { - let potential_input_methods = PotentialInputMethods::new(&xconn); - - let (mut inner, client_data) = { - let mut inner = Box::new(ImeInner::new(xconn, potential_input_methods)); - let inner_ptr = Box::into_raw(inner); - let client_data = inner_ptr as _; - let destroy_callback = ffi::XIMCallback { - client_data, - callback: Some(xim_destroy_callback), - }; - inner = unsafe { Box::from_raw(inner_ptr) }; - inner.destroy_callback = destroy_callback; - (inner, client_data) - }; - - let xconn = Arc::clone(&inner.xconn); - - let input_method = inner.potential_input_methods.open_im( - &xconn, - Some(&|| { - let _ = unsafe { set_instantiate_callback(&xconn, client_data) }; - }), - ); - - let is_fallback = input_method.is_fallback(); - if let Some(input_method) = input_method.ok() { - inner.im = input_method.im; - inner.is_fallback = is_fallback; - unsafe { - let result = set_destroy_callback(&xconn, input_method.im, &*inner) - .map_err(ImeCreationError::SetDestroyCallbackFailed); - if result.is_err() { - let _ = close_im(&xconn, input_method.im); - } - result?; - } - Ok(Ime { xconn, inner }) - } else { - Err(ImeCreationError::OpenFailure(inner.potential_input_methods)) - } - } - - pub fn is_destroyed(&self) -> bool { - self.inner.is_destroyed - } - - // This pattern is used for various methods here: - // Ok(_) indicates that nothing went wrong internally - // Ok(true) indicates that the action was actually performed - // Ok(false) indicates that the action is not presently applicable - pub fn create_context(&mut self, window: ffi::Window) -> Result { - let context = if self.is_destroyed() { - // Create empty entry in map, so that when IME is rebuilt, this window has a context. - None - } else { - Some(unsafe { ImeContext::new(&self.inner.xconn, self.inner.im, window, None) }?) - }; - self.inner.contexts.insert(window, context); - Ok(!self.is_destroyed()) - } - - pub fn get_context(&self, window: ffi::Window) -> Option { - if self.is_destroyed() { - return None; - } - if let Some(&Some(ref context)) = self.inner.contexts.get(&window) { - Some(context.ic) - } else { - None - } - } - - pub fn remove_context(&mut self, window: ffi::Window) -> Result { - if let Some(Some(context)) = self.inner.contexts.remove(&window) { - unsafe { - self.inner.destroy_ic_if_necessary(context.ic)?; - } - Ok(true) - } else { - Ok(false) - } - } - - pub fn focus(&mut self, window: ffi::Window) -> Result { - if self.is_destroyed() { - return Ok(false); - } - if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) { - context.focus(&self.xconn).map(|_| true) - } else { - Ok(false) - } - } - - pub fn unfocus(&mut self, window: ffi::Window) -> Result { - if self.is_destroyed() { - return Ok(false); - } - if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) { - context.unfocus(&self.xconn).map(|_| true) - } else { - Ok(false) - } - } - - pub fn send_xim_spot(&mut self, window: ffi::Window, x: i16, y: i16) { - if self.is_destroyed() { - return; - } - if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) { - context.set_spot(&self.xconn, x as _, y as _); - } - } -} - -impl Drop for Ime { - fn drop(&mut self) { - unsafe { - let _ = self.inner.destroy_all_contexts_if_necessary(); - let _ = self.inner.close_im_if_necessary(); - } - } -} diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs deleted file mode 100644 index a784e8b2..00000000 --- a/src/platform_impl/linux/x11/mod.rs +++ /dev/null @@ -1,704 +0,0 @@ -#![cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" -))] - -mod dnd; -mod event_processor; -mod events; -pub mod ffi; -mod ime; -mod monitor; -pub mod util; -mod window; -mod xdisplay; - -pub use self::{ - monitor::{MonitorHandle, VideoMode}, - window::UnownedWindow, - xdisplay::{XConnection, XError, XNotSupported}, -}; - -use std::{ - cell::RefCell, - collections::{HashMap, HashSet}, - ffi::CStr, - mem::{self, MaybeUninit}, - ops::Deref, - os::raw::*, - ptr, - rc::Rc, - slice, - sync::mpsc::Receiver, - sync::{mpsc, Arc, Weak}, - time::{Duration, Instant}, -}; - -use libc::{self, setlocale, LC_CTYPE}; - -use mio::{unix::SourceFd, Events, Interest, Poll, Token, Waker}; - -use mio_misc::{ - channel::{channel, SendError, Sender}, - queue::NotificationQueue, - NotificationId, -}; - -use self::{ - dnd::{Dnd, DndState}, - event_processor::EventProcessor, - ime::{Ime, ImeCreationError, ImeReceiver, ImeSender}, - util::modifiers::ModifierKeymap, -}; -use crate::{ - error::OsError as RootOsError, - event::{Event, StartCause}, - event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, - platform_impl::{platform::sticky_exit_callback, PlatformSpecificWindowBuilderAttributes}, - window::WindowAttributes, -}; - -const X_TOKEN: Token = Token(0); -const USER_REDRAW_TOKEN: Token = Token(1); - -pub struct EventLoopWindowTarget { - xconn: Arc, - wm_delete_window: ffi::Atom, - net_wm_ping: ffi::Atom, - ime_sender: ImeSender, - root: ffi::Window, - ime: RefCell, - windows: RefCell>>, - redraw_sender: Sender, - _marker: ::std::marker::PhantomData, -} - -pub struct EventLoop { - poll: Poll, - event_processor: EventProcessor, - redraw_channel: Receiver, - user_channel: Receiver, - user_sender: Sender, - target: Rc>, -} - -pub struct EventLoopProxy { - user_sender: Sender, -} - -impl Clone for EventLoopProxy { - fn clone(&self) -> Self { - EventLoopProxy { - user_sender: self.user_sender.clone(), - } - } -} - -impl EventLoop { - pub fn new(xconn: Arc) -> EventLoop { - let root = unsafe { (xconn.xlib.XDefaultRootWindow)(xconn.display) }; - - let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") }; - - let net_wm_ping = unsafe { xconn.get_atom_unchecked(b"_NET_WM_PING\0") }; - - let dnd = Dnd::new(Arc::clone(&xconn)) - .expect("Failed to call XInternAtoms when initializing drag and drop"); - - let (ime_sender, ime_receiver) = mpsc::channel(); - // Input methods will open successfully without setting the locale, but it won't be - // possible to actually commit pre-edit sequences. - unsafe { - // Remember default locale to restore it if target locale is unsupported - // by Xlib - let default_locale = setlocale(LC_CTYPE, ptr::null()); - setlocale(LC_CTYPE, b"\0".as_ptr() as *const _); - - // Check if set locale is supported by Xlib. - // If not, calls to some Xlib functions like `XSetLocaleModifiers` - // will fail. - let locale_supported = (xconn.xlib.XSupportsLocale)() == 1; - if !locale_supported { - let unsupported_locale = setlocale(LC_CTYPE, ptr::null()); - warn!( - "Unsupported locale \"{}\". Restoring default locale \"{}\".", - CStr::from_ptr(unsupported_locale).to_string_lossy(), - CStr::from_ptr(default_locale).to_string_lossy() - ); - // Restore default locale - setlocale(LC_CTYPE, default_locale); - } - } - let ime = RefCell::new({ - let result = Ime::new(Arc::clone(&xconn)); - if let Err(ImeCreationError::OpenFailure(ref state)) = result { - panic!("Failed to open input method: {:#?}", state); - } - result.expect("Failed to set input method destruction callback") - }); - - let randr_event_offset = xconn - .select_xrandr_input(root) - .expect("Failed to query XRandR extension"); - - let xi2ext = unsafe { - let mut ext = XExtension::default(); - - let res = (xconn.xlib.XQueryExtension)( - xconn.display, - b"XInputExtension\0".as_ptr() as *const c_char, - &mut ext.opcode, - &mut ext.first_event_id, - &mut ext.first_error_id, - ); - - if res == ffi::False { - panic!("X server missing XInput extension"); - } - - ext - }; - - unsafe { - let mut xinput_major_ver = ffi::XI_2_Major; - let mut xinput_minor_ver = ffi::XI_2_Minor; - if (xconn.xinput2.XIQueryVersion)( - xconn.display, - &mut xinput_major_ver, - &mut xinput_minor_ver, - ) != ffi::Success as libc::c_int - { - panic!( - "X server has XInput extension {}.{} but does not support XInput2", - xinput_major_ver, xinput_minor_ver, - ); - } - } - - xconn.update_cached_wm_info(root); - - let mut mod_keymap = ModifierKeymap::new(); - mod_keymap.reset_from_x_connection(&xconn); - - let poll = Poll::new().unwrap(); - let waker = Arc::new(Waker::new(poll.registry(), USER_REDRAW_TOKEN).unwrap()); - let queue = Arc::new(NotificationQueue::new(waker)); - - poll.registry() - .register(&mut SourceFd(&xconn.x11_fd), X_TOKEN, Interest::READABLE) - .unwrap(); - - let (user_sender, user_channel) = channel(queue.clone(), NotificationId::gen_next()); - - let (redraw_sender, redraw_channel) = channel(queue, NotificationId::gen_next()); - - let target = Rc::new(RootELW { - p: super::EventLoopWindowTarget::X(EventLoopWindowTarget { - ime, - root, - windows: Default::default(), - _marker: ::std::marker::PhantomData, - ime_sender, - xconn, - wm_delete_window, - net_wm_ping, - redraw_sender, - }), - _marker: ::std::marker::PhantomData, - }); - - let event_processor = EventProcessor { - target: target.clone(), - dnd, - devices: Default::default(), - randr_event_offset, - ime_receiver, - xi2ext, - mod_keymap, - device_mod_state: Default::default(), - num_touch: 0, - first_touch: None, - active_window: None, - }; - - // Register for device hotplug events - // (The request buffer is flushed during `init_device`) - get_xtarget(&target) - .xconn - .select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask) - .queue(); - - event_processor.init_device(ffi::XIAllDevices); - - let result = EventLoop { - poll, - redraw_channel, - user_channel, - user_sender, - event_processor, - target, - }; - - result - } - - pub fn create_proxy(&self) -> EventLoopProxy { - EventLoopProxy { - user_sender: self.user_sender.clone(), - } - } - - pub(crate) fn window_target(&self) -> &RootELW { - &self.target - } - - pub fn run_return(&mut self, mut callback: F) - where - F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), - { - let mut control_flow = ControlFlow::default(); - let mut events = Events::with_capacity(8); - let mut cause = StartCause::Init; - - loop { - sticky_exit_callback( - crate::event::Event::NewEvents(cause), - &self.target, - &mut control_flow, - &mut callback, - ); - - // Process all pending events - self.drain_events(&mut callback, &mut control_flow); - - // Empty the user event buffer - { - while let Ok(event) = self.user_channel.try_recv() { - sticky_exit_callback( - crate::event::Event::UserEvent(event), - &self.target, - &mut control_flow, - &mut callback, - ); - } - } - // send MainEventsCleared - { - sticky_exit_callback( - crate::event::Event::MainEventsCleared, - &self.target, - &mut control_flow, - &mut callback, - ); - } - // Empty the redraw requests - { - let mut windows = HashSet::new(); - - while let Ok(window_id) = self.redraw_channel.try_recv() { - windows.insert(window_id); - } - - for window_id in windows { - let window_id = crate::window::WindowId(super::WindowId::X(window_id)); - sticky_exit_callback( - Event::RedrawRequested(window_id), - &self.target, - &mut control_flow, - &mut callback, - ); - } - } - // send RedrawEventsCleared - { - sticky_exit_callback( - crate::event::Event::RedrawEventsCleared, - &self.target, - &mut control_flow, - &mut callback, - ); - } - - let start = Instant::now(); - let (deadline, timeout); - - match control_flow { - ControlFlow::Exit => break, - ControlFlow::Poll => { - cause = StartCause::Poll; - deadline = None; - timeout = Some(Duration::from_millis(0)); - } - ControlFlow::Wait => { - cause = StartCause::WaitCancelled { - start, - requested_resume: None, - }; - deadline = None; - timeout = None; - } - ControlFlow::WaitUntil(wait_deadline) => { - cause = StartCause::ResumeTimeReached { - start, - requested_resume: wait_deadline, - }; - timeout = if wait_deadline > start { - Some(wait_deadline - start) - } else { - Some(Duration::from_millis(0)) - }; - deadline = Some(wait_deadline); - } - } - - // If the XConnection already contains buffered events, we don't - // need to wait for data on the socket. - if !self.event_processor.poll() { - self.poll.poll(&mut events, timeout).unwrap(); - events.clear(); - } - - let wait_cancelled = deadline.map_or(false, |deadline| Instant::now() < deadline); - - if wait_cancelled { - cause = StartCause::WaitCancelled { - start, - requested_resume: deadline, - }; - } - } - - callback( - crate::event::Event::LoopDestroyed, - &self.target, - &mut control_flow, - ); - } - - pub fn run(mut self, callback: F) -> ! - where - F: 'static + FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), - { - self.run_return(callback); - ::std::process::exit(0); - } - - fn drain_events(&mut self, callback: &mut F, control_flow: &mut ControlFlow) - where - F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), - { - let target = &self.target; - let mut xev = MaybeUninit::uninit(); - - let wt = get_xtarget(&self.target); - - while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } { - let mut xev = unsafe { xev.assume_init() }; - self.event_processor.process_event(&mut xev, |event| { - sticky_exit_callback( - event, - target, - control_flow, - &mut |event, window_target, control_flow| { - if let Event::RedrawRequested(crate::window::WindowId( - super::WindowId::X(wid), - )) = event - { - wt.redraw_sender.send(wid).unwrap(); - } else { - callback(event, window_target, control_flow); - } - }, - ); - }); - } - } -} - -pub(crate) fn get_xtarget(target: &RootELW) -> &EventLoopWindowTarget { - match target.p { - super::EventLoopWindowTarget::X(ref target) => target, - #[cfg(feature = "wayland")] - _ => unreachable!(), - } -} - -impl EventLoopWindowTarget { - /// Returns the `XConnection` of this events loop. - #[inline] - pub fn x_connection(&self) -> &Arc { - &self.xconn - } -} - -impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.user_sender.send(event).map_err(|e| { - EventLoopClosed(if let SendError::Disconnected(x) = e { - x - } else { - unreachable!() - }) - }) - } -} - -struct DeviceInfo<'a> { - xconn: &'a XConnection, - info: *const ffi::XIDeviceInfo, - count: usize, -} - -impl<'a> DeviceInfo<'a> { - fn get(xconn: &'a XConnection, device: c_int) -> Option { - unsafe { - let mut count = 0; - let info = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count); - xconn.check_errors().ok()?; - - if info.is_null() || count == 0 { - None - } else { - Some(DeviceInfo { - xconn, - info, - count: count as usize, - }) - } - } - } -} - -impl<'a> Drop for DeviceInfo<'a> { - fn drop(&mut self) { - assert!(!self.info.is_null()); - unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) }; - } -} - -impl<'a> Deref for DeviceInfo<'a> { - type Target = [ffi::XIDeviceInfo]; - fn deref(&self) -> &Self::Target { - unsafe { slice::from_raw_parts(self.info, self.count) } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WindowId(ffi::Window); - -impl WindowId { - pub unsafe fn dummy() -> Self { - WindowId(0) - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId(c_int); - -impl DeviceId { - pub unsafe fn dummy() -> Self { - DeviceId(0) - } -} - -pub struct Window(Arc); - -impl Deref for Window { - type Target = UnownedWindow; - #[inline] - fn deref(&self) -> &UnownedWindow { - &*self.0 - } -} - -impl Window { - pub fn new( - event_loop: &EventLoopWindowTarget, - attribs: WindowAttributes, - pl_attribs: PlatformSpecificWindowBuilderAttributes, - ) -> Result { - let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?); - event_loop - .windows - .borrow_mut() - .insert(window.id(), Arc::downgrade(&window)); - Ok(Window(window)) - } -} - -impl Drop for Window { - fn drop(&mut self) { - let window = self.deref(); - let xconn = &window.xconn; - unsafe { - (xconn.xlib.XDestroyWindow)(xconn.display, window.id().0); - // If the window was somehow already destroyed, we'll get a `BadWindow` error, which we don't care about. - let _ = xconn.check_errors(); - } - } -} - -/// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure. This is a wrapper to -/// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed -struct GenericEventCookie<'a> { - xconn: &'a XConnection, - cookie: ffi::XGenericEventCookie, -} - -impl<'a> GenericEventCookie<'a> { - fn from_event<'b>( - xconn: &'b XConnection, - event: ffi::XEvent, - ) -> Option> { - unsafe { - let mut cookie: ffi::XGenericEventCookie = From::from(event); - if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True { - Some(GenericEventCookie { xconn, cookie }) - } else { - None - } - } - } -} - -impl<'a> Drop for GenericEventCookie<'a> { - fn drop(&mut self) { - unsafe { - (self.xconn.xlib.XFreeEventData)(self.xconn.display, &mut self.cookie); - } - } -} - -#[derive(Debug, Default, Copy, Clone)] -struct XExtension { - opcode: c_int, - first_event_id: c_int, - first_error_id: c_int, -} - -fn mkwid(w: ffi::Window) -> crate::window::WindowId { - crate::window::WindowId(crate::platform_impl::WindowId::X(WindowId(w))) -} -fn mkdid(w: c_int) -> crate::event::DeviceId { - crate::event::DeviceId(crate::platform_impl::DeviceId::X(DeviceId(w))) -} - -#[derive(Debug)] -struct Device { - name: String, - scroll_axes: Vec<(i32, ScrollAxis)>, - // For master devices, this is the paired device (pointer <-> keyboard). - // For slave devices, this is the master. - attachment: c_int, -} - -#[derive(Debug, Copy, Clone)] -struct ScrollAxis { - increment: f64, - orientation: ScrollOrientation, - position: f64, -} - -#[derive(Debug, Copy, Clone)] -enum ScrollOrientation { - Vertical, - Horizontal, -} - -impl Device { - fn new(el: &EventProcessor, info: &ffi::XIDeviceInfo) -> Self { - let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() }; - let mut scroll_axes = Vec::new(); - - let wt = get_xtarget(&el.target); - - if Device::physical_device(info) { - // Register for global raw events - let mask = ffi::XI_RawMotionMask - | ffi::XI_RawButtonPressMask - | ffi::XI_RawButtonReleaseMask - | ffi::XI_RawKeyPressMask - | ffi::XI_RawKeyReleaseMask; - // The request buffer is flushed when we poll for events - wt.xconn - .select_xinput_events(wt.root, info.deviceid, mask) - .queue(); - - // Identify scroll axes - for class_ptr in Device::classes(info) { - let class = unsafe { &**class_ptr }; - match class._type { - ffi::XIScrollClass => { - let info = unsafe { - mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class) - }; - scroll_axes.push(( - info.number, - ScrollAxis { - increment: info.increment, - orientation: match info.scroll_type { - ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal, - ffi::XIScrollTypeVertical => ScrollOrientation::Vertical, - _ => unreachable!(), - }, - position: 0.0, - }, - )); - } - _ => {} - } - } - } - - let mut device = Device { - name: name.into_owned(), - scroll_axes, - attachment: info.attachment, - }; - device.reset_scroll_position(info); - device - } - - fn reset_scroll_position(&mut self, info: &ffi::XIDeviceInfo) { - if Device::physical_device(info) { - for class_ptr in Device::classes(info) { - let class = unsafe { &**class_ptr }; - match class._type { - ffi::XIValuatorClass => { - let info = unsafe { - mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class) - }; - if let Some(&mut (_, ref mut axis)) = self - .scroll_axes - .iter_mut() - .find(|&&mut (axis, _)| axis == info.number) - { - axis.position = info.value; - } - } - _ => {} - } - } - } - } - - #[inline] - fn physical_device(info: &ffi::XIDeviceInfo) -> bool { - info._use == ffi::XISlaveKeyboard - || info._use == ffi::XISlavePointer - || info._use == ffi::XIFloatingSlave - } - - #[inline] - fn classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo] { - unsafe { - slice::from_raw_parts( - info.classes as *const *const ffi::XIAnyClassInfo, - info.num_classes as usize, - ) - } - } -} diff --git a/src/platform_impl/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs deleted file mode 100644 index 274e0cda..00000000 --- a/src/platform_impl/linux/x11/monitor.rs +++ /dev/null @@ -1,314 +0,0 @@ -use std::os::raw::*; - -use parking_lot::Mutex; - -use super::{ - ffi::{ - RRCrtc, RRCrtcChangeNotifyMask, RRMode, RROutputPropertyNotifyMask, - RRScreenChangeNotifyMask, True, Window, XRRCrtcInfo, XRRScreenResources, - }, - util, XConnection, XError, -}; -use crate::{ - dpi::{PhysicalPosition, PhysicalSize}, - monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, - platform_impl::{MonitorHandle as PlatformMonitorHandle, VideoMode as PlatformVideoMode}, -}; - -// Used for testing. This should always be committed as false. -const DISABLE_MONITOR_LIST_CACHING: bool = false; - -lazy_static! { - static ref MONITORS: Mutex>> = Mutex::default(); -} - -pub fn invalidate_cached_monitor_list() -> Option> { - // We update this lazily. - (*MONITORS.lock()).take() -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct VideoMode { - pub(crate) size: (u32, u32), - pub(crate) bit_depth: u16, - pub(crate) refresh_rate: u16, - pub(crate) native_mode: RRMode, - pub(crate) monitor: Option, -} - -impl VideoMode { - #[inline] - pub fn size(&self) -> PhysicalSize { - self.size.into() - } - - #[inline] - pub fn bit_depth(&self) -> u16 { - self.bit_depth - } - - #[inline] - pub fn refresh_rate(&self) -> u16 { - self.refresh_rate - } - - #[inline] - pub fn monitor(&self) -> RootMonitorHandle { - RootMonitorHandle { - inner: PlatformMonitorHandle::X(self.monitor.clone().unwrap()), - } - } -} - -#[derive(Debug, Clone)] -pub struct MonitorHandle { - /// The actual id - pub(crate) id: RRCrtc, - /// The name of the monitor - pub(crate) name: String, - /// The size of the monitor - dimensions: (u32, u32), - /// The position of the monitor in the X screen - position: (i32, i32), - /// If the monitor is the primary one - primary: bool, - /// The DPI scale factor - pub(crate) scale_factor: f64, - /// Used to determine which windows are on this monitor - pub(crate) rect: util::AaRect, - /// Supported video modes on this monitor - video_modes: Vec, -} - -impl PartialEq for MonitorHandle { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } -} - -impl Eq for MonitorHandle {} - -impl PartialOrd for MonitorHandle { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(&other)) - } -} - -impl Ord for MonitorHandle { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.id.cmp(&other.id) - } -} - -impl std::hash::Hash for MonitorHandle { - fn hash(&self, state: &mut H) { - self.id.hash(state); - } -} - -impl MonitorHandle { - fn new( - xconn: &XConnection, - resources: *mut XRRScreenResources, - id: RRCrtc, - crtc: *mut XRRCrtcInfo, - primary: bool, - ) -> Option { - let (name, scale_factor, video_modes) = unsafe { xconn.get_output_info(resources, crtc)? }; - let dimensions = unsafe { ((*crtc).width as u32, (*crtc).height as u32) }; - let position = unsafe { ((*crtc).x as i32, (*crtc).y as i32) }; - let rect = util::AaRect::new(position, dimensions); - Some(MonitorHandle { - id, - name, - scale_factor, - dimensions, - position, - primary, - rect, - video_modes, - }) - } - - pub fn dummy() -> Self { - MonitorHandle { - id: 0, - name: "".into(), - scale_factor: 1.0, - dimensions: (1, 1), - position: (0, 0), - primary: true, - rect: util::AaRect::new((0, 0), (1, 1)), - video_modes: Vec::new(), - } - } - - pub(crate) fn is_dummy(&self) -> bool { - // Zero is an invalid XID value; no real monitor will have it - self.id == 0 - } - - pub fn name(&self) -> Option { - Some(self.name.clone()) - } - - #[inline] - pub fn native_identifier(&self) -> u32 { - self.id as u32 - } - - pub fn size(&self) -> PhysicalSize { - self.dimensions.into() - } - - pub fn position(&self) -> PhysicalPosition { - self.position.into() - } - - #[inline] - pub fn scale_factor(&self) -> f64 { - self.scale_factor - } - - #[inline] - pub fn video_modes(&self) -> impl Iterator { - let monitor = self.clone(); - self.video_modes.clone().into_iter().map(move |mut x| { - x.monitor = Some(monitor.clone()); - RootVideoMode { - video_mode: PlatformVideoMode::X(x), - } - }) - } -} - -impl XConnection { - pub fn get_monitor_for_window(&self, window_rect: Option) -> MonitorHandle { - let monitors = self.available_monitors(); - - if monitors.is_empty() { - // Return a dummy monitor to avoid panicking - return MonitorHandle::dummy(); - } - - let default = monitors.get(0).unwrap(); - - let window_rect = match window_rect { - Some(rect) => rect, - None => return default.to_owned(), - }; - - let mut largest_overlap = 0; - let mut matched_monitor = default; - for monitor in &monitors { - let overlapping_area = window_rect.get_overlapping_area(&monitor.rect); - if overlapping_area > largest_overlap { - largest_overlap = overlapping_area; - matched_monitor = &monitor; - } - } - - matched_monitor.to_owned() - } - - fn query_monitor_list(&self) -> Vec { - unsafe { - let mut major = 0; - let mut minor = 0; - (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor); - - let root = (self.xlib.XDefaultRootWindow)(self.display); - let resources = if (major == 1 && minor >= 3) || major > 1 { - (self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root) - } else { - // WARNING: this function is supposedly very slow, on the order of hundreds of ms. - // Upon failure, `resources` will be null. - (self.xrandr.XRRGetScreenResources)(self.display, root) - }; - - if resources.is_null() { - panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist."); - } - - let mut available; - let mut has_primary = false; - - let primary = (self.xrandr.XRRGetOutputPrimary)(self.display, root); - available = Vec::with_capacity((*resources).ncrtc as usize); - for crtc_index in 0..(*resources).ncrtc { - let crtc_id = *((*resources).crtcs.offset(crtc_index as isize)); - let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id); - let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0; - if is_active { - let is_primary = *(*crtc).outputs.offset(0) == primary; - has_primary |= is_primary; - MonitorHandle::new(self, resources, crtc_id, crtc, is_primary) - .map(|monitor_id| available.push(monitor_id)); - } - (self.xrandr.XRRFreeCrtcInfo)(crtc); - } - - // If no monitors were detected as being primary, we just pick one ourselves! - if !has_primary { - if let Some(ref mut fallback) = available.first_mut() { - // Setting this here will come in handy if we ever add an `is_primary` method. - fallback.primary = true; - } - } - - (self.xrandr.XRRFreeScreenResources)(resources); - available - } - } - - pub fn available_monitors(&self) -> Vec { - let mut monitors_lock = MONITORS.lock(); - (*monitors_lock) - .as_ref() - .cloned() - .or_else(|| { - let monitors = Some(self.query_monitor_list()); - if !DISABLE_MONITOR_LIST_CACHING { - (*monitors_lock) = monitors.clone(); - } - monitors - }) - .unwrap() - } - - #[inline] - pub fn primary_monitor(&self) -> MonitorHandle { - self.available_monitors() - .into_iter() - .find(|monitor| monitor.primary) - .unwrap_or_else(MonitorHandle::dummy) - } - - pub fn select_xrandr_input(&self, root: Window) -> Result { - let has_xrandr = unsafe { - let mut major = 0; - let mut minor = 0; - (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor) - }; - assert!( - has_xrandr == True, - "[winit] XRandR extension not available." - ); - - let mut event_offset = 0; - let mut error_offset = 0; - let status = unsafe { - (self.xrandr.XRRQueryExtension)(self.display, &mut event_offset, &mut error_offset) - }; - - if status != True { - self.check_errors()?; - unreachable!("[winit] `XRRQueryExtension` failed but no error was received."); - } - - let mask = RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask | RRScreenChangeNotifyMask; - unsafe { (self.xrandr.XRRSelectInput)(self.display, root, mask) }; - - Ok(event_offset) - } -} diff --git a/src/platform_impl/linux/x11/util/atom.rs b/src/platform_impl/linux/x11/util/atom.rs deleted file mode 100644 index 41387224..00000000 --- a/src/platform_impl/linux/x11/util/atom.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::{ - collections::HashMap, - ffi::{CStr, CString}, - fmt::Debug, - os::raw::*, -}; - -use parking_lot::Mutex; - -use super::*; - -type AtomCache = HashMap; - -lazy_static! { - static ref ATOM_CACHE: Mutex = Mutex::new(HashMap::with_capacity(2048)); -} - -impl XConnection { - pub fn get_atom + Debug>(&self, name: T) -> ffi::Atom { - let name = name.as_ref(); - let mut atom_cache_lock = ATOM_CACHE.lock(); - let cached_atom = (*atom_cache_lock).get(name).cloned(); - if let Some(atom) = cached_atom { - atom - } else { - let atom = unsafe { - (self.xlib.XInternAtom)(self.display, name.as_ptr() as *const c_char, ffi::False) - }; - if atom == 0 { - panic!( - "`XInternAtom` failed, which really shouldn't happen. Atom: {:?}, Error: {:#?}", - name, - self.check_errors(), - ); - } - /*println!( - "XInternAtom name:{:?} atom:{:?}", - name, - atom, - );*/ - (*atom_cache_lock).insert(name.to_owned(), atom); - atom - } - } - - pub unsafe fn get_atom_unchecked(&self, name: &[u8]) -> ffi::Atom { - debug_assert!(CStr::from_bytes_with_nul(name).is_ok()); - let name = CStr::from_bytes_with_nul_unchecked(name); - self.get_atom(name) - } - - // Note: this doesn't use caching, for the sake of simplicity. - // If you're dealing with this many atoms, you'll usually want to cache them locally anyway. - pub unsafe fn get_atoms(&self, names: &[*mut c_char]) -> Result, XError> { - let mut atoms = Vec::with_capacity(names.len()); - (self.xlib.XInternAtoms)( - self.display, - names.as_ptr() as *mut _, - names.len() as c_int, - ffi::False, - atoms.as_mut_ptr(), - ); - self.check_errors()?; - atoms.set_len(names.len()); - /*println!( - "XInternAtoms atoms:{:?}", - atoms, - );*/ - Ok(atoms) - } -} diff --git a/src/platform_impl/linux/x11/util/client_msg.rs b/src/platform_impl/linux/x11/util/client_msg.rs deleted file mode 100644 index 2f2b7edf..00000000 --- a/src/platform_impl/linux/x11/util/client_msg.rs +++ /dev/null @@ -1,46 +0,0 @@ -use super::*; - -pub type ClientMsgPayload = [c_long; 5]; - -impl XConnection { - pub fn send_event>( - &self, - target_window: c_ulong, - event_mask: Option, - event: T, - ) -> Flusher<'_> { - let event_mask = event_mask.unwrap_or(ffi::NoEventMask); - unsafe { - (self.xlib.XSendEvent)( - self.display, - target_window, - ffi::False, - event_mask, - &mut event.into(), - ); - } - Flusher::new(self) - } - - pub fn send_client_msg( - &self, - window: c_ulong, // The window this is "about"; not necessarily this window - target_window: c_ulong, // The window we're sending to - message_type: ffi::Atom, - event_mask: Option, - data: ClientMsgPayload, - ) -> Flusher<'_> { - let event = ffi::XClientMessageEvent { - type_: ffi::ClientMessage, - display: self.display, - window, - message_type, - format: c_long::FORMAT as c_int, - data: unsafe { mem::transmute(data) }, - // These fields are ignored by `XSendEvent` - serial: 0, - send_event: 0, - }; - self.send_event(target_window, event_mask, event) - } -} diff --git a/src/platform_impl/linux/x11/util/cursor.rs b/src/platform_impl/linux/x11/util/cursor.rs deleted file mode 100644 index 684af49d..00000000 --- a/src/platform_impl/linux/x11/util/cursor.rs +++ /dev/null @@ -1,129 +0,0 @@ -use crate::window::CursorIcon; - -use super::*; - -impl XConnection { - pub fn set_cursor_icon(&self, window: ffi::Window, cursor: Option) { - let cursor = *self - .cursor_cache - .lock() - .entry(cursor) - .or_insert_with(|| self.get_cursor(cursor)); - - self.update_cursor(window, cursor); - } - - fn create_empty_cursor(&self) -> ffi::Cursor { - let data = 0; - let pixmap = unsafe { - let screen = (self.xlib.XDefaultScreen)(self.display); - let window = (self.xlib.XRootWindow)(self.display, screen); - (self.xlib.XCreateBitmapFromData)(self.display, window, &data, 1, 1) - }; - - if pixmap == 0 { - panic!("failed to allocate pixmap for cursor"); - } - - unsafe { - // We don't care about this color, since it only fills bytes - // in the pixmap which are not 0 in the mask. - let mut dummy_color = MaybeUninit::uninit(); - let cursor = (self.xlib.XCreatePixmapCursor)( - self.display, - pixmap, - pixmap, - dummy_color.as_mut_ptr(), - dummy_color.as_mut_ptr(), - 0, - 0, - ); - (self.xlib.XFreePixmap)(self.display, pixmap); - - cursor - } - } - - fn load_cursor(&self, name: &[u8]) -> ffi::Cursor { - unsafe { - (self.xcursor.XcursorLibraryLoadCursor)(self.display, name.as_ptr() as *const c_char) - } - } - - fn load_first_existing_cursor(&self, names: &[&[u8]]) -> ffi::Cursor { - for name in names.iter() { - let xcursor = self.load_cursor(name); - if xcursor != 0 { - return xcursor; - } - } - 0 - } - - fn get_cursor(&self, cursor: Option) -> ffi::Cursor { - let cursor = match cursor { - Some(cursor) => cursor, - None => return self.create_empty_cursor(), - }; - - let load = |name: &[u8]| self.load_cursor(name); - - let loadn = |names: &[&[u8]]| self.load_first_existing_cursor(names); - - // Try multiple names in some cases where the name - // differs on the desktop environments or themes. - // - // Try the better looking (or more suiting) names first. - match cursor { - CursorIcon::Alias => load(b"link\0"), - CursorIcon::Arrow => load(b"arrow\0"), - CursorIcon::Cell => load(b"plus\0"), - CursorIcon::Copy => load(b"copy\0"), - CursorIcon::Crosshair => load(b"crosshair\0"), - CursorIcon::Default => load(b"left_ptr\0"), - CursorIcon::Hand => loadn(&[b"hand2\0", b"hand1\0"]), - CursorIcon::Help => load(b"question_arrow\0"), - CursorIcon::Move => load(b"move\0"), - CursorIcon::Grab => loadn(&[b"openhand\0", b"grab\0"]), - CursorIcon::Grabbing => loadn(&[b"closedhand\0", b"grabbing\0"]), - CursorIcon::Progress => load(b"left_ptr_watch\0"), - CursorIcon::AllScroll => load(b"all-scroll\0"), - CursorIcon::ContextMenu => load(b"context-menu\0"), - - CursorIcon::NoDrop => loadn(&[b"no-drop\0", b"circle\0"]), - CursorIcon::NotAllowed => load(b"crossed_circle\0"), - - // Resize cursors - CursorIcon::EResize => load(b"right_side\0"), - CursorIcon::NResize => load(b"top_side\0"), - CursorIcon::NeResize => load(b"top_right_corner\0"), - CursorIcon::NwResize => load(b"top_left_corner\0"), - CursorIcon::SResize => load(b"bottom_side\0"), - CursorIcon::SeResize => load(b"bottom_right_corner\0"), - CursorIcon::SwResize => load(b"bottom_left_corner\0"), - CursorIcon::WResize => load(b"left_side\0"), - CursorIcon::EwResize => load(b"h_double_arrow\0"), - CursorIcon::NsResize => load(b"v_double_arrow\0"), - CursorIcon::NwseResize => loadn(&[b"bd_double_arrow\0", b"size_bdiag\0"]), - CursorIcon::NeswResize => loadn(&[b"fd_double_arrow\0", b"size_fdiag\0"]), - CursorIcon::ColResize => loadn(&[b"split_h\0", b"h_double_arrow\0"]), - CursorIcon::RowResize => loadn(&[b"split_v\0", b"v_double_arrow\0"]), - - CursorIcon::Text => loadn(&[b"text\0", b"xterm\0"]), - CursorIcon::VerticalText => load(b"vertical-text\0"), - - CursorIcon::Wait => load(b"watch\0"), - - CursorIcon::ZoomIn => load(b"zoom-in\0"), - CursorIcon::ZoomOut => load(b"zoom-out\0"), - } - } - - fn update_cursor(&self, window: ffi::Window, cursor: ffi::Cursor) { - unsafe { - (self.xlib.XDefineCursor)(self.display, window, cursor); - - self.flush_requests().expect("Failed to set the cursor"); - } - } -} diff --git a/src/platform_impl/linux/x11/util/format.rs b/src/platform_impl/linux/x11/util/format.rs deleted file mode 100644 index 1ab5e01f..00000000 --- a/src/platform_impl/linux/x11/util/format.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::{fmt::Debug, mem, os::raw::*}; - -// This isn't actually the number of the bits in the format. -// X11 does a match on this value to determine which type to call sizeof on. -// Thus, we use 32 for c_long, since 32 maps to c_long which maps to 64. -// ...if that sounds confusing, then you know why this enum is here. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum Format { - Char = 8, - Short = 16, - Long = 32, -} - -impl Format { - pub fn from_format(format: usize) -> Option { - match format { - 8 => Some(Format::Char), - 16 => Some(Format::Short), - 32 => Some(Format::Long), - _ => None, - } - } - - pub fn get_actual_size(&self) -> usize { - match self { - &Format::Char => mem::size_of::(), - &Format::Short => mem::size_of::(), - &Format::Long => mem::size_of::(), - } - } -} - -pub trait Formattable: Debug + Clone + Copy + PartialEq + PartialOrd { - const FORMAT: Format; -} - -// You might be surprised by the absence of c_int, but not as surprised as X11 would be by the presence of it. -impl Formattable for c_schar { - const FORMAT: Format = Format::Char; -} -impl Formattable for c_uchar { - const FORMAT: Format = Format::Char; -} -impl Formattable for c_short { - const FORMAT: Format = Format::Short; -} -impl Formattable for c_ushort { - const FORMAT: Format = Format::Short; -} -impl Formattable for c_long { - const FORMAT: Format = Format::Long; -} -impl Formattable for c_ulong { - const FORMAT: Format = Format::Long; -} diff --git a/src/platform_impl/linux/x11/util/geometry.rs b/src/platform_impl/linux/x11/util/geometry.rs deleted file mode 100644 index d8f466fc..00000000 --- a/src/platform_impl/linux/x11/util/geometry.rs +++ /dev/null @@ -1,389 +0,0 @@ -use std::cmp; - -use super::*; - -// Friendly neighborhood axis-aligned rectangle -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct AaRect { - x: i64, - y: i64, - width: i64, - height: i64, -} - -impl AaRect { - pub fn new((x, y): (i32, i32), (width, height): (u32, u32)) -> Self { - let (x, y) = (x as i64, y as i64); - let (width, height) = (width as i64, height as i64); - AaRect { - x, - y, - width, - height, - } - } - - pub fn contains_point(&self, x: i64, y: i64) -> bool { - x >= self.x && x <= self.x + self.width && y >= self.y && y <= self.y + self.height - } - - pub fn get_overlapping_area(&self, other: &Self) -> i64 { - let x_overlap = cmp::max( - 0, - cmp::min(self.x + self.width, other.x + other.width) - cmp::max(self.x, other.x), - ); - let y_overlap = cmp::max( - 0, - cmp::min(self.y + self.height, other.y + other.height) - cmp::max(self.y, other.y), - ); - x_overlap * y_overlap - } -} - -#[derive(Debug, Default)] -pub struct TranslatedCoords { - pub x_rel_root: c_int, - pub y_rel_root: c_int, - pub child: ffi::Window, -} - -#[derive(Debug, Default)] -pub struct Geometry { - pub root: ffi::Window, - // If you want positions relative to the root window, use translate_coords. - // Note that the overwhelming majority of window managers are reparenting WMs, thus the window - // ID we get from window creation is for a nested window used as the window's client area. If - // you call get_geometry with that window ID, then you'll get the position of that client area - // window relative to the parent it's nested in (the frame), which isn't helpful if you want - // to know the frame position. - pub x_rel_parent: c_int, - pub y_rel_parent: c_int, - // In that same case, this will give you client area size. - pub width: c_uint, - pub height: c_uint, - // xmonad and dwm were the only WMs tested that use the border return at all. - // The majority of WMs seem to simply fill it with 0 unconditionally. - pub border: c_uint, - pub depth: c_uint, -} - -#[derive(Debug, Clone)] -pub struct FrameExtents { - pub left: c_ulong, - pub right: c_ulong, - pub top: c_ulong, - pub bottom: c_ulong, -} - -impl FrameExtents { - pub fn new(left: c_ulong, right: c_ulong, top: c_ulong, bottom: c_ulong) -> Self { - FrameExtents { - left, - right, - top, - bottom, - } - } - - pub fn from_border(border: c_ulong) -> Self { - Self::new(border, border, border, border) - } -} - -#[derive(Debug, Clone)] -pub struct LogicalFrameExtents { - pub left: f64, - pub right: f64, - pub top: f64, - pub bottom: f64, -} - -#[derive(Debug, Clone, PartialEq)] -pub enum FrameExtentsHeuristicPath { - Supported, - UnsupportedNested, - UnsupportedBordered, -} - -#[derive(Debug, Clone)] -pub struct FrameExtentsHeuristic { - pub frame_extents: FrameExtents, - pub heuristic_path: FrameExtentsHeuristicPath, -} - -impl FrameExtentsHeuristic { - pub fn inner_pos_to_outer(&self, x: i32, y: i32) -> (i32, i32) { - use self::FrameExtentsHeuristicPath::*; - if self.heuristic_path != UnsupportedBordered { - ( - x - self.frame_extents.left as i32, - y - self.frame_extents.top as i32, - ) - } else { - (x, y) - } - } - - pub fn inner_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) { - ( - width.saturating_add( - self.frame_extents - .left - .saturating_add(self.frame_extents.right) as u32, - ), - height.saturating_add( - self.frame_extents - .top - .saturating_add(self.frame_extents.bottom) as u32, - ), - ) - } -} - -impl XConnection { - // This is adequate for inner_position - pub fn translate_coords( - &self, - window: ffi::Window, - root: ffi::Window, - ) -> Result { - let mut coords = TranslatedCoords::default(); - - unsafe { - (self.xlib.XTranslateCoordinates)( - self.display, - window, - root, - 0, - 0, - &mut coords.x_rel_root, - &mut coords.y_rel_root, - &mut coords.child, - ); - } - - self.check_errors()?; - Ok(coords) - } - - // This is adequate for inner_size - pub fn get_geometry(&self, window: ffi::Window) -> Result { - let mut geometry = Geometry::default(); - - let _status = unsafe { - (self.xlib.XGetGeometry)( - self.display, - window, - &mut geometry.root, - &mut geometry.x_rel_parent, - &mut geometry.y_rel_parent, - &mut geometry.width, - &mut geometry.height, - &mut geometry.border, - &mut geometry.depth, - ) - }; - - self.check_errors()?; - Ok(geometry) - } - - fn get_frame_extents(&self, window: ffi::Window) -> Option { - let extents_atom = unsafe { self.get_atom_unchecked(b"_NET_FRAME_EXTENTS\0") }; - - if !hint_is_supported(extents_atom) { - return None; - } - - // Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't - // support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to - // be unsupported by many smaller WMs. - let extents: Option> = self - .get_property(window, extents_atom, ffi::XA_CARDINAL) - .ok(); - - extents.and_then(|extents| { - if extents.len() >= 4 { - Some(FrameExtents { - left: extents[0], - right: extents[1], - top: extents[2], - bottom: extents[3], - }) - } else { - None - } - }) - } - - pub fn is_top_level(&self, window: ffi::Window, root: ffi::Window) -> Option { - let client_list_atom = unsafe { self.get_atom_unchecked(b"_NET_CLIENT_LIST\0") }; - - if !hint_is_supported(client_list_atom) { - return None; - } - - let client_list: Option> = self - .get_property(root, client_list_atom, ffi::XA_WINDOW) - .ok(); - - client_list.map(|client_list| client_list.contains(&window)) - } - - fn get_parent_window(&self, window: ffi::Window) -> Result { - let parent = unsafe { - let mut root = 0; - let mut parent = 0; - let mut children: *mut ffi::Window = ptr::null_mut(); - let mut nchildren = 0; - - // What's filled into `parent` if `window` is the root window? - let _status = (self.xlib.XQueryTree)( - self.display, - window, - &mut root, - &mut parent, - &mut children, - &mut nchildren, - ); - - // The list of children isn't used - if children != ptr::null_mut() { - (self.xlib.XFree)(children as *mut _); - } - - parent - }; - self.check_errors().map(|_| parent) - } - - fn climb_hierarchy( - &self, - window: ffi::Window, - root: ffi::Window, - ) -> Result { - let mut outer_window = window; - loop { - let candidate = self.get_parent_window(outer_window)?; - if candidate == root { - break; - } - outer_window = candidate; - } - Ok(outer_window) - } - - pub fn get_frame_extents_heuristic( - &self, - window: ffi::Window, - root: ffi::Window, - ) -> FrameExtentsHeuristic { - use self::FrameExtentsHeuristicPath::*; - - // Position relative to root window. - // With rare exceptions, this is the position of a nested window. Cases where the window - // isn't nested are outlined in the comments throghout this function, but in addition to - // that, fullscreen windows often aren't nested. - let (inner_y_rel_root, child) = { - let coords = self - .translate_coords(window, root) - .expect("Failed to translate window coordinates"); - (coords.y_rel_root, coords.child) - }; - - let (width, height, border) = { - let inner_geometry = self - .get_geometry(window) - .expect("Failed to get inner window geometry"); - ( - inner_geometry.width, - inner_geometry.height, - inner_geometry.border, - ) - }; - - // The first condition is only false for un-nested windows, but isn't always false for - // un-nested windows. Mutter/Muffin/Budgie and Marco present a mysterious discrepancy: - // when y is on the range [0, 2] and if the window has been unfocused since being - // undecorated (or was undecorated upon construction), the first condition is true, - // requiring us to rely on the second condition. - let nested = !(window == child || self.is_top_level(child, root) == Some(true)); - - // Hopefully the WM supports EWMH, allowing us to get exact info on the window frames. - if let Some(mut frame_extents) = self.get_frame_extents(window) { - // Mutter/Muffin/Budgie and Marco preserve their decorated frame extents when - // decorations are disabled, but since the window becomes un-nested, it's easy to - // catch. - if !nested { - frame_extents = FrameExtents::new(0, 0, 0, 0); - } - - // The difference between the nested window's position and the outermost window's - // position is equivalent to the frame size. In most scenarios, this is equivalent to - // manually climbing the hierarchy as is done in the case below. Here's a list of - // known discrepancies: - // * Mutter/Muffin/Budgie gives decorated windows a margin of 9px (only 7px on top) in - // addition to a 1px semi-transparent border. The margin can be easily observed by - // using a screenshot tool to get a screenshot of a selected window, and is - // presumably used for drawing drop shadows. Getting window geometry information - // via hierarchy-climbing results in this margin being included in both the - // position and outer size, so a window positioned at (0, 0) would be reported as - // having a position (-10, -8). - // * Compiz has a drop shadow margin just like Mutter/Muffin/Budgie, though it's 10px - // on all sides, and there's no additional border. - // * Enlightenment otherwise gets a y position equivalent to inner_y_rel_root. - // Without decorations, there's no difference. This is presumably related to - // Enlightenment's fairly unique concept of window position; it interprets - // positions given to XMoveWindow as a client area position rather than a position - // of the overall window. - - FrameExtentsHeuristic { - frame_extents, - heuristic_path: Supported, - } - } else if nested { - // If the position value we have is for a nested window used as the client area, we'll - // just climb up the hierarchy and get the geometry of the outermost window we're - // nested in. - let outer_window = self - .climb_hierarchy(window, root) - .expect("Failed to climb window hierarchy"); - let (outer_y, outer_width, outer_height) = { - let outer_geometry = self - .get_geometry(outer_window) - .expect("Failed to get outer window geometry"); - ( - outer_geometry.y_rel_parent, - outer_geometry.width, - outer_geometry.height, - ) - }; - - // Since we have the geometry of the outermost window and the geometry of the client - // area, we can figure out what's in between. - let diff_x = outer_width.saturating_sub(width); - let diff_y = outer_height.saturating_sub(height); - let offset_y = inner_y_rel_root.saturating_sub(outer_y) as c_uint; - - let left = diff_x / 2; - let right = left; - let top = offset_y; - let bottom = diff_y.saturating_sub(offset_y); - - let frame_extents = - FrameExtents::new(left.into(), right.into(), top.into(), bottom.into()); - FrameExtentsHeuristic { - frame_extents, - heuristic_path: UnsupportedNested, - } - } else { - // This is the case for xmonad and dwm, AKA the only WMs tested that supplied a - // border value. This is convenient, since we can use it to get an accurate frame. - let frame_extents = FrameExtents::from_border(border.into()); - FrameExtentsHeuristic { - frame_extents, - heuristic_path: UnsupportedBordered, - } - } - } -} diff --git a/src/platform_impl/linux/x11/util/hint.rs b/src/platform_impl/linux/x11/util/hint.rs deleted file mode 100644 index 222d8174..00000000 --- a/src/platform_impl/linux/x11/util/hint.rs +++ /dev/null @@ -1,341 +0,0 @@ -use std::slice; -use std::sync::Arc; - -use super::*; - -#[derive(Debug)] -#[allow(dead_code)] -pub enum StateOperation { - Remove = 0, // _NET_WM_STATE_REMOVE - Add = 1, // _NET_WM_STATE_ADD - Toggle = 2, // _NET_WM_STATE_TOGGLE -} - -impl From for StateOperation { - fn from(op: bool) -> Self { - if op { - StateOperation::Add - } else { - StateOperation::Remove - } - } -} - -/// X window type. Maps directly to -/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html). -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum WindowType { - /// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the - /// screen, allowing the desktop environment to have full control of the desktop, without the need for proxying - /// root window clicks. - Desktop, - /// A dock or panel feature. Typically a Window Manager would keep such windows on top of all other windows. - Dock, - /// Toolbar windows. "Torn off" from the main application. - Toolbar, - /// Pinnable menu windows. "Torn off" from the main application. - Menu, - /// A small persistent utility window, such as a palette or toolbox. - Utility, - /// The window is a splash screen displayed as an application is starting up. - Splash, - /// This is a dialog window. - Dialog, - /// A dropdown menu that usually appears when the user clicks on an item in a menu bar. - /// This property is typically used on override-redirect windows. - DropdownMenu, - /// A popup menu that usually appears when the user right clicks on an object. - /// This property is typically used on override-redirect windows. - PopupMenu, - /// A tooltip window. Usually used to show additional information when hovering over an object with the cursor. - /// This property is typically used on override-redirect windows. - Tooltip, - /// The window is a notification. - /// This property is typically used on override-redirect windows. - Notification, - /// This should be used on the windows that are popped up by combo boxes. - /// This property is typically used on override-redirect windows. - Combo, - /// This indicates the the window is being dragged. - /// This property is typically used on override-redirect windows. - Dnd, - /// This is a normal, top-level window. - Normal, -} - -impl Default for WindowType { - fn default() -> Self { - WindowType::Normal - } -} - -impl WindowType { - pub(crate) fn as_atom(&self, xconn: &Arc) -> ffi::Atom { - use self::WindowType::*; - let atom_name: &[u8] = match *self { - Desktop => b"_NET_WM_WINDOW_TYPE_DESKTOP\0", - Dock => b"_NET_WM_WINDOW_TYPE_DOCK\0", - Toolbar => b"_NET_WM_WINDOW_TYPE_TOOLBAR\0", - Menu => b"_NET_WM_WINDOW_TYPE_MENU\0", - Utility => b"_NET_WM_WINDOW_TYPE_UTILITY\0", - Splash => b"_NET_WM_WINDOW_TYPE_SPLASH\0", - Dialog => b"_NET_WM_WINDOW_TYPE_DIALOG\0", - DropdownMenu => b"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0", - PopupMenu => b"_NET_WM_WINDOW_TYPE_POPUP_MENU\0", - Tooltip => b"_NET_WM_WINDOW_TYPE_TOOLTIP\0", - Notification => b"_NET_WM_WINDOW_TYPE_NOTIFICATION\0", - Combo => b"_NET_WM_WINDOW_TYPE_COMBO\0", - Dnd => b"_NET_WM_WINDOW_TYPE_DND\0", - Normal => b"_NET_WM_WINDOW_TYPE_NORMAL\0", - }; - unsafe { xconn.get_atom_unchecked(atom_name) } - } -} - -pub struct MotifHints { - hints: MwmHints, -} - -#[repr(C)] -struct MwmHints { - flags: c_ulong, - functions: c_ulong, - decorations: c_ulong, - input_mode: c_long, - status: c_ulong, -} - -#[allow(dead_code)] -mod mwm { - use libc::c_ulong; - - // Motif WM hints are obsolete, but still widely supported. - // https://stackoverflow.com/a/1909708 - pub const MWM_HINTS_FUNCTIONS: c_ulong = 1 << 0; - pub const MWM_HINTS_DECORATIONS: c_ulong = 1 << 1; - - pub const MWM_FUNC_ALL: c_ulong = 1 << 0; - pub const MWM_FUNC_RESIZE: c_ulong = 1 << 1; - pub const MWM_FUNC_MOVE: c_ulong = 1 << 2; - pub const MWM_FUNC_MINIMIZE: c_ulong = 1 << 3; - pub const MWM_FUNC_MAXIMIZE: c_ulong = 1 << 4; - pub const MWM_FUNC_CLOSE: c_ulong = 1 << 5; -} - -impl MotifHints { - pub fn new() -> MotifHints { - MotifHints { - hints: MwmHints { - flags: 0, - functions: 0, - decorations: 0, - input_mode: 0, - status: 0, - }, - } - } - - pub fn set_decorations(&mut self, decorations: bool) { - self.hints.flags |= mwm::MWM_HINTS_DECORATIONS; - self.hints.decorations = decorations as c_ulong; - } - - pub fn set_maximizable(&mut self, maximizable: bool) { - if maximizable { - self.add_func(mwm::MWM_FUNC_MAXIMIZE); - } else { - self.remove_func(mwm::MWM_FUNC_MAXIMIZE); - } - } - - fn add_func(&mut self, func: c_ulong) { - if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS != 0 { - if self.hints.functions & mwm::MWM_FUNC_ALL != 0 { - self.hints.functions &= !func; - } else { - self.hints.functions |= func; - } - } - } - - fn remove_func(&mut self, func: c_ulong) { - if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS == 0 { - self.hints.flags |= mwm::MWM_HINTS_FUNCTIONS; - self.hints.functions = mwm::MWM_FUNC_ALL; - } - - if self.hints.functions & mwm::MWM_FUNC_ALL != 0 { - self.hints.functions |= func; - } else { - self.hints.functions &= !func; - } - } -} - -impl MwmHints { - fn as_slice(&self) -> &[c_ulong] { - unsafe { slice::from_raw_parts(self as *const _ as *const c_ulong, 5) } - } -} - -pub struct NormalHints<'a> { - size_hints: XSmartPointer<'a, ffi::XSizeHints>, -} - -impl<'a> NormalHints<'a> { - pub fn new(xconn: &'a XConnection) -> Self { - NormalHints { - size_hints: xconn.alloc_size_hints(), - } - } - - pub fn get_position(&self) -> Option<(i32, i32)> { - if has_flag(self.size_hints.flags, ffi::PPosition) { - Some((self.size_hints.x as i32, self.size_hints.y as i32)) - } else { - None - } - } - - pub fn set_position(&mut self, position: Option<(i32, i32)>) { - if let Some((x, y)) = position { - self.size_hints.flags |= ffi::PPosition; - self.size_hints.x = x as c_int; - self.size_hints.y = y as c_int; - } else { - self.size_hints.flags &= !ffi::PPosition; - } - } - - // WARNING: This hint is obsolete - pub fn set_size(&mut self, size: Option<(u32, u32)>) { - if let Some((width, height)) = size { - self.size_hints.flags |= ffi::PSize; - self.size_hints.width = width as c_int; - self.size_hints.height = height as c_int; - } else { - self.size_hints.flags &= !ffi::PSize; - } - } - - pub fn set_max_size(&mut self, max_size: Option<(u32, u32)>) { - if let Some((max_width, max_height)) = max_size { - self.size_hints.flags |= ffi::PMaxSize; - self.size_hints.max_width = max_width as c_int; - self.size_hints.max_height = max_height as c_int; - } else { - self.size_hints.flags &= !ffi::PMaxSize; - } - } - - pub fn set_min_size(&mut self, min_size: Option<(u32, u32)>) { - if let Some((min_width, min_height)) = min_size { - self.size_hints.flags |= ffi::PMinSize; - self.size_hints.min_width = min_width as c_int; - self.size_hints.min_height = min_height as c_int; - } else { - self.size_hints.flags &= !ffi::PMinSize; - } - } - - pub fn set_resize_increments(&mut self, resize_increments: Option<(u32, u32)>) { - if let Some((width_inc, height_inc)) = resize_increments { - self.size_hints.flags |= ffi::PResizeInc; - self.size_hints.width_inc = width_inc as c_int; - self.size_hints.height_inc = height_inc as c_int; - } else { - self.size_hints.flags &= !ffi::PResizeInc; - } - } - - pub fn set_base_size(&mut self, base_size: Option<(u32, u32)>) { - if let Some((base_width, base_height)) = base_size { - self.size_hints.flags |= ffi::PBaseSize; - self.size_hints.base_width = base_width as c_int; - self.size_hints.base_height = base_height as c_int; - } else { - self.size_hints.flags &= !ffi::PBaseSize; - } - } -} - -impl XConnection { - pub fn get_wm_hints( - &self, - window: ffi::Window, - ) -> Result, XError> { - let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) }; - self.check_errors()?; - let wm_hints = if wm_hints.is_null() { - self.alloc_wm_hints() - } else { - XSmartPointer::new(self, wm_hints).unwrap() - }; - Ok(wm_hints) - } - - pub fn set_wm_hints( - &self, - window: ffi::Window, - wm_hints: XSmartPointer<'_, ffi::XWMHints>, - ) -> Flusher<'_> { - unsafe { - (self.xlib.XSetWMHints)(self.display, window, wm_hints.ptr); - } - Flusher::new(self) - } - - pub fn get_normal_hints(&self, window: ffi::Window) -> Result, XError> { - let size_hints = self.alloc_size_hints(); - let mut supplied_by_user = MaybeUninit::uninit(); - unsafe { - (self.xlib.XGetWMNormalHints)( - self.display, - window, - size_hints.ptr, - supplied_by_user.as_mut_ptr(), - ); - } - self.check_errors().map(|_| NormalHints { size_hints }) - } - - pub fn set_normal_hints( - &self, - window: ffi::Window, - normal_hints: NormalHints<'_>, - ) -> Flusher<'_> { - unsafe { - (self.xlib.XSetWMNormalHints)(self.display, window, normal_hints.size_hints.ptr); - } - Flusher::new(self) - } - - pub fn get_motif_hints(&self, window: ffi::Window) -> MotifHints { - let motif_hints = unsafe { self.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") }; - - let mut hints = MotifHints::new(); - - if let Ok(props) = self.get_property::(window, motif_hints, motif_hints) { - hints.hints.flags = props.get(0).cloned().unwrap_or(0); - hints.hints.functions = props.get(1).cloned().unwrap_or(0); - hints.hints.decorations = props.get(2).cloned().unwrap_or(0); - hints.hints.input_mode = props.get(3).cloned().unwrap_or(0) as c_long; - hints.hints.status = props.get(4).cloned().unwrap_or(0); - } - - hints - } - - pub fn set_motif_hints(&self, window: ffi::Window, hints: &MotifHints) -> Flusher<'_> { - let motif_hints = unsafe { self.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") }; - - self.change_property( - window, - motif_hints, - motif_hints, - PropMode::Replace, - hints.hints.as_slice(), - ) - } -} diff --git a/src/platform_impl/linux/x11/util/icon.rs b/src/platform_impl/linux/x11/util/icon.rs deleted file mode 100644 index eb13d48b..00000000 --- a/src/platform_impl/linux/x11/util/icon.rs +++ /dev/null @@ -1,35 +0,0 @@ -use super::*; -use crate::icon::{Icon, Pixel, PIXEL_SIZE}; - -impl Pixel { - pub fn to_packed_argb(&self) -> Cardinal { - let mut cardinal = 0; - assert!(CARDINAL_SIZE >= PIXEL_SIZE); - let as_bytes = &mut cardinal as *mut _ as *mut u8; - unsafe { - *as_bytes.offset(0) = self.b; - *as_bytes.offset(1) = self.g; - *as_bytes.offset(2) = self.r; - *as_bytes.offset(3) = self.a; - } - cardinal - } -} - -impl Icon { - pub(crate) fn to_cardinals(&self) -> Vec { - let rgba_icon = &self.inner; - assert_eq!(rgba_icon.rgba.len() % PIXEL_SIZE, 0); - let pixel_count = rgba_icon.rgba.len() / PIXEL_SIZE; - assert_eq!(pixel_count, (rgba_icon.width * rgba_icon.height) as usize); - let mut data = Vec::with_capacity(pixel_count); - data.push(rgba_icon.width as Cardinal); - data.push(rgba_icon.height as Cardinal); - let pixels = rgba_icon.rgba.as_ptr() as *const Pixel; - for pixel_index in 0..pixel_count { - let pixel = unsafe { &*pixels.offset(pixel_index as isize) }; - data.push(pixel.to_packed_argb()); - } - data - } -} diff --git a/src/platform_impl/linux/x11/util/input.rs b/src/platform_impl/linux/x11/util/input.rs deleted file mode 100644 index e9f45aee..00000000 --- a/src/platform_impl/linux/x11/util/input.rs +++ /dev/null @@ -1,190 +0,0 @@ -use std::{slice, str}; - -use super::*; -use crate::event::ModifiersState; - -pub const VIRTUAL_CORE_POINTER: c_int = 2; -pub const VIRTUAL_CORE_KEYBOARD: c_int = 3; - -// A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to -// re-allocate (and make another round-trip) in the *vast* majority of cases. -// To test if `lookup_utf8` works correctly, set this to 1. -const TEXT_BUFFER_SIZE: usize = 1024; - -impl ModifiersState { - pub(crate) fn from_x11(state: &ffi::XIModifierState) -> Self { - ModifiersState::from_x11_mask(state.effective as c_uint) - } - - pub(crate) fn from_x11_mask(mask: c_uint) -> Self { - let mut m = ModifiersState::empty(); - m.set(ModifiersState::ALT, mask & ffi::Mod1Mask != 0); - m.set(ModifiersState::SHIFT, mask & ffi::ShiftMask != 0); - m.set(ModifiersState::CTRL, mask & ffi::ControlMask != 0); - m.set(ModifiersState::LOGO, mask & ffi::Mod4Mask != 0); - m - } -} - -// NOTE: Some of these fields are not used, but may be of use in the future. -pub struct PointerState<'a> { - xconn: &'a XConnection, - pub root: ffi::Window, - pub child: ffi::Window, - pub root_x: c_double, - pub root_y: c_double, - pub win_x: c_double, - pub win_y: c_double, - buttons: ffi::XIButtonState, - modifiers: ffi::XIModifierState, - pub group: ffi::XIGroupState, - pub relative_to_window: bool, -} - -impl<'a> PointerState<'a> { - pub fn get_modifier_state(&self) -> ModifiersState { - ModifiersState::from_x11(&self.modifiers) - } -} - -impl<'a> Drop for PointerState<'a> { - fn drop(&mut self) { - if !self.buttons.mask.is_null() { - unsafe { - // This is why you need to read the docs carefully... - (self.xconn.xlib.XFree)(self.buttons.mask as _); - } - } - } -} - -impl XConnection { - pub fn select_xinput_events( - &self, - window: c_ulong, - device_id: c_int, - mask: i32, - ) -> Flusher<'_> { - let mut event_mask = ffi::XIEventMask { - deviceid: device_id, - mask: &mask as *const _ as *mut c_uchar, - mask_len: mem::size_of_val(&mask) as c_int, - }; - unsafe { - (self.xinput2.XISelectEvents)( - self.display, - window, - &mut event_mask as *mut ffi::XIEventMask, - 1, // number of masks to read from pointer above - ); - } - Flusher::new(self) - } - - #[allow(dead_code)] - pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option> { - let status = unsafe { (self.xlib.XkbSelectEvents)(self.display, device_id, mask, mask) }; - if status == ffi::True { - Some(Flusher::new(self)) - } else { - None - } - } - - pub fn query_pointer( - &self, - window: ffi::Window, - device_id: c_int, - ) -> Result, XError> { - unsafe { - let mut root = 0; - let mut child = 0; - let mut root_x = 0.0; - let mut root_y = 0.0; - let mut win_x = 0.0; - let mut win_y = 0.0; - let mut buttons = Default::default(); - let mut modifiers = Default::default(); - let mut group = Default::default(); - - let relative_to_window = (self.xinput2.XIQueryPointer)( - self.display, - device_id, - window, - &mut root, - &mut child, - &mut root_x, - &mut root_y, - &mut win_x, - &mut win_y, - &mut buttons, - &mut modifiers, - &mut group, - ) == ffi::True; - - self.check_errors()?; - - Ok(PointerState { - xconn: self, - root, - child, - root_x, - root_y, - win_x, - win_y, - buttons, - modifiers, - group, - relative_to_window, - }) - } - } - - fn lookup_utf8_inner( - &self, - ic: ffi::XIC, - key_event: &mut ffi::XKeyEvent, - buffer: *mut u8, - size: usize, - ) -> (ffi::KeySym, ffi::Status, c_int) { - let mut keysym: ffi::KeySym = 0; - let mut status: ffi::Status = 0; - let count = unsafe { - (self.xlib.Xutf8LookupString)( - ic, - key_event, - buffer as *mut c_char, - size as c_int, - &mut keysym, - &mut status, - ) - }; - (keysym, status, count) - } - - pub fn lookup_utf8(&self, ic: ffi::XIC, key_event: &mut ffi::XKeyEvent) -> String { - // `assume_init` is safe here because the array consists of `MaybeUninit` values, - // which do not require initialization. - let mut buffer: [MaybeUninit; TEXT_BUFFER_SIZE] = - unsafe { MaybeUninit::uninit().assume_init() }; - // If the buffer overflows, we'll make a new one on the heap. - let mut vec; - - let (_, status, count) = - self.lookup_utf8_inner(ic, key_event, buffer.as_mut_ptr() as *mut u8, buffer.len()); - - let bytes = if status == ffi::XBufferOverflow { - vec = Vec::with_capacity(count as usize); - let (_, _, new_count) = - self.lookup_utf8_inner(ic, key_event, vec.as_mut_ptr(), vec.capacity()); - debug_assert_eq!(count, new_count); - - unsafe { vec.set_len(count as usize) }; - &vec[..count as usize] - } else { - unsafe { slice::from_raw_parts(buffer.as_ptr() as *const u8, count as usize) } - }; - - str::from_utf8(bytes).unwrap_or("").to_string() - } -} diff --git a/src/platform_impl/linux/x11/util/keys.rs b/src/platform_impl/linux/x11/util/keys.rs deleted file mode 100644 index fc0c9d90..00000000 --- a/src/platform_impl/linux/x11/util/keys.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::{iter::Enumerate, ptr, slice::Iter}; - -use super::*; - -pub struct Keymap { - keys: [u8; 32], -} - -pub struct KeymapIter<'a> { - iter: Enumerate>, - index: usize, - item: Option, -} - -impl Keymap { - pub fn iter(&self) -> KeymapIter<'_> { - KeymapIter { - iter: self.keys.iter().enumerate(), - index: 0, - item: None, - } - } -} - -impl<'a> IntoIterator for &'a Keymap { - type Item = ffi::KeyCode; - type IntoIter = KeymapIter<'a>; - - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -impl Iterator for KeymapIter<'_> { - type Item = ffi::KeyCode; - - fn next(&mut self) -> Option { - if self.item.is_none() { - while let Some((index, &item)) = self.iter.next() { - if item != 0 { - self.index = index; - self.item = Some(item); - break; - } - } - } - - self.item.take().map(|item| { - debug_assert!(item != 0); - - let bit = first_bit(item); - - if item != bit { - // Remove the first bit; save the rest for further iterations - self.item = Some(item ^ bit); - } - - let shift = bit.trailing_zeros() + (self.index * 8) as u32; - shift as ffi::KeyCode - }) - } -} - -impl XConnection { - pub fn keycode_to_keysym(&self, keycode: ffi::KeyCode) -> ffi::KeySym { - unsafe { (self.xlib.XKeycodeToKeysym)(self.display, keycode, 0) } - } - - pub fn lookup_keysym(&self, xkev: &mut ffi::XKeyEvent) -> ffi::KeySym { - let mut keysym = 0; - - unsafe { - (self.xlib.XLookupString)(xkev, ptr::null_mut(), 0, &mut keysym, ptr::null_mut()); - } - - keysym - } - - pub fn query_keymap(&self) -> Keymap { - let mut keys = [0; 32]; - - unsafe { - (self.xlib.XQueryKeymap)(self.display, keys.as_mut_ptr() as *mut c_char); - } - - Keymap { keys } - } -} - -fn first_bit(b: u8) -> u8 { - 1 << b.trailing_zeros() -} diff --git a/src/platform_impl/linux/x11/util/memory.rs b/src/platform_impl/linux/x11/util/memory.rs deleted file mode 100644 index c85cc371..00000000 --- a/src/platform_impl/linux/x11/util/memory.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::ops::{Deref, DerefMut}; - -use super::*; - -pub struct XSmartPointer<'a, T> { - xconn: &'a XConnection, - pub ptr: *mut T, -} - -impl<'a, T> XSmartPointer<'a, T> { - // You're responsible for only passing things to this that should be XFree'd. - // Returns None if ptr is null. - pub fn new(xconn: &'a XConnection, ptr: *mut T) -> Option { - if !ptr.is_null() { - Some(XSmartPointer { xconn, ptr }) - } else { - None - } - } -} - -impl<'a, T> Deref for XSmartPointer<'a, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.ptr } - } -} - -impl<'a, T> DerefMut for XSmartPointer<'a, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.ptr } - } -} - -impl<'a, T> Drop for XSmartPointer<'a, T> { - fn drop(&mut self) { - unsafe { - (self.xconn.xlib.XFree)(self.ptr as *mut _); - } - } -} - -impl XConnection { - pub fn alloc_class_hint(&self) -> XSmartPointer<'_, ffi::XClassHint> { - XSmartPointer::new(self, unsafe { (self.xlib.XAllocClassHint)() }) - .expect("`XAllocClassHint` returned null; out of memory") - } - - pub fn alloc_size_hints(&self) -> XSmartPointer<'_, ffi::XSizeHints> { - XSmartPointer::new(self, unsafe { (self.xlib.XAllocSizeHints)() }) - .expect("`XAllocSizeHints` returned null; out of memory") - } - - pub fn alloc_wm_hints(&self) -> XSmartPointer<'_, ffi::XWMHints> { - XSmartPointer::new(self, unsafe { (self.xlib.XAllocWMHints)() }) - .expect("`XAllocWMHints` returned null; out of memory") - } -} diff --git a/src/platform_impl/linux/x11/util/mod.rs b/src/platform_impl/linux/x11/util/mod.rs deleted file mode 100644 index df3d8913..00000000 --- a/src/platform_impl/linux/x11/util/mod.rs +++ /dev/null @@ -1,97 +0,0 @@ -// Welcome to the util module, where we try to keep you from shooting yourself in the foot. -// *results may vary - -mod atom; -mod client_msg; -mod cursor; -mod format; -mod geometry; -mod hint; -mod icon; -mod input; -pub mod keys; -mod memory; -pub mod modifiers; -mod randr; -mod window_property; -mod wm; - -pub use self::{ - atom::*, client_msg::*, format::*, geometry::*, hint::*, icon::*, input::*, memory::*, - randr::*, window_property::*, wm::*, -}; - -use std::{ - mem::{self, MaybeUninit}, - ops::BitAnd, - os::raw::*, - ptr, -}; - -use super::{ffi, XConnection, XError}; - -pub fn maybe_change(field: &mut Option, value: T) -> bool { - let wrapped = Some(value); - if *field != wrapped { - *field = wrapped; - true - } else { - false - } -} - -pub fn has_flag(bitset: T, flag: T) -> bool -where - T: Copy + PartialEq + BitAnd, -{ - bitset & flag == flag -} - -#[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."] -pub struct Flusher<'a> { - xconn: &'a XConnection, -} - -impl<'a> Flusher<'a> { - pub fn new(xconn: &'a XConnection) -> Self { - Flusher { xconn } - } - - // "I want this request sent now!" - pub fn flush(self) -> Result<(), XError> { - self.xconn.flush_requests() - } - - // "I want the response now too!" - pub fn sync(self) -> Result<(), XError> { - self.xconn.sync_with_server() - } - - // "I'm aware that this request hasn't been sent, and I'm okay with waiting." - pub fn queue(self) {} -} - -impl XConnection { - // This is impoartant, so pay attention! - // Xlib has an output buffer, and tries to hide the async nature of X from you. - // This buffer contains the requests you make, and is flushed under various circumstances: - // 1. `XPending`, `XNextEvent`, and `XWindowEvent` flush "as needed" - // 2. `XFlush` explicitly flushes - // 3. `XSync` flushes and blocks until all requests are responded to - // 4. Calls that have a return dependent on a response (i.e. `XGetWindowProperty`) sync internally. - // When in doubt, check the X11 source; if a function calls `_XReply`, it flushes and waits. - // All util functions that abstract an async function will return a `Flusher`. - pub fn flush_requests(&self) -> Result<(), XError> { - unsafe { (self.xlib.XFlush)(self.display) }; - //println!("XFlush"); - // This isn't necessarily a useful time to check for errors (since our request hasn't - // necessarily been processed yet) - self.check_errors() - } - - pub fn sync_with_server(&self) -> Result<(), XError> { - unsafe { (self.xlib.XSync)(self.display, ffi::False) }; - //println!("XSync"); - self.check_errors() - } -} diff --git a/src/platform_impl/linux/x11/util/modifiers.rs b/src/platform_impl/linux/x11/util/modifiers.rs deleted file mode 100644 index 7c951997..00000000 --- a/src/platform_impl/linux/x11/util/modifiers.rs +++ /dev/null @@ -1,187 +0,0 @@ -use std::{collections::HashMap, slice}; - -use super::*; - -use crate::event::{ElementState, ModifiersState}; - -// Offsets within XModifierKeymap to each set of keycodes. -// We are only interested in Shift, Control, Alt, and Logo. -// -// There are 8 sets total. The order of keycode sets is: -// Shift, Lock, Control, Mod1 (Alt), Mod2, Mod3, Mod4 (Logo), Mod5 -// -// https://tronche.com/gui/x/xlib/input/XSetModifierMapping.html -const SHIFT_OFFSET: usize = 0; -const CONTROL_OFFSET: usize = 2; -const ALT_OFFSET: usize = 3; -const LOGO_OFFSET: usize = 6; -const NUM_MODS: usize = 8; - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum Modifier { - Alt, - Ctrl, - Shift, - Logo, -} - -#[derive(Debug, Default)] -pub struct ModifierKeymap { - // Maps keycodes to modifiers - keys: HashMap, -} - -#[derive(Clone, Debug, Default)] -pub struct ModifierKeyState { - // Contains currently pressed modifier keys and their corresponding modifiers - keys: HashMap, - state: ModifiersState, -} - -impl ModifierKeymap { - pub fn new() -> ModifierKeymap { - ModifierKeymap::default() - } - - pub fn get_modifier(&self, keycode: ffi::KeyCode) -> Option { - self.keys.get(&keycode).cloned() - } - - pub fn reset_from_x_connection(&mut self, xconn: &XConnection) { - unsafe { - let keymap = (xconn.xlib.XGetModifierMapping)(xconn.display); - - if keymap.is_null() { - panic!("failed to allocate XModifierKeymap"); - } - - self.reset_from_x_keymap(&*keymap); - - (xconn.xlib.XFreeModifiermap)(keymap); - } - } - - pub fn reset_from_x_keymap(&mut self, keymap: &ffi::XModifierKeymap) { - let keys_per_mod = keymap.max_keypermod as usize; - - let keys = unsafe { - slice::from_raw_parts(keymap.modifiermap as *const _, keys_per_mod * NUM_MODS) - }; - - self.keys.clear(); - - self.read_x_keys(keys, SHIFT_OFFSET, keys_per_mod, Modifier::Shift); - self.read_x_keys(keys, CONTROL_OFFSET, keys_per_mod, Modifier::Ctrl); - self.read_x_keys(keys, ALT_OFFSET, keys_per_mod, Modifier::Alt); - self.read_x_keys(keys, LOGO_OFFSET, keys_per_mod, Modifier::Logo); - } - - fn read_x_keys( - &mut self, - keys: &[ffi::KeyCode], - offset: usize, - keys_per_mod: usize, - modifier: Modifier, - ) { - let start = offset * keys_per_mod; - let end = start + keys_per_mod; - - for &keycode in &keys[start..end] { - if keycode != 0 { - self.keys.insert(keycode, modifier); - } - } - } -} - -impl ModifierKeyState { - pub fn update_keymap(&mut self, mods: &ModifierKeymap) { - self.keys.retain(|k, v| { - if let Some(m) = mods.get_modifier(*k) { - *v = m; - true - } else { - false - } - }); - - self.reset_state(); - } - - pub fn update_state( - &mut self, - state: &ModifiersState, - except: Option, - ) -> Option { - let mut new_state = *state; - - match except { - Some(Modifier::Alt) => new_state.set(ModifiersState::ALT, self.state.alt()), - Some(Modifier::Ctrl) => new_state.set(ModifiersState::CTRL, self.state.ctrl()), - Some(Modifier::Shift) => new_state.set(ModifiersState::SHIFT, self.state.shift()), - Some(Modifier::Logo) => new_state.set(ModifiersState::LOGO, self.state.logo()), - None => (), - } - - if self.state == new_state { - None - } else { - self.keys.retain(|_k, v| get_modifier(&new_state, *v)); - self.state = new_state; - Some(new_state) - } - } - - pub fn modifiers(&self) -> ModifiersState { - self.state - } - - pub fn key_event(&mut self, state: ElementState, keycode: ffi::KeyCode, modifier: Modifier) { - match state { - ElementState::Pressed => self.key_press(keycode, modifier), - ElementState::Released => self.key_release(keycode), - } - } - - pub fn key_press(&mut self, keycode: ffi::KeyCode, modifier: Modifier) { - self.keys.insert(keycode, modifier); - - set_modifier(&mut self.state, modifier, true); - } - - pub fn key_release(&mut self, keycode: ffi::KeyCode) { - if let Some(modifier) = self.keys.remove(&keycode) { - if self.keys.values().find(|&&m| m == modifier).is_none() { - set_modifier(&mut self.state, modifier, false); - } - } - } - - fn reset_state(&mut self) { - let mut new_state = ModifiersState::default(); - - for &m in self.keys.values() { - set_modifier(&mut new_state, m, true); - } - - self.state = new_state; - } -} - -fn get_modifier(state: &ModifiersState, modifier: Modifier) -> bool { - match modifier { - Modifier::Alt => state.alt(), - Modifier::Ctrl => state.ctrl(), - Modifier::Shift => state.shift(), - Modifier::Logo => state.logo(), - } -} - -fn set_modifier(state: &mut ModifiersState, modifier: Modifier, value: bool) { - match modifier { - Modifier::Alt => state.set(ModifiersState::ALT, value), - Modifier::Ctrl => state.set(ModifiersState::CTRL, value), - Modifier::Shift => state.set(ModifiersState::SHIFT, value), - Modifier::Logo => state.set(ModifiersState::LOGO, value), - } -} diff --git a/src/platform_impl/linux/x11/util/randr.rs b/src/platform_impl/linux/x11/util/randr.rs deleted file mode 100644 index b9258c68..00000000 --- a/src/platform_impl/linux/x11/util/randr.rs +++ /dev/null @@ -1,220 +0,0 @@ -use std::{env, slice, str::FromStr}; - -use super::{ - ffi::{CurrentTime, RRCrtc, RRMode, Success, XRRCrtcInfo, XRRScreenResources}, - *, -}; -use crate::{dpi::validate_scale_factor, platform_impl::platform::x11::VideoMode}; - -/// Represents values of `WINIT_HIDPI_FACTOR`. -pub enum EnvVarDPI { - Randr, - Scale(f64), - NotSet, -} - -pub fn calc_dpi_factor( - (width_px, height_px): (u32, u32), - (width_mm, height_mm): (u64, u64), -) -> f64 { - // See http://xpra.org/trac/ticket/728 for more information. - if width_mm == 0 || height_mm == 0 { - warn!("XRandR reported that the display's 0mm in size, which is certifiably insane"); - return 1.0; - } - - let ppmm = ((width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)).sqrt(); - // Quantize 1/12 step size - let dpi_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0); - assert!(validate_scale_factor(dpi_factor)); - dpi_factor -} - -impl XConnection { - // Retrieve DPI from Xft.dpi property - pub unsafe fn get_xft_dpi(&self) -> Option { - (self.xlib.XrmInitialize)(); - let resource_manager_str = (self.xlib.XResourceManagerString)(self.display); - if resource_manager_str == ptr::null_mut() { - return None; - } - if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() { - let name: &str = "Xft.dpi:\t"; - for pair in res.split("\n") { - if pair.starts_with(&name) { - let res = &pair[name.len()..]; - return f64::from_str(&res).ok(); - } - } - } - None - } - pub unsafe fn get_output_info( - &self, - resources: *mut XRRScreenResources, - crtc: *mut XRRCrtcInfo, - ) -> Option<(String, f64, Vec)> { - let output_info = - (self.xrandr.XRRGetOutputInfo)(self.display, resources, *(*crtc).outputs.offset(0)); - if output_info.is_null() { - // When calling `XRRGetOutputInfo` on a virtual monitor (versus a physical display) - // it's possible for it to return null. - // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816596 - let _ = self.check_errors(); // discard `BadRROutput` error - return None; - } - - let screen = (self.xlib.XDefaultScreen)(self.display); - let bit_depth = (self.xlib.XDefaultDepth)(self.display, screen); - - let output_modes = - slice::from_raw_parts((*output_info).modes, (*output_info).nmode as usize); - let resource_modes = slice::from_raw_parts((*resources).modes, (*resources).nmode as usize); - - let modes = resource_modes - .iter() - // XRROutputInfo contains an array of mode ids that correspond to - // modes in the array in XRRScreenResources - .filter(|x| output_modes.iter().any(|id| x.id == *id)) - .map(|x| { - let refresh_rate = if x.dotClock > 0 && x.hTotal > 0 && x.vTotal > 0 { - x.dotClock as u64 * 1000 / (x.hTotal as u64 * x.vTotal as u64) - } else { - 0 - }; - - VideoMode { - size: (x.width, x.height), - refresh_rate: (refresh_rate as f32 / 1000.0).round() as u16, - bit_depth: bit_depth as u16, - native_mode: x.id, - // This is populated in `MonitorHandle::video_modes` as the - // video mode is returned to the user - monitor: None, - } - }) - .collect(); - - let name_slice = slice::from_raw_parts( - (*output_info).name as *mut u8, - (*output_info).nameLen as usize, - ); - let name = String::from_utf8_lossy(name_slice).into(); - // Override DPI if `WINIT_X11_SCALE_FACTOR` variable is set - let deprecated_dpi_override = env::var("WINIT_HIDPI_FACTOR").ok(); - if deprecated_dpi_override.is_some() { - warn!( - "The WINIT_HIDPI_FACTOR environment variable is deprecated; use WINIT_X11_SCALE_FACTOR" - ) - } - let dpi_env = env::var("WINIT_X11_SCALE_FACTOR").ok().map_or_else( - || EnvVarDPI::NotSet, - |var| { - if var.to_lowercase() == "randr" { - EnvVarDPI::Randr - } else if let Ok(dpi) = f64::from_str(&var) { - EnvVarDPI::Scale(dpi) - } else if var.is_empty() { - EnvVarDPI::NotSet - } else { - panic!( - "`WINIT_X11_SCALE_FACTOR` invalid; DPI factors must be either normal floats greater than 0, or `randr`. Got `{}`", - var - ); - } - }, - ); - - let scale_factor = match dpi_env { - EnvVarDPI::Randr => calc_dpi_factor( - ((*crtc).width as u32, (*crtc).height as u32), - ( - (*output_info).mm_width as u64, - (*output_info).mm_height as u64, - ), - ), - EnvVarDPI::Scale(dpi_override) => { - if !validate_scale_factor(dpi_override) { - panic!( - "`WINIT_X11_SCALE_FACTOR` invalid; DPI factors must be either normal floats greater than 0, or `randr`. Got `{}`", - dpi_override, - ); - } - dpi_override - } - EnvVarDPI::NotSet => { - if let Some(dpi) = self.get_xft_dpi() { - dpi / 96. - } else { - calc_dpi_factor( - ((*crtc).width as u32, (*crtc).height as u32), - ( - (*output_info).mm_width as u64, - (*output_info).mm_height as u64, - ), - ) - } - } - }; - - (self.xrandr.XRRFreeOutputInfo)(output_info); - Some((name, scale_factor, modes)) - } - pub fn set_crtc_config(&self, crtc_id: RRCrtc, mode_id: RRMode) -> Result<(), ()> { - unsafe { - let mut major = 0; - let mut minor = 0; - (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor); - - let root = (self.xlib.XDefaultRootWindow)(self.display); - let resources = if (major == 1 && minor >= 3) || major > 1 { - (self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root) - } else { - (self.xrandr.XRRGetScreenResources)(self.display, root) - }; - - let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id); - let status = (self.xrandr.XRRSetCrtcConfig)( - self.display, - resources, - crtc_id, - CurrentTime, - (*crtc).x, - (*crtc).y, - mode_id, - (*crtc).rotation, - (*crtc).outputs.offset(0), - 1, - ); - - (self.xrandr.XRRFreeCrtcInfo)(crtc); - (self.xrandr.XRRFreeScreenResources)(resources); - - if status == Success as i32 { - Ok(()) - } else { - Err(()) - } - } - } - pub fn get_crtc_mode(&self, crtc_id: RRCrtc) -> RRMode { - unsafe { - let mut major = 0; - let mut minor = 0; - (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor); - - let root = (self.xlib.XDefaultRootWindow)(self.display); - let resources = if (major == 1 && minor >= 3) || major > 1 { - (self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root) - } else { - (self.xrandr.XRRGetScreenResources)(self.display, root) - }; - - let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id); - let mode = (*crtc).mode; - (self.xrandr.XRRFreeCrtcInfo)(crtc); - (self.xrandr.XRRFreeScreenResources)(resources); - mode - } - } -} diff --git a/src/platform_impl/linux/x11/util/window_property.rs b/src/platform_impl/linux/x11/util/window_property.rs deleted file mode 100644 index 34977f36..00000000 --- a/src/platform_impl/linux/x11/util/window_property.rs +++ /dev/null @@ -1,142 +0,0 @@ -use super::*; - -pub type Cardinal = c_long; -pub const CARDINAL_SIZE: usize = mem::size_of::(); - -#[derive(Debug, Clone)] -pub enum GetPropertyError { - XError(XError), - TypeMismatch(ffi::Atom), - FormatMismatch(c_int), - NothingAllocated, -} - -impl GetPropertyError { - pub fn is_actual_property_type(&self, t: ffi::Atom) -> bool { - if let GetPropertyError::TypeMismatch(actual_type) = *self { - actual_type == t - } else { - false - } - } -} - -// Number of 32-bit chunks to retrieve per iteration of get_property's inner loop. -// To test if `get_property` works correctly, set this to 1. -const PROPERTY_BUFFER_SIZE: c_long = 1024; // 4k of RAM ought to be enough for anyone! - -#[derive(Debug)] -#[allow(dead_code)] -pub enum PropMode { - Replace = ffi::PropModeReplace as isize, - Prepend = ffi::PropModePrepend as isize, - Append = ffi::PropModeAppend as isize, -} - -impl XConnection { - pub fn get_property( - &self, - window: c_ulong, - property: ffi::Atom, - property_type: ffi::Atom, - ) -> Result, GetPropertyError> { - let mut data = Vec::new(); - let mut offset = 0; - - let mut done = false; - let mut actual_type = 0; - let mut actual_format = 0; - let mut quantity_returned = 0; - let mut bytes_after = 0; - let mut buf: *mut c_uchar = ptr::null_mut(); - - while !done { - unsafe { - (self.xlib.XGetWindowProperty)( - self.display, - window, - property, - // This offset is in terms of 32-bit chunks. - offset, - // This is the quanity of 32-bit chunks to receive at once. - PROPERTY_BUFFER_SIZE, - ffi::False, - property_type, - &mut actual_type, - &mut actual_format, - // This is the quantity of items we retrieved in our format, NOT of 32-bit chunks! - &mut quantity_returned, - // ...and this is a quantity of bytes. So, this function deals in 3 different units. - &mut bytes_after, - &mut buf, - ); - - if let Err(e) = self.check_errors() { - return Err(GetPropertyError::XError(e)); - } - - if actual_type != property_type { - return Err(GetPropertyError::TypeMismatch(actual_type)); - } - - let format_mismatch = Format::from_format(actual_format as _) != Some(T::FORMAT); - if format_mismatch { - return Err(GetPropertyError::FormatMismatch(actual_format)); - } - - if !buf.is_null() { - offset += PROPERTY_BUFFER_SIZE; - let new_data = - std::slice::from_raw_parts(buf as *mut T, quantity_returned as usize); - /*println!( - "XGetWindowProperty prop:{:?} fmt:{:02} len:{:02} off:{:02} out:{:02}, buf:{:?}", - property, - mem::size_of::() * 8, - data.len(), - offset, - quantity_returned, - new_data, - );*/ - data.extend_from_slice(&new_data); - // Fun fact: XGetWindowProperty allocates one extra byte at the end. - (self.xlib.XFree)(buf as _); // Don't try to access new_data after this. - } else { - return Err(GetPropertyError::NothingAllocated); - } - - done = bytes_after == 0; - } - } - - Ok(data) - } - - pub fn change_property<'a, T: Formattable>( - &'a self, - window: c_ulong, - property: ffi::Atom, - property_type: ffi::Atom, - mode: PropMode, - new_value: &[T], - ) -> Flusher<'a> { - debug_assert_eq!(mem::size_of::(), T::FORMAT.get_actual_size()); - unsafe { - (self.xlib.XChangeProperty)( - self.display, - window, - property, - property_type, - T::FORMAT as c_int, - mode as c_int, - new_value.as_ptr() as *const c_uchar, - new_value.len() as c_int, - ); - } - /*println!( - "XChangeProperty prop:{:?} val:{:?}", - property, - new_value, - );*/ - Flusher::new(self) - } -} diff --git a/src/platform_impl/linux/x11/util/wm.rs b/src/platform_impl/linux/x11/util/wm.rs deleted file mode 100644 index 693bc5c0..00000000 --- a/src/platform_impl/linux/x11/util/wm.rs +++ /dev/null @@ -1,119 +0,0 @@ -use parking_lot::Mutex; - -use super::*; - -// This info is global to the window manager. -lazy_static! { - static ref SUPPORTED_HINTS: Mutex> = Mutex::new(Vec::with_capacity(0)); - static ref WM_NAME: Mutex> = Mutex::new(None); -} - -pub fn hint_is_supported(hint: ffi::Atom) -> bool { - (*SUPPORTED_HINTS.lock()).contains(&hint) -} - -pub fn wm_name_is_one_of(names: &[&str]) -> bool { - if let Some(ref name) = *WM_NAME.lock() { - names.contains(&name.as_str()) - } else { - false - } -} - -impl XConnection { - pub fn update_cached_wm_info(&self, root: ffi::Window) { - *SUPPORTED_HINTS.lock() = self.get_supported_hints(root); - *WM_NAME.lock() = self.get_wm_name(root); - } - - fn get_supported_hints(&self, root: ffi::Window) -> Vec { - let supported_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTED\0") }; - self.get_property(root, supported_atom, ffi::XA_ATOM) - .unwrap_or_else(|_| Vec::with_capacity(0)) - } - - fn get_wm_name(&self, root: ffi::Window) -> Option { - let check_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTING_WM_CHECK\0") }; - let wm_name_atom = unsafe { self.get_atom_unchecked(b"_NET_WM_NAME\0") }; - - // Mutter/Muffin/Budgie doesn't have _NET_SUPPORTING_WM_CHECK in its _NET_SUPPORTED, despite - // it working and being supported. This has been reported upstream, but due to the - // inavailability of time machines, we'll just try to get _NET_SUPPORTING_WM_CHECK - // regardless of whether or not the WM claims to support it. - // - // Blackbox 0.70 also incorrectly reports not supporting this, though that appears to be fixed - // in 0.72. - /*if !supported_hints.contains(&check_atom) { - return None; - }*/ - - // IceWM (1.3.x and earlier) doesn't report supporting _NET_WM_NAME, but will nonetheless - // provide us with a value for it. Note that the unofficial 1.4 fork of IceWM works fine. - /*if !supported_hints.contains(&wm_name_atom) { - return None; - }*/ - - // Of the WMs tested, only xmonad and dwm fail to provide a WM name. - - // Querying this property on the root window will give us the ID of a child window created by - // the WM. - let root_window_wm_check = { - let result = self.get_property(root, check_atom, ffi::XA_WINDOW); - - let wm_check = result.ok().and_then(|wm_check| wm_check.get(0).cloned()); - - if let Some(wm_check) = wm_check { - wm_check - } else { - return None; - } - }; - - // Querying the same property on the child window we were given, we should get this child - // window's ID again. - let child_window_wm_check = { - let result = self.get_property(root_window_wm_check, check_atom, ffi::XA_WINDOW); - - let wm_check = result.ok().and_then(|wm_check| wm_check.get(0).cloned()); - - if let Some(wm_check) = wm_check { - wm_check - } else { - return None; - } - }; - - // These values should be the same. - if root_window_wm_check != child_window_wm_check { - return None; - } - - // All of that work gives us a window ID that we can get the WM name from. - let wm_name = { - let utf8_string_atom = unsafe { self.get_atom_unchecked(b"UTF8_STRING\0") }; - - let result = self.get_property(root_window_wm_check, wm_name_atom, utf8_string_atom); - - // IceWM requires this. IceWM was also the only WM tested that returns a null-terminated - // string. For more fun trivia, IceWM is also unique in including version and uname - // information in this string (this means you'll have to be careful if you want to match - // against it, though). - // The unofficial 1.4 fork of IceWM still includes the extra details, but properly - // returns a UTF8 string that isn't null-terminated. - let no_utf8 = if let Err(ref err) = result { - err.is_actual_property_type(ffi::XA_STRING) - } else { - false - }; - - if no_utf8 { - self.get_property(root_window_wm_check, wm_name_atom, ffi::XA_STRING) - } else { - result - } - } - .ok(); - - wm_name.and_then(|wm_name| String::from_utf8(wm_name).ok()) - } -} diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs deleted file mode 100644 index 26d3b5dd..00000000 --- a/src/platform_impl/linux/x11/window.rs +++ /dev/null @@ -1,1378 +0,0 @@ -use raw_window_handle::unix::XlibHandle; -use std::{ - cmp, env, - ffi::CString, - mem::{self, replace, MaybeUninit}, - os::raw::*, - path::Path, - ptr, slice, - sync::Arc, -}; - -use libc; -use mio_misc::channel::Sender; -use parking_lot::Mutex; - -use crate::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - error::{ExternalError, NotSupportedError, OsError as RootOsError}, - monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, - platform_impl::{ - x11::{ime::ImeContextCreationError, MonitorHandle as X11MonitorHandle}, - MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes, - VideoMode as PlatformVideoMode, - }, - window::{CursorIcon, Fullscreen, Icon, UserAttentionType, WindowAttributes}, -}; - -use super::{ffi, util, EventLoopWindowTarget, ImeSender, WindowId, XConnection, XError}; - -#[derive(Debug)] -pub struct SharedState { - pub cursor_pos: Option<(f64, f64)>, - pub size: Option<(u32, u32)>, - pub position: Option<(i32, i32)>, - pub inner_position: Option<(i32, i32)>, - pub inner_position_rel_parent: Option<(i32, i32)>, - pub last_monitor: X11MonitorHandle, - pub dpi_adjusted: Option<(u32, u32)>, - pub fullscreen: Option, - // Set when application calls `set_fullscreen` when window is not visible - pub desired_fullscreen: Option>, - // Used to restore position after exiting fullscreen - pub restore_position: Option<(i32, i32)>, - // Used to restore video mode after exiting fullscreen - pub desktop_video_mode: Option<(ffi::RRCrtc, ffi::RRMode)>, - pub frame_extents: Option, - pub min_inner_size: Option, - pub max_inner_size: Option, - pub resize_increments: Option, - pub base_size: Option, - pub visibility: Visibility, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum Visibility { - No, - Yes, - // Waiting for VisibilityNotify - YesWait, -} - -impl SharedState { - fn new(last_monitor: X11MonitorHandle, is_visible: bool) -> Mutex { - let visibility = if is_visible { - Visibility::YesWait - } else { - Visibility::No - }; - - Mutex::new(SharedState { - last_monitor, - visibility, - - cursor_pos: None, - size: None, - position: None, - inner_position: None, - inner_position_rel_parent: None, - dpi_adjusted: None, - fullscreen: None, - desired_fullscreen: None, - restore_position: None, - desktop_video_mode: None, - frame_extents: None, - min_inner_size: None, - max_inner_size: None, - resize_increments: None, - base_size: None, - }) - } -} - -unsafe impl Send for UnownedWindow {} -unsafe impl Sync for UnownedWindow {} - -pub struct UnownedWindow { - pub xconn: Arc, // never changes - xwindow: ffi::Window, // never changes - root: ffi::Window, // never changes - screen_id: i32, // never changes - cursor: Mutex, - cursor_grabbed: Mutex, - cursor_visible: Mutex, - ime_sender: Mutex, - pub shared_state: Mutex, - redraw_sender: Sender, -} - -impl UnownedWindow { - pub fn new( - event_loop: &EventLoopWindowTarget, - window_attrs: WindowAttributes, - pl_attribs: PlatformSpecificWindowBuilderAttributes, - ) -> Result { - let xconn = &event_loop.xconn; - let root = event_loop.root; - - let mut monitors = xconn.available_monitors(); - let guessed_monitor = if monitors.is_empty() { - X11MonitorHandle::dummy() - } else { - xconn - .query_pointer(root, util::VIRTUAL_CORE_POINTER) - .ok() - .and_then(|pointer_state| { - let (x, y) = (pointer_state.root_x as i64, pointer_state.root_y as i64); - - for i in 0..monitors.len() { - if monitors[i].rect.contains_point(x, y) { - return Some(monitors.swap_remove(i)); - } - } - - None - }) - .unwrap_or_else(|| monitors.swap_remove(0)) - }; - let scale_factor = guessed_monitor.scale_factor(); - - info!("Guessed window scale factor: {}", scale_factor); - - let max_inner_size: Option<(u32, u32)> = window_attrs - .max_inner_size - .map(|size| size.to_physical::(scale_factor).into()); - let min_inner_size: Option<(u32, u32)> = window_attrs - .min_inner_size - .map(|size| size.to_physical::(scale_factor).into()); - - let position = window_attrs - .position - .map(|position| position.to_physical::(scale_factor).into()); - - let dimensions = { - // x11 only applies constraints when the window is actively resized - // by the user, so we have to manually apply the initial constraints - let mut dimensions: (u32, u32) = window_attrs - .inner_size - .map(|size| size.to_physical::(scale_factor)) - .or_else(|| Some((800, 600).into())) - .map(Into::into) - .unwrap(); - if let Some(max) = max_inner_size { - dimensions.0 = cmp::min(dimensions.0, max.0); - dimensions.1 = cmp::min(dimensions.1, max.1); - } - if let Some(min) = min_inner_size { - dimensions.0 = cmp::max(dimensions.0, min.0); - dimensions.1 = cmp::max(dimensions.1, min.1); - } - debug!( - "Calculated physical dimensions: {}x{}", - dimensions.0, dimensions.1 - ); - dimensions - }; - - let screen_id = match pl_attribs.screen_id { - Some(id) => id, - None => unsafe { (xconn.xlib.XDefaultScreen)(xconn.display) }, - }; - - // creating - let mut set_win_attr = { - let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() }; - swa.colormap = if let Some(vi) = pl_attribs.visual_infos { - unsafe { - let visual = vi.visual; - (xconn.xlib.XCreateColormap)(xconn.display, root, visual, ffi::AllocNone) - } - } else { - 0 - }; - swa.event_mask = ffi::ExposureMask - | ffi::StructureNotifyMask - | ffi::VisibilityChangeMask - | ffi::KeyPressMask - | ffi::KeyReleaseMask - | ffi::KeymapStateMask - | ffi::ButtonPressMask - | ffi::ButtonReleaseMask - | ffi::PointerMotionMask; - swa.border_pixel = 0; - swa.override_redirect = pl_attribs.override_redirect as c_int; - swa - }; - - let mut window_attributes = ffi::CWBorderPixel | ffi::CWColormap | ffi::CWEventMask; - - if pl_attribs.override_redirect { - window_attributes |= ffi::CWOverrideRedirect; - } - - // finally creating the window - let xwindow = unsafe { - (xconn.xlib.XCreateWindow)( - xconn.display, - root, - position.map_or(0, |p: PhysicalPosition| p.x as c_int), - position.map_or(0, |p: PhysicalPosition| p.y as c_int), - dimensions.0 as c_uint, - dimensions.1 as c_uint, - 0, - match pl_attribs.visual_infos { - Some(vi) => vi.depth, - None => ffi::CopyFromParent, - }, - ffi::InputOutput as c_uint, - // TODO: If window wants transparency and `visual_infos` is None, - // we need to find our own visual which has an `alphaMask` which - // is > 0, like we do in glutin. - // - // It is non obvious which masks, if any, we should pass to - // `XGetVisualInfo`. winit doesn't receive any info about what - // properties the user wants. Users should consider choosing the - // visual themselves as glutin does. - match pl_attribs.visual_infos { - Some(vi) => vi.visual, - None => ffi::CopyFromParent as *mut ffi::Visual, - }, - window_attributes, - &mut set_win_attr, - ) - }; - - let mut window = UnownedWindow { - xconn: Arc::clone(xconn), - xwindow, - root, - screen_id, - cursor: Default::default(), - cursor_grabbed: Mutex::new(false), - cursor_visible: Mutex::new(true), - ime_sender: Mutex::new(event_loop.ime_sender.clone()), - shared_state: SharedState::new(guessed_monitor, window_attrs.visible), - redraw_sender: event_loop.redraw_sender.clone(), - }; - - // Title must be set before mapping. Some tiling window managers (i.e. i3) use the window - // title to determine placement/etc., so doing this after mapping would cause the WM to - // act on the wrong title state. - window.set_title_inner(&window_attrs.title).queue(); - window - .set_decorations_inner(window_attrs.decorations) - .queue(); - - { - // Enable drag and drop (TODO: extend API to make this toggleable) - unsafe { - let dnd_aware_atom = xconn.get_atom_unchecked(b"XdndAware\0"); - let version = &[5 as c_ulong]; // Latest version; hasn't changed since 2002 - xconn.change_property( - window.xwindow, - dnd_aware_atom, - ffi::XA_ATOM, - util::PropMode::Replace, - version, - ) - } - .queue(); - - // WM_CLASS must be set *before* mapping the window, as per ICCCM! - { - let (class, instance) = if let Some((instance, class)) = pl_attribs.class { - let instance = CString::new(instance.as_str()) - .expect("`WM_CLASS` instance contained null byte"); - let class = - CString::new(class.as_str()).expect("`WM_CLASS` class contained null byte"); - (instance, class) - } else { - let class = env::args() - .next() - .as_ref() - // Default to the name of the binary (via argv[0]) - .and_then(|path| Path::new(path).file_name()) - .and_then(|bin_name| bin_name.to_str()) - .map(|bin_name| bin_name.to_owned()) - .or_else(|| Some(window_attrs.title.clone())) - .and_then(|string| CString::new(string.as_str()).ok()) - .expect("Default `WM_CLASS` class contained null byte"); - // This environment variable is extraordinarily unlikely to actually be used... - let instance = env::var("RESOURCE_NAME") - .ok() - .and_then(|instance| CString::new(instance.as_str()).ok()) - .or_else(|| Some(class.clone())) - .expect("Default `WM_CLASS` instance contained null byte"); - (instance, class) - }; - - let mut class_hint = xconn.alloc_class_hint(); - (*class_hint).res_name = class.as_ptr() as *mut c_char; - (*class_hint).res_class = instance.as_ptr() as *mut c_char; - - unsafe { - (xconn.xlib.XSetClassHint)(xconn.display, window.xwindow, class_hint.ptr); - } //.queue(); - } - - window.set_pid().map(|flusher| flusher.queue()); - - window.set_window_types(pl_attribs.x11_window_types).queue(); - - if let Some(variant) = pl_attribs.gtk_theme_variant { - window.set_gtk_theme_variant(variant).queue(); - } - - // set size hints - { - let mut min_inner_size = window_attrs - .min_inner_size - .map(|size| size.to_physical::(scale_factor)); - let mut max_inner_size = window_attrs - .max_inner_size - .map(|size| size.to_physical::(scale_factor)); - - if !window_attrs.resizable { - if util::wm_name_is_one_of(&["Xfwm4"]) { - warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4"); - } else { - max_inner_size = Some(dimensions.into()); - min_inner_size = Some(dimensions.into()); - - let mut shared_state = window.shared_state.get_mut(); - shared_state.min_inner_size = window_attrs.min_inner_size; - shared_state.max_inner_size = window_attrs.max_inner_size; - shared_state.resize_increments = pl_attribs.resize_increments; - shared_state.base_size = pl_attribs.base_size; - } - } - - let mut normal_hints = util::NormalHints::new(xconn); - normal_hints.set_position(position.map(|PhysicalPosition { x, y }| (x, y))); - normal_hints.set_size(Some(dimensions)); - normal_hints.set_min_size(min_inner_size.map(Into::into)); - normal_hints.set_max_size(max_inner_size.map(Into::into)); - normal_hints.set_resize_increments( - pl_attribs - .resize_increments - .map(|size| size.to_physical::(scale_factor).into()), - ); - normal_hints.set_base_size( - pl_attribs - .base_size - .map(|size| size.to_physical::(scale_factor).into()), - ); - xconn.set_normal_hints(window.xwindow, normal_hints).queue(); - } - - // Set window icons - if let Some(icon) = window_attrs.window_icon { - window.set_icon_inner(icon).queue(); - } - - // Opt into handling window close - unsafe { - (xconn.xlib.XSetWMProtocols)( - xconn.display, - window.xwindow, - &[event_loop.wm_delete_window, event_loop.net_wm_ping] as *const ffi::Atom - as *mut ffi::Atom, - 2, - ); - } //.queue(); - - // Set visibility (map window) - if window_attrs.visible { - unsafe { - (xconn.xlib.XMapRaised)(xconn.display, window.xwindow); - } //.queue(); - } - - // Attempt to make keyboard input repeat detectable - unsafe { - let mut supported_ptr = ffi::False; - (xconn.xlib.XkbSetDetectableAutoRepeat)( - xconn.display, - ffi::True, - &mut supported_ptr, - ); - if supported_ptr == ffi::False { - return Err(os_error!(OsError::XMisc( - "`XkbSetDetectableAutoRepeat` failed" - ))); - } - } - - // Select XInput2 events - let mask = { - let mask = ffi::XI_MotionMask - | ffi::XI_ButtonPressMask - | ffi::XI_ButtonReleaseMask - //| ffi::XI_KeyPressMask - //| ffi::XI_KeyReleaseMask - | ffi::XI_EnterMask - | ffi::XI_LeaveMask - | ffi::XI_FocusInMask - | ffi::XI_FocusOutMask - | ffi::XI_TouchBeginMask - | ffi::XI_TouchUpdateMask - | ffi::XI_TouchEndMask; - mask - }; - xconn - .select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask) - .queue(); - - { - let result = event_loop.ime.borrow_mut().create_context(window.xwindow); - if let Err(err) = result { - let e = match err { - ImeContextCreationError::XError(err) => OsError::XError(err), - ImeContextCreationError::Null => { - OsError::XMisc("IME Context creation failed") - } - }; - return Err(os_error!(e)); - } - } - - // These properties must be set after mapping - if window_attrs.maximized { - window.set_maximized_inner(window_attrs.maximized).queue(); - } - if window_attrs.fullscreen.is_some() { - window - .set_fullscreen_inner(window_attrs.fullscreen.clone()) - .map(|flusher| flusher.queue()); - - if let Some(PhysicalPosition { x, y }) = position { - let shared_state = window.shared_state.get_mut(); - - shared_state.restore_position = Some((x, y)); - } - } - if window_attrs.always_on_top { - window - .set_always_on_top_inner(window_attrs.always_on_top) - .queue(); - } - } - - // We never want to give the user a broken window, since by then, it's too late to handle. - xconn - .sync_with_server() - .map(|_| window) - .map_err(|x_err| os_error!(OsError::XError(x_err))) - } - - fn set_pid(&self) -> Option> { - let pid_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_PID\0") }; - let client_machine_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") }; - unsafe { - // 64 would suffice for Linux, but 256 will be enough everywhere (as per SUSv2). For instance, this is - // the limit defined by OpenBSD. - const MAXHOSTNAMELEN: usize = 256; - // `assume_init` is safe here because the array consists of `MaybeUninit` values, - // which do not require initialization. - let mut buffer: [MaybeUninit; MAXHOSTNAMELEN] = - MaybeUninit::uninit().assume_init(); - let status = libc::gethostname(buffer.as_mut_ptr() as *mut c_char, buffer.len()); - if status != 0 { - return None; - } - ptr::write(buffer[MAXHOSTNAMELEN - 1].as_mut_ptr() as *mut u8, b'\0'); // a little extra safety - let hostname_length = libc::strlen(buffer.as_ptr() as *const c_char); - - let hostname = slice::from_raw_parts(buffer.as_ptr() as *const c_char, hostname_length); - - self.xconn - .change_property( - self.xwindow, - pid_atom, - ffi::XA_CARDINAL, - util::PropMode::Replace, - &[libc::getpid() as util::Cardinal], - ) - .queue(); - let flusher = self.xconn.change_property( - self.xwindow, - client_machine_atom, - ffi::XA_STRING, - util::PropMode::Replace, - &hostname[0..hostname_length], - ); - Some(flusher) - } - } - - fn set_window_types(&self, window_types: Vec) -> util::Flusher<'_> { - let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_WINDOW_TYPE\0") }; - let atoms: Vec<_> = window_types - .iter() - .map(|t| t.as_atom(&self.xconn)) - .collect(); - - self.xconn.change_property( - self.xwindow, - hint_atom, - ffi::XA_ATOM, - util::PropMode::Replace, - &atoms, - ) - } - - fn set_gtk_theme_variant(&self, variant: String) -> util::Flusher<'_> { - let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_GTK_THEME_VARIANT\0") }; - let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") }; - let variant = CString::new(variant).expect("`_GTK_THEME_VARIANT` contained null byte"); - self.xconn.change_property( - self.xwindow, - hint_atom, - utf8_atom, - util::PropMode::Replace, - variant.as_bytes(), - ) - } - - fn set_netwm( - &self, - operation: util::StateOperation, - properties: (c_long, c_long, c_long, c_long), - ) -> util::Flusher<'_> { - let state_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE\0") }; - self.xconn.send_client_msg( - self.xwindow, - self.root, - state_atom, - Some(ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask), - [ - operation as c_long, - properties.0, - properties.1, - properties.2, - properties.3, - ], - ) - } - - fn set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher<'_> { - let fullscreen_atom = - unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_FULLSCREEN\0") }; - let flusher = self.set_netwm(fullscreen.into(), (fullscreen_atom as c_long, 0, 0, 0)); - - if fullscreen { - // Ensure that the fullscreen window receives input focus to prevent - // locking up the user's display. - unsafe { - (self.xconn.xlib.XSetInputFocus)( - self.xconn.display, - self.xwindow, - ffi::RevertToParent, - ffi::CurrentTime, - ); - } - } - - flusher - } - - fn set_fullscreen_inner(&self, fullscreen: Option) -> Option> { - let mut shared_state_lock = self.shared_state.lock(); - - match shared_state_lock.visibility { - // Setting fullscreen on a window that is not visible will generate an error. - Visibility::No | Visibility::YesWait => { - shared_state_lock.desired_fullscreen = Some(fullscreen); - return None; - } - Visibility::Yes => (), - } - - let old_fullscreen = shared_state_lock.fullscreen.clone(); - if old_fullscreen == fullscreen { - return None; - } - shared_state_lock.fullscreen = fullscreen.clone(); - - match (&old_fullscreen, &fullscreen) { - // Store the desktop video mode before entering exclusive - // fullscreen, so we can restore it upon exit, as XRandR does not - // provide a mechanism to set this per app-session or restore this - // to the desktop video mode as macOS and Windows do - ( - &None, - &Some(Fullscreen::Exclusive(RootVideoMode { - video_mode: PlatformVideoMode::X(ref video_mode), - })), - ) - | ( - &Some(Fullscreen::Borderless(_)), - &Some(Fullscreen::Exclusive(RootVideoMode { - video_mode: PlatformVideoMode::X(ref video_mode), - })), - ) => { - let monitor = video_mode.monitor.as_ref().unwrap(); - shared_state_lock.desktop_video_mode = - Some((monitor.id, self.xconn.get_crtc_mode(monitor.id))); - } - // Restore desktop video mode upon exiting exclusive fullscreen - (&Some(Fullscreen::Exclusive(_)), &None) - | (&Some(Fullscreen::Exclusive(_)), &Some(Fullscreen::Borderless(_))) => { - let (monitor_id, mode_id) = shared_state_lock.desktop_video_mode.take().unwrap(); - self.xconn - .set_crtc_config(monitor_id, mode_id) - .expect("failed to restore desktop video mode"); - } - _ => (), - } - - drop(shared_state_lock); - - match fullscreen { - None => { - let flusher = self.set_fullscreen_hint(false); - let mut shared_state_lock = self.shared_state.lock(); - if let Some(position) = shared_state_lock.restore_position.take() { - drop(shared_state_lock); - self.set_position_inner(position.0, position.1).queue(); - } - Some(flusher) - } - Some(fullscreen) => { - let (video_mode, monitor) = match fullscreen { - Fullscreen::Exclusive(RootVideoMode { - video_mode: PlatformVideoMode::X(ref video_mode), - }) => (Some(video_mode), video_mode.monitor.clone().unwrap()), - Fullscreen::Borderless(Some(RootMonitorHandle { - inner: PlatformMonitorHandle::X(monitor), - })) => (None, monitor), - Fullscreen::Borderless(None) => (None, self.current_monitor()), - #[cfg(feature = "wayland")] - _ => unreachable!(), - }; - - // Don't set fullscreen on an invalid dummy monitor handle - if monitor.is_dummy() { - return None; - } - - if let Some(video_mode) = video_mode { - // FIXME: this is actually not correct if we're setting the - // video mode to a resolution higher than the current - // desktop resolution, because XRandR does not automatically - // reposition the monitors to the right and below this - // monitor. - // - // What ends up happening is we will get the fullscreen - // window showing up on those monitors as well, because - // their virtual position now overlaps with the monitor that - // we just made larger.. - // - // It'd be quite a bit of work to handle this correctly (and - // nobody else seems to bother doing this correctly either), - // so we're just leaving this broken. Fixing this would - // involve storing all CRTCs upon entering fullscreen, - // restoring them upon exit, and after entering fullscreen, - // repositioning displays to the right and below this - // display. I think there would still be edge cases that are - // difficult or impossible to handle correctly, e.g. what if - // a new monitor was plugged in while in fullscreen? - // - // I think we might just want to disallow setting the video - // mode higher than the current desktop video mode (I'm sure - // this will make someone unhappy, but it's very unusual for - // games to want to do this anyway). - self.xconn - .set_crtc_config(monitor.id, video_mode.native_mode) - .expect("failed to set video mode"); - } - - let window_position = self.outer_position_physical(); - self.shared_state.lock().restore_position = Some(window_position); - let monitor_origin: (i32, i32) = monitor.position().into(); - self.set_position_inner(monitor_origin.0, monitor_origin.1) - .queue(); - Some(self.set_fullscreen_hint(true)) - } - } - } - - #[inline] - pub fn fullscreen(&self) -> Option { - let shared_state = self.shared_state.lock(); - - shared_state - .desired_fullscreen - .clone() - .unwrap_or_else(|| shared_state.fullscreen.clone()) - } - - #[inline] - pub fn set_fullscreen(&self, fullscreen: Option) { - if let Some(flusher) = self.set_fullscreen_inner(fullscreen) { - flusher - .sync() - .expect("Failed to change window fullscreen state"); - self.invalidate_cached_frame_extents(); - } - } - - // Called by EventProcessor when a VisibilityNotify event is received - pub(crate) fn visibility_notify(&self) { - let mut shared_state = self.shared_state.lock(); - - match shared_state.visibility { - Visibility::No => unsafe { - (self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow); - }, - Visibility::Yes => (), - Visibility::YesWait => { - shared_state.visibility = Visibility::Yes; - - if let Some(fullscreen) = shared_state.desired_fullscreen.take() { - drop(shared_state); - self.set_fullscreen(fullscreen); - } - } - } - } - - #[inline] - pub fn current_monitor(&self) -> X11MonitorHandle { - self.shared_state.lock().last_monitor.clone() - } - - pub fn available_monitors(&self) -> Vec { - self.xconn.available_monitors() - } - - pub fn primary_monitor(&self) -> X11MonitorHandle { - self.xconn.primary_monitor() - } - - fn set_minimized_inner(&self, minimized: bool) -> util::Flusher<'_> { - unsafe { - if minimized { - let screen = (self.xconn.xlib.XDefaultScreen)(self.xconn.display); - - (self.xconn.xlib.XIconifyWindow)(self.xconn.display, self.xwindow, screen); - - util::Flusher::new(&self.xconn) - } else { - let atom = self.xconn.get_atom_unchecked(b"_NET_ACTIVE_WINDOW\0"); - - self.xconn.send_client_msg( - self.xwindow, - self.root, - atom, - Some(ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask), - [1, ffi::CurrentTime as c_long, 0, 0, 0], - ) - } - } - } - - #[inline] - pub fn set_minimized(&self, minimized: bool) { - self.set_minimized_inner(minimized) - .flush() - .expect("Failed to change window minimization"); - } - - fn set_maximized_inner(&self, maximized: bool) -> util::Flusher<'_> { - let horz_atom = unsafe { - self.xconn - .get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_HORZ\0") - }; - let vert_atom = unsafe { - self.xconn - .get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_VERT\0") - }; - self.set_netwm( - maximized.into(), - (horz_atom as c_long, vert_atom as c_long, 0, 0), - ) - } - - #[inline] - pub fn set_maximized(&self, maximized: bool) { - self.set_maximized_inner(maximized) - .flush() - .expect("Failed to change window maximization"); - self.invalidate_cached_frame_extents(); - } - - fn set_title_inner(&self, title: &str) -> util::Flusher<'_> { - let wm_name_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_NAME\0") }; - let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") }; - let title = CString::new(title).expect("Window title contained null byte"); - unsafe { - (self.xconn.xlib.XStoreName)( - self.xconn.display, - self.xwindow, - title.as_ptr() as *const c_char, - ); - self.xconn.change_property( - self.xwindow, - wm_name_atom, - utf8_atom, - util::PropMode::Replace, - title.as_bytes(), - ) - } - } - - #[inline] - pub fn set_title(&self, title: &str) { - self.set_title_inner(title) - .flush() - .expect("Failed to set window title"); - } - - fn set_decorations_inner(&self, decorations: bool) -> util::Flusher<'_> { - let mut hints = self.xconn.get_motif_hints(self.xwindow); - - hints.set_decorations(decorations); - - self.xconn.set_motif_hints(self.xwindow, &hints) - } - - #[inline] - pub fn set_decorations(&self, decorations: bool) { - self.set_decorations_inner(decorations) - .flush() - .expect("Failed to set decoration state"); - self.invalidate_cached_frame_extents(); - } - - fn set_maximizable_inner(&self, maximizable: bool) -> util::Flusher<'_> { - let mut hints = self.xconn.get_motif_hints(self.xwindow); - - hints.set_maximizable(maximizable); - - self.xconn.set_motif_hints(self.xwindow, &hints) - } - - fn set_always_on_top_inner(&self, always_on_top: bool) -> util::Flusher<'_> { - let above_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_ABOVE\0") }; - self.set_netwm(always_on_top.into(), (above_atom as c_long, 0, 0, 0)) - } - - #[inline] - pub fn set_always_on_top(&self, always_on_top: bool) { - self.set_always_on_top_inner(always_on_top) - .flush() - .expect("Failed to set always-on-top state"); - } - - fn set_icon_inner(&self, icon: Icon) -> util::Flusher<'_> { - let icon_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_ICON\0") }; - let data = icon.to_cardinals(); - self.xconn.change_property( - self.xwindow, - icon_atom, - ffi::XA_CARDINAL, - util::PropMode::Replace, - data.as_slice(), - ) - } - - fn unset_icon_inner(&self) -> util::Flusher<'_> { - let icon_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_ICON\0") }; - let empty_data: [util::Cardinal; 0] = []; - self.xconn.change_property( - self.xwindow, - icon_atom, - ffi::XA_CARDINAL, - util::PropMode::Replace, - &empty_data, - ) - } - - #[inline] - pub fn set_window_icon(&self, icon: Option) { - match icon { - Some(icon) => self.set_icon_inner(icon), - None => self.unset_icon_inner(), - } - .flush() - .expect("Failed to set icons"); - } - - #[inline] - pub fn set_visible(&self, visible: bool) { - let mut shared_state = self.shared_state.lock(); - - match (visible, shared_state.visibility) { - (true, Visibility::Yes) | (true, Visibility::YesWait) | (false, Visibility::No) => { - return - } - _ => (), - } - - if visible { - unsafe { - (self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow); - } - self.xconn - .flush_requests() - .expect("Failed to call XMapRaised"); - shared_state.visibility = Visibility::YesWait; - } else { - unsafe { - (self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow); - } - self.xconn - .flush_requests() - .expect("Failed to call XUnmapWindow"); - shared_state.visibility = Visibility::No; - } - } - - fn update_cached_frame_extents(&self) { - let extents = self - .xconn - .get_frame_extents_heuristic(self.xwindow, self.root); - (*self.shared_state.lock()).frame_extents = Some(extents); - } - - pub(crate) fn invalidate_cached_frame_extents(&self) { - (*self.shared_state.lock()).frame_extents.take(); - } - - pub(crate) fn outer_position_physical(&self) -> (i32, i32) { - let extents = (*self.shared_state.lock()).frame_extents.clone(); - if let Some(extents) = extents { - let (x, y) = self.inner_position_physical(); - extents.inner_pos_to_outer(x, y) - } else { - self.update_cached_frame_extents(); - self.outer_position_physical() - } - } - - #[inline] - pub fn outer_position(&self) -> Result, NotSupportedError> { - let extents = (*self.shared_state.lock()).frame_extents.clone(); - if let Some(extents) = extents { - let (x, y) = self.inner_position_physical(); - Ok(extents.inner_pos_to_outer(x, y).into()) - } else { - self.update_cached_frame_extents(); - self.outer_position() - } - } - - pub(crate) fn inner_position_physical(&self) -> (i32, i32) { - // This should be okay to unwrap since the only error XTranslateCoordinates can return - // is BadWindow, and if the window handle is bad we have bigger problems. - self.xconn - .translate_coords(self.xwindow, self.root) - .map(|coords| (coords.x_rel_root, coords.y_rel_root)) - .unwrap() - } - - #[inline] - pub fn inner_position(&self) -> Result, NotSupportedError> { - Ok(self.inner_position_physical().into()) - } - - pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher<'_> { - // There are a few WMs that set client area position rather than window position, so - // we'll translate for consistency. - if util::wm_name_is_one_of(&["Enlightenment", "FVWM"]) { - let extents = (*self.shared_state.lock()).frame_extents.clone(); - if let Some(extents) = extents { - x += extents.frame_extents.left as i32; - y += extents.frame_extents.top as i32; - } else { - self.update_cached_frame_extents(); - return self.set_position_inner(x, y); - } - } - unsafe { - (self.xconn.xlib.XMoveWindow)(self.xconn.display, self.xwindow, x as c_int, y as c_int); - } - util::Flusher::new(&self.xconn) - } - - pub(crate) fn set_position_physical(&self, x: i32, y: i32) { - self.set_position_inner(x, y) - .flush() - .expect("Failed to call `XMoveWindow`"); - } - - #[inline] - pub fn set_outer_position(&self, position: Position) { - let (x, y) = position.to_physical::(self.scale_factor()).into(); - self.set_position_physical(x, y); - } - - pub(crate) fn inner_size_physical(&self) -> (u32, u32) { - // This should be okay to unwrap since the only error XGetGeometry can return - // is BadWindow, and if the window handle is bad we have bigger problems. - self.xconn - .get_geometry(self.xwindow) - .map(|geo| (geo.width, geo.height)) - .unwrap() - } - - #[inline] - pub fn inner_size(&self) -> PhysicalSize { - self.inner_size_physical().into() - } - - #[inline] - pub fn outer_size(&self) -> PhysicalSize { - let extents = self.shared_state.lock().frame_extents.clone(); - if let Some(extents) = extents { - let (width, height) = self.inner_size_physical(); - extents.inner_size_to_outer(width, height).into() - } else { - self.update_cached_frame_extents(); - self.outer_size() - } - } - - pub(crate) fn set_inner_size_physical(&self, width: u32, height: u32) { - unsafe { - (self.xconn.xlib.XResizeWindow)( - self.xconn.display, - self.xwindow, - width as c_uint, - height as c_uint, - ); - self.xconn.flush_requests() - } - .expect("Failed to call `XResizeWindow`"); - } - - #[inline] - pub fn set_inner_size(&self, size: Size) { - let scale_factor = self.scale_factor(); - let (width, height) = size.to_physical::(scale_factor).into(); - self.set_inner_size_physical(width, height); - } - - fn update_normal_hints(&self, callback: F) -> Result<(), XError> - where - F: FnOnce(&mut util::NormalHints<'_>) -> (), - { - let mut normal_hints = self.xconn.get_normal_hints(self.xwindow)?; - callback(&mut normal_hints); - self.xconn - .set_normal_hints(self.xwindow, normal_hints) - .flush() - } - - pub(crate) fn set_min_inner_size_physical(&self, dimensions: Option<(u32, u32)>) { - self.update_normal_hints(|normal_hints| normal_hints.set_min_size(dimensions)) - .expect("Failed to call `XSetWMNormalHints`"); - } - - #[inline] - pub fn set_min_inner_size(&self, dimensions: Option) { - self.shared_state.lock().min_inner_size = dimensions; - let physical_dimensions = - dimensions.map(|dimensions| dimensions.to_physical::(self.scale_factor()).into()); - self.set_min_inner_size_physical(physical_dimensions); - } - - pub(crate) fn set_max_inner_size_physical(&self, dimensions: Option<(u32, u32)>) { - self.update_normal_hints(|normal_hints| normal_hints.set_max_size(dimensions)) - .expect("Failed to call `XSetWMNormalHints`"); - } - - #[inline] - pub fn set_max_inner_size(&self, dimensions: Option) { - self.shared_state.lock().max_inner_size = dimensions; - let physical_dimensions = - dimensions.map(|dimensions| dimensions.to_physical::(self.scale_factor()).into()); - self.set_max_inner_size_physical(physical_dimensions); - } - - pub(crate) fn adjust_for_dpi( - &self, - old_scale_factor: f64, - new_scale_factor: f64, - width: u32, - height: u32, - shared_state: &SharedState, - ) -> (u32, u32) { - let scale_factor = new_scale_factor / old_scale_factor; - self.update_normal_hints(|normal_hints| { - let dpi_adjuster = - |size: Size| -> (u32, u32) { size.to_physical::(new_scale_factor).into() }; - let max_size = shared_state.max_inner_size.map(&dpi_adjuster); - let min_size = shared_state.min_inner_size.map(&dpi_adjuster); - let resize_increments = shared_state.resize_increments.map(&dpi_adjuster); - let base_size = shared_state.base_size.map(&dpi_adjuster); - normal_hints.set_max_size(max_size); - normal_hints.set_min_size(min_size); - normal_hints.set_resize_increments(resize_increments); - normal_hints.set_base_size(base_size); - }) - .expect("Failed to update normal hints"); - - let new_width = (width as f64 * scale_factor).round() as u32; - let new_height = (height as f64 * scale_factor).round() as u32; - - (new_width, new_height) - } - - pub fn set_resizable(&self, resizable: bool) { - if util::wm_name_is_one_of(&["Xfwm4"]) { - // Making the window unresizable on Xfwm prevents further changes to `WM_NORMAL_HINTS` from being detected. - // This makes it impossible for resizing to be re-enabled, and also breaks DPI scaling. As such, we choose - // the lesser of two evils and do nothing. - warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4"); - return; - } - - let (min_size, max_size) = if resizable { - let shared_state_lock = self.shared_state.lock(); - ( - shared_state_lock.min_inner_size, - shared_state_lock.max_inner_size, - ) - } else { - let window_size = Some(Size::from(self.inner_size())); - (window_size.clone(), window_size) - }; - - self.set_maximizable_inner(resizable).queue(); - - let scale_factor = self.scale_factor(); - let min_inner_size = min_size - .map(|size| size.to_physical::(scale_factor)) - .map(Into::into); - let max_inner_size = max_size - .map(|size| size.to_physical::(scale_factor)) - .map(Into::into); - self.update_normal_hints(|normal_hints| { - normal_hints.set_min_size(min_inner_size); - normal_hints.set_max_size(max_inner_size); - }) - .expect("Failed to call `XSetWMNormalHints`"); - } - - #[inline] - pub fn xlib_display(&self) -> *mut c_void { - self.xconn.display as _ - } - - #[inline] - pub fn xlib_screen_id(&self) -> c_int { - self.screen_id - } - - #[inline] - pub fn xlib_xconnection(&self) -> Arc { - Arc::clone(&self.xconn) - } - - #[inline] - pub fn xlib_window(&self) -> c_ulong { - self.xwindow - } - - #[inline] - pub fn xcb_connection(&self) -> *mut c_void { - unsafe { (self.xconn.xlib_xcb.XGetXCBConnection)(self.xconn.display) as *mut _ } - } - - #[inline] - pub fn set_cursor_icon(&self, cursor: CursorIcon) { - let old_cursor = replace(&mut *self.cursor.lock(), cursor); - if cursor != old_cursor && *self.cursor_visible.lock() { - self.xconn.set_cursor_icon(self.xwindow, Some(cursor)); - } - } - - #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { - let mut grabbed_lock = self.cursor_grabbed.lock(); - if grab == *grabbed_lock { - return Ok(()); - } - unsafe { - // We ungrab before grabbing to prevent passive grabs from causing `AlreadyGrabbed`. - // Therefore, this is common to both codepaths. - (self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime); - } - let result = if grab { - let result = unsafe { - (self.xconn.xlib.XGrabPointer)( - self.xconn.display, - self.xwindow, - ffi::True, - (ffi::ButtonPressMask - | ffi::ButtonReleaseMask - | ffi::EnterWindowMask - | ffi::LeaveWindowMask - | ffi::PointerMotionMask - | ffi::PointerMotionHintMask - | ffi::Button1MotionMask - | ffi::Button2MotionMask - | ffi::Button3MotionMask - | ffi::Button4MotionMask - | ffi::Button5MotionMask - | ffi::ButtonMotionMask - | ffi::KeymapStateMask) as c_uint, - ffi::GrabModeAsync, - ffi::GrabModeAsync, - self.xwindow, - 0, - ffi::CurrentTime, - ) - }; - - match result { - ffi::GrabSuccess => Ok(()), - ffi::AlreadyGrabbed => { - Err("Cursor could not be grabbed: already grabbed by another client") - } - ffi::GrabInvalidTime => Err("Cursor could not be grabbed: invalid time"), - ffi::GrabNotViewable => { - Err("Cursor could not be grabbed: grab location not viewable") - } - ffi::GrabFrozen => Err("Cursor could not be grabbed: frozen by another client"), - _ => unreachable!(), - } - .map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err)))) - } else { - self.xconn - .flush_requests() - .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err)))) - }; - if result.is_ok() { - *grabbed_lock = grab; - } - result - } - - #[inline] - pub fn set_cursor_visible(&self, visible: bool) { - let mut visible_lock = self.cursor_visible.lock(); - if visible == *visible_lock { - return; - } - let cursor = if visible { - Some(*self.cursor.lock()) - } else { - None - }; - *visible_lock = visible; - drop(visible_lock); - self.xconn.set_cursor_icon(self.xwindow, cursor); - } - - #[inline] - pub fn scale_factor(&self) -> f64 { - self.current_monitor().scale_factor - } - - pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError> { - unsafe { - (self.xconn.xlib.XWarpPointer)(self.xconn.display, 0, self.xwindow, 0, 0, 0, 0, x, y); - self.xconn - .flush_requests() - .map_err(|e| ExternalError::Os(os_error!(OsError::XError(e)))) - } - } - - #[inline] - pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> { - let (x, y) = position.to_physical::(self.scale_factor()).into(); - self.set_cursor_position_physical(x, y) - } - - pub fn drag_window(&self) -> Result<(), ExternalError> { - let pointer = self - .xconn - .query_pointer(self.xwindow, util::VIRTUAL_CORE_POINTER) - .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err))))?; - - let window = self.inner_position().map_err(ExternalError::NotSupported)?; - - let message = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_MOVERESIZE\0") }; - - // we can't use `set_cursor_grab(false)` here because it doesn't run `XUngrabPointer` - // if the cursor isn't currently grabbed - let mut grabbed_lock = self.cursor_grabbed.lock(); - unsafe { - (self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime); - } - self.xconn - .flush_requests() - .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err))))?; - *grabbed_lock = false; - - // we keep the lock until we are done - self.xconn - .send_client_msg( - self.xwindow, - self.root, - message, - Some(ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask), - [ - (window.x as c_long + pointer.win_x as c_long), - (window.y as c_long + pointer.win_y as c_long), - 8, // _NET_WM_MOVERESIZE_MOVE - ffi::Button1 as c_long, - 1, - ], - ) - .flush() - .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err)))) - } - - pub(crate) fn set_ime_position_physical(&self, x: i32, y: i32) { - let _ = self - .ime_sender - .lock() - .send((self.xwindow, x as i16, y as i16)); - } - - #[inline] - pub fn set_ime_position(&self, spot: Position) { - let (x, y) = spot.to_physical::(self.scale_factor()).into(); - self.set_ime_position_physical(x, y); - } - - #[inline] - pub fn request_user_attention(&self, request_type: Option) { - let mut wm_hints = self - .xconn - .get_wm_hints(self.xwindow) - .expect("`XGetWMHints` failed"); - if request_type.is_some() { - (*wm_hints).flags |= ffi::XUrgencyHint; - } else { - (*wm_hints).flags &= !ffi::XUrgencyHint; - } - self.xconn - .set_wm_hints(self.xwindow, wm_hints) - .flush() - .expect("Failed to set urgency hint"); - } - - #[inline] - pub fn id(&self) -> WindowId { - WindowId(self.xwindow) - } - - #[inline] - pub fn request_redraw(&self) { - self.redraw_sender.send(WindowId(self.xwindow)).unwrap(); - } - - #[inline] - pub fn raw_window_handle(&self) -> XlibHandle { - XlibHandle { - window: self.xwindow, - display: self.xconn.display as _, - ..XlibHandle::empty() - } - } -} diff --git a/src/platform_impl/linux/x11/xdisplay.rs b/src/platform_impl/linux/x11/xdisplay.rs deleted file mode 100644 index 25065f04..00000000 --- a/src/platform_impl/linux/x11/xdisplay.rs +++ /dev/null @@ -1,165 +0,0 @@ -use std::{collections::HashMap, error::Error, fmt, os::raw::c_int, ptr}; - -use libc; -use parking_lot::Mutex; - -use crate::window::CursorIcon; - -use super::ffi; - -/// A connection to an X server. -pub struct XConnection { - pub xlib: ffi::Xlib, - /// Exposes XRandR functions from version < 1.5 - pub xrandr: ffi::Xrandr_2_2_0, - /// Exposes XRandR functions from version = 1.5 - pub xrandr_1_5: Option, - pub xcursor: ffi::Xcursor, - pub xinput2: ffi::XInput2, - pub xlib_xcb: ffi::Xlib_xcb, - pub xrender: ffi::Xrender, - pub display: *mut ffi::Display, - pub x11_fd: c_int, - pub latest_error: Mutex>, - pub cursor_cache: Mutex, ffi::Cursor>>, -} - -unsafe impl Send for XConnection {} -unsafe impl Sync for XConnection {} - -pub type XErrorHandler = - Option libc::c_int>; - -impl XConnection { - pub fn new(error_handler: XErrorHandler) -> Result { - // opening the libraries - let xlib = ffi::Xlib::open()?; - let xcursor = ffi::Xcursor::open()?; - let xrandr = ffi::Xrandr_2_2_0::open()?; - let xrandr_1_5 = ffi::Xrandr::open().ok(); - let xinput2 = ffi::XInput2::open()?; - let xlib_xcb = ffi::Xlib_xcb::open()?; - let xrender = ffi::Xrender::open()?; - - unsafe { (xlib.XInitThreads)() }; - unsafe { (xlib.XSetErrorHandler)(error_handler) }; - - // calling XOpenDisplay - let display = unsafe { - let display = (xlib.XOpenDisplay)(ptr::null()); - if display.is_null() { - return Err(XNotSupported::XOpenDisplayFailed); - } - display - }; - - // Get X11 socket file descriptor - let fd = unsafe { (xlib.XConnectionNumber)(display) }; - - Ok(XConnection { - xlib, - xrandr, - xrandr_1_5, - xcursor, - xinput2, - xlib_xcb, - xrender, - display, - x11_fd: fd, - latest_error: Mutex::new(None), - cursor_cache: Default::default(), - }) - } - - /// Checks whether an error has been triggered by the previous function calls. - #[inline] - pub fn check_errors(&self) -> Result<(), XError> { - let error = self.latest_error.lock().take(); - if let Some(error) = error { - Err(error) - } else { - Ok(()) - } - } - - /// Ignores any previous error. - #[inline] - pub fn ignore_error(&self) { - *self.latest_error.lock() = None; - } -} - -impl fmt::Debug for XConnection { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.display.fmt(f) - } -} - -impl Drop for XConnection { - #[inline] - fn drop(&mut self) { - unsafe { (self.xlib.XCloseDisplay)(self.display) }; - } -} - -/// Error triggered by xlib. -#[derive(Debug, Clone)] -pub struct XError { - pub description: String, - pub error_code: u8, - pub request_code: u8, - pub minor_code: u8, -} - -impl Error for XError {} - -impl fmt::Display for XError { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!( - formatter, - "X error: {} (code: {}, request code: {}, minor code: {})", - self.description, self.error_code, self.request_code, self.minor_code - ) - } -} - -/// Error returned if this system doesn't have XLib or can't create an X connection. -#[derive(Clone, Debug)] -pub enum XNotSupported { - /// Failed to load one or several shared libraries. - LibraryOpenError(ffi::OpenError), - /// Connecting to the X server with `XOpenDisplay` failed. - XOpenDisplayFailed, // TODO: add better message -} - -impl From for XNotSupported { - #[inline] - fn from(err: ffi::OpenError) -> XNotSupported { - XNotSupported::LibraryOpenError(err) - } -} - -impl XNotSupported { - fn description(&self) -> &'static str { - match self { - XNotSupported::LibraryOpenError(_) => "Failed to load one of xlib's shared libraries", - XNotSupported::XOpenDisplayFailed => "Failed to open connection to X server", - } - } -} - -impl Error for XNotSupported { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match *self { - XNotSupported::LibraryOpenError(ref err) => Some(err), - _ => None, - } - } -} - -impl fmt::Display for XNotSupported { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - formatter.write_str(self.description()) - } -}