native/desktop-linux/src/linux/events.rs (592 lines of code) (raw):

use core::f64; use std::{ ffi::{CStr, CString}, fmt::Write, }; use desktop_common::{ ffi_utils::{BorrowedArray, BorrowedStrPtr}, logger::PanicDefault, }; use enumflags2::{BitFlag, BitFlags, bitflags}; use crate::linux::{ application_api::{DataSource, DragAndDropAction}, geometry::{LogicalPixels, LogicalPoint, LogicalSize, PhysicalSize}, xdg_desktop_settings_api::XdgDesktopSetting, }; // return true if event was handled pub type EventHandler = extern "C" fn(&Event) -> bool; #[repr(transparent)] #[derive(Debug, Clone, Copy)] pub struct Timestamp(pub u32); #[repr(transparent)] #[derive(Debug, Clone, Copy)] pub struct ScreenId(pub u32); #[repr(transparent)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub struct WindowId(pub i64); #[repr(transparent)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub struct RequestId(pub u32); impl PanicDefault for RequestId { fn default() -> Self { Self(0) } } #[derive(Debug, Clone, Copy)] #[repr(transparent)] pub struct MouseButton(pub u32); #[bitflags] #[repr(u8)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum KeyModifier { /// The "control" key Ctrl = 0b0000_0001, /// The "alt" key Alt = 0b0000_0010, /// The "shift" key Shift = 0b0000_0100, /// The "Caps lock" key CapsLock = 0b0000_1000, /// The "logo" key /// /// Also known as the "windows" or "super" key on a keyboard. Logo = 0b0001_0000, /// The "Num lock" key NumLock = 0b0010_0000, } #[derive(Default, Clone, Copy, Eq, PartialEq)] #[repr(transparent)] pub struct KeyModifierBitflag(pub u8); impl std::fmt::Debug for KeyModifierBitflag { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "KeyModifierBitflag({:#08b}, ", &self.0)?; let bitflags = KeyModifier::from_bits(self.0).unwrap(); for (i, field) in bitflags.into_iter().enumerate() { if i > 0 { f.write_char('|')?; } field.fmt(f)?; } f.write_char(')') } } impl From<KeyModifier> for KeyModifierBitflag { fn from(value: KeyModifier) -> Self { Self(BitFlags::from_flag(value).bits_c()) } } impl From<BitFlags<KeyModifier>> for KeyModifierBitflag { fn from(value: BitFlags<KeyModifier>) -> Self { Self(value.bits_c()) } } impl KeyModifierBitflag { pub const EMPTY: Self = Self(0); } #[repr(transparent)] #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct KeyCode(pub u32); #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum WindowDecorationMode { /// The window should draw client side decorations. Client, /// The server will draw window decorations. Server, } #[repr(C)] #[derive(Debug)] /// Some examples: /// /// * `{ mime_type: "text/uri-list", data: "file:///data/some-file\r\nfile:///data/Some%20File%20With%20Spaces.txt\r\n" }` /// * `{ mime_type: "text/plain;charset=utf-8", data: "some text\r\nhere" }` pub struct DataTransferContent<'a> { pub mime_type: BorrowedStrPtr<'a>, pub data: BorrowedArray<'a, u8>, } impl<'a> DataTransferContent<'a> { #[must_use] pub fn new(mime_type: &'a CStr, data: &'a [u8]) -> Self { Self { mime_type: BorrowedStrPtr::new(mime_type), data: BorrowedArray::from_slice(data), } } #[must_use] pub fn null() -> Self { Self { mime_type: BorrowedStrPtr::null(), data: BorrowedArray::null(), } } } #[repr(C)] #[derive(Debug)] pub struct DataTransferEvent<'a> { pub serial: i32, pub content: DataTransferContent<'a>, } impl<'a> From<DataTransferEvent<'a>> for Event<'a> { fn from(value: DataTransferEvent<'a>) -> Self { Self::DataTransfer(value) } } #[repr(C)] #[derive(Debug)] pub struct DragAndDropLeaveEvent { pub window_id: WindowId, } impl From<DragAndDropLeaveEvent> for Event<'_> { fn from(value: DragAndDropLeaveEvent) -> Self { Self::DragAndDropLeave(value) } } #[repr(C)] #[derive(Debug)] pub struct DragAndDropFinishedEvent { pub window_id: WindowId, pub action: DragAndDropAction, } impl From<DragAndDropFinishedEvent> for Event<'_> { fn from(value: DragAndDropFinishedEvent) -> Self { Self::DragAndDropFinished(value) } } #[repr(C)] #[derive(Debug)] pub struct DropPerformedEvent<'a> { pub window_id: WindowId, pub content: DataTransferContent<'a>, pub action: DragAndDropAction, } impl<'a> From<DropPerformedEvent<'a>> for Event<'a> { fn from(value: DropPerformedEvent<'a>) -> Self { Self::DropPerformed(value) } } #[repr(C)] #[derive(Debug)] pub struct DataTransferAvailableEvent<'a> { pub data_source: DataSource, pub mime_types: BorrowedStrPtr<'a>, } impl<'a> From<DataTransferAvailableEvent<'a>> for Event<'a> { fn from(value: DataTransferAvailableEvent<'a>) -> Self { Self::DataTransferAvailable(value) } } impl<'a> DataTransferAvailableEvent<'a> { #[must_use] pub const fn new(data_source: DataSource, mime_types: &'a CStr) -> Self { Self { data_source, mime_types: BorrowedStrPtr::new(mime_types), } } } #[repr(C)] #[derive(Debug)] pub struct DataTransferCancelledEvent { pub data_source: DataSource, } impl From<DataTransferCancelledEvent> for Event<'_> { fn from(value: DataTransferCancelledEvent) -> Self { Self::DataTransferCancelled(value) } } #[repr(C)] #[derive(Debug)] pub struct KeyDownEvent<'a> { pub code: KeyCode, pub characters: BorrowedStrPtr<'a>, pub key: u32, pub is_repeat: bool, } impl<'a> From<KeyDownEvent<'a>> for Event<'a> { fn from(value: KeyDownEvent<'a>) -> Self { Self::KeyDown(value) } } impl<'a> KeyDownEvent<'a> { pub(crate) fn new(code: KeyCode, key: u32, characters: Option<&'a CString>, is_repeat: bool) -> Self { Self { code, characters: BorrowedStrPtr::new_optional(characters), key, is_repeat, } } } #[repr(C)] #[derive(Debug)] pub struct KeyUpEvent { pub code: KeyCode, pub key: u32, } impl From<KeyUpEvent> for Event<'_> { fn from(value: KeyUpEvent) -> Self { Self::KeyUp(value) } } #[repr(C)] #[derive(Debug)] pub struct ModifiersChangedEvent { pub modifiers: KeyModifierBitflag, } impl From<ModifiersChangedEvent> for Event<'_> { fn from(value: ModifiersChangedEvent) -> Self { Self::ModifiersChanged(value) } } #[repr(C)] #[derive(Debug)] pub struct MouseEnteredEvent { pub window_id: WindowId, pub location_in_window: LogicalPoint, } impl From<MouseEnteredEvent> for Event<'_> { fn from(value: MouseEnteredEvent) -> Self { Self::MouseEntered(value) } } #[repr(C)] #[derive(Debug)] pub struct MouseExitedEvent { pub window_id: WindowId, pub location_in_window: LogicalPoint, } impl From<MouseExitedEvent> for Event<'_> { fn from(value: MouseExitedEvent) -> Self { Self::MouseExited(value) } } #[repr(C)] #[derive(Debug)] pub struct MouseMovedEvent { pub window_id: WindowId, pub location_in_window: LogicalPoint, pub timestamp: Timestamp, } impl From<MouseMovedEvent> for Event<'_> { fn from(value: MouseMovedEvent) -> Self { Self::MouseMoved(value) } } #[repr(C)] #[derive(Debug)] pub struct MouseDownEvent { pub window_id: WindowId, pub button: MouseButton, pub location_in_window: LogicalPoint, pub timestamp: Timestamp, } impl From<MouseDownEvent> for Event<'_> { fn from(value: MouseDownEvent) -> Self { Self::MouseDown(value) } } #[repr(C)] #[derive(Debug)] pub struct MouseUpEvent { pub window_id: WindowId, pub button: MouseButton, pub location_in_window: LogicalPoint, pub timestamp: Timestamp, } impl From<MouseUpEvent> for Event<'_> { fn from(value: MouseUpEvent) -> Self { Self::MouseUp(value) } } #[repr(C)] #[derive(Debug)] pub struct ScrollData { pub delta: LogicalPixels, pub wheel_value120: i32, pub is_inverted: bool, pub is_stop: bool, } #[repr(C)] #[derive(Debug)] pub struct ScrollWheelEvent { pub window_id: WindowId, pub location_in_window: LogicalPoint, pub timestamp: Timestamp, pub horizontal_scroll: ScrollData, pub vertical_scroll: ScrollData, } impl From<ScrollWheelEvent> for Event<'_> { fn from(value: ScrollWheelEvent) -> Self { Self::ScrollWheel(value) } } #[repr(C)] #[derive(Debug)] pub struct TextInputPreeditStringData<'a> { /// Can be null pub text: BorrowedStrPtr<'a>, pub cursor_begin_byte_pos: i32, pub cursor_end_byte_pos: i32, } impl Default for TextInputPreeditStringData<'_> { fn default() -> Self { Self { text: BorrowedStrPtr::new_optional(None), cursor_begin_byte_pos: 0, cursor_end_byte_pos: 0, } } } #[repr(C)] #[derive(Debug, Default)] pub struct TextInputDeleteSurroundingTextData { pub before_length_in_bytes: u32, pub after_length_in_bytes: u32, } #[repr(C)] #[derive(Debug)] pub struct TextInputAvailabilityEvent { pub window_id: WindowId, /// Indicates if the Text Input support is available. /// Call `application_text_input_enable` to enable it or `application_text_input_disable` to disable it afterward. pub available: bool, } impl From<TextInputAvailabilityEvent> for Event<'_> { fn from(value: TextInputAvailabilityEvent) -> Self { Self::TextInputAvailability(value) } } /// The application must proceed by evaluating the changes in the following order: /// 1. Replace the existing preedit string with the cursor. /// 2. Delete the requested surrounding text. /// 3. Insert the commit string with the cursor at its end. /// 4. Calculate surrounding text to send. /// 5. Insert the new preedit text in the cursor position. /// 6. Place the cursor inside the preedit text. #[repr(C)] #[derive(Debug)] pub struct TextInputEvent<'a> { pub has_preedit_string: bool, pub preedit_string: TextInputPreeditStringData<'a>, pub has_commit_string: bool, /// Can be null pub commit_string: BorrowedStrPtr<'a>, pub has_delete_surrounding_text: bool, pub delete_surrounding_text: TextInputDeleteSurroundingTextData, } impl<'a> From<TextInputEvent<'a>> for Event<'a> { fn from(value: TextInputEvent<'a>) -> Self { Self::TextInput(value) } } #[repr(C)] #[derive(Debug)] pub struct WindowCapabilities { /// `show_window_menu` is available. pub window_menu: bool, /// Window can be maximized and unmaximized. pub maximize: bool, /// Window can be fullscreened and unfullscreened. pub fullscreen: bool, /// Window can be minimized. pub minimize: bool, } #[repr(C)] #[derive(Debug)] pub struct WindowCloseRequestEvent { pub window_id: WindowId, } impl From<WindowCloseRequestEvent> for Event<'_> { fn from(value: WindowCloseRequestEvent) -> Self { Self::WindowCloseRequest(value) } } #[repr(C)] #[derive(Debug)] pub struct WindowConfigureEvent { pub window_id: WindowId, pub size: LogicalSize, pub active: bool, pub maximized: bool, pub fullscreen: bool, pub decoration_mode: WindowDecorationMode, pub capabilities: WindowCapabilities, } impl From<WindowConfigureEvent> for Event<'_> { fn from(value: WindowConfigureEvent) -> Self { Self::WindowConfigure(value) } } #[repr(C)] #[derive(Debug, Default)] pub struct SoftwareDrawData { /// Can be null, to indicate that the software drawing is not being used pub canvas: *mut u8, pub stride: i32, } #[repr(C)] #[derive(Debug)] pub struct WindowDrawEvent { pub window_id: WindowId, pub software_draw_data: SoftwareDrawData, pub physical_size: PhysicalSize, pub scale: f64, } impl From<WindowDrawEvent> for Event<'_> { fn from(value: WindowDrawEvent) -> Self { Self::WindowDraw(value) } } #[repr(C)] #[derive(Debug)] pub struct DragIconDrawEvent { pub software_draw_data: SoftwareDrawData, pub physical_size: PhysicalSize, pub scale: f64, } impl From<DragIconDrawEvent> for Event<'_> { fn from(value: DragIconDrawEvent) -> Self { Self::DragIconDraw(value) } } #[repr(C)] #[derive(Debug)] pub struct WindowKeyboardEnterEvent<'a> { pub window_id: WindowId, pub raw: BorrowedArray<'a, u32>, pub keysyms: BorrowedArray<'a, u32>, } impl<'a> WindowKeyboardEnterEvent<'a> { pub(crate) fn new(window_id: WindowId, raw: &'a [u32], keysyms: &'a [u32]) -> Self { Self { window_id, raw: BorrowedArray::from_slice(raw), keysyms: BorrowedArray::from_slice(keysyms), } } } impl<'a> From<WindowKeyboardEnterEvent<'a>> for Event<'a> { fn from(value: WindowKeyboardEnterEvent<'a>) -> Self { Self::WindowKeyboardEnter(value) } } #[repr(C)] #[derive(Debug)] pub struct WindowKeyboardLeaveEvent { pub window_id: WindowId, } impl From<WindowKeyboardLeaveEvent> for Event<'_> { fn from(value: WindowKeyboardLeaveEvent) -> Self { Self::WindowKeyboardLeave(value) } } #[repr(C)] #[derive(Debug)] pub struct WindowScaleChangedEvent { pub window_id: WindowId, pub new_scale: f64, } impl From<WindowScaleChangedEvent> for Event<'_> { fn from(value: WindowScaleChangedEvent) -> Self { Self::WindowScaleChanged(value) } } #[repr(C)] #[derive(Debug)] pub struct WindowScreenChangeEvent { pub window_id: WindowId, pub new_screen_id: ScreenId, } impl From<WindowScreenChangeEvent> for Event<'_> { fn from(value: WindowScreenChangeEvent) -> Self { Self::WindowScreenChange(value) } } #[repr(C)] #[derive(Debug)] pub struct FileChooserResponse<'a> { pub request_id: RequestId, pub newline_separated_files: BorrowedStrPtr<'a>, } impl<'a> From<FileChooserResponse<'a>> for Event<'a> { fn from(value: FileChooserResponse<'a>) -> Self { Self::FileChooserResponse(value) } } #[repr(C)] #[derive(Debug)] pub struct ActivationTokenResponse<'a> { pub request_id: u32, pub token: BorrowedStrPtr<'a>, } impl<'a> ActivationTokenResponse<'a> { #[must_use] pub const fn new(request_id: u32, token: &'a CStr) -> Self { Self { request_id, token: BorrowedStrPtr::new(token), } } } impl<'a> From<ActivationTokenResponse<'a>> for Event<'a> { fn from(value: ActivationTokenResponse<'a>) -> Self { Self::ActivationTokenResponse(value) } } #[repr(C)] #[derive(Debug)] pub struct NotificationShownEvent { pub request_id: RequestId, /// Value `0` indicates an error. pub notification_id: u32, } impl From<NotificationShownEvent> for Event<'_> { fn from(value: NotificationShownEvent) -> Self { Self::NotificationShown(value) } } #[repr(C)] #[derive(Debug)] pub struct NotificationClosedEvent<'a> { pub notification_id: u32, /// Optional. Present only if notification was activated, and the application has an associated `.desktop` file. pub activation_token: BorrowedStrPtr<'a>, } impl<'a> NotificationClosedEvent<'a> { #[must_use] pub fn new(notification_id: u32, activation_token: Option<&'a CString>) -> Self { Self { notification_id, activation_token: BorrowedStrPtr::new_optional(activation_token), } } } impl<'a> From<NotificationClosedEvent<'a>> for Event<'a> { fn from(value: NotificationClosedEvent<'a>) -> Self { Self::NotificationClosed(value) } } #[repr(C)] #[derive(Debug)] pub enum Event<'a> { ApplicationStarted, /// Return `true` from the event handler if the application should _not_ terminate. ApplicationWantsToTerminate, ApplicationWillTerminate, DisplayConfigurationChange, XdgDesktopSettingChange(XdgDesktopSetting<'a>), /// Data received from clipboard or primary selection. For drag&drop, see `DropPerformed`. DataTransfer(DataTransferEvent<'a>), /// Drag&drop targeting our application left the specified window. DragAndDropLeave(DragAndDropLeaveEvent), /// Drag&drop that was initiated from our window has finished. DragAndDropFinished(DragAndDropFinishedEvent), DragIconDraw(DragIconDrawEvent), /// Drag&drop targeting our window is finished, and we received data from it. DropPerformed(DropPerformedEvent<'a>), /// Reported for clipboard and primary selection. DataTransferAvailable(DataTransferAvailableEvent<'a>), /// Data transfer for data from our application was canceled DataTransferCancelled(DataTransferCancelledEvent), FileChooserResponse(FileChooserResponse<'a>), ActivationTokenResponse(ActivationTokenResponse<'a>), NotificationShown(NotificationShownEvent), NotificationClosed(NotificationClosedEvent<'a>), /// Modifier keys (e.g Ctrl, Shift, etc) are never reported. Use `ModifiersChanged` for them. KeyDown(KeyDownEvent<'a>), /// Modifier keys (e.g Ctrl, Shift, etc) are never reported. Use `ModifiersChanged` for them. KeyUp(KeyUpEvent), ModifiersChanged(ModifiersChangedEvent), MouseEntered(MouseEnteredEvent), MouseExited(MouseExitedEvent), MouseMoved(MouseMovedEvent), MouseDown(MouseDownEvent), MouseUp(MouseUpEvent), ScrollWheel(ScrollWheelEvent), TextInputAvailability(TextInputAvailabilityEvent), TextInput(TextInputEvent<'a>), WindowCloseRequest(WindowCloseRequestEvent), WindowConfigure(WindowConfigureEvent), WindowDraw(WindowDrawEvent), WindowKeyboardEnter(WindowKeyboardEnterEvent<'a>), WindowKeyboardLeave(WindowKeyboardLeaveEvent), WindowScaleChanged(WindowScaleChangedEvent), WindowScreenChange(WindowScreenChangeEvent), }